fix(extensions): use safe legacy multimodel capability fallback
This commit is contained in:
parent
5007f3eebb
commit
8423656b97
4 changed files with 125 additions and 4 deletions
|
|
@ -1,7 +1,10 @@
|
||||||
import { execCli } from '@main/utils/childProcess';
|
import { execCli } from '@main/utils/childProcess';
|
||||||
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities';
|
import {
|
||||||
|
createDefaultCliExtensionCapabilities,
|
||||||
|
createLegacyRuntimeFallbackCliExtensionCapabilities,
|
||||||
|
} from '@shared/utils/providerExtensionCapabilities';
|
||||||
|
|
||||||
import { resolveGeminiRuntimeAuth } from './geminiRuntimeAuth';
|
import { resolveGeminiRuntimeAuth } from './geminiRuntimeAuth';
|
||||||
import { buildProviderAwareCliEnv } from './providerAwareCliEnv';
|
import { buildProviderAwareCliEnv } from './providerAwareCliEnv';
|
||||||
|
|
@ -145,7 +148,7 @@ function createDefaultProviderStatus(providerId: CliProviderId): CliProviderStat
|
||||||
capabilities: {
|
capabilities: {
|
||||||
teamLaunch: false,
|
teamLaunch: false,
|
||||||
oneShot: false,
|
oneShot: false,
|
||||||
extensions: createDefaultCliExtensionCapabilities(),
|
extensions: createLegacyRuntimeFallbackCliExtensionCapabilities(),
|
||||||
},
|
},
|
||||||
selectedBackendId: null,
|
selectedBackendId: null,
|
||||||
resolvedBackendId: null,
|
resolvedBackendId: null,
|
||||||
|
|
@ -159,7 +162,9 @@ function createDefaultProviderStatus(providerId: CliProviderId): CliProviderStat
|
||||||
function mapRuntimeExtensionCapabilities(
|
function mapRuntimeExtensionCapabilities(
|
||||||
capabilities?: RuntimeExtensionCapabilitiesResponse
|
capabilities?: RuntimeExtensionCapabilitiesResponse
|
||||||
): CliProviderStatus['capabilities']['extensions'] {
|
): CliProviderStatus['capabilities']['extensions'] {
|
||||||
const defaults = createDefaultCliExtensionCapabilities();
|
const defaults = capabilities
|
||||||
|
? createDefaultCliExtensionCapabilities()
|
||||||
|
: createLegacyRuntimeFallbackCliExtensionCapabilities();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,27 @@ const SUPPORTED_SHARED_CAPABILITY: CliExtensionCapability = {
|
||||||
reason: null,
|
reason: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LEGACY_MULTIMODEL_FALLBACK_CAPABILITIES: CliExtensionCapabilities = {
|
||||||
|
plugins: {
|
||||||
|
status: 'unsupported',
|
||||||
|
ownership: 'shared',
|
||||||
|
reason:
|
||||||
|
'This runtime does not declare plugin capability support. Upgrade the runtime to manage plugins here.',
|
||||||
|
},
|
||||||
|
mcp: {
|
||||||
|
status: 'read-only',
|
||||||
|
ownership: 'shared',
|
||||||
|
reason:
|
||||||
|
'This runtime does not declare MCP management support. Upgrade the runtime to install or remove MCP servers here.',
|
||||||
|
},
|
||||||
|
skills: {
|
||||||
|
...SUPPORTED_SHARED_CAPABILITY,
|
||||||
|
},
|
||||||
|
apiKeys: {
|
||||||
|
...SUPPORTED_SHARED_CAPABILITY,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export function createDefaultCliExtensionCapabilities(
|
export function createDefaultCliExtensionCapabilities(
|
||||||
overrides?: Partial<CliExtensionCapabilities>
|
overrides?: Partial<CliExtensionCapabilities>
|
||||||
): CliExtensionCapabilities {
|
): CliExtensionCapabilities {
|
||||||
|
|
@ -22,10 +43,22 @@ export function createDefaultCliExtensionCapabilities(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createLegacyRuntimeFallbackCliExtensionCapabilities(
|
||||||
|
overrides?: Partial<CliExtensionCapabilities>
|
||||||
|
): CliExtensionCapabilities {
|
||||||
|
return {
|
||||||
|
plugins: { ...LEGACY_MULTIMODEL_FALLBACK_CAPABILITIES.plugins },
|
||||||
|
mcp: { ...LEGACY_MULTIMODEL_FALLBACK_CAPABILITIES.mcp },
|
||||||
|
skills: { ...LEGACY_MULTIMODEL_FALLBACK_CAPABILITIES.skills },
|
||||||
|
apiKeys: { ...LEGACY_MULTIMODEL_FALLBACK_CAPABILITIES.apiKeys },
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function getCliProviderExtensionCapabilities(
|
export function getCliProviderExtensionCapabilities(
|
||||||
provider: Pick<CliProviderStatus, 'capabilities'>
|
provider: Pick<CliProviderStatus, 'capabilities'>
|
||||||
): CliExtensionCapabilities {
|
): CliExtensionCapabilities {
|
||||||
return provider.capabilities.extensions ?? createDefaultCliExtensionCapabilities();
|
return provider.capabilities.extensions ?? createLegacyRuntimeFallbackCliExtensionCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCliProviderExtensionCapability(
|
export function getCliProviderExtensionCapability(
|
||||||
|
|
|
||||||
|
|
@ -254,4 +254,43 @@ describe('ClaudeMultimodelBridgeService', () => {
|
||||||
});
|
});
|
||||||
expect(provider.statusMessage).toContain('ANTHROPIC_API_KEY');
|
expect(provider.statusMessage).toContain('ANTHROPIC_API_KEY');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back conservatively when the runtime omits extension capability metadata', async () => {
|
||||||
|
execCliMock.mockResolvedValue({
|
||||||
|
stdout: JSON.stringify({
|
||||||
|
providers: {
|
||||||
|
codex: {
|
||||||
|
supported: true,
|
||||||
|
authenticated: true,
|
||||||
|
verificationState: 'verified',
|
||||||
|
canLoginFromUi: true,
|
||||||
|
capabilities: {
|
||||||
|
teamLaunch: true,
|
||||||
|
oneShot: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ClaudeMultimodelBridgeService } =
|
||||||
|
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
|
||||||
|
const service = new ClaudeMultimodelBridgeService();
|
||||||
|
|
||||||
|
const provider = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'codex');
|
||||||
|
|
||||||
|
expect(provider).toMatchObject({
|
||||||
|
providerId: 'codex',
|
||||||
|
capabilities: {
|
||||||
|
extensions: {
|
||||||
|
plugins: { status: 'unsupported' },
|
||||||
|
mcp: { status: 'read-only' },
|
||||||
|
skills: { status: 'supported' },
|
||||||
|
apiKeys: { status: 'supported' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
44
test/shared/utils/providerExtensionCapabilities.test.ts
Normal file
44
test/shared/utils/providerExtensionCapabilities.test.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createLegacyRuntimeFallbackCliExtensionCapabilities,
|
||||||
|
getCliProviderExtensionCapabilities,
|
||||||
|
} from '@shared/utils/providerExtensionCapabilities';
|
||||||
|
|
||||||
|
import type { CliProviderStatus } from '@shared/types';
|
||||||
|
|
||||||
|
function makeProvider(
|
||||||
|
overrides?: Partial<CliProviderStatus>
|
||||||
|
): Pick<CliProviderStatus, 'capabilities'> {
|
||||||
|
return {
|
||||||
|
capabilities: {
|
||||||
|
teamLaunch: false,
|
||||||
|
oneShot: false,
|
||||||
|
...(overrides?.capabilities ?? {}),
|
||||||
|
} as CliProviderStatus['capabilities'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('providerExtensionCapabilities', () => {
|
||||||
|
it('returns conservative fallback capabilities when runtime omits extension metadata', () => {
|
||||||
|
const capabilities = getCliProviderExtensionCapabilities(
|
||||||
|
makeProvider({
|
||||||
|
capabilities: {
|
||||||
|
teamLaunch: true,
|
||||||
|
oneShot: true,
|
||||||
|
} as CliProviderStatus['capabilities'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(capabilities).toEqual(createLegacyRuntimeFallbackCliExtensionCapabilities());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps plugins unsupported and mcp read-only in the legacy multimodel fallback', () => {
|
||||||
|
const capabilities = createLegacyRuntimeFallbackCliExtensionCapabilities();
|
||||||
|
|
||||||
|
expect(capabilities.plugins.status).toBe('unsupported');
|
||||||
|
expect(capabilities.mcp.status).toBe('read-only');
|
||||||
|
expect(capabilities.skills.status).toBe('supported');
|
||||||
|
expect(capabilities.apiKeys.status).toBe('supported');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue