diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2847df1a15..0d952b6aebd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -563,8 +563,8 @@ importers: specifier: ^1.14.0 version: 1.14.0(typescript@5.8.3) '@roo-code/cloud': - specifier: ^0.4.0 - version: 0.4.0 + specifier: ^0.5.0 + version: 0.5.0 '@roo-code/ipc': specifier: workspace:^ version: link:../packages/ipc @@ -3065,11 +3065,11 @@ packages: cpu: [x64] os: [win32] - '@roo-code/cloud@0.4.0': - resolution: {integrity: sha512-1a27RG2YjQFfsU5UlfbQnpj/K/6gYBcysp2FXaX9+VaaTh5ZzReQeHJ9uREnyE059zoFpVuNywwNxGadzyotWw==} + '@roo-code/cloud@0.5.0': + resolution: {integrity: sha512-4u6Ce2Rmr5a9nxhjGUMRRWUWhZc63EmF/UJ/+Az5/1JARMOp0kHN5Pwqz2QAgfD137+TFSBKQORpiN0GXrdt2w==} - '@roo-code/types@1.42.0': - resolution: {integrity: sha512-AITVSV6WFd17jE8lQXFy7PkHam8M+mMkT7o9ipGZZ3cV7SbrnmL/Hg/HjkA9lkdJYbcC5dEK94py8KVBQn8Umw==} + '@roo-code/types@1.44.0': + resolution: {integrity: sha512-3xbW4pYaCgWuHF5qOsiXpIcd281dlFTe1zboUGgcUUsB414Hu3pQI86PdgJxVGtZgxtaca0eHTQ2Sqjqq8nPlA==} '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -6267,8 +6267,8 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - ioredis@5.7.0: - resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==} + ioredis@5.6.1: + resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} engines: {node: '>=12.22.0'} ip-address@9.0.5: @@ -12191,16 +12191,18 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@roo-code/cloud@0.4.0': + '@roo-code/cloud@0.5.0': dependencies: - '@roo-code/types': 1.42.0 - ioredis: 5.7.0 + '@roo-code/types': 1.44.0 + ioredis: 5.6.1 p-wait-for: 5.0.2 zod: 3.25.76 transitivePeerDependencies: - supports-color - '@roo-code/types@1.42.0': {} + '@roo-code/types@1.44.0': + dependencies: + zod: 3.25.76 '@sec-ant/readable-stream@0.4.1': {} @@ -15963,7 +15965,7 @@ snapshots: internmap@2.0.3: {} - ioredis@5.7.0: + ioredis@5.6.1: dependencies: '@ioredis/commands': 1.3.0 cluster-key-slot: 1.1.2 diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 3cb6abe7f73..e0c332d16fe 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -32,7 +32,7 @@ import { isBlockingAsk, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" -import { CloudService } from "@roo-code/cloud" +import { CloudService, TaskBridgeService } from "@roo-code/cloud" // api import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api" @@ -118,6 +118,7 @@ export type TaskOptions = { parentTask?: Task taskNumber?: number onCreated?: (task: Task) => void + enableTaskBridge?: boolean } export class Task extends EventEmitter implements TaskLike { @@ -237,6 +238,9 @@ export class Task extends EventEmitter implements TaskLike { checkpointService?: RepoPerTaskCheckpointService checkpointServiceInitializing = false + // Task Bridge + taskBridgeService?: TaskBridgeService + // Streaming isWaitingForFirstChunk = false isStreaming = false @@ -268,6 +272,7 @@ export class Task extends EventEmitter implements TaskLike { parentTask, taskNumber = -1, onCreated, + enableTaskBridge = false, }: TaskOptions) { super() @@ -345,6 +350,11 @@ export class Task extends EventEmitter implements TaskLike { this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit) + // Initialize TaskBridgeService only if enabled + if (enableTaskBridge) { + this.taskBridgeService = TaskBridgeService.getInstance() + } + onCreated?.(this) if (startTask) { @@ -931,6 +941,11 @@ export class Task extends EventEmitter implements TaskLike { // Start / Abort / Resume private async startTask(task?: string, images?: string[]): Promise { + if (this.taskBridgeService) { + await this.taskBridgeService.initialize() + await this.taskBridgeService.subscribeToTask(this) + } + // `conversationHistory` (for API) and `clineMessages` (for webview) // need to be in sync. // If the extension process were killed, then on restart the @@ -982,6 +997,11 @@ export class Task extends EventEmitter implements TaskLike { } private async resumeTaskFromHistory() { + if (this.taskBridgeService) { + await this.taskBridgeService.initialize() + await this.taskBridgeService.subscribeToTask(this) + } + const modifiedClineMessages = await this.getSavedClineMessages() // Remove any resume messages that may have been added before @@ -1227,6 +1247,13 @@ export class Task extends EventEmitter implements TaskLike { this.pauseInterval = undefined } + // Unsubscribe from TaskBridge service. + if (this.taskBridgeService) { + this.taskBridgeService + .unsubscribeFromTask(this.taskId) + .catch((error) => console.error("Error unsubscribing from task bridge:", error)) + } + // Release any terminals associated with this task. try { // Release any terminals associated with this task. diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index ed8f8a27d13..384de58be7b 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -17,7 +17,6 @@ import { type ProviderSettings, type RooCodeSettings, type ProviderSettingsEntry, - type ProviderSettingsWithId, type TelemetryProperties, type TelemetryPropertiesProvider, type CodeActionId, @@ -66,6 +65,7 @@ import { fileExistsAtPath } from "../../utils/fs" import { setTtsEnabled, setTtsSpeed } from "../../utils/tts" import { getWorkspaceGitInfo } from "../../utils/git" import { getWorkspacePath } from "../../utils/path" +import { isRemoteControlEnabled } from "../../utils/remoteControl" import { setPanel } from "../../activate/registerCommands" @@ -111,6 +111,8 @@ export class ClineProvider protected mcpHub?: McpHub // Change from private to protected private marketplaceManager: MarketplaceManager private mdmService?: MdmService + private taskCreationCallback: (task: Task) => void + private taskEventListeners: WeakMap void>> = new WeakMap() public isViewLaunched = false public settingsImportedAt?: number @@ -162,6 +164,40 @@ export class ClineProvider this.marketplaceManager = new MarketplaceManager(this.context, this.customModesManager) + this.taskCreationCallback = (instance: Task) => { + this.emit(RooCodeEventName.TaskCreated, instance) + + // Create named listener functions so we can remove them later. + const onTaskStarted = () => this.emit(RooCodeEventName.TaskStarted, instance.taskId) + const onTaskCompleted = (taskId: string, tokenUsage: any, toolUsage: any) => + this.emit(RooCodeEventName.TaskCompleted, taskId, tokenUsage, toolUsage) + const onTaskAborted = () => this.emit(RooCodeEventName.TaskAborted, instance.taskId) + const onTaskFocused = () => this.emit(RooCodeEventName.TaskFocused, instance.taskId) + const onTaskUnfocused = () => this.emit(RooCodeEventName.TaskUnfocused, instance.taskId) + const onTaskActive = (taskId: string) => this.emit(RooCodeEventName.TaskActive, taskId) + const onTaskIdle = (taskId: string) => this.emit(RooCodeEventName.TaskIdle, taskId) + + // Attach the listeners. + instance.on(RooCodeEventName.TaskStarted, onTaskStarted) + instance.on(RooCodeEventName.TaskCompleted, onTaskCompleted) + instance.on(RooCodeEventName.TaskAborted, onTaskAborted) + instance.on(RooCodeEventName.TaskFocused, onTaskFocused) + instance.on(RooCodeEventName.TaskUnfocused, onTaskUnfocused) + instance.on(RooCodeEventName.TaskActive, onTaskActive) + instance.on(RooCodeEventName.TaskIdle, onTaskIdle) + + // Store the cleanup functions for later removal. + this.taskEventListeners.set(instance, [ + () => instance.off(RooCodeEventName.TaskStarted, onTaskStarted), + () => instance.off(RooCodeEventName.TaskCompleted, onTaskCompleted), + () => instance.off(RooCodeEventName.TaskAborted, onTaskAborted), + () => instance.off(RooCodeEventName.TaskFocused, onTaskFocused), + () => instance.off(RooCodeEventName.TaskUnfocused, onTaskUnfocused), + () => instance.off(RooCodeEventName.TaskActive, onTaskActive), + () => instance.off(RooCodeEventName.TaskIdle, onTaskIdle), + ]) + } + // Initialize Roo Code Cloud profile sync. this.initializeCloudProfileSync().catch((error) => { this.log(`Failed to initialize cloud profile sync: ${error}`) @@ -297,6 +333,14 @@ export class ClineProvider task.emit(RooCodeEventName.TaskUnfocused) + // Remove event listeners before clearing the reference. + const cleanupFunctions = this.taskEventListeners.get(task) + + if (cleanupFunctions) { + cleanupFunctions.forEach((cleanup) => cleanup()) + this.taskEventListeners.delete(task) + } + // Make sure no reference kept, once promises end it will be // garbage collected. task = undefined @@ -654,12 +698,17 @@ export class ClineProvider enableCheckpoints, fuzzyMatchThreshold, experiments, + cloudUserInfo, + remoteControlEnabled, } = await this.getState() if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) { throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist")) } + // Determine if TaskBridge should be enabled + const enableTaskBridge = isRemoteControlEnabled(cloudUserInfo, remoteControlEnabled) + const task = new Task({ provider: this, apiConfiguration, @@ -673,7 +722,8 @@ export class ClineProvider rootTask: this.clineStack.length > 0 ? this.clineStack[0] : undefined, parentTask, taskNumber: this.clineStack.length + 1, - onCreated: (instance) => this.emit(RooCodeEventName.TaskCreated, instance), + onCreated: this.taskCreationCallback, + enableTaskBridge, ...options, }) @@ -738,8 +788,13 @@ export class ClineProvider enableCheckpoints, fuzzyMatchThreshold, experiments, + cloudUserInfo, + remoteControlEnabled, } = await this.getState() + // Determine if TaskBridge should be enabled + const enableTaskBridge = isRemoteControlEnabled(cloudUserInfo, remoteControlEnabled) + const task = new Task({ provider: this, apiConfiguration, @@ -752,7 +807,8 @@ export class ClineProvider rootTask: historyItem.rootTask, parentTask: historyItem.parentTask, taskNumber: historyItem.number, - onCreated: (instance) => this.emit(RooCodeEventName.TaskCreated, instance), + onCreated: this.taskCreationCallback, + enableTaskBridge, }) await this.addClineToStack(task) @@ -1631,6 +1687,7 @@ export class ClineProvider includeDiagnosticMessages, maxDiagnosticMessages, includeTaskHistoryInEnhance, + remoteControlEnabled, } = await this.getState() const telemetryKey = process.env.POSTHOG_API_KEY @@ -1758,6 +1815,7 @@ export class ClineProvider includeDiagnosticMessages: includeDiagnosticMessages ?? true, maxDiagnosticMessages: maxDiagnosticMessages ?? 50, includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? false, + remoteControlEnabled: remoteControlEnabled ?? false, } } @@ -1945,6 +2003,8 @@ export class ClineProvider maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50, // Add includeTaskHistoryInEnhance setting includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? false, + // Add remoteControlEnabled setting + remoteControlEnabled: stateValues.remoteControlEnabled ?? false, } } @@ -2057,6 +2117,55 @@ export class ClineProvider return true } + /** + * Handle remote control enabled/disabled state changes + * Manages ExtensionBridgeService and TaskBridgeService lifecycle + */ + public async handleRemoteControlToggle(enabled: boolean): Promise { + const { + CloudService: CloudServiceImport, + ExtensionBridgeService, + TaskBridgeService, + } = await import("@roo-code/cloud") + const userInfo = CloudServiceImport.instance.getUserInfo() + + // Handle ExtensionBridgeService using static method + await ExtensionBridgeService.handleRemoteControlState(userInfo, enabled, this, (message: string) => + this.log(message), + ) + + if (isRemoteControlEnabled(userInfo, enabled)) { + // Set up TaskBridgeService for the currently active task if one exists + const currentTask = this.getCurrentCline() + if (currentTask && !currentTask.taskBridgeService) { + try { + currentTask.taskBridgeService = TaskBridgeService.getInstance() + await currentTask.taskBridgeService.subscribeToTask(currentTask) + this.log(`[TaskBridgeService] Subscribed current task ${currentTask.taskId} to TaskBridge`) + } catch (error) { + const message = `[TaskBridgeService#subscribeToTask] ${error instanceof Error ? error.message : String(error)}` + this.log(message) + console.error(message) + } + } + } else { + // Disconnect TaskBridgeService for all tasks in the stack + for (const task of this.clineStack) { + if (task.taskBridgeService) { + try { + await task.taskBridgeService.unsubscribeFromTask(task.taskId) + task.taskBridgeService = undefined + this.log(`[TaskBridgeService] Unsubscribed task ${task.taskId} from TaskBridge`) + } catch (error) { + const message = `[TaskBridgeService#unsubscribeFromTask] for task ${task.taskId}: ${error instanceof Error ? error.message : String(error)}` + this.log(message) + console.error(message) + } + } + } + } + } + /** * Returns properties to be included in every telemetry event * This method is called by the telemetry service to get context information diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index fdb7e904257..743e3b0c132 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -906,6 +906,11 @@ export const webviewMessageHandler = async ( await updateGlobalState("enableMcpServerCreation", message.bool ?? true) await provider.postStateToWebview() break + case "remoteControlEnabled": + await updateGlobalState("remoteControlEnabled", message.bool ?? false) + await provider.handleRemoteControlToggle(message.bool ?? false) + await provider.postStateToWebview() + break case "refreshAllMcpServers": { const mcpHub = provider.getMcpHub() if (mcpHub) { diff --git a/src/extension.ts b/src/extension.ts index beb69b30b54..ea6ab4e1b48 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,7 +12,7 @@ try { console.warn("Failed to load environment variables:", e) } -import { CloudService } from "@roo-code/cloud" +import { CloudService, ExtensionBridgeService } from "@roo-code/cloud" import { TelemetryService, PostHogTelemetryClient } from "@roo-code/telemetry" import "./utils/path" // Necessary to have access to String.prototype.toPosix. @@ -29,6 +29,7 @@ import { CodeIndexManager } from "./services/code-index/manager" import { MdmService } from "./services/mdm/MdmService" import { migrateSettings } from "./utils/migrateSettings" import { autoImportSettings } from "./utils/autoImportSettings" +import { isRemoteControlEnabled } from "./utils/remoteControl" import { API } from "./extension/api" import { @@ -71,37 +72,13 @@ export async function activate(context: vscode.ExtensionContext) { console.warn("Failed to register PostHogTelemetryClient:", error) } - // Create logger for cloud services + // Create logger for cloud services. const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel)) - // Initialize Roo Code Cloud service. - const cloudService = await CloudService.createInstance(context, cloudLogger) - - try { - if (cloudService.telemetryClient) { - TelemetryService.instance.register(cloudService.telemetryClient) - } - } catch (error) { - outputChannel.appendLine( - `[CloudService] Failed to register TelemetryClient: ${error instanceof Error ? error.message : String(error)}`, - ) - } - - const postStateListener = () => { - ClineProvider.getVisibleInstance()?.postStateToWebview() - } - - cloudService.on("auth-state-changed", postStateListener) - cloudService.on("user-info", postStateListener) - cloudService.on("settings-updated", postStateListener) - - // Add to subscriptions for proper cleanup on deactivate - context.subscriptions.push(cloudService) - // Initialize MDM service const mdmService = await MdmService.createInstance(cloudLogger) - // Initialize i18n for internationalization support + // Initialize i18n for internationalization support. initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language)) // Initialize terminal shell execution handlers. @@ -126,6 +103,29 @@ export async function activate(context: vscode.ExtensionContext) { ) } + // Initialize Roo Code Cloud service. + const cloudService = await CloudService.createInstance(context, cloudLogger) + + const postStateListener = () => ClineProvider.getVisibleInstance()?.postStateToWebview() + + cloudService.on("auth-state-changed", postStateListener) + cloudService.on("settings-updated", postStateListener) + + cloudService.on("user-info", ({ userInfo }) => { + postStateListener() + + // Check if remote control is enabled in user settings + const remoteControlEnabled = contextProxy.getValue("remoteControlEnabled") + + // Handle ExtensionBridgeService state using static method + ExtensionBridgeService.handleRemoteControlState(userInfo, remoteControlEnabled, provider, (message: string) => + outputChannel.appendLine(message), + ) + }) + + // Add to subscriptions for proper cleanup on deactivate. + context.subscriptions.push(cloudService) + const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, codeIndexManager, mdmService) TelemetryService.instance.setProvider(provider) @@ -139,7 +139,7 @@ export async function activate(context: vscode.ExtensionContext) { }), ) - // Auto-import configuration if specified in settings + // Auto-import configuration if specified in settings. try { await autoImportSettings(outputChannel, { providerSettingsManager: provider.providerSettingsManager, @@ -232,6 +232,14 @@ export async function activate(context: vscode.ExtensionContext) { // This method is called when your extension is deactivated. export async function deactivate() { outputChannel.appendLine(`${Package.name} extension deactivated`) + + // Cleanup Extension Bridge service. + const extensionBridgeService = ExtensionBridgeService.getInstance() + + if (extensionBridgeService) { + await extensionBridgeService.disconnect() + } + await McpServerManager.cleanup(extensionContext) TelemetryService.instance.shutdown() TerminalRegistry.cleanup() diff --git a/src/package.json b/src/package.json index aa2110dfd51..d35f6f34ddf 100644 --- a/src/package.json +++ b/src/package.json @@ -420,7 +420,7 @@ "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.9.0", "@qdrant/js-client-rest": "^1.14.0", - "@roo-code/cloud": "^0.4.0", + "@roo-code/cloud": "^0.5.0", "@roo-code/ipc": "workspace:^", "@roo-code/telemetry": "workspace:^", "@roo-code/types": "workspace:^", diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 930edeac732..2313d7d177c 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -270,6 +270,7 @@ export type ExtensionState = Pick< | "profileThresholds" | "includeDiagnosticMessages" | "maxDiagnosticMessages" + | "remoteControlEnabled" > & { version: string clineMessages: ClineMessage[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index cb8759d851c..2d94896bf5b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -130,6 +130,7 @@ export interface WebviewMessage { | "terminalCompressProgressBar" | "mcpEnabled" | "enableMcpServerCreation" + | "remoteControlEnabled" | "searchCommits" | "alwaysApproveResubmit" | "requestDelaySeconds" diff --git a/src/utils/remoteControl.ts b/src/utils/remoteControl.ts new file mode 100644 index 00000000000..f003b522d1d --- /dev/null +++ b/src/utils/remoteControl.ts @@ -0,0 +1,11 @@ +import type { CloudUserInfo } from "@roo-code/types" + +/** + * Determines if remote control features should be enabled + * @param cloudUserInfo - User information from cloud service + * @param remoteControlEnabled - User's remote control setting + * @returns true if remote control should be enabled + */ +export function isRemoteControlEnabled(cloudUserInfo?: CloudUserInfo | null, remoteControlEnabled?: boolean): boolean { + return !!(cloudUserInfo?.id && cloudUserInfo.extensionBridgeEnabled && remoteControlEnabled) +} diff --git a/webview-ui/src/components/account/AccountView.tsx b/webview-ui/src/components/account/AccountView.tsx index e3d1a293a77..e36818cc3aa 100644 --- a/webview-ui/src/components/account/AccountView.tsx +++ b/webview-ui/src/components/account/AccountView.tsx @@ -5,8 +5,12 @@ import type { CloudUserInfo } from "@roo-code/types" import { TelemetryEventName } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" +import { useExtensionState } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" import { telemetryClient } from "@src/utils/TelemetryClient" +import { ToggleSwitch } from "@/components/ui/toggle-switch" + +import { History, PiggyBank, Router, SquareArrowOutUpRightIcon } from "lucide-react" type AccountViewProps = { userInfo: CloudUserInfo | null @@ -17,6 +21,7 @@ type AccountViewProps = { export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: AccountViewProps) => { const { t } = useAppTranslation() + const { remoteControlEnabled, setRemoteControlEnabled } = useExtensionState() const wasAuthenticatedRef = useRef(false) const rooLogoUri = (window as any).IMAGES_BASE_URI + "/roo-logo.svg" @@ -51,11 +56,17 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: vscode.postMessage({ type: "openExternal", url: cloudUrl }) } + const handleRemoteControlToggle = () => { + const newValue = !remoteControlEnabled + setRemoteControlEnabled(newValue) + vscode.postMessage({ type: "remoteControlEnabled", bool: newValue }) + } + return ( -
+

{t("account:title")}

- + {t("settings:common.done")}
@@ -77,13 +88,13 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: )}
{userInfo.name && ( -

{userInfo.name}

+

{userInfo.name}

)} {userInfo?.email && ( -

{userInfo?.email}

+

{userInfo?.email}

)} {userInfo?.organizationName && ( -
+
{userInfo.organizationImageUrl && ( )} + + {/* Remote Control Toggle - only show if user has extension bridge enabled */} + {userInfo?.extensionBridgeEnabled && ( +
+
+ + {t("account:remoteControl")} +
+
+ {t("account:remoteControlDescription")} +
+
+
+ )} +
{t("account:visitCloudWebsite")} @@ -125,30 +157,31 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
-

+

{t("account:cloudBenefitsTitle")}

-

- {t("account:cloudBenefitsSubtitle")} -

-
    -
  • - - {t("account:cloudBenefitHistory")} +
      +
    • + + {t("account:cloudBenefitWalkaway")}
    • -
    • - +
    • + {t("account:cloudBenefitSharing")}
    • -
    • - +
    • + + {t("account:cloudBenefitHistory")} +
    • +
    • + {t("account:cloudBenefitMetrics")}
-
- +
+ {t("account:connect")}
diff --git a/webview-ui/src/components/account/__tests__/AccountView.spec.tsx b/webview-ui/src/components/account/__tests__/AccountView.spec.tsx index d6fd3013e64..2af759d615d 100644 --- a/webview-ui/src/components/account/__tests__/AccountView.spec.tsx +++ b/webview-ui/src/components/account/__tests__/AccountView.spec.tsx @@ -11,11 +11,17 @@ vi.mock("@src/i18n/TranslationContext", () => ({ "settings:common.done": "Done", "account:signIn": "Connect to Roo Code Cloud", "account:cloudBenefitsTitle": "Connect to Roo Code Cloud", - "account:cloudBenefitsSubtitle": "Sync your prompts and telemetry to enable:", - "account:cloudBenefitHistory": "Online task history", - "account:cloudBenefitSharing": "Sharing and collaboration features", - "account:cloudBenefitMetrics": "Task, token, and cost-based usage metrics", + "account:cloudBenefitWalkaway": "Follow and control tasks from anywhere with Roomote Control", + "account:cloudBenefitSharing": "Share tasks with others", + "account:cloudBenefitHistory": "Access your task history", + "account:cloudBenefitMetrics": "Get a holistic view of your token consumption", "account:logOut": "Log out", + "account:connect": "Connect Now", + "account:visitCloudWebsite": "Visit Roo Code Cloud", + "account:remoteControl": "Roomote Control", + "account:remoteControlDescription": + "Enable following and interacting with tasks in this workspace with Roo Code Cloud", + "account:profilePicture": "Profile picture", } return translations[key] || key }, @@ -36,6 +42,14 @@ vi.mock("@src/utils/TelemetryClient", () => ({ }, })) +// Mock the extension state context +vi.mock("@src/context/ExtensionStateContext", () => ({ + useExtensionState: () => ({ + remoteControlEnabled: false, + setRemoteControlEnabled: vi.fn(), + }), +})) + // Mock window global for images Object.defineProperty(window, "IMAGES_BASE_URI", { value: "/images", @@ -55,13 +69,13 @@ describe("AccountView", () => { // Check that the benefits section is displayed expect(screen.getByRole("heading", { name: "Connect to Roo Code Cloud" })).toBeInTheDocument() - expect(screen.getByText("Sync your prompts and telemetry to enable:")).toBeInTheDocument() - expect(screen.getByText("Online task history")).toBeInTheDocument() - expect(screen.getByText("Sharing and collaboration features")).toBeInTheDocument() - expect(screen.getByText("Task, token, and cost-based usage metrics")).toBeInTheDocument() + expect(screen.getByText("Follow and control tasks from anywhere with Roomote Control")).toBeInTheDocument() + expect(screen.getByText("Share tasks with others")).toBeInTheDocument() + expect(screen.getByText("Access your task history")).toBeInTheDocument() + expect(screen.getByText("Get a holistic view of your token consumption")).toBeInTheDocument() // Check that the connect button is also present - expect(screen.getByText("account:connect")).toBeInTheDocument() + expect(screen.getByText("Connect Now")).toBeInTheDocument() }) it("should not display benefits when user is authenticated", () => { @@ -80,13 +94,60 @@ describe("AccountView", () => { ) // Check that the benefits section is NOT displayed - expect(screen.queryByText("Sync your prompts and telemetry to enable:")).not.toBeInTheDocument() - expect(screen.queryByText("Online task history")).not.toBeInTheDocument() - expect(screen.queryByText("Sharing and collaboration features")).not.toBeInTheDocument() - expect(screen.queryByText("Task, token, and cost-based usage metrics")).not.toBeInTheDocument() + expect( + screen.queryByText("Follow and control tasks from anywhere with Roomote Control"), + ).not.toBeInTheDocument() + expect(screen.queryByText("Share tasks with others")).not.toBeInTheDocument() + expect(screen.queryByText("Access your task history")).not.toBeInTheDocument() + expect(screen.queryByText("Get a holistic view of your token consumption")).not.toBeInTheDocument() // Check that user info is displayed instead expect(screen.getByText("Test User")).toBeInTheDocument() expect(screen.getByText("test@example.com")).toBeInTheDocument() }) + + it("should display remote control toggle when user has extension bridge enabled", () => { + const mockUserInfo = { + name: "Test User", + email: "test@example.com", + extensionBridgeEnabled: true, + } + + render( + {}} + />, + ) + + // Check that the remote control toggle is displayed + expect(screen.getByTestId("remote-control-toggle")).toBeInTheDocument() + expect(screen.getByText("Roomote Control")).toBeInTheDocument() + expect( + screen.getByText("Enable following and interacting with tasks in this workspace with Roo Code Cloud"), + ).toBeInTheDocument() + }) + + it("should not display remote control toggle when user does not have extension bridge enabled", () => { + const mockUserInfo = { + name: "Test User", + email: "test@example.com", + extensionBridgeEnabled: false, + } + + render( + {}} + />, + ) + + // Check that the remote control toggle is NOT displayed + expect(screen.queryByTestId("remote-control-toggle")).not.toBeInTheDocument() + expect(screen.queryByText("Roomote Control")).not.toBeInTheDocument() + }) }) diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index 21c531937f8..d470f7a6581 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -617,7 +617,9 @@ const ModesView = ({ onDone }: ModesViewProps) => { aria-expanded={open} className="justify-between w-full" data-testid="mode-select-trigger"> -
{getCurrentMode()?.name || t("prompts:modes.selectMode")}
+
+ {getCurrentMode()?.name || t("prompts:modes.selectMode")} +
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index da7ab633583..12f13bdf555 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -97,6 +97,8 @@ export interface ExtensionStateContextType extends ExtensionState { setMcpEnabled: (value: boolean) => void enableMcpServerCreation: boolean setEnableMcpServerCreation: (value: boolean) => void + remoteControlEnabled: boolean + setRemoteControlEnabled: (value: boolean) => void alwaysApproveResubmit?: boolean setAlwaysApproveResubmit: (value: boolean) => void requestDelaySeconds: number @@ -195,6 +197,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode terminalShellIntegrationTimeout: 4000, mcpEnabled: true, enableMcpServerCreation: false, + remoteControlEnabled: false, alwaysApproveResubmit: false, requestDelaySeconds: 5, currentApiConfigName: "default", @@ -408,6 +411,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode profileThresholds: state.profileThresholds ?? {}, alwaysAllowFollowupQuestions, followupAutoApproveTimeoutMs, + remoteControlEnabled: state.remoteControlEnabled ?? false, setExperimentEnabled: (id, enabled) => setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })), setApiConfiguration, @@ -454,6 +458,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })), setEnableMcpServerCreation: (value) => setState((prevState) => ({ ...prevState, enableMcpServerCreation: value })), + setRemoteControlEnabled: (value) => setState((prevState) => ({ ...prevState, remoteControlEnabled: value })), setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })), setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })), setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })), diff --git a/webview-ui/src/i18n/locales/ca/account.json b/webview-ui/src/i18n/locales/ca/account.json index a94a978b876..2804cc8dfa0 100644 --- a/webview-ui/src/i18n/locales/ca/account.json +++ b/webview-ui/src/i18n/locales/ca/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Historial de tasques en línia", "cloudBenefitSharing": "Funcions de compartició i col·laboració", "cloudBenefitMetrics": "Mètriques d'ús basades en tasques, tokens i costos", + "cloudBenefitWalkaway": "Segueix i controla tasques des de qualsevol lloc amb Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Permet seguir i interactuar amb tasques en aquest espai de treball amb Roo Code Cloud", "visitCloudWebsite": "Visita Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/de/account.json b/webview-ui/src/i18n/locales/de/account.json index bd4d71eadab..6edaf58fff3 100644 --- a/webview-ui/src/i18n/locales/de/account.json +++ b/webview-ui/src/i18n/locales/de/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Online-Aufgabenverlauf", "cloudBenefitSharing": "Freigabe- und Kollaborationsfunktionen", "cloudBenefitMetrics": "Aufgaben-, Token- und kostenbasierte Nutzungsmetriken", + "cloudBenefitWalkaway": "Verfolge und steuere Aufgaben von überall mit Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Ermöglicht das Verfolgen und Interagieren mit Aufgaben in diesem Arbeitsbereich mit Roo Code Cloud", "visitCloudWebsite": "Roo Code Cloud besuchen" } diff --git a/webview-ui/src/i18n/locales/en/account.json b/webview-ui/src/i18n/locales/en/account.json index f900abb2977..a73acef4329 100644 --- a/webview-ui/src/i18n/locales/en/account.json +++ b/webview-ui/src/i18n/locales/en/account.json @@ -4,11 +4,13 @@ "logOut": "Log out", "testApiAuthentication": "Test API Authentication", "signIn": "Connect to Roo Code Cloud", - "connect": "Connect", + "connect": "Connect Now", "cloudBenefitsTitle": "Connect to Roo Code Cloud", - "cloudBenefitsSubtitle": "Sync your prompts and telemetry to enable:", - "cloudBenefitHistory": "Online task history", - "cloudBenefitSharing": "Sharing and collaboration features", - "cloudBenefitMetrics": "Task, token, and cost-based usage metrics", - "visitCloudWebsite": "Visit Roo Code Cloud" + "cloudBenefitWalkaway": "Follow and control tasks from anywhere with Roomote Control", + "cloudBenefitSharing": "Share tasks with others", + "cloudBenefitHistory": "Access your task history", + "cloudBenefitMetrics": "Get a holistic view of your token consumption", + "visitCloudWebsite": "Visit Roo Code Cloud", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Enable following and interacting with tasks in this workspace with Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index a48213110ab..b20482d1b2f 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -686,7 +686,7 @@ "name": "Enable concurrent file edits", "description": "When enabled, Roo can edit multiple files in a single request. When disabled, Roo must edit files one at a time. Disabling this can help when working with less capable models or when you want more control over file modifications." }, -"PREVENT_FOCUS_DISRUPTION": { + "PREVENT_FOCUS_DISRUPTION": { "name": "Background editing", "description": "Prevent editor focus disruption when enabled. File edits happen in the background without opening diff views or stealing focus. You can continue working uninterrupted while Roo makes changes. Files can be opened without focus to capture diagnostics or kept closed entirely." }, diff --git a/webview-ui/src/i18n/locales/es/account.json b/webview-ui/src/i18n/locales/es/account.json index 2bda10e82f6..c8398ae25a6 100644 --- a/webview-ui/src/i18n/locales/es/account.json +++ b/webview-ui/src/i18n/locales/es/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Historial de tareas en línea", "cloudBenefitSharing": "Funciones de compartir y colaboración", "cloudBenefitMetrics": "Métricas de uso basadas en tareas, tokens y costos", + "cloudBenefitWalkaway": "Sigue y controla tareas desde cualquier lugar con Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Permite seguir e interactuar con tareas en este espacio de trabajo con Roo Code Cloud", "visitCloudWebsite": "Visitar Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/fr/account.json b/webview-ui/src/i18n/locales/fr/account.json index 1af4483c5c8..e50d11af15e 100644 --- a/webview-ui/src/i18n/locales/fr/account.json +++ b/webview-ui/src/i18n/locales/fr/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Historique des tâches en ligne", "cloudBenefitSharing": "Fonctionnalités de partage et collaboration", "cloudBenefitMetrics": "Métriques d'utilisation basées sur les tâches, tokens et coûts", + "cloudBenefitWalkaway": "Suivez et contrôlez les tâches depuis n'importe où avec Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Permet de suivre et d'interagir avec les tâches dans cet espace de travail avec Roo Code Cloud", "visitCloudWebsite": "Visiter Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/hi/account.json b/webview-ui/src/i18n/locales/hi/account.json index be6ea00d88b..485bc00633c 100644 --- a/webview-ui/src/i18n/locales/hi/account.json +++ b/webview-ui/src/i18n/locales/hi/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "ऑनलाइन कार्य इतिहास", "cloudBenefitSharing": "साझाकरण और सहयोग सुविधाएं", "cloudBenefitMetrics": "कार्य, token और लागत आधारित उपयोग मेट्रिक्स", + "cloudBenefitWalkaway": "Roomote Control के साथ कहीं से भी कार्यों को फॉलो और नियंत्रित करें", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Roo Code Cloud के साथ इस वर्कस्पेस में कार्यों को फॉलो और इंटरैक्ट करने की सुविधा दें", "visitCloudWebsite": "Roo Code Cloud पर जाएं" } diff --git a/webview-ui/src/i18n/locales/id/account.json b/webview-ui/src/i18n/locales/id/account.json index 57f3fec0df6..a3b6f4b97e4 100644 --- a/webview-ui/src/i18n/locales/id/account.json +++ b/webview-ui/src/i18n/locales/id/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Riwayat tugas online", "cloudBenefitSharing": "Fitur berbagi dan kolaborasi", "cloudBenefitMetrics": "Metrik penggunaan berdasarkan tugas, token, dan biaya", + "cloudBenefitWalkaway": "Ikuti dan kontrol tugas dari mana saja dengan Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Memungkinkan mengikuti dan berinteraksi dengan tugas di workspace ini dengan Roo Code Cloud", "visitCloudWebsite": "Kunjungi Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/it/account.json b/webview-ui/src/i18n/locales/it/account.json index fda13f563c3..7ffb5694078 100644 --- a/webview-ui/src/i18n/locales/it/account.json +++ b/webview-ui/src/i18n/locales/it/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Cronologia attività online", "cloudBenefitSharing": "Funzionalità di condivisione e collaborazione", "cloudBenefitMetrics": "Metriche di utilizzo basate su attività, token e costi", + "cloudBenefitWalkaway": "Segui e controlla le attività da qualsiasi luogo con Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Abilita il monitoraggio e l'interazione con le attività in questo workspace con Roo Code Cloud", "visitCloudWebsite": "Visita Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/ja/account.json b/webview-ui/src/i18n/locales/ja/account.json index b41eaf7895b..331d613f9b9 100644 --- a/webview-ui/src/i18n/locales/ja/account.json +++ b/webview-ui/src/i18n/locales/ja/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "オンラインタスク履歴", "cloudBenefitSharing": "共有とコラボレーション機能", "cloudBenefitMetrics": "タスク、Token、コストベースの使用メトリクス", + "cloudBenefitWalkaway": "Roomote Controlでどこからでもタスクをフォローし制御", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Roo Code Cloudでこのワークスペースのタスクをフォローし操作することを有効にする", "visitCloudWebsite": "Roo Code Cloudを訪問" } diff --git a/webview-ui/src/i18n/locales/ko/account.json b/webview-ui/src/i18n/locales/ko/account.json index 6ad06d43fa3..98b09b6e3d0 100644 --- a/webview-ui/src/i18n/locales/ko/account.json +++ b/webview-ui/src/i18n/locales/ko/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "온라인 작업 기록", "cloudBenefitSharing": "공유 및 협업 기능", "cloudBenefitMetrics": "작업, 토큰, 비용 기반 사용 메트릭", + "cloudBenefitWalkaway": "Roomote Control로 어디서나 작업을 팔로우하고 제어하세요", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Roo Code Cloud로 이 워크스페이스의 작업을 팔로우하고 상호작용할 수 있게 합니다", "visitCloudWebsite": "Roo Code Cloud 방문" } diff --git a/webview-ui/src/i18n/locales/nl/account.json b/webview-ui/src/i18n/locales/nl/account.json index 15ceb1865be..94d08b44094 100644 --- a/webview-ui/src/i18n/locales/nl/account.json +++ b/webview-ui/src/i18n/locales/nl/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Online taakgeschiedenis", "cloudBenefitSharing": "Deel- en samenwerkingsfuncties", "cloudBenefitMetrics": "Taak-, token- en kostengebaseerde gebruiksstatistieken", + "cloudBenefitWalkaway": "Volg en beheer taken van overal met Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Schakel het volgen en interacteren met taken in deze workspace in met Roo Code Cloud", "visitCloudWebsite": "Bezoek Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/pl/account.json b/webview-ui/src/i18n/locales/pl/account.json index fdb0e4d8946..b25f29b1bbe 100644 --- a/webview-ui/src/i18n/locales/pl/account.json +++ b/webview-ui/src/i18n/locales/pl/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Historia zadań online", "cloudBenefitSharing": "Funkcje udostępniania i współpracy", "cloudBenefitMetrics": "Metryki użycia oparte na zadaniach, tokenach i kosztach", + "cloudBenefitWalkaway": "Śledź i kontroluj zadania z dowolnego miejsca za pomocą Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Umożliwia śledzenie i interakcję z zadaniami w tym obszarze roboczym za pomocą Roo Code Cloud", "visitCloudWebsite": "Odwiedź Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/pt-BR/account.json b/webview-ui/src/i18n/locales/pt-BR/account.json index 5492ca75201..5b4f457b996 100644 --- a/webview-ui/src/i18n/locales/pt-BR/account.json +++ b/webview-ui/src/i18n/locales/pt-BR/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Histórico de tarefas online", "cloudBenefitSharing": "Recursos de compartilhamento e colaboração", "cloudBenefitMetrics": "Métricas de uso baseadas em tarefas, tokens e custos", + "cloudBenefitWalkaway": "Acompanhe e controle tarefas de qualquer lugar com Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Permite acompanhar e interagir com tarefas neste workspace com Roo Code Cloud", "visitCloudWebsite": "Visitar Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/ru/account.json b/webview-ui/src/i18n/locales/ru/account.json index 1c8dcf52894..4f4a2de167c 100644 --- a/webview-ui/src/i18n/locales/ru/account.json +++ b/webview-ui/src/i18n/locales/ru/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Онлайн-история задач", "cloudBenefitSharing": "Функции обмена и совместной работы", "cloudBenefitMetrics": "Метрики использования на основе задач, токенов и затрат", + "cloudBenefitWalkaway": "Отслеживайте и управляйте задачами откуда угодно с Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Позволяет отслеживать и взаимодействовать с задачами в этом рабочем пространстве с Roo Code Cloud", "visitCloudWebsite": "Посетить Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/tr/account.json b/webview-ui/src/i18n/locales/tr/account.json index a344ce940fe..03131e3fb55 100644 --- a/webview-ui/src/i18n/locales/tr/account.json +++ b/webview-ui/src/i18n/locales/tr/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Çevrimiçi görev geçmişi", "cloudBenefitSharing": "Paylaşım ve işbirliği özellikleri", "cloudBenefitMetrics": "Görev, token ve maliyet tabanlı kullanım metrikleri", + "cloudBenefitWalkaway": "Roomote Control ile görevleri her yerden takip et ve kontrol et", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Bu çalışma alanındaki görevleri Roo Code Cloud ile takip etme ve etkileşim kurma imkanı sağlar", "visitCloudWebsite": "Roo Code Cloud'u ziyaret et" } diff --git a/webview-ui/src/i18n/locales/vi/account.json b/webview-ui/src/i18n/locales/vi/account.json index 0e826b75ad8..3224160ba30 100644 --- a/webview-ui/src/i18n/locales/vi/account.json +++ b/webview-ui/src/i18n/locales/vi/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "Lịch sử tác vụ trực tuyến", "cloudBenefitSharing": "Tính năng chia sẻ và cộng tác", "cloudBenefitMetrics": "Số liệu sử dụng dựa trên tác vụ, token và chi phí", + "cloudBenefitWalkaway": "Theo dõi và điều khiển tác vụ từ bất kỳ đâu với Roomote Control", + "remoteControl": "Roomote Control", + "remoteControlDescription": "Cho phép theo dõi và tương tác với các tác vụ trong workspace này với Roo Code Cloud", "visitCloudWebsite": "Truy cập Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/zh-CN/account.json b/webview-ui/src/i18n/locales/zh-CN/account.json index 65a4c1d2210..9e097472a0f 100644 --- a/webview-ui/src/i18n/locales/zh-CN/account.json +++ b/webview-ui/src/i18n/locales/zh-CN/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "在线任务历史", "cloudBenefitSharing": "共享和协作功能", "cloudBenefitMetrics": "基于任务、Token 和成本的使用指标", + "cloudBenefitWalkaway": "使用 Roomote Control 随时随地跟踪和控制任务", + "remoteControl": "Roomote Control", + "remoteControlDescription": "允许通过 Roo Code Cloud 跟踪和操作此工作区中的任务", "visitCloudWebsite": "访问 Roo Code Cloud" } diff --git a/webview-ui/src/i18n/locales/zh-TW/account.json b/webview-ui/src/i18n/locales/zh-TW/account.json index dca8d3231cb..edd25dcf182 100644 --- a/webview-ui/src/i18n/locales/zh-TW/account.json +++ b/webview-ui/src/i18n/locales/zh-TW/account.json @@ -10,5 +10,8 @@ "cloudBenefitHistory": "線上工作歷史", "cloudBenefitSharing": "分享和協作功能", "cloudBenefitMetrics": "基於工作、Token 和成本的使用指標", + "cloudBenefitWalkaway": "使用 Roomote Control 隨時隨地追蹤和控制工作", + "remoteControl": "Roomote Control", + "remoteControlDescription": "允許透過 Roo Code Cloud 追蹤和操作此工作區中的工作", "visitCloudWebsite": "造訪 Roo Code Cloud" }