Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/apps/desktop/src/api/ohos/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod ohos_file_system;
pub mod window;
6 changes: 6 additions & 0 deletions src/apps/desktop/src/api/ohos/ohos_file_system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::Appstate;
use bitfun_core::util::open_dialog_file;
#[tauri::command]
pub async fn open_oh_file_dialog() -> Result<String, String> {
open_dialog_file().await
}
12 changes: 11 additions & 1 deletion src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use tauri::Manager;

// Re-export API
pub use api::*;

use std::path::PathBuf;
use api::ai_rules_api::*;
use api::clipboard_file_api::*;
use api::commands::*;
Expand Down Expand Up @@ -209,6 +209,16 @@ pub async fn _run() {
}

logging::register_runtime_log_state(startup_log_level, session_log_dir.clone());
{
let candidates = ["mobile-web/dist","mobile-web","dist"];
let mut found = false;
let path = PathBuf::from("/data/storage/el2/base/files/dist");
if path.join("index.html").exists() {
log::info!("Found bundled mobile-web at: {}", path.display());
api::remote_connect_api::set_mobile_web_resource_path(path);
found = true;
}
}

for step in startup_timings.steps() {
log::debug!(
Expand Down
32 changes: 19 additions & 13 deletions src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class EntryAbility extends RustAbility {
public moduleName: string = "bitfun_desktop_lib";
public defaultPage: boolean = true;
public commonEventListener: CommonEventListener | undefined = undefined;
public remote_url: string = "";

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
super.onCreate(want, launchParam);
Expand Down Expand Up @@ -97,6 +98,11 @@ export default class EntryAbility extends RustAbility {
runDeveco(this.context, arg);
return '';
});
RustModule.registerArktsFunction('send_remote_url', async (err: Error, arg: string): Promise<string> => {
hilog.info(DOMAIN_NUMBER, TAG, 'get remote url ' + arg);
this.remote_url = arg;
return '';
});
RustModule.registerArktsFunction('harmony_create', async (err: Error, arg: string): Promise<string> => {
await fileIo.copyDir('/storage/Users/currentUser/Documents/DevecoStudioProjects/MyApplication',
'/storage/Users/currentUser/Documents/files', 1).then(() => {
Expand All @@ -122,19 +128,19 @@ export default class EntryAbility extends RustAbility {
}

private sendOnlyCallback = (sharableTable: harmonyShare.SharableTarget) => {
let filePath = "/data/storage/el2/base/files/dist/output.txt";
fileIo.readText(filePath).then((content: string) => {
console.info("readText success:" + content);
let shareData: systemShare.SharedData = new systemShare.SharedData({
utd: uniformTypeDescriptor.UniformDataType.HYPERLINK,
content,
title: "Bitfun",
description: "Phone",
});
sharableTable.share(shareData)
}).catch((err: BusinessError) => {
hilog.error(DOMAIN, 'vnext', 'sendOnlyCallback error:' + err);
});
if (this.remote_url.length == 0) {
let content = this.remote_url;
let shareData: systemShare.SharedData = new systemShare.SharedData({
utd: uniformTypeDescriptor.UniformDataType.HYPERLINK,
content,
title: "Bitfun",
description: "Phone",
});
sharableTable.share(shareData)
}
else {
hilog.error(DOMAIN, 'vnext', 'sendOnlyCallback error: remote url is empty');
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ crate-type = ["rlib"]
[dependencies]
# Inherit shared dependencies from workspace
tokio = { workspace = true }
napi-ohos = { workspace = true }
napi-derive-ohos = { workspace = true }
tokio-stream = { workspace = true }
tokio-util = { workspace = true }
async-trait = { workspace = true }
Expand All @@ -38,7 +40,9 @@ aes = "0.8"
hex = "0.4"
dashmap = { workspace = true }
indexmap = { workspace = true }

lazy_static = "1.4"
once_cell = "1.19"
parking_lot = "0.12"
reqwest = { workspace = true }

# Debug Log HTTP Server
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
use crate::agentic::tools::framework::{
Tool, ToolRenderOptions, ToolResult, ToolUseContext, ValidationResult,
};
use napi_ohos::threadsafe_function::ThreadsafeFunctionCallMode;
use crate::util::errors::{BitFunError, BitFunResult};
use async_trait::async_trait;
use napi_derive_ohos::napi;
use parking_lot::{Condvar, Mutex};
use serde_json::{json, Value};
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use crate::util::JS_THREADSAFE_FUNCTION;
struct BuildState {
result: Option<String>,
notified: bool,
}

static BUILD_STATE: once_cell::sync::Lazy<Arc<(Mutex<BuildState>, Condvar)>> =
once_cell::sync::Lazy::new(|| {
Arc::new((
Mutex::new(BuildState {
result: None,
notified: false,
}),
Condvar::new(),
))
});

pub struct HarmonyBuildTool {}

impl HarmonyBuildTool {
pub fn new() -> Self {
Self {}
}

fn validate_project_path(&self, project_path: &str) -> bool {
let path = Path::new(project_path);
path.exists() && path.is_dir()
}

async fn execute_build(&self, project_path: &str) -> BitFunResult<String> {
log::info!("HarmonyOS build for project: {}", project_path);

{
let (lock, cvar) = &**BUILD_STATE;
let mut state = lock.lock();
state.result = None;
state.notified = false;
cvar.notify_all();
}

match call_harmony_build(project_path.to_string()) {
Ok(_) => {
log::info!("call_harmony_build success");
let timeout = Duration::from_secs(60);
let (lock, cvar) = &**BUILD_STATE;
let mut state = lock.lock();

let wait_result = cvar.wait_for(&mut state, timeout);
if !wait_result.timed_out() && state.notified {
if let Some(msg) = &state.result {
log::info!("Build result received: {}", msg);
return Ok(msg.clone());
}
}

log::error!("Build timeout");
Err(BitFunError::tool(
"Build timeout: no result received within 1 minute".to_string(),
))
}
Err(_) => {
log::error!("call_harmony_build failed");
Err(BitFunError::tool(
"Build failed: call_harmony_build failed".to_string(),
))
}
}
}
}

#[async_trait]
impl Tool for HarmonyBuildTool {
fn name(&self) -> &str {
"HarmonyBuild"
}

async fn description(&self) -> BitFunResult<String> {
Ok(
r#"HarmonyOS application build tool. Builds a HarmonyOS project.

Usage:
- The project_path parameter must be an absolute path to a HarmonyOS project

Example:
- Build project: {"project_path": "path/to/harmony/project"}"#
.to_string(),
)
}

fn input_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"project_path": {
"type": "string",
"description": "The absolute path to the HarmonyOS project"
}
},
"required": [ "project_path" ],
"additionalProperties": false
})
}

fn is_readonly(&self) -> bool {
false
}

fn is_concurrency_safe(&self, _input: Option<&Value>) -> bool {
false
}

fn needs_permissions(&self, _input: Option<&Value>) -> bool {
true
}

async fn validate_input(
&self,
input: &Value,
_context: Option<&ToolUseContext>,
) -> ValidationResult {
let project_path = match input.get("project_path").and_then(|v| v.as_str()) {
Some(path) => path,
None => {
return ValidationResult {
result: false,
message: Some("project_path is required".to_string()),
error_code: Some(400),
meta: None,
};
}
};

if project_path.is_empty() {
return ValidationResult {
result: false,
message: Some("project_path cannot be empty".to_string()),
error_code: Some(400),
meta: None,
};
}

if !self.validate_project_path(project_path) {
return ValidationResult {
result: false,
message: Some(format!(
"Project path does not exist or is not a directory: {}",
project_path
)),
error_code: Some(404),
meta: None,
};
}

ValidationResult {
result: true,
message: None,
error_code: None,
meta: None,
}
}

fn render_tool_use_message(&self, input: &Value, options: &ToolRenderOptions) -> String {
let project_path = input
.get("project_path")
.and_then(|v| v.as_str())
.unwrap_or("");

if options.verbose {
format!("HarmmonyOS build on project: {}", project_path)
} else {
format!("HarmonyOS build: {}", project_path)
}
}

async fn call_impl(
&self,
input: &Value,
_context: &ToolUseContext,
) -> BitFunResult<Vec<ToolResult>> {
let project_path = input
.get("project_path")
.and_then(|v| v.as_str())
.ok_or_else(|| BitFunError::tool("project_path is required".to_string()))?;

let result = self.execute_build(project_path).await?;

Ok(vec![ToolResult::Result {
data: json!({
"project_path": project_path,
"success": true
}),
result_for_assistant: Some(result),
image_attachments: None,
}])
}
}
#[napi]
pub fn set_build_result(msg: String) {
log::info!("set_build_result msg: {}", msg);
let (lock, cvar) = &**BUILD_STATE;
let mut state = lock.lock();
state.result = Some(msg);
state.notified = true;
cvar.notify_all();
}
pub fn call_harmony_build(args: String) -> Result<String, String> {
let result = Ok(args);
let results = Arc::new(Mutex::new(String::default()));
match JS_THREADSAFE_FUNCTION.write().get("call_harmony_build") {
None => {
log::error!("call_harmony_build has not register");
Err("The Arkts has not register the function".to_owned())
}
Some(function) => {
function.call_with_return_value(
result,
ThreadsafeFunctionCallMode::Blocking,
move |result, _| {
match result {
Ok(_) => {
log::info!("call_harmony_build successfully");
}
Err(err) => {
log::error!("call_harmony_build failed with error: {}", err);
}
}
Ok(())
},
);
let res = results.lock().to_string();
Ok(res)
}
}
}
2 changes: 1 addition & 1 deletion src/crates/core/src/agentic/tools/implementations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub mod terminal_control_tool;
pub mod todo_write_tool;
pub mod util;
pub mod web_tools;

pub mod harmony_build_tool;
pub use ask_user_question_tool::AskUserQuestionTool;
pub use bash_tool::BashTool;
pub use code_review_tool::CodeReviewTool;
Expand Down
Loading
Loading