grok-cli-acp 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +42 -0
- package/.github/workflows/ci.yml +30 -0
- package/.github/workflows/rust.yml +22 -0
- package/.grok/.env.example +85 -0
- package/.grok/COMPLETE_FIX_SUMMARY.md +466 -0
- package/.grok/ENV_CONFIG_GUIDE.md +173 -0
- package/.grok/QUICK_REFERENCE.md +180 -0
- package/.grok/README.md +104 -0
- package/.grok/TESTING_GUIDE.md +393 -0
- package/CHANGELOG.md +465 -0
- package/CODE_REVIEW_SUMMARY.md +414 -0
- package/COMPLETE_FIX_SUMMARY.md +415 -0
- package/CONFIGURATION.md +489 -0
- package/CONTEXT_FILES_GUIDE.md +419 -0
- package/CONTRIBUTING.md +55 -0
- package/CURSOR_POSITION_FIX.md +206 -0
- package/Cargo.toml +88 -0
- package/ERROR_HANDLING_REPORT.md +361 -0
- package/FINAL_FIX_SUMMARY.md +462 -0
- package/FIXES.md +37 -0
- package/FIXES_SUMMARY.md +87 -0
- package/GROK_API_MIGRATION_SUMMARY.md +111 -0
- package/LICENSE +22 -0
- package/MIGRATION_TO_GROK_API.md +223 -0
- package/README.md +504 -0
- package/REVIEW_COMPLETE.md +416 -0
- package/REVIEW_QUICK_REFERENCE.md +173 -0
- package/SECURITY.md +463 -0
- package/SECURITY_AUDIT.md +661 -0
- package/SETUP.md +287 -0
- package/TESTING_TOOLS.md +88 -0
- package/TESTING_TOOL_EXECUTION.md +239 -0
- package/TOOL_EXECUTION_FIX.md +491 -0
- package/VERIFICATION_CHECKLIST.md +419 -0
- package/docs/API.md +74 -0
- package/docs/CHAT_LOGGING.md +39 -0
- package/docs/CURSOR_FIX_DEMO.md +306 -0
- package/docs/ERROR_HANDLING_GUIDE.md +547 -0
- package/docs/FILE_OPERATIONS.md +449 -0
- package/docs/INTERACTIVE.md +401 -0
- package/docs/PROJECT_CREATION_GUIDE.md +570 -0
- package/docs/QUICKSTART.md +378 -0
- package/docs/QUICK_REFERENCE.md +691 -0
- package/docs/RELEASE_NOTES_0.1.2.md +240 -0
- package/docs/TOOLS.md +459 -0
- package/docs/TOOLS_QUICK_REFERENCE.md +210 -0
- package/docs/ZED_INTEGRATION.md +371 -0
- package/docs/extensions.md +464 -0
- package/docs/settings.md +293 -0
- package/examples/extensions/logging-hook/README.md +91 -0
- package/examples/extensions/logging-hook/extension.json +22 -0
- package/package.json +30 -0
- package/scripts/test_acp.py +252 -0
- package/scripts/test_acp.sh +143 -0
- package/scripts/test_acp_simple.sh +72 -0
- package/src/acp/mod.rs +741 -0
- package/src/acp/protocol.rs +323 -0
- package/src/acp/security.rs +298 -0
- package/src/acp/tools.rs +697 -0
- package/src/bin/banner_demo.rs +216 -0
- package/src/bin/docgen.rs +18 -0
- package/src/bin/installer.rs +217 -0
- package/src/cli/app.rs +310 -0
- package/src/cli/commands/acp.rs +721 -0
- package/src/cli/commands/chat.rs +485 -0
- package/src/cli/commands/code.rs +513 -0
- package/src/cli/commands/config.rs +394 -0
- package/src/cli/commands/health.rs +442 -0
- package/src/cli/commands/history.rs +421 -0
- package/src/cli/commands/mod.rs +14 -0
- package/src/cli/commands/settings.rs +1384 -0
- package/src/cli/mod.rs +166 -0
- package/src/config/mod.rs +2212 -0
- package/src/display/ascii_art.rs +139 -0
- package/src/display/banner.rs +289 -0
- package/src/display/components/input.rs +323 -0
- package/src/display/components/mod.rs +2 -0
- package/src/display/components/settings_list.rs +306 -0
- package/src/display/interactive.rs +1255 -0
- package/src/display/mod.rs +62 -0
- package/src/display/terminal.rs +42 -0
- package/src/display/tips.rs +316 -0
- package/src/grok_client_ext.rs +177 -0
- package/src/hooks/loader.rs +407 -0
- package/src/hooks/mod.rs +158 -0
- package/src/lib.rs +174 -0
- package/src/main.rs +65 -0
- package/src/mcp/client.rs +195 -0
- package/src/mcp/config.rs +20 -0
- package/src/mcp/mod.rs +6 -0
- package/src/mcp/protocol.rs +67 -0
- package/src/utils/auth.rs +41 -0
- package/src/utils/chat_logger.rs +568 -0
- package/src/utils/context.rs +390 -0
- package/src/utils/mod.rs +16 -0
- package/src/utils/network.rs +320 -0
- package/src/utils/rate_limiter.rs +166 -0
- package/src/utils/session.rs +73 -0
- package/src/utils/shell_permissions.rs +389 -0
- package/src/utils/telemetry.rs +41 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
//! Config command handler for grok-cli
|
|
2
|
+
//!
|
|
3
|
+
//! Handles configuration management operations including showing, setting,
|
|
4
|
+
//! getting, initializing, and validating configuration settings.
|
|
5
|
+
|
|
6
|
+
use anyhow::{anyhow, Result};
|
|
7
|
+
use colored::*;
|
|
8
|
+
|
|
9
|
+
use crate::cli::{confirm, print_error, print_info, print_success, print_warning};
|
|
10
|
+
use crate::config::Config;
|
|
11
|
+
use crate::ConfigAction;
|
|
12
|
+
|
|
13
|
+
/// Handle configuration-related commands
|
|
14
|
+
pub async fn handle_config_action(action: ConfigAction, config: &Config) -> Result<()> {
|
|
15
|
+
match action {
|
|
16
|
+
ConfigAction::Show => show_config(config).await,
|
|
17
|
+
ConfigAction::Set { key, value } => set_config_value(&key, &value).await,
|
|
18
|
+
ConfigAction::Get { key } => get_config_value(&key).await,
|
|
19
|
+
ConfigAction::Init { force } => init_config(force).await,
|
|
20
|
+
ConfigAction::Validate => validate_config().await,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Show current configuration
|
|
25
|
+
async fn show_config(config: &Config) -> Result<()> {
|
|
26
|
+
println!("{}", "⚙️ Grok CLI Configuration".cyan().bold());
|
|
27
|
+
println!();
|
|
28
|
+
|
|
29
|
+
// API Configuration
|
|
30
|
+
println!("{}", "API Configuration:".green().bold());
|
|
31
|
+
let api_key_display = if config.api_key.is_some() {
|
|
32
|
+
"✓ Set (hidden)".green()
|
|
33
|
+
} else {
|
|
34
|
+
"✗ Not set".red()
|
|
35
|
+
};
|
|
36
|
+
println!(" API Key: {}", api_key_display);
|
|
37
|
+
println!(" Default Model: {}", config.default_model.cyan());
|
|
38
|
+
println!(" Temperature: {}", config.default_temperature);
|
|
39
|
+
println!(" Max Tokens: {}", config.default_max_tokens);
|
|
40
|
+
println!(" Timeout: {}s", config.timeout_secs);
|
|
41
|
+
println!(" Max Retries: {}", config.max_retries);
|
|
42
|
+
println!();
|
|
43
|
+
|
|
44
|
+
// ACP Configuration
|
|
45
|
+
println!("{}", "ACP Configuration:".green().bold());
|
|
46
|
+
let acp_status = if config.acp.enabled {
|
|
47
|
+
"✓ Enabled".green()
|
|
48
|
+
} else {
|
|
49
|
+
"✗ Disabled".red()
|
|
50
|
+
};
|
|
51
|
+
println!(" Status: {}", acp_status);
|
|
52
|
+
println!(" Bind Host: {}", config.acp.bind_host);
|
|
53
|
+
let port_display = config
|
|
54
|
+
.acp
|
|
55
|
+
.default_port
|
|
56
|
+
.map(|p| p.to_string())
|
|
57
|
+
.unwrap_or_else(|| "Auto-assign".to_string());
|
|
58
|
+
println!(" Default Port: {}", port_display);
|
|
59
|
+
println!(" Protocol Version: {}", config.acp.protocol_version);
|
|
60
|
+
let dev_mode = if config.acp.dev_mode {
|
|
61
|
+
"✓ Enabled".yellow()
|
|
62
|
+
} else {
|
|
63
|
+
"✗ Disabled".dimmed()
|
|
64
|
+
};
|
|
65
|
+
println!(" Dev Mode: {}", dev_mode);
|
|
66
|
+
println!();
|
|
67
|
+
|
|
68
|
+
// Network Configuration
|
|
69
|
+
println!("{}", "Network Configuration:".green().bold());
|
|
70
|
+
let starlink_opt = if config.network.starlink_optimizations {
|
|
71
|
+
"✓ Enabled".green()
|
|
72
|
+
} else {
|
|
73
|
+
"✗ Disabled".red()
|
|
74
|
+
};
|
|
75
|
+
println!(" Starlink Optimizations: {}", starlink_opt);
|
|
76
|
+
println!(" Base Retry Delay: {}s", config.network.base_retry_delay);
|
|
77
|
+
println!(" Max Retry Delay: {}s", config.network.max_retry_delay);
|
|
78
|
+
let health_monitoring = if config.network.health_monitoring {
|
|
79
|
+
"✓ Enabled".green()
|
|
80
|
+
} else {
|
|
81
|
+
"✗ Disabled".dimmed()
|
|
82
|
+
};
|
|
83
|
+
println!(" Health Monitoring: {}", health_monitoring);
|
|
84
|
+
println!(" Connect Timeout: {}s", config.network.connect_timeout);
|
|
85
|
+
println!(" Read Timeout: {}s", config.network.read_timeout);
|
|
86
|
+
println!();
|
|
87
|
+
|
|
88
|
+
// UI Configuration
|
|
89
|
+
println!("{}", "UI Configuration:".green().bold());
|
|
90
|
+
let colors = if config.ui.colors {
|
|
91
|
+
"✓ Enabled".green()
|
|
92
|
+
} else {
|
|
93
|
+
"✗ Disabled".dimmed()
|
|
94
|
+
};
|
|
95
|
+
println!(" Colors: {}", colors);
|
|
96
|
+
let progress = if config.ui.progress_bars {
|
|
97
|
+
"✓ Enabled".green()
|
|
98
|
+
} else {
|
|
99
|
+
"✗ Disabled".dimmed()
|
|
100
|
+
};
|
|
101
|
+
println!(" Progress Bars: {}", progress);
|
|
102
|
+
let verbose_errors = if config.ui.verbose_errors {
|
|
103
|
+
"✓ Enabled".yellow()
|
|
104
|
+
} else {
|
|
105
|
+
"✗ Disabled".dimmed()
|
|
106
|
+
};
|
|
107
|
+
println!(" Verbose Errors: {}", verbose_errors);
|
|
108
|
+
let terminal_width = if config.ui.terminal_width == 0 {
|
|
109
|
+
"Auto-detect".to_string()
|
|
110
|
+
} else {
|
|
111
|
+
config.ui.terminal_width.to_string()
|
|
112
|
+
};
|
|
113
|
+
println!(" Terminal Width: {}", terminal_width);
|
|
114
|
+
let unicode = if config.ui.unicode {
|
|
115
|
+
"✓ Enabled".green()
|
|
116
|
+
} else {
|
|
117
|
+
"✗ Disabled".dimmed()
|
|
118
|
+
};
|
|
119
|
+
println!(" Unicode: {}", unicode);
|
|
120
|
+
println!();
|
|
121
|
+
|
|
122
|
+
// Logging Configuration
|
|
123
|
+
println!("{}", "Logging Configuration:".green().bold());
|
|
124
|
+
println!(" Level: {}", config.logging.level.cyan());
|
|
125
|
+
let file_logging = if config.logging.file_logging {
|
|
126
|
+
"✓ Enabled".green()
|
|
127
|
+
} else {
|
|
128
|
+
"✗ Disabled".dimmed()
|
|
129
|
+
};
|
|
130
|
+
println!(" File Logging: {}", file_logging);
|
|
131
|
+
if let Some(ref log_file) = config.logging.log_file {
|
|
132
|
+
println!(" Log File: {}", log_file.display());
|
|
133
|
+
}
|
|
134
|
+
println!(" Max File Size: {} MB", config.logging.max_file_size_mb);
|
|
135
|
+
println!(" Rotation Count: {}", config.logging.rotation_count);
|
|
136
|
+
println!();
|
|
137
|
+
|
|
138
|
+
// Configuration source information
|
|
139
|
+
println!("{}", "Configuration Source:".green().bold());
|
|
140
|
+
if let Some(ref source) = config.config_source {
|
|
141
|
+
println!(" {}", source.display());
|
|
142
|
+
} else {
|
|
143
|
+
println!(" Unknown");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Ok(())
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Set a configuration value
|
|
150
|
+
async fn set_config_value(key: &str, value: &str) -> Result<()> {
|
|
151
|
+
print_info(&format!(
|
|
152
|
+
"Setting configuration: {} = {}",
|
|
153
|
+
key.cyan(),
|
|
154
|
+
value.yellow()
|
|
155
|
+
));
|
|
156
|
+
|
|
157
|
+
// Load current config
|
|
158
|
+
let mut config = Config::load(None).await?;
|
|
159
|
+
|
|
160
|
+
// Set the value
|
|
161
|
+
config
|
|
162
|
+
.set_value(key, value)
|
|
163
|
+
.map_err(|e| anyhow!("Failed to set configuration value: {}", e))?;
|
|
164
|
+
|
|
165
|
+
// Validate the updated config
|
|
166
|
+
if let Err(e) = config.validate() {
|
|
167
|
+
print_error(&format!("Invalid configuration value: {}", e));
|
|
168
|
+
return Err(e);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Save the config
|
|
172
|
+
config
|
|
173
|
+
.save(None)
|
|
174
|
+
.await
|
|
175
|
+
.map_err(|e| anyhow!("Failed to save configuration: {}", e))?;
|
|
176
|
+
|
|
177
|
+
print_success(&format!("Configuration updated: {} = {}", key, value));
|
|
178
|
+
|
|
179
|
+
// Show a relevant tip based on the key that was set
|
|
180
|
+
show_config_tip(key);
|
|
181
|
+
|
|
182
|
+
Ok(())
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get a configuration value
|
|
186
|
+
async fn get_config_value(key: &str) -> Result<()> {
|
|
187
|
+
print_info(&format!("Getting configuration value for: {}", key.cyan()));
|
|
188
|
+
|
|
189
|
+
// Load current config
|
|
190
|
+
let config = Config::load(None).await?;
|
|
191
|
+
|
|
192
|
+
// Get the value
|
|
193
|
+
match config.get_value(key) {
|
|
194
|
+
Ok(value) => {
|
|
195
|
+
if key == "api_key" && !value.is_empty() {
|
|
196
|
+
println!("{}: {}", key.cyan(), "*** (hidden) ***".dimmed());
|
|
197
|
+
} else {
|
|
198
|
+
println!("{}: {}", key.cyan(), value.yellow());
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
Err(e) => {
|
|
202
|
+
print_error(&format!("Configuration key not found: {}", e));
|
|
203
|
+
return Err(e);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
Ok(())
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// Initialize configuration with defaults
|
|
211
|
+
async fn init_config(force: bool) -> Result<()> {
|
|
212
|
+
print_info("Initializing Grok CLI configuration...");
|
|
213
|
+
|
|
214
|
+
if !force {
|
|
215
|
+
let config_path = Config::default_config_path()?;
|
|
216
|
+
if config_path.exists() {
|
|
217
|
+
print_warning("Configuration file already exists!");
|
|
218
|
+
println!(" Path: {}", config_path.display());
|
|
219
|
+
|
|
220
|
+
if !confirm("Do you want to overwrite the existing configuration?")? {
|
|
221
|
+
print_info("Configuration initialization cancelled.");
|
|
222
|
+
return Ok(());
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
match Config::init(force).await {
|
|
228
|
+
Ok(config_path) => {
|
|
229
|
+
print_success("Configuration initialized successfully!");
|
|
230
|
+
println!(" Path: {}", config_path.display());
|
|
231
|
+
println!();
|
|
232
|
+
print_info("Next steps:");
|
|
233
|
+
println!(
|
|
234
|
+
" 1. Set your X API key: {}",
|
|
235
|
+
"grok config set api_key YOUR_API_KEY".yellow()
|
|
236
|
+
);
|
|
237
|
+
println!(" 2. Verify configuration: {}", "grok config show".yellow());
|
|
238
|
+
println!(" 3. Test connection: {}", "grok health --api".yellow());
|
|
239
|
+
}
|
|
240
|
+
Err(e) => {
|
|
241
|
+
print_error(&format!("Failed to initialize configuration: {}", e));
|
|
242
|
+
return Err(e);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
Ok(())
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/// Validate current configuration
|
|
250
|
+
async fn validate_config() -> Result<()> {
|
|
251
|
+
print_info("Validating configuration...");
|
|
252
|
+
|
|
253
|
+
let config = Config::load(None).await?;
|
|
254
|
+
|
|
255
|
+
match config.validate() {
|
|
256
|
+
Ok(()) => {
|
|
257
|
+
print_success("Configuration is valid!");
|
|
258
|
+
|
|
259
|
+
// Additional checks
|
|
260
|
+
let mut warnings = Vec::new();
|
|
261
|
+
let mut suggestions = Vec::new();
|
|
262
|
+
|
|
263
|
+
// Check API key
|
|
264
|
+
if config.api_key.is_none() {
|
|
265
|
+
warnings.push("No API key configured".to_string());
|
|
266
|
+
suggestions
|
|
267
|
+
.push("Set your X API key with: grok config set api_key YOUR_KEY".to_string());
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check network settings for Starlink
|
|
271
|
+
if config.network.starlink_optimizations {
|
|
272
|
+
print_info("Starlink optimizations are enabled");
|
|
273
|
+
if config.network.base_retry_delay < 2 {
|
|
274
|
+
suggestions.push("Consider increasing base_retry_delay to 2+ seconds for satellite connections".to_string());
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check ACP settings
|
|
279
|
+
if config.acp.enabled {
|
|
280
|
+
print_info("ACP (Zed integration) is enabled");
|
|
281
|
+
if let Some(port) = config.acp.default_port
|
|
282
|
+
&& port < 1024 {
|
|
283
|
+
warnings.push(format!("ACP port {} may require elevated privileges", port));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Display warnings and suggestions
|
|
288
|
+
if !warnings.is_empty() {
|
|
289
|
+
println!();
|
|
290
|
+
println!("{}", "⚠️ Warnings:".yellow().bold());
|
|
291
|
+
for warning in warnings {
|
|
292
|
+
println!(" • {}", warning.yellow());
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if !suggestions.is_empty() {
|
|
297
|
+
println!();
|
|
298
|
+
println!("{}", "💡 Suggestions:".blue().bold());
|
|
299
|
+
for suggestion in suggestions {
|
|
300
|
+
println!(" • {}", suggestion);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
Err(e) => {
|
|
305
|
+
print_error(&format!("Configuration validation failed: {}", e));
|
|
306
|
+
|
|
307
|
+
println!();
|
|
308
|
+
print_info("To fix configuration issues:");
|
|
309
|
+
println!(" 1. Check values with: {}", "grok config show".yellow());
|
|
310
|
+
println!(
|
|
311
|
+
" 2. Reset to defaults: {}",
|
|
312
|
+
"grok config init --force".yellow()
|
|
313
|
+
);
|
|
314
|
+
println!(
|
|
315
|
+
" 3. Set values manually: {}",
|
|
316
|
+
"grok config set <key> <value>".yellow()
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
return Err(e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
Ok(())
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/// Show a helpful tip based on the configuration key that was set
|
|
327
|
+
fn show_config_tip(key: &str) {
|
|
328
|
+
match key {
|
|
329
|
+
"api_key" => {
|
|
330
|
+
print_info("💡 Test your API key with: grok health --api");
|
|
331
|
+
}
|
|
332
|
+
"acp.enabled" => {
|
|
333
|
+
print_info("💡 Start ACP server for Zed integration with: grok acp server");
|
|
334
|
+
}
|
|
335
|
+
"network.starlink_optimizations" => {
|
|
336
|
+
print_info("💡 Starlink optimizations help with satellite network instability");
|
|
337
|
+
}
|
|
338
|
+
"default_model" => {
|
|
339
|
+
print_info("💡 Available models: grok-2-latest, grok-2, grok-1");
|
|
340
|
+
}
|
|
341
|
+
"logging.level" => {
|
|
342
|
+
print_info("💡 Valid log levels: trace, debug, info, warn, error");
|
|
343
|
+
}
|
|
344
|
+
_ => {}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/// List all available configuration keys
|
|
349
|
+
pub fn list_config_keys() -> Vec<(&'static str, &'static str)> {
|
|
350
|
+
vec![
|
|
351
|
+
("api_key", "X API key for Grok access"),
|
|
352
|
+
("default_model", "Default model to use"),
|
|
353
|
+
("default_temperature", "Default temperature (0.0-2.0)"),
|
|
354
|
+
("default_max_tokens", "Default maximum tokens"),
|
|
355
|
+
("timeout_secs", "Request timeout in seconds"),
|
|
356
|
+
("max_retries", "Maximum retry attempts"),
|
|
357
|
+
("acp.enabled", "Enable ACP functionality"),
|
|
358
|
+
("acp.bind_host", "ACP server bind host"),
|
|
359
|
+
(
|
|
360
|
+
"network.starlink_optimizations",
|
|
361
|
+
"Enable Starlink optimizations",
|
|
362
|
+
),
|
|
363
|
+
("ui.colors", "Enable colored output"),
|
|
364
|
+
("ui.progress_bars", "Enable progress bars"),
|
|
365
|
+
("ui.verbose_errors", "Show detailed errors"),
|
|
366
|
+
("ui.unicode", "Enable Unicode characters"),
|
|
367
|
+
("logging.level", "Log level (trace/debug/info/warn/error)"),
|
|
368
|
+
("logging.file_logging", "Enable file logging"),
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
#[cfg(test)]
|
|
373
|
+
mod tests {
|
|
374
|
+
use super::*;
|
|
375
|
+
|
|
376
|
+
#[test]
|
|
377
|
+
fn test_list_config_keys() {
|
|
378
|
+
let keys = list_config_keys();
|
|
379
|
+
assert!(!keys.is_empty());
|
|
380
|
+
|
|
381
|
+
// Check that we have the essential keys
|
|
382
|
+
assert!(keys.iter().any(|(key, _)| *key == "api_key"));
|
|
383
|
+
assert!(keys.iter().any(|(key, _)| *key == "default_model"));
|
|
384
|
+
assert!(keys.iter().any(|(key, _)| *key == "acp.enabled"));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#[test]
|
|
388
|
+
fn test_config_tip_coverage() {
|
|
389
|
+
// Test that show_config_tip doesn't panic for various keys
|
|
390
|
+
show_config_tip("api_key");
|
|
391
|
+
show_config_tip("unknown_key");
|
|
392
|
+
show_config_tip("acp.enabled");
|
|
393
|
+
}
|
|
394
|
+
}
|