diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a0cb09263..027a9cf756 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.5.0 - version: 0.5.0 + specifier: ^0.8.0 + version: 0.8.0 '@roo-code/ipc': specifier: workspace:^ version: link:../packages/ipc @@ -3065,11 +3065,11 @@ packages: cpu: [x64] os: [win32] - '@roo-code/cloud@0.5.0': - resolution: {integrity: sha512-4u6Ce2Rmr5a9nxhjGUMRRWUWhZc63EmF/UJ/+Az5/1JARMOp0kHN5Pwqz2QAgfD137+TFSBKQORpiN0GXrdt2w==} + '@roo-code/cloud@0.8.0': + resolution: {integrity: sha512-pzuJ6U60zKJ8KkE+dud58/oHpOa6LYdkyQNvqnTRztz1kietVJG8ugakTXQcPmNQH5lLKAi1b0ri/cfhl8JBZQ==} - '@roo-code/types@1.44.0': - resolution: {integrity: sha512-3xbW4pYaCgWuHF5qOsiXpIcd281dlFTe1zboUGgcUUsB414Hu3pQI86PdgJxVGtZgxtaca0eHTQ2Sqjqq8nPlA==} + '@roo-code/types@1.45.0': + resolution: {integrity: sha512-+rFJ9HcukDl59X9pI6lgZDtJHtKqHg6BIdS4LG759X+5MV7wvlNb4S+bQ3qKqIlBsQhH17bzIJCfsWZwjwxecw==} '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -3414,6 +3414,9 @@ packages: resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -5014,6 +5017,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -5361,6 +5373,13 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -6267,8 +6286,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: @@ -8609,6 +8628,14 @@ packages: resolution: {integrity: sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==} engines: {node: '>= 18'} + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + socks-proxy-agent@8.0.5: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} @@ -9653,6 +9680,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.2: resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} @@ -9696,6 +9735,10 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -12191,16 +12234,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@roo-code/cloud@0.5.0': + '@roo-code/cloud@0.8.0': dependencies: - '@roo-code/types': 1.44.0 - ioredis: 5.7.0 + '@roo-code/types': 1.45.0 + ioredis: 5.6.1 p-wait-for: 5.0.2 + socket.io-client: 4.8.1 zod: 3.25.76 transitivePeerDependencies: + - bufferutil - supports-color + - utf-8-validate - '@roo-code/types@1.44.0': + '@roo-code/types@1.45.0': dependencies: zod: 3.25.76 @@ -12727,6 +12773,8 @@ snapshots: '@smithy/util-buffer-from': 4.0.0 tslib: 2.8.1 + '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/utils@0.3.0': {} '@swc/counter@0.1.3': {} @@ -14519,6 +14567,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.3.7: + dependencies: + ms: 2.1.3 + debug@4.4.1(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -14752,6 +14804,20 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -15965,7 +16031,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 @@ -18799,6 +18865,24 @@ snapshots: smol-toml@1.3.4: {} + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 @@ -20090,6 +20174,8 @@ snapshots: wrappy@1.0.2: {} + ws@8.17.1: {} + ws@8.18.2: {} ws@8.18.3: @@ -20108,6 +20194,8 @@ snapshots: xmlchars@2.2.0: {} + xmlhttprequest-ssl@2.1.2: {} + xtend@4.0.2: {} y18n@5.0.8: {} diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 5e96b6fb16..88023cd474 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" @@ -107,6 +107,7 @@ export type TaskOptions = { apiConfiguration: ProviderSettings enableDiff?: boolean enableCheckpoints?: boolean + enableTaskBridge?: boolean fuzzyMatchThreshold?: number consecutiveMistakeLimit?: number task?: string @@ -237,6 +238,10 @@ export class Task extends EventEmitter implements TaskLike { checkpointService?: RepoPerTaskCheckpointService checkpointServiceInitializing = false + // Task Bridge + enableTaskBridge: boolean + taskBridgeService: TaskBridgeService | null = null + // Streaming isWaitingForFirstChunk = false isStreaming = false @@ -260,6 +265,7 @@ export class Task extends EventEmitter implements TaskLike { apiConfiguration, enableDiff = false, enableCheckpoints = true, + enableTaskBridge = false, fuzzyMatchThreshold = 1.0, consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, task, @@ -308,6 +314,7 @@ export class Task extends EventEmitter implements TaskLike { this.globalStoragePath = provider.context.globalStorageUri.fsPath this.diffViewProvider = new DiffViewProvider(this.cwd, this) this.enableCheckpoints = enableCheckpoints + this.enableTaskBridge = enableTaskBridge this.rootTask = rootTask this.parentTask = parentTask @@ -972,6 +979,22 @@ export class Task extends EventEmitter implements TaskLike { // Start / Abort / Resume private async startTask(task?: string, images?: string[]): Promise { + if (this.enableTaskBridge && CloudService.hasInstance()) { + if (!this.taskBridgeService) { + const bridgeConfig = await CloudService.instance.cloudAPI?.bridgeConfig() + + if (bridgeConfig) { + this.taskBridgeService = await TaskBridgeService.createInstance({ + ...bridgeConfig, + }) + } + } + + if (this.taskBridgeService) { + 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 @@ -1023,6 +1046,22 @@ export class Task extends EventEmitter implements TaskLike { } private async resumeTaskFromHistory() { + if (this.enableTaskBridge && CloudService.hasInstance()) { + if (!this.taskBridgeService) { + const bridgeConfig = await CloudService.instance.cloudAPI?.bridgeConfig() + + if (bridgeConfig) { + this.taskBridgeService = await TaskBridgeService.createInstance({ + ...bridgeConfig, + }) + } + } + + if (this.taskBridgeService) { + await this.taskBridgeService.subscribeToTask(this) + } + } + const modifiedClineMessages = await this.getSavedClineMessages() // Remove any resume messages that may have been added before @@ -1268,6 +1307,14 @@ 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)) + this.taskBridgeService = null + } + // 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 274060a19b..64521b92da 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" @@ -112,6 +112,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 @@ -161,12 +163,66 @@ 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}`) }) } + /** + * Override EventEmitter's on method to match TaskProviderLike interface + */ + override on( + event: K, + listener: (...args: TaskProviderEvents[K]) => void | Promise, + ): this { + return super.on(event, listener as any) + } + + /** + * Override EventEmitter's off method to match TaskProviderLike interface + */ + override off( + event: K, + listener: (...args: TaskProviderEvents[K]) => void | Promise, + ): this { + return super.off(event, listener as any) + } + /** * Initialize cloud profile synchronization */ @@ -296,6 +352,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 @@ -652,6 +716,8 @@ export class ClineProvider enableCheckpoints, fuzzyMatchThreshold, experiments, + cloudUserInfo, + remoteControlEnabled, } = await this.getState() if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) { @@ -671,7 +737,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: isRemoteControlEnabled(cloudUserInfo, remoteControlEnabled), ...options, }) @@ -736,8 +803,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, @@ -750,7 +822,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) @@ -1629,6 +1702,7 @@ export class ClineProvider includeDiagnosticMessages, maxDiagnosticMessages, includeTaskHistoryInEnhance, + remoteControlEnabled, } = await this.getState() const telemetryKey = process.env.POSTHOG_API_KEY @@ -1756,6 +1830,7 @@ export class ClineProvider includeDiagnosticMessages: includeDiagnosticMessages ?? true, maxDiagnosticMessages: maxDiagnosticMessages ?? 50, includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? false, + remoteControlEnabled: remoteControlEnabled ?? false, } } @@ -1943,6 +2018,8 @@ export class ClineProvider maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50, // Add includeTaskHistoryInEnhance setting includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? false, + // Add remoteControlEnabled setting + remoteControlEnabled: stateValues.remoteControlEnabled ?? false, } } @@ -2055,6 +2132,80 @@ export class ClineProvider return true } + /** + * Handle remote control enabled/disabled state changes + * Manages ExtensionBridgeService and TaskBridgeService lifecycle + */ + public async handleRemoteControlToggle(enabled: boolean) { + const { + CloudService: CloudServiceImport, + ExtensionBridgeService, + TaskBridgeService, + } = await import("@roo-code/cloud") + + const userInfo = CloudServiceImport.instance.getUserInfo() + + const bridgeConfig = await CloudServiceImport.instance.cloudAPI?.bridgeConfig() + + if (!bridgeConfig) { + this.log("[CloudService] Failed to get bridge config") + return + } + + ExtensionBridgeService.handleRemoteControlState( + userInfo, + enabled, + { ...bridgeConfig, provider: 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 && CloudService.hasInstance()) { + try { + if (!currentTask.taskBridgeService) { + const bridgeConfig = await CloudService.instance.cloudAPI?.bridgeConfig() + + if (bridgeConfig) { + currentTask.taskBridgeService = await TaskBridgeService.createInstance({ + ...bridgeConfig, + }) + } + } + + if (currentTask.taskBridgeService) { + 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 = null + 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) + } + } + } + + TaskBridgeService.resetInstance() + } + } + /** * 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 f5dc6a467f..3d008a67c5 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -914,6 +914,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 2d95902295..3df954fa0a 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. @@ -136,6 +113,45 @@ export async function activate(context: vscode.ExtensionContext) { } } + // 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("settings-updated", postStateListener) + + cloudService.on("user-info", async ({ userInfo }) => { + postStateListener() + + const bridgeConfig = await cloudService.cloudAPI?.bridgeConfig() + + if (!bridgeConfig) { + outputChannel.appendLine("[CloudService] Failed to get bridge config") + return + } + + ExtensionBridgeService.handleRemoteControlState( + userInfo, + contextProxy.getValue("remoteControlEnabled"), + { ...bridgeConfig, 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, mdmService) TelemetryService.instance.setProvider(provider) @@ -145,7 +161,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, @@ -263,6 +279,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 499cd403ce..3d7d4dad7f 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.5.0", + "@roo-code/cloud": "^0.8.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 3ddd69945c..f9ac305e07 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -271,6 +271,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 cb8759d851..2d94896bf5 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 0000000000..f003b522d1 --- /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 e3d1a293a7..f01aec9377 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, 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 && ( )} + + {userInfo?.extensionBridgeEnabled && ( +
+
+ + {t("account:remoteControl")} +
+
+ {t("account:remoteControlDescription")} +
+
+
+ )} +
{t("account:visitCloudWebsite")} @@ -125,30 +156,27 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
-

+

{t("account:cloudBenefitsTitle")}

-

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

-
    -
  • - - {t("account:cloudBenefitHistory")} -
  • -
  • - +
      +
    • + {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 d6fd3013e6..c960ca4662 100644 --- a/webview-ui/src/components/account/__tests__/AccountView.spec.tsx +++ b/webview-ui/src/components/account/__tests__/AccountView.spec.tsx @@ -11,11 +11,16 @@ 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: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 +41,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 +68,12 @@ 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("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 +92,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/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index da7ab63358..12f13bdf55 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 a94a978b87..2804cc8dfa 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 bd4d71eada..6edaf58fff 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 f900abb297..a73acef432 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/es/account.json b/webview-ui/src/i18n/locales/es/account.json index 2bda10e82f..c8398ae25a 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 1af4483c5c..e50d11af15 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 be6ea00d88..485bc00633 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 57f3fec0df..a3b6f4b97e 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 fda13f563c..7ffb569407 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 b41eaf7895..331d613f9b 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 6ad06d43fa..98b09b6e3d 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 15ceb1865b..94d08b4409 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 fdb0e4d894..b25f29b1bb 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 5492ca7520..5b4f457b99 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 1c8dcf5289..4f4a2de167 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 a344ce940f..03131e3fb5 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 0e826b75ad..3224160ba3 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 65a4c1d221..9e097472a0 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 dca8d3231c..edd25dcf18 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" }