Skip to content

Commit 80a139d

Browse files
authored
fix: ov cmd and repo uri (#339)
1 parent 793e991 commit 80a139d

File tree

9 files changed

+90
-30
lines changed

9 files changed

+90
-30
lines changed

crates/ov_cli/ovcli.conf.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"url": "http://localhost:1933",
3+
"api_key": "your-api-key",
4+
"timeout": 60.0,
5+
"output": "table",
6+
"echo_command": true
7+
}

crates/ov_cli/src/client.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,14 @@ impl HttpClient {
322322
self.get("/api/v1/fs/ls", &params).await
323323
}
324324

325-
pub async fn tree(&self, uri: &str, output: &str, abs_limit: i32, show_all_hidden: bool, node_limit: i32) -> Result<serde_json::Value> {
325+
pub async fn tree(&self, uri: &str, output: &str, abs_limit: i32, show_all_hidden: bool, node_limit: i32, level_limit: i32) -> Result<serde_json::Value> {
326326
let params = vec![
327327
("uri".to_string(), uri.to_string()),
328328
("output".to_string(), output.to_string()),
329329
("abs_limit".to_string(), abs_limit.to_string()),
330330
("show_all_hidden".to_string(), show_all_hidden.to_string()),
331331
("node_limit".to_string(), node_limit.to_string()),
332+
("level_limit".to_string(), level_limit.to_string()),
332333
];
333334
self.get("/api/v1/fs/tree", &params).await
334335
}

crates/ov_cli/src/commands/filesystem.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ pub async fn tree(
2626
abs_limit: i32,
2727
show_all_hidden: bool,
2828
node_limit: i32,
29+
level_limit: i32,
2930
output_format: OutputFormat,
3031
compact: bool,
3132
) -> Result<()> {
32-
let result = client.tree(uri, output, abs_limit, show_all_hidden, node_limit).await?;
33+
let result = client.tree(uri, output, abs_limit, show_all_hidden, node_limit, level_limit).await?;
3334
output_success(&result, output_format, compact);
3435
Ok(())
3536
}

crates/ov_cli/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub struct Config {
1313
pub timeout: f64,
1414
#[serde(default = "default_output_format")]
1515
pub output: String,
16+
#[serde(default = "default_echo_command")]
17+
pub echo_command: bool,
1618
}
1719

1820
fn default_url() -> String {
@@ -27,6 +29,10 @@ fn default_output_format() -> String {
2729
"table".to_string()
2830
}
2931

32+
fn default_echo_command() -> bool {
33+
true
34+
}
35+
3036
impl Default for Config {
3137
fn default() -> Self {
3238
Self {
@@ -35,6 +41,7 @@ impl Default for Config {
3541
agent_id: None,
3642
timeout: 60.0,
3743
output: "table".to_string(),
44+
echo_command: true,
3845
}
3946
}
4047
}

crates/ov_cli/src/main.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ enum Commands {
195195
#[arg(short, long)]
196196
all: bool,
197197
/// Maximum number of nodes to list
198-
#[arg(long = "node-limit", short = 'n', default_value = "1000")]
198+
#[arg(long = "node-limit", short = 'n', default_value = "256")]
199199
node_limit: i32,
200200
},
201201
/// Get directory tree
@@ -209,8 +209,11 @@ enum Commands {
209209
#[arg(short, long)]
210210
all: bool,
211211
/// Maximum number of nodes to list
212-
#[arg(long = "node-limit", short = 'n', default_value = "1000")]
212+
#[arg(long = "node-limit", short = 'n', default_value = "256")]
213213
node_limit: i32,
214+
/// Maximum depth level to traverse (default: 3)
215+
#[arg(short = 'L', long = "level-limit", default_value = "3")]
216+
level_limit: i32,
214217
},
215218
/// Create directory
216219
Mkdir {
@@ -530,8 +533,8 @@ async fn main() {
530533
Commands::Ls { uri, simple, recursive, abs_limit, all, node_limit } => {
531534
handle_ls(uri, simple, recursive, abs_limit, all, node_limit, ctx).await
532535
}
533-
Commands::Tree { uri, abs_limit, all, node_limit } => {
534-
handle_tree(uri, abs_limit, all, node_limit, ctx).await
536+
Commands::Tree { uri, abs_limit, all, node_limit, level_limit } => {
537+
handle_tree(uri, abs_limit, all, node_limit, level_limit, ctx).await
535538
}
536539
Commands::Mkdir { uri } => {
537540
handle_mkdir(uri, ctx).await
@@ -877,16 +880,42 @@ async fn handle_search(
877880
commands::search::search(&client, &query, &uri, session_id, limit, threshold, ctx.output_format, ctx.compact).await
878881
}
879882

883+
/// Print command with specified parameters for debugging
884+
fn print_command_echo(command: &str, params: &str, echo_enabled: bool) {
885+
if echo_enabled {
886+
println!("cmd: {} {}", command, params);
887+
}
888+
}
889+
880890
async fn handle_ls(uri: String, simple: bool, recursive: bool, abs_limit: i32, show_all_hidden: bool, node_limit: i32, ctx: CliContext) -> Result<()> {
891+
let mut params = vec![
892+
uri.clone(),
893+
format!("-l {}", abs_limit),
894+
format!("-n {}", node_limit),
895+
];
896+
if simple { params.push("-s".to_string()); }
897+
if recursive { params.push("-r".to_string()); }
898+
if show_all_hidden { params.push("-a".to_string()); }
899+
print_command_echo("ov ls", &params.join(" "), ctx.config.echo_command);
900+
881901
let client = ctx.get_client();
882902
let api_output = if ctx.compact { "agent" } else { "original" };
883903
commands::filesystem::ls(&client, &uri, simple, recursive, api_output, abs_limit, show_all_hidden, node_limit, ctx.output_format, ctx.compact).await
884904
}
885905

886-
async fn handle_tree(uri: String, abs_limit: i32, show_all_hidden: bool, node_limit: i32, ctx: CliContext) -> Result<()> {
906+
async fn handle_tree(uri: String, abs_limit: i32, show_all_hidden: bool, node_limit: i32, level_limit: i32, ctx: CliContext) -> Result<()> {
907+
let mut params = vec![
908+
uri.clone(),
909+
format!("-l {}", abs_limit),
910+
format!("-n {}", node_limit),
911+
format!("-L {}", level_limit),
912+
];
913+
if show_all_hidden { params.push("-a".to_string()); }
914+
print_command_echo("ov tree", &params.join(" "), ctx.config.echo_command);
915+
887916
let client = ctx.get_client();
888917
let api_output = if ctx.compact { "agent" } else { "original" };
889-
commands::filesystem::tree(&client, &uri, api_output, abs_limit, show_all_hidden, node_limit, ctx.output_format, ctx.compact).await
918+
commands::filesystem::tree(&client, &uri, api_output, abs_limit, show_all_hidden, node_limit, level_limit, ctx.output_format, ctx.compact).await
890919
}
891920

892921
async fn handle_mkdir(uri: String, ctx: CliContext) -> Result<()> {

openviking/parse/tree_builder.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,38 +133,39 @@ async def finalize_from_temp(
133133
if original_name != doc_name:
134134
logger.debug(f"[TreeBuilder] Sanitized doc name: {original_name!r} -> {doc_name!r}")
135135

136-
# 2. Determine base_uri and final document name with org/repo for GitHub/GitLab
137-
if base_uri is None:
138-
base_uri = self._get_base_uri(scope, source_path, source_format)
139-
140136
# Check if source_path is a GitHub/GitLab URL and extract org/repo
141137
final_doc_name = doc_name
142138
if source_path and source_format == "repository":
143139
parsed_org_repo = parse_code_hosting_url(source_path)
144140
if parsed_org_repo:
145141
final_doc_name = parsed_org_repo
146142

143+
# 2. Determine base_uri and final document name with org/repo for GitHub/GitLab
144+
auto_base_uri = self._get_base_uri(scope, source_path, source_format)
145+
147146
# 3. Check if base_uri exists - if it does, use it as parent directory
148-
try:
149-
await viking_fs.stat(base_uri)
150-
base_exists = True
151-
except Exception:
152-
base_exists = False
147+
base_exists = False
148+
if base_uri:
149+
try:
150+
await viking_fs.stat(base_uri)
151+
base_exists = True
152+
except Exception:
153+
base_exists = False
153154

154155
if base_exists:
155156
if "/" in final_doc_name:
156157
repo_name_only = final_doc_name.split("/")[-1]
157158
else:
158159
repo_name_only = final_doc_name
159-
candidate_uri = VikingURI(base_uri).join(repo_name_only).uri
160+
candidate_uri = VikingURI(base_uri or auto_base_uri).join(repo_name_only).uri
160161
else:
161162
if "/" in final_doc_name:
162163
parts = final_doc_name.split("/")
163164
sanitized_parts = [VikingURI.sanitize_segment(p) for p in parts if p]
164-
base_viking_uri = VikingURI(base_uri)
165+
base_viking_uri = VikingURI(base_uri or auto_base_uri)
165166
candidate_uri = VikingURI.build(base_viking_uri.scope, *sanitized_parts)
166167
else:
167-
candidate_uri = VikingURI(base_uri).join(doc_name).uri
168+
candidate_uri = VikingURI(base_uri or auto_base_uri).join(doc_name).uri
168169
final_uri = await self._resolve_unique_uri(candidate_uri, ctx=ctx)
169170

170171
if final_uri != candidate_uri:

openviking/server/routers/filesystem.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ async def tree(
4848
abs_limit: int = Query(256, description="Abstract limit (only for agent output)"),
4949
show_all_hidden: bool = Query(False, description="List all hidden files, like -a"),
5050
node_limit: int = Query(1000, description="Maximum number of nodes to list"),
51+
level_limit: int = Query(3, description="Maximum depth level to traverse"),
5152
_ctx: RequestContext = Depends(get_request_context),
5253
):
5354
"""Get directory tree."""
@@ -59,6 +60,7 @@ async def tree(
5960
abs_limit=abs_limit,
6061
show_all_hidden=show_all_hidden,
6162
node_limit=node_limit,
63+
level_limit=level_limit,
6264
)
6365
return Response(status="ok", result=result)
6466

openviking/service/fs_service.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ async def ls(
4242
abs_limit: int = 256,
4343
show_all_hidden: bool = False,
4444
node_limit: int = 1000,
45+
level_limit: int = 3,
4546
) -> List[Any]:
4647
"""List directory contents.
4748
@@ -65,6 +66,7 @@ async def ls(
6566
output="original",
6667
show_all_hidden=show_all_hidden,
6768
node_limit=node_limit,
69+
level_limit=level_limit,
6870
)
6971
else:
7072
entries = await viking_fs.ls(
@@ -80,6 +82,7 @@ async def ls(
8082
abs_limit=abs_limit,
8183
show_all_hidden=show_all_hidden,
8284
node_limit=node_limit,
85+
level_limit=level_limit,
8386
)
8487
else:
8588
entries = await viking_fs.ls(
@@ -114,6 +117,7 @@ async def tree(
114117
abs_limit: int = 128,
115118
show_all_hidden: bool = False,
116119
node_limit: int = 1000,
120+
level_limit: int = 3,
117121
) -> List[Dict[str, Any]]:
118122
"""Get directory tree."""
119123
viking_fs = self._ensure_initialized()
@@ -124,6 +128,7 @@ async def tree(
124128
abs_limit=abs_limit,
125129
show_all_hidden=show_all_hidden,
126130
node_limit=node_limit,
131+
level_limit=level_limit,
127132
)
128133

129134
async def stat(self, uri: str, ctx: RequestContext) -> Dict[str, Any]:

openviking/storage/viking_fs.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ async def tree(
384384
abs_limit: int = 256,
385385
show_all_hidden: bool = False,
386386
node_limit: int = 1000,
387+
level_limit: int = 3,
387388
ctx: Optional[RequestContext] = None,
388389
) -> List[Dict[str, Any]]:
389390
"""
@@ -394,6 +395,8 @@ async def tree(
394395
output: str = "original" or "agent"
395396
abs_limit: int = 256 (for agent output abstract truncation)
396397
show_all_hidden: bool = False (list all hidden files, like -a)
398+
node_limit: int = 1000 (maximum number of nodes to list)
399+
level_limit: int = 3 (maximum depth level to traverse)
397400
398401
output="original"
399402
[{'name': '.abstract.md', 'size': 100, 'mode': 420, 'modTime': '2026-02-11T16:52:16.256334192+08:00', 'isDir': False, 'meta': {...}, 'rel_path': '.abstract.md', 'uri': 'viking://resources...'}]
@@ -403,9 +406,11 @@ async def tree(
403406
"""
404407
self._ensure_access(uri, ctx)
405408
if output == "original":
406-
return await self._tree_original(uri, show_all_hidden, node_limit, ctx=ctx)
409+
return await self._tree_original(uri, show_all_hidden, node_limit, level_limit, ctx=ctx)
407410
elif output == "agent":
408-
return await self._tree_agent(uri, abs_limit, show_all_hidden, node_limit, ctx=ctx)
411+
return await self._tree_agent(
412+
uri, abs_limit, show_all_hidden, node_limit, level_limit, ctx=ctx
413+
)
409414
else:
410415
raise ValueError(f"Invalid output format: {output}")
411416

@@ -414,15 +419,16 @@ async def _tree_original(
414419
uri: str,
415420
show_all_hidden: bool = False,
416421
node_limit: int = 1000,
422+
level_limit: int = 3,
417423
ctx: Optional[RequestContext] = None,
418424
) -> List[Dict[str, Any]]:
419425
"""Recursively list all contents (original format)."""
420426
path = self._uri_to_path(uri, ctx=ctx)
421427
all_entries = []
422428
real_ctx = self._ctx_or_default(ctx)
423429

424-
async def _walk(current_path: str, current_rel: str):
425-
if len(all_entries) >= node_limit:
430+
async def _walk(current_path: str, current_rel: str, current_depth: int):
431+
if len(all_entries) >= node_limit or current_depth > level_limit:
426432
return
427433
for entry in self._ls_entries(current_path):
428434
if len(all_entries) >= node_limit:
@@ -438,13 +444,13 @@ async def _walk(current_path: str, current_rel: str):
438444
continue
439445
if entry.get("isDir"):
440446
all_entries.append(new_entry)
441-
await _walk(f"{current_path}/{name}", rel_path)
447+
await _walk(f"{current_path}/{name}", rel_path, current_depth + 1)
442448
elif not name.startswith("."):
443449
all_entries.append(new_entry)
444450
elif show_all_hidden:
445451
all_entries.append(new_entry)
446452

447-
await _walk(path, "")
453+
await _walk(path, "", 0)
448454
return all_entries
449455

450456
async def _tree_agent(
@@ -453,6 +459,7 @@ async def _tree_agent(
453459
abs_limit: int,
454460
show_all_hidden: bool = False,
455461
node_limit: int = 1000,
462+
level_limit: int = 3,
456463
ctx: Optional[RequestContext] = None,
457464
) -> List[Dict[str, Any]]:
458465
"""Recursively list all contents (agent format with abstracts)."""
@@ -461,8 +468,8 @@ async def _tree_agent(
461468
now = datetime.now()
462469
real_ctx = self._ctx_or_default(ctx)
463470

464-
async def _walk(current_path: str, current_rel: str):
465-
if len(all_entries) >= node_limit:
471+
async def _walk(current_path: str, current_rel: str, current_depth: int):
472+
if len(all_entries) >= node_limit or current_depth > level_limit:
466473
return
467474
for entry in self._ls_entries(current_path):
468475
if len(all_entries) >= node_limit:
@@ -482,13 +489,13 @@ async def _walk(current_path: str, current_rel: str):
482489
continue
483490
if entry.get("isDir"):
484491
all_entries.append(new_entry)
485-
await _walk(f"{current_path}/{name}", rel_path)
492+
await _walk(f"{current_path}/{name}", rel_path, current_depth + 1)
486493
elif not name.startswith("."):
487494
all_entries.append(new_entry)
488495
elif show_all_hidden:
489496
all_entries.append(new_entry)
490497

491-
await _walk(path, "")
498+
await _walk(path, "", 0)
492499

493500
await self._batch_fetch_abstracts(all_entries, abs_limit, ctx=ctx)
494501

0 commit comments

Comments
 (0)