From bf41db266d78a329c80ced7fdb670e45394b2ce1 Mon Sep 17 00:00:00 2001 From: infiniti <52129260+developerInfiniti@users.noreply.github.com> Date: Mon, 25 May 2026 20:59:12 +0300 Subject: [PATCH] fix(opencode): explain managed profile link failures Co-authored-by: iliya --- ...TeamsRuntimeProviderManagementCliClient.ts | 45 +++++++++++++++++-- ...RuntimeProviderManagementCliClient.test.ts | 42 +++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/features/runtime-provider-management/main/infrastructure/AgentTeamsRuntimeProviderManagementCliClient.ts b/src/features/runtime-provider-management/main/infrastructure/AgentTeamsRuntimeProviderManagementCliClient.ts index 493956c5..802681f8 100644 --- a/src/features/runtime-provider-management/main/infrastructure/AgentTeamsRuntimeProviderManagementCliClient.ts +++ b/src/features/runtime-provider-management/main/infrastructure/AgentTeamsRuntimeProviderManagementCliClient.ts @@ -151,13 +151,17 @@ function sanitizeRuntimeProviderError(error: unknown): RuntimeProviderManagement ? (rawCode as RuntimeProviderManagementErrorDto['code']) : 'runtime-unhealthy'; const diagnostics = sanitizeRuntimeProviderDiagnostics(error.diagnostics); + const message = + sanitizeNullableRuntimeProviderText(error.message) ?? + 'Runtime provider management command failed'; return { code, - message: - sanitizeNullableRuntimeProviderText(error.message) ?? - 'Runtime provider management command failed', + message, recoverable: typeof error.recoverable === 'boolean' ? error.recoverable : true, - diagnostics: withRuntimeProviderErrorCode(code, diagnostics), + diagnostics: withRuntimeProviderErrorCode( + code, + diagnostics ?? buildOpenCodeProfileNodeModulesLinkDiagnostics(message) + ), }; } @@ -217,6 +221,39 @@ function sanitizeNullableRuntimeProviderText(value: unknown): string | null { return typeof value === 'string' ? sanitizeRuntimeProviderText(value) : null; } +function buildOpenCodeProfileNodeModulesLinkDiagnostics( + message: string +): RuntimeProviderManagementErrorDto['diagnostics'] { + const normalized = message.toLowerCase(); + const isAccessDeniedLinkFailure = + (normalized.includes('eperm') || normalized.includes('eacces')) && + normalized.includes('symlink') && + normalized.includes('opencode') && + normalized.includes('node_modules'); + if (!isAccessDeniedLinkFailure) { + return null; + } + + const summary = 'OpenCode managed profile node_modules link was blocked.'; + const likelyCause = + 'Windows denied creating the managed OpenCode profile node_modules link. Newer Agent Teams runtimes fall back to a junction or local profile directory.'; + return { + summary, + likelyCause, + binaryPath: null, + command: null, + projectPath: null, + exitCode: null, + stderrPreview: message, + stdoutPreview: null, + hints: [ + 'Update the Agent Teams runtime and refresh the OpenCode provider catalog.', + 'If you must use an older runtime, enable Windows Developer Mode or run Agent Teams AI as Administrator.', + 'If the error persists after updating, refresh again so the runtime can rebuild the managed OpenCode profile node_modules path.', + ], + }; +} + function extractJsonObject(raw: string): T { const start = raw.indexOf('{'); if (start < 0) { diff --git a/test/main/features/runtime-provider-management/AgentTeamsRuntimeProviderManagementCliClient.test.ts b/test/main/features/runtime-provider-management/AgentTeamsRuntimeProviderManagementCliClient.test.ts index a916ddde..368e006c 100644 --- a/test/main/features/runtime-provider-management/AgentTeamsRuntimeProviderManagementCliClient.test.ts +++ b/test/main/features/runtime-provider-management/AgentTeamsRuntimeProviderManagementCliClient.test.ts @@ -857,6 +857,48 @@ describe('AgentTeamsRuntimeProviderManagementCliClient', () => { expect(JSON.stringify(response)).not.toContain('sk-secret-value-123456'); }); + it('adds actionable diagnostics for OpenCode managed profile node_modules symlink failures', async () => { + const runtimeMessage = [ + 'Runtime provider management command failed unexpectedly:', + "EPERM: operation not permitted, symlink 'C:\\Users\\Swarog\\AppData\\Local\\claude-multimodel-nodejs\\Cache\\opencode\\shared-cache\\config-node_modules'", + "-> 'C:\\Users\\Swarog\\AppData\\Local\\claude-multimodel-nodejs\\Data\\opencode\\profiles\\abc123\\config\\opencode\\node_modules'", + ].join(' '); + const error = new Error('Command failed: /repo/cli-dev runtime providers view'); + Object.assign(error, { + stdout: JSON.stringify({ + schemaVersion: 1, + runtimeId: 'opencode', + error: { + code: 'runtime-unhealthy', + message: runtimeMessage, + recoverable: true, + }, + }), + stderr: '', + }); + execCliMock.mockRejectedValue(error); + + const client = new AgentTeamsRuntimeProviderManagementCliClient(); + const response = await client.loadView({ + runtimeId: 'opencode', + }); + + expect(response.error?.message).toBe(runtimeMessage); + expect(response.error?.diagnostics?.summary).toBe( + 'OpenCode managed profile node_modules link was blocked.' + ); + expect(response.error?.diagnostics?.likelyCause).toContain( + 'Windows denied creating the managed OpenCode profile node_modules link' + ); + expect(response.error?.diagnostics?.stderrPreview).toBe(runtimeMessage); + expect(response.error?.diagnostics?.hints).toEqual( + expect.arrayContaining([ + 'Update the Agent Teams runtime and refresh the OpenCode provider catalog.', + 'If you must use an older runtime, enable Windows Developer Mode or run Agent Teams AI as Administrator.', + ]) + ); + }); + it('does not let non-object error logs shadow a later valid runtime response', async () => { const validResponse = { schemaVersion: 1,