fix(agents): allow WorkBuddy configuration without detected app

This commit is contained in:
lzt404 committed 2026-09-23 02:47:28 +08:00
1 parent 0bb6eab4cb
commit b0f69877f8
6 files changed
+50 -7

No files matched your search

+2 -1
View File
@@ -1257,7 +1257,8 @@ pub(crate) fn validate_agent_can_enable(
));
}
let detection = inspect_agent_config(client, home, port, api_key);
if !detection.installed {
let workbuddy_config_available = client == AgentClient::WorkBuddy && detection.config_exists;
if !detection.installed && !workbuddy_config_available {
return Err(format!("{} is not installed", client.name()));
}
Ok(())
+6 -1
View File
@@ -1003,7 +1003,12 @@ pub(crate) fn inspect_agent_config(
if !client.supported_platform() {
warnings.push("当前平台不支持 Claude Desktop 3P 配置".to_string());
} else if !installed && config_exists {
warnings.push("只检测到配置文件,未检测到客户端".to_string());
warnings.push(if client == AgentClient::WorkBuddy {
"只检测到 WorkBuddy 配置文件,仍可编辑配置并接入 CPA;启动功能不可用"
.to_string()
} else {
"只检测到配置文件,未检测到客户端".to_string()
});
}
if let Some(message) = error.as_ref() {
warnings.push(message.clone());
+25
View File
@@ -181,6 +181,31 @@ fn workbuddy_status_checks_endpoint_key_visibility_and_tool_support() {
);
}
#[test]
fn workbuddy_config_file_allows_cpa_connection_without_detected_application() {
let home = Home::new();
home.save(&json!({}));
let status = inspect_agent_config(AgentClient::WorkBuddy, &home.0, 8317, "test-key");
assert!(!status.installed);
assert!(status.config_exists);
assert!(status.config_valid);
assert!(status.launch_targets.is_empty());
assert!(validate_agent_can_enable(
AgentClient::WorkBuddy,
&home.0,
8317,
"test-key"
)
.is_ok());
home.apply("gpt-one").unwrap();
let connected = inspect_agent_config(AgentClient::WorkBuddy, &home.0, 8317, "test-key");
assert!(!connected.installed);
assert!(connected.configured);
assert_eq!(connected.current_model.as_deref(), Some("gpt-one"));
}
#[test]
fn workbuddy_visibility_restores_original_references_and_keeps_user_edits() {
for external_edit in [false, true] {
+8 -4
View File
@@ -102,6 +102,7 @@ type AgentConfigStatus = {
cliVersion: string | null;
appVersion: string | null;
pluginVersion: string | null;
configExists: boolean;
configValid: boolean;
configured: boolean;
connectionState: 'configured' | 'not-configured' | 'needs-update' | 'invalid';
@@ -1196,9 +1197,12 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
&& claudeModelMappingsDraft.autoCompactPct >= 1
&& claudeModelMappingsDraft.autoCompactPct <= 100
);
const canEnable = Boolean(
const canConfigureActiveClient = Boolean(
activeStatus?.supportedPlatform
&& activeStatus.installed
&& (activeStatus.installed || (selected === 'workbuddy' && activeStatus.configExists)),
);
const canEnable = Boolean(
canConfigureActiveClient
&& !modelLoading
&& (isClaudeModelMappingClient
? claudeMappingsReady && claudeCodeRuntimeSettingsReady
@@ -2091,7 +2095,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
value={isClaudeModelMappingClient ? claudeModelMappingsDraft.sonnet : selectedModel}
loading={modelLoading}
error={modelError}
disabled={busy || !activeStatus?.installed || !activeStatus.supportedPlatform}
disabled={busy || !canConfigureActiveClient}
onChange={selectEmbeddedModel}
onRefresh={refreshModels}
/>
@@ -2187,7 +2191,7 @@ export function AgentsPage({ embedded = false, onConfigurationApplied }: AgentsP
value={selectedModel}
loading={modelLoading}
error={modelError}
disabled={busy || !activeStatus?.installed || !activeStatus.supportedPlatform}
disabled={busy || !canConfigureActiveClient}
onChange={selectModel}
onRefresh={refreshModels}
/>
+8
View File
@@ -141,6 +141,14 @@ const path = require('node:path');
await open(mode + 'not-installed');
assert.ok(await button('启动 App').isDisabled());
assert.ok(await button('重启 App').isDisabled());
await open(mode + 'client=workbuddy&config-only&fresh');
assert.ok(await page.locator('.agent-model-trigger').isEnabled());
assert.ok(await button('一键接入').isEnabled());
assert.ok(await button('启动 App').isDisabled());
assert.ok(await button('重启 App').isDisabled());
await button('一键接入').click();
await page.waitForFunction(() => window.fixtureCalls.some(call => call.cmd === 'update_agent_config'));
assert.deepEqual((await calls('update_agent_config'))[0].args.client, 'workbuddy');
await open(mode + 'client=kimi-code');
assert.equal(await button('重启 App').count(), 0);
+1 -1
View File
@@ -35,7 +35,7 @@ mockIPC(async (cmd,args:any) => {
calls.push({cmd,args});
if(cmd==='plugin:event|listen') return 1;
if(cmd==='plugin:event|unlisten'||cmd==='set_app_locale') return null;
if(cmd==='get_agent_config_statuses'||cmd==='refresh_agent_config_statuses') return ids.map(id=>({id,name:id,supportedPlatform:true,installed:!params.has('not-installed'),pluginInstalled:!params.has('no-plugin'),launchTargets:params.has('not-installed')?[]:['claude-desktop','zcode','workbuddy'].includes(id)?[{id:'app',label:id,detail:'test desktop'}]:['codex','opencode'].includes(id)&&!params.has('cli-only')?[...(params.has('app-only')?[]:[{id:'cli',label:'CLI',detail:'test CLI'}]),{id:'app',label:'APP',detail:'test desktop'}]:[{id:'cli',label:'CLI',detail:'test CLI'}],version:'1.0',cliVersion:'1.0',appVersion:null,pluginVersion:'1.0',configValid:params.get('state')!=='invalid',connectionState:params.get('state') || (currentModel?'configured':'not-configured'),configured:!!currentModel,configurationSynchronized:!!currentModel,currentModel,oauthConfiguration:id==='codex'&&currentOauth,codexNativeOauth:id==='codex'&&nativeOauth,modificationEnabled:!!currentModel,modificationState:currentModel?'applied':'unconfigured',backupAvailable:false,appliedModel:currentModel,claudeCodeModelMappings:id==='claude-code'?currentMappings[id]??null:null,claudeDesktopModelMappings:id==='claude-desktop'?currentMappings[id]??null:null,warnings:[],error:null})).map(status=>status.id==='codex'&&(nativeOauth||codexClosed)?{...status,configured:false,connectionState:'not-configured',configurationSynchronized:false,currentModel:null,appliedModel:null,modificationEnabled:false,modificationState:'unconfigured',oauthConfiguration:false}:status);
if(cmd==='get_agent_config_statuses'||cmd==='refresh_agent_config_statuses') return ids.map(id=>({id,name:id,supportedPlatform:true,installed:!params.has('not-installed')&&!(params.has('config-only')&&id==='workbuddy'),pluginInstalled:!params.has('no-plugin'),launchTargets:params.has('not-installed')||(params.has('config-only')&&id==='workbuddy')?[]:['claude-desktop','zcode','workbuddy'].includes(id)?[{id:'app',label:id,detail:'test desktop'}]:['codex','opencode'].includes(id)&&!params.has('cli-only')?[...(params.has('app-only')?[]:[{id:'cli',label:'CLI',detail:'test CLI'}]),{id:'app',label:'APP',detail:'test desktop'}]:[{id:'cli',label:'CLI',detail:'test CLI'}],version:'1.0',cliVersion:'1.0',appVersion:null,pluginVersion:'1.0',configExists:!params.has('not-installed')||(params.has('config-only')&&id==='workbuddy'),configValid:params.get('state')!=='invalid',connectionState:params.get('state') || (currentModel?'configured':'not-configured'),configured:!!currentModel,configurationSynchronized:!!currentModel,currentModel,oauthConfiguration:id==='codex'&&currentOauth,codexNativeOauth:id==='codex'&&nativeOauth,modificationEnabled:!!currentModel,modificationState:currentModel?'applied':'unconfigured',backupAvailable:false,appliedModel:currentModel,claudeCodeModelMappings:id==='claude-code'?currentMappings[id]??null:null,claudeDesktopModelMappings:id==='claude-desktop'?currentMappings[id]??null:null,warnings:[],error:null})).map(status=>status.id==='codex'&&(nativeOauth||codexClosed)?{...status,configured:false,connectionState:'not-configured',configurationSynchronized:false,currentModel:null,appliedModel:null,modificationEnabled:false,modificationState:'unconfigured',oauthConfiguration:false}:status);
if(cmd==='close_codex_config_modification') {
closeCount++;
if(params.has('fail-close')&&closeCount===1)throw new Error('模拟关闭失败,已回滚');