@iaforged/context-code 2.1.5 → 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Task.js +1 -1
- package/dist/src/commands/add-dir/add-dir.js +1 -1
- package/dist/src/commands/add-dir/validation.js +1 -1
- package/dist/src/constants/oauth.js +1 -1
- package/dist/src/context/mailbox.js +1 -1
- package/dist/src/context/voice.js +1 -1
- package/dist/src/hooks/useTerminalSize.js +1 -1
- package/dist/src/ink/Ansi.js +1 -1
- package/dist/src/ink/clearTerminal.js +1 -1
- package/dist/src/ink/colorize.js +1 -1
- package/dist/src/ink/components/App.js +1 -1
- package/dist/src/ink/components/Button.js +1 -1
- package/dist/src/ink/components/ClockContext.js +1 -1
- package/dist/src/ink/components/CursorDeclarationContext.js +1 -1
- package/dist/src/ink/components/Link.js +1 -1
- package/dist/src/ink/components/StdinContext.js +1 -1
- package/dist/src/ink/components/TerminalFocusContext.js +1 -1
- package/dist/src/ink/dom.js +1 -1
- package/dist/src/ink/events/keyboard-event.js +1 -1
- package/dist/src/ink/hit-test.js +1 -1
- package/dist/src/ink/hooks/use-animation-frame.js +1 -1
- package/dist/src/ink/hooks/use-app.js +1 -1
- package/dist/src/ink/hooks/use-input.js +1 -1
- package/dist/src/ink/hooks/use-interval.js +1 -1
- package/dist/src/ink/hooks/use-selection.js +1 -1
- package/dist/src/ink/hooks/use-tab-status.js +1 -1
- package/dist/src/ink/hooks/use-terminal-focus.js +1 -1
- package/dist/src/ink/hooks/use-terminal-title.js +1 -1
- package/dist/src/ink/hooks/use-terminal-viewport.js +1 -1
- package/dist/src/ink/ink.js +1 -1
- package/dist/src/ink/layout/yoga.js +1 -1
- package/dist/src/ink/line-width-cache.js +1 -1
- package/dist/src/ink/log-update.js +1 -1
- package/dist/src/ink/measure-text.js +1 -1
- package/dist/src/ink/output.js +1 -1
- package/dist/src/ink/parse-keypress.js +1 -1
- package/dist/src/ink/reconciler.js +1 -1
- package/dist/src/ink/render-border.js +1 -1
- package/dist/src/ink/render-node-to-output.js +1 -1
- package/dist/src/ink/render-to-screen.js +1 -1
- package/dist/src/ink/renderer.js +1 -1
- package/dist/src/ink/root.js +1 -1
- package/dist/src/ink/screen.js +1 -1
- package/dist/src/ink/searchHighlight.js +1 -1
- package/dist/src/ink/selection.js +1 -1
- package/dist/src/ink/squash-text-nodes.js +1 -1
- package/dist/src/ink/stringWidth.js +1 -1
- package/dist/src/ink/tabstops.js +1 -1
- package/dist/src/ink/terminal.js +1 -1
- package/dist/src/ink/termio/osc.js +1 -1
- package/dist/src/ink/termio/parser.js +1 -1
- package/dist/src/ink/termio/tokenize.js +1 -1
- package/dist/src/ink/useTerminalNotification.js +1 -1
- package/dist/src/ink/warn.js +1 -1
- package/dist/src/ink/widest-line.js +1 -1
- package/dist/src/ink/wrap-text.js +1 -1
- package/dist/src/ink/wrapAnsi.js +1 -1
- package/dist/src/native-ts/yoga-layout/index.js +1 -1
- package/dist/src/schemas/hooks.js +1 -1
- package/dist/src/services/SessionMemory/sessionMemoryUtils.js +1 -1
- package/dist/src/services/api/client.js +1 -1
- package/dist/src/services/api/dumpPrompts.js +1 -1
- package/dist/src/services/api/errorUtils.js +1 -1
- package/dist/src/services/api/promptCacheBreakDetection.js +1 -1
- package/dist/src/services/api/withRetry.js +1 -1
- package/dist/src/services/autoDream/consolidationLock.js +1 -1
- package/dist/src/services/mcp/elicitationHandler.js +1 -1
- package/dist/src/services/mcp/mcpStringUtils.js +1 -1
- package/dist/src/services/mcp/oauthPort.js +1 -1
- package/dist/src/services/mcp/vscodeSdkMcp.js +1 -1
- package/dist/src/services/oauth/client.js +1 -1
- package/dist/src/services/oauth/getOauthProfile.js +1 -1
- package/dist/src/services/objetivo/types.js +1 -1
- package/dist/src/services/rateLimitMocking.js +1 -1
- package/dist/src/services/remoteManagedSettings/syncCacheState.js +1 -1
- package/dist/src/skills/bundledSkills.js +1 -1
- package/dist/src/tasks/DreamTask/DreamTask.js +1 -1
- package/dist/src/tools/AgentTool/agentMemory.js +1 -1
- package/dist/src/tools/AgentTool/forkSubagent.js +1 -1
- package/dist/src/tools/BashTool/BashToolResultMessage.js +1 -1
- package/dist/src/tools/BashTool/UI.js +1 -1
- package/dist/src/tools/BashTool/sedEditParser.js +1 -1
- package/dist/src/tools/BashTool/utils.js +1 -1
- package/dist/src/tools/FileReadTool/imageProcessor.js +1 -1
- package/dist/src/tools/FileReadTool/prompt.js +1 -1
- package/dist/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.js +1 -1
- package/dist/src/tools/ListMcpResourcesTool/UI.js +1 -1
- package/dist/src/tools/MCPTool/MCPTool.js +1 -1
- package/dist/src/tools/MCPTool/UI.js +1 -1
- package/dist/src/tools/McpAuthTool/McpAuthTool.js +1 -1
- package/dist/src/tools/NotebookEditTool/prompt.js +1 -1
- package/dist/src/tools/PowerShellTool/PowerShellTool.js +1 -1
- package/dist/src/tools/PowerShellTool/UI.js +1 -1
- package/dist/src/tools/PowerShellTool/gitSafety.js +1 -1
- package/dist/src/tools/PowerShellTool/modeValidation.js +1 -1
- package/dist/src/tools/PowerShellTool/pathValidation.js +1 -1
- package/dist/src/tools/PowerShellTool/powershellPermissions.js +1 -1
- package/dist/src/tools/PowerShellTool/powershellSecurity.js +1 -1
- package/dist/src/tools/PowerShellTool/prompt.js +1 -1
- package/dist/src/tools/PowerShellTool/readOnlyValidation.js +1 -1
- package/dist/src/tools/REPLTool/constants.js +1 -1
- package/dist/src/tools/REPLTool/primitiveTools.js +1 -1
- package/dist/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.js +1 -1
- package/dist/src/tools/ReadMcpResourceTool/UI.js +1 -1
- package/dist/src/tools/ScheduleCronTool/prompt.js +1 -1
- package/dist/src/tools/SkillTool/prompt.js +1 -1
- package/dist/src/tools/TodoWriteTool/TodoWriteTool.js +1 -1
- package/dist/src/tools/ToolSearchTool/prompt.js +1 -1
- package/dist/src/tools/WebSearchTool/prompt.js +1 -1
- package/dist/src/tools/shared/gitOperationTracking.js +1 -1
- package/dist/src/types/permissions.js +1 -1
- package/dist/src/utils/Cursor.js +1 -1
- package/dist/src/utils/QueryGuard.js +1 -1
- package/dist/src/utils/Shell.js +1 -1
- package/dist/src/utils/ShellCommand.js +1 -1
- package/dist/src/utils/activityManager.js +1 -1
- package/dist/src/utils/advisor.js +1 -1
- package/dist/src/utils/appleTerminalBackup.js +1 -1
- package/dist/src/utils/argumentSubstitution.js +1 -1
- package/dist/src/utils/authFileDescriptor.js +1 -1
- package/dist/src/utils/autoUpdater.js +1 -1
- package/dist/src/utils/background/remote/preconditions.js +1 -1
- package/dist/src/utils/background/remote/remoteSession.js +1 -1
- package/dist/src/utils/bash/ShellSnapshot.js +1 -1
- package/dist/src/utils/bash/ast.js +1 -1
- package/dist/src/utils/bash/bashParser.js +1 -1
- package/dist/src/utils/bash/bashPipeCommand.js +1 -1
- package/dist/src/utils/bash/parser.js +1 -1
- package/dist/src/utils/bash/shellQuote.js +1 -1
- package/dist/src/utils/bash/shellQuoting.js +1 -1
- package/dist/src/utils/billing.js +1 -1
- package/dist/src/utils/caCerts.js +1 -1
- package/dist/src/utils/claudeInChrome/common.js +1 -1
- package/dist/src/utils/claudeInChrome/setupPortable.js +1 -1
- package/dist/src/utils/claudemd.js +1 -1
- package/dist/src/utils/collapseBackgroundBashNotifications.js +1 -1
- package/dist/src/utils/collapseReadSearch.js +1 -1
- package/dist/src/utils/completionCache.js +1 -1
- package/dist/src/utils/computerUse/common.js +1 -1
- package/dist/src/utils/concurrentSessions.js +1 -1
- package/dist/src/utils/context.js +1 -1
- package/dist/src/utils/cron.js +1 -1
- package/dist/src/utils/cronTasks.js +1 -1
- package/dist/src/utils/cwd.js +1 -1
- package/dist/src/utils/debug.js +1 -1
- package/dist/src/utils/debugFilter.js +1 -1
- package/dist/src/utils/detectRepository.js +1 -1
- package/dist/src/utils/diagLogs.js +1 -1
- package/dist/src/utils/diff.js +1 -1
- package/dist/src/utils/directMemberMessage.js +1 -1
- package/dist/src/utils/doctorDiagnostic.js +1 -1
- package/dist/src/utils/dxt/helpers.js +1 -1
- package/dist/src/utils/dxt/zip.js +1 -1
- package/dist/src/utils/earlyInput.js +1 -1
- package/dist/src/utils/editor.js +1 -1
- package/dist/src/utils/effort.js +1 -1
- package/dist/src/utils/embeddedTools.js +1 -1
- package/dist/src/utils/envDynamic.js +1 -1
- package/dist/src/utils/envUtils.js +1 -1
- package/dist/src/utils/execFileNoThrowPortable.js +1 -1
- package/dist/src/utils/execSyncWrapper.js +1 -1
- package/dist/src/utils/exportRenderer.js +1 -1
- package/dist/src/utils/extraUsage.js +1 -1
- package/dist/src/utils/fastMode.js +1 -1
- package/dist/src/utils/fileOperationAnalytics.js +1 -1
- package/dist/src/utils/fileRead.js +1 -1
- package/dist/src/utils/findExecutable.js +1 -1
- package/dist/src/utils/format.js +1 -1
- package/dist/src/utils/frontmatterParser.js +1 -1
- package/dist/src/utils/fsOperations.js +1 -1
- package/dist/src/utils/fullscreen.js +1 -1
- package/dist/src/utils/genericProcessUtils.js +1 -1
- package/dist/src/utils/getWorktreePaths.js +1 -1
- package/dist/src/utils/git/gitConfigParser.js +1 -1
- package/dist/src/utils/git/gitFilesystem.js +1 -1
- package/dist/src/utils/git/gitignore.js +1 -1
- package/dist/src/utils/gitDiff.js +1 -1
- package/dist/src/utils/gitSettings.js +1 -1
- package/dist/src/utils/glob.js +1 -1
- package/dist/src/utils/gracefulShutdown.js +1 -1
- package/dist/src/utils/groupToolUses.js +1 -1
- package/dist/src/utils/handlePromptSubmit.js +1 -1
- package/dist/src/utils/hash.js +1 -1
- package/dist/src/utils/hooks/fileChangedWatcher.js +1 -1
- package/dist/src/utils/hooks/hooksSettings.js +1 -1
- package/dist/src/utils/hooks/registerSkillHooks.js +1 -1
- package/dist/src/utils/hooks/sessionHooks.js +1 -1
- package/dist/src/utils/http.js +1 -1
- package/dist/src/utils/hyperlink.js +1 -1
- package/dist/src/utils/ide.js +1 -1
- package/dist/src/utils/idePathConversion.js +1 -1
- package/dist/src/utils/imagePaste.js +1 -1
- package/dist/src/utils/imageResizer.js +1 -1
- package/dist/src/utils/imageStore.js +1 -1
- package/dist/src/utils/inProcessTeammateHelpers.js +1 -1
- package/dist/src/utils/ink.js +1 -1
- package/dist/src/utils/jetbrains.js +1 -1
- package/dist/src/utils/json.js +1 -1
- package/dist/src/utils/listSessionsImpl.js +1 -1
- package/dist/src/utils/localInstaller.js +1 -1
- package/dist/src/utils/lockfile.js +1 -1
- package/dist/src/utils/logoV2Utils.js +1 -1
- package/dist/src/utils/markdown.js +1 -1
- package/dist/src/utils/mcp/dateTimeParser.js +1 -1
- package/dist/src/utils/mcpOutputStorage.js +1 -1
- package/dist/src/utils/mcpValidation.js +1 -1
- package/dist/src/utils/memoize.js +1 -1
- package/dist/src/utils/memory/types.js +1 -1
- package/dist/src/utils/memoryFileDetection.js +1 -1
- package/dist/src/utils/messageQueueManager.js +1 -1
- package/dist/src/utils/messages/mappers.js +1 -1
- package/dist/src/utils/messages/systemInit.js +1 -1
- package/dist/src/utils/model/antModels.js +1 -1
- package/dist/src/utils/model/check1mAccess.js +1 -1
- package/dist/src/utils/model/contextWindowUpgradeCheck.js +1 -1
- package/dist/src/utils/model/model.js +1 -1
- package/dist/src/utils/model/modelAllowlist.js +1 -1
- package/dist/src/utils/model/modelCapabilities.js +1 -1
- package/dist/src/utils/model/modelOptions.js +1 -1
- package/dist/src/utils/model/modelStrings.js +1 -1
- package/dist/src/utils/model/providerModels.js +1 -1
- package/dist/src/utils/model/providerProfiles.js +1 -1
- package/dist/src/utils/model/providerProfilesDb.js +1 -1
- package/dist/src/utils/model/providerSwitch.js +1 -1
- package/dist/src/utils/model/providers.js +1 -1
- package/dist/src/utils/modelCost.js +1 -1
- package/dist/src/utils/modifiers.js +1 -1
- package/dist/src/utils/mtls.js +1 -1
- package/dist/src/utils/nativeInstaller/download.js +1 -1
- package/dist/src/utils/nativeInstaller/installer.js +1 -1
- package/dist/src/utils/nativeInstaller/packageManagers.js +1 -1
- package/dist/src/utils/nativeInstaller/pidLock.js +1 -1
- package/dist/src/utils/notebook.js +1 -1
- package/dist/src/utils/pasteStore.js +1 -1
- package/dist/src/utils/path.js +1 -1
- package/dist/src/utils/permissions/PermissionMode.js +1 -1
- package/dist/src/utils/permissions/PermissionPromptToolResultSchema.js +1 -1
- package/dist/src/utils/permissions/PermissionUpdate.js +1 -1
- package/dist/src/utils/permissions/PermissionUpdateSchema.js +1 -1
- package/dist/src/utils/permissions/autoModeState.js +1 -1
- package/dist/src/utils/permissions/bypassPermissionsKillswitch.js +1 -1
- package/dist/src/utils/permissions/filesystem.js +1 -1
- package/dist/src/utils/permissions/getNextPermissionMode.js +1 -1
- package/dist/src/utils/permissions/pathValidation.js +1 -1
- package/dist/src/utils/permissions/permissionRuleParser.js +1 -1
- package/dist/src/utils/permissions/permissionsDb.js +1 -1
- package/dist/src/utils/permissions/permissionsLoader.js +1 -1
- package/dist/src/utils/permissions/shellRuleMatching.js +1 -1
- package/dist/src/utils/planModeV2.js +1 -1
- package/dist/src/utils/plans.js +1 -1
- package/dist/src/utils/platform.js +1 -1
- package/dist/src/utils/plugins/addDirPluginSettings.js +1 -1
- package/dist/src/utils/plugins/cacheUtils.js +1 -1
- package/dist/src/utils/plugins/dependencyResolver.js +1 -1
- package/dist/src/utils/plugins/fetchTelemetry.js +1 -1
- package/dist/src/utils/plugins/gitAvailability.js +1 -1
- package/dist/src/utils/plugins/hintRecommendation.js +1 -1
- package/dist/src/utils/plugins/installedPluginsManager.js +1 -1
- package/dist/src/utils/plugins/loadPluginAgents.js +1 -1
- package/dist/src/utils/plugins/loadPluginCommands.js +1 -1
- package/dist/src/utils/plugins/loadPluginHooks.js +1 -1
- package/dist/src/utils/plugins/loadPluginOutputStyles.js +1 -1
- package/dist/src/utils/plugins/lspPluginIntegration.js +1 -1
- package/dist/src/utils/plugins/lspRecommendation.js +1 -1
- package/dist/src/utils/plugins/managedPlugins.js +1 -1
- package/dist/src/utils/plugins/marketplaceHelpers.js +1 -1
- package/dist/src/utils/plugins/marketplaceManager.js +1 -1
- package/dist/src/utils/plugins/mcpPluginIntegration.js +1 -1
- package/dist/src/utils/plugins/mcpbHandler.js +1 -1
- package/dist/src/utils/plugins/officialMarketplaceGcs.js +1 -1
- package/dist/src/utils/plugins/officialMarketplaceStartupCheck.js +1 -1
- package/dist/src/utils/plugins/orphanedPluginFilter.js +1 -1
- package/dist/src/utils/plugins/performStartupChecks.js +1 -1
- package/dist/src/utils/plugins/pluginAutoupdate.js +1 -1
- package/dist/src/utils/plugins/pluginBlocklist.js +1 -1
- package/dist/src/utils/plugins/pluginDirectories.js +1 -1
- package/dist/src/utils/plugins/pluginFlagging.js +1 -1
- package/dist/src/utils/plugins/pluginInstallationHelpers.js +1 -1
- package/dist/src/utils/plugins/pluginLoader.js +1 -1
- package/dist/src/utils/plugins/pluginOptionsStorage.js +1 -1
- package/dist/src/utils/plugins/pluginPolicy.js +1 -1
- package/dist/src/utils/plugins/pluginStartupCheck.js +1 -1
- package/dist/src/utils/plugins/pluginVersioning.js +1 -1
- package/dist/src/utils/plugins/reconciler.js +1 -1
- package/dist/src/utils/plugins/refresh.js +1 -1
- package/dist/src/utils/plugins/schemas.js +1 -1
- package/dist/src/utils/plugins/walkPluginMarkdown.js +1 -1
- package/dist/src/utils/plugins/zipCache.js +1 -1
- package/dist/src/utils/powershell/parser.js +1 -1
- package/dist/src/utils/processUserInput/processBashCommand.js +1 -1
- package/dist/src/utils/processUserInput/processSlashCommand.js +1 -1
- package/dist/src/utils/processUserInput/processTextPrompt.js +1 -1
- package/dist/src/utils/processUserInput/processUserInput.js +1 -1
- package/dist/src/utils/profilerBase.js +1 -1
- package/dist/src/utils/promptCategory.js +1 -1
- package/dist/src/utils/promptEditor.js +1 -1
- package/dist/src/utils/promptShellExecution.js +1 -1
- package/dist/src/utils/proxy.js +1 -1
- package/dist/src/utils/queryHelpers.js +1 -1
- package/dist/src/utils/queryProfiler.js +1 -1
- package/dist/src/utils/queueProcessor.js +1 -1
- package/dist/src/utils/readFileInRange.js +1 -1
- package/dist/src/utils/releaseNotes.js +1 -1
- package/dist/src/utils/renderOptions.js +1 -1
- package/dist/src/utils/ripgrep.js +1 -1
- package/dist/src/utils/sandbox/sandbox-adapter.js +1 -1
- package/dist/src/utils/sdkEventQueue.js +1 -1
- package/dist/src/utils/secureStorage/index.js +1 -1
- package/dist/src/utils/secureStorage/macOsKeychainHelpers.js +1 -1
- package/dist/src/utils/secureStorage/macOsKeychainStorage.js +1 -1
- package/dist/src/utils/secureStorage/plainTextStorage.js +1 -1
- package/dist/src/utils/secureStorage/sqliteStorage.js +1 -1
- package/dist/src/utils/sessionEnvironment.js +1 -1
- package/dist/src/utils/sessionIngressAuth.js +1 -1
- package/dist/src/utils/sessionRestore.js +1 -1
- package/dist/src/utils/sessionStart.js +1 -1
- package/dist/src/utils/sessionTitle.js +1 -1
- package/dist/src/utils/settings/managedPath.js +1 -1
- package/dist/src/utils/settings/mdm/rawRead.js +1 -1
- package/dist/src/utils/settings/mdm/settings.js +1 -1
- package/dist/src/utils/settings/permissionValidation.js +1 -1
- package/dist/src/utils/settings/pluginOnlyPolicy.js +1 -1
- package/dist/src/utils/settings/schemaOutput.js +1 -1
- package/dist/src/utils/settings/settings.js +1 -1
- package/dist/src/utils/settings/types.js +1 -1
- package/dist/src/utils/settings/validateEditTool.js +1 -1
- package/dist/src/utils/settings/validation.js +1 -1
- package/dist/src/utils/shell/bashProvider.js +1 -1
- package/dist/src/utils/shell/powershellDetection.js +1 -1
- package/dist/src/utils/shell/powershellProvider.js +1 -1
- package/dist/src/utils/shell/readOnlyCommandValidation.js +1 -1
- package/dist/src/utils/shell/resolveDefaultShell.js +1 -1
- package/dist/src/utils/shell/shellToolUtils.js +1 -1
- package/dist/src/utils/shell/specPrefix.js +1 -1
- package/dist/src/utils/shellConfig.js +1 -1
- package/dist/src/utils/sideQuestion.js +1 -1
- package/dist/src/utils/skills/skillChangeDetector.js +1 -1
- package/dist/src/utils/slashCommandParsing.js +1 -1
- package/dist/src/utils/sliceAnsi.js +1 -1
- package/dist/src/utils/slowOperations.js +1 -1
- package/dist/src/utils/standaloneAgent.js +1 -1
- package/dist/src/utils/startupProfiler.js +1 -1
- package/dist/src/utils/staticRender.js +1 -1
- package/dist/src/utils/status.js +1 -1
- package/dist/src/utils/statusNoticeDefinitions.js +1 -1
- package/dist/src/utils/suggestions/commandSuggestions.js +1 -1
- package/dist/src/utils/suggestions/directoryCompletion.js +1 -1
- package/dist/src/utils/suggestions/shellHistoryCompletion.js +1 -1
- package/dist/src/utils/suggestions/skillUsageTracking.js +1 -1
- package/dist/src/utils/suggestions/slackChannelSuggestions.js +1 -1
- package/dist/src/utils/swarm/backends/detection.js +1 -1
- package/dist/src/utils/swarm/permissionSync.js +1 -1
- package/dist/src/utils/swarm/reconnection.js +1 -1
- package/dist/src/utils/swarm/spawnUtils.js +91 -1
- package/dist/src/utils/swarm/teammateInit.js +1 -1
- package/dist/src/utils/systemDirectories.js +1 -1
- package/dist/src/utils/systemPrompt.js +1 -1
- package/dist/src/utils/systemTheme.js +1 -1
- package/dist/src/utils/task/TaskOutput.js +1 -1
- package/dist/src/utils/task/diskOutput.js +1 -1
- package/dist/src/utils/tasks.js +1 -1
- package/dist/src/utils/teamDiscovery.js +1 -1
- package/dist/src/utils/teamMemoryOps.js +1 -1
- package/dist/src/utils/teammateMailbox.js +1 -1
- package/dist/src/utils/telemetry/betaSessionTracing.js +1 -1
- package/dist/src/utils/telemetry/bigqueryExporter.js +1 -1
- package/dist/src/utils/telemetry/events.js +1 -1
- package/dist/src/utils/telemetry/instrumentation.js +1 -1
- package/dist/src/utils/telemetry/logger.js +1 -1
- package/dist/src/utils/telemetry/perfettoTracing.js +1 -1
- package/dist/src/utils/telemetry/pluginTelemetry.js +1 -1
- package/dist/src/utils/telemetry/sessionTracing.js +1 -1
- package/dist/src/utils/telemetryAttributes.js +1 -1
- package/dist/src/utils/teleport/api.js +1 -1
- package/dist/src/utils/teleport/environments.js +1 -1
- package/dist/src/utils/teleport/gitBundle.js +1 -1
- package/dist/src/utils/teleport.js +1 -1
- package/dist/src/utils/tempfile.js +1 -1
- package/dist/src/utils/terminal.js +1 -1
- package/dist/src/utils/terminalPanel.js +1 -1
- package/dist/src/utils/textHighlighting.js +1 -1
- package/dist/src/utils/theme.js +1 -1
- package/dist/src/utils/themes/bootstrap.js +1 -1
- package/dist/src/utils/themes/loader.js +1 -1
- package/dist/src/utils/thinking.js +1 -1
- package/dist/src/utils/tmuxSocket.js +1 -1
- package/dist/src/utils/tokens.js +1 -1
- package/dist/src/utils/toolPool.js +1 -1
- package/dist/src/utils/toolResultStorage.js +1 -1
- package/dist/src/utils/transcriptSearch.js +1 -1
- package/dist/src/utils/truncate.js +1 -1
- package/dist/src/utils/ultraplan/keyword.js +1 -1
- package/dist/src/utils/unaryLogging.js +1 -1
- package/dist/src/utils/undercover.js +1 -1
- package/dist/src/utils/user.js +1 -1
- package/dist/src/utils/userPromptKeywords.js +1 -1
- package/dist/src/utils/which.js +1 -1
- package/dist/src/utils/windowsPaths.js +1 -1
- package/dist/src/utils/worktree.js +1 -1
- package/dist/src/utils/zodToJsonSchema.js +1 -1
- package/dist/src/vim/operators.js +1 -1
- package/dist/src/vim/textObjects.js +1 -1
- package/dist/src/vim/transitions.js +1 -1
- package/dist/src/voice/voiceModeEnabled.js +1 -1
- package/dist/src/webapp/auth.js +1 -1
- package/dist/src/webapp/tunnel.js +1 -1
- package/dist/src/whatsapp/bridge.js +1 -1
- package/dist/src/whatsapp/mirror.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname as t,join as n}from"path";import{logForDebugging as e}from"../debug.js";import{errorMessage as s,isENOENT as i,toError as l}from"../errors.js";import{getFsImplementation as o}from"../fsOperations.js";import{logError as a}from"../log.js";import{jsonParse as r,jsonStringify as c,writeFileSync_DEPRECATED as u}from"../slowOperations.js";import{getPluginsDirectory as d}from"./pluginDirectories.js";import{InstalledPluginsFileSchemaV1 as g,InstalledPluginsFileSchemaV2 as p}from"./schemas.js";import{getOriginalCwd as f}from"../../bootstrap/state.js";import{getCwd as m}from"../cwd.js";import{getHeadForDir as h}from"../git/gitFilesystem.js";import{getSettings_DEPRECATED as P,getSettingsForSource as v}from"../settings/settings.js";import{getPluginById as y}from"./marketplaceManager.js";import{parsePluginIdentifier as j,settingSourceToScope as I}from"./pluginIdentifier.js";import{getPluginCachePath as S,getVersionedCachePath as $}from"./pluginLoader.js";let k=!1,w=null,F=null;export function getInstalledPluginsFilePath(){return n(d(),"installed_plugins.json")}export function getInstalledPluginsV2FilePath(){return n(d(),"installed_plugins_v2.json")}export function clearInstalledPluginsCache(){w=null,F=null,e("Cleared installed plugins cache")}export function migrateToSinglePluginFile(){if(k)return;const t=o(),n=getInstalledPluginsFilePath(),d=getInstalledPluginsV2FilePath();try{try{t.renameSync(d,n),e("Renamed installed_plugins_v2.json to installed_plugins.json");return cleanupLegacyCache(loadInstalledPluginsV2()),void(k=!0)}catch(t){if(!i(t))throw t}let s;try{s=t.readFileSync(n,{encoding:"utf-8"})}catch(t){if(!i(t))throw t;return void(k=!0)}const l=r(s);if(1===("number"==typeof l?.version?l.version:1)){const t=g().parse(l),s=migrateV1ToV2(t);u(n,c(s,null,2),{encoding:"utf-8",flush:!0}),e(`Converted installed_plugins.json from V1 to V2 format (${Object.keys(t.plugins).length} plugins)`),cleanupLegacyCache(s)}k=!0}catch(t){const n=s(t);e(`Failed to migrate plugin files: ${n}`,{level:"error"}),a(l(t)),k=!0}}function cleanupLegacyCache(t){const i=o(),l=S();try{const s=new Set;for(const n of Object.values(t.plugins))for(const t of n)s.add(t.installPath);const o=i.readdirSync(l);for(const t of o){if(!t.isDirectory())continue;const o=t.name,a=n(l,o),r=i.readdirSync(a);r.some(t=>{if(!t.isDirectory())return!1;const e=n(a,t.name);return i.readdirSync(e).some(t=>t.isDirectory())})||(s.has(a)||(i.rmSync(a,{recursive:!0,force:!0}),e(`Cleaned up legacy cache directory: ${o}`)))}}catch(t){const n=s(t);e(`Failed to clean up legacy cache: ${n}`,{level:"warn"})}}export function resetMigrationState(){k=!1}function readInstalledPluginsFileRaw(){const t=o(),n=getInstalledPluginsFilePath();let e;try{e=t.readFileSync(n,{encoding:"utf-8"})}catch(t){if(i(t))return null;throw t}const s=r(e);return{version:"number"==typeof s?.version?s.version:1,data:s}}function migrateV1ToV2(t){const n={};for(const[e,s]of Object.entries(t.plugins)){const t=$(e,s.version);n[e]=[{scope:"user",installPath:t,version:s.version,installedAt:s.installedAt,lastUpdated:s.lastUpdated,gitCommitSha:s.gitCommitSha}]}return{version:2,plugins:n}}export function loadInstalledPluginsV2(){if(null!==w)return w;const t=getInstalledPluginsFilePath();try{const n=readInstalledPluginsFileRaw();if(n){if(2===n.version){const s=p().parse(n.data);return w=s,e(`Loaded ${Object.keys(s.plugins).length} installed plugins from ${t}`),s}const s=g().parse(n.data),i=migrateV1ToV2(s);return w=i,e(`Loaded and converted ${Object.keys(s.plugins).length} plugins from V1 format`),i}return e("installed_plugins.json doesn't exist, returning empty V2 object"),w={version:2,plugins:{}},w}catch(t){const n=s(t);return e(`Failed to load installed_plugins.json: ${n}. Starting with empty state.`,{level:"error"}),a(l(t)),w={version:2,plugins:{}},w}}function saveInstalledPluginsV2(t){const n=o(),i=getInstalledPluginsFilePath();try{n.mkdirSync(d());const s=c(t,null,2);u(i,s,{encoding:"utf-8",flush:!0}),w=t,e(`Saved ${Object.keys(t.plugins).length} installed plugins to ${i}`)}catch(t){s(t);throw a(l(t)),t}}export function addPluginInstallation(t,n,s,i,l){const o=loadInstalledPluginsFromDisk(),a=o.plugins[t]||[],r=a.findIndex(t=>t.scope===n&&t.projectPath===l),c={scope:n,installPath:s,version:i.version,installedAt:i.installedAt||(new Date).toISOString(),lastUpdated:(new Date).toISOString(),gitCommitSha:i.gitCommitSha,...l&&{projectPath:l}};r>=0?(a[r]=c,e(`Updated installation for ${t} at scope ${n}`)):(a.push(c),e(`Added installation for ${t} at scope ${n}`)),o.plugins[t]=a,saveInstalledPluginsV2(o)}export function removePluginInstallation(t,n,s){const i=loadInstalledPluginsFromDisk(),l=i.plugins[t];l&&(i.plugins[t]=l.filter(t=>!(t.scope===n&&t.projectPath===s)),0===i.plugins[t].length&&delete i.plugins[t],saveInstalledPluginsV2(i),e(`Removed installation for ${t} at scope ${n}`))}export function getInMemoryInstalledPlugins(){return null===F&&(F=loadInstalledPluginsV2()),F}export function loadInstalledPluginsFromDisk(){try{const t=readInstalledPluginsFileRaw();if(t){if(2===t.version)return p().parse(t.data);return migrateV1ToV2(g().parse(t.data))}return{version:2,plugins:{}}}catch(t){const n=s(t);return e(`Failed to load installed plugins from disk: ${n}`,{level:"error"}),{version:2,plugins:{}}}}export function updateInstallationPathOnDisk(t,n,s,i,l,o){const a=loadInstalledPluginsFromDisk(),r=a.plugins[t];if(!r)return void e(`Cannot update ${t} on disk: plugin not found in installed plugins`);const d=r.find(t=>t.scope===n&&t.projectPath===s);if(d){d.installPath=i,d.version=l,d.lastUpdated=(new Date).toISOString(),void 0!==o&&(d.gitCommitSha=o);const n=getInstalledPluginsFilePath();u(n,c(a,null,2),{encoding:"utf-8",flush:!0}),w=null,e(`Updated ${t} on disk to version ${l} at ${i}`)}else e(`Cannot update ${t} on disk: no installation for scope ${n}`)}export function hasPendingUpdates(){const t=getInMemoryInstalledPlugins(),n=loadInstalledPluginsFromDisk();for(const[e,s]of Object.entries(n.plugins)){const n=t.plugins[e];if(n)for(const t of s){const e=n.find(n=>n.scope===t.scope&&n.projectPath===t.projectPath);if(e&&e.installPath!==t.installPath)return!0}}return!1}export function getPendingUpdateCount(){let t=0;const n=getInMemoryInstalledPlugins(),e=loadInstalledPluginsFromDisk();for(const[s,i]of Object.entries(e.plugins)){const e=n.plugins[s];if(e)for(const n of i){const s=e.find(t=>t.scope===n.scope&&t.projectPath===n.projectPath);s&&s.installPath!==n.installPath&&t++}}return t}export function getPendingUpdatesDetails(){const t=[],n=getInMemoryInstalledPlugins(),e=loadInstalledPluginsFromDisk();for(const[s,i]of Object.entries(e.plugins)){const e=n.plugins[s];if(e)for(const n of i){const i=e.find(t=>t.scope===n.scope&&t.projectPath===n.projectPath);i&&i.installPath!==n.installPath&&t.push({pluginId:s,scope:n.scope,oldVersion:i.version||"unknown",newVersion:n.version||"unknown"})}}return t}export function resetInMemoryState(){F=null}export async function initializeVersionedPlugins(){migrateToSinglePluginFile();try{await migrateFromEnabledPlugins()}catch(t){a(t)}const t=getInMemoryInstalledPlugins();e(`Initialized versioned plugins system with ${Object.keys(t.plugins).length} plugins`)}export function removeAllPluginsForMarketplace(t){if(!t)return{orphanedPaths:[],removedPluginIds:[]};const n=loadInstalledPluginsFromDisk(),s=`@${t}`,i=new Set,l=[];for(const t of Object.keys(n.plugins))if(t.endsWith(s)){for(const e of n.plugins[t]??[])e.installPath&&i.add(e.installPath);delete n.plugins[t],l.push(t),e(`Removed installed plugin for marketplace removal: ${t}`)}return l.length>0&&saveInstalledPluginsV2(n),{orphanedPaths:Array.from(i),removedPluginIds:l}}export function isInstallationRelevantToCurrentProject(t){return"user"===t.scope||"managed"===t.scope||t.projectPath===f()}export function isPluginInstalled(t){const n=loadInstalledPluginsV2().plugins[t];return!(!n||0===n.length)&&(!!n.some(isInstallationRelevantToCurrentProject)&&void 0!==P().enabledPlugins?.[t])}export function isPluginGloballyInstalled(t){const n=loadInstalledPluginsV2().plugins[t];if(!n||0===n.length)return!1;return!!n.some(t=>"user"===t.scope||"managed"===t.scope)&&void 0!==P().enabledPlugins?.[t]}export function addInstalledPlugin(t,n,s="user",i){const l=loadInstalledPluginsFromDisk(),o={scope:s,installPath:n.installPath,version:n.version,installedAt:n.installedAt,lastUpdated:n.lastUpdated,gitCommitSha:n.gitCommitSha,...i&&{projectPath:i}},a=l.plugins[t]||[],r=a.findIndex(t=>t.scope===s&&t.projectPath===i),c=r>=0;c?a[r]=o:a.push(o),l.plugins[t]=a,saveInstalledPluginsV2(l),e(`${c?"Updated":"Added"} installed plugin: ${t} (scope: ${s})`)}export function removeInstalledPlugin(t){const n=loadInstalledPluginsFromDisk(),s=n.plugins[t];if(!s||0===s.length)return;const i=s[0],l=i?{version:i.version||"unknown",installedAt:i.installedAt||(new Date).toISOString(),lastUpdated:i.lastUpdated,installPath:i.installPath,gitCommitSha:i.gitCommitSha}:void 0;return delete n.plugins[t],saveInstalledPluginsV2(n),e(`Removed installed plugin: ${t}`),l}export{getGitCommitSha};export function deletePluginCache(n){const i=o();try{i.rmSync(n,{recursive:!0,force:!0}),e(`Deleted plugin cache at ${n}`);const s=S();if(n.includes("/cache/")&&n.startsWith(s)){const l=t(n);if(l!==s&&l.startsWith(s))try{0===i.readdirSync(l).length&&(i.rmdirSync(l),e(`Deleted empty plugin directory at ${l}`))}catch{}}}catch(t){const e=s(t);throw a(l(t)),new Error(`Failed to delete plugin cache at ${n}: ${e}`)}}async function getGitCommitSha(t){return await h(t)??void 0}function getPluginVersionFromManifest(t,s){const i=o(),l=n(t,".claude-plugin","plugin.json");try{const t=i.readFileSync(l,{encoding:"utf-8"});return r(t).version||"unknown"}catch{return e(`Could not read version from manifest for ${s}`),"unknown"}}export async function migrateFromEnabledPlugins(){const t=P().enabledPlugins||{};if(0===Object.keys(t).length)return;const s=readInstalledPluginsFileRaw(),l=null!==s;if(l&&2===s?.version&&s){const n=p().safeParse(s.data);if(n?.success){const s=n.data.plugins;if(Object.keys(t).filter(t=>t.includes("@")).every(t=>{const n=s[t];return n&&n.length>0}))return void e("All plugins already exist, skipping migration")}}e(l?"Syncing installed_plugins.json with enabledPlugins from all settings.json files":"Creating installed_plugins.json from settings.json files");const a=(new Date).toISOString(),r=m(),c=new Map,u=["userSettings","projectSettings","localSettings"];for(const t of u){const n=v(t),e=n?.enabledPlugins||{};for(const n of Object.keys(e)){if(!n.includes("@"))continue;const e=I(t);c.set(n,{scope:e,projectPath:"user"===e?void 0:r})}}let d={};if(l){d={...loadInstalledPluginsV2().plugins}}let g=0,f=0;for(const[t,s]of c){const l=d[t];if(l&&l.length>0){const n=l[0];!n||n.scope===s.scope&&n.projectPath===s.projectPath||(n.scope=s.scope,s.projectPath?n.projectPath=s.projectPath:delete n.projectPath,n.lastUpdated=a,g++,e(`Updated ${t} scope to ${s.scope} (settings.json is source of truth)`))}else{const{name:l,marketplace:r}=j(t);if(!l||!r)continue;try{e(`Looking up plugin ${t} in marketplace ${r}`);const c=await y(t);if(!c){e(`Plugin ${t} not found in any marketplace, skipping`);continue}const{entry:u,marketplaceInstallLocation:g}=c;let p,m,h="unknown";if("string"==typeof u.source)p=n(g,u.source),h=getPluginVersionFromManifest(p,t),m=await getGitCommitSha(p);else{const s=S(),a=l.replace(/[^a-zA-Z0-9-_]/g,"-"),r=n(s,a);let c;try{c=(await o().readdir(r)).map(t=>"string"==typeof t?t:t.name)}catch(n){if(!i(n))throw n;e(`External plugin ${t} not in cache, skipping`);continue}p=r,c.includes(".claude-plugin")&&(h=getPluginVersionFromManifest(r,t)),m=await getGitCommitSha(r)}"unknown"===h&&u.version&&(h=u.version),"unknown"===h&&m&&(h=m.substring(0,12)),d[t]=[{scope:s.scope,installPath:$(t,h),version:h,installedAt:a,lastUpdated:a,gitCommitSha:m,...s.projectPath&&{projectPath:s.projectPath}}],f++,e(`Added ${t} with scope ${s.scope}`)}catch(n){e(`Failed to add plugin ${t}: ${n}`)}}}if(!l||g>0||f>0){saveInstalledPluginsV2({version:2,plugins:d}),e(`Sync completed: ${f} added, ${g} updated in installed_plugins.json`)}}
|
|
1
|
+
import{dirname as t,join as n}from"path";import{logForDebugging as e}from"../debug.js";import{errorMessage as s,isENOENT as i,toError as l}from"../errors.js";import{getFsImplementation as o}from"../fsOperations.js";import{logError as a}from"../log.js";import{jsonParse as r,jsonStringify as c,writeFileSync_DEPRECATED as u}from"../slowOperations.js";import{getPluginsDirectory as d}from"./pluginDirectories.js";import{InstalledPluginsFileSchemaV1 as g,InstalledPluginsFileSchemaV2 as p}from"./schemas.js";import{getOriginalCwd as f}from"../../bootstrap/state.js";import{getCwd as m}from"../cwd.js";import{getHeadForDir as h}from"../git/gitFilesystem.js";import{getSettings_DEPRECATED as P,getSettingsForSource as y}from"../settings/settings.js";import{getPluginById as v}from"./marketplaceManager.js";import{parsePluginIdentifier as I,settingSourceToScope as j}from"./pluginIdentifier.js";import{getPluginCachePath as S,getVersionedCachePath as F}from"./pluginLoader.js";let $=!1,k=null,w=null;export function getInstalledPluginsFilePath(){return n(d(),"installed_plugins.json")}export function getInstalledPluginsV2FilePath(){return n(d(),"installed_plugins_v2.json")}export function clearInstalledPluginsCache(){k=null,w=null,e("Cleared installed plugins cache")}export function migrateToSinglePluginFile(){if($)return;const t=o(),n=getInstalledPluginsFilePath(),d=getInstalledPluginsV2FilePath();try{try{return t.renameSync(d,n),e("Renamed installed_plugins_v2.json to installed_plugins.json"),cleanupLegacyCache(loadInstalledPluginsV2()),void($=!0)}catch(t){if(!i(t))throw t}let s;try{s=t.readFileSync(n,{encoding:"utf-8"})}catch(t){if(!i(t))throw t;return void($=!0)}const l=r(s);if(1===("number"==typeof l?.version?l.version:1)){const t=g().parse(l),s=migrateV1ToV2(t);u(n,c(s,null,2),{encoding:"utf-8",flush:!0}),e(`Converted installed_plugins.json from V1 to V2 format (${Object.keys(t.plugins).length} plugins)`),cleanupLegacyCache(s)}$=!0}catch(t){const n=s(t);e(`Failed to migrate plugin files: ${n}`,{level:"error"}),a(l(t)),$=!0}}function cleanupLegacyCache(t){const i=o(),l=S();try{const s=new Set;for(const n of Object.values(t.plugins))for(const t of n)s.add(t.installPath);const o=i.readdirSync(l);for(const t of o){if(!t.isDirectory())continue;const o=t.name,a=n(l,o);i.readdirSync(a).some(t=>{if(!t.isDirectory())return!1;const e=n(a,t.name);return i.readdirSync(e).some(t=>t.isDirectory())})||s.has(a)||(i.rmSync(a,{recursive:!0,force:!0}),e(`Cleaned up legacy cache directory: ${o}`))}}catch(t){const n=s(t);e(`Failed to clean up legacy cache: ${n}`,{level:"warn"})}}export function resetMigrationState(){$=!1}function readInstalledPluginsFileRaw(){const t=o(),n=getInstalledPluginsFilePath();let e;try{e=t.readFileSync(n,{encoding:"utf-8"})}catch(t){if(i(t))return null;throw t}const s=r(e);return{version:"number"==typeof s?.version?s.version:1,data:s}}function migrateV1ToV2(t){const n={};for(const[e,s]of Object.entries(t.plugins)){const t=F(e,s.version);n[e]=[{scope:"user",installPath:t,version:s.version,installedAt:s.installedAt,lastUpdated:s.lastUpdated,gitCommitSha:s.gitCommitSha}]}return{version:2,plugins:n}}export function loadInstalledPluginsV2(){if(null!==k)return k;const t=getInstalledPluginsFilePath();try{const n=readInstalledPluginsFileRaw();if(n){if(2===n.version){const s=p().parse(n.data);return k=s,e(`Loaded ${Object.keys(s.plugins).length} installed plugins from ${t}`),s}const s=g().parse(n.data),i=migrateV1ToV2(s);return k=i,e(`Loaded and converted ${Object.keys(s.plugins).length} plugins from V1 format`),i}return e("installed_plugins.json doesn't exist, returning empty V2 object"),k={version:2,plugins:{}},k}catch(t){const n=s(t);return e(`Failed to load installed_plugins.json: ${n}. Starting with empty state.`,{level:"error"}),a(l(t)),k={version:2,plugins:{}},k}}function saveInstalledPluginsV2(t){const n=o(),i=getInstalledPluginsFilePath();try{n.mkdirSync(d());const s=c(t,null,2);u(i,s,{encoding:"utf-8",flush:!0}),k=t,e(`Saved ${Object.keys(t.plugins).length} installed plugins to ${i}`)}catch(t){throw s(t),a(l(t)),t}}export function addPluginInstallation(t,n,s,i,l){const o=loadInstalledPluginsFromDisk(),a=o.plugins[t]||[],r=a.findIndex(t=>t.scope===n&&t.projectPath===l),c={scope:n,installPath:s,version:i.version,installedAt:i.installedAt||(new Date).toISOString(),lastUpdated:(new Date).toISOString(),gitCommitSha:i.gitCommitSha,...l&&{projectPath:l}};r>=0?(a[r]=c,e(`Updated installation for ${t} at scope ${n}`)):(a.push(c),e(`Added installation for ${t} at scope ${n}`)),o.plugins[t]=a,saveInstalledPluginsV2(o)}export function removePluginInstallation(t,n,s){const i=loadInstalledPluginsFromDisk(),l=i.plugins[t];l&&(i.plugins[t]=l.filter(t=>!(t.scope===n&&t.projectPath===s)),0===i.plugins[t].length&&delete i.plugins[t],saveInstalledPluginsV2(i),e(`Removed installation for ${t} at scope ${n}`))}export function getInMemoryInstalledPlugins(){return null===w&&(w=loadInstalledPluginsV2()),w}export function loadInstalledPluginsFromDisk(){try{const t=readInstalledPluginsFileRaw();return t?2===t.version?p().parse(t.data):migrateV1ToV2(g().parse(t.data)):{version:2,plugins:{}}}catch(t){const n=s(t);return e(`Failed to load installed plugins from disk: ${n}`,{level:"error"}),{version:2,plugins:{}}}}export function updateInstallationPathOnDisk(t,n,s,i,l,o){const a=loadInstalledPluginsFromDisk(),r=a.plugins[t];if(!r)return void e(`Cannot update ${t} on disk: plugin not found in installed plugins`);const d=r.find(t=>t.scope===n&&t.projectPath===s);if(d){d.installPath=i,d.version=l,d.lastUpdated=(new Date).toISOString(),void 0!==o&&(d.gitCommitSha=o);const n=getInstalledPluginsFilePath();u(n,c(a,null,2),{encoding:"utf-8",flush:!0}),k=null,e(`Updated ${t} on disk to version ${l} at ${i}`)}else e(`Cannot update ${t} on disk: no installation for scope ${n}`)}export function hasPendingUpdates(){const t=getInMemoryInstalledPlugins(),n=loadInstalledPluginsFromDisk();for(const[e,s]of Object.entries(n.plugins)){const n=t.plugins[e];if(n)for(const t of s){const e=n.find(n=>n.scope===t.scope&&n.projectPath===t.projectPath);if(e&&e.installPath!==t.installPath)return!0}}return!1}export function getPendingUpdateCount(){let t=0;const n=getInMemoryInstalledPlugins(),e=loadInstalledPluginsFromDisk();for(const[s,i]of Object.entries(e.plugins)){const e=n.plugins[s];if(e)for(const n of i){const s=e.find(t=>t.scope===n.scope&&t.projectPath===n.projectPath);s&&s.installPath!==n.installPath&&t++}}return t}export function getPendingUpdatesDetails(){const t=[],n=getInMemoryInstalledPlugins(),e=loadInstalledPluginsFromDisk();for(const[s,i]of Object.entries(e.plugins)){const e=n.plugins[s];if(e)for(const n of i){const i=e.find(t=>t.scope===n.scope&&t.projectPath===n.projectPath);i&&i.installPath!==n.installPath&&t.push({pluginId:s,scope:n.scope,oldVersion:i.version||"unknown",newVersion:n.version||"unknown"})}}return t}export function resetInMemoryState(){w=null}export async function initializeVersionedPlugins(){migrateToSinglePluginFile();try{await migrateFromEnabledPlugins()}catch(t){a(t)}const t=getInMemoryInstalledPlugins();e(`Initialized versioned plugins system with ${Object.keys(t.plugins).length} plugins`)}export function removeAllPluginsForMarketplace(t){if(!t)return{orphanedPaths:[],removedPluginIds:[]};const n=loadInstalledPluginsFromDisk(),s=`@${t}`,i=new Set,l=[];for(const t of Object.keys(n.plugins))if(t.endsWith(s)){for(const e of n.plugins[t]??[])e.installPath&&i.add(e.installPath);delete n.plugins[t],l.push(t),e(`Removed installed plugin for marketplace removal: ${t}`)}return l.length>0&&saveInstalledPluginsV2(n),{orphanedPaths:Array.from(i),removedPluginIds:l}}export function isInstallationRelevantToCurrentProject(t){return"user"===t.scope||"managed"===t.scope||t.projectPath===f()}export function isPluginInstalled(t){const n=loadInstalledPluginsV2().plugins[t];return!(!n||0===n.length||!n.some(isInstallationRelevantToCurrentProject)||void 0===P().enabledPlugins?.[t])}export function isPluginGloballyInstalled(t){const n=loadInstalledPluginsV2().plugins[t];return!(!n||0===n.length)&&(!!n.some(t=>"user"===t.scope||"managed"===t.scope)&&void 0!==P().enabledPlugins?.[t])}export function addInstalledPlugin(t,n,s="user",i){const l=loadInstalledPluginsFromDisk(),o={scope:s,installPath:n.installPath,version:n.version,installedAt:n.installedAt,lastUpdated:n.lastUpdated,gitCommitSha:n.gitCommitSha,...i&&{projectPath:i}},a=l.plugins[t]||[],r=a.findIndex(t=>t.scope===s&&t.projectPath===i),c=r>=0;c?a[r]=o:a.push(o),l.plugins[t]=a,saveInstalledPluginsV2(l),e(`${c?"Updated":"Added"} installed plugin: ${t} (scope: ${s})`)}export function removeInstalledPlugin(t){const n=loadInstalledPluginsFromDisk(),s=n.plugins[t];if(!s||0===s.length)return;const i=s[0],l=i?{version:i.version||"unknown",installedAt:i.installedAt||(new Date).toISOString(),lastUpdated:i.lastUpdated,installPath:i.installPath,gitCommitSha:i.gitCommitSha}:void 0;return delete n.plugins[t],saveInstalledPluginsV2(n),e(`Removed installed plugin: ${t}`),l}export{getGitCommitSha};export function deletePluginCache(n){const i=o();try{i.rmSync(n,{recursive:!0,force:!0}),e(`Deleted plugin cache at ${n}`);const s=S();if(n.includes("/cache/")&&n.startsWith(s)){const l=t(n);if(l!==s&&l.startsWith(s))try{0===i.readdirSync(l).length&&(i.rmdirSync(l),e(`Deleted empty plugin directory at ${l}`))}catch{}}}catch(t){const e=s(t);throw a(l(t)),new Error(`Failed to delete plugin cache at ${n}: ${e}`)}}async function getGitCommitSha(t){return await h(t)??void 0}function getPluginVersionFromManifest(t,s){const i=o(),l=n(t,".claude-plugin","plugin.json");try{const t=i.readFileSync(l,{encoding:"utf-8"});return r(t).version||"unknown"}catch{return e(`Could not read version from manifest for ${s}`),"unknown"}}export async function migrateFromEnabledPlugins(){const t=P().enabledPlugins||{};if(0===Object.keys(t).length)return;const s=readInstalledPluginsFileRaw(),l=null!==s;if(l&&2===s?.version&&s){const n=p().safeParse(s.data);if(n?.success){const s=n.data.plugins;if(Object.keys(t).filter(t=>t.includes("@")).every(t=>{const n=s[t];return n&&n.length>0}))return void e("All plugins already exist, skipping migration")}}e(l?"Syncing installed_plugins.json with enabledPlugins from all settings.json files":"Creating installed_plugins.json from settings.json files");const a=(new Date).toISOString(),r=m(),c=new Map,u=["userSettings","projectSettings","localSettings"];for(const t of u){const n=y(t),e=n?.enabledPlugins||{};for(const n of Object.keys(e)){if(!n.includes("@"))continue;const e=j(t);c.set(n,{scope:e,projectPath:"user"===e?void 0:r})}}let d={};l&&(d={...loadInstalledPluginsV2().plugins});let g=0,f=0;for(const[t,s]of c){const l=d[t];if(l&&l.length>0){const n=l[0];!n||n.scope===s.scope&&n.projectPath===s.projectPath||(n.scope=s.scope,s.projectPath?n.projectPath=s.projectPath:delete n.projectPath,n.lastUpdated=a,g++,e(`Updated ${t} scope to ${s.scope} (settings.json is source of truth)`))}else{const{name:l,marketplace:r}=I(t);if(!l||!r)continue;try{e(`Looking up plugin ${t} in marketplace ${r}`);const c=await v(t);if(!c){e(`Plugin ${t} not found in any marketplace, skipping`);continue}const{entry:u,marketplaceInstallLocation:g}=c;let p,m,h="unknown";if("string"==typeof u.source)p=n(g,u.source),h=getPluginVersionFromManifest(p,t),m=await getGitCommitSha(p);else{const s=S(),a=l.replace(/[^a-zA-Z0-9-_]/g,"-"),r=n(s,a);let c;try{c=(await o().readdir(r)).map(t=>"string"==typeof t?t:t.name)}catch(n){if(!i(n))throw n;e(`External plugin ${t} not in cache, skipping`);continue}p=r,c.includes(".claude-plugin")&&(h=getPluginVersionFromManifest(r,t)),m=await getGitCommitSha(r)}"unknown"===h&&u.version&&(h=u.version),"unknown"===h&&m&&(h=m.substring(0,12)),d[t]=[{scope:s.scope,installPath:F(t,h),version:h,installedAt:a,lastUpdated:a,gitCommitSha:m,...s.projectPath&&{projectPath:s.projectPath}}],f++,e(`Added ${t} with scope ${s.scope}`)}catch(n){e(`Failed to add plugin ${t}: ${n}`)}}}(!l||g>0||f>0)&&(saveInstalledPluginsV2({version:2,plugins:d}),e(`Sync completed: ${f} added, ${g} updated in installed_plugins.json`))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"lodash-es/memoize.js";import{basename as t}from"path";import{isAutoMemoryEnabled as e}from"../../memdir/paths.js";import{loadAgentMemoryPrompt as
|
|
1
|
+
import o from"lodash-es/memoize.js";import{basename as t}from"path";import{isAutoMemoryEnabled as e}from"../../memdir/paths.js";import{loadAgentMemoryPrompt as r}from"../../tools/AgentTool/agentMemory.js";import{FILE_EDIT_TOOL_NAME as n}from"../../tools/FileEditTool/constants.js";import{FILE_READ_TOOL_NAME as a}from"../../tools/FileReadTool/prompt.js";import{FILE_WRITE_TOOL_NAME as s}from"../../tools/FileWriteTool/prompt.js";import{getPluginErrorMessage as i}from"../../types/plugin.js";import{logForDebugging as l}from"../debug.js";import{EFFORT_LEVELS as m,parseEffortValue as g}from"../effort.js";import{coerceDescriptionToString as d,parseFrontmatter as c,parsePositiveIntFromFrontmatter as u}from"../frontmatterParser.js";import{getFsImplementation as f,isDuplicatePath as p}from"../fsOperations.js";import{parseAgentToolsFromFrontmatter as h,parseSlashCommandToolsFromFrontmatter as F}from"../markdownConfigLoader.js";import{loadAllPluginsCacheOnly as v}from"./pluginLoader.js";import{loadPluginOptions as $,substitutePluginVariables as w,substituteUserConfigInContent as y}from"./pluginOptionsStorage.js";import{walkPluginMarkdown as P}from"./walkPluginMarkdown.js";const T=["user","project","local"];async function loadAgentsFromDirectory(o,t,e,r,n,a){const s=[];return await P(o,async(o,i)=>{const l=await loadAgentFromFile(o,t,i,e,r,n,a);l&&s.push(l)},{logLabel:"agents"}),s}async function loadAgentFromFile(o,i,v,P,j,A,E){const L=f();if(p(L,o,E))return null;try{const f=await L.readFile(o,{encoding:"utf-8"}),{frontmatter:p,content:E}=c(f,o),b=p.name||t(o).replace(/\.md$/,""),k=[i,...v,b].join(":"),M=d(p.description,k)??d(p["when-to-use"],k)??`Agent from ${i} plugin`;let O=h(p.tools);const _=F(p.skills),C=p.color,D=p.model;let I;if("string"==typeof D&&D.trim().length>0){const o=D.trim();I="inherit"===o.toLowerCase()?"inherit":o}const S=p.background,x="true"===S||!0===S||void 0;let V=w(E.trim(),{path:j,source:P});A.userConfig&&(V=y(V,$(P),A.userConfig));const R=p.memory;let N;void 0!==R&&(T.includes(R)?N=R:l(`Plugin agent file ${o} has invalid memory value '${R}'. Valid options: ${T.join(", ")}`));const U="worktree"===p.isolation?"worktree":void 0,W=p.effort,z=void 0!==W?g(W):void 0;void 0!==W&&void 0===z&&l(`Plugin agent file ${o} has invalid effort '${W}'. Valid options: ${m.join(", ")} or an integer`);for(const t of["permissionMode","hooks","mcpServers"])void 0!==p[t]&&l(`Plugin agent file ${o} sets ${t}, which is ignored for plugin agents. Use .claude/agents/ for this level of control.`,{level:"warn"});const q=p.maxTurns,B=u(q);void 0!==q&&void 0===B&&l(`Plugin agent file ${o} has invalid maxTurns '${q}'. Must be a positive integer.`);const G=void 0!==p.disallowedTools?h(p.disallowedTools):void 0;if(e()&&N&&void 0!==O){const o=new Set(O);for(const t of[s,n,a])o.has(t)||(O=[...O,t])}return{agentType:k,whenToUse:M,tools:O,...void 0!==G?{disallowedTools:G}:{},...void 0!==_?{skills:_}:{},getSystemPrompt:()=>{if(e()&&N){const o=r(k,N);return V+"\n\n"+o}return V},source:"plugin",color:C,model:I,filename:b,plugin:P,...x?{background:x}:{},...N?{memory:N}:{},...U?{isolation:U}:{},...void 0!==z?{effort:z}:{},...void 0!==B?{maxTurns:B}:{}}}catch(t){return l(`Failed to load agent from ${o}: ${t}`,{level:"error"}),null}}export const loadPluginAgents=o(async()=>{const{enabled:o,errors:t}=await v();t.length>0&&l(`Plugin loading errors: ${t.map(o=>i(o)).join(", ")}`);const e=(await Promise.all(o.map(async o=>{const t=new Set,e=[];if(o.agentsPath)try{const r=await loadAgentsFromDirectory(o.agentsPath,o.name,o.source,o.path,o.manifest,t);e.push(...r),r.length>0&&l(`Loaded ${r.length} agents from plugin ${o.name} default directory`)}catch(t){l(`Failed to load agents from plugin ${o.name} default directory: ${t}`,{level:"error"})}if(o.agentsPaths){const r=await Promise.all(o.agentsPaths.map(async e=>{try{const r=f(),n=await r.stat(e);if(n.isDirectory()){const r=await loadAgentsFromDirectory(e,o.name,o.source,o.path,o.manifest,t);return r.length>0&&l(`Loaded ${r.length} agents from plugin ${o.name} custom path: ${e}`),r}if(n.isFile()&&e.endsWith(".md")){const r=await loadAgentFromFile(e,o.name,[],o.source,o.path,o.manifest,t);if(r)return l(`Loaded agent from plugin ${o.name} custom file: ${e}`),[r]}return[]}catch(t){return l(`Failed to load agents from plugin ${o.name} custom path ${e}: ${t}`,{level:"error"}),[]}}));for(const o of r)e.push(...o)}return e}))).flat();return l(`Total plugin agents loaded: ${e.length}`),e});export function clearPluginAgentCache(){loadPluginAgents.cache?.clear?.()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"lodash-es/memoize.js";import{basename as e,dirname as o,join as n}from"path";import{getInlinePlugins as i,getSessionId as a}from"../../bootstrap/state.js";import{getPluginErrorMessage as l}from"../../types/plugin.js";import{parseArgumentNames as r,substituteArguments as s}from"../argumentSubstitution.js";import{logForDebugging as m}from"../debug.js";import{EFFORT_LEVELS as c,parseEffortValue as d}from"../effort.js";import{isBareMode as u}from"../envUtils.js";import{isENOENT as f}from"../errors.js";import{coerceDescriptionToString as g,parseBooleanFrontmatter as p,parseFrontmatter as h,parseShellFrontmatter as $}from"../frontmatterParser.js";import{getFsImplementation as P,isDuplicatePath as k}from"../fsOperations.js";import{extractDescriptionFromMarkdown as y,parseSlashCommandToolsFromFrontmatter as w}from"../markdownConfigLoader.js";import{parseUserSpecifiedModel as S}from"../model/model.js";import{executeShellCommandsInPrompt as F}from"../promptShellExecution.js";import{loadAllPluginsCacheOnly as v}from"./pluginLoader.js";import{loadPluginOptions as C,substitutePluginVariables as j,substituteUserConfigInContent as b}from"./pluginOptionsStorage.js";import{walkPluginMarkdown as D}from"./walkPluginMarkdown.js";function isSkillFile(t){return/^skill\.md$/i.test(e(t))}function getCommandNameFromFile(t,n,i){if(isSkillFile(t)){const a=o(t),l=o(a),r=e(a),s=l.startsWith(n)?l.slice(n.length).replace(/^\//,""):"",m=s?s.split("/").join(":"):"";return m?`${i}:${m}:${r}`:`${i}:${r}`}{const a=o(t),l=e(t).replace(/\.md$/,""),r=a.startsWith(n)?a.slice(n.length).replace(/^\//,""):"",s=r?r.split("/").join(":"):"";return s?`${i}:${s}:${l}`:`${i}:${l}`}}async function loadCommandsFromDirectory(t,n,i,a,l,r={isSkillMode:!1},s=new Set){const c=await async function(t,e,o){const n=[],i=P();return await D(t,async t=>{if(k(i,t,o))return;const a=await i.readFile(t,{encoding:"utf-8"}),{frontmatter:l,content:r}=h(a,t);n.push({filePath:t,baseDir:e,frontmatter:l,content:r})},{stopAtSkillDir:!0,logLabel:"commands"}),n}(t,t,s),d=function(t){const n=new Map;for(const e of t){const t=o(e.filePath),i=n.get(t)??[];i.push(e),n.set(t,i)}const i=[];for(const[t,o]of n){const n=o.filter(t=>isSkillFile(t.filePath));if(n.length>0){const o=n[0];n.length>1&&m(`Multiple skill files found in ${t}, using ${e(o.filePath)}`),i.push(o)}else i.push(...o)}return i}(c),u=[];for(const t of d){const e=createPluginCommand(getCommandNameFromFile(t.filePath,t.baseDir,n),t,i,a,l,isSkillFile(t.filePath),r);e&&u.push(e)}return u}function createPluginCommand(t,e,n,i,l,u,f={isSkillMode:!1}){try{const{frontmatter:h,content:P}=e,k=g(h.description,t),v=k??y(P,u?"Plugin skill":"Plugin command"),D=h["allowed-tools"],L="string"==typeof D?j(D,{path:l,source:n}):Array.isArray(D)?D.map(t=>"string"==typeof t?j(t,{path:l,source:n}):t):D,M=w(L),x=h["argument-hint"],A=r(h.arguments),I=h.when_to_use,T=h.version,H=h.name,N="inherit"===h.model?void 0:h.model?S(h.model):void 0,_=h.effort,E=void 0!==_?d(_):void 0;void 0!==_&&void 0===E&&m(`Plugin command ${t} has invalid effort '${_}'. Valid options: ${c.join(", ")} or an integer`);const O=p(h["disable-model-invocation"]),U=h["user-invocable"],K=void 0===U||p(U),W=$(h.shell,t);return{type:"prompt",name:t,description:v,hasUserSpecifiedDescription:null!==k,allowedTools:M,argumentHint:x,argNames:A.length>0?A:void 0,whenToUse:I,version:T,model:N,effort:E,disableModelInvocation:O,userInvocable:K,contentLength:P.length,source:"plugin",loadedFrom:u||f.isSkillMode?"plugin":void 0,pluginInfo:{pluginManifest:i,repository:n},isHidden:!K,progressMessage:u||f.isSkillMode?"loading":"running",userFacingName:()=>H||t,async getPromptForCommand(r,m){let c=f.isSkillMode?`Base directory for this skill: ${o(e.filePath)}\n\n${P}`:P;if(c=s(c,r,!0,A),c=j(c,{path:l,source:n}),i.userConfig&&(c=b(c,C(n),i.userConfig)),f.isSkillMode){const t=o(e.filePath),n="win32"===process.platform?t.replace(/\\/g,"/"):t;c=c.replace(/\$\{CLAUDE_SKILL_DIR\}/g,n)}return c=c.replace(/\$\{CLAUDE_SESSION_ID\}/g,a()),c=await F(c,{...m,getAppState(){const t=m.getAppState();return{...t,toolPermissionContext:{...t.toolPermissionContext,alwaysAllowRules:{...t.toolPermissionContext.alwaysAllowRules,command:M}}}}},`/${t}`,W),[{type:"text",text:c}]}}}catch(t){return m(`Failed to create command from ${e.filePath}: ${t}`,{level:"error"}),null}}export const getPluginCommands=t(async()=>{if(m("getPluginCommands: start"),u()&&0===i().length)return[];const{enabled:t,errors:a}=await v();m(`getPluginCommands: enabled plugins=${t.length}`),a.length>0&&m(`Plugin loading errors: ${a.map(t=>l(t)).join(", ")}`);const r=(await Promise.all(t.map(async t=>{m(`getPluginCommands: loading plugin ${t.name}`);const i=new Set,a=[];if(t.commandsPath)try{const e=await loadCommandsFromDirectory(t.commandsPath,t.name,t.source,t.manifest,t.path,{isSkillMode:!1},i);a.push(...e),e.length>0&&m(`Loaded ${e.length} commands from plugin ${t.name} default directory`)}catch(e){m(`Failed to load commands from plugin ${t.name} default directory: ${e}`,{level:"error"})}if(t.commandsPaths){m(`Plugin ${t.name} has commandsPaths: ${t.commandsPaths.join(", ")}`);const l=await Promise.all(t.commandsPaths.map(async a=>{try{const l=P(),r=await l.stat(a);if(m(`Checking commandPath ${a} - isDirectory: ${r.isDirectory()}, isFile: ${r.isFile()}`),r.isDirectory()){const e=await loadCommandsFromDirectory(a,t.name,t.source,t.manifest,t.path,{isSkillMode:!1},i);return e.length>0?m(`Loaded ${e.length} commands from plugin ${t.name} custom path: ${a}`):m(`Warning: No commands found in plugin ${t.name} custom directory: ${a}. Expected .md files or SKILL.md in subdirectories.`,{level:"warn"}),e}if(r.isFile()&&a.endsWith(".md")){if(k(l,a,i))return[];const r=await l.readFile(a,{encoding:"utf-8"}),{frontmatter:s,content:c}=h(r,a);let d,u;if(t.commandsMetadata)for(const[e,o]of Object.entries(t.commandsMetadata))if(o.source){if(a===n(t.path,o.source)){d=`${t.name}:${e}`,u=o;break}}d||(d=`${t.name}:${e(a).replace(/\.md$/,"")}`);const f=u?{...s,...u.description&&{description:u.description},...u.argumentHint&&{"argument-hint":u.argumentHint},...u.model&&{model:u.model},...u.allowedTools&&{"allowed-tools":u.allowedTools.join(",")}}:s,g=createPluginCommand(d,{filePath:a,baseDir:o(a),frontmatter:f,content:c},t.source,t.manifest,t.path,!1);if(g)return m(`Loaded command from plugin ${t.name} custom file: ${a}${u?" (with metadata override)":""}`),[g]}return[]}catch(e){return m(`Failed to load commands from plugin ${t.name} custom path ${a}: ${e}`,{level:"error"}),[]}}));for(const t of l)a.push(...t)}if(t.commandsMetadata)for(const[e,o]of Object.entries(t.commandsMetadata))if(o.content&&!o.source)try{const{frontmatter:n,content:i}=h(o.content,`<inline:${t.name}:${e}>`),l={...n,...o.description&&{description:o.description},...o.argumentHint&&{"argument-hint":o.argumentHint},...o.model&&{model:o.model},...o.allowedTools&&{"allowed-tools":o.allowedTools.join(",")}},r=`${t.name}:${e}`,s=createPluginCommand(r,{filePath:`<inline:${r}>`,baseDir:t.path,frontmatter:l,content:i},t.source,t.manifest,t.path,!1);s&&(a.push(s),m(`Loaded inline content command from plugin ${t.name}: ${r}`))}catch(o){m(`Failed to load inline content command ${e} from plugin ${t.name}: ${o}`,{level:"error"})}return m(`getPluginCommands: plugin ${t.name} loaded commands=${a.length}`),a}))).flat();return m(`Total plugin commands loaded: ${r.length}`),r});export function clearPluginCommandCache(){getPluginCommands.cache?.clear?.()}async function loadSkillsFromDirectory(t,i,a,l,r,s){const c=P(),d=[],u=n(t,"SKILL.md");let g,p=null;try{p=await c.readFile(u,{encoding:"utf-8"})}catch(t){if(!f(t))return m(`Failed to load skill from ${u}: ${t}`,{level:"error"}),d}if(null!==p){if(k(c,u,s))return d;try{const{frontmatter:n,content:s}=h(p,u),m=`${i}:${e(t)}`,c=createPluginCommand(m,{filePath:u,baseDir:o(u),frontmatter:n,content:s},a,l,r,!0,{isSkillMode:!0});c&&d.push(c)}catch(t){m(`Failed to load skill from ${u}: ${t}`,{level:"error"})}return d}try{g=await c.readdir(t)}catch(e){return f(e)||m(`Failed to load skills from directory ${t}: ${e}`,{level:"error"}),d}return await Promise.all(g.map(async e=>{if(!e.isDirectory()&&!e.isSymbolicLink())return;const u=n(t,e.name),g=n(u,"SKILL.md");let p;try{p=await c.readFile(g,{encoding:"utf-8"})}catch(t){return void(f(t)||m(`Failed to load skill from ${g}: ${t}`,{level:"error"}))}if(!k(c,g,s))try{const{frontmatter:t,content:n}=h(p,g),s=`${i}:${e.name}`,m=createPluginCommand(s,{filePath:g,baseDir:o(g),frontmatter:t,content:n},a,l,r,!0,{isSkillMode:!0});m&&d.push(m)}catch(t){m(`Failed to load skill from ${g}: ${t}`,{level:"error"})}})),d}export const getPluginSkills=t(async()=>{if(u()&&0===i().length)return[];const{enabled:t,errors:e}=await v();e.length>0&&m(`Plugin loading errors: ${e.map(t=>l(t)).join(", ")}`),m(`getPluginSkills: Processing ${t.length} enabled plugins`);const o=(await Promise.all(t.map(async t=>{const e=new Set,o=[];if(m(`Checking plugin ${t.name}: skillsPath=${t.skillsPath?"exists":"none"}, skillsPaths=${t.skillsPaths?t.skillsPaths.length:0} paths`),t.skillsPath){m(`Attempting to load skills from plugin ${t.name} default skillsPath: ${t.skillsPath}`);try{const n=await loadSkillsFromDirectory(t.skillsPath,t.name,t.source,t.manifest,t.path,e);o.push(...n),m(`Loaded ${n.length} skills from plugin ${t.name} default directory`)}catch(e){m(`Failed to load skills from plugin ${t.name} default directory: ${e}`,{level:"error"})}}if(t.skillsPaths){m(`Attempting to load skills from plugin ${t.name} skillsPaths: ${t.skillsPaths.join(", ")}`);const n=await Promise.all(t.skillsPaths.map(async o=>{try{m(`Loading from skillPath: ${o} for plugin ${t.name}`);const n=await loadSkillsFromDirectory(o,t.name,t.source,t.manifest,t.path,e);return m(`Loaded ${n.length} skills from plugin ${t.name} custom path: ${o}`),n}catch(e){return m(`Failed to load skills from plugin ${t.name} custom path ${o}: ${e}`,{level:"error"}),[]}}));for(const t of n)o.push(...t)}return o}))).flat();return m(`Total plugin skills loaded: ${o.length}`),o});export function clearPluginSkillsCache(){getPluginSkills.cache?.clear?.()}
|
|
1
|
+
import e from"lodash-es/memoize.js";import{basename as t,dirname as o,join as n}from"path";import{getInlinePlugins as a,getSessionId as i}from"../../bootstrap/state.js";import{getPluginErrorMessage as r}from"../../types/plugin.js";import{parseArgumentNames as l,substituteArguments as s}from"../argumentSubstitution.js";import{logForDebugging as m}from"../debug.js";import{EFFORT_LEVELS as c,parseEffortValue as d}from"../effort.js";import{isBareMode as u}from"../envUtils.js";import{isENOENT as f}from"../errors.js";import{coerceDescriptionToString as g,parseBooleanFrontmatter as p,parseFrontmatter as h,parseShellFrontmatter as $}from"../frontmatterParser.js";import{getFsImplementation as P,isDuplicatePath as k}from"../fsOperations.js";import{extractDescriptionFromMarkdown as y,parseSlashCommandToolsFromFrontmatter as w}from"../markdownConfigLoader.js";import{parseUserSpecifiedModel as S}from"../model/model.js";import{executeShellCommandsInPrompt as F}from"../promptShellExecution.js";import{loadAllPluginsCacheOnly as C}from"./pluginLoader.js";import{loadPluginOptions as v,substitutePluginVariables as b,substituteUserConfigInContent as j}from"./pluginOptionsStorage.js";import{walkPluginMarkdown as D}from"./walkPluginMarkdown.js";function isSkillFile(e){return/^skill\.md$/i.test(t(e))}function getCommandNameFromFile(e,n,a){if(isSkillFile(e)){const i=o(e),r=o(i),l=t(i),s=r.startsWith(n)?r.slice(n.length).replace(/^\//,""):"",m=s?s.split("/").join(":"):"";return m?`${a}:${m}:${l}`:`${a}:${l}`}{const i=o(e),r=t(e).replace(/\.md$/,""),l=i.startsWith(n)?i.slice(n.length).replace(/^\//,""):"",s=l?l.split("/").join(":"):"";return s?`${a}:${s}:${r}`:`${a}:${r}`}}async function loadCommandsFromDirectory(e,n,a,i,r,l={isSkillMode:!1},s=new Set){const c=await async function(e,t,o){const n=[],a=P();return await D(e,async e=>{if(k(a,e,o))return;const i=await a.readFile(e,{encoding:"utf-8"}),{frontmatter:r,content:l}=h(i,e);n.push({filePath:e,baseDir:t,frontmatter:r,content:l})},{stopAtSkillDir:!0,logLabel:"commands"}),n}(e,e,s),d=function(e){const n=new Map;for(const t of e){const e=o(t.filePath),a=n.get(e)??[];a.push(t),n.set(e,a)}const a=[];for(const[e,o]of n){const n=o.filter(e=>isSkillFile(e.filePath));if(n.length>0){const o=n[0];n.length>1&&m(`Multiple skill files found in ${e}, using ${t(o.filePath)}`),a.push(o)}else a.push(...o)}return a}(c),u=[];for(const e of d){const t=createPluginCommand(getCommandNameFromFile(e.filePath,e.baseDir,n),e,a,i,r,isSkillFile(e.filePath),l);t&&u.push(t)}return u}function createPluginCommand(e,t,n,a,r,u,f={isSkillMode:!1}){try{const{frontmatter:h,content:P}=t,k=g(h.description,e),C=k??y(P,u?"Plugin skill":"Plugin command"),D=h["allowed-tools"],M="string"==typeof D?b(D,{path:r,source:n}):Array.isArray(D)?D.map(e=>"string"==typeof e?b(e,{path:r,source:n}):e):D,L=w(M),I=h["argument-hint"],x=l(h.arguments),A=h.when_to_use,E=h.version,T=h.name,N="inherit"===h.model?void 0:h.model?S(h.model):void 0,O=h.effort,U=void 0!==O?d(O):void 0;void 0!==O&&void 0===U&&m(`Plugin command ${e} has invalid effort '${O}'. Valid options: ${c.join(", ")} or an integer`);const _=p(h["disable-model-invocation"]),H=h["user-invocable"],K=void 0===H||p(H),R=$(h.shell,e);return{type:"prompt",name:e,description:C,hasUserSpecifiedDescription:null!==k,allowedTools:L,argumentHint:I,argNames:x.length>0?x:void 0,whenToUse:A,version:E,model:N,effort:U,disableModelInvocation:_,userInvocable:K,contentLength:P.length,source:"plugin",loadedFrom:u||f.isSkillMode?"plugin":void 0,pluginInfo:{pluginManifest:a,repository:n},isHidden:!K,progressMessage:u||f.isSkillMode?"loading":"running",userFacingName:()=>T||e,async getPromptForCommand(l,m){let c=f.isSkillMode?`Base directory for this skill: ${o(t.filePath)}\n\n${P}`:P;if(c=s(c,l,!0,x),c=b(c,{path:r,source:n}),a.userConfig&&(c=j(c,v(n),a.userConfig)),f.isSkillMode){const e=o(t.filePath),n="win32"===process.platform?e.replace(/\\/g,"/"):e;c=c.replace(/\$\{CLAUDE_SKILL_DIR\}/g,n)}return c=c.replace(/\$\{CLAUDE_SESSION_ID\}/g,i()),c=await F(c,{...m,getAppState(){const e=m.getAppState();return{...e,toolPermissionContext:{...e.toolPermissionContext,alwaysAllowRules:{...e.toolPermissionContext.alwaysAllowRules,command:L}}}}},`/${e}`,R),[{type:"text",text:c}]}}}catch(e){return m(`Failed to create command from ${t.filePath}: ${e}`,{level:"error"}),null}}export const getPluginCommands=e(async()=>{if(m("getPluginCommands: start"),u()&&0===a().length)return[];const{enabled:e,errors:i}=await C();m(`getPluginCommands: enabled plugins=${e.length}`),i.length>0&&m(`Plugin loading errors: ${i.map(e=>r(e)).join(", ")}`);const l=(await Promise.all(e.map(async e=>{m(`getPluginCommands: loading plugin ${e.name}`);const a=new Set,i=[];if(e.commandsPath)try{const t=await loadCommandsFromDirectory(e.commandsPath,e.name,e.source,e.manifest,e.path,{isSkillMode:!1},a);i.push(...t),t.length>0&&m(`Loaded ${t.length} commands from plugin ${e.name} default directory`)}catch(t){m(`Failed to load commands from plugin ${e.name} default directory: ${t}`,{level:"error"})}if(e.commandsPaths){m(`Plugin ${e.name} has commandsPaths: ${e.commandsPaths.join(", ")}`);const r=await Promise.all(e.commandsPaths.map(async i=>{try{const r=P(),l=await r.stat(i);if(m(`Checking commandPath ${i} - isDirectory: ${l.isDirectory()}, isFile: ${l.isFile()}`),l.isDirectory()){const t=await loadCommandsFromDirectory(i,e.name,e.source,e.manifest,e.path,{isSkillMode:!1},a);return t.length>0?m(`Loaded ${t.length} commands from plugin ${e.name} custom path: ${i}`):m(`Warning: No commands found in plugin ${e.name} custom directory: ${i}. Expected .md files or SKILL.md in subdirectories.`,{level:"warn"}),t}if(l.isFile()&&i.endsWith(".md")){if(k(r,i,a))return[];const l=await r.readFile(i,{encoding:"utf-8"}),{frontmatter:s,content:c}=h(l,i);let d,u;if(e.commandsMetadata)for(const[t,o]of Object.entries(e.commandsMetadata))if(o.source&&i===n(e.path,o.source)){d=`${e.name}:${t}`,u=o;break}d||(d=`${e.name}:${t(i).replace(/\.md$/,"")}`);const f=u?{...s,...u.description&&{description:u.description},...u.argumentHint&&{"argument-hint":u.argumentHint},...u.model&&{model:u.model},...u.allowedTools&&{"allowed-tools":u.allowedTools.join(",")}}:s,g=createPluginCommand(d,{filePath:i,baseDir:o(i),frontmatter:f,content:c},e.source,e.manifest,e.path,!1);if(g)return m(`Loaded command from plugin ${e.name} custom file: ${i}${u?" (with metadata override)":""}`),[g]}return[]}catch(t){return m(`Failed to load commands from plugin ${e.name} custom path ${i}: ${t}`,{level:"error"}),[]}}));for(const e of r)i.push(...e)}if(e.commandsMetadata)for(const[t,o]of Object.entries(e.commandsMetadata))if(o.content&&!o.source)try{const{frontmatter:n,content:a}=h(o.content,`<inline:${e.name}:${t}>`),r={...n,...o.description&&{description:o.description},...o.argumentHint&&{"argument-hint":o.argumentHint},...o.model&&{model:o.model},...o.allowedTools&&{"allowed-tools":o.allowedTools.join(",")}},l=`${e.name}:${t}`,s=createPluginCommand(l,{filePath:`<inline:${l}>`,baseDir:e.path,frontmatter:r,content:a},e.source,e.manifest,e.path,!1);s&&(i.push(s),m(`Loaded inline content command from plugin ${e.name}: ${l}`))}catch(o){m(`Failed to load inline content command ${t} from plugin ${e.name}: ${o}`,{level:"error"})}return m(`getPluginCommands: plugin ${e.name} loaded commands=${i.length}`),i}))).flat();return m(`Total plugin commands loaded: ${l.length}`),l});export function clearPluginCommandCache(){getPluginCommands.cache?.clear?.()}async function loadSkillsFromDirectory(e,a,i,r,l,s){const c=P(),d=[],u=n(e,"SKILL.md");let g,p=null;try{p=await c.readFile(u,{encoding:"utf-8"})}catch(e){if(!f(e))return m(`Failed to load skill from ${u}: ${e}`,{level:"error"}),d}if(null!==p){if(k(c,u,s))return d;try{const{frontmatter:n,content:s}=h(p,u),m=createPluginCommand(`${a}:${t(e)}`,{filePath:u,baseDir:o(u),frontmatter:n,content:s},i,r,l,!0,{isSkillMode:!0});m&&d.push(m)}catch(e){m(`Failed to load skill from ${u}: ${e}`,{level:"error"})}return d}try{g=await c.readdir(e)}catch(t){return f(t)||m(`Failed to load skills from directory ${e}: ${t}`,{level:"error"}),d}return await Promise.all(g.map(async t=>{if(!t.isDirectory()&&!t.isSymbolicLink())return;const u=n(e,t.name),g=n(u,"SKILL.md");let p;try{p=await c.readFile(g,{encoding:"utf-8"})}catch(e){return void(f(e)||m(`Failed to load skill from ${g}: ${e}`,{level:"error"}))}if(!k(c,g,s))try{const{frontmatter:e,content:n}=h(p,g),s=createPluginCommand(`${a}:${t.name}`,{filePath:g,baseDir:o(g),frontmatter:e,content:n},i,r,l,!0,{isSkillMode:!0});s&&d.push(s)}catch(e){m(`Failed to load skill from ${g}: ${e}`,{level:"error"})}})),d}export const getPluginSkills=e(async()=>{if(u()&&0===a().length)return[];const{enabled:e,errors:t}=await C();t.length>0&&m(`Plugin loading errors: ${t.map(e=>r(e)).join(", ")}`),m(`getPluginSkills: Processing ${e.length} enabled plugins`);const o=(await Promise.all(e.map(async e=>{const t=new Set,o=[];if(m(`Checking plugin ${e.name}: skillsPath=${e.skillsPath?"exists":"none"}, skillsPaths=${e.skillsPaths?e.skillsPaths.length:0} paths`),e.skillsPath){m(`Attempting to load skills from plugin ${e.name} default skillsPath: ${e.skillsPath}`);try{const n=await loadSkillsFromDirectory(e.skillsPath,e.name,e.source,e.manifest,e.path,t);o.push(...n),m(`Loaded ${n.length} skills from plugin ${e.name} default directory`)}catch(t){m(`Failed to load skills from plugin ${e.name} default directory: ${t}`,{level:"error"})}}if(e.skillsPaths){m(`Attempting to load skills from plugin ${e.name} skillsPaths: ${e.skillsPaths.join(", ")}`);const n=await Promise.all(e.skillsPaths.map(async o=>{try{m(`Loading from skillPath: ${o} for plugin ${e.name}`);const n=await loadSkillsFromDirectory(o,e.name,e.source,e.manifest,e.path,t);return m(`Loaded ${n.length} skills from plugin ${e.name} custom path: ${o}`),n}catch(t){return m(`Failed to load skills from plugin ${e.name} custom path ${o}: ${t}`,{level:"error"}),[]}}));for(const e of n)o.push(...e)}return o}))).flat();return m(`Total plugin skills loaded: ${o.length}`),o});export function clearPluginSkillsCache(){getPluginSkills.cache?.clear?.()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"lodash-es/memoize.js";import{clearRegisteredPluginHooks as o,getRegisteredHooks as t,registerHookCallbacks as n}from"../../bootstrap/state.js";import{logForDebugging as s}from"../debug.js";import{settingsChangeDetector as i}from"../settings/changeDetector.js";import{getSettings_DEPRECATED as
|
|
1
|
+
import e from"lodash-es/memoize.js";import{clearRegisteredPluginHooks as o,getRegisteredHooks as t,registerHookCallbacks as n}from"../../bootstrap/state.js";import{logForDebugging as s}from"../debug.js";import{settingsChangeDetector as i}from"../settings/changeDetector.js";import{getSettings_DEPRECATED as a,getSettingsForSource as r}from"../settings/settings.js";import{jsonStringify as l}from"../slowOperations.js";import{clearPluginCache as g,loadAllPluginsCacheOnly as c}from"./pluginLoader.js";let u,p=!1;function convertPluginHooksToMatchers(e){const o={PreToolUse:[],PostToolUse:[],PostToolUseFailure:[],PermissionDenied:[],Notification:[],UserPromptSubmit:[],SessionStart:[],SessionEnd:[],Stop:[],StopFailure:[],SubagentStart:[],SubagentStop:[],PreCompact:[],PostCompact:[],PermissionRequest:[],Setup:[],TeammateIdle:[],TaskCreated:[],TaskCompleted:[],Elicitation:[],ElicitationResult:[],ConfigChange:[],WorktreeCreate:[],WorktreeRemove:[],InstructionsLoaded:[],CwdChanged:[],FileChanged:[]};if(!e.hooksConfig)return o;for(const[t,n]of Object.entries(e.hooksConfig)){const s=t;if(o[s])for(const t of n)t.hooks.length>0&&o[s].push({matcher:t.matcher,hooks:t.hooks,pluginRoot:e.path,pluginName:e.name,pluginId:e.source})}return o}export const loadPluginHooks=e(async()=>{const{enabled:e}=await c(),t={PreToolUse:[],PostToolUse:[],PostToolUseFailure:[],PermissionDenied:[],Notification:[],UserPromptSubmit:[],SessionStart:[],SessionEnd:[],Stop:[],StopFailure:[],SubagentStart:[],SubagentStop:[],PreCompact:[],PostCompact:[],PermissionRequest:[],Setup:[],TeammateIdle:[],TaskCreated:[],TaskCompleted:[],Elicitation:[],ElicitationResult:[],ConfigChange:[],WorktreeCreate:[],WorktreeRemove:[],InstructionsLoaded:[],CwdChanged:[],FileChanged:[]};for(const o of e){if(!o.hooksConfig)continue;s(`Loading hooks from plugin: ${o.name}`);const e=convertPluginHooksToMatchers(o);for(const o of Object.keys(e))t[o].push(...e[o])}o(),n(t);const i=Object.values(t).reduce((e,o)=>e+o.reduce((e,o)=>e+o.hooks.length,0),0);s(`Registered ${i} hooks from ${e.length} plugins`)});export function clearPluginHookCache(){loadPluginHooks.cache?.clear?.()}export async function pruneRemovedPluginHooks(){if(!t())return;const{enabled:e}=await c(),s=new Set(e.map(e=>e.path)),i=t();if(!i)return;const a={};for(const[e,o]of Object.entries(i)){const t=o.filter(e=>"pluginRoot"in e&&s.has(e.pluginRoot));t.length>0&&(a[e]=t)}o(),n(a)}export function resetHotReloadState(){p=!1,u=void 0}export function getPluginAffectingSettingsSnapshot(){const e=a(),o=r("policySettings"),sortKeys=e=>e?Object.fromEntries(Object.entries(e).sort()):{};return l({enabledPlugins:sortKeys(e.enabledPlugins),extraKnownMarketplaces:sortKeys(e.extraKnownMarketplaces),strictKnownMarketplaces:o?.strictKnownMarketplaces??[],blockedMarketplaces:o?.blockedMarketplaces??[]})}export function setupPluginHookHotReload(){p||(p=!0,u=getPluginAffectingSettingsSnapshot(),i.subscribe(e=>{if("policySettings"===e){const e=getPluginAffectingSettingsSnapshot();if(e===u)return void s("Plugin hooks: skipping reload, plugin-affecting settings unchanged");u=e,s("Plugin hooks: reloading due to plugin-affecting settings change"),g("loadPluginHooks: plugin-affecting settings changed"),clearPluginHookCache(),loadPluginHooks()}}))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"lodash-es/memoize.js";import{basename as o}from"path";import{getPluginErrorMessage as e}from"../../types/plugin.js";import{logForDebugging as r}from"../debug.js";import{coerceDescriptionToString as
|
|
1
|
+
import t from"lodash-es/memoize.js";import{basename as o}from"path";import{getPluginErrorMessage as e}from"../../types/plugin.js";import{logForDebugging as r}from"../debug.js";import{coerceDescriptionToString as a,parseFrontmatter as l}from"../frontmatterParser.js";import{getFsImplementation as n,isDuplicatePath as s}from"../fsOperations.js";import{extractDescriptionFromMarkdown as u}from"../markdownConfigLoader.js";import{loadAllPluginsCacheOnly as i}from"./pluginLoader.js";import{walkPluginMarkdown as p}from"./walkPluginMarkdown.js";async function loadOutputStylesFromDirectory(t,o,e){const r=[];return await p(t,async t=>{const a=await loadOutputStyleFromFile(t,o,e);a&&r.push(a)},{logLabel:"output-styles"}),r}async function loadOutputStyleFromFile(t,e,i){const p=n();if(s(p,t,i))return null;try{const r=await p.readFile(t,{encoding:"utf-8"}),{frontmatter:n,content:s}=l(r,t),i=o(t,".md"),m=`${e}:${n.name||i}`,c=a(n.description,m)??u(s,`Output style from ${e} plugin`),d=n["force-for-plugin"],f=!0===d||"true"===d||!1!==d&&"false"!==d&&void 0;return{name:m,description:c,prompt:s.trim(),source:"plugin",forceForPlugin:f}}catch(o){return r(`Failed to load output style from ${t}: ${o}`,{level:"error"}),null}}export const loadPluginOutputStyles=t(async()=>{const{enabled:t,errors:o}=await i(),a=[];o.length>0&&r(`Plugin loading errors: ${o.map(t=>e(t)).join(", ")}`);for(const o of t){const e=new Set;if(o.outputStylesPath)try{const t=await loadOutputStylesFromDirectory(o.outputStylesPath,o.name,e);a.push(...t),t.length>0&&r(`Loaded ${t.length} output styles from plugin ${o.name} default directory`)}catch(t){r(`Failed to load output styles from plugin ${o.name} default directory: ${t}`,{level:"error"})}if(o.outputStylesPaths)for(const l of o.outputStylesPaths)try{const t=n(),s=await t.stat(l);if(s.isDirectory()){const t=await loadOutputStylesFromDirectory(l,o.name,e);a.push(...t),t.length>0&&r(`Loaded ${t.length} output styles from plugin ${o.name} custom path: ${l}`)}else if(s.isFile()&&l.endsWith(".md")){const t=await loadOutputStyleFromFile(l,o.name,e);t&&(a.push(t),r(`Loaded output style from plugin ${o.name} custom file: ${l}`))}}catch(t){r(`Failed to load output styles from plugin ${o.name} custom path ${l}: ${t}`,{level:"error"})}}return r(`Total plugin output styles loaded: ${a.length}`),a});export function clearPluginOutputStyleCache(){loadPluginOutputStyles.cache?.clear?.()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFile as e}from"fs/promises";import{join as r,relative as s,resolve as n}from"path";import{z as o}from"zod/v4";import{expandEnvVarsInString as i}from"../../services/mcp/envExpansion.js";import{logForDebugging as
|
|
1
|
+
import{readFile as e}from"fs/promises";import{join as r,relative as s,resolve as n}from"path";import{z as o}from"zod/v4";import{expandEnvVarsInString as i}from"../../services/mcp/envExpansion.js";import{logForDebugging as a}from"../debug.js";import{isENOENT as t,toError as l}from"../errors.js";import{logError as c}from"../log.js";import{jsonParse as p}from"../slowOperations.js";import{getPluginDataDir as g}from"./pluginDirectories.js";import{getPluginStorageId as u,loadPluginOptions as f,substitutePluginVariables as m,substituteUserConfigVariables as d}from"./pluginOptionsStorage.js";import{LspServerConfigSchema as v}from"./schemas.js";function validatePathWithinPlugin(e,r){const o=n(e),i=n(e,r),a=s(o,i);return a.startsWith("..")||n(a)===a?null:i}export async function loadPluginLspServers(s,n=[]){const i={},g=r(s.path,".lsp.json");try{const r=await e(g,"utf-8"),a=p(r),t=o.record(o.string(),v()).safeParse(a);if(t.success)Object.assign(i,t.data);else{const e=`LSP config validation failed for .lsp.json in plugin ${s.name}: ${t.error.message}`;c(new Error(e)),n.push({type:"lsp-config-invalid",plugin:s.name,serverName:".lsp.json",validationError:t.error.message,source:"plugin"})}}catch(e){t(e)||(e instanceof Error?(s.name,e.message):s.name,c(l(e)),n.push({type:"lsp-config-invalid",plugin:s.name,serverName:".lsp.json",validationError:e instanceof Error?`Failed to parse JSON: ${e.message}`:"Failed to parse JSON file",source:"plugin"}))}if(s.manifest.lspServers){const r=await async function(r,s,n,i){const t={},g=Array.isArray(r)?r:[r];for(const r of g)if("string"==typeof r){const g=validatePathWithinPlugin(s,r);if(!g){const e=`Security: Path traversal attempt blocked in plugin ${n}: ${r}`;c(new Error(e)),a(e,{level:"warn"}),i.push({type:"lsp-config-invalid",plugin:n,serverName:r,validationError:"Invalid path: must be relative and within plugin directory",source:"plugin"});continue}try{const s=await e(g,"utf-8"),a=p(s),l=o.record(o.string(),v()).safeParse(a);if(l.success)Object.assign(t,l.data);else{const e=`LSP config validation failed for ${r} in plugin ${n}: ${l.error.message}`;c(new Error(e)),i.push({type:"lsp-config-invalid",plugin:n,serverName:r,validationError:l.error.message,source:"plugin"})}}catch(e){e instanceof Error&&e.message,c(l(e)),i.push({type:"lsp-config-invalid",plugin:n,serverName:r,validationError:e instanceof Error?`Failed to parse JSON: ${e.message}`:"Failed to parse JSON file",source:"plugin"})}}else for(const[e,s]of Object.entries(r)){const r=v().safeParse(s);if(r.success)t[e]=r.data;else{const s=`LSP config validation failed for inline server "${e}" in plugin ${n}: ${r.error.message}`;c(new Error(s)),i.push({type:"lsp-config-invalid",plugin:n,serverName:e,validationError:r.error.message,source:"plugin"})}}return Object.keys(t).length>0?t:void 0}(s.manifest.lspServers,s.path,s.name,n);r&&Object.assign(i,r)}return Object.keys(i).length>0?i:void 0}export function resolvePluginLspEnvironment(e,r,s,n){const o=[],resolveValue=e=>{let n=m(e,r);s&&(n=d(n,s));const{expanded:a,missingVars:t}=i(n);return o.push(...t),a},t={...e};t.command&&(t.command=resolveValue(t.command)),t.args&&(t.args=t.args.map(e=>resolveValue(e)));const l={CLAUDE_PLUGIN_ROOT:r.path,CLAUDE_PLUGIN_DATA:g(r.source),...t.env||{}};for(const[e,r]of Object.entries(l))"CLAUDE_PLUGIN_ROOT"!==e&&"CLAUDE_PLUGIN_DATA"!==e&&(l[e]=resolveValue(r));if(t.env=l,t.workspaceFolder&&(t.workspaceFolder=resolveValue(t.workspaceFolder)),o.length>0){const e=`Missing environment variables in plugin LSP config: ${[...new Set(o)].join(", ")}`;c(new Error(e)),a(e,{level:"warn"})}return t}export function addPluginScopeToLspServers(e,r){const s={};for(const[n,o]of Object.entries(e))s[`plugin:${r}:${n}`]={...o,scope:"dynamic",source:r};return s}export async function getPluginLspServers(e,r=[]){if(!e.enabled)return;const s=e.lspServers||await loadPluginLspServers(e,r);if(!s)return;const n=e.manifest.userConfig?f(u(e)):void 0,o={};for(const[i,a]of Object.entries(s))o[i]=resolvePluginLspEnvironment(a,e,n,r);return addPluginScopeToLspServers(o,e.name)}export async function extractLspServersFromPlugins(e,r=[]){const s={};for(const n of e){if(!n.enabled)continue;const e=await loadPluginLspServers(n,r);if(e){const r=addPluginScopeToLspServers(e,n.name);Object.assign(s,r),n.lspServers=e,a(`Loaded ${Object.keys(e).length} LSP servers from plugin ${n.name}`)}}return s}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{extname as n}from"path";import{isBinaryInstalled as e}from"../binaryCheck.js";import{getGlobalConfig as o,saveGlobalConfig as t}from"../config.js";import{logForDebugging as i}from"../debug.js";import{isPluginInstalled as
|
|
1
|
+
import{extname as n}from"path";import{isBinaryInstalled as e}from"../binaryCheck.js";import{getGlobalConfig as o,saveGlobalConfig as t}from"../config.js";import{logForDebugging as i}from"../debug.js";import{isPluginInstalled as s}from"./installedPluginsManager.js";import{getMarketplace as r,loadKnownMarketplacesConfig as a}from"./marketplaceManager.js";import{ALLOWED_OFFICIAL_MARKETPLACE_NAMES as c}from"./schemas.js";function isOfficialMarketplace(n){return c.has(n.toLowerCase())}function extractLspInfoFromManifest(n){if(!n)return null;if("string"==typeof n)return i("[lspRecommendation] Skipping string path lspServers (not readable from marketplace)"),null;if(Array.isArray(n)){for(const e of n){if("string"==typeof e)continue;const n=extractFromServerConfigRecord(e);if(n)return n}return null}return extractFromServerConfigRecord(n)}function isRecord(n){return"object"==typeof n&&null!==n}function extractFromServerConfigRecord(n){const e=new Set;let o=null;for(const[t,i]of Object.entries(n)){if(!isRecord(i))continue;o||"string"!=typeof i.command||(o=i.command);const n=i.extensionToLanguage;if(isRecord(n))for(const o of Object.keys(n))e.add(o.toLowerCase())}return o&&0!==e.size?{extensions:e,command:o}:null}export async function getMatchingLspPlugins(t){if(isLspRecommendationsDisabled())return i("[lspRecommendation] Recommendations are disabled"),[];const c=n(t).toLowerCase();if(!c)return i("[lspRecommendation] No file extension found"),[];i(`[lspRecommendation] Looking for LSP plugins for ${c}`);const m=await async function(){const n=new Map;try{const e=await a();for(const o of Object.keys(e))try{const e=await r(o),t=isOfficialMarketplace(o);for(const i of e.plugins){if(!i.lspServers)continue;const e=extractLspInfoFromManifest(i.lspServers);if(!e)continue;const s=`${i.name}@${o}`;n.set(s,{entry:i,marketplaceName:o,extensions:e.extensions,command:e.command,isOfficial:t})}}catch(n){i(`[lspRecommendation] Failed to load marketplace ${o}: ${n}`)}}catch(n){i(`[lspRecommendation] Failed to load marketplaces config: ${n}`)}return n}(),l=o().lspRecommendationNeverPlugins??[],f=[];for(const[n,e]of m)e.extensions.has(c)&&(l.includes(n)?i(`[lspRecommendation] Skipping ${n} (in never suggest list)`):s(n)?i(`[lspRecommendation] Skipping ${n} (already installed)`):f.push({info:e,pluginId:n}));const d=[];for(const{info:n,pluginId:o}of f)await e(n.command)?(d.push({info:n,pluginId:o}),i(`[lspRecommendation] Binary '${n.command}' found for ${o}`)):i(`[lspRecommendation] Skipping ${o} (binary '${n.command}' not found)`);return d.sort((n,e)=>n.info.isOfficial&&!e.info.isOfficial?-1:!n.info.isOfficial&&e.info.isOfficial?1:0),d.map(({info:n,pluginId:e})=>({pluginId:e,pluginName:n.entry.name,marketplaceName:n.marketplaceName,description:n.entry.description,isOfficial:n.isOfficial,extensions:Array.from(n.extensions),command:n.command}))}export function addToNeverSuggest(n){t(e=>{const o=e.lspRecommendationNeverPlugins??[];return o.includes(n)?e:{...e,lspRecommendationNeverPlugins:[...o,n]}}),i(`[lspRecommendation] Added ${n} to never suggest`)}export function incrementIgnoredCount(){t(n=>{const e=(n.lspRecommendationIgnoredCount??0)+1;return{...n,lspRecommendationIgnoredCount:e}}),i("[lspRecommendation] Incremented ignored count")}export function isLspRecommendationsDisabled(){const n=o();return!0===n.lspRecommendationDisabled||(n.lspRecommendationIgnoredCount??0)>=5}export function resetIgnoredCount(){t(n=>0===(n.lspRecommendationIgnoredCount??0)?n:{...n,lspRecommendationIgnoredCount:0}),i("[lspRecommendation] Reset ignored count")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getSettingsForSource as
|
|
1
|
+
import{getSettingsForSource as t}from"../settings/settings.js";export function getManagedPluginNames(){const e=t("policySettings")?.enabledPlugins;if(!e)return null;const n=new Set;for(const[t,s]of Object.entries(e)){if("boolean"!=typeof s||!t.includes("@"))continue;const e=t.split("@")[0];e&&n.add(e)}return n.size>0?n:null}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"lodash-es/isEqual.js";import{toError as r}from"../errors.js";import{logError as e}from"../log.js";import{getSettingsForSource as n}from"../settings/settings.js";import{plural as a}from"../stringUtils.js";import{checkGitAvailable as o}from"./gitAvailability.js";import{getMarketplace as
|
|
1
|
+
import t from"lodash-es/isEqual.js";import{toError as r}from"../errors.js";import{logError as e}from"../log.js";import{getSettingsForSource as n}from"../settings/settings.js";import{plural as a}from"../stringUtils.js";import{checkGitAvailable as o}from"./gitAvailability.js";import{getMarketplace as s}from"./marketplaceManager.js";export function formatFailureDetails(t,r){const e=t.slice(0,2).map(t=>{const e=t.reason||t.error||"unknown error";return r?`${t.name} (${e})`:t.name}).join(r?"; ":", "),n=t.length-2;return`${e}${n>0?` and ${n} more`:""}`}export function getMarketplaceSourceDisplay(t){switch(t.source){case"github":return t.repo;case"url":case"git":return t.url;case"directory":case"file":return t.path;case"settings":return`settings:${t.name}`;default:return"Unknown source"}}export function createPluginId(t,r){return`${t}@${r}`}export async function loadMarketplacesWithGracefulDegradation(t){const n=[],a=[];for(const[o,c]of Object.entries(t)){if(!isSourceAllowedByPolicy(c.source))continue;let u=null;try{u=await s(o)}catch(t){const n=t instanceof Error?t.message:String(t);a.push({name:o,error:n}),e(r(t))}n.push({name:o,config:c,data:u})}return{marketplaces:n,failures:a}}export function formatMarketplaceLoadingErrors(t,r){if(0===t.length)return null;if(r>0){const r=1===t.length?`Warning: Failed to load marketplace '${t[0].name}': ${t[0].error}`:`Warning: Failed to load ${t.length} marketplaces: ${function(t){return t.map(t=>t.name).join(", ")}(t)}`;return{type:"warning",message:r}}return{type:"error",message:`Failed to load all marketplaces. Errors: ${formatFailureErrors(t)}`}}function formatFailureErrors(t){return t.map(t=>`${t.name}: ${t.error}`).join("; ")}export function getStrictKnownMarketplaces(){const t=n("policySettings");return t?.strictKnownMarketplaces?t.strictKnownMarketplaces:null}export function getBlockedMarketplaces(){const t=n("policySettings");return t?.blockedMarketplaces?t.blockedMarketplaces:null}export function getPluginTrustMessage(){return n("policySettings")?.pluginTrustMessage}export function extractHostFromSource(t){switch(t.source){case"github":return"github.com";case"git":{const r=t.url.match(/^[^@]+@([^:]+):/);if(r?.[1])return r[1];try{return new URL(t.url).hostname}catch{return null}}case"url":try{return new URL(t.url).hostname}catch{return null}default:return null}}export function getHostPatternsFromAllowlist(){const t=getStrictKnownMarketplaces();return t?t.filter(t=>"hostPattern"===t.source).map(t=>t.hostPattern):[]}function extractGitHubRepoFromGitUrl(t){const r=t.match(/^git@github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);if(r&&r[1])return r[1];const e=t.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);return e&&e[1]?e[1]:null}function blockedConstraintMatches(t,r){return!t||(t||void 0)===(r||void 0)}export function isSourceInBlocklist(t){const r=getBlockedMarketplaces();return null!==r&&r.some(r=>function(t,r){if(t.source===r.source)switch(t.source){case"github":{const e=r;return t.repo===e.repo&&blockedConstraintMatches(e.ref,t.ref)&&blockedConstraintMatches(e.path,t.path)}case"git":{const e=r;return t.url===e.url&&blockedConstraintMatches(e.ref,t.ref)&&blockedConstraintMatches(e.path,t.path)}case"url":return t.url===r.url;case"npm":return t.package===r.package;case"file":case"directory":return t.path===r.path;case"settings":return t.name===r.name;default:return!1}return("git"===t.source&&"github"===r.source&&extractGitHubRepoFromGitUrl(t.url)===r.repo||"github"===t.source&&"git"===r.source&&extractGitHubRepoFromGitUrl(r.url)===t.repo)&&blockedConstraintMatches(r.ref,t.ref)&&blockedConstraintMatches(r.path,t.path)}(t,r))}export function isSourceAllowedByPolicy(r){if(isSourceInBlocklist(r))return!1;const n=getStrictKnownMarketplaces();return null===n||n.some(n=>"hostPattern"===n.source?function(t,r){const n=extractHostFromSource(t);if(!n)return!1;try{return new RegExp(r.hostPattern).test(n)}catch{return e(new Error(`Invalid hostPattern regex: ${r.hostPattern}`)),!1}}(r,n):"pathPattern"===n.source?function(t,r){if("file"!==t.source&&"directory"!==t.source)return!1;try{return new RegExp(r.pathPattern).test(t.path)}catch{return e(new Error(`Invalid pathPattern regex: ${r.pathPattern}`)),!1}}(r,n):function(r,e){if(r.source!==e.source)return!1;switch(r.source){case"url":return r.url===e.url;case"github":return r.repo===e.repo&&(r.ref||void 0)===(e.ref||void 0)&&(r.path||void 0)===(e.path||void 0);case"git":return r.url===e.url&&(r.ref||void 0)===(e.ref||void 0)&&(r.path||void 0)===(e.path||void 0);case"npm":return r.package===e.package;case"file":case"directory":return r.path===e.path;case"settings":return r.name===e.name&&t(r.plugins,e.plugins);default:return!1}}(r,n))}export function formatSourceForDisplay(t){switch(t.source){case"github":return`github:${t.repo}${t.ref?`@${t.ref}`:""}`;case"url":return t.url;case"git":return`git:${t.url}${t.ref?`@${t.ref}`:""}`;case"npm":return`npm:${t.package}`;case"file":return`file:${t.path}`;case"directory":return`dir:${t.path}`;case"hostPattern":return`hostPattern:${t.hostPattern}`;case"pathPattern":return`pathPattern:${t.pathPattern}`;case"settings":return`settings:${t.name} (${t.plugins.length} ${a(t.plugins.length,"plugin")})`;default:return"unknown source"}}export async function detectEmptyMarketplaceReason({configuredMarketplaceCount:t,failedMarketplaceCount:r}){if(!await o())return"git-not-installed";const e=getStrictKnownMarketplaces();if(null!==e){if(0===e.length)return"all-blocked-by-policy";if(0===t)return"policy-restricts-sources"}return 0===t?"no-marketplaces-configured":r>0&&r===t?"all-marketplaces-failed":"all-plugins-installed"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"axios";import{writeFile as t}from"fs/promises";import r from"lodash-es/isEqual.js";import a from"lodash-es/memoize.js";import{basename as o,dirname as n,isAbsolute as i,join as s,resolve as c,sep as l}from"path";import{getFeatureValue_CACHED_MAY_BE_STALE as d}from"../../services/analytics/growthbook.js";import{logForDebugging as u}from"../debug.js";import{isEnvTruthy as p}from"../envUtils.js";import{ConfigParseError as f,errorMessage as h,getErrnoCode as m,isENOENT as g,toError as w}from"../errors.js";import{execFileNoThrow as k,execFileNoThrowWithCwd as y}from"../execFileNoThrow.js";import{getFsImplementation as $}from"../fsOperations.js";import{gitExe as M}from"../git.js";import{logError as S}from"../log.js";import{getInitialSettings as v,getSettingsForSource as C,updateSettingsForSource as T}from"../settings/settings.js";import{jsonParse as b,jsonStringify as P,writeFileSync_DEPRECATED as E}from"../slowOperations.js";import{getAddDirEnabledPlugins as O,getAddDirExtraMarketplaces as j}from"./addDirPluginSettings.js";import{markPluginVersionOrphaned as U}from"./cacheUtils.js";import{classifyFetchError as H,logPluginFetch as F}from"./fetchTelemetry.js";import{removeAllPluginsForMarketplace as _}from"./installedPluginsManager.js";import{extractHostFromSource as D,formatSourceForDisplay as G,getHostPatternsFromAllowlist as I,getStrictKnownMarketplaces as x,isSourceAllowedByPolicy as L,isSourceInBlocklist as K}from"./marketplaceHelpers.js";import{OFFICIAL_MARKETPLACE_NAME as A,OFFICIAL_MARKETPLACE_SOURCE as R}from"./officialMarketplace.js";import{fetchOfficialMarketplaceFromGcs as N}from"./officialMarketplaceGcs.js";import{deletePluginDataDir as W,getPluginSeedDirs as B,getPluginsDirectory as z}from"./pluginDirectories.js";import{parsePluginIdentifier as q}from"./pluginIdentifier.js";import{deletePluginOptions as V}from"./pluginOptionsStorage.js";import{isLocalMarketplaceSource as X,KnownMarketplacesFileSchema as J,PluginMarketplaceSchema as Y,validateOfficialNameSource as Q}from"./schemas.js";function getKnownMarketplacesFile(){return s(z(),"known_marketplaces.json")}export function getMarketplacesCacheDir(){return s(z(),"marketplaces")}export function clearMarketplacesCache(){getMarketplace.cache?.clear?.()}export function getDeclaredMarketplaces(){const e={},t={...O(),...v().enabledPlugins??{}};for(const[r,a]of Object.entries(t))if(a&&q(r).marketplace===A){e[A]={source:R,sourceIsFallback:!0};break}return{...e,...j(),...v().extraKnownMarketplaces??{}}}export function getMarketplaceDeclaringSource(e){const t=["localSettings","projectSettings","userSettings"];for(const r of t){const t=C(r);if(t?.extraKnownMarketplaces?.[e])return r}return null}export function saveMarketplaceToSettings(e,t,r="userSettings"){const a={...(C(r)??{}).extraKnownMarketplaces};a[e]=t,T(r,{extraKnownMarketplaces:a})}export async function loadKnownMarketplacesConfig(){const e=$(),t=getKnownMarketplacesFile();try{const r=await e.readFile(t,{encoding:"utf-8"}),a=b(r),o=J().safeParse(a);if(!o.success){const e=`Marketplace configuration file is corrupted: ${o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`;throw u(e,{level:"error"}),new f(e,t,a)}return o.data}catch(e){if(g(e))return{};if(e instanceof f)throw e;const t=`Failed to load marketplace configuration: ${h(e)}`;throw u(t,{level:"error"}),new Error(t)}}export async function loadKnownMarketplacesConfigSafe(){try{return await loadKnownMarketplacesConfig()}catch{return{}}}export async function saveKnownMarketplacesConfig(e){const t=J().safeParse(e),r=getKnownMarketplacesFile();if(!t.success)throw new f(`Invalid marketplace config: ${t.error.message}`,r,e);const a=$(),o=s(r,"..");await a.mkdir(o),E(r,P(t.data,null,2),{encoding:"utf-8",flush:!0})}export async function registerSeedMarketplaces(){const e=B();if(0===e.length)return!1;const t=await loadKnownMarketplacesConfig(),a=new Set;let o=0;for(const n of e){const e=await readSeedKnownMarketplaces(n);if(e)for(const[i,s]of Object.entries(e)){if(a.has(i))continue;const e=await findSeedMarketplaceLocation(n,i);if(!e){u(`Seed marketplace '${i}' not found under ${n}/marketplaces/, skipping`,{level:"warn"});continue}a.add(i);const c={source:s.source,installLocation:e,lastUpdated:s.lastUpdated,autoUpdate:!1};r(t[i],c)||(t[i]=c,o++)}}return o>0&&(await saveKnownMarketplacesConfig(t),u(`Synced ${o} marketplace(s) from seed dir(s)`),!0)}async function readSeedKnownMarketplaces(e){const t=s(e,"known_marketplaces.json");try{const r=await $().readFile(t,{encoding:"utf-8"}),a=J().safeParse(b(r));return a.success?a.data:(u(`Seed known_marketplaces.json invalid at ${e}: ${a.error.message}`,{level:"warn"}),null)}catch(t){return g(t)||u(`Failed to read seed known_marketplaces.json at ${e}: ${t}`,{level:"warn"}),null}}async function findSeedMarketplaceLocation(e,t){const r=s(e,"marketplaces",t),a=s(e,"marketplaces",`${t}.json`);for(const e of[r,a])try{return await readCachedMarketplace(e),e}catch{}return null}function seedDirFor(e){return B().find(t=>e===t||e.startsWith(t+l))}const Z={GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:""};function getPluginGitTimeoutMs(){const e=process.env.CONTEXT_CODE_PLUGIN_GIT_TIMEOUT_MS??process.env.CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS;if(e){const t=parseInt(e,10);if(!isNaN(t)&&t>0)return t}return 12e4}export async function gitPull(e,t,r){u(`git pull: cwd=${e} ref=${t??"default"}`);const a={...process.env,...Z},o=r?.disableCredentialHelper?["-c","credential.helper="]:[];if(t){const n=await y(M(),[...o,"fetch","origin",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});if(0!==n.code)return enhanceGitPullErrorMessages(n);const i=await y(M(),[...o,"checkout",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});if(0!==i.code)return enhanceGitPullErrorMessages(i);const s=await y(M(),[...o,"pull","origin",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});return 0!==s.code?enhanceGitPullErrorMessages(s):(await gitSubmoduleUpdate(e,o,a,r?.sparsePaths),s)}const n=await y(M(),[...o,"pull","origin","HEAD"],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});return 0!==n.code?enhanceGitPullErrorMessages(n):(await gitSubmoduleUpdate(e,o,a,r?.sparsePaths),n)}async function gitSubmoduleUpdate(e,t,r,a){if(a&&a.length>0)return;if(!await $().stat(s(e,".gitmodules")).then(()=>!0,()=>!1))return;const o=await y(M(),["-c","core.sshCommand=ssh -o BatchMode=yes -o StrictHostKeyChecking=yes",...t,"submodule","update","--init","--recursive","--depth","1"],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:r});0!==o.code&&u(`git submodule update failed (non-fatal): ${o.stderr}`,{level:"warn"})}function enhanceGitPullErrorMessages(e){if(0===e.code)return e;if(e.error?.includes("timed out")){const t=Math.round(getPluginGitTimeoutMs()/1e3);return{...e,stderr:`Git pull timed out after ${t}s. Try increasing the timeout via CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS environment variable.\n\nOriginal error: ${e.stderr}`}}return e.stderr.includes("REMOTE HOST IDENTIFICATION HAS CHANGED")?{...e,stderr:`SSH host key for this marketplace's git host has changed (server key rotation or possible MITM). Remove the stale entry with: ssh-keygen -R <host>\nThen connect once manually to accept the new key.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("Host key verification failed")?{...e,stderr:`SSH host key verification failed while updating marketplace. The host key is not in your known_hosts file. Connect once manually to add it (e.g., ssh -T git@<host>), or remove and re-add the marketplace with an HTTPS URL.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("Permission denied (publickey)")||e.stderr.includes("Could not read from remote repository")?{...e,stderr:`SSH authentication failed while updating marketplace. Please ensure your SSH keys are configured.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("timed out")||e.stderr.includes("Could not resolve host")?{...e,stderr:`Network error while updating marketplace. Please check your internet connection.\n\nOriginal error: ${e.stderr}`}:e}async function isGitHubSshLikelyConfigured(){try{const e=await k("ssh",["-T","-o","BatchMode=yes","-o","ConnectTimeout=2","-o","StrictHostKeyChecking=yes","git@github.com"],{timeout:3e3}),t=1===e.code&&(e.stderr?.includes("successfully authenticated")||e.stdout?.includes("successfully authenticated"));return u(`SSH config check: code=${e.code} configured=${t}`),t}catch(e){return u(`SSH configuration check failed: ${h(e)}`,{level:"warn"}),!1}}function extractSshHost(e){const t=e.match(/^[^@]+@([^:]+):/);return t?.[1]??null}export async function gitClone(e,t,r,a){const o=a&&a.length>0,n=["-c","core.sshCommand=ssh -o BatchMode=yes -o StrictHostKeyChecking=yes","clone","--depth","1"];o?n.push("--filter=blob:none","--no-checkout"):n.push("--recurse-submodules","--shallow-submodules"),r&&n.push("--branch",r),n.push(e,t);const i=getPluginGitTimeoutMs();u(`git clone: url=${redactUrlCredentials(e)} ref=${r??"default"} timeout=${i}ms`);const s=await y(M(),n,{timeout:i,stdin:"ignore",env:{...process.env,...Z}}),c=redactUrlCredentials(e);if(e!==c&&(s.error&&(s.error=s.error.replaceAll(e,c)),s.stderr&&(s.stderr=s.stderr.replaceAll(e,c))),0===s.code){if(o){const e=await y(M(),["sparse-checkout","set","--cone","--",...a],{cwd:t,timeout:i,stdin:"ignore",env:{...process.env,...Z}});if(0!==e.code)return{code:e.code,stderr:`git sparse-checkout set failed: ${e.stderr}`};const r=await y(M(),["checkout","HEAD"],{cwd:t,timeout:i,stdin:"ignore",env:{...process.env,...Z}});if(0!==r.code)return{code:r.code,stderr:`git checkout after sparse-checkout failed: ${r.stderr}`}}return u(`git clone succeeded: ${redactUrlCredentials(e)}`),s}if(u(`git clone failed: url=${redactUrlCredentials(e)} code=${s.code} error=${s.error??"none"} stderr=${s.stderr}`,{level:"warn"}),s.error?.includes("timed out"))return{...s,stderr:`Git clone timed out after ${Math.round(i/1e3)}s. The repository may be too large for the current timeout. Set CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS to increase it (e.g., 300000 for 5 minutes).\n\nOriginal error: ${s.stderr}`};if(s.stderr){if(s.stderr.includes("REMOTE HOST IDENTIFICATION HAS CHANGED")){const t=extractSshHost(e),r=t?`ssh-keygen -R ${t}`:"ssh-keygen -R <host>";return{...s,stderr:`SSH host key has changed (server key rotation or possible MITM). Remove the stale known_hosts entry:\n ${r}\nThen connect once manually to verify and accept the new key.\n\nOriginal error: ${s.stderr}`}}if(s.stderr.includes("Host key verification failed")){const t=extractSshHost(e),r=t?`ssh -T git@${t}`:"ssh -T git@<host>";return{...s,stderr:`SSH host key is not in your known_hosts file. To add it, connect once manually (this will show the fingerprint for you to verify):\n ${r}\n\nOr use an HTTPS URL instead (recommended for public repos).\n\nOriginal error: ${s.stderr}`}}if(s.stderr.includes("Permission denied (publickey)")||s.stderr.includes("Could not read from remote repository"))return{...s,stderr:`SSH authentication failed. Please ensure your SSH keys are configured for GitHub, or use an HTTPS URL instead.\n\nOriginal error: ${s.stderr}`};if((l=s.stderr).includes("Authentication failed")||l.includes("could not read Username")||l.includes("terminal prompts disabled")||l.includes("403")||l.includes("401"))return{...s,stderr:`HTTPS authentication failed. Please ensure your credential helper is configured (e.g., gh auth login).\n\nOriginal error: ${s.stderr}`};if(s.stderr.includes("timed out")||s.stderr.includes("timeout")||s.stderr.includes("Could not resolve host"))return{...s,stderr:`Network error or timeout while cloning repository. Please check your internet connection and try again.\n\nOriginal error: ${s.stderr}`}}var l;return s.stderr?s:{code:s.code,stderr:s.error||`git clone exited with code ${s.code} (no stderr output). Run with --debug to see the full command.`}}function safeCallProgress(e,t){if(e)try{e(t)}catch(e){u(`Progress callback error: ${h(e)}`,{level:"warn"})}}export async function reconcileSparseCheckout(e,t){const r={...process.env,...Z};if(t&&t.length>0)return y(M(),["sparse-checkout","set","--cone","--",...t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:r});const a=await y(M(),["config","--get","core.sparseCheckout"],{cwd:e,stdin:"ignore",env:r});return 0===a.code&&"true"===a.stdout.trim()?{code:1,stderr:"sparsePaths removed from config but repository is sparse; re-cloning for full checkout"}:{code:0,stderr:""}}async function cacheMarketplaceFromGit(e,t,r,a,o,n){const i=$(),s=Math.round(getPluginGitTimeoutMs()/1e3);safeCallProgress(o,`Refreshing marketplace cache (timeout: ${s}s)…`);const c=await reconcileSparseCheckout(t,a);if(0===c.code){const o=performance.now(),i=await gitPull(t,r,{disableCredentialHelper:n?.disableCredentialHelper,sparsePaths:a});if(F("marketplace_pull",e,0===i.code?"success":"failure",performance.now()-o,0===i.code?void 0:H(i.stderr)),0===i.code)return;u(`git pull failed, will re-clone: ${i.stderr}`,{level:"warn"})}else u(`sparse-checkout reconcile requires re-clone: ${c.stderr}`);try{await i.rm(t,{recursive:!0}),u(`Found stale marketplace directory at ${t}, cleaning up to allow re-clone`,{level:"warn"}),safeCallProgress(o,"Found stale directory, cleaning up and re-cloning…")}catch(e){if(!g(e)){const r=h(e);throw new Error(`Failed to clean up existing marketplace directory. Please manually delete the directory at ${t} and try again.\n\nTechnical details: ${r}`)}}const l=r?` (ref: ${r})`:"";safeCallProgress(o,`Cloning repository (timeout: ${s}s): ${redactUrlCredentials(e)}${l}`);const d=performance.now(),p=await gitClone(e,t,r,a);if(F("marketplace_clone",e,0===p.code?"success":"failure",performance.now()-d,0===p.code?void 0:H(p.stderr)),0!==p.code){try{await i.rm(t,{recursive:!0,force:!0})}catch{}throw new Error(`Failed to clone marketplace repository: ${p.stderr}`)}safeCallProgress(o,"Clone complete, validating marketplace…")}function redactUrlCredentials(e){try{const t=new URL(e);if(("http:"===t.protocol||"https:"===t.protocol)&&(t.username||t.password))return t.username&&(t.username="***"),t.password&&(t.password="***"),t.toString()}catch{}return e}async function cacheMarketplaceFromUrl(t,r,a,o){const n=$(),i=redactUrlCredentials(t);safeCallProgress(o,`Downloading marketplace from ${i}`),u(`Downloading marketplace from URL: ${i}`),a&&Object.keys(a).length>0&&u(`Using custom headers: ${P(function(e){return Object.fromEntries(Object.entries(e).map(([e])=>[e,"***REDACTED***"]))}(a))}`);const c={...a,"User-Agent":"Claude-Code-Plugin-Manager"};let l;const d=performance.now();try{l=await e.get(t,{timeout:1e4,headers:c})}catch(r){if(F("marketplace_url",t,"failure",performance.now()-d,H(r)),e.isAxiosError(r)){if("ECONNREFUSED"===r.code||"ENOTFOUND"===r.code)throw new Error(`Could not connect to ${i}. Please check your internet connection and verify the URL is correct.\n\nTechnical details: ${r.message}`);if("ETIMEDOUT"===r.code)throw new Error(`Request timed out while downloading marketplace from ${i}. The server may be slow or unreachable.\n\nTechnical details: ${r.message}`);if(r.response)throw new Error(`HTTP ${r.response.status} error while downloading marketplace from ${i}. The marketplace file may not exist at this URL.\n\nTechnical details: ${r.message}`)}throw new Error(`Failed to download marketplace from ${i}: ${h(r)}`)}safeCallProgress(o,"Validating marketplace data");const p=Y().safeParse(l.data);if(!p.success)throw F("marketplace_url",t,"failure",performance.now()-d,"invalid_schema"),new f(`Invalid marketplace schema from URL: ${p.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`,i,l.data);F("marketplace_url",t,"success",performance.now()-d),safeCallProgress(o,"Saving marketplace to cache");const m=s(r,"..");await n.mkdir(m),E(r,P(p.data,null,2),{encoding:"utf-8",flush:!0})}async function parseFileWithSchema(e,t){const r=$(),a=await r.readFile(e,{encoding:"utf-8"});let o;try{o=b(a)}catch(t){throw new f(`Invalid JSON in ${e}: ${h(t)}`,e,a)}const n=t.safeParse(o);if(!n.success)throw new f(`Invalid schema: ${e} ${n.error?.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`,e,o);return n.data}async function loadAndCacheMarketplace(e,r){const a=$(),i=getMarketplacesCacheDir();let d,p;await a.mkdir(i);let f=!1;const m=function(e){return"github"===e.source?e.repo.replace("/","-"):"npm"===e.source?e.package.replace("@","").replace("/","-"):"file"===e.source?o(e.path).replace(".json",""):"directory"===e.source?o(e.path):"temp_"+Date.now()}(e);try{switch(e.source){case"url":d=s(i,`${m}.json`),f=!0,await cacheMarketplaceFromUrl(e.url,d,e.headers,r),p=d;break;case"github":{const t=`git@github.com:${e.repo}.git`,o=`https://github.com/${e.repo}.git`;d=s(i,m),f=!0;let n=null;if(await isGitHubSshLikelyConfigured()){safeCallProgress(r,`Cloning via SSH: ${t}`);try{await cacheMarketplaceFromGit(t,d,e.ref,e.sparsePaths,r)}catch(t){n=w(t),S(n),safeCallProgress(r,`SSH clone failed, retrying with HTTPS: ${o}`),u(`SSH clone failed for ${e.repo} despite SSH being configured, falling back to HTTPS`,{level:"info"}),await a.rm(d,{recursive:!0,force:!0});try{await cacheMarketplaceFromGit(o,d,e.ref,e.sparsePaths,r),n=null}catch(e){n=w(e),S(n)}}}else{safeCallProgress(r,`SSH not configured, cloning via HTTPS: ${o}`),u(`SSH not configured for GitHub, using HTTPS for ${e.repo}`,{level:"info"});try{await cacheMarketplaceFromGit(o,d,e.ref,e.sparsePaths,r)}catch(o){n=w(o),S(n),safeCallProgress(r,`HTTPS clone failed, retrying with SSH: ${t}`),u(`HTTPS clone failed for ${e.repo} (${n.message}), falling back to SSH`,{level:"info"}),await a.rm(d,{recursive:!0,force:!0});try{await cacheMarketplaceFromGit(t,d,e.ref,e.sparsePaths,r),n=null}catch(e){n=w(e),S(n)}}}if(n)throw n;p=s(d,e.path||".claude-plugin/marketplace.json");break}case"git":d=s(i,m),f=!0,await cacheMarketplaceFromGit(e.url,d,e.ref,e.sparsePaths,r),p=s(d,e.path||".claude-plugin/marketplace.json");break;case"npm":throw new Error("NPM marketplace sources not yet implemented");case"file":{const t=c(e.path);p=t,d=n(n(t)),f=!1;break}case"directory":{const t=c(e.path);p=s(t,".claude-plugin","marketplace.json"),d=t,f=!1;break}case"settings":d=s(i,e.name),p=s(d,".claude-plugin","marketplace.json"),f=!1,await a.mkdir(n(p)),await t(p,P({name:e.name,owner:e.owner??{name:"settings"},plugins:e.plugins},null,2));break;default:throw new Error("Unsupported marketplace source type")}let o;u(`Reading marketplace from ${p}`);try{o=await parseFileWithSchema(p,Y())}catch(e){if(g(e))throw new Error(`Marketplace file not found at ${p}`);throw new Error(`Failed to parse marketplace file at ${p}: ${h(e)}`)}const k=s(i,o.name),y=c(k),$=c(i);if(!y.startsWith($+l))throw new Error(`Marketplace name '${o.name}' resolves to a path outside the cache directory`);if(d!==k&&!X(e))try{try{r?.("Cleaning up old marketplace cache…")}catch(e){u(`Progress callback error: ${h(e)}`,{level:"warn"})}await a.rm(k,{recursive:!0,force:!0}),await a.rename(d,k),d=k,f=!1}catch(e){const t=h(e);throw new Error(`Failed to finalize marketplace cache. Please manually delete the directory at ${k} if it exists and try again.\n\nTechnical details: ${t}`)}return{marketplace:o,cachePath:d}}catch(t){if(f&&d&&!X(e))try{await a.rm(d,{recursive:!0,force:!0})}catch(e){u(`Warning: Failed to clean up temporary marketplace cache at ${d}: ${h(e)}`,{level:"warn"})}throw t}}export async function addMarketplaceSource(e,t){let a=e;if(X(e)&&!i(e.path)&&(a={...e,path:c(e.path)}),!L(a)){if(K(a))throw new Error(`Marketplace source '${G(a)}' is blocked by enterprise policy.`);const e=x()||[],t=I(),r=D(a);let o=`Marketplace source '${G(a)}'`;throw r&&(o+=` (${r})`),o+=" is blocked by enterprise policy.",e.length>0?o+=` Allowed sources: ${e.map(e=>G(e)).join(", ")}`:o+=" No external marketplaces are allowed.","github"===a.source&&t.length>0&&(o+=`\n\nTip: The shorthand "${a.repo}" assumes github.com. For internal GitHub Enterprise, use the full URL:\n git@your-github-host.com:${a.repo}.git`),new Error(o)}const o=await loadKnownMarketplacesConfig();for(const[e,t]of Object.entries(o))if(r(t.source,a))return u(`Source already materialized as '${e}', skipping clone`),{name:e,alreadyMaterialized:!0,resolvedSource:a};const{marketplace:n,cachePath:s}=await loadAndCacheMarketplace(a,t),d=Q(n.name,a);if(d)throw new Error(d);const p=await loadKnownMarketplacesConfig(),f=p[n.name];if(f){const e=seedDirFor(f.installLocation);if(e)throw new Error(`Marketplace '${n.name}' is seed-managed (${e}). To use a different source, ask your admin to update the seed, or use a different marketplace name.`);if(u(`Marketplace '${n.name}' exists with different source — overwriting`),!X(f.source)){const e=c(getMarketplacesCacheDir()),t=c(f.installLocation);if(t===c(s));else if(t===e||t.startsWith(e+l)){const e=$();await e.rm(f.installLocation,{recursive:!0,force:!0})}else u(`Skipping cleanup of old installLocation (${f.installLocation}) — outside ${e}. The path is corrupted; leaving it alone and overwriting the config entry.`,{level:"warn"})}}return p[n.name]={source:a,installLocation:s,lastUpdated:(new Date).toISOString()},await saveKnownMarketplacesConfig(p),u(`Added marketplace source: ${n.name}`),{name:n.name,alreadyMaterialized:!1,resolvedSource:a}}export async function removeMarketplaceSource(e){const t=await loadKnownMarketplacesConfig();if(!t[e])throw new Error(`Marketplace '${e}' not found`);const r=seedDirFor(t[e].installLocation);if(r)throw new Error(`Marketplace '${e}' is registered from the read-only seed directory (${r}) and will be re-registered on next startup. To stop using its plugins: claude plugin disable <plugin>@${e}`);delete t[e],await saveKnownMarketplacesConfig(t);const a=$(),o=getMarketplacesCacheDir(),n=s(o,e);await a.rm(n,{recursive:!0,force:!0});const i=s(o,`${e}.json`);await a.rm(i,{force:!0});const c=["userSettings","projectSettings","localSettings"];for(const t of c){const r=C(t);if(!r)continue;let a=!1;const o={};if(r.extraKnownMarketplaces?.[e]){const t={...r.extraKnownMarketplaces};t[e]=void 0,o.extraKnownMarketplaces=t,a=!0}if(r.enabledPlugins){const t=`@${e}`,n={...r.enabledPlugins};let i=!1;for(const e in n)e.endsWith(t)&&(n[e]=void 0,i=!0);i&&(o.enabledPlugins=n,a=!0)}if(a){const r=T(t,o);r.error?(S(r.error),u(`Failed to clean up marketplace '${e}' from ${t} settings: ${r.error.message}`)):u(`Cleaned up marketplace '${e}' from ${t} settings`)}}const{orphanedPaths:l,removedPluginIds:d}=_(e);for(const e of l)await U(e);for(const e of d)V(e),await W(e);u(`Removed marketplace source: ${e}`)}async function readCachedMarketplace(e){const t=s(e,".claude-plugin","marketplace.json");try{return await parseFileWithSchema(t,Y())}catch(e){if(e instanceof f)throw e;const t=m(e);if("ENOENT"!==t&&"ENOTDIR"!==t)throw e}return await parseFileWithSchema(e,Y())}export async function getMarketplaceCacheOnly(e){const t=$(),r=getKnownMarketplacesFile();try{const a=await t.readFile(r,{encoding:"utf-8"}),o=b(a)[e];return o?await readCachedMarketplace(o.installLocation):null}catch(t){return g(t)||u(`Failed to read cached marketplace ${e}: ${h(t)}`,{level:"warn"}),null}}export const getMarketplace=a(async e=>{const t=await loadKnownMarketplacesConfig(),r=t[e];if(!r)throw new Error(`Marketplace '${e}' not found in configuration. Available marketplaces: ${Object.keys(t).join(", ")}`);if(X(r.source)&&!i(r.source.path))throw new Error(`Marketplace "${e}" has a relative source path (${r.source.path}) in known_marketplaces.json — this is stale state from an older Context Code version. Run 'claude marketplace remove ${e}' and re-add it from the original project directory.`);try{return await readCachedMarketplace(r.installLocation)}catch(t){u(`Cache corrupted or missing for marketplace ${e}, re-fetching from source: ${h(t)}`,{level:"warn"})}let a;try{({marketplace:a}=await loadAndCacheMarketplace(r.source))}catch(t){throw new Error(`Failed to load marketplace "${e}" from source (${r.source.source}): ${h(t)}`)}return t[e].lastUpdated=(new Date).toISOString(),await saveKnownMarketplacesConfig(t),a});export async function getPluginByIdCacheOnly(e){const{name:t,marketplace:r}=q(e);if(!t||!r)return null;const a=$(),o=getKnownMarketplacesFile();try{const e=await a.readFile(o,{encoding:"utf-8"}),n=b(e)[r];if(!n)return null;const i=await getMarketplaceCacheOnly(r);if(!i)return null;const s=i.plugins.find(e=>e.name===t);return s?{entry:s,marketplaceInstallLocation:n.installLocation}:null}catch{return null}}export async function getPluginById(e){const t=await getPluginByIdCacheOnly(e);if(t)return t;const{name:r,marketplace:a}=q(e);if(!r||!a)return null;try{const e=(await loadKnownMarketplacesConfig())[a];if(!e)return null;const t=(await getMarketplace(a)).plugins.find(e=>e.name===r);return t?{entry:t,marketplaceInstallLocation:e.installLocation}:null}catch(t){return u(`Could not find plugin ${e}: ${h(t)}`,{level:"debug"}),null}}export async function refreshAllMarketplaces(){const e=await loadKnownMarketplacesConfig();for(const[t,r]of Object.entries(e))if(seedDirFor(r.installLocation))u(`Skipping seed-managed marketplace '${t}' in bulk refresh`);else if("settings"!==r.source.source){if(t===A){if(null!==await N(r.installLocation,getMarketplacesCacheDir())){e[t].lastUpdated=(new Date).toISOString();continue}if(!d("tengu_plugin_official_mkt_git_fallback",!0)){u("Skipping official marketplace bulk refresh: GCS failed, git fallback disabled");continue}}try{const{cachePath:a}=await loadAndCacheMarketplace(r.source);e[t].lastUpdated=(new Date).toISOString(),e[t].installLocation=a}catch(e){u(`Failed to refresh marketplace ${t}: ${h(e)}`,{level:"error"})}}await saveKnownMarketplacesConfig(e)}export async function refreshMarketplace(e,t,r){const a=await loadKnownMarketplacesConfig(),o=a[e];if(!o)throw new Error(`Marketplace '${e}' not found. Available marketplaces: ${Object.keys(a).join(", ")}`);if(getMarketplace.cache?.delete?.(e),"settings"!==o.source.source)try{const n=o.installLocation,i=o.source,s=seedDirFor(n);if(s)throw new Error(`Marketplace '${e}' is seed-managed (${s}) and its content is controlled by the seed image. To update: ask your admin to update the seed.`);if(!X(i)){const t=c(getMarketplacesCacheDir()),r=c(n);if(r!==t&&!r.startsWith(t+l))throw new Error(`Marketplace '${e}' has a corrupted installLocation (${n}) — expected a path inside ${t}. This can happen after cross-platform path writes or manual edits to known_marketplaces.json. Run: claude plugin marketplace remove "${e}" and re-add it.`)}if(e===A){if(null!==await N(n,getMarketplacesCacheDir()))return a[e]={...o,lastUpdated:(new Date).toISOString()},void await saveKnownMarketplacesConfig(a);if(!d("tengu_plugin_official_mkt_git_fallback",!0))throw new Error("Official marketplace GCS fetch failed and git fallback is disabled");u("Official marketplace GCS failed; falling back to git",{level:"warn"})}if("github"===i.source||"git"===i.source){if("github"===i.source){const e=`git@github.com:${i.repo}.git`,a=`https://github.com/${i.repo}.git`;if(p(process.env.CONTEXT_CODE_REMOTE)||p(process.env.CLAUDE_CODE_REMOTE))await cacheMarketplaceFromGit(a,n,i.ref,i.sparsePaths,t,r);else{const o=await isGitHubSshLikelyConfigured(),s=o?e:a,c=o?a:e;try{await cacheMarketplaceFromGit(s,n,i.ref,i.sparsePaths,t,r)}catch{u(`Marketplace refresh failed with ${o?"SSH":"HTTPS"} for ${i.repo}, falling back to ${o?"HTTPS":"SSH"}`,{level:"info"}),await cacheMarketplaceFromGit(c,n,i.ref,i.sparsePaths,t,r)}}}else await cacheMarketplaceFromGit(i.url,n,i.ref,i.sparsePaths,t,r);try{await readCachedMarketplace(n)}catch{const t="github"===i.source?i.repo:redactUrlCredentials(i.url);throw new Error(`The marketplace.json file is no longer present in this repository.\n\n${"claude-code-plugins"===e?'We\'ve deprecated "claude-code-plugins" in favor of "claude-plugins-official".':"This marketplace may have been deprecated or moved to a new location."}\nSource: ${t}\n\nYou can remove this marketplace with: claude plugin marketplace remove "${e}"`)}}else if("url"===i.source)await cacheMarketplaceFromUrl(i.url,n,i.headers,t);else{if(!X(i))throw new Error("Unsupported marketplace source type for refresh");safeCallProgress(t,"Validating local marketplace"),await readCachedMarketplace(n)}a[e].lastUpdated=(new Date).toISOString(),await saveKnownMarketplacesConfig(a),u(`Successfully refreshed marketplace: ${e}`)}catch(t){const r=t instanceof Error?t.message:String(t);throw u(`Failed to refresh marketplace ${e}: ${r}`,{level:"error"}),new Error(`Failed to refresh marketplace '${e}': ${r}`)}else u(`Skipping refresh for settings-sourced marketplace '${e}' — no upstream`)}export async function setMarketplaceAutoUpdate(e,t){const r=await loadKnownMarketplacesConfig(),a=r[e];if(!a)throw new Error(`Marketplace '${e}' not found. Available marketplaces: ${Object.keys(r).join(", ")}`);const o=seedDirFor(a.installLocation);if(o)throw new Error(`Marketplace '${e}' is seed-managed (${o}) and auto-update is always disabled for seed content. To update: ask your admin to update the seed.`);if(a.autoUpdate===t)return;r[e]={...a,autoUpdate:t},await saveKnownMarketplacesConfig(r);const n=getMarketplaceDeclaringSource(e);if(n){const r=C(n)?.extraKnownMarketplaces?.[e];r&&saveMarketplaceToSettings(e,{source:r.source,autoUpdate:t},n)}u(`Set autoUpdate=${t} for marketplace: ${e}`)}export const _test={redactUrlCredentials};
|
|
1
|
+
import e from"axios";import{writeFile as t}from"fs/promises";import r from"lodash-es/isEqual.js";import a from"lodash-es/memoize.js";import{basename as o,dirname as n,isAbsolute as s,join as i,resolve as c,sep as l}from"path";import{getFeatureValue_CACHED_MAY_BE_STALE as d}from"../../services/analytics/growthbook.js";import{logForDebugging as u}from"../debug.js";import{isEnvTruthy as p}from"../envUtils.js";import{ConfigParseError as f,errorMessage as h,getErrnoCode as g,isENOENT as m,toError as w}from"../errors.js";import{execFileNoThrow as k,execFileNoThrowWithCwd as y}from"../execFileNoThrow.js";import{getFsImplementation as $}from"../fsOperations.js";import{gitExe as M}from"../git.js";import{logError as S}from"../log.js";import{getInitialSettings as C,getSettingsForSource as v,updateSettingsForSource as T}from"../settings/settings.js";import{jsonParse as P,jsonStringify as E,writeFileSync_DEPRECATED as b}from"../slowOperations.js";import{getAddDirEnabledPlugins as F,getAddDirExtraMarketplaces as O}from"./addDirPluginSettings.js";import{markPluginVersionOrphaned as j}from"./cacheUtils.js";import{classifyFetchError as _,logPluginFetch as H}from"./fetchTelemetry.js";import{removeAllPluginsForMarketplace as U}from"./installedPluginsManager.js";import{extractHostFromSource as D,formatSourceForDisplay as I,getHostPatternsFromAllowlist as x,getStrictKnownMarketplaces as G,isSourceAllowedByPolicy as A,isSourceInBlocklist as L}from"./marketplaceHelpers.js";import{OFFICIAL_MARKETPLACE_NAME as K,OFFICIAL_MARKETPLACE_SOURCE as N}from"./officialMarketplace.js";import{fetchOfficialMarketplaceFromGcs as R}from"./officialMarketplaceGcs.js";import{deletePluginDataDir as W,getPluginSeedDirs as B,getPluginsDirectory as z}from"./pluginDirectories.js";import{parsePluginIdentifier as V}from"./pluginIdentifier.js";import{deletePluginOptions as q}from"./pluginOptionsStorage.js";import{isLocalMarketplaceSource as X,KnownMarketplacesFileSchema as Y,PluginMarketplaceSchema as J,validateOfficialNameSource as Q}from"./schemas.js";function getKnownMarketplacesFile(){return i(z(),"known_marketplaces.json")}export function getMarketplacesCacheDir(){return i(z(),"marketplaces")}export function clearMarketplacesCache(){getMarketplace.cache?.clear?.()}export function getDeclaredMarketplaces(){const e={},t={...F(),...C().enabledPlugins??{}};for(const[r,a]of Object.entries(t))if(a&&V(r).marketplace===K){e[K]={source:N,sourceIsFallback:!0};break}return{...e,...O(),...C().extraKnownMarketplaces??{}}}export function getMarketplaceDeclaringSource(e){const t=["localSettings","projectSettings","userSettings"];for(const r of t){const t=v(r);if(t?.extraKnownMarketplaces?.[e])return r}return null}export function saveMarketplaceToSettings(e,t,r="userSettings"){const a={...(v(r)??{}).extraKnownMarketplaces};a[e]=t,T(r,{extraKnownMarketplaces:a})}export async function loadKnownMarketplacesConfig(){const e=$(),t=getKnownMarketplacesFile();try{const r=await e.readFile(t,{encoding:"utf-8"}),a=P(r),o=Y().safeParse(a);if(!o.success){const e=`Marketplace configuration file is corrupted: ${o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`;throw u(e,{level:"error"}),new f(e,t,a)}return o.data}catch(e){if(m(e))return{};if(e instanceof f)throw e;const t=`Failed to load marketplace configuration: ${h(e)}`;throw u(t,{level:"error"}),new Error(t)}}export async function loadKnownMarketplacesConfigSafe(){try{return await loadKnownMarketplacesConfig()}catch{return{}}}export async function saveKnownMarketplacesConfig(e){const t=Y().safeParse(e),r=getKnownMarketplacesFile();if(!t.success)throw new f(`Invalid marketplace config: ${t.error.message}`,r,e);const a=$(),o=i(r,"..");await a.mkdir(o),b(r,E(t.data,null,2),{encoding:"utf-8",flush:!0})}export async function registerSeedMarketplaces(){const e=B();if(0===e.length)return!1;const t=await loadKnownMarketplacesConfig(),a=new Set;let o=0;for(const n of e){const e=await readSeedKnownMarketplaces(n);if(e)for(const[s,i]of Object.entries(e)){if(a.has(s))continue;const e=await findSeedMarketplaceLocation(n,s);if(!e){u(`Seed marketplace '${s}' not found under ${n}/marketplaces/, skipping`,{level:"warn"});continue}a.add(s);const c={source:i.source,installLocation:e,lastUpdated:i.lastUpdated,autoUpdate:!1};r(t[s],c)||(t[s]=c,o++)}}return o>0&&(await saveKnownMarketplacesConfig(t),u(`Synced ${o} marketplace(s) from seed dir(s)`),!0)}async function readSeedKnownMarketplaces(e){const t=i(e,"known_marketplaces.json");try{const r=await $().readFile(t,{encoding:"utf-8"}),a=Y().safeParse(P(r));return a.success?a.data:(u(`Seed known_marketplaces.json invalid at ${e}: ${a.error.message}`,{level:"warn"}),null)}catch(t){return m(t)||u(`Failed to read seed known_marketplaces.json at ${e}: ${t}`,{level:"warn"}),null}}async function findSeedMarketplaceLocation(e,t){const r=i(e,"marketplaces",t),a=i(e,"marketplaces",`${t}.json`);for(const e of[r,a])try{return await readCachedMarketplace(e),e}catch{}return null}function seedDirFor(e){return B().find(t=>e===t||e.startsWith(t+l))}const Z={GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:""};function getPluginGitTimeoutMs(){const e=process.env.CONTEXT_CODE_PLUGIN_GIT_TIMEOUT_MS??process.env.CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS;if(e){const t=parseInt(e,10);if(!isNaN(t)&&t>0)return t}return 12e4}export async function gitPull(e,t,r){u(`git pull: cwd=${e} ref=${t??"default"}`);const a={...process.env,...Z},o=r?.disableCredentialHelper?["-c","credential.helper="]:[];if(t){const n=await y(M(),[...o,"fetch","origin",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});if(0!==n.code)return enhanceGitPullErrorMessages(n);const s=await y(M(),[...o,"checkout",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});if(0!==s.code)return enhanceGitPullErrorMessages(s);const i=await y(M(),[...o,"pull","origin",t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});return 0!==i.code?enhanceGitPullErrorMessages(i):(await gitSubmoduleUpdate(e,o,a,r?.sparsePaths),i)}const n=await y(M(),[...o,"pull","origin","HEAD"],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:a});return 0!==n.code?enhanceGitPullErrorMessages(n):(await gitSubmoduleUpdate(e,o,a,r?.sparsePaths),n)}async function gitSubmoduleUpdate(e,t,r,a){if(a&&a.length>0)return;if(!await $().stat(i(e,".gitmodules")).then(()=>!0,()=>!1))return;const o=await y(M(),["-c","core.sshCommand=ssh -o BatchMode=yes -o StrictHostKeyChecking=yes",...t,"submodule","update","--init","--recursive","--depth","1"],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:r});0!==o.code&&u(`git submodule update failed (non-fatal): ${o.stderr}`,{level:"warn"})}function enhanceGitPullErrorMessages(e){if(0===e.code)return e;if(e.error?.includes("timed out")){const t=Math.round(getPluginGitTimeoutMs()/1e3);return{...e,stderr:`Git pull timed out after ${t}s. Try increasing the timeout via CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS environment variable.\n\nOriginal error: ${e.stderr}`}}return e.stderr.includes("REMOTE HOST IDENTIFICATION HAS CHANGED")?{...e,stderr:`SSH host key for this marketplace's git host has changed (server key rotation or possible MITM). Remove the stale entry with: ssh-keygen -R <host>\nThen connect once manually to accept the new key.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("Host key verification failed")?{...e,stderr:`SSH host key verification failed while updating marketplace. The host key is not in your known_hosts file. Connect once manually to add it (e.g., ssh -T git@<host>), or remove and re-add the marketplace with an HTTPS URL.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("Permission denied (publickey)")||e.stderr.includes("Could not read from remote repository")?{...e,stderr:`SSH authentication failed while updating marketplace. Please ensure your SSH keys are configured.\n\nOriginal error: ${e.stderr}`}:e.stderr.includes("timed out")||e.stderr.includes("Could not resolve host")?{...e,stderr:`Network error while updating marketplace. Please check your internet connection.\n\nOriginal error: ${e.stderr}`}:e}async function isGitHubSshLikelyConfigured(){try{const e=await k("ssh",["-T","-o","BatchMode=yes","-o","ConnectTimeout=2","-o","StrictHostKeyChecking=yes","git@github.com"],{timeout:3e3}),t=1===e.code&&(e.stderr?.includes("successfully authenticated")||e.stdout?.includes("successfully authenticated"));return u(`SSH config check: code=${e.code} configured=${t}`),t}catch(e){return u(`SSH configuration check failed: ${h(e)}`,{level:"warn"}),!1}}function extractSshHost(e){const t=e.match(/^[^@]+@([^:]+):/);return t?.[1]??null}export async function gitClone(e,t,r,a){const o=a&&a.length>0,n=["-c","core.sshCommand=ssh -o BatchMode=yes -o StrictHostKeyChecking=yes","clone","--depth","1"];o?n.push("--filter=blob:none","--no-checkout"):n.push("--recurse-submodules","--shallow-submodules"),r&&n.push("--branch",r),n.push(e,t);const s=getPluginGitTimeoutMs();u(`git clone: url=${redactUrlCredentials(e)} ref=${r??"default"} timeout=${s}ms`);const i=await y(M(),n,{timeout:s,stdin:"ignore",env:{...process.env,...Z}}),c=redactUrlCredentials(e);if(e!==c&&(i.error&&(i.error=i.error.replaceAll(e,c)),i.stderr&&(i.stderr=i.stderr.replaceAll(e,c))),0===i.code){if(o){const e=await y(M(),["sparse-checkout","set","--cone","--",...a],{cwd:t,timeout:s,stdin:"ignore",env:{...process.env,...Z}});if(0!==e.code)return{code:e.code,stderr:`git sparse-checkout set failed: ${e.stderr}`};const r=await y(M(),["checkout","HEAD"],{cwd:t,timeout:s,stdin:"ignore",env:{...process.env,...Z}});if(0!==r.code)return{code:r.code,stderr:`git checkout after sparse-checkout failed: ${r.stderr}`}}return u(`git clone succeeded: ${redactUrlCredentials(e)}`),i}if(u(`git clone failed: url=${redactUrlCredentials(e)} code=${i.code} error=${i.error??"none"} stderr=${i.stderr}`,{level:"warn"}),i.error?.includes("timed out"))return{...i,stderr:`Git clone timed out after ${Math.round(s/1e3)}s. The repository may be too large for the current timeout. Set CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS to increase it (e.g., 300000 for 5 minutes).\n\nOriginal error: ${i.stderr}`};if(i.stderr){if(i.stderr.includes("REMOTE HOST IDENTIFICATION HAS CHANGED")){const t=extractSshHost(e),r=t?`ssh-keygen -R ${t}`:"ssh-keygen -R <host>";return{...i,stderr:`SSH host key has changed (server key rotation or possible MITM). Remove the stale known_hosts entry:\n ${r}\nThen connect once manually to verify and accept the new key.\n\nOriginal error: ${i.stderr}`}}if(i.stderr.includes("Host key verification failed")){const t=extractSshHost(e),r=t?`ssh -T git@${t}`:"ssh -T git@<host>";return{...i,stderr:`SSH host key is not in your known_hosts file. To add it, connect once manually (this will show the fingerprint for you to verify):\n ${r}\n\nOr use an HTTPS URL instead (recommended for public repos).\n\nOriginal error: ${i.stderr}`}}if(i.stderr.includes("Permission denied (publickey)")||i.stderr.includes("Could not read from remote repository"))return{...i,stderr:`SSH authentication failed. Please ensure your SSH keys are configured for GitHub, or use an HTTPS URL instead.\n\nOriginal error: ${i.stderr}`};if((l=i.stderr).includes("Authentication failed")||l.includes("could not read Username")||l.includes("terminal prompts disabled")||l.includes("403")||l.includes("401"))return{...i,stderr:`HTTPS authentication failed. Please ensure your credential helper is configured (e.g., gh auth login).\n\nOriginal error: ${i.stderr}`};if(i.stderr.includes("timed out")||i.stderr.includes("timeout")||i.stderr.includes("Could not resolve host"))return{...i,stderr:`Network error or timeout while cloning repository. Please check your internet connection and try again.\n\nOriginal error: ${i.stderr}`}}var l;return i.stderr?i:{code:i.code,stderr:i.error||`git clone exited with code ${i.code} (no stderr output). Run with --debug to see the full command.`}}function safeCallProgress(e,t){if(e)try{e(t)}catch(e){u(`Progress callback error: ${h(e)}`,{level:"warn"})}}export async function reconcileSparseCheckout(e,t){const r={...process.env,...Z};if(t&&t.length>0)return y(M(),["sparse-checkout","set","--cone","--",...t],{cwd:e,timeout:getPluginGitTimeoutMs(),stdin:"ignore",env:r});const a=await y(M(),["config","--get","core.sparseCheckout"],{cwd:e,stdin:"ignore",env:r});return 0===a.code&&"true"===a.stdout.trim()?{code:1,stderr:"sparsePaths removed from config but repository is sparse; re-cloning for full checkout"}:{code:0,stderr:""}}async function cacheMarketplaceFromGit(e,t,r,a,o,n){const s=$(),i=Math.round(getPluginGitTimeoutMs()/1e3);safeCallProgress(o,`Refreshing marketplace cache (timeout: ${i}s)…`);const c=await reconcileSparseCheckout(t,a);if(0===c.code){const o=performance.now(),s=await gitPull(t,r,{disableCredentialHelper:n?.disableCredentialHelper,sparsePaths:a});if(H("marketplace_pull",e,0===s.code?"success":"failure",performance.now()-o,0===s.code?void 0:_(s.stderr)),0===s.code)return;u(`git pull failed, will re-clone: ${s.stderr}`,{level:"warn"})}else u(`sparse-checkout reconcile requires re-clone: ${c.stderr}`);try{await s.rm(t,{recursive:!0}),u(`Found stale marketplace directory at ${t}, cleaning up to allow re-clone`,{level:"warn"}),safeCallProgress(o,"Found stale directory, cleaning up and re-cloning…")}catch(e){if(!m(e)){const r=h(e);throw new Error(`Failed to clean up existing marketplace directory. Please manually delete the directory at ${t} and try again.\n\nTechnical details: ${r}`)}}const l=r?` (ref: ${r})`:"";safeCallProgress(o,`Cloning repository (timeout: ${i}s): ${redactUrlCredentials(e)}${l}`);const d=performance.now(),p=await gitClone(e,t,r,a);if(H("marketplace_clone",e,0===p.code?"success":"failure",performance.now()-d,0===p.code?void 0:_(p.stderr)),0!==p.code){try{await s.rm(t,{recursive:!0,force:!0})}catch{}throw new Error(`Failed to clone marketplace repository: ${p.stderr}`)}safeCallProgress(o,"Clone complete, validating marketplace…")}function redactUrlCredentials(e){try{const t=new URL(e);if(("http:"===t.protocol||"https:"===t.protocol)&&(t.username||t.password))return t.username&&(t.username="***"),t.password&&(t.password="***"),t.toString()}catch{}return e}async function cacheMarketplaceFromUrl(t,r,a,o){const n=$(),s=redactUrlCredentials(t);safeCallProgress(o,`Downloading marketplace from ${s}`),u(`Downloading marketplace from URL: ${s}`),a&&Object.keys(a).length>0&&u(`Using custom headers: ${E(function(e){return Object.fromEntries(Object.entries(e).map(([e])=>[e,"***REDACTED***"]))}(a))}`);const c={...a,"User-Agent":"Claude-Code-Plugin-Manager"};let l;const d=performance.now();try{l=await e.get(t,{timeout:1e4,headers:c})}catch(r){if(H("marketplace_url",t,"failure",performance.now()-d,_(r)),e.isAxiosError(r)){if("ECONNREFUSED"===r.code||"ENOTFOUND"===r.code)throw new Error(`Could not connect to ${s}. Please check your internet connection and verify the URL is correct.\n\nTechnical details: ${r.message}`);if("ETIMEDOUT"===r.code)throw new Error(`Request timed out while downloading marketplace from ${s}. The server may be slow or unreachable.\n\nTechnical details: ${r.message}`);if(r.response)throw new Error(`HTTP ${r.response.status} error while downloading marketplace from ${s}. The marketplace file may not exist at this URL.\n\nTechnical details: ${r.message}`)}throw new Error(`Failed to download marketplace from ${s}: ${h(r)}`)}safeCallProgress(o,"Validating marketplace data");const p=J().safeParse(l.data);if(!p.success)throw H("marketplace_url",t,"failure",performance.now()-d,"invalid_schema"),new f(`Invalid marketplace schema from URL: ${p.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`,s,l.data);H("marketplace_url",t,"success",performance.now()-d),safeCallProgress(o,"Saving marketplace to cache");const g=i(r,"..");await n.mkdir(g),b(r,E(p.data,null,2),{encoding:"utf-8",flush:!0})}async function parseFileWithSchema(e,t){const r=$(),a=await r.readFile(e,{encoding:"utf-8"});let o;try{o=P(a)}catch(t){throw new f(`Invalid JSON in ${e}: ${h(t)}`,e,a)}const n=t.safeParse(o);if(!n.success)throw new f(`Invalid schema: ${e} ${n.error?.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")}`,e,o);return n.data}async function loadAndCacheMarketplace(e,r){const a=$(),s=getMarketplacesCacheDir();let d,p;await a.mkdir(s);let f=!1;const g=function(e){return"github"===e.source?e.repo.replace("/","-"):"npm"===e.source?e.package.replace("@","").replace("/","-"):"file"===e.source?o(e.path).replace(".json",""):"directory"===e.source?o(e.path):"temp_"+Date.now()}(e);try{switch(e.source){case"url":d=i(s,`${g}.json`),f=!0,await cacheMarketplaceFromUrl(e.url,d,e.headers,r),p=d;break;case"github":{const t=`git@github.com:${e.repo}.git`,o=`https://github.com/${e.repo}.git`;d=i(s,g),f=!0;let n=null;if(await isGitHubSshLikelyConfigured()){safeCallProgress(r,`Cloning via SSH: ${t}`);try{await cacheMarketplaceFromGit(t,d,e.ref,e.sparsePaths,r)}catch(t){n=w(t),S(n),safeCallProgress(r,`SSH clone failed, retrying with HTTPS: ${o}`),u(`SSH clone failed for ${e.repo} despite SSH being configured, falling back to HTTPS`,{level:"info"}),await a.rm(d,{recursive:!0,force:!0});try{await cacheMarketplaceFromGit(o,d,e.ref,e.sparsePaths,r),n=null}catch(e){n=w(e),S(n)}}}else{safeCallProgress(r,`SSH not configured, cloning via HTTPS: ${o}`),u(`SSH not configured for GitHub, using HTTPS for ${e.repo}`,{level:"info"});try{await cacheMarketplaceFromGit(o,d,e.ref,e.sparsePaths,r)}catch(o){n=w(o),S(n),safeCallProgress(r,`HTTPS clone failed, retrying with SSH: ${t}`),u(`HTTPS clone failed for ${e.repo} (${n.message}), falling back to SSH`,{level:"info"}),await a.rm(d,{recursive:!0,force:!0});try{await cacheMarketplaceFromGit(t,d,e.ref,e.sparsePaths,r),n=null}catch(e){n=w(e),S(n)}}}if(n)throw n;p=i(d,e.path||".claude-plugin/marketplace.json");break}case"git":d=i(s,g),f=!0,await cacheMarketplaceFromGit(e.url,d,e.ref,e.sparsePaths,r),p=i(d,e.path||".claude-plugin/marketplace.json");break;case"npm":throw new Error("NPM marketplace sources not yet implemented");case"file":{const t=c(e.path);p=t,d=n(n(t)),f=!1;break}case"directory":{const t=c(e.path);p=i(t,".claude-plugin","marketplace.json"),d=t,f=!1;break}case"settings":d=i(s,e.name),p=i(d,".claude-plugin","marketplace.json"),f=!1,await a.mkdir(n(p)),await t(p,E({name:e.name,owner:e.owner??{name:"settings"},plugins:e.plugins},null,2));break;default:throw new Error("Unsupported marketplace source type")}let o;u(`Reading marketplace from ${p}`);try{o=await parseFileWithSchema(p,J())}catch(e){if(m(e))throw new Error(`Marketplace file not found at ${p}`);throw new Error(`Failed to parse marketplace file at ${p}: ${h(e)}`)}const k=i(s,o.name),y=c(k),$=c(s);if(!y.startsWith($+l))throw new Error(`Marketplace name '${o.name}' resolves to a path outside the cache directory`);if(d!==k&&!X(e))try{try{r?.("Cleaning up old marketplace cache…")}catch(e){u(`Progress callback error: ${h(e)}`,{level:"warn"})}await a.rm(k,{recursive:!0,force:!0}),await a.rename(d,k),d=k,f=!1}catch(e){const t=h(e);throw new Error(`Failed to finalize marketplace cache. Please manually delete the directory at ${k} if it exists and try again.\n\nTechnical details: ${t}`)}return{marketplace:o,cachePath:d}}catch(t){if(f&&d&&!X(e))try{await a.rm(d,{recursive:!0,force:!0})}catch(e){u(`Warning: Failed to clean up temporary marketplace cache at ${d}: ${h(e)}`,{level:"warn"})}throw t}}export async function addMarketplaceSource(e,t){let a=e;if(X(e)&&!s(e.path)&&(a={...e,path:c(e.path)}),!A(a)){if(L(a))throw new Error(`Marketplace source '${I(a)}' is blocked by enterprise policy.`);const e=G()||[],t=x(),r=D(a);let o=`Marketplace source '${I(a)}'`;throw r&&(o+=` (${r})`),o+=" is blocked by enterprise policy.",e.length>0?o+=` Allowed sources: ${e.map(e=>I(e)).join(", ")}`:o+=" No external marketplaces are allowed.","github"===a.source&&t.length>0&&(o+=`\n\nTip: The shorthand "${a.repo}" assumes github.com. For internal GitHub Enterprise, use the full URL:\n git@your-github-host.com:${a.repo}.git`),new Error(o)}const o=await loadKnownMarketplacesConfig();for(const[e,t]of Object.entries(o))if(r(t.source,a))return u(`Source already materialized as '${e}', skipping clone`),{name:e,alreadyMaterialized:!0,resolvedSource:a};const{marketplace:n,cachePath:i}=await loadAndCacheMarketplace(a,t),d=Q(n.name,a);if(d)throw new Error(d);const p=await loadKnownMarketplacesConfig(),f=p[n.name];if(f){const e=seedDirFor(f.installLocation);if(e)throw new Error(`Marketplace '${n.name}' is seed-managed (${e}). To use a different source, ask your admin to update the seed, or use a different marketplace name.`);if(u(`Marketplace '${n.name}' exists with different source — overwriting`),!X(f.source)){const e=c(getMarketplacesCacheDir()),t=c(f.installLocation);if(t===c(i));else if(t===e||t.startsWith(e+l)){const e=$();await e.rm(f.installLocation,{recursive:!0,force:!0})}else u(`Skipping cleanup of old installLocation (${f.installLocation}) — outside ${e}. The path is corrupted; leaving it alone and overwriting the config entry.`,{level:"warn"})}}return p[n.name]={source:a,installLocation:i,lastUpdated:(new Date).toISOString()},await saveKnownMarketplacesConfig(p),u(`Added marketplace source: ${n.name}`),{name:n.name,alreadyMaterialized:!1,resolvedSource:a}}export async function removeMarketplaceSource(e){const t=await loadKnownMarketplacesConfig();if(!t[e])throw new Error(`Marketplace '${e}' not found`);const r=seedDirFor(t[e].installLocation);if(r)throw new Error(`Marketplace '${e}' is registered from the read-only seed directory (${r}) and will be re-registered on next startup. To stop using its plugins: claude plugin disable <plugin>@${e}`);delete t[e],await saveKnownMarketplacesConfig(t);const a=$(),o=getMarketplacesCacheDir(),n=i(o,e);await a.rm(n,{recursive:!0,force:!0});const s=i(o,`${e}.json`);await a.rm(s,{force:!0});const c=["userSettings","projectSettings","localSettings"];for(const t of c){const r=v(t);if(!r)continue;let a=!1;const o={};if(r.extraKnownMarketplaces?.[e]){const t={...r.extraKnownMarketplaces};t[e]=void 0,o.extraKnownMarketplaces=t,a=!0}if(r.enabledPlugins){const t=`@${e}`,n={...r.enabledPlugins};let s=!1;for(const e in n)e.endsWith(t)&&(n[e]=void 0,s=!0);s&&(o.enabledPlugins=n,a=!0)}if(a){const r=T(t,o);r.error?(S(r.error),u(`Failed to clean up marketplace '${e}' from ${t} settings: ${r.error.message}`)):u(`Cleaned up marketplace '${e}' from ${t} settings`)}}const{orphanedPaths:l,removedPluginIds:d}=U(e);for(const e of l)await j(e);for(const e of d)q(e),await W(e);u(`Removed marketplace source: ${e}`)}async function readCachedMarketplace(e){const t=i(e,".claude-plugin","marketplace.json");try{return await parseFileWithSchema(t,J())}catch(e){if(e instanceof f)throw e;const t=g(e);if("ENOENT"!==t&&"ENOTDIR"!==t)throw e}return await parseFileWithSchema(e,J())}export async function getMarketplaceCacheOnly(e){const t=$(),r=getKnownMarketplacesFile();try{const a=await t.readFile(r,{encoding:"utf-8"}),o=P(a)[e];return o?await readCachedMarketplace(o.installLocation):null}catch(t){return m(t)||u(`Failed to read cached marketplace ${e}: ${h(t)}`,{level:"warn"}),null}}export const getMarketplace=a(async e=>{const t=await loadKnownMarketplacesConfig(),r=t[e];if(!r)throw new Error(`Marketplace '${e}' not found in configuration. Available marketplaces: ${Object.keys(t).join(", ")}`);if(X(r.source)&&!s(r.source.path))throw new Error(`Marketplace "${e}" has a relative source path (${r.source.path}) in known_marketplaces.json — this is stale state from an older Context Code version. Run 'claude marketplace remove ${e}' and re-add it from the original project directory.`);try{return await readCachedMarketplace(r.installLocation)}catch(t){u(`Cache corrupted or missing for marketplace ${e}, re-fetching from source: ${h(t)}`,{level:"warn"})}let a;try{({marketplace:a}=await loadAndCacheMarketplace(r.source))}catch(t){throw new Error(`Failed to load marketplace "${e}" from source (${r.source.source}): ${h(t)}`)}return t[e].lastUpdated=(new Date).toISOString(),await saveKnownMarketplacesConfig(t),a});export async function getPluginByIdCacheOnly(e){const{name:t,marketplace:r}=V(e);if(!t||!r)return null;const a=$(),o=getKnownMarketplacesFile();try{const e=await a.readFile(o,{encoding:"utf-8"}),n=P(e)[r];if(!n)return null;const s=await getMarketplaceCacheOnly(r);if(!s)return null;const i=s.plugins.find(e=>e.name===t);return i?{entry:i,marketplaceInstallLocation:n.installLocation}:null}catch{return null}}export async function getPluginById(e){const t=await getPluginByIdCacheOnly(e);if(t)return t;const{name:r,marketplace:a}=V(e);if(!r||!a)return null;try{const e=(await loadKnownMarketplacesConfig())[a];if(!e)return null;const t=(await getMarketplace(a)).plugins.find(e=>e.name===r);return t?{entry:t,marketplaceInstallLocation:e.installLocation}:null}catch(t){return u(`Could not find plugin ${e}: ${h(t)}`,{level:"debug"}),null}}export async function refreshAllMarketplaces(){const e=await loadKnownMarketplacesConfig();for(const[t,r]of Object.entries(e))if(seedDirFor(r.installLocation))u(`Skipping seed-managed marketplace '${t}' in bulk refresh`);else if("settings"!==r.source.source){if(t===K){if(null!==await R(r.installLocation,getMarketplacesCacheDir())){e[t].lastUpdated=(new Date).toISOString();continue}if(!d("tengu_plugin_official_mkt_git_fallback",!0)){u("Skipping official marketplace bulk refresh: GCS failed, git fallback disabled");continue}}try{const{cachePath:a}=await loadAndCacheMarketplace(r.source);e[t].lastUpdated=(new Date).toISOString(),e[t].installLocation=a}catch(e){u(`Failed to refresh marketplace ${t}: ${h(e)}`,{level:"error"})}}await saveKnownMarketplacesConfig(e)}export async function refreshMarketplace(e,t,r){const a=await loadKnownMarketplacesConfig(),o=a[e];if(!o)throw new Error(`Marketplace '${e}' not found. Available marketplaces: ${Object.keys(a).join(", ")}`);if(getMarketplace.cache?.delete?.(e),"settings"!==o.source.source)try{const n=o.installLocation,s=o.source,i=seedDirFor(n);if(i)throw new Error(`Marketplace '${e}' is seed-managed (${i}) and its content is controlled by the seed image. To update: ask your admin to update the seed.`);if(!X(s)){const t=c(getMarketplacesCacheDir()),r=c(n);if(r!==t&&!r.startsWith(t+l))throw new Error(`Marketplace '${e}' has a corrupted installLocation (${n}) — expected a path inside ${t}. This can happen after cross-platform path writes or manual edits to known_marketplaces.json. Run: claude plugin marketplace remove "${e}" and re-add it.`)}if(e===K){if(null!==await R(n,getMarketplacesCacheDir()))return a[e]={...o,lastUpdated:(new Date).toISOString()},void await saveKnownMarketplacesConfig(a);if(!d("tengu_plugin_official_mkt_git_fallback",!0))throw new Error("Official marketplace GCS fetch failed and git fallback is disabled");u("Official marketplace GCS failed; falling back to git",{level:"warn"})}if("github"===s.source||"git"===s.source){if("github"===s.source){const e=`git@github.com:${s.repo}.git`,a=`https://github.com/${s.repo}.git`;if(p(process.env.CONTEXT_CODE_REMOTE)||p(process.env.CLAUDE_CODE_REMOTE))await cacheMarketplaceFromGit(a,n,s.ref,s.sparsePaths,t,r);else{const o=await isGitHubSshLikelyConfigured(),i=o?e:a,c=o?a:e;try{await cacheMarketplaceFromGit(i,n,s.ref,s.sparsePaths,t,r)}catch{u(`Marketplace refresh failed with ${o?"SSH":"HTTPS"} for ${s.repo}, falling back to ${o?"HTTPS":"SSH"}`,{level:"info"}),await cacheMarketplaceFromGit(c,n,s.ref,s.sparsePaths,t,r)}}}else await cacheMarketplaceFromGit(s.url,n,s.ref,s.sparsePaths,t,r);try{await readCachedMarketplace(n)}catch{const t="github"===s.source?s.repo:redactUrlCredentials(s.url);throw new Error(`The marketplace.json file is no longer present in this repository.\n\n${"claude-code-plugins"===e?'We\'ve deprecated "claude-code-plugins" in favor of "claude-plugins-official".':"This marketplace may have been deprecated or moved to a new location."}\nSource: ${t}\n\nYou can remove this marketplace with: claude plugin marketplace remove "${e}"`)}}else if("url"===s.source)await cacheMarketplaceFromUrl(s.url,n,s.headers,t);else{if(!X(s))throw new Error("Unsupported marketplace source type for refresh");safeCallProgress(t,"Validating local marketplace"),await readCachedMarketplace(n)}a[e].lastUpdated=(new Date).toISOString(),await saveKnownMarketplacesConfig(a),u(`Successfully refreshed marketplace: ${e}`)}catch(t){const r=t instanceof Error?t.message:String(t);throw u(`Failed to refresh marketplace ${e}: ${r}`,{level:"error"}),new Error(`Failed to refresh marketplace '${e}': ${r}`)}else u(`Skipping refresh for settings-sourced marketplace '${e}' — no upstream`)}export async function setMarketplaceAutoUpdate(e,t){const r=await loadKnownMarketplacesConfig(),a=r[e];if(!a)throw new Error(`Marketplace '${e}' not found. Available marketplaces: ${Object.keys(r).join(", ")}`);const o=seedDirFor(a.installLocation);if(o)throw new Error(`Marketplace '${e}' is seed-managed (${o}) and auto-update is always disabled for seed content. To update: ask your admin to update the seed.`);if(a.autoUpdate===t)return;r[e]={...a,autoUpdate:t},await saveKnownMarketplacesConfig(r);const n=getMarketplaceDeclaringSource(e);if(n){const r=v(n)?.extraKnownMarketplaces?.[e];r&&saveMarketplaceToSettings(e,{source:r.source,autoUpdate:t},n)}u(`Set autoUpdate=${t} for marketplace: ${e}`)}export const _test={redactUrlCredentials};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as e}from"path";import{expandEnvVarsInString as r}from"../../services/mcp/envExpansion.js";import{McpServerConfigSchema as n}from"../../services/mcp/types.js";import{logForDebugging as s}from"../debug.js";import{errorMessage as o,isENOENT as t}from"../errors.js";import{getFsImplementation as i}from"../fsOperations.js";import{jsonParse as a}from"../slowOperations.js";import{isMcpbSource as c,loadMcpbFile as l,loadMcpServerUserConfig as u,validateUserConfig as p}from"./mcpbHandler.js";import{getPluginDataDir as f}from"./pluginDirectories.js";import{getPluginStorageId as m,loadPluginOptions as
|
|
1
|
+
import{join as e}from"path";import{expandEnvVarsInString as r}from"../../services/mcp/envExpansion.js";import{McpServerConfigSchema as n}from"../../services/mcp/types.js";import{logForDebugging as s}from"../debug.js";import{errorMessage as o,isENOENT as t}from"../errors.js";import{getFsImplementation as i}from"../fsOperations.js";import{jsonParse as a}from"../slowOperations.js";import{isMcpbSource as c,loadMcpbFile as l,loadMcpServerUserConfig as u,validateUserConfig as p}from"./mcpbHandler.js";import{getPluginDataDir as f}from"./pluginDirectories.js";import{getPluginStorageId as m,loadPluginOptions as g,substitutePluginVariables as d,substituteUserConfigVariables as v}from"./pluginOptionsStorage.js";async function loadMcpServersFromMcpb(e,r,n){try{s(`Loading MCP servers from MCPB: ${r}`);const n=e.repository,o=await l(r,e.path,n,r=>{s(`MCPB [${e.name}]: ${r}`)});if("status"in o&&"needs-config"===o.status)return s(`MCPB ${r} requires user configuration. User can configure via: /plugin → Manage plugins → ${e.name} → Configure`),null;const t=o,i=t.manifest.name;return s(`Loaded MCP server "${i}" from MCPB (extracted to ${t.extractedPath})`),{[i]:t.mcpConfig}}catch(t){const i=o(t);s(`Failed to load MCPB ${r}: ${i}`,{level:"error"});const a=`${e.name}@${e.repository}`;return r.startsWith("http")&&(i.includes("download")||i.includes("network"))?n.push({type:"mcpb-download-failed",source:a,plugin:e.name,url:r,reason:i}):i.includes("manifest")||i.includes("user configuration")?n.push({type:"mcpb-invalid-manifest",source:a,plugin:e.name,mcpbPath:r,validationError:i}):n.push({type:"mcpb-extract-failed",source:a,plugin:e.name,mcpbPath:r,reason:i}),null}}export async function loadPluginMcpServers(e,r=[]){let n={};const o=await loadMcpServersFromFile(e.path,".mcp.json");if(o&&(n={...n,...o}),e.manifest.mcpServers){const o=e.manifest.mcpServers;if("string"==typeof o)if(c(o)){const s=await loadMcpServersFromMcpb(e,o,r);s&&(n={...n,...s})}else{const r=await loadMcpServersFromFile(e.path,o);r&&(n={...n,...r})}else if(Array.isArray(o)){const t=await Promise.all(o.map(async n=>{try{return"string"==typeof n?c(n)?await loadMcpServersFromMcpb(e,n,r):await loadMcpServersFromFile(e.path,n):n}catch(r){return s(`Failed to load MCP servers from spec for plugin ${e.name}: ${r}`,{level:"error"}),null}}));for(const e of t)e&&(n={...n,...e})}else n={...n,...o}}return Object.keys(n).length>0?n:void 0}async function loadMcpServersFromFile(r,o){const c=i(),l=e(r,o);let u;try{u=await c.readFile(l,{encoding:"utf-8"})}catch(e){return t(e)||s(`Failed to load MCP servers from ${l}: ${e}`,{level:"error"}),null}try{const e=a(u),r=e.mcpServers||e,o={};for(const[e,t]of Object.entries(r)){const r=n().safeParse(t);r.success?o[e]=r.data:s(`Invalid MCP server config for ${e} in ${l}: ${r.error.message}`,{level:"error"})}return o}catch(e){return s(`Failed to load MCP servers from ${l}: ${e}`,{level:"error"}),null}}export function getUnconfiguredChannels(e){const r=e.manifest.channels;if(!r||0===r.length)return[];const n=e.repository,s=[];for(const e of r){if(!e.userConfig||0===Object.keys(e.userConfig).length)continue;const r=u(n,e.server)??{};p(r,e.userConfig).valid||s.push({server:e.server,displayName:e.displayName??e.server,configSchema:e.userConfig})}return s}export function addPluginScopeToServers(e,r,n){const s={};for(const[o,t]of Object.entries(e)){const e={...t,scope:"dynamic",pluginSource:n};s[`plugin:${r}:${o}`]=e}return s}export async function extractMcpServersFromPlugins(e,r=[]){const n={},t=await Promise.all(e.map(async e=>{if(!e.enabled)return null;const n=await loadPluginMcpServers(e,r);if(!n)return null;const t={};for(const[s,i]of Object.entries(n)){const a=buildMcpUserConfig(e,s);try{t[s]=resolvePluginMcpEnvironment(i,e,a,r,e.name,s)}catch(n){r?.push({type:"generic-error",source:s,plugin:e.name,error:o(n)})}}return e.mcpServers=n,s(`Loaded ${Object.keys(n).length} MCP servers from plugin ${e.name}`),addPluginScopeToServers(t,e.name,e.source)}));for(const e of t)e&&Object.assign(n,e);return n}function buildMcpUserConfig(e,r){const n=e.manifest.userConfig?g(m(e)):void 0,s=function(e,r){const n=e.manifest.channels?.find(e=>e.server===r);if(n?.userConfig)return u(e.repository,r)??void 0}(e,r);if(n||s)return{...n,...s}}export function resolvePluginMcpEnvironment(e,n,o,t,i,a){const c=[],resolveValue=e=>{let s=d(e,n);o&&(s=v(s,o));const{expanded:t,missingVars:i}=r(s);return c.push(...i),t};let l;switch(e.type){case void 0:case"stdio":{const r={...e};r.command&&(r.command=resolveValue(r.command)),r.args&&(r.args=r.args.map(e=>resolveValue(e)));const s={CLAUDE_PLUGIN_ROOT:n.path,CLAUDE_PLUGIN_DATA:f(n.source),...r.env||{}};for(const[e,r]of Object.entries(s))"CLAUDE_PLUGIN_ROOT"!==e&&"CLAUDE_PLUGIN_DATA"!==e&&(s[e]=resolveValue(r));r.env=s,l=r;break}case"sse":case"http":case"ws":{const r={...e};if(r.url&&(r.url=resolveValue(r.url)),r.headers){const e={};for(const[n,s]of Object.entries(r.headers))e[n]=resolveValue(s);r.headers=e}l=r;break}case"sse-ide":case"ws-ide":case"sdk":case"claudeai-proxy":l=e}if(t&&c.length>0){const e=[...new Set(c)].join(", ");s(`Missing environment variables in plugin MCP config: ${e}`,{level:"warn"}),i&&a&&t.push({type:"mcp-config-invalid",source:`plugin:${i}`,plugin:i,serverName:a,validationError:`Missing environment variables: ${e}`})}return l}export async function getPluginMcpServers(e,r=[]){if(!e.enabled)return;const n=e.mcpServers||await loadPluginMcpServers(e,r);if(!n)return;const s={};for(const[t,i]of Object.entries(n)){const a=buildMcpUserConfig(e,t);try{s[t]=resolvePluginMcpEnvironment(i,e,a,r,e.name,t)}catch(n){r?.push({type:"generic-error",source:t,plugin:e.name,error:o(n)})}}return addPluginScopeToServers(s,e.name,e.source)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"axios";import{createHash as t}from"crypto";import{chmod as r,writeFile as n}from"fs/promises";import{dirname as a,join as
|
|
1
|
+
import e from"axios";import{createHash as t}from"crypto";import{chmod as r,writeFile as n}from"fs/promises";import{dirname as a,join as s}from"path";import{logForDebugging as o}from"../debug.js";import{parseAndValidateManifestFromBytes as i}from"../dxt/helpers.js";import{parseZipModes as c,unzipFile as f}from"../dxt/zip.js";import{errorMessage as g,getErrnoCode as d,isENOENT as l,toError as u}from"../errors.js";import{getFsImplementation as h}from"../fsOperations.js";import{logError as m}from"../log.js";import{getSecureStorage as p}from"../secureStorage/index.js";import{getSettings_DEPRECATED as w,updateSettingsForSource as C}from"../settings/settings.js";import{jsonParse as $,jsonStringify as v}from"../slowOperations.js";import{getSystemDirectories as y}from"../systemDirectories.js";import{classifyFetchError as b,logPluginFetch as M}from"./fetchTelemetry.js";export function isMcpbSource(e){return e.endsWith(".mcpb")||e.endsWith(".dxt")}function isUrl(e){return e.startsWith("http://")||e.startsWith("https://")}function getMcpbCacheDir(e){return s(e,".mcpb-cache")}function getMetadataPath(e,r){const n=t("md5").update(r).digest("hex").substring(0,8);return s(e,`${n}.metadata.json`)}function serverSecretsKey(e,t){return`${e}/${t}`}export function loadMcpServerUserConfig(e,t){try{const r=w(),n=r.pluginConfigs?.[e]?.mcpServers?.[t],a=p().read()?.pluginSecrets?.[serverSecretsKey(e,t)];return n||a?(o(`Loaded user config for ${e}/${t} (settings + secureStorage)`),{...n,...a}):null}catch(r){const n=u(r);return m(n),o(`Failed to load user config for ${e}/${t}: ${r}`,{level:"error"}),null}}export function saveMcpServerUserConfig(e,t,r,n){try{const a={},s={};for(const[e,t]of Object.entries(r))!0===n[e]?.sensitive?s[e]=String(t):a[e]=t;const i=new Set(Object.keys(s)),c=new Set(Object.keys(a)),f=p(),g=serverSecretsKey(e,t),d=f.read()?.pluginSecrets?.[g]??void 0,l=d?Object.fromEntries(Object.entries(d).filter(([e])=>!c.has(e))):void 0,u=l&&d&&Object.keys(l).length!==Object.keys(d).length;if(Object.keys(s).length>0||u){const e=f.read()??{};e.pluginSecrets||(e.pluginSecrets={}),e.pluginSecrets[g]={...l,...s};const t=f.update(e);if(!t.success)throw new Error(`Failed to save sensitive config to secure storage for ${g}`);t.warning&&o(`Server secrets save warning: ${t.warning}`,{level:"warn"}),u&&o(`saveMcpServerUserConfig: scrubbed ${Object.keys(d).length-Object.keys(l).length} stale non-sensitive key(s) from secureStorage for ${g}`)}const h=w(),m=h.pluginConfigs?.[e]?.mcpServers?.[t]??{},$=Object.keys(m).filter(e=>i.has(e));if(Object.keys(a).length>0||$.length>0){h.pluginConfigs||(h.pluginConfigs={}),h.pluginConfigs[e]||(h.pluginConfigs[e]={}),h.pluginConfigs[e].mcpServers||(h.pluginConfigs[e].mcpServers={});const r=Object.fromEntries($.map(e=>[e,void 0]));h.pluginConfigs[e].mcpServers[t]={...a,...r};const n=C("userSettings",h);if(n.error)throw n.error;$.length>0&&o(`saveMcpServerUserConfig: scrubbed ${$.length} plaintext sensitive key(s) from settings.json for ${e}/${t}`)}o(`Saved user config for ${e}/${t} (${Object.keys(a).length} non-sensitive, ${Object.keys(s).length} sensitive)`)}catch(r){const n=u(r);throw m(n),new Error(`Failed to save user configuration for ${e}/${t}: ${n.message}`)}}export function validateUserConfig(e,t){const r=[];for(const[n,a]of Object.entries(t)){const t=e[n];!a.required||void 0!==t&&""!==t?void 0!==t&&""!==t&&("string"===a.type?Array.isArray(t)?a.multiple?t.every(e=>"string"==typeof e)||r.push(`${a.title||n} must be an array of strings`):r.push(`${a.title||n} must be a string, not an array`):"string"!=typeof t&&r.push(`${a.title||n} must be a string`):"number"===a.type&&"number"!=typeof t?r.push(`${a.title||n} must be a number`):"boolean"===a.type&&"boolean"!=typeof t?r.push(`${a.title||n} must be a boolean`):"file"!==a.type&&"directory"!==a.type||"string"==typeof t||r.push(`${a.title||n} must be a path string`),"number"===a.type&&"number"==typeof t&&(void 0!==a.min&&t<a.min&&r.push(`${a.title||n} must be at least ${a.min}`),void 0!==a.max&&t>a.max&&r.push(`${a.title||n} must be at most ${a.max}`))):r.push(`${a.title||n} is required but not provided`)}return{valid:0===r.length,errors:r}}async function generateMcpConfig(e,t,r={}){const{getMcpConfigForManifest:n}=await import("@anthropic-ai/mcpb"),a=await n({manifest:e,extensionPath:t,systemDirs:y(),userConfig:r,pathSeparator:"/"});if(!a){const t=new Error(`Failed to generate MCP server configuration from manifest "${e.name}"`);throw m(t),t}return a}async function loadCacheMetadata(e,t){const r=h(),n=getMetadataPath(e,t);try{const e=await r.readFile(n,{encoding:"utf-8"});return $(e)}catch(e){if("ENOENT"===d(e))return null;const t=u(e);return m(t),o(`Failed to load MCPB cache metadata: ${e}`,{level:"error"}),null}}async function saveCacheMetadata(e,t,r){const a=getMetadataPath(e,t);await h().mkdir(e),await n(a,v(r,null,2),"utf-8")}export async function checkMcpbChanged(e,t){const r=h(),n=getMcpbCacheDir(t),a=await loadCacheMetadata(n,e);if(!a)return!0;try{await r.stat(a.extractedPath)}catch(e){return"ENOENT"===d(e)?o(`MCPB extraction path missing: ${a.extractedPath}`):o(`MCPB extraction path inaccessible: ${a.extractedPath}: ${e}`,{level:"error"}),!0}if(!isUrl(e)){const n=s(t,e);let i;try{i=await r.stat(n)}catch(e){return"ENOENT"===d(e)?o(`MCPB source file missing: ${n}`):o(`MCPB source file inaccessible: ${n}: ${e}`,{level:"error"}),!0}const c=new Date(a.cachedAt).getTime(),f=Math.floor(i.mtimeMs);if(f>c)return o(`MCPB file modified: ${new Date(f)} > ${new Date(c)}`),!0}return!1}export async function loadMcpbFile(d,u,p,w,C,$){const v=h(),y=getMcpbCacheDir(u);await v.mkdir(y),o(`Loading MCPB from source: ${d}`);const S=await loadCacheMetadata(y,d);if(S&&!await checkMcpbChanged(d,u)){o(`Using cached MCPB from ${S.extractedPath} (hash: ${S.contentHash})`);const e=s(S.extractedPath,"manifest.json");let t;try{t=await v.readFile(e,{encoding:"utf-8"})}catch(t){if(l(t)){const t=new Error(`Cached manifest not found: ${e}`);throw m(t),t}throw t}const r=(new TextEncoder).encode(t),n=await i(r);if(n.user_config&&Object.keys(n.user_config).length>0){const e=n.name,t=loadMcpServerUserConfig(p,e),r=C||t||{},a=validateUserConfig(r,n.user_config);return $||!a.valid?{status:"needs-config",manifest:n,extractedPath:S.extractedPath,contentHash:S.contentHash,configSchema:n.user_config,existingConfig:t||{},validationErrors:a.valid?[]:a.errors}:(C&&saveMcpServerUserConfig(p,e,C,n.user_config??{}),{manifest:n,mcpConfig:await generateMcpConfig(n,S.extractedPath,r),extractedPath:S.extractedPath,contentHash:S.contentHash})}return{manifest:n,mcpConfig:await generateMcpConfig(n,S.extractedPath),extractedPath:S.extractedPath,contentHash:S.contentHash}}let x,P;if(isUrl(d)){const r=t("md5").update(d).digest("hex").substring(0,8);P=s(y,`${r}.mcpb`),x=await async function(t,r,a){o(`Downloading MCPB from ${t}`),a&&a(`Downloading ${t}...`);const s=performance.now();let i=!1;try{const c=await e.get(t,{timeout:12e4,responseType:"arraybuffer",maxRedirects:5,onDownloadProgress:e=>{if(e.total&&a){const t=Math.round(e.loaded/e.total*100);a(`Downloading... ${t}%`)}}}),f=new Uint8Array(c.data);return M("mcpb",t,"success",performance.now()-s),i=!0,await n(r,Buffer.from(f)),o(`Downloaded ${f.length} bytes to ${r}`),a&&a("Download complete"),f}catch(e){i||M("mcpb",t,"failure",performance.now()-s,b(e));const r=g(e),n=new Error(`Failed to download MCPB file from ${t}: ${r}`);throw m(n),n}}(d,P,w)}else{const e=s(u,d);w&&w(`Loading ${d}...`);try{x=await v.readFileBytes(e),P=e}catch(t){if(l(t)){const t=new Error(`MCPB file not found: ${e}`);throw m(t),t}throw t}}const j=(E=x,t("sha256").update(E).digest("hex").substring(0,16));var E;o(`MCPB content hash: ${j}`),w&&w("Extracting MCPB archive...");const O=await f(Buffer.from(x)),D=c(x),k=O["manifest.json"];if(!k){const e=new Error("No manifest.json found in MCPB file");throw m(e),e}const B=await i(k);if(o(`MCPB manifest: ${B.name} v${B.version} by ${B.author.name}`),!B.server){const e=new Error(`MCPB manifest for "${B.name}" does not define a server configuration`);throw m(e),e}const F=s(y,j);if(await async function(e,t,i,c){c&&c("Extracting files..."),await h().mkdir(t);let f=0;const g=Object.entries(e).filter(([e])=>!e.endsWith("/")),d=g.length;for(const[e,o]of g){const g=s(t,e),l=a(g);if(l!==t&&await h().mkdir(l),e.endsWith(".json")||e.endsWith(".js")||e.endsWith(".ts")||e.endsWith(".txt")||e.endsWith(".md")||e.endsWith(".yml")||e.endsWith(".yaml")){const e=(new TextDecoder).decode(o);await n(g,e,"utf-8")}else await n(g,Buffer.from(o));const u=i[e];u&&73&u&&await r(g,511&u).catch(()=>{}),f++,c&&f%10==0&&c(`Extracted ${f}/${d} files`)}o(`Extracted ${f} files to ${t}`),c&&c(`Extraction complete (${f} files)`)}(O,F,D,w),B.user_config&&Object.keys(B.user_config).length>0){const e=B.name,t=loadMcpServerUserConfig(p,e),r=C||t||{},n=validateUserConfig(r,B.user_config);if(!n.valid){const e={source:d,contentHash:j,extractedPath:F,cachedAt:(new Date).toISOString(),lastChecked:(new Date).toISOString()};return await saveCacheMetadata(y,d,e),{status:"needs-config",manifest:B,extractedPath:F,contentHash:j,configSchema:B.user_config,existingConfig:t||{},validationErrors:n.errors}}C&&saveMcpServerUserConfig(p,e,C,B.user_config??{}),w&&w("Generating MCP server configuration...");const a=await generateMcpConfig(B,F,r),s={source:d,contentHash:j,extractedPath:F,cachedAt:(new Date).toISOString(),lastChecked:(new Date).toISOString()};return await saveCacheMetadata(y,d,s),{manifest:B,mcpConfig:a,extractedPath:F,contentHash:j}}w&&w("Generating MCP server configuration...");const U=await generateMcpConfig(B,F),H={source:d,contentHash:j,extractedPath:F,cachedAt:(new Date).toISOString(),lastChecked:(new Date).toISOString()};return await saveCacheMetadata(y,d,H),o(`Successfully loaded MCPB: ${B.name} (extracted to ${F})`),{manifest:B,mcpConfig:U,extractedPath:F,contentHash:j}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import e from"axios";import{chmod as t,mkdir as r,readFile as s,rename as a,rm as i,writeFile as o}from"fs/promises";import{dirname as n,join as c,resolve as l,sep as f}from"path";import{waitForScrollIdle as p}from"../../bootstrap/state.js";import{logEvent as u}from"../../services/analytics/index.js";import{logForDebugging as m}from"../debug.js";import{parseZipModes as d,unzipFile as h}from"../dxt/zip.js";import{errorMessage as E,getErrnoCode as w}from"../errors.js";const g="https://downloads.claude.ai/claude-code-releases/plugins/claude-plugins-official";export async function fetchOfficialMarketplaceFromGcs(w,y){const _=l(y),O=l(w);if(O!==_&&!O.startsWith(_+f))return m(`fetchOfficialMarketplaceFromGcs: refusing path outside cache dir: ${w}`,{level:"error"}),null;await p();const v=performance.now();let b,F,$,k="failed";try{const l=await e.get(`${g}/latest`,{responseType:"text",timeout:1e4});if(b=String(l.data).trim(),!b)throw new Error("latest pointer returned empty body");const f=c(w,".gcs-sha");if(await s(f,"utf8").then(e=>e.trim(),()=>null)===b)return k="noop",b;const p=await e.get(`${g}/${b}.zip`,{responseType:"arraybuffer",timeout:6e4}),u=Buffer.from(p.data);F=u.length;const m=await h(u),E=d(u),y=`${w}.staging`;await i(y,{recursive:!0,force:!0}),await r(y,{recursive:!0});for(const[e,s]of Object.entries(m)){if(!e.startsWith("marketplaces/claude-plugins-official/"))continue;const a=e.slice(37);if(!a||a.endsWith("/"))continue;const i=c(y,a);await r(n(i),{recursive:!0}),await o(i,s);const l=E[e];l&&73&l&&await t(i,511&l).catch(()=>{})}return await o(c(y,".gcs-sha"),b),await i(w,{recursive:!0,force:!0}),await a(y,w),k="updated",b}catch(e){return $=classifyGcsError(e),m(`Official marketplace GCS fetch failed: ${E(e)}`,{level:"warn"}),null}finally{u("tengu_plugin_remote_fetch",{source:"marketplace_gcs",host:"downloads.claude.ai",is_official:!0,outcome:k,duration_ms:Math.round(performance.now()-v),...void 0!==F&&{bytes:F},...b&&{sha:b},...$&&{error_kind:$}})}}const y=new Set(["ENOSPC","EACCES","EPERM","EXDEV","EBUSY","ENOENT","ENOTDIR","EROFS","EMFILE","ENAMETOOLONG"]);export function classifyGcsError(t){if(e.isAxiosError(t))return"ECONNABORTED"===t.code?"timeout":t.response?`http_${t.response.status}`:"network";const r=w(t);if(r&&/^E[A-Z]+$/.test(r)&&!r.startsWith("ERR_"))return y.has(r)?`fs_${r}`:"fs_other";if("number"==typeof t?.code)return"zip_parse";const s=E(t);return/unzip|invalid zip|central directory/i.test(s)?"zip_parse":/empty body/.test(s)?"empty_latest":"other"}
|