Skip to content

Commit f794f4e

Browse files
Merge pull request #69 from coval-ai/feat/aci-1-agent-mode-foundation
feat: add agent mode output foundation
2 parents b15fcb7 + 68184a5 commit f794f4e

24 files changed

Lines changed: 1381 additions & 217 deletions

src/cli.rs

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::{Parser, Subcommand};
33
use crate::client::CovalClient;
44
use crate::commands;
55
use crate::config::Config;
6-
use crate::output::OutputFormat;
6+
use crate::output::{OutputContext, OutputFormat};
77

88
#[derive(Parser)]
99
#[command(name = "coval")]
@@ -21,6 +21,9 @@ pub struct Cli {
2121

2222
#[arg(long, global = true, env = "COVAL_API_URL")]
2323
pub api_url: Option<String>,
24+
25+
#[arg(long, global = true)]
26+
pub agent: bool,
2427
}
2528

2629
#[derive(Subcommand)]
@@ -100,18 +103,68 @@ pub enum Commands {
100103
},
101104
}
102105

103-
pub async fn run(cli: Cli) -> anyhow::Result<()> {
106+
impl Commands {
107+
pub fn resource(&self) -> &'static str {
108+
match self {
109+
Self::Login(_) | Self::Whoami => "auth",
110+
Self::Config { .. } => "config",
111+
Self::Agents { .. } => "agents",
112+
Self::Conversations { .. } => "conversations",
113+
Self::Runs { .. } => "runs",
114+
Self::Simulations { .. } => "simulations",
115+
Self::TestSets { .. } => "test-sets",
116+
Self::TestCases { .. } => "test-cases",
117+
Self::Personas { .. } => "personas",
118+
Self::Metrics { .. } => "metrics",
119+
Self::Mutations { .. } => "mutations",
120+
Self::ApiKeys { .. } => "api-keys",
121+
Self::RunTemplates { .. } => "run-templates",
122+
Self::ScheduledRuns { .. } => "scheduled-runs",
123+
Self::Dashboards { command } => match command {
124+
commands::dashboards::DashboardCommands::Widgets { .. } => "widgets",
125+
_ => "dashboards",
126+
},
127+
Self::ReviewAnnotations { .. } => "review-annotations",
128+
Self::ReviewProjects { .. } => "review-projects",
129+
}
130+
}
131+
132+
pub fn operation(&self) -> &'static str {
133+
match self {
134+
Self::Login(_) => "login",
135+
Self::Whoami => "whoami",
136+
Self::Config { command } => command.operation(),
137+
Self::Agents { command } => command.operation(),
138+
Self::Conversations { command } => command.operation(),
139+
Self::Runs { command } => command.operation(),
140+
Self::Simulations { command } => command.operation(),
141+
Self::TestSets { command } => command.operation(),
142+
Self::TestCases { command } => command.operation(),
143+
Self::Personas { command } => command.operation(),
144+
Self::Metrics { command } => command.operation(),
145+
Self::Mutations { command } => command.operation(),
146+
Self::ApiKeys { command } => command.operation(),
147+
Self::RunTemplates { command } => command.operation(),
148+
Self::ScheduledRuns { command } => command.operation(),
149+
Self::Dashboards { command } => command.operation(),
150+
Self::ReviewAnnotations { command } => command.operation(),
151+
Self::ReviewProjects { command } => command.operation(),
152+
}
153+
}
154+
}
155+
156+
pub async fn run(cli: Cli, ctx: &OutputContext) -> anyhow::Result<()> {
104157
let config = Config::load().unwrap_or_default();
105158
let api_key = cli.api_key.or(config.api_key);
106159
let api_url = cli.api_url.or(config.api_url);
107160

108161
match cli.command {
109-
Commands::Login(args) => commands::auth::login(args).await,
162+
Commands::Login(args) => commands::auth::login(args, ctx).await,
110163
Commands::Whoami => {
111-
commands::auth::whoami(api_key.as_ref());
164+
commands::auth::whoami(api_key.as_ref(), ctx);
112165
Ok(())
113166
}
114-
Commands::Config { command } => commands::config::execute(command),
167+
Commands::Config { command } => commands::config::execute(command, ctx),
115168
_ => {
116169
let api_key = api_key.ok_or_else(|| {
117170
anyhow::anyhow!(
@@ -122,49 +175,47 @@ pub async fn run(cli: Cli) -> anyhow::Result<()> {
122175

123176
match cli.command {
124177
Commands::Agents { command } => {
125-
commands::agents::execute(command, &client, cli.format).await
178+
commands::agents::execute(command, &client, ctx).await
126179
}
127180
Commands::Conversations { command } => {
128-
commands::conversations::execute(command, &client, cli.format).await
129-
}
130-
Commands::Runs { command } => {
131-
commands::runs::execute(command, &client, cli.format).await
181+
commands::conversations::execute(command, &client, ctx).await
132182
}
183+
Commands::Runs { command } => commands::runs::execute(command, &client, ctx).await,
133184
Commands::Simulations { command } => {
134-
commands::simulations::execute(command, &client, cli.format).await
185+
commands::simulations::execute(command, &client, ctx).await
135186
}
136187
Commands::TestSets { command } => {
137-
commands::test_sets::execute(command, &client, cli.format).await
188+
commands::test_sets::execute(command, &client, ctx).await
138189
}
139190
Commands::TestCases { command } => {
140-
commands::test_cases::execute(command, &client, cli.format).await
191+
commands::test_cases::execute(command, &client, ctx).await
141192
}
142193
Commands::Personas { command } => {
143-
commands::personas::execute(command, &client, cli.format).await
194+
commands::personas::execute(command, &client, ctx).await
144195
}
145196
Commands::Metrics { command } => {
146-
commands::metrics::execute(command, &client, cli.format).await
197+
commands::metrics::execute(command, &client, ctx).await
147198
}
148199
Commands::Mutations { command } => {
149-
commands::mutations::execute(command, &client, cli.format).await
200+
commands::mutations::execute(command, &client, ctx).await
150201
}
151202
Commands::ApiKeys { command } => {
152-
commands::api_keys::execute(command, &client, cli.format).await
203+
commands::api_keys::execute(command, &client, ctx).await
153204
}
154205
Commands::RunTemplates { command } => {
155-
commands::run_templates::execute(command, &client, cli.format).await
206+
commands::run_templates::execute(command, &client, ctx).await
156207
}
157208
Commands::ScheduledRuns { command } => {
158-
commands::scheduled_runs::execute(command, &client, cli.format).await
209+
commands::scheduled_runs::execute(command, &client, ctx).await
159210
}
160211
Commands::Dashboards { command } => {
161-
commands::dashboards::execute(command, &client, cli.format).await
212+
commands::dashboards::execute(command, &client, ctx).await
162213
}
163214
Commands::ReviewAnnotations { command } => {
164-
commands::review_annotations::execute(command, &client, cli.format).await
215+
commands::review_annotations::execute(command, &client, ctx).await
165216
}
166217
Commands::ReviewProjects { command } => {
167-
commands::review_projects::execute(command, &client, cli.format).await
218+
commands::review_projects::execute(command, &client, ctx).await
168219
}
169220
_ => unreachable!(),
170221
}

src/client/models/conversation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub struct GetConversationResponse {
117117
pub conversation: Conversation,
118118
}
119119

120-
#[derive(Debug, Deserialize)]
120+
#[derive(Debug, Serialize, Deserialize)]
121121
pub struct ConversationAudioUrlResponse {
122122
pub audio_url: String,
123123
pub conversation_id: String,

src/client/models/simulation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub struct GetSimulationResponse {
8585
pub simulation: Simulation,
8686
}
8787

88-
#[derive(Debug, Deserialize)]
88+
#[derive(Debug, Serialize, Deserialize)]
8989
pub struct AudioUrlResponse {
9090
pub audio_url: String,
9191
pub simulation_id: String,

src/commands/agents.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::{Args, Subcommand};
33

44
use crate::client::models::{AgentType, CreateAgentRequest, ListParams, UpdateAgentRequest};
55
use crate::client::CovalClient;
6-
use crate::output::{print_list, print_one, print_success, OutputFormat};
6+
use crate::output::{emit_list, emit_one, emit_success, OutputContext};
77

88
#[derive(Subcommand)]
99
pub enum AgentCommands {
@@ -14,6 +14,18 @@ pub enum AgentCommands {
1414
Delete(DeleteArgs),
1515
}
1616

17+
impl AgentCommands {
18+
pub fn operation(&self) -> &'static str {
19+
match self {
20+
Self::List(_) => "list",
21+
Self::Get(_) => "get",
22+
Self::Create(_) => "create",
23+
Self::Update(_) => "update",
24+
Self::Delete(_) => "delete",
25+
}
26+
}
27+
}
28+
1729
#[derive(Args)]
1830
pub struct ListArgs {
1931
#[arg(long)]
@@ -87,7 +99,8 @@ pub struct DeleteArgs {
8799
agent_id: String,
88100
}
89101

90-
pub async fn execute(cmd: AgentCommands, client: &CovalClient, format: OutputFormat) -> Result<()> {
102+
pub async fn execute(cmd: AgentCommands, client: &CovalClient, ctx: &OutputContext) -> Result<()> {
103+
let operation = cmd.operation();
91104
match cmd {
92105
AgentCommands::List(args) => {
93106
let params = ListParams {
@@ -97,11 +110,11 @@ pub async fn execute(cmd: AgentCommands, client: &CovalClient, format: OutputFor
97110
..Default::default()
98111
};
99112
let response = client.agents().list(params).await?;
100-
print_list(&response.agents, format);
113+
emit_list(ctx, "agents", operation, &response.agents);
101114
}
102115
AgentCommands::Get(args) => {
103116
let agent = client.agents().get(&args.agent_id).await?;
104-
print_one(&agent, format);
117+
emit_one(ctx, "agents", operation, &agent);
105118
}
106119
AgentCommands::Create(args) => {
107120
let metadata = args
@@ -121,7 +134,7 @@ pub async fn execute(cmd: AgentCommands, client: &CovalClient, format: OutputFor
121134
test_set_ids: args.test_set_ids,
122135
};
123136
let agent = client.agents().create(req).await?;
124-
print_one(&agent, format);
137+
emit_one(ctx, "agents", operation, &agent);
125138
}
126139
AgentCommands::Update(args) => {
127140
let metadata = args
@@ -141,11 +154,11 @@ pub async fn execute(cmd: AgentCommands, client: &CovalClient, format: OutputFor
141154
test_set_ids: args.test_set_ids,
142155
};
143156
let agent = client.agents().update(&args.agent_id, req).await?;
144-
print_one(&agent, format);
157+
emit_one(ctx, "agents", operation, &agent);
145158
}
146159
AgentCommands::Delete(args) => {
147160
client.agents().delete(&args.agent_id).await?;
148-
print_success("Agent deleted.");
161+
emit_success(ctx, "agents", operation, "Agent deleted.");
149162
}
150163
}
151164
Ok(())

src/commands/api_keys.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::client::models::{
66
UpdateApiKeyRequest,
77
};
88
use crate::client::CovalClient;
9-
use crate::output::{print_list, print_one, print_success, OutputFormat};
9+
use crate::output::{
10+
emit_list, emit_one, emit_one_with_warnings, emit_success, AgentWarning, OutputContext,
11+
};
1012

1113
#[derive(Subcommand)]
1214
pub enum ApiKeyCommands {
@@ -16,6 +18,17 @@ pub enum ApiKeyCommands {
1618
Delete(DeleteArgs),
1719
}
1820

21+
impl ApiKeyCommands {
22+
pub fn operation(&self) -> &'static str {
23+
match self {
24+
Self::List(_) => "list",
25+
Self::Create(_) => "create",
26+
Self::Update(_) => "update",
27+
Self::Delete(_) => "delete",
28+
}
29+
}
30+
}
31+
1932
#[derive(Args)]
2033
pub struct ListArgs {
2134
#[arg(long)]
@@ -58,11 +71,8 @@ pub struct DeleteArgs {
5871
api_key_id: String,
5972
}
6073

61-
pub async fn execute(
62-
cmd: ApiKeyCommands,
63-
client: &CovalClient,
64-
format: OutputFormat,
65-
) -> Result<()> {
74+
pub async fn execute(cmd: ApiKeyCommands, client: &CovalClient, ctx: &OutputContext) -> Result<()> {
75+
let operation = cmd.operation();
6676
match cmd {
6777
ApiKeyCommands::List(args) => {
6878
let params = ListParams {
@@ -75,7 +85,7 @@ pub async fn execute(
7585
.api_keys()
7686
.list(params, args.status, args.environment)
7787
.await?;
78-
print_list(&response.api_keys, format);
88+
emit_list(ctx, "api-keys", operation, &response.api_keys);
7989
}
8090
ApiKeyCommands::Create(args) => {
8191
let req = CreateApiKeyRequest {
@@ -87,22 +97,37 @@ pub async fn execute(
8797
};
8898
let api_key = client.api_keys().create(req).await?;
8999
if !api_key.key.is_empty() && !api_key.key.contains("***") {
90-
eprintln!("WARNING: Store this key now. It will not be shown again.");
91-
eprintln!("Key: {}", api_key.key);
100+
if ctx.agent {
101+
emit_one_with_warnings(
102+
ctx,
103+
"api-keys",
104+
operation,
105+
&api_key,
106+
vec![AgentWarning::new(
107+
"store_api_key",
108+
"Store this key now. It will not be shown again.",
109+
)],
110+
);
111+
} else {
112+
eprintln!("WARNING: Store this key now. It will not be shown again.");
113+
eprintln!("Key: {}", api_key.key);
114+
emit_one(ctx, "api-keys", operation, &api_key);
115+
}
116+
} else {
117+
emit_one(ctx, "api-keys", operation, &api_key);
92118
}
93-
print_one(&api_key, format);
94119
}
95120
ApiKeyCommands::Update(args) => {
96121
let req = UpdateApiKeyRequest {
97122
status: args.status,
98123
reason: args.reason,
99124
};
100125
let api_key = client.api_keys().update(&args.api_key_id, req).await?;
101-
print_one(&api_key, format);
126+
emit_one(ctx, "api-keys", operation, &api_key);
102127
}
103128
ApiKeyCommands::Delete(args) => {
104129
client.api_keys().delete(&args.api_key_id).await?;
105-
print_success("API key deleted.");
130+
emit_success(ctx, "api-keys", operation, "API key deleted.");
106131
}
107132
}
108133
Ok(())

0 commit comments

Comments
 (0)