feat(helexa-acp): HELEXA_ACP_LOG_FILE env for editor-host logging
All checks were successful
build-prerelease / Resolve version stamps (push) Successful in 37s
CI / Format (push) Successful in 37s
CI / Clippy (push) Successful in 2m44s
CI / Test (push) Successful in 5m3s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
build-prerelease / Build cortex binary (push) Successful in 4m36s
build-prerelease / Build neuron-blackwell (push) Successful in 6m1s
build-prerelease / Package cortex RPM (push) Successful in 1m22s
build-prerelease / Build neuron-ampere (push) Successful in 8m23s
build-prerelease / Build neuron-ada (push) Successful in 5m26s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 2m57s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 3m48s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 6m43s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 59s
All checks were successful
build-prerelease / Resolve version stamps (push) Successful in 37s
CI / Format (push) Successful in 37s
CI / Clippy (push) Successful in 2m44s
CI / Test (push) Successful in 5m3s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
build-prerelease / Build cortex binary (push) Successful in 4m36s
build-prerelease / Build neuron-blackwell (push) Successful in 6m1s
build-prerelease / Package cortex RPM (push) Successful in 1m22s
build-prerelease / Build neuron-ampere (push) Successful in 8m23s
build-prerelease / Build neuron-ada (push) Successful in 5m26s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 2m57s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 3m48s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 6m43s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 59s
Editors that launch ACP agents (Zed today) don't reliably surface the child's stderr — and `args` in an `agent_servers` config is exec-args, not shell, so the usual `&>>` redirect trick doesn't work. Add a HELEXA_ACP_LOG_FILE env var that, when set to an absolute path, routes the tracing subscriber to append-write that file (ANSI off) instead of stderr. RUST_LOG still controls levels. Unopenable paths fall back to stderr with a warning so a typo doesn't silence the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,59 @@ use agent::Agent;
|
|||||||
use config::{Config, EndpointConfig, WireApi};
|
use config::{Config, EndpointConfig, WireApi};
|
||||||
use provider::{Provider, openai_chat::OpenAIChatProvider};
|
use provider::{Provider, openai_chat::OpenAIChatProvider};
|
||||||
|
|
||||||
|
/// Set up tracing. Logs go to stderr by default — stdout is
|
||||||
|
/// reserved for the JSON-RPC stream. Setting `HELEXA_ACP_LOG_FILE`
|
||||||
|
/// to an absolute path appends logs to that file instead, which is
|
||||||
|
/// the practical way to capture debug output when the agent runs
|
||||||
|
/// under an editor (Zed, etc.) that doesn't surface stderr.
|
||||||
|
///
|
||||||
|
/// `RUST_LOG` still controls levels (e.g. `helexa_acp=debug`).
|
||||||
|
/// ANSI colours are auto-stripped when writing to a file so the log
|
||||||
|
/// is plain text.
|
||||||
|
fn init_tracing() {
|
||||||
|
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
|
||||||
|
|
||||||
|
let log_file = std::env::var("HELEXA_ACP_LOG_FILE")
|
||||||
|
.ok()
|
||||||
|
.filter(|s| !s.is_empty());
|
||||||
|
|
||||||
|
match log_file {
|
||||||
|
Some(path) => match std::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&path)
|
||||||
|
{
|
||||||
|
Ok(file) => {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_writer(std::sync::Mutex::new(file))
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.with_ansi(false)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Fall back to stderr and shout. We don't want a
|
||||||
|
// typo'd log path to silence the agent entirely.
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.init();
|
||||||
|
tracing::warn!(
|
||||||
|
path = %path,
|
||||||
|
error = %e,
|
||||||
|
"HELEXA_ACP_LOG_FILE could not be opened; using stderr"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Build a provider for `endpoint` according to its declared
|
/// Build a provider for `endpoint` according to its declared
|
||||||
/// `wire_api`. Future wire types (OpenAI Responses, Anthropic
|
/// `wire_api`. Future wire types (OpenAI Responses, Anthropic
|
||||||
/// /v1/messages, Ollama native) slot in here without changing the
|
/// /v1/messages, Ollama native) slot in here without changing the
|
||||||
@@ -49,14 +102,7 @@ fn build_provider(endpoint: EndpointConfig) -> anyhow::Result<Arc<dyn Provider>>
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// Logs go to stderr — stdout is reserved for the JSON-RPC stream.
|
init_tracing();
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_writer(std::io::stderr)
|
|
||||||
.with_env_filter(
|
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
||||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
|
||||||
)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let cfg = Config::load()
|
let cfg = Config::load()
|
||||||
.map_err(|e| agent_client_protocol::util::internal_error(format!("config: {e:#}")))?;
|
.map_err(|e| agent_client_protocol::util::internal_error(format!("config: {e:#}")))?;
|
||||||
|
|||||||
Reference in New Issue
Block a user