Skip to content

Commit 885783e

Browse files
qin-ctxclaude
andauthored
fix(client): 修复单文件在远程server部署时上传失败问题 (#331) (#352)
add-resource/add-skill 添加单个文件到远程 server 时,之前只处理了目录场景 (zip后上传),单文件直接传本地路径导致 server 端报 Path does not exist。 现在对 Python HTTP client 和 Rust CLI client 的 add_resource/add_skill 方法 均增加 is_file() 分支,单文件直接通过 _upload_temp_file 上传。 附带修复: - Rust CLI config 支持 OPENVIKING_CLI_CONFIG_FILE 环境变量(与 Python 侧一致) - Rust CLI add-resource --timeout 改为可选参数(仅配合 --wait 使用) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 46b3e76 commit 885783e

File tree

4 files changed

+123
-50
lines changed

4 files changed

+123
-50
lines changed

crates/ov_cli/src/client.rs

Lines changed: 85 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -433,30 +433,63 @@ impl HttpClient {
433433
directly_upload_media: bool,
434434
) -> Result<serde_json::Value> {
435435
let path_obj = Path::new(path);
436-
437-
// Check if it's a local directory and not a local server
438-
if path_obj.exists() && path_obj.is_dir() && !self.is_local_server() {
439-
// Zip the directory
440-
let zip_file = self.zip_directory(path_obj)?;
441-
let temp_path = self.upload_temp_file(zip_file.path()).await?;
442-
443-
let body = serde_json::json!({
444-
"temp_path": temp_path,
445-
"target": target,
446-
"reason": reason,
447-
"instruction": instruction,
448-
"wait": wait,
449-
"timeout": timeout,
450-
"strict": strict,
451-
"ignore_dirs": ignore_dirs,
452-
"include": include,
453-
"exclude": exclude,
454-
"directly_upload_media": directly_upload_media,
455-
});
456-
457-
self.post("/api/v1/resources", &body).await
436+
437+
if path_obj.exists() && !self.is_local_server() {
438+
if path_obj.is_dir() {
439+
let zip_file = self.zip_directory(path_obj)?;
440+
let temp_path = self.upload_temp_file(zip_file.path()).await?;
441+
442+
let body = serde_json::json!({
443+
"temp_path": temp_path,
444+
"target": target,
445+
"reason": reason,
446+
"instruction": instruction,
447+
"wait": wait,
448+
"timeout": timeout,
449+
"strict": strict,
450+
"ignore_dirs": ignore_dirs,
451+
"include": include,
452+
"exclude": exclude,
453+
"directly_upload_media": directly_upload_media,
454+
});
455+
456+
self.post("/api/v1/resources", &body).await
457+
} else if path_obj.is_file() {
458+
let temp_path = self.upload_temp_file(path_obj).await?;
459+
460+
let body = serde_json::json!({
461+
"temp_path": temp_path,
462+
"target": target,
463+
"reason": reason,
464+
"instruction": instruction,
465+
"wait": wait,
466+
"timeout": timeout,
467+
"strict": strict,
468+
"ignore_dirs": ignore_dirs,
469+
"include": include,
470+
"exclude": exclude,
471+
"directly_upload_media": directly_upload_media,
472+
});
473+
474+
self.post("/api/v1/resources", &body).await
475+
} else {
476+
let body = serde_json::json!({
477+
"path": path,
478+
"target": target,
479+
"reason": reason,
480+
"instruction": instruction,
481+
"wait": wait,
482+
"timeout": timeout,
483+
"strict": strict,
484+
"ignore_dirs": ignore_dirs,
485+
"include": include,
486+
"exclude": exclude,
487+
"directly_upload_media": directly_upload_media,
488+
});
489+
490+
self.post("/api/v1/resources", &body).await
491+
}
458492
} else {
459-
// Regular case - use path directly
460493
let body = serde_json::json!({
461494
"path": path,
462495
"target": target,
@@ -470,7 +503,7 @@ impl HttpClient {
470503
"exclude": exclude,
471504
"directly_upload_media": directly_upload_media,
472505
});
473-
506+
474507
self.post("/api/v1/resources", &body).await
475508
}
476509
}
@@ -483,16 +516,34 @@ impl HttpClient {
483516
) -> Result<serde_json::Value> {
484517
let path_obj = Path::new(data);
485518

486-
if path_obj.exists() && path_obj.is_dir() && !self.is_local_server() {
487-
let zip_file = self.zip_directory(path_obj)?;
488-
let temp_path = self.upload_temp_file(zip_file.path()).await?;
489-
490-
let body = serde_json::json!({
491-
"temp_path": temp_path,
492-
"wait": wait,
493-
"timeout": timeout,
494-
});
495-
self.post("/api/v1/skills", &body).await
519+
if path_obj.exists() && !self.is_local_server() {
520+
if path_obj.is_dir() {
521+
let zip_file = self.zip_directory(path_obj)?;
522+
let temp_path = self.upload_temp_file(zip_file.path()).await?;
523+
524+
let body = serde_json::json!({
525+
"temp_path": temp_path,
526+
"wait": wait,
527+
"timeout": timeout,
528+
});
529+
self.post("/api/v1/skills", &body).await
530+
} else if path_obj.is_file() {
531+
let temp_path = self.upload_temp_file(path_obj).await?;
532+
533+
let body = serde_json::json!({
534+
"temp_path": temp_path,
535+
"wait": wait,
536+
"timeout": timeout,
537+
});
538+
self.post("/api/v1/skills", &body).await
539+
} else {
540+
let body = serde_json::json!({
541+
"data": data,
542+
"wait": wait,
543+
"timeout": timeout,
544+
});
545+
self.post("/api/v1/skills", &body).await
546+
}
496547
} else {
497548
let body = serde_json::json!({
498549
"data": data,

crates/ov_cli/src/config.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::path::PathBuf;
33

44
use crate::error::{Error, Result};
55

6+
const OPENVIKING_CLI_CONFIG_ENV: &str = "OPENVIKING_CLI_CONFIG_FILE";
7+
68
#[derive(Debug, Clone, Serialize, Deserialize)]
79
pub struct Config {
810
#[serde(default = "default_url")]
@@ -53,6 +55,14 @@ impl Config {
5355
}
5456

5557
pub fn load_default() -> Result<Self> {
58+
// Resolution order: env var > default path
59+
if let Ok(env_path) = std::env::var(OPENVIKING_CLI_CONFIG_ENV) {
60+
let p = PathBuf::from(env_path);
61+
if p.exists() {
62+
return Self::from_file(&p.to_string_lossy());
63+
}
64+
}
65+
5666
let config_path = default_config_path()?;
5767
if config_path.exists() {
5868
Self::from_file(&config_path.to_string_lossy())

crates/ov_cli/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ enum Commands {
7474
/// Wait until processing is complete
7575
#[arg(long)]
7676
wait: bool,
77-
/// Wait timeout in seconds
77+
/// Wait timeout in seconds (only used with --wait)
7878
#[arg(long)]
79-
timeout: f64,
79+
timeout: Option<f64>,
8080
/// No strict mode for directory scanning
8181
#[arg(long = "no-strict", default_value_t = false)]
8282
no_strict: bool,
@@ -588,7 +588,7 @@ async fn handle_add_resource(
588588
reason: String,
589589
instruction: String,
590590
wait: bool,
591-
timeout: f64,
591+
timeout: Option<f64>,
592592
no_strict: bool,
593593
ignore_dirs: Option<String>,
594594
include: Option<String>,
@@ -635,7 +635,7 @@ async fn handle_add_resource(
635635
reason,
636636
instruction,
637637
wait,
638-
Some(timeout),
638+
timeout,
639639
strict,
640640
ignore_dirs,
641641
include,

openviking_cli/client/http.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,19 @@ async def add_resource(
304304
}
305305

306306
path_obj = Path(path)
307-
if path_obj.exists() and path_obj.is_dir() and not self._is_local_server():
308-
zip_path = self._zip_directory(path)
309-
try:
310-
temp_path = await self._upload_temp_file(zip_path)
307+
if path_obj.exists() and not self._is_local_server():
308+
if path_obj.is_dir():
309+
zip_path = self._zip_directory(path)
310+
try:
311+
temp_path = await self._upload_temp_file(zip_path)
312+
request_data["temp_path"] = temp_path
313+
finally:
314+
Path(zip_path).unlink(missing_ok=True)
315+
elif path_obj.is_file():
316+
temp_path = await self._upload_temp_file(path)
311317
request_data["temp_path"] = temp_path
312-
finally:
313-
Path(zip_path).unlink(missing_ok=True)
318+
else:
319+
request_data["path"] = path
314320
else:
315321
request_data["path"] = path
316322

@@ -334,13 +340,19 @@ async def add_skill(
334340

335341
if isinstance(data, str):
336342
path_obj = Path(data)
337-
if path_obj.exists() and path_obj.is_dir() and not self._is_local_server():
338-
zip_path = self._zip_directory(data)
339-
try:
340-
temp_path = await self._upload_temp_file(zip_path)
343+
if path_obj.exists() and not self._is_local_server():
344+
if path_obj.is_dir():
345+
zip_path = self._zip_directory(data)
346+
try:
347+
temp_path = await self._upload_temp_file(zip_path)
348+
request_data["temp_path"] = temp_path
349+
finally:
350+
Path(zip_path).unlink(missing_ok=True)
351+
elif path_obj.is_file():
352+
temp_path = await self._upload_temp_file(data)
341353
request_data["temp_path"] = temp_path
342-
finally:
343-
Path(zip_path).unlink(missing_ok=True)
354+
else:
355+
request_data["data"] = data
344356
else:
345357
request_data["data"] = data
346358
else:

0 commit comments

Comments
 (0)