From ca75fa1079f85c7faeed9d6d469f10fe13414966 Mon Sep 17 00:00:00 2001 From: "zhiheng.liu" Date: Fri, 20 Feb 2026 16:22:12 +0800 Subject: [PATCH] fix: resolve relative CLI paths to absolute before sending to server Both Python and Rust CLIs were passing relative paths as-is to the server, which resolved them relative to its own CWD instead of the caller's. Fix by canonicalizing any path that exists on the local filesystem before sending. Covers add-resource, add-skill, export, and import in both CLIs. --- crates/ov_cli/src/main.rs | 20 ++++++++++++++++---- openviking_cli/cli/commands/pack.py | 8 ++++++-- openviking_cli/cli/commands/resources.py | 12 ++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/ov_cli/src/main.rs b/crates/ov_cli/src/main.rs index 8ce25384..61201474 100644 --- a/crates/ov_cli/src/main.rs +++ b/crates/ov_cli/src/main.rs @@ -470,6 +470,18 @@ async fn main() { } } +/// Resolve a relative local path to absolute. Pass through URLs and raw content unchanged. +fn resolve_path(s: String) -> String { + let p = std::path::Path::new(&s); + if p.exists() { + std::fs::canonicalize(p) + .map(|abs| abs.to_string_lossy().into_owned()) + .unwrap_or(s) + } else { + s + } +} + async fn handle_add_resource( mut path: String, to: Option, @@ -509,7 +521,7 @@ async fn handle_add_resource( let client = ctx.get_client(); commands::resources::add_resource( - &client, &path, to, reason, instruction, wait, timeout, ctx.output_format, ctx.compact + &client, &resolve_path(path), to, reason, instruction, wait, timeout, ctx.output_format, ctx.compact ).await } @@ -521,7 +533,7 @@ async fn handle_add_skill( ) -> Result<()> { let client = ctx.get_client(); commands::resources::add_skill( - &client, &data, wait, timeout, ctx.output_format, ctx.compact + &client, &resolve_path(data), wait, timeout, ctx.output_format, ctx.compact ).await } @@ -556,7 +568,7 @@ async fn handle_unlink( async fn handle_export(uri: String, to: String, ctx: CliContext) -> Result<()> { let client = ctx.get_client(); - commands::pack::export(&client, &uri, &to, ctx.output_format, ctx.compact + commands::pack::export(&client, &uri, &resolve_path(to), ctx.output_format, ctx.compact ).await } @@ -569,7 +581,7 @@ async fn handle_import( ) -> Result<()> { let client = ctx.get_client(); commands::pack::import( - &client, &file_path, &target_uri, force, no_vectorize, ctx.output_format, ctx.compact + &client, &resolve_path(file_path), &target_uri, force, no_vectorize, ctx.output_format, ctx.compact ).await } diff --git a/openviking_cli/cli/commands/pack.py b/openviking_cli/cli/commands/pack.py index 2f8d900e..2c298e50 100644 --- a/openviking_cli/cli/commands/pack.py +++ b/openviking_cli/cli/commands/pack.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 """Import/export pack commands.""" +from pathlib import Path + import typer from openviking_cli.cli.errors import run @@ -17,7 +19,8 @@ def export_command( to: str = typer.Argument(..., help="Output .ovpack file path"), ) -> None: """Export context as .ovpack.""" - run(ctx, lambda client: {"file": client.export_ovpack(uri, to)}) + abs_to = str(Path(to).resolve()) + run(ctx, lambda client: {"file": client.export_ovpack(uri, abs_to)}) @app.command("import") def import_command( @@ -32,11 +35,12 @@ def import_command( ), ) -> None: """Import .ovpack into target URI.""" + abs_file_path = str(Path(file_path).resolve()) run( ctx, lambda client: { "uri": client.import_ovpack( - file_path=file_path, + file_path=abs_file_path, target=target_uri, force=force, vectorize=not no_vectorize, diff --git a/openviking_cli/cli/commands/resources.py b/openviking_cli/cli/commands/resources.py index a9bfc28f..85f5a2cb 100644 --- a/openviking_cli/cli/commands/resources.py +++ b/openviking_cli/cli/commands/resources.py @@ -10,6 +10,14 @@ from openviking_cli.cli.errors import run +def _resolve_path(value: str) -> str: + """Resolve a relative local path to absolute. Pass through URLs and raw content.""" + p = Path(value) + if p.exists(): + return str(p.resolve()) + return value + + def register(app: typer.Typer) -> None: """Register resource commands.""" @@ -79,7 +87,7 @@ def add_resource_command( run( ctx, lambda client: client.add_resource( - path=final_path, + path=_resolve_path(final_path), target=to, reason=reason, instruction=instruction, @@ -99,7 +107,7 @@ def add_skill_command( run( ctx, lambda client: client.add_skill( - data=data, + data=_resolve_path(data), wait=wait, timeout=timeout, ),