fix(opencode): explain managed profile link failures

Co-authored-by: iliya <iliyazelenkog@gmail.com>
This commit is contained in:
infiniti 2026-05-25 20:59:12 +03:00 committed by GitHub
parent 7e5fa14b75
commit bf41db266d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 83 additions and 4 deletions

View file

@ -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<T>(raw: string): T {
const start = raw.indexOf('{');
if (start < 0) {

View file

@ -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,