From 7b5a1d987d45873d90d6bb4923bac63532091448 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Tue, 17 Jun 2025 14:00:27 -0400 Subject: [PATCH] Turn on marketplace (#4788) --- packages/types/src/experiment.ts | 3 +- src/core/webview/ClineProvider.ts | 28 +------ src/core/webview/webviewMessageHandler.ts | 28 ------- src/package.json | 4 +- .../marketplace-setting-check.spec.ts | 41 +++------- src/shared/__tests__/experiments.spec.ts | 41 ---------- src/shared/experiments.ts | 2 - webview-ui/src/App.tsx | 15 +--- webview-ui/src/__tests__/App.test.tsx | 81 +++++-------------- .../settings/ExperimentalSettings.tsx | 2 +- 10 files changed, 43 insertions(+), 202 deletions(-) diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts index dfb7cca1d5..e48aeab4f5 100644 --- a/packages/types/src/experiment.ts +++ b/packages/types/src/experiment.ts @@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js" * ExperimentId */ -export const experimentIds = ["powerSteering", "disableCompletionCommand", "marketplace", "multiFileApplyDiff"] as const +export const experimentIds = ["powerSteering", "disableCompletionCommand", "multiFileApplyDiff"] as const export const experimentIdsSchema = z.enum(experimentIds) @@ -19,7 +19,6 @@ export type ExperimentId = z.infer export const experimentsSchema = z.object({ powerSteering: z.boolean().optional(), disableCompletionCommand: z.boolean().optional(), - marketplace: z.boolean().optional(), multiFileApplyDiff: z.boolean().optional(), }) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 7b3589c3b5..94264ba0de 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -502,9 +502,6 @@ export class ClineProvider // If the extension is starting a new session, clear previous task state. await this.removeClineFromStack() - // Set initial VSCode context for experiments - await this.updateVSCodeContext() - this.log("Webview view resolved") } @@ -1245,29 +1242,12 @@ export class ClineProvider const state = await this.getStateToPostToWebview() this.postMessageToWebview({ type: "state", state }) - // Update VSCode context for experiments - await this.updateVSCodeContext() - // Check MDM compliance and send user to account tab if not compliant if (!this.checkMdmCompliance()) { await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" }) } } - /** - * Updates VSCode context variables for experiments so they can be used in when clauses - */ - private async updateVSCodeContext() { - const { experiments } = await this.getState() - - // Set context for marketplace experiment - await vscode.commands.executeCommand( - "setContext", - `${Package.name}.marketplaceEnabled`, - experiments.marketplace ?? false, - ) - } - /** * Checks if there is a file-based system prompt override for the given mode */ @@ -1356,14 +1336,12 @@ export class ClineProvider const allowedCommands = vscode.workspace.getConfiguration(Package.name).get("allowedCommands") || [] const cwd = this.cwd - // Only fetch marketplace data if the feature is enabled + // Fetch marketplace data let marketplaceItems: any[] = [] let marketplaceInstalledMetadata: any = { project: {}, global: {} } - if (experiments.marketplace) { - marketplaceItems = (await this.marketplaceManager.getCurrentItems()) || [] - marketplaceInstalledMetadata = await this.marketplaceManager.getInstallationMetadata() - } + marketplaceItems = (await this.marketplaceManager.getCurrentItems()) || [] + marketplaceInstalledMetadata = await this.marketplaceManager.getInstallationMetadata() // Check if there's a system prompt override for the current mode const currentMode = mode ?? defaultModeSlug diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index a4d9dafecf..713d703b6d 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1465,13 +1465,6 @@ export const webviewMessageHandler = async ( break } case "filterMarketplaceItems": { - // Check if marketplace is enabled before making API calls - const { experiments } = await provider.getState() - if (!experiments.marketplace) { - console.log("Marketplace: Feature disabled, skipping API call") - break - } - if (marketplaceManager && message.filters) { try { await marketplaceManager.updateWithFilteredItems({ @@ -1489,13 +1482,6 @@ export const webviewMessageHandler = async ( } case "installMarketplaceItem": { - // Check if marketplace is enabled before installing - const { experiments } = await provider.getState() - if (!experiments.marketplace) { - console.log("Marketplace: Feature disabled, skipping installation") - break - } - if (marketplaceManager && message.mpItem && message.mpInstallOptions) { try { const configFilePath = await marketplaceManager.installMarketplaceItem( @@ -1525,13 +1511,6 @@ export const webviewMessageHandler = async ( } case "removeInstalledMarketplaceItem": { - // Check if marketplace is enabled before removing - const { experiments } = await provider.getState() - if (!experiments.marketplace) { - console.log("Marketplace: Feature disabled, skipping removal") - break - } - if (marketplaceManager && message.mpItem && message.mpInstallOptions) { try { await marketplaceManager.removeInstalledMarketplaceItem(message.mpItem, message.mpInstallOptions) @@ -1544,13 +1523,6 @@ export const webviewMessageHandler = async ( } case "installMarketplaceItemWithParameters": { - // Check if marketplace is enabled before installing with parameters - const { experiments } = await provider.getState() - if (!experiments.marketplace) { - console.log("Marketplace: Feature disabled, skipping installation with parameters") - break - } - if (marketplaceManager && message.payload && "item" in message.payload && "parameters" in message.payload) { try { const configFilePath = await marketplaceManager.installMarketplaceItem(message.payload.item, { diff --git a/src/package.json b/src/package.json index 51822f4361..3e76a598bf 100644 --- a/src/package.json +++ b/src/package.json @@ -232,7 +232,7 @@ { "command": "roo-cline.marketplaceButtonClicked", "group": "navigation@4", - "when": "view == roo-cline.SidebarProvider && roo-cline.marketplaceEnabled" + "when": "view == roo-cline.SidebarProvider" }, { "command": "roo-cline.historyButtonClicked", @@ -274,7 +274,7 @@ { "command": "roo-cline.marketplaceButtonClicked", "group": "navigation@4", - "when": "activeWebviewPanelId == roo-cline.TabPanelProvider && roo-cline.marketplaceEnabled" + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" }, { "command": "roo-cline.historyButtonClicked", diff --git a/src/services/marketplace/__tests__/marketplace-setting-check.spec.ts b/src/services/marketplace/__tests__/marketplace-setting-check.spec.ts index c80efe1e7f..87f49c5fab 100644 --- a/src/services/marketplace/__tests__/marketplace-setting-check.spec.ts +++ b/src/services/marketplace/__tests__/marketplace-setting-check.spec.ts @@ -6,21 +6,22 @@ import { webviewMessageHandler } from "../../../core/webview/webviewMessageHandl const mockProvider = { getState: vi.fn(), postStateToWebview: vi.fn(), + postMessageToWebview: vi.fn(), } as any const mockMarketplaceManager = { updateWithFilteredItems: vi.fn(), } as any -describe("Marketplace Setting Check", () => { +describe("Marketplace General Availability", () => { beforeEach(() => { vi.clearAllMocks() }) - it("should skip API calls when marketplace is disabled", async () => { - // Mock experiments with marketplace disabled + it("should allow marketplace API calls (marketplace is generally available)", async () => { + // Mock state without marketplace experiment (since it's now generally available) mockProvider.getState.mockResolvedValue({ - experiments: { marketplace: false }, + experiments: {}, }) const message = { @@ -30,25 +31,7 @@ describe("Marketplace Setting Check", () => { await webviewMessageHandler(mockProvider, message, mockMarketplaceManager) - // Should not call marketplace manager methods - expect(mockMarketplaceManager.updateWithFilteredItems).not.toHaveBeenCalled() - expect(mockProvider.postStateToWebview).not.toHaveBeenCalled() - }) - - it("should allow API calls when marketplace is enabled", async () => { - // Mock experiments with marketplace enabled - mockProvider.getState.mockResolvedValue({ - experiments: { marketplace: true }, - }) - - const message = { - type: "filterMarketplaceItems" as const, - filters: { type: "mcp", search: "", tags: [] }, - } - - await webviewMessageHandler(mockProvider, message, mockMarketplaceManager) - - // Should call marketplace manager methods + // Should call marketplace manager methods since marketplace is generally available expect(mockMarketplaceManager.updateWithFilteredItems).toHaveBeenCalledWith({ type: "mcp", search: "", @@ -57,13 +40,13 @@ describe("Marketplace Setting Check", () => { expect(mockProvider.postStateToWebview).toHaveBeenCalled() }) - it("should skip installation when marketplace is disabled", async () => { - // Mock experiments with marketplace disabled + it("should allow marketplace installation (marketplace is generally available)", async () => { + // Mock state without marketplace experiment (since it's now generally available) mockProvider.getState.mockResolvedValue({ - experiments: { marketplace: false }, + experiments: {}, }) - const mockInstallMarketplaceItem = vi.fn() + const mockInstallMarketplaceItem = vi.fn().mockResolvedValue(undefined) const mockMarketplaceManagerWithInstall = { installMarketplaceItem: mockInstallMarketplaceItem, } @@ -83,7 +66,7 @@ describe("Marketplace Setting Check", () => { await webviewMessageHandler(mockProvider, message, mockMarketplaceManagerWithInstall as any) - // Should not call install method - expect(mockInstallMarketplaceItem).not.toHaveBeenCalled() + // Should call install method since marketplace is generally available + expect(mockInstallMarketplaceItem).toHaveBeenCalledWith(message.mpItem, message.mpInstallOptions) }) }) diff --git a/src/shared/__tests__/experiments.spec.ts b/src/shared/__tests__/experiments.spec.ts index cc79e30ef4..3d63d9a97f 100644 --- a/src/shared/__tests__/experiments.spec.ts +++ b/src/shared/__tests__/experiments.spec.ts @@ -27,7 +27,6 @@ describe("experiments", () => { it("returns false when POWER_STEERING experiment is not enabled", () => { const experiments: Record = { powerSteering: false, - marketplace: false, disableCompletionCommand: false, multiFileApplyDiff: false, } @@ -37,7 +36,6 @@ describe("experiments", () => { it("returns true when experiment POWER_STEERING is enabled", () => { const experiments: Record = { powerSteering: true, - marketplace: false, disableCompletionCommand: false, multiFileApplyDiff: false, } @@ -47,49 +45,10 @@ describe("experiments", () => { it("returns false when experiment is not present", () => { const experiments: Record = { powerSteering: false, - marketplace: false, disableCompletionCommand: false, multiFileApplyDiff: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) }) - describe("MARKETPLACE", () => { - it("is configured correctly", () => { - expect(EXPERIMENT_IDS.MARKETPLACE).toBe("marketplace") - expect(experimentConfigsMap.MARKETPLACE).toMatchObject({ - enabled: false, - }) - }) - }) - - describe("isEnabled for MARKETPLACE", () => { - it("returns false when MARKETPLACE experiment is not enabled", () => { - const experiments: Record = { - powerSteering: false, - marketplace: false, - disableCompletionCommand: false, - multiFileApplyDiff: false, - } - expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(false) - }) - - it("returns true when MARKETPLACE experiment is enabled", () => { - const experiments: Record = { - powerSteering: false, - marketplace: true, - disableCompletionCommand: false, - multiFileApplyDiff: false, - } - expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(true) - }) - - it("returns false when MARKETPLACE experiment is not present", () => { - const experiments: Record = { - powerSteering: false, - // marketplace missing - } as any - expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(false) - }) - }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 8e71e71ff0..adf8231cb0 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -1,7 +1,6 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId, Experiments } from "@roo-code/types" export const EXPERIMENT_IDS = { - MARKETPLACE: "marketplace", MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff", DISABLE_COMPLETION_COMMAND: "disableCompletionCommand", POWER_STEERING: "powerSteering", @@ -16,7 +15,6 @@ interface ExperimentConfig { } export const experimentConfigsMap: Record = { - MARKETPLACE: { enabled: false }, MULTI_FILE_APPLY_DIFF: { enabled: false }, DISABLE_COMPLETION_COMMAND: { enabled: false }, POWER_STEERING: { enabled: false }, diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index e8e321d920..f412fcbe8f 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -40,7 +40,6 @@ const App = () => { telemetrySetting, telemetryKey, machineId, - experiments, cloudUserInfo, cloudIsAuthenticated, mdmCompliant, @@ -93,10 +92,6 @@ const App = () => { // Handle switchTab action with tab parameter if (message.action === "switchTab" && message.tab) { const targetTab = message.tab as Tab - // Don't switch to marketplace tab if the experiment is disabled - if (targetTab === "marketplace" && !experiments.marketplace) { - return - } switchTab(targetTab) setCurrentSection(undefined) } else { @@ -105,10 +100,6 @@ const App = () => { const section = message.values?.section as string | undefined if (newTab) { - // Don't switch to marketplace tab if the experiment is disabled - if (newTab === "marketplace" && !experiments.marketplace) { - return - } switchTab(newTab) setCurrentSection(section) } @@ -124,7 +115,7 @@ const App = () => { chatViewRef.current?.acceptInput() } }, - [switchTab, experiments], + [switchTab], ) useEvent("message", onMessage) @@ -147,10 +138,10 @@ const App = () => { // Track marketplace tab views useEffect(() => { - if (tab === "marketplace" && experiments.marketplace) { + if (tab === "marketplace") { telemetryClient.capture(TelemetryEventName.MARKETPLACE_TAB_VIEWED) } - }, [tab, experiments.marketplace]) + }, [tab]) if (!didHydrateState) { return null diff --git a/webview-ui/src/__tests__/App.test.tsx b/webview-ui/src/__tests__/App.test.tsx index 5718e79e92..6d9859a4d8 100644 --- a/webview-ui/src/__tests__/App.test.tsx +++ b/webview-ui/src/__tests__/App.test.tsx @@ -104,7 +104,7 @@ describe("App", () => { didHydrateState: true, showWelcome: false, shouldShowAnnouncement: false, - experiments: { marketplace: false }, + experiments: {}, language: "en", }) }) @@ -224,74 +224,35 @@ describe("App", () => { expect(screen.queryByTestId(`${view}-view`)).not.toBeInTheDocument() }) - describe("marketplace experiment", () => { - it("does not switch to marketplace tab when experiment is disabled", async () => { - mockUseExtensionState.mockReturnValue({ - didHydrateState: true, - showWelcome: false, - shouldShowAnnouncement: false, - experiments: { marketplace: false }, - language: "en", - }) + it("switches to marketplace view when receiving marketplaceButtonClicked action", async () => { + render() - render() - - act(() => { - triggerMessage("marketplaceButtonClicked") - }) - - // Should remain on chat view - const chatView = screen.getByTestId("chat-view") - expect(chatView.getAttribute("data-hidden")).toBe("false") - expect(screen.queryByTestId("marketplace-view")).not.toBeInTheDocument() + act(() => { + triggerMessage("marketplaceButtonClicked") }) - it("switches to marketplace tab when experiment is enabled", async () => { - mockUseExtensionState.mockReturnValue({ - didHydrateState: true, - showWelcome: false, - shouldShowAnnouncement: false, - experiments: { marketplace: true }, - language: "en", - }) + const marketplaceView = await screen.findByTestId("marketplace-view") + expect(marketplaceView).toBeInTheDocument() - render() + const chatView = screen.getByTestId("chat-view") + expect(chatView.getAttribute("data-hidden")).toBe("true") + }) - act(() => { - triggerMessage("marketplaceButtonClicked") - }) + it("returns to chat view when clicking done in marketplace view", async () => { + render() - const marketplaceView = await screen.findByTestId("marketplace-view") - expect(marketplaceView).toBeInTheDocument() - - const chatView = screen.getByTestId("chat-view") - expect(chatView.getAttribute("data-hidden")).toBe("true") + act(() => { + triggerMessage("marketplaceButtonClicked") }) - it("returns to chat view when clicking done in marketplace view", async () => { - mockUseExtensionState.mockReturnValue({ - didHydrateState: true, - showWelcome: false, - shouldShowAnnouncement: false, - experiments: { marketplace: true }, - language: "en", - }) + const marketplaceView = await screen.findByTestId("marketplace-view") - render() - - act(() => { - triggerMessage("marketplaceButtonClicked") - }) - - const marketplaceView = await screen.findByTestId("marketplace-view") - - act(() => { - marketplaceView.click() - }) - - const chatView = screen.getByTestId("chat-view") - expect(chatView.getAttribute("data-hidden")).toBe("false") - expect(screen.queryByTestId("marketplace-view")).not.toBeInTheDocument() + act(() => { + marketplaceView.click() }) + + const chatView = screen.getByTestId("chat-view") + expect(chatView.getAttribute("data-hidden")).toBe("false") + expect(screen.queryByTestId("marketplace-view")).not.toBeInTheDocument() }) }) diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 9c84318ffa..79d8afefb2 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -52,7 +52,7 @@ export const ExperimentalSettings = ({
{Object.entries(experimentConfigsMap) - .filter((config) => config[0] !== "DIFF_STRATEGY" && config[0] !== "MULTI_SEARCH_AND_REPLACE") + .filter(([key]) => key in EXPERIMENT_IDS) .map((config) => { if (config[0] === "MULTI_FILE_APPLY_DIFF") { return (