exa-ai 0.3.0 → 0.4.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -591
  3. data/exe/exa-ai +112 -9
  4. data/exe/exa-ai-answer +1 -5
  5. data/exe/exa-ai-context +1 -4
  6. data/exe/exa-ai-enrichment-cancel +107 -0
  7. data/exe/exa-ai-enrichment-create +235 -0
  8. data/exe/exa-ai-enrichment-delete +121 -0
  9. data/exe/exa-ai-enrichment-get +103 -0
  10. data/exe/exa-ai-enrichment-list +98 -0
  11. data/exe/exa-ai-enrichment-update +170 -0
  12. data/exe/exa-ai-find-similar +240 -0
  13. data/exe/exa-ai-get-contents +1 -4
  14. data/exe/exa-ai-research-get +1 -2
  15. data/exe/exa-ai-research-list +1 -2
  16. data/exe/exa-ai-research-start +1 -3
  17. data/exe/exa-ai-search +1 -3
  18. data/exe/exa-ai-webset-cancel +96 -0
  19. data/exe/exa-ai-webset-create +192 -0
  20. data/exe/exa-ai-webset-delete +110 -0
  21. data/exe/exa-ai-webset-get +92 -0
  22. data/exe/exa-ai-webset-item-delete +111 -0
  23. data/exe/exa-ai-webset-item-get +104 -0
  24. data/exe/exa-ai-webset-item-list +93 -0
  25. data/exe/exa-ai-webset-list +90 -0
  26. data/exe/exa-ai-webset-search-cancel +103 -0
  27. data/exe/exa-ai-webset-search-create +233 -0
  28. data/exe/exa-ai-webset-search-get +104 -0
  29. data/exe/exa-ai-webset-update +139 -0
  30. data/lib/exa/cli/base.rb +3 -3
  31. data/lib/exa/cli/formatters/enrichment_formatter.rb +69 -0
  32. data/lib/exa/cli/formatters/webset_formatter.rb +68 -0
  33. data/lib/exa/cli/formatters/webset_item_formatter.rb +69 -0
  34. data/lib/exa/client.rb +172 -0
  35. data/lib/exa/connection.rb +8 -1
  36. data/lib/exa/resources/webset.rb +74 -0
  37. data/lib/exa/resources/webset_collection.rb +33 -0
  38. data/lib/exa/resources/webset_enrichment.rb +71 -0
  39. data/lib/exa/resources/webset_enrichment_collection.rb +28 -0
  40. data/lib/exa/resources/webset_search.rb +112 -0
  41. data/lib/exa/services/parameter_converter.rb +1 -0
  42. data/lib/exa/services/websets/cancel.rb +36 -0
  43. data/lib/exa/services/websets/cancel_enrichment.rb +35 -0
  44. data/lib/exa/services/websets/cancel_search.rb +44 -0
  45. data/lib/exa/services/websets/create.rb +45 -0
  46. data/lib/exa/services/websets/create_enrichment.rb +35 -0
  47. data/lib/exa/services/websets/create_search.rb +48 -0
  48. data/lib/exa/services/websets/create_search_validator.rb +128 -0
  49. data/lib/exa/services/websets/create_validator.rb +189 -0
  50. data/lib/exa/services/websets/delete.rb +36 -0
  51. data/lib/exa/services/websets/delete_enrichment.rb +35 -0
  52. data/lib/exa/services/websets/delete_item.rb +20 -0
  53. data/lib/exa/services/websets/get_item.rb +20 -0
  54. data/lib/exa/services/websets/get_search.rb +43 -0
  55. data/lib/exa/services/websets/list.rb +25 -0
  56. data/lib/exa/services/websets/list_items.rb +20 -0
  57. data/lib/exa/services/websets/retrieve.rb +47 -0
  58. data/lib/exa/services/websets/retrieve_enrichment.rb +35 -0
  59. data/lib/exa/services/websets/update.rb +37 -0
  60. data/lib/exa/services/websets/update_enrichment.rb +36 -0
  61. data/lib/exa/services/websets_parameter_converter.rb +45 -0
  62. data/lib/exa/version.rb +1 -1
  63. data/lib/exa-ai.rb +5 -0
  64. data/lib/exa.rb +26 -0
  65. metadata +65 -3
data/exe/exa-ai CHANGED
@@ -1,21 +1,38 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require "exa"
6
- require "exa/cli/base"
4
+ require "exa-ai"
7
5
 
8
6
  # Global CLI interface for Exa API
9
7
 
10
8
  module ExaCLI
11
9
  AVAILABLE_COMMANDS = {
12
10
  "search" => "Search the web",
11
+ "find-similar" => "Find content similar to a URL",
13
12
  "answer" => "Generate an answer to a question",
14
13
  "context" => "Get code context from repositories",
15
14
  "get-contents" => "Retrieve page contents",
16
15
  "research-start" => "Start a research task",
17
16
  "research-get" => "Get research task status",
18
- "research-list" => "List research tasks"
17
+ "research-list" => "List research tasks",
18
+ "webset-create" => "Create a new webset",
19
+ "webset-get" => "Get webset details",
20
+ "webset-list" => "List websets",
21
+ "webset-update" => "Update a webset",
22
+ "webset-delete" => "Delete a webset",
23
+ "webset-cancel" => "Cancel a webset",
24
+ "webset-search-create" => "Create a search in a webset",
25
+ "webset-search-get" => "Get webset search details",
26
+ "webset-search-cancel" => "Cancel a webset search",
27
+ "webset-item-list" => "List items in a webset",
28
+ "webset-item-get" => "Get webset item details",
29
+ "webset-item-delete" => "Delete a webset item",
30
+ "enrichment-create" => "Create an enrichment",
31
+ "enrichment-get" => "Get enrichment details",
32
+ "enrichment-list" => "List enrichments",
33
+ "enrichment-update" => "Update an enrichment",
34
+ "enrichment-delete" => "Delete an enrichment",
35
+ "enrichment-cancel" => "Cancel an enrichment"
19
36
  }.freeze
20
37
 
21
38
  def self.run
@@ -31,6 +48,8 @@ module ExaCLI
31
48
  exit 1
32
49
  when "search"
33
50
  exec File.expand_path("../exa-ai-search", __FILE__), *ARGV[1..]
51
+ when "find-similar"
52
+ exec File.expand_path("../exa-ai-find-similar", __FILE__), *ARGV[1..]
34
53
  when "answer"
35
54
  exec File.expand_path("../exa-ai-answer", __FILE__), *ARGV[1..]
36
55
  when "context"
@@ -43,6 +62,42 @@ module ExaCLI
43
62
  exec File.expand_path("../exa-ai-research-get", __FILE__), *ARGV[1..]
44
63
  when "research-list"
45
64
  exec File.expand_path("../exa-ai-research-list", __FILE__), *ARGV[1..]
65
+ when "webset-create"
66
+ exec File.expand_path("../exa-ai-webset-create", __FILE__), *ARGV[1..]
67
+ when "webset-get"
68
+ exec File.expand_path("../exa-ai-webset-get", __FILE__), *ARGV[1..]
69
+ when "webset-list"
70
+ exec File.expand_path("../exa-ai-webset-list", __FILE__), *ARGV[1..]
71
+ when "webset-update"
72
+ exec File.expand_path("../exa-ai-webset-update", __FILE__), *ARGV[1..]
73
+ when "webset-delete"
74
+ exec File.expand_path("../exa-ai-webset-delete", __FILE__), *ARGV[1..]
75
+ when "webset-cancel"
76
+ exec File.expand_path("../exa-ai-webset-cancel", __FILE__), *ARGV[1..]
77
+ when "webset-search-create"
78
+ exec File.expand_path("../exa-ai-webset-search-create", __FILE__), *ARGV[1..]
79
+ when "webset-search-get"
80
+ exec File.expand_path("../exa-ai-webset-search-get", __FILE__), *ARGV[1..]
81
+ when "webset-search-cancel"
82
+ exec File.expand_path("../exa-ai-webset-search-cancel", __FILE__), *ARGV[1..]
83
+ when "webset-item-list"
84
+ exec File.expand_path("../exa-ai-webset-item-list", __FILE__), *ARGV[1..]
85
+ when "webset-item-get"
86
+ exec File.expand_path("../exa-ai-webset-item-get", __FILE__), *ARGV[1..]
87
+ when "webset-item-delete"
88
+ exec File.expand_path("../exa-ai-webset-item-delete", __FILE__), *ARGV[1..]
89
+ when "enrichment-create"
90
+ exec File.expand_path("../exa-ai-enrichment-create", __FILE__), *ARGV[1..]
91
+ when "enrichment-get"
92
+ exec File.expand_path("../exa-ai-enrichment-get", __FILE__), *ARGV[1..]
93
+ when "enrichment-list"
94
+ exec File.expand_path("../exa-ai-enrichment-list", __FILE__), *ARGV[1..]
95
+ when "enrichment-update"
96
+ exec File.expand_path("../exa-ai-enrichment-update", __FILE__), *ARGV[1..]
97
+ when "enrichment-delete"
98
+ exec File.expand_path("../exa-ai-enrichment-delete", __FILE__), *ARGV[1..]
99
+ when "enrichment-cancel"
100
+ exec File.expand_path("../exa-ai-enrichment-cancel", __FILE__), *ARGV[1..]
46
101
  else
47
102
  print_error_for_command(ARGV[0])
48
103
  exit 1
@@ -54,11 +109,51 @@ module ExaCLI
54
109
  puts ""
55
110
  puts "Usage: exa-ai <command> [options]"
56
111
  puts ""
57
- puts "Commands:"
58
- AVAILABLE_COMMANDS.each do |cmd, desc|
59
- puts " #{cmd.ljust(20)} #{desc}"
60
- end
61
- puts ""
112
+
113
+ print_command_section("Core Search", [
114
+ ["search", "Search the web"],
115
+ ["find-similar", "Find content similar to a URL"],
116
+ ["answer", "Generate an answer to a question"],
117
+ ["context", "Get code context from repositories"],
118
+ ["get-contents", "Retrieve page contents"]
119
+ ])
120
+
121
+ print_command_section("Research", [
122
+ ["research-start", "Start a research task"],
123
+ ["research-get", "Get research task status"],
124
+ ["research-list", "List research tasks"]
125
+ ])
126
+
127
+ print_command_section("Websets", [
128
+ ["webset-create", "Create a new webset"],
129
+ ["webset-get", "Get webset details"],
130
+ ["webset-list", "List websets"],
131
+ ["webset-update", "Update a webset"],
132
+ ["webset-delete", "Delete a webset"],
133
+ ["webset-cancel", "Cancel a webset"]
134
+ ])
135
+
136
+ print_command_section("Webset Searches", [
137
+ ["webset-search-create", "Create a search in a webset"],
138
+ ["webset-search-get", "Get webset search details"],
139
+ ["webset-search-cancel", "Cancel a webset search"]
140
+ ])
141
+
142
+ print_command_section("Webset Items", [
143
+ ["webset-item-list", "List items in a webset"],
144
+ ["webset-item-get", "Get webset item details"],
145
+ ["webset-item-delete", "Delete a webset item"]
146
+ ])
147
+
148
+ print_command_section("Webset Enrichments", [
149
+ ["enrichment-create", "Create an enrichment"],
150
+ ["enrichment-get", "Get enrichment details"],
151
+ ["enrichment-list", "List enrichments"],
152
+ ["enrichment-update", "Update an enrichment"],
153
+ ["enrichment-delete", "Delete an enrichment"],
154
+ ["enrichment-cancel", "Cancel an enrichment"]
155
+ ])
156
+
62
157
  puts "Global Options:"
63
158
  puts " --help, -h Show this help message"
64
159
  puts " --version Show version number"
@@ -69,6 +164,14 @@ module ExaCLI
69
164
  puts " exa-ai research-start --instructions 'Find AI papers'"
70
165
  end
71
166
 
167
+ def self.print_command_section(title, commands)
168
+ puts "#{title}:"
169
+ commands.each do |cmd, desc|
170
+ puts " #{cmd.ljust(24)} #{desc}"
171
+ end
172
+ puts ""
173
+ end
174
+
72
175
  def self.print_error_for_command(cmd)
73
176
  return print_help if cmd.nil?
74
177
 
data/exe/exa-ai-answer CHANGED
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Set up load paths
5
- require "bundler/setup"
6
- require "exa"
7
- require "exa/cli/base"
8
- require "exa/cli/formatters/answer_formatter"
4
+ require "exa-ai"
9
5
 
10
6
  # Parse command-line arguments
11
7
  def parse_args(argv)
data/exe/exa-ai-context CHANGED
@@ -1,10 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require "exa"
6
- require "exa/cli/base"
7
- require "exa/cli/formatters/context_formatter"
4
+ require "exa-ai"
8
5
 
9
6
  # Parse command line arguments
10
7
  def parse_args(args)
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse arguments
7
+ webset_id = nil
8
+ enrichment_id = nil
9
+ api_key = nil
10
+ output_format = "json"
11
+
12
+ args = ARGV.dup
13
+ while args.any?
14
+ arg = args.shift
15
+ case arg
16
+ when "--api-key"
17
+ api_key = args.shift
18
+ when "--output-format"
19
+ output_format = args.shift
20
+ when "--help", "-h"
21
+ puts <<~HELP
22
+ Usage: exa-ai enrichment-cancel <webset_id> <enrichment_id> [OPTIONS]
23
+
24
+ Cancel a running enrichment
25
+
26
+ Arguments:
27
+ webset_id ID of the webset (required)
28
+ enrichment_id ID of the enrichment to cancel (required)
29
+
30
+ Options:
31
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
32
+ --output-format FMT Output format: json, pretty, or text (default: json)
33
+ --help, -h Show this help message
34
+
35
+ Examples:
36
+ exa-ai enrichment-cancel ws_123 enr_456
37
+ exa-ai enrichment-cancel ws_123 enr_456 --output-format pretty
38
+ HELP
39
+ exit 0
40
+ else
41
+ # First positional argument is webset_id, second is enrichment_id
42
+ if webset_id.nil?
43
+ webset_id = arg
44
+ elsif enrichment_id.nil?
45
+ enrichment_id = arg
46
+ else
47
+ $stderr.puts "Unknown option: #{arg}"
48
+ exit 1
49
+ end
50
+ end
51
+ end
52
+
53
+ # Validate
54
+ if webset_id.nil?
55
+ $stderr.puts "Error: webset_id argument is required"
56
+ $stderr.puts "Usage: exa-ai enrichment-cancel <webset_id> <enrichment_id> [OPTIONS]"
57
+ $stderr.puts "Try 'exa-ai enrichment-cancel --help' for more information"
58
+ exit 1
59
+ end
60
+
61
+ if enrichment_id.nil?
62
+ $stderr.puts "Error: enrichment_id argument is required"
63
+ $stderr.puts "Usage: exa-ai enrichment-cancel <webset_id> <enrichment_id> [OPTIONS]"
64
+ $stderr.puts "Try 'exa-ai enrichment-cancel --help' for more information"
65
+ exit 1
66
+ end
67
+
68
+ begin
69
+ # Resolve API key and format
70
+ api_key = Exa::CLI::Base.resolve_api_key(api_key)
71
+ output_format = Exa::CLI::Base.resolve_output_format(output_format)
72
+
73
+ # Build client
74
+ client = Exa::CLI::Base.build_client(api_key)
75
+
76
+ # Cancel enrichment
77
+ enrichment = client.cancel_enrichment(webset_id: webset_id, id: enrichment_id)
78
+
79
+ # Format and output
80
+ output = Exa::CLI::Formatters::EnrichmentFormatter.format(enrichment, output_format)
81
+ puts output
82
+
83
+ rescue Exa::NotFound => e
84
+ $stderr.puts "Enrichment not found: #{e.message}"
85
+ exit 1
86
+ rescue Exa::ConfigurationError => e
87
+ $stderr.puts "Configuration error: #{e.message}"
88
+ exit 1
89
+ rescue Exa::Unauthorized => e
90
+ $stderr.puts "Authentication error: #{e.message}"
91
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
92
+ exit 1
93
+ rescue Exa::ClientError => e
94
+ $stderr.puts "Client error: #{e.message}"
95
+ exit 1
96
+ rescue Exa::ServerError => e
97
+ $stderr.puts "Server error: #{e.message}"
98
+ $stderr.puts "The Exa API may be experiencing issues. Please try again later."
99
+ exit 1
100
+ rescue Exa::Error => e
101
+ $stderr.puts "Error: #{e.message}"
102
+ exit 1
103
+ rescue StandardError => e
104
+ $stderr.puts "Unexpected error: #{e.message}"
105
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
106
+ exit 1
107
+ end
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ VALID_FORMATS = %w[text url options].freeze
7
+
8
+ # Recursively convert hash keys from strings to symbols
9
+ def deep_symbolize_keys(obj)
10
+ case obj
11
+ when Hash
12
+ obj.each_with_object({}) do |(key, value), result|
13
+ result[key.to_sym] = deep_symbolize_keys(value)
14
+ end
15
+ when Array
16
+ obj.map { |item| deep_symbolize_keys(item) }
17
+ else
18
+ obj
19
+ end
20
+ end
21
+
22
+ # Parse JSON string or load from file (supports @file.json syntax)
23
+ def parse_json_or_file(value)
24
+ json_data = if value.start_with?("@")
25
+ file_path = value[1..]
26
+ JSON.parse(File.read(file_path))
27
+ else
28
+ JSON.parse(value)
29
+ end
30
+ deep_symbolize_keys(json_data)
31
+ rescue JSON::ParserError => e
32
+ $stderr.puts "Error: Invalid JSON: #{e.message}"
33
+ exit 1
34
+ rescue Errno::ENOENT => e
35
+ $stderr.puts "Error: File not found: #{e.message}"
36
+ exit 1
37
+ end
38
+
39
+ # Parse command-line arguments
40
+ def parse_args(argv)
41
+ # Check for help first
42
+ if argv.include?("--help") || argv.include?("-h")
43
+ puts <<~HELP
44
+ Usage: exa-ai enrichment-create <webset_id> --description TEXT --format TYPE [OPTIONS]
45
+
46
+ Create a new enrichment for a webset
47
+
48
+ Required:
49
+ <webset_id> Webset ID
50
+ --description TEXT What to extract
51
+ --format TYPE One of: #{VALID_FORMATS.join(', ')}
52
+
53
+ Options:
54
+ --title TEXT Display title
55
+ --options JSON Array of {label: "..."} (required if format=options, supports @file.json)
56
+ --instructions TEXT Additional instructions
57
+ --metadata JSON Custom metadata (supports @file.json)
58
+ --wait Wait for enrichment to complete
59
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
60
+ --output-format FMT Output format: json, pretty, or text (default: json)
61
+ --help, -h Show this help message
62
+
63
+ Examples:
64
+ exa-ai enrichment-create ws_123 --description "Company size" --format text
65
+ exa-ai enrichment-create ws_123 --description "Industry" --format options --options options.json
66
+ exa-ai enrichment-create ws_123 --description "Website URL" --format url --wait
67
+ HELP
68
+ exit 0
69
+ end
70
+
71
+ args = {
72
+ output_format: "json",
73
+ api_key: nil,
74
+ wait: false
75
+ }
76
+
77
+ # First, check for positional argument (webset_id)
78
+ if argv.empty? || argv[0].start_with?("--")
79
+ return args
80
+ end
81
+
82
+ args[:webset_id] = argv[0]
83
+ i = 1
84
+
85
+ while i < argv.length
86
+ arg = argv[i]
87
+ case arg
88
+ when "--description"
89
+ args[:description] = argv[i + 1]
90
+ i += 2
91
+ when "--format"
92
+ args[:format] = argv[i + 1]
93
+ i += 2
94
+ when "--title"
95
+ args[:title] = argv[i + 1]
96
+ i += 2
97
+ when "--options"
98
+ args[:options] = parse_json_or_file(argv[i + 1])
99
+ i += 2
100
+ when "--instructions"
101
+ args[:instructions] = argv[i + 1]
102
+ i += 2
103
+ when "--metadata"
104
+ args[:metadata] = parse_json_or_file(argv[i + 1])
105
+ i += 2
106
+ when "--wait"
107
+ args[:wait] = true
108
+ i += 1
109
+ when "--api-key"
110
+ args[:api_key] = argv[i + 1]
111
+ i += 2
112
+ when "--output-format"
113
+ args[:output_format] = argv[i + 1]
114
+ i += 2
115
+ else
116
+ $stderr.puts "Unknown option: #{arg}"
117
+ exit 1
118
+ end
119
+ end
120
+
121
+ args
122
+ end
123
+
124
+ # Main execution
125
+ begin
126
+ args = parse_args(ARGV)
127
+
128
+ # Validate required parameters
129
+ unless args[:webset_id]
130
+ $stderr.puts "Error: <webset_id> is required"
131
+ $stderr.puts "Run 'exa-ai enrichment-create --help' for usage information"
132
+ exit 1
133
+ end
134
+
135
+ unless args[:description]
136
+ $stderr.puts "Error: --description is required"
137
+ $stderr.puts "Run 'exa-ai enrichment-create --help' for usage information"
138
+ exit 1
139
+ end
140
+
141
+ unless args[:format]
142
+ $stderr.puts "Error: --format is required"
143
+ $stderr.puts "Run 'exa-ai enrichment-create --help' for usage information"
144
+ exit 1
145
+ end
146
+
147
+ # Validate format
148
+ unless VALID_FORMATS.include?(args[:format])
149
+ $stderr.puts "Error: format must be one of: #{VALID_FORMATS.join(', ')}"
150
+ exit 1
151
+ end
152
+
153
+ # Validate options requirement for 'options' format
154
+ if args[:format] == "options" && !args[:options]
155
+ $stderr.puts "Error: --options is required when format is 'options'"
156
+ exit 1
157
+ end
158
+
159
+ # Resolve API key
160
+ api_key = Exa::CLI::Base.resolve_api_key(args[:api_key])
161
+
162
+ # Resolve output format
163
+ output_format = Exa::CLI::Base.resolve_output_format(args[:output_format])
164
+
165
+ # Build client
166
+ client = Exa::CLI::Base.build_client(api_key)
167
+
168
+ # Prepare enrichment parameters
169
+ enrichment_params = {
170
+ description: args[:description],
171
+ format: args[:format]
172
+ }
173
+ enrichment_params[:title] = args[:title] if args[:title]
174
+ enrichment_params[:options] = args[:options] if args[:options]
175
+ enrichment_params[:instructions] = args[:instructions] if args[:instructions]
176
+ enrichment_params[:metadata] = args[:metadata] if args[:metadata]
177
+
178
+ # Create enrichment
179
+ enrichment = client.create_enrichment(webset_id: args[:webset_id], **enrichment_params)
180
+
181
+ # If --wait flag is set, poll until enrichment is complete
182
+ if args[:wait]
183
+ $stderr.print "Creating enrichment... "
184
+
185
+ begin
186
+ final_enrichment = Exa::CLI::Polling.poll(max_duration: 300, initial_delay: 2, max_delay: 10) do
187
+ current = client.get_enrichment(webset_id: args[:webset_id], id: enrichment.id)
188
+
189
+ case current.status
190
+ when "pending"
191
+ $stderr.print "⏳"
192
+ when "running"
193
+ $stderr.print "⚙️"
194
+ end
195
+
196
+ done = current.completed?
197
+ { done: done, result: current, status: current.status }
198
+ end
199
+
200
+ $stderr.puts " COMPLETED"
201
+ enrichment = final_enrichment
202
+
203
+ rescue Exa::CLI::Polling::TimeoutError
204
+ $stderr.puts "\nWarning: Enrichment did not complete within timeout"
205
+ $stderr.puts "Enrichment ID: #{enrichment.id}"
206
+ $stderr.puts "Check status with: exa-ai enrichment-get #{args[:webset_id]} #{enrichment.id}"
207
+ end
208
+ end
209
+
210
+ # Format and output result
211
+ output = Exa::CLI::Formatters::EnrichmentFormatter.format(enrichment, output_format)
212
+ puts output
213
+
214
+ rescue Exa::ConfigurationError => e
215
+ $stderr.puts "Configuration error: #{e.message}"
216
+ exit 1
217
+ rescue Exa::Unauthorized => e
218
+ $stderr.puts "Authentication error: #{e.message}"
219
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
220
+ exit 1
221
+ rescue Exa::ClientError => e
222
+ $stderr.puts "Client error: #{e.message}"
223
+ exit 1
224
+ rescue Exa::ServerError => e
225
+ $stderr.puts "Server error: #{e.message}"
226
+ $stderr.puts "The Exa API may be experiencing issues. Please try again later."
227
+ exit 1
228
+ rescue Exa::Error => e
229
+ $stderr.puts "Error: #{e.message}"
230
+ exit 1
231
+ rescue StandardError => e
232
+ $stderr.puts "Unexpected error: #{e.message}"
233
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
234
+ exit 1
235
+ end
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse arguments
7
+ webset_id = nil
8
+ enrichment_id = nil
9
+ force = false
10
+ api_key = nil
11
+ output_format = "json"
12
+
13
+ args = ARGV.dup
14
+ while args.any?
15
+ arg = args.shift
16
+ case arg
17
+ when "--force"
18
+ force = true
19
+ when "--api-key"
20
+ api_key = args.shift
21
+ when "--output-format"
22
+ output_format = args.shift
23
+ when "--help", "-h"
24
+ puts <<~HELP
25
+ Usage: exa-ai enrichment-delete <webset_id> <enrichment_id> [OPTIONS]
26
+
27
+ Delete an enrichment
28
+
29
+ Arguments:
30
+ webset_id ID of the webset (required)
31
+ enrichment_id ID of the enrichment to delete (required)
32
+
33
+ Options:
34
+ --force Skip confirmation prompt
35
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
36
+ --output-format FMT Output format: json, pretty, or text (default: json)
37
+ --help, -h Show this help message
38
+
39
+ Examples:
40
+ exa-ai enrichment-delete ws_123 enr_456
41
+ exa-ai enrichment-delete ws_123 enr_456 --force
42
+ HELP
43
+ exit 0
44
+ else
45
+ # First positional argument is webset_id, second is enrichment_id
46
+ if webset_id.nil?
47
+ webset_id = arg
48
+ elsif enrichment_id.nil?
49
+ enrichment_id = arg
50
+ else
51
+ $stderr.puts "Unknown option: #{arg}"
52
+ exit 1
53
+ end
54
+ end
55
+ end
56
+
57
+ # Validate
58
+ if webset_id.nil?
59
+ $stderr.puts "Error: webset_id argument is required"
60
+ $stderr.puts "Usage: exa-ai enrichment-delete <webset_id> <enrichment_id> [OPTIONS]"
61
+ $stderr.puts "Try 'exa-ai enrichment-delete --help' for more information"
62
+ exit 1
63
+ end
64
+
65
+ if enrichment_id.nil?
66
+ $stderr.puts "Error: enrichment_id argument is required"
67
+ $stderr.puts "Usage: exa-ai enrichment-delete <webset_id> <enrichment_id> [OPTIONS]"
68
+ $stderr.puts "Try 'exa-ai enrichment-delete --help' for more information"
69
+ exit 1
70
+ end
71
+
72
+ begin
73
+ # Resolve API key and format
74
+ api_key = Exa::CLI::Base.resolve_api_key(api_key)
75
+ output_format = Exa::CLI::Base.resolve_output_format(output_format)
76
+
77
+ # Confirm deletion unless --force is used
78
+ unless force
79
+ $stderr.print "Delete enrichment #{enrichment_id} from webset #{webset_id}? (y/N): "
80
+ response = $stdin.gets.chomp.downcase
81
+ unless response == "y" || response == "yes"
82
+ $stderr.puts "Cancelled"
83
+ exit 0
84
+ end
85
+ end
86
+
87
+ # Build client
88
+ client = Exa::CLI::Base.build_client(api_key)
89
+
90
+ # Delete enrichment
91
+ result = client.delete_enrichment(webset_id: webset_id, id: enrichment_id)
92
+
93
+ # Format and output
94
+ output = Exa::CLI::Formatters::EnrichmentFormatter.format(result, output_format)
95
+ puts output
96
+
97
+ rescue Exa::NotFound => e
98
+ $stderr.puts "Enrichment not found: #{e.message}"
99
+ exit 1
100
+ rescue Exa::ConfigurationError => e
101
+ $stderr.puts "Configuration error: #{e.message}"
102
+ exit 1
103
+ rescue Exa::Unauthorized => e
104
+ $stderr.puts "Authentication error: #{e.message}"
105
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
106
+ exit 1
107
+ rescue Exa::ClientError => e
108
+ $stderr.puts "Client error: #{e.message}"
109
+ exit 1
110
+ rescue Exa::ServerError => e
111
+ $stderr.puts "Server error: #{e.message}"
112
+ $stderr.puts "The Exa API may be experiencing issues. Please try again later."
113
+ exit 1
114
+ rescue Exa::Error => e
115
+ $stderr.puts "Error: #{e.message}"
116
+ exit 1
117
+ rescue StandardError => e
118
+ $stderr.puts "Unexpected error: #{e.message}"
119
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
120
+ exit 1
121
+ end