Remove dependency on posthog-rs (#7811)
This commit is contained in:
Generated
+11
-127
@@ -750,7 +750,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
@@ -786,7 +786,7 @@ dependencies = [
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sha1",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower",
|
||||
@@ -810,7 +810,7 @@ dependencies = [
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -829,7 +829,7 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -4322,7 +4322,6 @@ dependencies = [
|
||||
"opentelemetry_sdk",
|
||||
"pastey",
|
||||
"pctx_code_mode",
|
||||
"posthog-rs",
|
||||
"pulldown-cmark",
|
||||
"rand 0.8.5",
|
||||
"rayon",
|
||||
@@ -4573,7 +4572,7 @@ dependencies = [
|
||||
"url",
|
||||
"utoipa 4.2.3",
|
||||
"uuid",
|
||||
"winreg 0.55.0",
|
||||
"winreg",
|
||||
"wiremock",
|
||||
]
|
||||
|
||||
@@ -5006,7 +5005,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.2",
|
||||
"system-configuration 0.7.0",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -7334,25 +7333,6 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "posthog-rs"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42508f04262bd31d4865363a2f665d56e337a8ddbc1de02c006697c489312a"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"regex",
|
||||
"reqwest 0.11.27",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
@@ -7994,49 +7974,6 @@ version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.32",
|
||||
"hyper-rustls 0.24.2",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 0.1.2",
|
||||
"system-configuration 0.5.1",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 0.25.4",
|
||||
"winreg 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
@@ -8070,7 +8007,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tokio-util",
|
||||
@@ -8119,7 +8056,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tokio-util",
|
||||
@@ -8458,15 +8395,6 @@ dependencies = [
|
||||
"security-framework 3.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.14.0"
|
||||
@@ -10124,12 +10052,6 @@ dependencies = [
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
@@ -10212,17 +10134,6 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.7.0"
|
||||
@@ -10231,17 +10142,7 @@ checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10788,7 +10689,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"socket2 0.6.2",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
@@ -10819,7 +10720,7 @@ dependencies = [
|
||||
"indexmap 2.13.0",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
@@ -11447,7 +11348,6 @@ checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -11739,12 +11639,6 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
@@ -12377,16 +12271,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
|
||||
@@ -122,7 +122,6 @@ schemars = { workspace = true, features = [
|
||||
"derive",
|
||||
] }
|
||||
insta = "1.43.2"
|
||||
posthog-rs = "0.4.3"
|
||||
shellexpand = { workspace = true }
|
||||
indexmap = "2.12.0"
|
||||
ignore = { workspace = true }
|
||||
|
||||
+147
-110
@@ -1,5 +1,3 @@
|
||||
//! PostHog telemetry - fires once per session creation.
|
||||
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::{get_enabled_extensions, Config};
|
||||
use crate::session::session_manager::CURRENT_SCHEMA_VERSION;
|
||||
@@ -9,12 +7,14 @@ use crate::subprocess::SubprocessExt;
|
||||
use chrono::{DateTime, Utc};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
const POSTHOG_API_KEY: &str = "phc_RyX5CaY01VtZJCQyhSR5KFh6qimUy81YwxsEpotAftT";
|
||||
const POSTHOG_CAPTURE_URL: &str = "https://us.i.posthog.com/capture/";
|
||||
|
||||
/// Config key for telemetry opt-out preference
|
||||
pub const TELEMETRY_ENABLED_KEY: &str = "GOOSE_TELEMETRY_ENABLED";
|
||||
@@ -31,7 +31,6 @@ static TELEMETRY_DISABLED_BY_ENV: Lazy<AtomicBool> = Lazy::new(|| {
|
||||
/// Returns Some(true) if telemetry is enabled, Some(false) if disabled,
|
||||
/// or None if the user hasn't made a choice yet.
|
||||
pub fn get_telemetry_choice() -> Option<bool> {
|
||||
// If disabled by env var, treat as explicit choice to disable
|
||||
if TELEMETRY_DISABLED_BY_ENV.load(Ordering::Relaxed) {
|
||||
return Some(false);
|
||||
}
|
||||
@@ -52,6 +51,44 @@ pub fn is_telemetry_enabled() -> bool {
|
||||
get_telemetry_choice().unwrap_or(false)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PostHog HTTP API
|
||||
// ============================================================================
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct CaptureEvent {
|
||||
api_key: &'static str,
|
||||
event: String,
|
||||
distinct_id: String,
|
||||
properties: HashMap<String, serde_json::Value>,
|
||||
timestamp: Option<String>,
|
||||
}
|
||||
|
||||
async fn posthog_capture(
|
||||
event_name: &str,
|
||||
distinct_id: &str,
|
||||
properties: HashMap<String, serde_json::Value>,
|
||||
) -> Result<(), String> {
|
||||
let payload = CaptureEvent {
|
||||
api_key: POSTHOG_API_KEY,
|
||||
event: event_name.to_string(),
|
||||
distinct_id: distinct_id.to_string(),
|
||||
properties,
|
||||
timestamp: Some(Utc::now().to_rfc3339()),
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
client
|
||||
.post(POSTHOG_CAPTURE_URL)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("{e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Installation Tracking
|
||||
// ============================================================================
|
||||
@@ -209,6 +246,18 @@ fn get_session_is_resumed() -> bool {
|
||||
SESSION_IS_RESUMED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Property Helpers
|
||||
// ============================================================================
|
||||
|
||||
fn insert(
|
||||
props: &mut HashMap<String, serde_json::Value>,
|
||||
key: &str,
|
||||
val: impl Into<serde_json::Value>,
|
||||
) {
|
||||
props.insert(key.to_string(), val.into());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Telemetry Events
|
||||
// ============================================================================
|
||||
@@ -281,151 +330,148 @@ async fn send_error_event(
|
||||
error_type: &str,
|
||||
context: ErrorContext,
|
||||
) -> Result<(), String> {
|
||||
let client = posthog_rs::client(POSTHOG_API_KEY).await;
|
||||
let mut event = posthog_rs::Event::new("error", &installation.installation_id);
|
||||
let mut props = HashMap::new();
|
||||
|
||||
event.insert_prop("error_type", error_type).ok();
|
||||
event
|
||||
.insert_prop("error_category", classify_error(error_type))
|
||||
.ok();
|
||||
event.insert_prop("source", "backend").ok();
|
||||
event.insert_prop("version", env!("CARGO_PKG_VERSION")).ok();
|
||||
event.insert_prop("interface", get_session_interface()).ok();
|
||||
event.insert_prop("os", std::env::consts::OS).ok();
|
||||
event.insert_prop("arch", std::env::consts::ARCH).ok();
|
||||
insert(&mut props, "error_type", error_type);
|
||||
insert(&mut props, "error_category", classify_error(error_type));
|
||||
insert(&mut props, "source", "backend");
|
||||
insert(&mut props, "version", env!("CARGO_PKG_VERSION"));
|
||||
insert(&mut props, "interface", get_session_interface());
|
||||
insert(&mut props, "os", std::env::consts::OS);
|
||||
insert(&mut props, "arch", std::env::consts::ARCH);
|
||||
|
||||
if let Some(component) = &context.component {
|
||||
event.insert_prop("component", component.as_str()).ok();
|
||||
insert(&mut props, "component", component.as_str());
|
||||
}
|
||||
if let Some(action) = &context.action {
|
||||
event.insert_prop("action", action.as_str()).ok();
|
||||
insert(&mut props, "action", action.as_str());
|
||||
}
|
||||
if let Some(error_message) = &context.error_message {
|
||||
let sanitized = sanitize_string(error_message);
|
||||
event.insert_prop("error_message", sanitized).ok();
|
||||
insert(&mut props, "error_message", sanitize_string(error_message));
|
||||
}
|
||||
|
||||
if let Some(platform_version) = get_platform_version() {
|
||||
event.insert_prop("platform_version", platform_version).ok();
|
||||
insert(&mut props, "platform_version", platform_version);
|
||||
}
|
||||
|
||||
let config = Config::global();
|
||||
if let Ok(provider) = config.get_param::<String>("GOOSE_PROVIDER") {
|
||||
event.insert_prop("provider", provider).ok();
|
||||
insert(&mut props, "provider", provider);
|
||||
}
|
||||
if let Ok(model) = config.get_param::<String>("GOOSE_MODEL") {
|
||||
event.insert_prop("model", model).ok();
|
||||
insert(&mut props, "model", model);
|
||||
}
|
||||
|
||||
client.capture(event).await.map_err(|e| format!("{:?}", e))
|
||||
posthog_capture("error", &installation.installation_id, props).await
|
||||
}
|
||||
|
||||
async fn send_custom_slash_command_event(installation: &InstallationData) -> Result<(), String> {
|
||||
let client = posthog_rs::client(POSTHOG_API_KEY).await;
|
||||
let mut event =
|
||||
posthog_rs::Event::new("custom_slash_command_used", &installation.installation_id);
|
||||
let mut props = HashMap::new();
|
||||
|
||||
event.insert_prop("source", "backend").ok();
|
||||
event.insert_prop("version", env!("CARGO_PKG_VERSION")).ok();
|
||||
event.insert_prop("interface", get_session_interface()).ok();
|
||||
event.insert_prop("os", std::env::consts::OS).ok();
|
||||
event.insert_prop("arch", std::env::consts::ARCH).ok();
|
||||
insert(&mut props, "source", "backend");
|
||||
insert(&mut props, "version", env!("CARGO_PKG_VERSION"));
|
||||
insert(&mut props, "interface", get_session_interface());
|
||||
insert(&mut props, "os", std::env::consts::OS);
|
||||
insert(&mut props, "arch", std::env::consts::ARCH);
|
||||
|
||||
if let Some(platform_version) = get_platform_version() {
|
||||
event.insert_prop("platform_version", platform_version).ok();
|
||||
insert(&mut props, "platform_version", platform_version);
|
||||
}
|
||||
|
||||
client.capture(event).await.map_err(|e| format!("{:?}", e))
|
||||
posthog_capture(
|
||||
"custom_slash_command_used",
|
||||
&installation.installation_id,
|
||||
props,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn send_session_event(installation: &InstallationData) -> Result<(), String> {
|
||||
let client = posthog_rs::client(POSTHOG_API_KEY).await;
|
||||
let mut event = posthog_rs::Event::new("session_started", &installation.installation_id);
|
||||
let mut props = HashMap::new();
|
||||
|
||||
event.insert_prop("os", std::env::consts::OS).ok();
|
||||
event.insert_prop("arch", std::env::consts::ARCH).ok();
|
||||
event.insert_prop("version", env!("CARGO_PKG_VERSION")).ok();
|
||||
event.insert_prop("is_dev", is_dev_mode()).ok();
|
||||
insert(&mut props, "os", std::env::consts::OS);
|
||||
insert(&mut props, "arch", std::env::consts::ARCH);
|
||||
insert(&mut props, "version", env!("CARGO_PKG_VERSION"));
|
||||
insert(&mut props, "is_dev", is_dev_mode());
|
||||
|
||||
if let Some(platform_version) = get_platform_version() {
|
||||
event.insert_prop("platform_version", platform_version).ok();
|
||||
insert(&mut props, "platform_version", platform_version);
|
||||
}
|
||||
|
||||
event
|
||||
.insert_prop("install_method", detect_install_method())
|
||||
.ok();
|
||||
insert(&mut props, "install_method", detect_install_method());
|
||||
insert(&mut props, "interface", get_session_interface());
|
||||
insert(&mut props, "is_resumed", get_session_is_resumed());
|
||||
insert(&mut props, "session_number", installation.session_count);
|
||||
|
||||
event.insert_prop("interface", get_session_interface()).ok();
|
||||
|
||||
event
|
||||
.insert_prop("is_resumed", get_session_is_resumed())
|
||||
.ok();
|
||||
|
||||
event
|
||||
.insert_prop("session_number", installation.session_count)
|
||||
.ok();
|
||||
let days_since_install = (Utc::now() - installation.first_seen).num_days();
|
||||
event
|
||||
.insert_prop("days_since_install", days_since_install)
|
||||
.ok();
|
||||
insert(&mut props, "days_since_install", days_since_install);
|
||||
|
||||
let config = Config::global();
|
||||
if let Ok(provider) = config.get_param::<String>("GOOSE_PROVIDER") {
|
||||
event.insert_prop("provider", provider).ok();
|
||||
insert(&mut props, "provider", provider);
|
||||
}
|
||||
if let Ok(model) = config.get_param::<String>("GOOSE_MODEL") {
|
||||
event.insert_prop("model", model).ok();
|
||||
insert(&mut props, "model", model);
|
||||
}
|
||||
|
||||
if let Ok(mode) = config.get_param::<String>("GOOSE_MODE") {
|
||||
event.insert_prop("setting_mode", mode).ok();
|
||||
insert(&mut props, "setting_mode", mode);
|
||||
}
|
||||
if let Ok(max_turns) = config.get_param::<i64>("GOOSE_MAX_TURNS") {
|
||||
event.insert_prop("setting_max_turns", max_turns).ok();
|
||||
insert(&mut props, "setting_max_turns", max_turns);
|
||||
}
|
||||
|
||||
if let Ok(lead_model) = config.get_param::<String>("GOOSE_LEAD_MODEL") {
|
||||
event.insert_prop("setting_lead_model", lead_model).ok();
|
||||
insert(&mut props, "setting_lead_model", lead_model);
|
||||
}
|
||||
if let Ok(lead_provider) = config.get_param::<String>("GOOSE_LEAD_PROVIDER") {
|
||||
event
|
||||
.insert_prop("setting_lead_provider", lead_provider)
|
||||
.ok();
|
||||
insert(&mut props, "setting_lead_provider", lead_provider);
|
||||
}
|
||||
if let Ok(lead_turns) = config.get_param::<i64>("GOOSE_LEAD_TURNS") {
|
||||
event.insert_prop("setting_lead_turns", lead_turns).ok();
|
||||
insert(&mut props, "setting_lead_turns", lead_turns);
|
||||
}
|
||||
if let Ok(lead_failure_threshold) = config.get_param::<i64>("GOOSE_LEAD_FAILURE_THRESHOLD") {
|
||||
event
|
||||
.insert_prop("setting_lead_failure_threshold", lead_failure_threshold)
|
||||
.ok();
|
||||
insert(
|
||||
&mut props,
|
||||
"setting_lead_failure_threshold",
|
||||
lead_failure_threshold,
|
||||
);
|
||||
}
|
||||
if let Ok(lead_fallback_turns) = config.get_param::<i64>("GOOSE_LEAD_FALLBACK_TURNS") {
|
||||
event
|
||||
.insert_prop("setting_lead_fallback_turns", lead_fallback_turns)
|
||||
.ok();
|
||||
insert(
|
||||
&mut props,
|
||||
"setting_lead_fallback_turns",
|
||||
lead_fallback_turns,
|
||||
);
|
||||
}
|
||||
|
||||
let extensions = get_enabled_extensions();
|
||||
event.insert_prop("extensions_count", extensions.len()).ok();
|
||||
insert(&mut props, "extensions_count", extensions.len() as u64);
|
||||
let extension_names: Vec<String> = extensions.iter().map(|e| e.name()).collect();
|
||||
event.insert_prop("extensions", extension_names).ok();
|
||||
insert(
|
||||
&mut props,
|
||||
"extensions",
|
||||
serde_json::Value::Array(
|
||||
extension_names
|
||||
.into_iter()
|
||||
.map(serde_json::Value::String)
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
event
|
||||
.insert_prop("db_schema_version", CURRENT_SCHEMA_VERSION)
|
||||
.ok();
|
||||
insert(
|
||||
&mut props,
|
||||
"db_schema_version",
|
||||
CURRENT_SCHEMA_VERSION as u64,
|
||||
);
|
||||
|
||||
let session_manager = SessionManager::instance();
|
||||
if let Ok(insights) = session_manager.get_insights().await {
|
||||
event
|
||||
.insert_prop("total_sessions", insights.total_sessions)
|
||||
.ok();
|
||||
event
|
||||
.insert_prop("total_tokens", insights.total_tokens)
|
||||
.ok();
|
||||
insert(&mut props, "total_sessions", insights.total_sessions as u64);
|
||||
insert(&mut props, "total_tokens", insights.total_tokens as u64);
|
||||
}
|
||||
|
||||
client.capture(event).await.map_err(|e| format!("{:?}", e))
|
||||
posthog_capture("session_started", &installation.installation_id, props).await
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -490,22 +536,16 @@ use std::sync::LazyLock;
|
||||
|
||||
static SENSITIVE_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
|
||||
vec![
|
||||
// File paths with usernames (Unix)
|
||||
Regex::new(r"/Users/[^/\s]+").unwrap(),
|
||||
Regex::new(r"/home/[^/\s]+").unwrap(),
|
||||
// File paths with usernames (Windows)
|
||||
Regex::new(r"(?i)C:\\Users\\[^\\\s]+").unwrap(),
|
||||
// API keys and tokens (common patterns)
|
||||
Regex::new(r"sk-[a-zA-Z0-9]{20,}").unwrap(),
|
||||
Regex::new(r"pk-[a-zA-Z0-9]{20,}").unwrap(),
|
||||
Regex::new(r"(?i)key[_-]?[a-zA-Z0-9]{16,}").unwrap(),
|
||||
Regex::new(r"(?i)token[_-]?[a-zA-Z0-9]{16,}").unwrap(),
|
||||
Regex::new(r"(?i)bearer\s+[a-zA-Z0-9._-]+").unwrap(),
|
||||
// Email addresses
|
||||
Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap(),
|
||||
// URLs with auth info
|
||||
Regex::new(r"https?://[^:]+:[^@]+@").unwrap(),
|
||||
// UUIDs (might be session/user IDs in error messages)
|
||||
Regex::new(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")
|
||||
.unwrap(),
|
||||
]
|
||||
@@ -539,7 +579,7 @@ fn sanitize_value(value: serde_json::Value) -> serde_json::Value {
|
||||
// ============================================================================
|
||||
pub async fn emit_event(
|
||||
event_name: &str,
|
||||
mut properties: std::collections::HashMap<String, serde_json::Value>,
|
||||
mut properties: HashMap<String, serde_json::Value>,
|
||||
) -> Result<(), String> {
|
||||
if !is_telemetry_enabled() {
|
||||
return Ok(());
|
||||
@@ -551,17 +591,15 @@ pub async fn emit_event(
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
let installation = load_or_create_installation();
|
||||
let client = posthog_rs::client(POSTHOG_API_KEY).await;
|
||||
let mut event = posthog_rs::Event::new(event_name, &installation.installation_id);
|
||||
|
||||
event.insert_prop("os", std::env::consts::OS).ok();
|
||||
event.insert_prop("arch", std::env::consts::ARCH).ok();
|
||||
event.insert_prop("version", env!("CARGO_PKG_VERSION")).ok();
|
||||
event.insert_prop("interface", "desktop").ok();
|
||||
event.insert_prop("source", "ui").ok();
|
||||
insert(&mut properties, "os", std::env::consts::OS);
|
||||
insert(&mut properties, "arch", std::env::consts::ARCH);
|
||||
insert(&mut properties, "version", env!("CARGO_PKG_VERSION"));
|
||||
insert(&mut properties, "interface", "desktop");
|
||||
insert(&mut properties, "source", "ui");
|
||||
|
||||
if let Some(platform_version) = get_platform_version() {
|
||||
event.insert_prop("platform_version", platform_version).ok();
|
||||
insert(&mut properties, "platform_version", platform_version);
|
||||
}
|
||||
|
||||
if event_name == "error_occurred" || event_name == "app_crashed" {
|
||||
@@ -574,19 +612,18 @@ pub async fn emit_event(
|
||||
}
|
||||
}
|
||||
|
||||
for (key, value) in properties {
|
||||
let key_lower = key.to_lowercase();
|
||||
if key_lower.contains("key")
|
||||
|| key_lower.contains("token")
|
||||
|| key_lower.contains("secret")
|
||||
|| key_lower.contains("password")
|
||||
|| key_lower.contains("credential")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let sanitized_value = sanitize_value(value);
|
||||
event.insert_prop(&key, sanitized_value).ok();
|
||||
}
|
||||
let sanitized: HashMap<String, serde_json::Value> = properties
|
||||
.into_iter()
|
||||
.filter(|(key, _)| {
|
||||
let key_lower = key.to_lowercase();
|
||||
!key_lower.contains("key")
|
||||
&& !key_lower.contains("token")
|
||||
&& !key_lower.contains("secret")
|
||||
&& !key_lower.contains("password")
|
||||
&& !key_lower.contains("credential")
|
||||
})
|
||||
.map(|(k, v)| (k, sanitize_value(v)))
|
||||
.collect();
|
||||
|
||||
client.capture(event).await.map_err(|e| format!("{:?}", e))
|
||||
posthog_capture(event_name, &installation.installation_id, sanitized).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user