exa-ai 0.11.2 → 0.12.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e774a0ceaa01d08c065102cc910ff17ea3fac78661cfe53ae3c9d0c746df8d7
4
- data.tar.gz: 36130c9406743f8a174861aebac4f506cd389ba1dba37bb5afc31f5dcc97423f
3
+ metadata.gz: aac702bd80757b3b6f52899e116899184df36530b6330c496c405e52a247eb95
4
+ data.tar.gz: 46669a7169fc6f2155b2869b7bf90740d6e28d90855a35e8064707778115b8db
5
5
  SHA512:
6
- metadata.gz: 747230d50dfe2944de0d3f5b0629c35d75a8f9f25e8299955f0ec90ef9c3bbc266313c29aabde6f9dfb797544e07319e66890e216738b118794d4eabde952b3a
7
- data.tar.gz: 177d1a957cd88311ab13430eca68eac9fb39808ee03ce91ce68f9513bbba3f3d54db5f1b08a08d17d80f9aac5ca2759f376d547431670216e47e5f2e66052c2f
6
+ metadata.gz: c6db2aaa317b4e3b4a07c2a09a6af6b41e701961333b4dc3fa46c3c66bd535c529c82b92091a160be1d8ed7c25e7a7bc91933aa29278a4dd809bca92a6d1036d
7
+ data.tar.gz: 49159797c3615649188ab4a16d076b29a3b2134446731d6cf38c6fb59fddb4e49b823cdd07ef7390d030458cc04fabdcb20278581df2120b7c69721dbf6bf548
data/README.md CHANGED
@@ -9,6 +9,7 @@ Ruby client for the Exa.ai API. Search and analyze web content using neural sear
9
9
  - [Configuration](#configuration)
10
10
  - [Quick Start](#quick-start)
11
11
  - [Features](#features)
12
+ - [Agent](#agent)
12
13
  - [Error Handling](#error-handling)
13
14
  - [Documentation](#documentation)
14
15
  - [Development](#development)
@@ -226,6 +227,117 @@ The gem provides complete access to Exa's API endpoints:
226
227
  - **Enrichments** — Create and manage AI-powered data enrichment tasks on websets
227
228
  - **Imports** — Upload CSV files to import external data into websets
228
229
 
230
+ ### Agent
231
+ - **Agent API** — Asynchronous research agent with citations, streaming, and full run lifecycle
232
+
233
+ ## Agent
234
+
235
+ Exa's Agent API runs asynchronous research agents that search the web, synthesize findings, and return structured output with source citations. Runs are long-lived — create one, poll for completion, and stream events as it progresses.
236
+
237
+ ### Ruby API
238
+
239
+ ```ruby
240
+ require 'exa-ai'
241
+
242
+ client = Exa::Client.new(api_key: ENV['EXA_API_KEY'])
243
+
244
+ # Create an async agent run
245
+ run = client.agent_run_create(
246
+ query: "AI infrastructure startups that raised Series A in 2025",
247
+ effort: "high"
248
+ )
249
+ puts run.id # => "run_abc123"
250
+ puts run.status # => "queued"
251
+ puts run.queued? # => true
252
+
253
+ # Poll for the result
254
+ run = client.agent_run_get(run.id)
255
+ if run.completed?
256
+ puts run.output[:text]
257
+ puts run.output[:structured] # already-parsed JSON — no string parsing needed
258
+ puts run.output[:grounding]
259
+ end
260
+
261
+ # Create a run with structured output and a system prompt
262
+ run = client.agent_run_create(
263
+ query: "AI infrastructure startups that raised Series A in 2025",
264
+ system_prompt: "Return only companies headquartered in the US.",
265
+ effort: "high",
266
+ output_schema: {
267
+ type: "object",
268
+ properties: {
269
+ companies: { type: "array", items: { type: "string" } }
270
+ }
271
+ }
272
+ )
273
+
274
+ # Attach premium data partners (Exa Connect) alongside web search.
275
+ # The agent queries each partner where it's strongest and blends the
276
+ # results into one grounded, structured answer.
277
+ run = client.agent_run_create(
278
+ query: "Profile Anthropic: total funding and estimated monthly web traffic",
279
+ data_sources: [{ provider: "fiber_ai" }, { provider: "similarweb" }],
280
+ output_schema: {
281
+ type: "object",
282
+ properties: {
283
+ name: { type: "string" },
284
+ totalFunding: { type: "string" }, # from Fiber.ai
285
+ monthlyVisits: { type: "number" } # from Similarweb
286
+ }
287
+ }
288
+ )
289
+
290
+ # Stream events as they arrive
291
+ client.agent_run_stream(query: "AI infrastructure startups that raised Series A in 2025") do |event, data|
292
+ puts "#{event}: #{data.inspect}"
293
+ # event is the SSE event-type string, e.g. "agent_run.completed"
294
+ # data is the parsed JSON hash
295
+ end
296
+
297
+ # List recent runs
298
+ runs = client.agent_run_list(limit: 5)
299
+ puts runs.has_more # => true/false
300
+ puts runs.next_cursor # => pagination cursor
301
+ runs.data.each { |r| puts "#{r.id}: #{r.status}" }
302
+
303
+ # Fetch a run's events
304
+ events = client.agent_run_events(run.id)
305
+
306
+ # Cancel or delete a run
307
+ client.agent_run_cancel(run.id)
308
+ client.agent_run_delete(run.id)
309
+ ```
310
+
311
+ > **Note:** Multi-word keys inside `input`, `data_sources` items, and `metadata` are passed through verbatim — supply them in the exact shape the API expects, the same contract as `output_schema`.
312
+
313
+ ### Command Line
314
+
315
+ ```bash
316
+ # Create a run and wait for it to finish
317
+ exa-ai agent-run-create --query "AI infrastructure startups that raised Series A in 2025" --wait --output-format pretty
318
+
319
+ # Attach premium data partners (Exa Connect) with a structured schema.
320
+ # Run `exa-ai agent-run-create --help` to see every provider and when to use it:
321
+ # fiber_ai, similarweb, baselayer, affiliate, particle_news, financial_datasets, jinko
322
+ exa-ai agent-run-create --wait \
323
+ --query "Profile Anthropic: total funding and monthly web traffic" \
324
+ --data-sources fiber_ai,similarweb \
325
+ --output-schema '{"type":"object","properties":{"name":{"type":"string"},"totalFunding":{"type":"string"},"monthlyVisits":{"type":"number"}}}'
326
+
327
+ # Fetch an existing run
328
+ exa-ai agent-run-get <run_id>
329
+
330
+ # List recent runs
331
+ exa-ai agent-run-list --limit 5
332
+
333
+ # Cancel or delete a run
334
+ exa-ai agent-run-cancel <run_id>
335
+ exa-ai agent-run-delete <run_id>
336
+
337
+ # Fetch a run's events
338
+ exa-ai agent-run-events <run_id>
339
+ ```
340
+
229
341
  ## Error Handling
230
342
 
231
343
  ```ruby
data/exe/exa-ai CHANGED
@@ -44,7 +44,13 @@ module ExaCLI
44
44
  "monitor-update" => "Update a monitor",
45
45
  "monitor-delete" => "Delete a monitor",
46
46
  "monitor-runs-list" => "List monitor runs",
47
- "monitor-runs-get" => "Get monitor run details"
47
+ "monitor-runs-get" => "Get monitor run details",
48
+ "agent-run-create" => "Create an agent run",
49
+ "agent-run-get" => "Get agent run details",
50
+ "agent-run-list" => "List agent runs",
51
+ "agent-run-cancel" => "Cancel an agent run",
52
+ "agent-run-delete" => "Delete an agent run",
53
+ "agent-run-events" => "Get events for an agent run"
48
54
  }.freeze
49
55
 
50
56
  def self.run
@@ -134,6 +140,18 @@ module ExaCLI
134
140
  exec File.expand_path("../exa-ai-monitor-runs-list", __FILE__), *ARGV[1..]
135
141
  when "monitor-runs-get"
136
142
  exec File.expand_path("../exa-ai-monitor-runs-get", __FILE__), *ARGV[1..]
143
+ when "agent-run-create"
144
+ exec File.expand_path("../exa-ai-agent-run-create", __FILE__), *ARGV[1..]
145
+ when "agent-run-get"
146
+ exec File.expand_path("../exa-ai-agent-run-get", __FILE__), *ARGV[1..]
147
+ when "agent-run-list"
148
+ exec File.expand_path("../exa-ai-agent-run-list", __FILE__), *ARGV[1..]
149
+ when "agent-run-cancel"
150
+ exec File.expand_path("../exa-ai-agent-run-cancel", __FILE__), *ARGV[1..]
151
+ when "agent-run-delete"
152
+ exec File.expand_path("../exa-ai-agent-run-delete", __FILE__), *ARGV[1..]
153
+ when "agent-run-events"
154
+ exec File.expand_path("../exa-ai-agent-run-events", __FILE__), *ARGV[1..]
137
155
  else
138
156
  print_error_for_command(ARGV[0])
139
157
  exit 1
@@ -198,6 +216,15 @@ module ExaCLI
198
216
  ["import-delete", "Delete an import"]
199
217
  ])
200
218
 
219
+ print_command_section("Agent Runs", [
220
+ ["agent-run-create", "Create an agent run"],
221
+ ["agent-run-get", "Get agent run details"],
222
+ ["agent-run-list", "List agent runs"],
223
+ ["agent-run-cancel", "Cancel an agent run"],
224
+ ["agent-run-delete", "Delete an agent run"],
225
+ ["agent-run-events", "Get events for an agent run"]
226
+ ])
227
+
201
228
  print_command_section("Webset Monitors", [
202
229
  ["monitor-create", "Create a webset monitor"],
203
230
  ["monitor-get", "Get monitor details"],
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse command-line arguments
7
+ api_key = nil
8
+ run_id = nil
9
+ output_format = "json"
10
+
11
+ args = ARGV.dup
12
+ while args.any?
13
+ arg = args.shift
14
+ case arg
15
+ when "--api-key"
16
+ api_key = args.shift
17
+ when "--output-format"
18
+ output_format = args.shift
19
+ when "--help", "-h"
20
+ puts <<~HELP
21
+ Usage: exa-ai agent-run-cancel <run_id> [options]
22
+
23
+ Cancel an in-progress agent run.
24
+
25
+ Arguments:
26
+ run_id ID of the agent run to cancel
27
+
28
+ Options:
29
+ --api-key KEY Exa API key (or use EXA_API_KEY env var)
30
+ --output-format FORMAT Output format: json, pretty, or text (default: json)
31
+ --help, -h Show this help message
32
+
33
+ Examples:
34
+ exa-ai agent-run-cancel run_123
35
+ exa-ai agent-run-cancel run_123 --output-format pretty
36
+ HELP
37
+ exit 0
38
+ else
39
+ # First positional argument is the run_id
40
+ if run_id.nil?
41
+ run_id = arg
42
+ else
43
+ warn "Unknown option: #{arg}"
44
+ exit 1
45
+ end
46
+ end
47
+ end
48
+
49
+ # Validate required arguments
50
+ if run_id.nil?
51
+ warn "Error: Run ID argument required"
52
+ warn "Usage: exa-ai agent-run-cancel <run_id> [options]"
53
+ warn "Try 'exa-ai agent-run-cancel --help' for more information"
54
+ exit 1
55
+ end
56
+
57
+ begin
58
+ # Resolve API key
59
+ api_key = Exa::CLI::Base.resolve_api_key(api_key)
60
+
61
+ # Build client
62
+ client = Exa::CLI::Base.build_client(api_key)
63
+
64
+ # Call API
65
+ result = client.agent_run_cancel(run_id)
66
+
67
+ # Format output
68
+ formatted = Exa::CLI::Formatters::AgentRunFormatter.format_run(result, output_format)
69
+ puts formatted
70
+
71
+ rescue Exa::NotFound => e
72
+ warn "Agent run not found: #{e.message}"
73
+ exit 1
74
+ rescue Exa::Unauthorized => e
75
+ warn "Authentication failed: #{e.message}"
76
+ warn "Please provide a valid API key via --api-key or EXA_API_KEY environment variable"
77
+ exit 1
78
+ rescue Exa::ClientError => e
79
+ warn "Client error: #{e.message}"
80
+ exit 1
81
+ rescue Exa::ServerError => e
82
+ warn "Server error: #{e.message}"
83
+ exit 1
84
+ rescue Exa::Error => e
85
+ warn "Error: #{e.message}"
86
+ exit 1
87
+ rescue StandardError => e
88
+ warn "Unexpected error: #{e.message}"
89
+ exit 1
90
+ end
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse a JSON flag value: inline JSON, or @path to read JSON from a file.
7
+ def parse_json_arg(value, flag_name)
8
+ json = value.to_s.start_with?("@") ? File.read(value.to_s[1..]) : value.to_s
9
+ JSON.parse(json)
10
+ rescue JSON::ParserError => e
11
+ $stderr.puts "Error: #{flag_name} is not valid JSON (#{e.message})"
12
+ exit 1
13
+ rescue Errno::ENOENT
14
+ $stderr.puts "Error: #{flag_name} file not found: #{value.to_s[1..]}"
15
+ exit 1
16
+ end
17
+
18
+ # Parse command-line arguments
19
+ def parse_args(argv)
20
+ args = {
21
+ output_format: "json",
22
+ api_key: nil,
23
+ wait: false
24
+ }
25
+
26
+ i = 0
27
+ while i < argv.length
28
+ arg = argv[i]
29
+ case arg
30
+ when "--query"
31
+ args[:query] = argv[i + 1]
32
+ i += 2
33
+ when "--system-prompt"
34
+ args[:system_prompt] = argv[i + 1]
35
+ i += 2
36
+ when "--effort"
37
+ args[:effort] = argv[i + 1]
38
+ i += 2
39
+ when "--data-sources"
40
+ # ponytail: comma-separated slugs cover all documented usage ({provider: slug}).
41
+ # For richer per-provider config, use the Ruby API (client.agent_run_create).
42
+ args[:data_sources] = argv[i + 1].to_s.split(",").map { |s| { provider: s.strip } }
43
+ i += 2
44
+ when "--input-data"
45
+ args[:input_data] = parse_json_arg(argv[i + 1], "--input-data")
46
+ i += 2
47
+ when "--input-exclusion"
48
+ args[:input_exclusion] = parse_json_arg(argv[i + 1], "--input-exclusion")
49
+ i += 2
50
+ when "--previous-run-id"
51
+ args[:previous_run_id] = argv[i + 1]
52
+ i += 2
53
+ when "--output-schema"
54
+ args[:output_schema] = parse_json_arg(argv[i + 1], "--output-schema")
55
+ i += 2
56
+ when "--output-format"
57
+ args[:output_format] = argv[i + 1]
58
+ i += 2
59
+ when "--wait"
60
+ args[:wait] = true
61
+ i += 1
62
+ when "--api-key"
63
+ args[:api_key] = argv[i + 1]
64
+ i += 2
65
+ when "--help", "-h"
66
+ puts <<~HELP
67
+ Usage: exa-ai agent-run-create --query "TEXT" [OPTIONS]
68
+
69
+ Create an agent run using Exa AI. Optionally attach premium data
70
+ partners (Exa Connect) so the agent queries their databases alongside
71
+ web search and blends everything into one grounded answer.
72
+
73
+ Options:
74
+ --query TEXT Query for the agent run (required)
75
+ --system-prompt TEXT System prompt to guide the agent
76
+ --effort LEVEL Effort level: low, medium, high
77
+ --data-sources LIST Comma-separated partner slugs to attach (see
78
+ "Data sources" below), e.g. fiber_ai,similarweb
79
+ --input-data JSON JSON array of objects for the agent to process,
80
+ or @file.json to read from a file. Each element
81
+ is a record the agent enriches or acts on.
82
+ --input-exclusion JSON JSON array of objects to exclude from results,
83
+ or @file.json. Useful to skip already-known rows.
84
+ --previous-run-id ID Continue from a completed run. The agent picks
85
+ up where the prior run left off.
86
+ --output-schema JSON JSON schema for structured output, or @file.json
87
+ to read it from a file. Recommended alongside
88
+ --data-sources so the agent knows which fields
89
+ each partner should fill.
90
+ --wait Stream the run and wait for it to complete
91
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
92
+ --output-format FMT Output format: json, pretty, or text (default: json)
93
+ --help, -h Show this help message
94
+
95
+ Data sources (Exa Connect):
96
+ Each attached partner is billed per call on top of normal agent costs.
97
+ The agent picks the right source per step and cites everything.
98
+
99
+ fiber_ai GTM & recruiting. 40M+ companies, 850M+ people,
100
+ work/personal emails and phones. Use for B2B
101
+ prospecting, CRM enrichment, finding contacts.
102
+ similarweb Web analytics. Traffic estimates, global rankings,
103
+ and competitors for any domain. Use for traffic
104
+ benchmarking and market sizing.
105
+ baselayer Compliance & KYB. Verify US businesses: officers,
106
+ state registrations, risk scores. Use for KYB and
107
+ vendor/customer screening.
108
+ affiliate Commerce. Product catalog with live pricing,
109
+ brands, and merchant links. Use for price
110
+ comparison and shopping assistants.
111
+ particle_news Media intelligence. Podcast transcripts with
112
+ speaker attribution and timestamps. Use to find
113
+ who said what on podcasts.
114
+ financial_datasets Finance. Ticker-based news for US public
115
+ companies. Use to track news/earnings for stocks.
116
+ jinko Travel. Destinations ranked by lowest available
117
+ fare from your departure airports. Use for
118
+ budget trip ideas.
119
+
120
+ Examples:
121
+ exa-ai agent-run-create --query "Find Ruby performance tips"
122
+ exa-ai agent-run-create --query "Analyze AI trends" --wait
123
+
124
+ # Enrich a list of companies from a JSON file:
125
+ exa-ai agent-run-create --wait \\
126
+ --query "For each company, find the CEO and their LinkedIn URL." \\
127
+ --input-data @companies.json \\
128
+ --output-schema '{"type":"object","properties":{"companies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ceo":{"type":"string"},"linkedin":{"type":"string"}}}}}}'
129
+
130
+ # Company profile blending two partners + web, as structured JSON:
131
+ exa-ai agent-run-create --wait \\
132
+ --query "Profile Anthropic: total funding and monthly web traffic" \\
133
+ --data-sources fiber_ai,similarweb \\
134
+ --output-schema '{"type":"object","properties":{"name":{"type":"string"},"totalFunding":{"type":"string"},"monthlyVisits":{"type":"number"}}}'
135
+
136
+ # KYB check with a larger schema read from a file:
137
+ exa-ai agent-run-create --wait --query "Verify Stripe, Inc." \\
138
+ --data-sources baselayer --output-schema @schema.json
139
+ HELP
140
+ exit 0
141
+ else
142
+ i += 1
143
+ end
144
+ end
145
+
146
+ args
147
+ end
148
+
149
+ # Main execution
150
+ begin
151
+ args = parse_args(ARGV)
152
+
153
+ # Validate query
154
+ if args[:query].nil? || args[:query].empty?
155
+ $stderr.puts "Error: --query flag is required"
156
+ $stderr.puts "Run 'exa-ai agent-run-create --help' for usage information"
157
+ exit 1
158
+ end
159
+
160
+ # Resolve API key
161
+ api_key = Exa::CLI::Base.resolve_api_key(args[:api_key])
162
+
163
+ # Resolve output format
164
+ output_format = Exa::CLI::Base.resolve_output_format(args[:output_format])
165
+
166
+ # Build client
167
+ client = Exa::CLI::Base.build_client(api_key)
168
+
169
+ # Prepare run parameters
170
+ run_params = { query: args[:query] }
171
+ run_params[:system_prompt] = args[:system_prompt] if args[:system_prompt]
172
+ run_params[:effort] = args[:effort] if args[:effort]
173
+ run_params[:data_sources] = args[:data_sources] if args[:data_sources]
174
+ if args[:input_data] || args[:input_exclusion]
175
+ input = {}
176
+ input[:data] = args[:input_data] if args[:input_data]
177
+ input[:exclusion] = args[:input_exclusion] if args[:input_exclusion]
178
+ run_params[:input] = input
179
+ end
180
+ run_params[:previous_run_id] = args[:previous_run_id] if args[:previous_run_id]
181
+ run_params[:output_schema] = args[:output_schema] if args[:output_schema]
182
+
183
+ if args[:wait]
184
+ # Stream the run and watch it to completion. The SSE stream creates the run
185
+ # and pushes lifecycle events (created/started/completed); the final run is
186
+ # rendered the instant it lands — lower latency than polling, and the run
187
+ # still persists with an id.
188
+ start = Time.now
189
+ run_id = nil
190
+ phase = "starting"
191
+ done = false
192
+
193
+ # ponytail: the SSE block only fires on events, and nothing arrives between
194
+ # "started" and "completed". A background thread paints a live elapsed timer
195
+ # so the user can see it's alive during the silent running phase.
196
+ painter = Thread.new do
197
+ frames = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏]
198
+ i = 0
199
+ until done
200
+ elapsed = (Time.now - start).round
201
+ $stderr.print "\r#{frames[i % frames.size]} #{run_id || "agent run"} #{phase} #{elapsed}s "
202
+ $stderr.flush
203
+ i += 1
204
+ sleep 0.2
205
+ end
206
+ end
207
+
208
+ final = nil
209
+ stream_error = nil
210
+ begin
211
+ final = client.agent_run_stream(**run_params) do |event, data|
212
+ run_id ||= data["id"] if data.is_a?(Hash)
213
+ phase = "queued" if event == "agent_run.created"
214
+ phase = "running" if event == "agent_run.started"
215
+ end
216
+ rescue Faraday::Error, Exa::Error => e
217
+ stream_error = e # stream dropped mid-run; recover via polling below if we have an id
218
+ ensure
219
+ done = true
220
+ painter.join
221
+ end
222
+
223
+ # Fallback: stream ended/dropped without a terminal event. If we learned the
224
+ # run id, poll it to completion rather than losing the run.
225
+ if final.nil? && run_id
226
+ $stderr.print "\r(stream interrupted; polling #{run_id}) "
227
+ begin
228
+ final = Exa::CLI::Polling.poll(max_duration: 300, initial_delay: 2, max_delay: 10) do
229
+ current = client.agent_run_get(run_id)
230
+ { done: current.finished?, result: current, status: current.status }
231
+ end
232
+ rescue Exa::CLI::Polling::TimeoutError
233
+ $stderr.puts "\nError: run did not complete within 5 minutes"
234
+ $stderr.puts "Run ID: #{run_id} — check later with: exa-ai agent-run-get #{run_id}"
235
+ exit 1
236
+ end
237
+ end
238
+
239
+ $stderr.print "\r" + (" " * 72) + "\r"
240
+
241
+ if final.nil?
242
+ $stderr.puts "Error: the run did not produce a result#{stream_error ? " (#{stream_error.message})" : ""}"
243
+ $stderr.puts "Run ID: #{run_id}" if run_id
244
+ exit 1
245
+ end
246
+
247
+ $stderr.puts "#{final.completed? ? "✓" : "•"} #{final.status} · #{(Time.now - start).round}s"
248
+ puts Exa::CLI::Formatters::AgentRunFormatter.format_run(final, output_format)
249
+ $stdout.flush
250
+ exit 1 if final.failed?
251
+ else
252
+ # Not waiting: create and return the initial (queued) run.
253
+ run = client.agent_run_create(**run_params)
254
+ puts Exa::CLI::Formatters::AgentRunFormatter.format_run(run, output_format)
255
+ end
256
+
257
+ rescue Exa::ConfigurationError => e
258
+ $stderr.puts "Configuration error: #{e.message}"
259
+ exit 1
260
+ rescue Exa::Unauthorized => e
261
+ $stderr.puts "Authentication error: #{e.message}"
262
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
263
+ exit 1
264
+ rescue Exa::ClientError => e
265
+ $stderr.puts "Client error: #{e.message}"
266
+ exit 1
267
+ rescue Exa::ServerError => e
268
+ $stderr.puts "Server error: #{e.message}"
269
+ $stderr.puts "The Exa API may be experiencing issues. Please try again later."
270
+ exit 1
271
+ rescue Exa::Error => e
272
+ $stderr.puts "Error: #{e.message}"
273
+ exit 1
274
+ rescue StandardError => e
275
+ $stderr.puts "Unexpected error: #{e.message}"
276
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
277
+ exit 1
278
+ end
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse command-line arguments
7
+ api_key = nil
8
+ run_id = nil
9
+ output_format = "json"
10
+
11
+ args = ARGV.dup
12
+ while args.any?
13
+ arg = args.shift
14
+ case arg
15
+ when "--api-key"
16
+ api_key = args.shift
17
+ when "--output-format"
18
+ output_format = args.shift
19
+ when "--help", "-h"
20
+ puts <<~HELP
21
+ Usage: exa-ai agent-run-delete <run_id> [options]
22
+
23
+ Delete an agent run.
24
+
25
+ Arguments:
26
+ run_id ID of the agent run to delete
27
+
28
+ Options:
29
+ --api-key KEY Exa API key (or use EXA_API_KEY env var)
30
+ --output-format FORMAT Output format: json, pretty, or text (default: json)
31
+ --help, -h Show this help message
32
+
33
+ Examples:
34
+ exa-ai agent-run-delete run_123
35
+ exa-ai agent-run-delete run_123 --output-format pretty
36
+ HELP
37
+ exit 0
38
+ else
39
+ # First positional argument is the run_id
40
+ if run_id.nil?
41
+ run_id = arg
42
+ else
43
+ warn "Unknown option: #{arg}"
44
+ exit 1
45
+ end
46
+ end
47
+ end
48
+
49
+ # Validate required arguments
50
+ if run_id.nil?
51
+ warn "Error: Run ID argument required"
52
+ warn "Usage: exa-ai agent-run-delete <run_id> [options]"
53
+ warn "Try 'exa-ai agent-run-delete --help' for more information"
54
+ exit 1
55
+ end
56
+
57
+ begin
58
+ # Resolve API key
59
+ api_key = Exa::CLI::Base.resolve_api_key(api_key)
60
+
61
+ # Build client
62
+ client = Exa::CLI::Base.build_client(api_key)
63
+
64
+ # Call API
65
+ result = client.agent_run_delete(run_id)
66
+
67
+ # Format output
68
+ if result.respond_to?(:to_h)
69
+ formatted = Exa::CLI::Formatters::AgentRunFormatter.format_run(result, output_format)
70
+ puts formatted
71
+ else
72
+ puts({ deleted: true, id: run_id }.to_json) if output_format == "json"
73
+ puts "Deleted agent run: #{run_id}" if output_format == "pretty"
74
+ puts run_id if output_format == "text"
75
+ end
76
+
77
+ rescue Exa::NotFound => e
78
+ warn "Agent run not found: #{e.message}"
79
+ exit 1
80
+ rescue Exa::Unauthorized => e
81
+ warn "Authentication failed: #{e.message}"
82
+ warn "Please provide a valid API key via --api-key or EXA_API_KEY environment variable"
83
+ exit 1
84
+ rescue Exa::ClientError => e
85
+ warn "Client error: #{e.message}"
86
+ exit 1
87
+ rescue Exa::ServerError => e
88
+ warn "Server error: #{e.message}"
89
+ exit 1
90
+ rescue Exa::Error => e
91
+ warn "Error: #{e.message}"
92
+ exit 1
93
+ rescue StandardError => e
94
+ warn "Unexpected error: #{e.message}"
95
+ exit 1
96
+ end