mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-09 03:17:58 +00:00
Bring back roomote control (#6796)
Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
This commit is contained in:
parent
4ae9014466
commit
b1300e5550
30 changed files with 558 additions and 85 deletions
114
pnpm-lock.yaml
generated
114
pnpm-lock.yaml
generated
|
|
@ -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: {}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,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"
|
||||
|
|
@ -108,6 +108,7 @@ export type TaskOptions = {
|
|||
apiConfiguration: ProviderSettings
|
||||
enableDiff?: boolean
|
||||
enableCheckpoints?: boolean
|
||||
enableTaskBridge?: boolean
|
||||
fuzzyMatchThreshold?: number
|
||||
consecutiveMistakeLimit?: number
|
||||
task?: string
|
||||
|
|
@ -238,6 +239,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
checkpointService?: RepoPerTaskCheckpointService
|
||||
checkpointServiceInitializing = false
|
||||
|
||||
// Task Bridge
|
||||
enableTaskBridge: boolean
|
||||
taskBridgeService: TaskBridgeService | null = null
|
||||
|
||||
// Streaming
|
||||
isWaitingForFirstChunk = false
|
||||
isStreaming = false
|
||||
|
|
@ -261,6 +266,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
apiConfiguration,
|
||||
enableDiff = false,
|
||||
enableCheckpoints = true,
|
||||
enableTaskBridge = false,
|
||||
fuzzyMatchThreshold = 1.0,
|
||||
consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
|
||||
task,
|
||||
|
|
@ -309,6 +315,7 @@ export class Task extends EventEmitter<TaskEvents> 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
|
||||
|
|
@ -973,6 +980,22 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// Start / Abort / Resume
|
||||
|
||||
private async startTask(task?: string, images?: string[]): Promise<void> {
|
||||
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
|
||||
|
|
@ -1024,6 +1047,22 @@ export class Task extends EventEmitter<TaskEvents> 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
|
||||
|
|
@ -1269,6 +1308,14 @@ export class Task extends EventEmitter<TaskEvents> 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.
|
||||
|
|
|
|||
|
|
@ -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<Task, Array<() => 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<K extends keyof TaskProviderEvents>(
|
||||
event: K,
|
||||
listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
|
||||
): this {
|
||||
return super.on(event, listener as any)
|
||||
}
|
||||
|
||||
/**
|
||||
* Override EventEmitter's off method to match TaskProviderLike interface
|
||||
*/
|
||||
override off<K extends keyof TaskProviderEvents>(
|
||||
event: K,
|
||||
listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
|
||||
): 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
|
||||
|
|
|
|||
|
|
@ -923,6 +923,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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:^",
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ export type ExtensionState = Pick<
|
|||
| "profileThresholds"
|
||||
| "includeDiagnosticMessages"
|
||||
| "maxDiagnosticMessages"
|
||||
| "remoteControlEnabled"
|
||||
> & {
|
||||
version: string
|
||||
clineMessages: ClineMessage[]
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ export interface WebviewMessage {
|
|||
| "terminalCompressProgressBar"
|
||||
| "mcpEnabled"
|
||||
| "enableMcpServerCreation"
|
||||
| "remoteControlEnabled"
|
||||
| "searchCommits"
|
||||
| "alwaysApproveResubmit"
|
||||
| "requestDelaySeconds"
|
||||
|
|
|
|||
11
src/utils/remoteControl.ts
Normal file
11
src/utils/remoteControl.ts
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<div className="flex flex-col h-full p-4 bg-vscode-editor-background">
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-xl font-medium text-vscode-foreground">{t("account:title")}</h1>
|
||||
<VSCodeButton appearance="primary" onClick={onDone}>
|
||||
<VSCodeButton appearance="secondary" onClick={onDone}>
|
||||
{t("settings:common.done")}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
|
@ -77,13 +88,13 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
|
|||
)}
|
||||
</div>
|
||||
{userInfo.name && (
|
||||
<h2 className="text-lg font-medium text-vscode-foreground mb-0">{userInfo.name}</h2>
|
||||
<h2 className="text-lg font-medium text-vscode-foreground my-0">{userInfo.name}</h2>
|
||||
)}
|
||||
{userInfo?.email && (
|
||||
<p className="text-sm text-vscode-descriptionForeground">{userInfo?.email}</p>
|
||||
<p className="text-sm text-vscode-descriptionForeground my-0">{userInfo?.email}</p>
|
||||
)}
|
||||
{userInfo?.organizationName && (
|
||||
<div className="flex items-center gap-2 text-sm text-vscode-descriptionForeground">
|
||||
<div className="flex items-center gap-2 text-sm text-vscode-descriptionForeground mt-2">
|
||||
{userInfo.organizationImageUrl && (
|
||||
<img
|
||||
src={userInfo.organizationImageUrl}
|
||||
|
|
@ -96,6 +107,26 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{userInfo?.extensionBridgeEnabled && (
|
||||
<div className="border-t border-vscode-widget-border pt-4 mt-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<ToggleSwitch
|
||||
checked={remoteControlEnabled}
|
||||
onChange={handleRemoteControlToggle}
|
||||
size="medium"
|
||||
aria-label={t("account:remoteControl")}
|
||||
data-testid="remote-control-toggle"
|
||||
/>
|
||||
<span className="font-medium text-vscode-foreground">{t("account:remoteControl")}</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-4 ml-8">
|
||||
{t("account:remoteControlDescription")}
|
||||
</div>
|
||||
<hr className="border-vscode-widget-border mb-4" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<VSCodeButton appearance="secondary" onClick={handleVisitCloudWebsite} className="w-full">
|
||||
{t("account:visitCloudWebsite")}
|
||||
|
|
@ -125,30 +156,27 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
|
|||
</div>
|
||||
|
||||
<div className="flex flex-col mb-6 text-center">
|
||||
<h2 className="text-lg font-medium text-vscode-foreground mb-2">
|
||||
<h2 className="text-xl font-bold text-vscode-foreground mb-2">
|
||||
{t("account:cloudBenefitsTitle")}
|
||||
</h2>
|
||||
<p className="text-md text-vscode-descriptionForeground mb-4">
|
||||
{t("account:cloudBenefitsSubtitle")}
|
||||
</p>
|
||||
<ul className="text-sm text-vscode-descriptionForeground space-y-2 max-w-xs mx-auto">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2 text-vscode-foreground">•</span>
|
||||
{t("account:cloudBenefitHistory")}
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2 text-vscode-foreground">•</span>
|
||||
<ul className="text-vscode-descriptionForeground space-y-3 mx-auto px-8">
|
||||
<li className="flex items-start text-left gap-4">
|
||||
<SquareArrowOutUpRightIcon size="16" className="shrink-0" />
|
||||
{t("account:cloudBenefitSharing")}
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2 text-vscode-foreground">•</span>
|
||||
<li className="flex items-start text-left gap-4">
|
||||
<History size="16" className="shrink-0" />
|
||||
{t("account:cloudBenefitHistory")}
|
||||
</li>
|
||||
<li className="flex items-start text-left gap-4">
|
||||
<PiggyBank size="16" className="shrink-0" />
|
||||
{t("account:cloudBenefitMetrics")}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-full">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-1/2">
|
||||
{t("account:connect")}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
<AccountView
|
||||
userInfo={mockUserInfo}
|
||||
isAuthenticated={true}
|
||||
cloudApiUrl="https://app.roocode.com"
|
||||
onDone={() => {}}
|
||||
/>,
|
||||
)
|
||||
|
||||
// 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(
|
||||
<AccountView
|
||||
userInfo={mockUserInfo}
|
||||
isAuthenticated={true}
|
||||
cloudApiUrl="https://app.roocode.com"
|
||||
onDone={() => {}}
|
||||
/>,
|
||||
)
|
||||
|
||||
// Check that the remote control toggle is NOT displayed
|
||||
expect(screen.queryByTestId("remote-control-toggle")).not.toBeInTheDocument()
|
||||
expect(screen.queryByText("Roomote Control")).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 })),
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ca/account.json
generated
3
webview-ui/src/i18n/locales/ca/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/account.json
generated
3
webview-ui/src/i18n/locales/de/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/account.json
generated
3
webview-ui/src/i18n/locales/es/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/account.json
generated
3
webview-ui/src/i18n/locales/fr/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/account.json
generated
3
webview-ui/src/i18n/locales/hi/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "ऑनलाइन कार्य इतिहास",
|
||||
"cloudBenefitSharing": "साझाकरण और सहयोग सुविधाएं",
|
||||
"cloudBenefitMetrics": "कार्य, token और लागत आधारित उपयोग मेट्रिक्स",
|
||||
"cloudBenefitWalkaway": "Roomote Control के साथ कहीं से भी कार्यों को फॉलो और नियंत्रित करें",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "Roo Code Cloud के साथ इस वर्कस्पेस में कार्यों को फॉलो और इंटरैक्ट करने की सुविधा दें",
|
||||
"visitCloudWebsite": "Roo Code Cloud पर जाएं"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/account.json
generated
3
webview-ui/src/i18n/locales/id/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/account.json
generated
3
webview-ui/src/i18n/locales/it/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/account.json
generated
3
webview-ui/src/i18n/locales/ja/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "オンラインタスク履歴",
|
||||
"cloudBenefitSharing": "共有とコラボレーション機能",
|
||||
"cloudBenefitMetrics": "タスク、Token、コストベースの使用メトリクス",
|
||||
"cloudBenefitWalkaway": "Roomote Controlでどこからでもタスクをフォローし制御",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "Roo Code Cloudでこのワークスペースのタスクをフォローし操作することを有効にする",
|
||||
"visitCloudWebsite": "Roo Code Cloudを訪問"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/account.json
generated
3
webview-ui/src/i18n/locales/ko/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "온라인 작업 기록",
|
||||
"cloudBenefitSharing": "공유 및 협업 기능",
|
||||
"cloudBenefitMetrics": "작업, 토큰, 비용 기반 사용 메트릭",
|
||||
"cloudBenefitWalkaway": "Roomote Control로 어디서나 작업을 팔로우하고 제어하세요",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "Roo Code Cloud로 이 워크스페이스의 작업을 팔로우하고 상호작용할 수 있게 합니다",
|
||||
"visitCloudWebsite": "Roo Code Cloud 방문"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/account.json
generated
3
webview-ui/src/i18n/locales/nl/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/account.json
generated
3
webview-ui/src/i18n/locales/pl/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/account.json
generated
3
webview-ui/src/i18n/locales/pt-BR/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/account.json
generated
3
webview-ui/src/i18n/locales/ru/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "Онлайн-история задач",
|
||||
"cloudBenefitSharing": "Функции обмена и совместной работы",
|
||||
"cloudBenefitMetrics": "Метрики использования на основе задач, токенов и затрат",
|
||||
"cloudBenefitWalkaway": "Отслеживайте и управляйте задачами откуда угодно с Roomote Control",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "Позволяет отслеживать и взаимодействовать с задачами в этом рабочем пространстве с Roo Code Cloud",
|
||||
"visitCloudWebsite": "Посетить Roo Code Cloud"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/account.json
generated
3
webview-ui/src/i18n/locales/tr/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/account.json
generated
3
webview-ui/src/i18n/locales/vi/account.json
generated
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/account.json
generated
3
webview-ui/src/i18n/locales/zh-CN/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "在线任务历史",
|
||||
"cloudBenefitSharing": "共享和协作功能",
|
||||
"cloudBenefitMetrics": "基于任务、Token 和成本的使用指标",
|
||||
"cloudBenefitWalkaway": "使用 Roomote Control 随时随地跟踪和控制任务",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "允许通过 Roo Code Cloud 跟踪和操作此工作区中的任务",
|
||||
"visitCloudWebsite": "访问 Roo Code Cloud"
|
||||
}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/account.json
generated
3
webview-ui/src/i18n/locales/zh-TW/account.json
generated
|
|
@ -10,5 +10,8 @@
|
|||
"cloudBenefitHistory": "線上工作歷史",
|
||||
"cloudBenefitSharing": "分享和協作功能",
|
||||
"cloudBenefitMetrics": "基於工作、Token 和成本的使用指標",
|
||||
"cloudBenefitWalkaway": "使用 Roomote Control 隨時隨地追蹤和控制工作",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "允許透過 Roo Code Cloud 追蹤和操作此工作區中的工作",
|
||||
"visitCloudWebsite": "造訪 Roo Code Cloud"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue