Skip to content

Commit 9540c8b

Browse files
SWangHashSWangHash
authored andcommitted
!121 merge fix/ohos-agent-auto-install into main
feat(acp): add platform-aware agent provisioning Created-by: guantw Commit-by: guantw Merged-by: SWangHash Description: ## Summary - Select ACP Agent presets by execution platform: HarmonyOS local runtimes expose Kimi Code, Qwen Code, and CodeBuddy Code, while standard local and remote runtimes keep the existing catalog. - Add managed HarmonyOS provisioning with formula-specific HarmonyBrew paths for Kimi Code and Qwen Code, plus HarmonyBrew-owned Node/npm and an OHOS compatibility shim for CodeBuddy Code. - Reuse already installed CLIs, add requirement and provisioning status reporting with cancellation-safe cleanup, and update localized setup actions and errors. ## Type and Areas Type: Feature / UI/UX / test Areas: ACP interface, desktop/Tauri API, Web UI settings, HarmonyOS provisioning, i18n ## Motivation / Impact HarmonyOS users can detect, install, and add supported ACP agents from BitFun without treating unrelated HNP tools as valid runtimes. Existing installations are reused instead of reinstalled. Standard local and remote workspaces continue to use the existing portable Agent catalog and npm-based setup path. ## Verification - `pnpm run fmt:rs` — passed. - `pnpm --dir src/web-ui exec vitest run src/infrastructure/config/components/AcpAgentsConfig.test.tsx` — passed (17/17). - Focused ESLint checks for `AcpAgentsConfig.tsx` and `AcpAgentsConfig.test.tsx` — passed. - `pnpm run check:repo-hygiene` — passed. - `git diff --check` — passed. - `pnpm --dir src/web-ui build` — passed. - Built, signed, inspected, and installed the HarmonyOS HAP on a USB device. - `pnpm run type-check:web` still reports pre-existing duplicate implementation errors in `src/web-ui/src/infrastructure/api/WorkspaceAPI.ts`; this PR does not modify that file. ## Reviewer Notes - HarmonyOS detection-only support for OpenCode is intentionally out of scope. - Managed provisioning does not modify the user's HarmonyBrew configuration; it resolves explicit HarmonyBrew executables and prefixes for child processes. - The HarmonyOS local path was exercised on a device. Standard local and remote runtime execution was not manually re-exercised; focused tests cover catalog selection and action states. ## Checklist - [x] This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts. - [x] Relevant verification is recorded above, or skipped checks are explained. - [x] User-facing strings, docs, and locales are updated where applicable. See merge request: OpenHarmonyPCDeveloper/BitFun!121
2 parents 940be2f + 9da9eb5 commit 9540c8b

22 files changed

Lines changed: 2710 additions & 122 deletions

File tree

‎Cargo.lock‎

Lines changed: 98 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/apps/cli/src/acp_cli.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ impl ExternalAcpClient {
6767
enabled: true,
6868
readonly: false,
6969
permission_mode: AcpClientPermissionMode::Ask,
70+
local_override: None,
7071
}
7172
}
7273
}

‎src/apps/desktop/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ tauri = { workspace = true }
3838
tauri-plugin-opener = { workspace = true }
3939
tauri-plugin-fs = { workspace = true }
4040
tauri-plugin-log = { workspace = true }
41-
tauri-plugin-dialog = { workspace = true }
4241
napi-ohos = { workspace = true }
4342
napi-derive-ohos = { workspace = true }
4443
keepawake = { workspace = true }
@@ -105,6 +104,7 @@ objc2-vision = { workspace = true, features = ["std", "VNRecognizeTextRequest",
105104
# Secret Service provider; the desktop host injects an ArkTS-backed vault
106105
# at startup instead.
107106
[target.'cfg(not(target_env = "ohos"))'.dependencies]
107+
tauri-plugin-dialog = { workspace = true }
108108
tauri-plugin-updater = { workspace = true }
109109
tauri-plugin-window-state = { workspace = true }
110110
tauri-plugin-single-instance = { workspace = true }

‎src/apps/desktop/capabilities/default.json‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"core:window:allow-set-min-size",
4747
"opener:default",
4848
"opener:allow-open-url",
49-
"dialog:default",
5049
{
5150
"identifier": "opener:allow-open-path",
5251
"allow": [
@@ -94,4 +93,4 @@
9493
]
9594
}
9695
]
97-
}
96+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "../gen/schemas/desktop-schema.json",
3+
"identifier": "desktop-dialog",
4+
"description": "Native dialog permissions for desktop platforms where the dialog plugin is available",
5+
"windows": ["main", "agent-companion-pet", "spotlight"],
6+
"permissions": ["dialog:default"],
7+
"platforms": ["macOS", "windows", "linux"]
8+
}

‎src/apps/desktop/src/api/acp_client_api.rs‎

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
use crate::api::app_state::AppState;
44
use crate::api::session_storage_path::desktop_effective_session_storage_path;
55
use crate::startup_trace::DesktopStartupTrace;
6+
#[cfg(target_env = "ohos")]
7+
use bitfun_acp::client::PROVISIONING_PROGRESS_EVENT;
68
use bitfun_acp::client::{
7-
AcpAvailableCommand, AcpClientInfo, AcpClientPermissionResponse, AcpClientRequirementProbe,
8-
AcpClientStreamEvent, AcpSessionOptions, CreateAcpFlowSessionRecordResponse,
9-
SetAcpSessionConfigOptionRequest, SetAcpSessionModelRequest,
10-
SubmitAcpPermissionResponseRequest,
9+
AcpAvailableCommand, AcpClientInfo, AcpClientInstallOutcome, AcpClientPermissionResponse,
10+
AcpClientRequirementProbe, AcpClientStreamEvent, AcpSessionOptions,
11+
CreateAcpFlowSessionRecordResponse, SetAcpSessionConfigOptionRequest,
12+
SetAcpSessionModelRequest, SubmitAcpPermissionResponseRequest,
1113
};
1214
use serde::{Deserialize, Serialize};
1315
use std::time::Instant;
@@ -19,6 +21,8 @@ pub struct AcpClientIdRequest {
1921
pub client_id: String,
2022
#[serde(default)]
2123
pub remote_connection_id: Option<String>,
24+
#[serde(default)]
25+
pub cancel_install: bool,
2226
}
2327

2428
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -184,16 +188,68 @@ pub async fn predownload_acp_client_adapter(
184188
#[tauri::command]
185189
pub async fn install_acp_client_cli(
186190
state: State<'_, AppState>,
191+
app_handle: AppHandle,
187192
request: AcpClientIdRequest,
188-
) -> Result<(), String> {
193+
) -> Result<AcpClientInstallOutcome, String> {
189194
let service = state
190195
.acp_client_service
191196
.as_ref()
192197
.ok_or_else(|| "ACP client service not initialized".to_string())?;
193-
service
194-
.install_client_cli(&request.client_id, request.remote_connection_id.as_deref())
195-
.await
196-
.map_err(|e| e.to_string())
198+
let remote_connection_id = request
199+
.remote_connection_id
200+
.as_deref()
201+
.map(str::trim)
202+
.filter(|value| !value.is_empty());
203+
204+
if request.cancel_install {
205+
if remote_connection_id.is_some() {
206+
return Err(
207+
"Remote ACP CLI installation cannot be cancelled by the local managed installer"
208+
.to_string(),
209+
);
210+
}
211+
if !service.cancel_managed_client_provisioning(&request.client_id) {
212+
return Err(format!(
213+
"No managed ACP installation is running for '{}'",
214+
request.client_id
215+
));
216+
}
217+
return Ok(AcpClientInstallOutcome::cancellation_requested(
218+
&request.client_id,
219+
));
220+
}
221+
222+
if let Some(remote_connection_id) = remote_connection_id {
223+
service
224+
.install_client_cli(&request.client_id, Some(remote_connection_id))
225+
.await
226+
.map_err(|e| e.to_string())?;
227+
return Ok(AcpClientInstallOutcome::cli_installed(&request.client_id));
228+
}
229+
230+
#[cfg(target_env = "ohos")]
231+
{
232+
let event_app_handle = app_handle.clone();
233+
let emit_progress = move |progress| {
234+
if let Err(error) = event_app_handle.emit(PROVISIONING_PROGRESS_EVENT, progress) {
235+
log::warn!("Failed to emit ACP provisioning progress: {}", error);
236+
}
237+
};
238+
return service
239+
.provision_managed_client(&request.client_id, emit_progress)
240+
.await
241+
.map_err(|e| e.to_string());
242+
}
243+
244+
#[cfg(not(target_env = "ohos"))]
245+
{
246+
let _ = app_handle;
247+
service
248+
.install_client_cli(&request.client_id, None)
249+
.await
250+
.map_err(|e| e.to_string())?;
251+
Ok(AcpClientInstallOutcome::cli_installed(&request.client_id))
252+
}
197253
}
198254

199255
#[tauri::command]

0 commit comments

Comments
 (0)