Skip to content

Commit ad846c8

Browse files
committed
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
1 parent 97331bc commit ad846c8

File tree

2 files changed

+113
-6
lines changed

2 files changed

+113
-6
lines changed

src/activate/registerCommands.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,38 @@ export type RegisterCommandOptions = {
6464
}
6565

6666
export const registerCommands = (options: RegisterCommandOptions) => {
67-
const { context } = options
67+
const { context, outputChannel } = options
68+
const failedCommands: string[] = []
6869

6970
for (const [id, callback] of Object.entries(getCommandsMap(options))) {
7071
const command = getCommand(id as CommandId)
71-
context.subscriptions.push(vscode.commands.registerCommand(command, callback))
72+
try {
73+
// Check if command already exists (might have been registered as placeholder)
74+
const existingIndex = context.subscriptions.findIndex((sub) => (sub as any).command === command)
75+
76+
// Remove existing placeholder if found
77+
if (existingIndex !== -1) {
78+
const existing = context.subscriptions[existingIndex]
79+
context.subscriptions.splice(existingIndex, 1)
80+
existing.dispose()
81+
}
82+
83+
// Register the command with proper handler
84+
const disposable = vscode.commands.registerCommand(command, callback)
85+
context.subscriptions.push(disposable)
86+
} catch (error) {
87+
failedCommands.push(id)
88+
outputChannel.appendLine(
89+
`[Commands] Failed to register command '${id}': ${error instanceof Error ? error.message : String(error)}`,
90+
)
91+
}
92+
}
93+
94+
if (failedCommands.length > 0) {
95+
outputChannel.appendLine(
96+
`[Commands] Warning: Failed to register ${failedCommands.length} command(s): ${failedCommands.join(", ")}`,
97+
)
98+
// Don't throw - allow extension to continue with partial functionality
7299
}
73100
}
74101

src/extension.ts

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,24 @@ export async function activate(context: vscode.ExtensionContext) {
8080
// Create logger for cloud services.
8181
const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel))
8282

83-
// Initialize MDM service
84-
const mdmService = await MdmService.createInstance(cloudLogger)
83+
// Initialize MDM service (with timeout for slow environments)
84+
let mdmService: MdmService | undefined
85+
try {
86+
mdmService = await Promise.race([
87+
MdmService.createInstance(cloudLogger),
88+
new Promise<MdmService | undefined>((resolve) =>
89+
setTimeout(() => {
90+
outputChannel.appendLine("[MDM] Initialization timeout - continuing without MDM")
91+
resolve(undefined)
92+
}, 5000),
93+
),
94+
])
95+
} catch (error) {
96+
outputChannel.appendLine(
97+
`[MDM] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
98+
)
99+
mdmService = undefined
100+
}
85101

86102
// Initialize i18n for internationalization support.
87103
initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language))
@@ -97,7 +113,24 @@ export async function activate(context: vscode.ExtensionContext) {
97113
context.globalState.update("allowedCommands", defaultCommands)
98114
}
99115

100-
const contextProxy = await ContextProxy.getInstance(context)
116+
// Get context proxy with timeout for slow environments
117+
let contextProxy: ContextProxy
118+
try {
119+
contextProxy = await Promise.race([
120+
ContextProxy.getInstance(context),
121+
new Promise<ContextProxy>((resolve, reject) =>
122+
setTimeout(() => {
123+
reject(new Error("ContextProxy initialization timeout"))
124+
}, 10000),
125+
),
126+
])
127+
} catch (error) {
128+
outputChannel.appendLine(
129+
`[ContextProxy] Failed to initialize, using fallback: ${error instanceof Error ? error.message : String(error)}`,
130+
)
131+
// Force creation even if slow
132+
contextProxy = await ContextProxy.getInstance(context)
133+
}
101134

102135
// Initialize code index managers for all workspace folders.
103136
const codeIndexManagers: CodeIndexManager[] = []
@@ -125,6 +158,45 @@ export async function activate(context: vscode.ExtensionContext) {
125158
// Initialize the provider *before* the Roo Code Cloud service.
126159
const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, mdmService)
127160

161+
// CRITICAL: Register commands early to ensure they're available on slow environments
162+
// This must happen before any long-running async operations
163+
try {
164+
registerCommands({ context, outputChannel, provider })
165+
outputChannel.appendLine("[Commands] Successfully registered all commands")
166+
} catch (error) {
167+
outputChannel.appendLine(
168+
`[Commands] CRITICAL: Failed to register commands: ${error instanceof Error ? error.message : String(error)}`,
169+
)
170+
// Attempt individual command registration as fallback
171+
try {
172+
const criticalCommands = [
173+
"settingsButtonClicked",
174+
"plusButtonClicked",
175+
"mcpButtonClicked",
176+
"historyButtonClicked",
177+
]
178+
for (const cmdId of criticalCommands) {
179+
try {
180+
const command = `${Package.name}.${cmdId}`
181+
if (!context.subscriptions.find((sub) => (sub as any).command === command)) {
182+
context.subscriptions.push(
183+
vscode.commands.registerCommand(command, () => {
184+
outputChannel.appendLine(`[Command] ${cmdId} invoked but handler not fully initialized`)
185+
vscode.window.showWarningMessage(
186+
`Extension is still initializing. Please try again in a moment.`,
187+
)
188+
}),
189+
)
190+
}
191+
} catch (cmdError) {
192+
outputChannel.appendLine(`[Commands] Failed to register fallback for ${cmdId}: ${cmdError}`)
193+
}
194+
}
195+
} catch (fallbackError) {
196+
outputChannel.appendLine(`[Commands] Failed to register fallback commands: ${fallbackError}`)
197+
}
198+
}
199+
128200
// Initialize Roo Code Cloud service.
129201
const postStateListener = () => ClineProvider.getVisibleInstance()?.postStateToWebview()
130202

@@ -225,7 +297,15 @@ export async function activate(context: vscode.ExtensionContext) {
225297
)
226298
}
227299

228-
registerCommands({ context, outputChannel, provider })
300+
// Commands already registered earlier, but re-register to ensure proper handlers
301+
// This overwrites the temporary handlers with the actual ones
302+
try {
303+
registerCommands({ context, outputChannel, provider })
304+
} catch (error) {
305+
outputChannel.appendLine(
306+
`[Commands] Failed to re-register commands with proper handlers: ${error instanceof Error ? error.message : String(error)}`,
307+
)
308+
}
229309

230310
/**
231311
* We use the text document content provider API to show the left side for diff

0 commit comments

Comments
 (0)