fix: improve command registration for slow environments like PROOT-distro/Termux

- Add early command registration before potentially blocking operations
- Add timeout handling for MDM and ContextProxy initialization
- Improve error handling in registerCommands to continue with partial functionality
- Register placeholder handlers for critical commands as fallback
- Re-register commands with proper handlers after provider initialization

This ensures commands are available even on slow environments that may timeout during initialization.

Fixes #8816
This commit is contained in:
Roo Code 2025-10-25 06:49:49 +00:00
parent 97331bcb2e
commit ad846c81d6
2 changed files with 113 additions and 6 deletions

View file

@ -64,11 +64,38 @@ export type RegisterCommandOptions = {
}
export const registerCommands = (options: RegisterCommandOptions) => {
const { context } = options
const { context, outputChannel } = options
const failedCommands: string[] = []
for (const [id, callback] of Object.entries(getCommandsMap(options))) {
const command = getCommand(id as CommandId)
context.subscriptions.push(vscode.commands.registerCommand(command, callback))
try {
// Check if command already exists (might have been registered as placeholder)
const existingIndex = context.subscriptions.findIndex((sub) => (sub as any).command === command)
// Remove existing placeholder if found
if (existingIndex !== -1) {
const existing = context.subscriptions[existingIndex]
context.subscriptions.splice(existingIndex, 1)
existing.dispose()
}
// Register the command with proper handler
const disposable = vscode.commands.registerCommand(command, callback)
context.subscriptions.push(disposable)
} catch (error) {
failedCommands.push(id)
outputChannel.appendLine(
`[Commands] Failed to register command '${id}': ${error instanceof Error ? error.message : String(error)}`,
)
}
}
if (failedCommands.length > 0) {
outputChannel.appendLine(
`[Commands] Warning: Failed to register ${failedCommands.length} command(s): ${failedCommands.join(", ")}`,
)
// Don't throw - allow extension to continue with partial functionality
}
}

View file

@ -80,8 +80,24 @@ export async function activate(context: vscode.ExtensionContext) {
// Create logger for cloud services.
const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel))
// Initialize MDM service
const mdmService = await MdmService.createInstance(cloudLogger)
// Initialize MDM service (with timeout for slow environments)
let mdmService: MdmService | undefined
try {
mdmService = await Promise.race([
MdmService.createInstance(cloudLogger),
new Promise<MdmService | undefined>((resolve) =>
setTimeout(() => {
outputChannel.appendLine("[MDM] Initialization timeout - continuing without MDM")
resolve(undefined)
}, 5000),
),
])
} catch (error) {
outputChannel.appendLine(
`[MDM] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
)
mdmService = undefined
}
// Initialize i18n for internationalization support.
initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language))
@ -97,7 +113,24 @@ export async function activate(context: vscode.ExtensionContext) {
context.globalState.update("allowedCommands", defaultCommands)
}
const contextProxy = await ContextProxy.getInstance(context)
// Get context proxy with timeout for slow environments
let contextProxy: ContextProxy
try {
contextProxy = await Promise.race([
ContextProxy.getInstance(context),
new Promise<ContextProxy>((resolve, reject) =>
setTimeout(() => {
reject(new Error("ContextProxy initialization timeout"))
}, 10000),
),
])
} catch (error) {
outputChannel.appendLine(
`[ContextProxy] Failed to initialize, using fallback: ${error instanceof Error ? error.message : String(error)}`,
)
// Force creation even if slow
contextProxy = await ContextProxy.getInstance(context)
}
// Initialize code index managers for all workspace folders.
const codeIndexManagers: CodeIndexManager[] = []
@ -125,6 +158,45 @@ export async function activate(context: vscode.ExtensionContext) {
// Initialize the provider *before* the Roo Code Cloud service.
const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, mdmService)
// CRITICAL: Register commands early to ensure they're available on slow environments
// This must happen before any long-running async operations
try {
registerCommands({ context, outputChannel, provider })
outputChannel.appendLine("[Commands] Successfully registered all commands")
} catch (error) {
outputChannel.appendLine(
`[Commands] CRITICAL: Failed to register commands: ${error instanceof Error ? error.message : String(error)}`,
)
// Attempt individual command registration as fallback
try {
const criticalCommands = [
"settingsButtonClicked",
"plusButtonClicked",
"mcpButtonClicked",
"historyButtonClicked",
]
for (const cmdId of criticalCommands) {
try {
const command = `${Package.name}.${cmdId}`
if (!context.subscriptions.find((sub) => (sub as any).command === command)) {
context.subscriptions.push(
vscode.commands.registerCommand(command, () => {
outputChannel.appendLine(`[Command] ${cmdId} invoked but handler not fully initialized`)
vscode.window.showWarningMessage(
`Extension is still initializing. Please try again in a moment.`,
)
}),
)
}
} catch (cmdError) {
outputChannel.appendLine(`[Commands] Failed to register fallback for ${cmdId}: ${cmdError}`)
}
}
} catch (fallbackError) {
outputChannel.appendLine(`[Commands] Failed to register fallback commands: ${fallbackError}`)
}
}
// Initialize Roo Code Cloud service.
const postStateListener = () => ClineProvider.getVisibleInstance()?.postStateToWebview()
@ -225,7 +297,15 @@ export async function activate(context: vscode.ExtensionContext) {
)
}
registerCommands({ context, outputChannel, provider })
// Commands already registered earlier, but re-register to ensure proper handlers
// This overwrites the temporary handlers with the actual ones
try {
registerCommands({ context, outputChannel, provider })
} catch (error) {
outputChannel.appendLine(
`[Commands] Failed to re-register commands with proper handlers: ${error instanceof Error ? error.message : String(error)}`,
)
}
/**
* We use the text document content provider API to show the left side for diff