Ask for permission in the CLI (#6475)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -2,6 +2,7 @@ use anyhow::Result;
|
||||
use clap::{Args, CommandFactory, Parser, Subcommand};
|
||||
use clap_complete::{generate, Shell as ClapShell};
|
||||
use goose::config::{Config, ExtensionConfig};
|
||||
use goose::posthog::get_telemetry_choice;
|
||||
use goose_mcp::mcp_server_runner::{serve, McpCommand};
|
||||
use goose_mcp::{
|
||||
AutoVisualiserRouter, ComputerControllerServer, DeveloperServer, MemoryServer, TutorialServer,
|
||||
@@ -9,7 +10,7 @@ use goose_mcp::{
|
||||
|
||||
use crate::commands::acp::run_acp_agent;
|
||||
use crate::commands::bench::agent_generator;
|
||||
use crate::commands::configure::handle_configure;
|
||||
use crate::commands::configure::{configure_telemetry_consent_dialog, handle_configure};
|
||||
use crate::commands::info::handle_info;
|
||||
use crate::commands::project::{handle_project_default, handle_projects_interactive};
|
||||
use crate::commands::recipe::{handle_deeplink, handle_list, handle_open, handle_validate};
|
||||
@@ -1039,6 +1040,10 @@ async fn handle_interactive_session(
|
||||
session_opts: SessionOptions,
|
||||
extension_opts: ExtensionOptions,
|
||||
) -> Result<()> {
|
||||
if get_telemetry_choice().is_none() {
|
||||
configure_telemetry_consent_dialog()?;
|
||||
}
|
||||
|
||||
let session_start = std::time::Instant::now();
|
||||
let session_type = if resume { "resumed" } else { "new" };
|
||||
|
||||
@@ -1247,6 +1252,10 @@ async fn handle_run_command(
|
||||
output_opts: OutputOptions,
|
||||
model_opts: ModelOptions,
|
||||
) -> Result<()> {
|
||||
if run_behavior.interactive && get_telemetry_choice().is_none() {
|
||||
configure_telemetry_consent_dialog()?;
|
||||
}
|
||||
|
||||
let parsed = parse_run_input(&input_opts, output_opts.quiet)?;
|
||||
|
||||
let Some((input_config, recipe_info)) = parsed else {
|
||||
@@ -1397,6 +1406,10 @@ async fn handle_default_session() -> Result<()> {
|
||||
return handle_configure().await;
|
||||
}
|
||||
|
||||
if get_telemetry_choice().is_none() {
|
||||
configure_telemetry_consent_dialog()?;
|
||||
}
|
||||
|
||||
let session_id = get_or_create_session_id(None, false, false).await?;
|
||||
|
||||
let mut session = build_session(SessionBuilderConfig {
|
||||
|
||||
@@ -19,6 +19,7 @@ use goose::config::{
|
||||
};
|
||||
use goose::conversation::message::Message;
|
||||
use goose::model::ModelConfig;
|
||||
use goose::posthog::{get_telemetry_choice, TELEMETRY_ENABLED_KEY};
|
||||
use goose::providers::provider_test::test_provider_configuration;
|
||||
use goose::providers::{create, providers, retry_operation, RetryConfig};
|
||||
use goose::session::{SessionManager, SessionType};
|
||||
@@ -39,16 +40,78 @@ pub async fn handle_configure() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_first_time_setup(config: &Config) -> anyhow::Result<()> {
|
||||
pub fn configure_telemetry_consent_dialog() -> anyhow::Result<bool> {
|
||||
let config = Config::global();
|
||||
|
||||
println!();
|
||||
println!("{}", style("Help improve goose").bold());
|
||||
println!();
|
||||
println!(
|
||||
"{}",
|
||||
style("Welcome to goose! Let's get you set up with a provider.").dim()
|
||||
style("Would you like to help improve goose by sharing anonymous usage data?").dim()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style("This helps us understand how goose is used and identify areas for improvement.")
|
||||
.dim()
|
||||
);
|
||||
println!();
|
||||
println!("{}", style("What we collect:").dim());
|
||||
println!(
|
||||
"{}",
|
||||
style(" • Operating system, version, and architecture").dim()
|
||||
);
|
||||
println!("{}", style(" • goose version and install method").dim());
|
||||
println!("{}", style(" • Provider and model used").dim());
|
||||
println!(
|
||||
"{}",
|
||||
style(" • Extensions and tool usage counts (names only)").dim()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(" • Session metrics (duration, interaction count, token usage)").dim()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(" • Error types (e.g., \"rate_limit\", \"auth\" - no details)").dim()
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
"{}",
|
||||
style("We never collect your conversations, code, tool arguments, error messages,").dim()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style("or any personal data. You can change this anytime with 'goose configure'.").dim()
|
||||
);
|
||||
println!();
|
||||
|
||||
let enabled = cliclack::confirm("Share anonymous usage data to help improve goose?")
|
||||
.initial_value(true)
|
||||
.interact()?;
|
||||
|
||||
config.set_param(TELEMETRY_ENABLED_KEY, enabled)?;
|
||||
|
||||
if enabled {
|
||||
let _ = cliclack::log::success("Thank you for helping improve goose!");
|
||||
} else {
|
||||
let _ = cliclack::log::info("Telemetry disabled. You can enable it anytime in settings.");
|
||||
}
|
||||
|
||||
Ok(enabled)
|
||||
}
|
||||
|
||||
async fn handle_first_time_setup(config: &Config) -> anyhow::Result<()> {
|
||||
println!();
|
||||
println!("{}", style("Welcome to goose! Let's get you set up.").dim());
|
||||
println!(
|
||||
"{}",
|
||||
style(" you can rerun this command later to update your configuration").dim()
|
||||
);
|
||||
println!();
|
||||
|
||||
configure_telemetry_consent_dialog()?;
|
||||
|
||||
println!();
|
||||
cliclack::intro(style(" goose-configure ").on_cyan().black())?;
|
||||
|
||||
@@ -1031,6 +1094,11 @@ pub fn remove_extension_dialog() -> anyhow::Result<()> {
|
||||
pub async fn configure_settings_dialog() -> anyhow::Result<()> {
|
||||
let setting_type = cliclack::select("What setting would you like to configure?")
|
||||
.item("goose_mode", "goose mode", "Configure goose mode")
|
||||
.item(
|
||||
"telemetry",
|
||||
"Telemetry",
|
||||
"Enable or disable anonymous usage data collection",
|
||||
)
|
||||
.item(
|
||||
"tool_permission",
|
||||
"Tool Permission",
|
||||
@@ -1069,6 +1137,9 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
|
||||
"goose_mode" => {
|
||||
configure_goose_mode_dialog()?;
|
||||
}
|
||||
"telemetry" => {
|
||||
configure_telemetry_dialog()?;
|
||||
}
|
||||
"tool_permission" => {
|
||||
configure_tool_permissions_dialog().await.and(Ok(()))?;
|
||||
// No need to print config file path since it's already handled.
|
||||
@@ -1140,6 +1211,37 @@ pub fn configure_goose_mode_dialog() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_telemetry_dialog() -> anyhow::Result<()> {
|
||||
let config = Config::global();
|
||||
|
||||
if std::env::var("GOOSE_TELEMETRY_OFF").is_ok() {
|
||||
let _ = cliclack::log::info("Notice: GOOSE_TELEMETRY_OFF environment variable is set and will override the configuration here.");
|
||||
}
|
||||
|
||||
let current_choice = get_telemetry_choice();
|
||||
let current_status = match current_choice {
|
||||
Some(true) => "Enabled",
|
||||
Some(false) => "Disabled",
|
||||
None => "Not set",
|
||||
};
|
||||
|
||||
let _ = cliclack::log::info(format!("Current telemetry status: {}", current_status));
|
||||
|
||||
let enabled = cliclack::confirm("Share anonymous usage data to help improve goose?")
|
||||
.initial_value(current_choice.unwrap_or(true))
|
||||
.interact()?;
|
||||
|
||||
config.set_param(TELEMETRY_ENABLED_KEY, enabled)?;
|
||||
|
||||
if enabled {
|
||||
cliclack::outro("Telemetry enabled - thank you for helping improve goose!")?;
|
||||
} else {
|
||||
cliclack::outro("Telemetry disabled")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
|
||||
let config = Config::global();
|
||||
|
||||
|
||||
@@ -24,22 +24,30 @@ static TELEMETRY_DISABLED_BY_ENV: Lazy<AtomicBool> = Lazy::new(|| {
|
||||
.into()
|
||||
});
|
||||
|
||||
/// Check if the user has made a telemetry choice.
|
||||
///
|
||||
/// 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);
|
||||
}
|
||||
|
||||
let config = Config::global();
|
||||
config.get_param::<bool>(TELEMETRY_ENABLED_KEY).ok()
|
||||
}
|
||||
|
||||
/// Check if telemetry is enabled.
|
||||
///
|
||||
/// Returns false if:
|
||||
/// - GOOSE_TELEMETRY_OFF environment variable is set to "1" or "true"
|
||||
/// - GOOSE_TELEMETRY_ENABLED config value is set to false
|
||||
/// - User has not made a telemetry choice yet (opt-in required)
|
||||
///
|
||||
/// Returns true otherwise (telemetry is opt-out, enabled by default)
|
||||
/// Returns true only if the user has explicitly opted in.
|
||||
pub fn is_telemetry_enabled() -> bool {
|
||||
if TELEMETRY_DISABLED_BY_ENV.load(Ordering::Relaxed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let config = Config::global();
|
||||
config
|
||||
.get_param::<bool>(TELEMETRY_ENABLED_KEY)
|
||||
.unwrap_or(true)
|
||||
get_telemetry_choice().unwrap_or(false)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user