feat: journal access notice, 1.60.0, install script, changelog, riscv protoc fallback
- Journal pane now distinguishes 'no entries' from 'no journal access': journalctl exits 0 with empty output when the agent's user simply can't see the target's entries (demo mode / user-run agents), explaining itself only on stderr. The agent forwards that hint as an additive JournalResponse.notice and the client renders it with practical advice. Verified E2E via a stub journalctl emulating the unprivileged case. - Version 1.60.0 across all crates (1.51 would read fine, but the repo's scheme is 1.40/1.50/…, and a literal 1.6.0 would sort BELOW 1.50.0 in semver). All user-facing version strings already come from CARGO_PKG_VERSION — a stale binary was the only way to see an old one. - scripts/install.sh: build-from-source install/upgrade for the test fleet (Linux + macOS). Detects in-repo checkouts, installs rustup when missing, replaces a systemd socktop-agent service binary in place and restarts it, requires system protoc on riscv64. - build.rs (agent + connector): fall back to $PROTOC / PATH when protoc-bin-vendored has no binary for the host (riscv64) — native SBC builds previously panicked in the build script. - CHANGELOG.md covering v1.50.0 -> 1.60.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1412,6 +1412,25 @@ pub async fn collect_journal_entries(pid: u32) -> Result<JournalResponse, String
|
||||
// Sort by timestamp (newest first)
|
||||
entries.sort_by_key(|e| std::cmp::Reverse(e.timestamp_us));
|
||||
|
||||
// journalctl exits 0 with no output when the invoking user simply cannot
|
||||
// SEE the process's entries (e.g. a user-run agent asking about a system
|
||||
// service) — but it explains itself on stderr ("You are currently not
|
||||
// seeing messages from other users and the system…"). Pass that hint
|
||||
// along so the client can distinguish "no logs" from "no access".
|
||||
let notice = if entries.is_empty() {
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
let hint: String = err
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.is_empty())
|
||||
.take(2)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
if hint.is_empty() { None } else { Some(hint) }
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response_timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| format!("Time error: {e}"))?
|
||||
@@ -1424,6 +1443,7 @@ pub async fn collect_journal_entries(pid: u32) -> Result<JournalResponse, String
|
||||
entries,
|
||||
total_count,
|
||||
truncated,
|
||||
notice,
|
||||
cached_at: response_timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn cert_paths() -> (PathBuf, PathBuf) {
|
||||
pub fn ensure_self_signed_cert() -> anyhow::Result<(PathBuf, PathBuf)> {
|
||||
let (cert_path, key_path) = cert_paths();
|
||||
if cert_path.exists() && key_path.exists() {
|
||||
// Keys generated by agents older than 1.51 were written with the
|
||||
// Keys generated by agents older than 1.60 were written with the
|
||||
// default umask (typically 0644): tighten them on startup.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
|
||||
@@ -126,5 +126,9 @@ pub struct JournalResponse {
|
||||
pub entries: Vec<JournalEntry>,
|
||||
pub total_count: u32,
|
||||
pub truncated: bool,
|
||||
/// journalctl's own explanation when the result is empty because of
|
||||
/// journal ACCESS (not absence of logs) — e.g. a user-run agent asking
|
||||
/// about a system service. None when entries exist or nothing to say.
|
||||
pub notice: Option<String>,
|
||||
pub cached_at: u64, // Unix timestamp when this data was cached
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user