Compare commits
5 Commits
06889ec85a
...
robot-meta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60075cd400 | ||
|
|
ddab186315 | ||
|
|
d6d1686f8e | ||
|
|
5c44ee91fb | ||
|
|
6aff96d32f |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1324,7 +1324,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lore"
|
name = "lore"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asupersync",
|
"asupersync",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lore"
|
name = "lore"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Gitlore - Local GitLab data management with semantic search"
|
description = "Gitlore - Local GitLab data management with semantic search"
|
||||||
authors = ["Taylor Eernisse"]
|
authors = ["Taylor Eernisse"]
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ fn print_combined_ingest_json(
|
|||||||
notes_upserted: mrs.notes_upserted,
|
notes_upserted: mrs.notes_upserted,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@@ -975,9 +975,7 @@ async fn handle_auth_test(
|
|||||||
name: result.name.clone(),
|
name: result.name.clone(),
|
||||||
gitlab_url: result.base_url.clone(),
|
gitlab_url: result.base_url.clone(),
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
@@ -1038,9 +1036,7 @@ async fn handle_doctor(
|
|||||||
success: result.success,
|
success: result.success,
|
||||||
checks: result.checks,
|
checks: result.checks,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
@@ -1085,9 +1081,7 @@ fn handle_version(robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Some(git_hash)
|
Some(git_hash)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else if git_hash.is_empty() {
|
} else if git_hash.is_empty() {
|
||||||
@@ -1245,9 +1239,7 @@ async fn handle_migrate(
|
|||||||
after_version,
|
after_version,
|
||||||
migrated: after_version > before_version,
|
migrated: after_version > before_version,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else if after_version > before_version {
|
} else if after_version > before_version {
|
||||||
@@ -1328,7 +1320,7 @@ fn handle_file_history(
|
|||||||
|
|
||||||
if robot_mode {
|
if robot_mode {
|
||||||
let elapsed_ms = start.elapsed().as_millis() as u64;
|
let elapsed_ms = start.elapsed().as_millis() as u64;
|
||||||
print_file_history_json(&result, elapsed_ms);
|
print_file_history_json(&result, elapsed_ms)?;
|
||||||
} else {
|
} else {
|
||||||
print_file_history(&result);
|
print_file_history(&result);
|
||||||
}
|
}
|
||||||
@@ -1384,7 +1376,7 @@ fn handle_trace(
|
|||||||
|
|
||||||
if robot_mode {
|
if robot_mode {
|
||||||
let elapsed_ms = start.elapsed().as_millis() as u64;
|
let elapsed_ms = start.elapsed().as_millis() as u64;
|
||||||
print_trace_json(&result, elapsed_ms, line_requested);
|
print_trace_json(&result, elapsed_ms, line_requested)?;
|
||||||
} else {
|
} else {
|
||||||
print_trace(&result);
|
print_trace(&result);
|
||||||
}
|
}
|
||||||
@@ -1962,9 +1954,7 @@ async fn handle_health(
|
|||||||
schema_version,
|
schema_version,
|
||||||
actions,
|
actions,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
|
|||||||
"mentioned_in": "[{entity_type:string, project:string, iid:int, title:string, state:string, attention_state:string, attention_reason:string, updated_at_iso:string, web_url:string?}]",
|
"mentioned_in": "[{entity_type:string, project:string, iid:int, title:string, state:string, attention_state:string, attention_reason:string, updated_at_iso:string, web_url:string?}]",
|
||||||
"activity": "[{timestamp_iso:string, event_type:string, entity_type:string, entity_iid:int, project:string, actor:string?, is_own:bool, summary:string, body_preview:string?}]"
|
"activity": "[{timestamp_iso:string, event_type:string, entity_type:string, entity_iid:int, project:string, actor:string?, is_own:bool, summary:string, body_preview:string?}]"
|
||||||
},
|
},
|
||||||
"meta": {"elapsed_ms": "int"}
|
"meta": {"elapsed_ms": "int", "gitlab_base_url": "string (GitLab instance URL for constructing entity links: {base_url}/{project}/-/issues/{iid})"}
|
||||||
},
|
},
|
||||||
"fields_presets": {
|
"fields_presets": {
|
||||||
"me_items_minimal": ["iid", "title", "attention_state", "attention_reason", "updated_at_iso"],
|
"me_items_minimal": ["iid", "title", "attention_state", "attention_reason", "updated_at_iso"],
|
||||||
@@ -396,7 +396,8 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
|
|||||||
"since_default": "1d for activity feed",
|
"since_default": "1d for activity feed",
|
||||||
"issue_filter": "Only In Progress / In Review status issues shown",
|
"issue_filter": "Only In Progress / In Review status issues shown",
|
||||||
"since_last_check": "Cursor-based inbox showing events since last run. Null on first run (no cursor yet). Groups events by entity (issue/MR). Sources: others' comments on your items, @mentions, assignment/review-request notes. Cursor auto-advances after each run. Use --reset-cursor to clear.",
|
"since_last_check": "Cursor-based inbox showing events since last run. Null on first run (no cursor yet). Groups events by entity (issue/MR). Sources: others' comments on your items, @mentions, assignment/review-request notes. Cursor auto-advances after each run. Use --reset-cursor to clear.",
|
||||||
"cursor_persistence": "Stored per user in ~/.local/share/lore/me_cursor_<username>.json. --project filters display only for since-last-check; cursor still advances for all projects for that user."
|
"cursor_persistence": "Stored per user in ~/.local/share/lore/me_cursor_<username>.json. --project filters display only for since-last-check; cursor still advances for all projects for that user.",
|
||||||
|
"url_construction": "Use meta.gitlab_base_url + project + entity_type + iid to build links: {gitlab_base_url}/{project}/-/{issues|merge_requests}/{iid}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"robot-docs": {
|
"robot-docs": {
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ pub fn print_event_count_json(counts: &EventCounts, elapsed_ms: u64) {
|
|||||||
},
|
},
|
||||||
total: counts.total(),
|
total: counts.total(),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
@@ -325,7 +325,7 @@ pub fn print_count_json(result: &CountResult, elapsed_ms: u64) {
|
|||||||
system_excluded: result.system_count,
|
system_excluded: result.system_count,
|
||||||
breakdown,
|
breakdown,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub fn print_cron_install_json(result: &CronInstallResult, elapsed_ms: u64) {
|
|||||||
log_path: result.log_path.display().to_string(),
|
log_path: result.log_path.display().to_string(),
|
||||||
replaced: result.replaced,
|
replaced: result.replaced,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
@@ -128,7 +128,7 @@ pub fn print_cron_uninstall_json(result: &CronUninstallResult, elapsed_ms: u64)
|
|||||||
action: "uninstall",
|
action: "uninstall",
|
||||||
was_installed: result.was_installed,
|
was_installed: result.was_installed,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
@@ -284,7 +284,7 @@ pub fn print_cron_status_json(info: &CronStatusInfo, elapsed_ms: u64) {
|
|||||||
last_sync_at: info.last_sync.as_ref().map(|s| s.started_at_iso.clone()),
|
last_sync_at: info.last_sync.as_ref().map(|s| s.started_at_iso.clone()),
|
||||||
last_sync_status: info.last_sync.as_ref().map(|s| s.status.clone()),
|
last_sync_status: info.last_sync.as_ref().map(|s| s.status.clone()),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ pub fn print_drift_human(response: &DriftResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_drift_json(response: &DriftResponse, elapsed_ms: u64) {
|
pub fn print_drift_json(response: &DriftResponse, elapsed_ms: u64) {
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": response,
|
"data": response,
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ pub fn print_embed_json(result: &EmbedCommandResult, elapsed_ms: u64) {
|
|||||||
let output = EmbedJsonOutput {
|
let output = EmbedJsonOutput {
|
||||||
ok: true,
|
ok: true,
|
||||||
data: result,
|
data: result,
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -821,7 +821,8 @@ fn fetch_related_entities(
|
|||||||
LEFT JOIN merge_requests mr ON er.target_entity_type = 'merge_request' AND mr.id = er.target_entity_id \
|
LEFT JOIN merge_requests mr ON er.target_entity_type = 'merge_request' AND mr.id = er.target_entity_id \
|
||||||
WHERE er.source_entity_type = ?1 AND er.source_entity_id = ?2 \
|
WHERE er.source_entity_type = ?1 AND er.source_entity_id = ?2 \
|
||||||
AND er.reference_type != 'closes' \
|
AND er.reference_type != 'closes' \
|
||||||
AND er.target_entity_iid IS NOT NULL",
|
AND er.target_entity_iid IS NOT NULL \
|
||||||
|
ORDER BY er.target_entity_type, er.target_entity_iid",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let outgoing: Vec<RelatedEntityInfo> = out_stmt
|
let outgoing: Vec<RelatedEntityInfo> = out_stmt
|
||||||
@@ -845,7 +846,8 @@ fn fetch_related_entities(
|
|||||||
LEFT JOIN merge_requests mr ON er.source_entity_type = 'merge_request' AND mr.id = er.source_entity_id \
|
LEFT JOIN merge_requests mr ON er.source_entity_type = 'merge_request' AND mr.id = er.source_entity_id \
|
||||||
WHERE er.target_entity_type = ?1 AND er.target_entity_id = ?2 \
|
WHERE er.target_entity_type = ?1 AND er.target_entity_id = ?2 \
|
||||||
AND er.reference_type != 'closes' \
|
AND er.reference_type != 'closes' \
|
||||||
AND COALESCE(i.iid, mr.iid) IS NOT NULL",
|
AND COALESCE(i.iid, mr.iid) IS NOT NULL \
|
||||||
|
ORDER BY er.source_entity_type, COALESCE(i.iid, mr.iid)",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let incoming: Vec<RelatedEntityInfo> = in_stmt
|
let incoming: Vec<RelatedEntityInfo> = in_stmt
|
||||||
@@ -1020,7 +1022,7 @@ pub fn handle_explain(
|
|||||||
let elapsed_ms = start.elapsed().as_millis() as u64;
|
let elapsed_ms = start.elapsed().as_millis() as u64;
|
||||||
|
|
||||||
if robot_mode {
|
if robot_mode {
|
||||||
print_explain_json(&result, elapsed_ms);
|
print_explain_json(&result, elapsed_ms)?;
|
||||||
} else {
|
} else {
|
||||||
print_explain(&result);
|
print_explain(&result);
|
||||||
}
|
}
|
||||||
@@ -1032,13 +1034,18 @@ pub fn handle_explain(
|
|||||||
// Output rendering (Task 5 fills these in fully)
|
// Output rendering (Task 5 fills these in fully)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn print_explain_json(result: &ExplainResult, elapsed_ms: u64) {
|
pub fn print_explain_json(result: &ExplainResult, elapsed_ms: u64) -> Result<()> {
|
||||||
let response = serde_json::json!({
|
let response = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": result,
|
"data": result,
|
||||||
"meta": { "elapsed_ms": elapsed_ms }
|
"meta": { "elapsed_ms": elapsed_ms }
|
||||||
});
|
});
|
||||||
println!("{}", serde_json::to_string(&response).unwrap_or_default());
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string(&response)
|
||||||
|
.map_err(|e| LoreError::Other(format!("JSON serialization failed: {e}")))?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_explain(result: &ExplainResult) {
|
pub fn print_explain(result: &ExplainResult) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use tracing::info;
|
|||||||
use crate::Config;
|
use crate::Config;
|
||||||
use crate::cli::render::{self, Icons, Theme};
|
use crate::cli::render::{self, Icons, Theme};
|
||||||
use crate::core::db::create_connection;
|
use crate::core::db::create_connection;
|
||||||
use crate::core::error::Result;
|
use crate::core::error::{LoreError, Result};
|
||||||
use crate::core::file_history::resolve_rename_chain;
|
use crate::core::file_history::resolve_rename_chain;
|
||||||
use crate::core::paths::get_db_path;
|
use crate::core::paths::get_db_path;
|
||||||
use crate::core::project::resolve_project;
|
use crate::core::project::resolve_project;
|
||||||
@@ -391,7 +391,7 @@ pub fn print_file_history(result: &FileHistoryResult) {
|
|||||||
|
|
||||||
// ── Robot (JSON) output ─────────────────────────────────────────────────────
|
// ── Robot (JSON) output ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn print_file_history_json(result: &FileHistoryResult, elapsed_ms: u64) {
|
pub fn print_file_history_json(result: &FileHistoryResult, elapsed_ms: u64) -> Result<()> {
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": {
|
"data": {
|
||||||
@@ -409,5 +409,10 @@ pub fn print_file_history_json(result: &FileHistoryResult, elapsed_ms: u64) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("{}", serde_json::to_string(&output).unwrap_or_default());
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string(&output)
|
||||||
|
.map_err(|e| LoreError::Other(format!("JSON serialization failed: {e}")))?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ pub fn print_generate_docs_json(result: &GenerateDocsResult, elapsed_ms: u64) {
|
|||||||
unchanged: result.unchanged,
|
unchanged: result.unchanged,
|
||||||
errored: result.errored,
|
errored: result.errored,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ pub fn print_ingest_summary_json(result: &IngestResult, elapsed_ms: u64) {
|
|||||||
status_enrichment,
|
status_enrichment,
|
||||||
status_enrichment_errors: result.status_enrichment_errors,
|
status_enrichment_errors: result.status_enrichment_errors,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ pub fn print_list_mrs(result: &MrListResult) {
|
|||||||
|
|
||||||
pub fn print_list_mrs_json(result: &MrListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
pub fn print_list_mrs_json(result: &MrListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
||||||
let json_result = MrListResultJson::from(result);
|
let json_result = MrListResultJson::from(result);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ pub fn print_list_notes(result: &NoteListResult) {
|
|||||||
|
|
||||||
pub fn print_list_notes_json(result: &NoteListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
pub fn print_list_notes_json(result: &NoteListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
||||||
let json_result = NoteListResultJson::from(result);
|
let json_result = NoteListResultJson::from(result);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ pub fn run_me(config: &Config, args: &MeArgs, robot_mode: bool) -> Result<()> {
|
|||||||
|
|
||||||
if robot_mode {
|
if robot_mode {
|
||||||
let fields = args.fields.as_deref();
|
let fields = args.fields.as_deref();
|
||||||
render_robot::print_me_json(&dashboard, elapsed_ms, fields)?;
|
render_robot::print_me_json(&dashboard, elapsed_ms, fields, &config.gitlab.base_url)?;
|
||||||
} else if show_all {
|
} else if show_all {
|
||||||
render_human::print_me_dashboard(&dashboard, single_project);
|
render_human::print_me_dashboard(&dashboard, single_project);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ pub fn print_me_json(
|
|||||||
dashboard: &MeDashboard,
|
dashboard: &MeDashboard,
|
||||||
elapsed_ms: u64,
|
elapsed_ms: u64,
|
||||||
fields: Option<&[String]>,
|
fields: Option<&[String]>,
|
||||||
|
gitlab_base_url: &str,
|
||||||
) -> crate::core::error::Result<()> {
|
) -> crate::core::error::Result<()> {
|
||||||
let envelope = MeJsonEnvelope {
|
let envelope = MeJsonEnvelope {
|
||||||
ok: true,
|
ok: true,
|
||||||
data: MeDataJson::from_dashboard(dashboard),
|
data: MeDataJson::from_dashboard(dashboard),
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::with_base_url(elapsed_ms, gitlab_base_url),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = serde_json::to_value(&envelope)
|
let mut value = serde_json::to_value(&envelope)
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ pub fn print_related_human(response: &RelatedResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_related_json(response: &RelatedResponse, elapsed_ms: u64) {
|
pub fn print_related_json(response: &RelatedResponse, elapsed_ms: u64) {
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": response,
|
"data": response,
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ impl From<&MrNoteDetail> for MrNoteDetailJson {
|
|||||||
|
|
||||||
pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
||||||
let json_result = IssueDetailJson::from(issue);
|
let json_result = IssueDetailJson::from(issue);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
@@ -571,7 +571,7 @@ pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
|||||||
|
|
||||||
pub fn print_show_mr_json(mr: &MrDetail, elapsed_ms: u64) {
|
pub fn print_show_mr_json(mr: &MrDetail, elapsed_ms: u64) {
|
||||||
let json_result = MrDetailJson::from(mr);
|
let json_result = MrDetailJson::from(mr);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -583,7 +583,7 @@ pub fn print_stats_json(result: &StatsResult, elapsed_ms: u64) {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ pub fn print_sync_status_json(result: &SyncStatusResult, elapsed_ms: u64) {
|
|||||||
system_notes: result.summary.system_note_count,
|
system_notes: result.summary.system_note_count,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::cli::render::{Icons, Theme};
|
use crate::cli::render::{Icons, Theme};
|
||||||
|
use crate::core::error::{LoreError, Result};
|
||||||
use crate::core::trace::{TraceChain, TraceResult};
|
use crate::core::trace::{TraceChain, TraceResult};
|
||||||
|
|
||||||
/// Parse a path with optional `:line` suffix.
|
/// Parse a path with optional `:line` suffix.
|
||||||
@@ -152,7 +153,11 @@ fn truncate_body(body: &str, max: usize) -> String {
|
|||||||
format!("{}...", &body[..boundary])
|
format!("{}...", &body[..boundary])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_trace_json(result: &TraceResult, elapsed_ms: u64, line_requested: Option<u32>) {
|
pub fn print_trace_json(
|
||||||
|
result: &TraceResult,
|
||||||
|
elapsed_ms: u64,
|
||||||
|
line_requested: Option<u32>,
|
||||||
|
) -> Result<()> {
|
||||||
// Truncate discussion bodies for token efficiency in robot mode
|
// Truncate discussion bodies for token efficiency in robot mode
|
||||||
let chains: Vec<serde_json::Value> = result
|
let chains: Vec<serde_json::Value> = result
|
||||||
.trace_chains
|
.trace_chains
|
||||||
@@ -205,7 +210,12 @@ pub fn print_trace_json(result: &TraceResult, elapsed_ms: u64, line_requested: O
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("{}", serde_json::to_string(&output).unwrap_or_default());
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string(&output)
|
||||||
|
.map_err(|e| LoreError::Other(format!("JSON serialization failed: {e}")))?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ pub fn print_who_json(run: &WhoRun, args: &WhoArgs, elapsed_ms: u64) {
|
|||||||
resolved_input,
|
resolved_input,
|
||||||
result: data,
|
result: data,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = serde_json::to_value(&output).unwrap_or_else(|e| {
|
let mut value = serde_json::to_value(&output).unwrap_or_else(|e| {
|
||||||
|
|||||||
@@ -3,6 +3,26 @@ use serde::Serialize;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct RobotMeta {
|
pub struct RobotMeta {
|
||||||
pub elapsed_ms: u64,
|
pub elapsed_ms: u64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gitlab_base_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RobotMeta {
|
||||||
|
/// Standard meta with timing only.
|
||||||
|
pub fn new(elapsed_ms: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
elapsed_ms,
|
||||||
|
gitlab_base_url: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Meta with GitLab base URL for URL construction by consumers.
|
||||||
|
pub fn with_base_url(elapsed_ms: u64, base_url: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
elapsed_ms,
|
||||||
|
gitlab_base_url: Some(base_url.trim_end_matches('/').to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter JSON object fields in-place for `--fields` support.
|
/// Filter JSON object fields in-place for `--fields` support.
|
||||||
@@ -133,4 +153,27 @@ mod tests {
|
|||||||
let expanded = expand_fields_preset(&fields, "notes");
|
let expanded = expand_fields_preset(&fields, "notes");
|
||||||
assert_eq!(expanded, ["id", "body"]);
|
assert_eq!(expanded, ["id", "body"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_new_omits_base_url() {
|
||||||
|
let meta = RobotMeta::new(42);
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["elapsed_ms"], 42);
|
||||||
|
assert!(json.get("gitlab_base_url").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_with_base_url_includes_it() {
|
||||||
|
let meta = RobotMeta::with_base_url(99, "https://gitlab.example.com");
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["elapsed_ms"], 99);
|
||||||
|
assert_eq!(json["gitlab_base_url"], "https://gitlab.example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_with_base_url_strips_trailing_slash() {
|
||||||
|
let meta = RobotMeta::with_base_url(0, "https://gitlab.example.com/");
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["gitlab_base_url"], "https://gitlab.example.com");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ fn setup() -> Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_discussion_id(conn: &Connection) -> i64 {
|
fn get_discussion_id(conn: &Connection) -> i64 {
|
||||||
conn.query_row("SELECT id FROM discussions LIMIT 1", [], |row| row.get(0))
|
conn.query_row(
|
||||||
|
"SELECT id FROM discussions ORDER BY id LIMIT 1",
|
||||||
|
[],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -786,7 +786,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_mr_discussion_id(conn: &Connection) -> i64 {
|
fn get_mr_discussion_id(conn: &Connection) -> i64 {
|
||||||
conn.query_row("SELECT id FROM discussions LIMIT 1", [], |row| row.get(0))
|
conn.query_row(
|
||||||
|
"SELECT id FROM discussions ORDER BY id LIMIT 1",
|
||||||
|
[],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,14 +242,16 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let project_id: i64 = conn
|
let project_id: i64 = conn
|
||||||
.query_row("SELECT id FROM projects LIMIT 1", [], |row| row.get(0))
|
.query_row("SELECT id FROM projects ORDER BY id LIMIT 1", [], |row| {
|
||||||
|
row.get(0)
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
enqueue_job(&conn, project_id, "issue", 42, 100, "resource_events", None).unwrap();
|
enqueue_job(&conn, project_id, "issue", 42, 100, "resource_events", None).unwrap();
|
||||||
|
|
||||||
let job_id: i64 = conn
|
let job_id: i64 = conn
|
||||||
.query_row(
|
.query_row(
|
||||||
"SELECT id FROM pending_dependent_fetches LIMIT 1",
|
"SELECT id FROM pending_dependent_fetches ORDER BY id LIMIT 1",
|
||||||
[],
|
[],
|
||||||
|row| row.get(0),
|
|row| row.get(0),
|
||||||
)
|
)
|
||||||
@@ -301,7 +303,9 @@ mod tests {
|
|||||||
let (conn, _job_id) = setup_db_with_job();
|
let (conn, _job_id) = setup_db_with_job();
|
||||||
|
|
||||||
let project_id: i64 = conn
|
let project_id: i64 = conn
|
||||||
.query_row("SELECT id FROM projects LIMIT 1", [], |row| row.get(0))
|
.query_row("SELECT id FROM projects ORDER BY id LIMIT 1", [], |row| {
|
||||||
|
row.get(0)
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let jobs = claim_jobs(&conn, "resource_events", project_id, 10).unwrap();
|
let jobs = claim_jobs(&conn, "resource_events", project_id, 10).unwrap();
|
||||||
assert_eq!(jobs.len(), 1);
|
assert_eq!(jobs.len(), 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user