exa-ai 0.4.1 → 0.6.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 +4 -4
- data/README.md +10 -0
- data/exe/exa-ai +61 -7
- data/exe/exa-ai-answer +9 -8
- data/exe/exa-ai-context +6 -5
- data/exe/exa-ai-enrichment-cancel +1 -0
- data/exe/exa-ai-enrichment-create +1 -0
- data/exe/exa-ai-enrichment-delete +1 -0
- data/exe/exa-ai-enrichment-get +1 -0
- data/exe/exa-ai-enrichment-list +1 -0
- data/exe/exa-ai-enrichment-update +1 -0
- data/exe/exa-ai-find-similar +1 -0
- data/exe/exa-ai-get-contents +9 -8
- data/exe/exa-ai-import-create +235 -0
- data/exe/exa-ai-import-delete +92 -0
- data/exe/exa-ai-import-get +92 -0
- data/exe/exa-ai-import-list +72 -0
- data/exe/exa-ai-import-update +164 -0
- data/exe/exa-ai-monitor-create +223 -0
- data/exe/exa-ai-monitor-delete +101 -0
- data/exe/exa-ai-monitor-get +92 -0
- data/exe/exa-ai-monitor-list +90 -0
- data/exe/exa-ai-monitor-runs-get +103 -0
- data/exe/exa-ai-monitor-runs-list +110 -0
- data/exe/exa-ai-monitor-update +207 -0
- data/exe/exa-ai-research-get +7 -7
- data/exe/exa-ai-research-list +5 -5
- data/exe/exa-ai-research-start +8 -7
- data/exe/exa-ai-search +20 -13
- data/exe/exa-ai-webset-cancel +1 -0
- data/exe/exa-ai-webset-create +44 -6
- data/exe/exa-ai-webset-delete +1 -0
- data/exe/exa-ai-webset-get +1 -0
- data/exe/exa-ai-webset-item-get +1 -0
- data/exe/exa-ai-webset-item-list +1 -0
- data/exe/exa-ai-webset-list +1 -0
- data/exe/exa-ai-webset-search-cancel +1 -0
- data/exe/exa-ai-webset-search-create +1 -0
- data/exe/exa-ai-webset-search-get +1 -0
- data/exe/exa-ai-webset-update +1 -0
- data/lib/exa/cli/base.rb +8 -2
- data/lib/exa/cli/formatters/answer_formatter.rb +2 -0
- data/lib/exa/cli/formatters/contents_formatter.rb +2 -0
- data/lib/exa/cli/formatters/context_formatter.rb +2 -0
- data/lib/exa/cli/formatters/enrichment_formatter.rb +4 -0
- data/lib/exa/cli/formatters/import_formatter.rb +85 -0
- data/lib/exa/cli/formatters/monitor_formatter.rb +81 -0
- data/lib/exa/cli/formatters/monitor_run_formatter.rb +75 -0
- data/lib/exa/cli/formatters/research_formatter.rb +4 -0
- data/lib/exa/cli/formatters/search_formatter.rb +2 -0
- data/lib/exa/cli/formatters/webset_formatter.rb +4 -0
- data/lib/exa/cli/formatters/webset_item_formatter.rb +4 -0
- data/lib/exa/client.rb +137 -0
- data/lib/exa/constants/websets.rb +19 -0
- data/lib/exa/middleware/raise_error.rb +5 -0
- data/lib/exa/resources/import.rb +86 -0
- data/lib/exa/resources/import_collection.rb +33 -0
- data/lib/exa/resources/monitor.rb +48 -0
- data/lib/exa/resources/monitor_collection.rb +30 -0
- data/lib/exa/resources/monitor_run.rb +52 -0
- data/lib/exa/resources/monitor_run_collection.rb +30 -0
- data/lib/exa/services/search.rb +21 -1
- data/lib/exa/services/websets/create_validator.rb +5 -3
- data/lib/exa/services/websets/import_create.rb +40 -0
- data/lib/exa/services/websets/import_delete.rb +37 -0
- data/lib/exa/services/websets/import_get.rb +37 -0
- data/lib/exa/services/websets/import_list.rb +25 -0
- data/lib/exa/services/websets/import_update.rb +38 -0
- data/lib/exa/services/websets/import_upload.rb +58 -0
- data/lib/exa/services/websets/monitors/create.rb +42 -0
- data/lib/exa/services/websets/monitors/delete.rb +32 -0
- data/lib/exa/services/websets/monitors/get.rb +33 -0
- data/lib/exa/services/websets/monitors/list.rb +27 -0
- data/lib/exa/services/websets/monitors/runs/get.rb +37 -0
- data/lib/exa/services/websets/monitors/runs/list.rb +30 -0
- data/lib/exa/services/websets/monitors/update.rb +33 -0
- data/lib/exa/version.rb +1 -1
- data/lib/exa.rb +23 -0
- metadata +50 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4458cc74fb6924d5e7e57300ba28ae1db399d0d89bf68495b1203f5fe29d73d2
|
|
4
|
+
data.tar.gz: 00b7f59be62aad198438660acdec2e1a2e3c86539bd1a462c4ea03f45c08a87b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b45e4d6a35d33910e9ce4b154b1b432efba13732b9c237ab94da5962944fae65bf3562a426dc9fa9c18af8c820e8db5d1a4ae3417605d171a136de2d01f149db
|
|
7
|
+
data.tar.gz: 98f082e62eeef3cf7851abcda1cdd61ee5edd548c2cd0d5a65b7645510248ebd0a2274b743e83b7ff354e1ebb4016a92ab83e8a4f93300c255d298fdb9a2683b
|
data/README.md
CHANGED
|
@@ -128,6 +128,15 @@ exa-ai enrichment-get WEBSET_ID ENRICHMENT_ID
|
|
|
128
128
|
exa-ai enrichment-update WEBSET_ID ENRICHMENT_ID --description "Updated description"
|
|
129
129
|
exa-ai enrichment-delete WEBSET_ID ENRICHMENT_ID --force
|
|
130
130
|
exa-ai enrichment-cancel WEBSET_ID ENRICHMENT_ID
|
|
131
|
+
|
|
132
|
+
# Webset Imports
|
|
133
|
+
exa-ai webset-import-create companies.csv --count 100 --title "My Companies" --format csv --entity-type company
|
|
134
|
+
exa-ai webset-import-create data.csv --count 50 --title "Tech Startups" --format csv --entity-type company --csv-identifier 0
|
|
135
|
+
exa-ai webset-import-create import.csv --count 100 --title "Import" --format csv --entity-type company --metadata '{"source":"crm"}' --quiet
|
|
136
|
+
exa-ai webset-import-list
|
|
137
|
+
exa-ai webset-import-get IMPORT_ID
|
|
138
|
+
exa-ai webset-import-update IMPORT_ID --title "Updated Title"
|
|
139
|
+
exa-ai webset-import-delete IMPORT_ID
|
|
131
140
|
```
|
|
132
141
|
|
|
133
142
|
## Features
|
|
@@ -150,6 +159,7 @@ The gem provides complete access to Exa's API endpoints:
|
|
|
150
159
|
- **Webset Searches** — Run searches within websets and manage search tasks
|
|
151
160
|
- **Webset Items** — List, retrieve, and manage individual items in websets
|
|
152
161
|
- **Enrichments** — Create and manage AI-powered data enrichment tasks on websets
|
|
162
|
+
- **Imports** — Upload CSV files to import external data into websets
|
|
153
163
|
|
|
154
164
|
## Error Handling
|
|
155
165
|
|
data/exe/exa-ai
CHANGED
|
@@ -32,7 +32,19 @@ module ExaCLI
|
|
|
32
32
|
"enrichment-list" => "List enrichments",
|
|
33
33
|
"enrichment-update" => "Update an enrichment",
|
|
34
34
|
"enrichment-delete" => "Delete an enrichment",
|
|
35
|
-
"enrichment-cancel" => "Cancel an enrichment"
|
|
35
|
+
"enrichment-cancel" => "Cancel an enrichment",
|
|
36
|
+
"import-create" => "Create a new import",
|
|
37
|
+
"import-list" => "List imports",
|
|
38
|
+
"import-get" => "Get import details",
|
|
39
|
+
"import-update" => "Update an import",
|
|
40
|
+
"import-delete" => "Delete an import",
|
|
41
|
+
"monitor-create" => "Create a webset monitor",
|
|
42
|
+
"monitor-get" => "Get monitor details",
|
|
43
|
+
"monitor-list" => "List monitors",
|
|
44
|
+
"monitor-update" => "Update a monitor",
|
|
45
|
+
"monitor-delete" => "Delete a monitor",
|
|
46
|
+
"monitor-runs-list" => "List monitor runs",
|
|
47
|
+
"monitor-runs-get" => "Get monitor run details"
|
|
36
48
|
}.freeze
|
|
37
49
|
|
|
38
50
|
def self.run
|
|
@@ -44,7 +56,7 @@ module ExaCLI
|
|
|
44
56
|
print_help
|
|
45
57
|
exit 0
|
|
46
58
|
when "--api-key"
|
|
47
|
-
puts "Use --api-key flag with specific commands"
|
|
59
|
+
$stderr.puts "Error: Use --api-key flag with specific commands"
|
|
48
60
|
exit 1
|
|
49
61
|
when "search"
|
|
50
62
|
exec File.expand_path("../exa-ai-search", __FILE__), *ARGV[1..]
|
|
@@ -98,6 +110,30 @@ module ExaCLI
|
|
|
98
110
|
exec File.expand_path("../exa-ai-enrichment-delete", __FILE__), *ARGV[1..]
|
|
99
111
|
when "enrichment-cancel"
|
|
100
112
|
exec File.expand_path("../exa-ai-enrichment-cancel", __FILE__), *ARGV[1..]
|
|
113
|
+
when "import-create"
|
|
114
|
+
exec File.expand_path("../exa-ai-import-create", __FILE__), *ARGV[1..]
|
|
115
|
+
when "import-list"
|
|
116
|
+
exec File.expand_path("../exa-ai-import-list", __FILE__), *ARGV[1..]
|
|
117
|
+
when "import-get"
|
|
118
|
+
exec File.expand_path("../exa-ai-import-get", __FILE__), *ARGV[1..]
|
|
119
|
+
when "import-update"
|
|
120
|
+
exec File.expand_path("../exa-ai-import-update", __FILE__), *ARGV[1..]
|
|
121
|
+
when "import-delete"
|
|
122
|
+
exec File.expand_path("../exa-ai-import-delete", __FILE__), *ARGV[1..]
|
|
123
|
+
when "monitor-create"
|
|
124
|
+
exec File.expand_path("../exa-ai-monitor-create", __FILE__), *ARGV[1..]
|
|
125
|
+
when "monitor-get"
|
|
126
|
+
exec File.expand_path("../exa-ai-monitor-get", __FILE__), *ARGV[1..]
|
|
127
|
+
when "monitor-list"
|
|
128
|
+
exec File.expand_path("../exa-ai-monitor-list", __FILE__), *ARGV[1..]
|
|
129
|
+
when "monitor-update"
|
|
130
|
+
exec File.expand_path("../exa-ai-monitor-update", __FILE__), *ARGV[1..]
|
|
131
|
+
when "monitor-delete"
|
|
132
|
+
exec File.expand_path("../exa-ai-monitor-delete", __FILE__), *ARGV[1..]
|
|
133
|
+
when "monitor-runs-list"
|
|
134
|
+
exec File.expand_path("../exa-ai-monitor-runs-list", __FILE__), *ARGV[1..]
|
|
135
|
+
when "monitor-runs-get"
|
|
136
|
+
exec File.expand_path("../exa-ai-monitor-runs-get", __FILE__), *ARGV[1..]
|
|
101
137
|
else
|
|
102
138
|
print_error_for_command(ARGV[0])
|
|
103
139
|
exit 1
|
|
@@ -154,6 +190,24 @@ module ExaCLI
|
|
|
154
190
|
["enrichment-cancel", "Cancel an enrichment"]
|
|
155
191
|
])
|
|
156
192
|
|
|
193
|
+
print_command_section("Imports", [
|
|
194
|
+
["import-create", "Create a new import"],
|
|
195
|
+
["import-list", "List imports"],
|
|
196
|
+
["import-get", "Get import details"],
|
|
197
|
+
["import-update", "Update an import"],
|
|
198
|
+
["import-delete", "Delete an import"]
|
|
199
|
+
])
|
|
200
|
+
|
|
201
|
+
print_command_section("Webset Monitors", [
|
|
202
|
+
["monitor-create", "Create a webset monitor"],
|
|
203
|
+
["monitor-get", "Get monitor details"],
|
|
204
|
+
["monitor-list", "List monitors"],
|
|
205
|
+
["monitor-update", "Update a monitor"],
|
|
206
|
+
["monitor-delete", "Delete a monitor"],
|
|
207
|
+
["monitor-runs-list", "List monitor runs"],
|
|
208
|
+
["monitor-runs-get", "Get monitor run details"]
|
|
209
|
+
])
|
|
210
|
+
|
|
157
211
|
puts "Global Options:"
|
|
158
212
|
puts " --help, -h Show this help message"
|
|
159
213
|
puts " --version Show version number"
|
|
@@ -175,15 +229,15 @@ module ExaCLI
|
|
|
175
229
|
def self.print_error_for_command(cmd)
|
|
176
230
|
return print_help if cmd.nil?
|
|
177
231
|
|
|
178
|
-
puts "Error: Unknown command '#{cmd}'"
|
|
179
|
-
puts ""
|
|
232
|
+
$stderr.puts "Error: Unknown command '#{cmd}'"
|
|
233
|
+
$stderr.puts ""
|
|
180
234
|
|
|
181
235
|
# Try to suggest similar commands
|
|
182
236
|
suggestion = suggest_command(cmd)
|
|
183
|
-
puts "Did you mean: #{suggestion}?" if suggestion
|
|
237
|
+
$stderr.puts "Did you mean: #{suggestion}?" if suggestion
|
|
184
238
|
|
|
185
|
-
puts ""
|
|
186
|
-
puts "Run 'exa-ai --help' for usage information."
|
|
239
|
+
$stderr.puts ""
|
|
240
|
+
$stderr.puts "Run 'exa-ai --help' for usage information."
|
|
187
241
|
end
|
|
188
242
|
|
|
189
243
|
def self.suggest_command(input)
|
data/exe/exa-ai-answer
CHANGED
|
@@ -38,7 +38,7 @@ def parse_args(argv)
|
|
|
38
38
|
i += 2
|
|
39
39
|
when "--help", "-h"
|
|
40
40
|
puts <<~HELP
|
|
41
|
-
Usage: exa-
|
|
41
|
+
Usage: exa-ai answer QUERY [OPTIONS]
|
|
42
42
|
|
|
43
43
|
Generate an answer to a question using Exa AI
|
|
44
44
|
|
|
@@ -55,12 +55,12 @@ def parse_args(argv)
|
|
|
55
55
|
--help, -h Show this help message
|
|
56
56
|
|
|
57
57
|
Examples:
|
|
58
|
-
exa-
|
|
59
|
-
exa-
|
|
60
|
-
exa-
|
|
61
|
-
exa-
|
|
62
|
-
exa-
|
|
63
|
-
exa-
|
|
58
|
+
exa-ai answer "What is the capital of France?"
|
|
59
|
+
exa-ai answer "Latest AI breakthroughs" --stream
|
|
60
|
+
exa-ai answer "Latest AI breakthroughs" --text
|
|
61
|
+
exa-ai answer "Ruby best practices" --output-format pretty
|
|
62
|
+
exa-ai answer "What is the capital of France?" --output-schema '{"type":"object","properties":{"city":{"type":"string"},"state":{"type":"string"}}}'
|
|
63
|
+
exa-ai answer "What is Paris?" --system-prompt "Respond in the voice of a pirate"
|
|
64
64
|
HELP
|
|
65
65
|
exit 0
|
|
66
66
|
else
|
|
@@ -80,7 +80,7 @@ begin
|
|
|
80
80
|
# Validate query
|
|
81
81
|
if args[:query].nil? || args[:query].empty?
|
|
82
82
|
$stderr.puts "Error: Query is required"
|
|
83
|
-
$stderr.puts "Run 'exa-
|
|
83
|
+
$stderr.puts "Run 'exa-ai answer --help' for usage information"
|
|
84
84
|
exit 1
|
|
85
85
|
end
|
|
86
86
|
|
|
@@ -125,6 +125,7 @@ begin
|
|
|
125
125
|
result = client.answer(args[:query], **answer_params)
|
|
126
126
|
output = Exa::CLI::Formatters::AnswerFormatter.format(result, output_format)
|
|
127
127
|
puts output
|
|
128
|
+
$stdout.flush
|
|
128
129
|
end
|
|
129
130
|
|
|
130
131
|
rescue Exa::ConfigurationError => e
|
data/exe/exa-ai-context
CHANGED
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
def print_help
|
|
44
44
|
puts "Exa Context - Get code context from repositories"
|
|
45
45
|
puts ""
|
|
46
|
-
puts "Usage: exa-
|
|
46
|
+
puts "Usage: exa-ai context <query> [options]"
|
|
47
47
|
puts ""
|
|
48
48
|
puts "Arguments:"
|
|
49
49
|
puts " query Search query for code context (required)"
|
|
@@ -55,9 +55,9 @@ def print_help
|
|
|
55
55
|
puts " --help, -h Show this help message"
|
|
56
56
|
puts ""
|
|
57
57
|
puts "Examples:"
|
|
58
|
-
puts " exa-
|
|
59
|
-
puts " exa-
|
|
60
|
-
puts " exa-
|
|
58
|
+
puts " exa-ai context 'React hooks'"
|
|
59
|
+
puts " exa-ai context 'authentication with JWT in Ruby' --tokens-num 5000"
|
|
60
|
+
puts " exa-ai context 'React hooks' --output-format text"
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
begin
|
|
@@ -68,7 +68,7 @@ begin
|
|
|
68
68
|
unless options[:query]
|
|
69
69
|
puts "Error: Query argument required"
|
|
70
70
|
puts ""
|
|
71
|
-
puts "Run 'exa-
|
|
71
|
+
puts "Run 'exa-ai context --help' for usage information."
|
|
72
72
|
exit 1
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -91,6 +91,7 @@ begin
|
|
|
91
91
|
# Format and output
|
|
92
92
|
output = Exa::CLI::Formatters::ContextFormatter.format(result, output_format)
|
|
93
93
|
puts output
|
|
94
|
+
$stdout.flush
|
|
94
95
|
rescue Exa::Error => e
|
|
95
96
|
puts "Error: #{e.message}"
|
|
96
97
|
exit 1
|
|
@@ -210,6 +210,7 @@ begin
|
|
|
210
210
|
# Format and output result
|
|
211
211
|
output = Exa::CLI::Formatters::EnrichmentFormatter.format(enrichment, output_format)
|
|
212
212
|
puts output
|
|
213
|
+
$stdout.flush
|
|
213
214
|
|
|
214
215
|
rescue Exa::ConfigurationError => e
|
|
215
216
|
$stderr.puts "Configuration error: #{e.message}"
|
data/exe/exa-ai-enrichment-get
CHANGED
data/exe/exa-ai-enrichment-list
CHANGED
data/exe/exa-ai-find-similar
CHANGED
data/exe/exa-ai-get-contents
CHANGED
|
@@ -164,7 +164,7 @@ end
|
|
|
164
164
|
|
|
165
165
|
def print_help
|
|
166
166
|
puts <<~HELP
|
|
167
|
-
Usage: exa-
|
|
167
|
+
Usage: exa-ai get-contents <urls> [options]
|
|
168
168
|
|
|
169
169
|
Retrieve full page contents from URLs
|
|
170
170
|
|
|
@@ -203,12 +203,12 @@ def print_help
|
|
|
203
203
|
--help, -h Show this help message
|
|
204
204
|
|
|
205
205
|
Examples:
|
|
206
|
-
exa-
|
|
207
|
-
exa-
|
|
208
|
-
exa-
|
|
209
|
-
exa-
|
|
210
|
-
exa-
|
|
211
|
-
exa-
|
|
206
|
+
exa-ai get-contents 'https://example.com'
|
|
207
|
+
exa-ai get-contents 'https://example.com' --text
|
|
208
|
+
exa-ai get-contents 'https://example.com' --text --text-max-characters 3000 --include-html-tags
|
|
209
|
+
exa-ai get-contents 'url1,url2' --summary --summary-query "Be terse"
|
|
210
|
+
exa-ai get-contents 'https://example.com' --subpages 1 --subpage-target about
|
|
211
|
+
exa-ai get-contents 'https://example.com' --links 5 --image-links 10
|
|
212
212
|
HELP
|
|
213
213
|
end
|
|
214
214
|
|
|
@@ -220,7 +220,7 @@ begin
|
|
|
220
220
|
if options[:ids].empty?
|
|
221
221
|
$stderr.puts "Error: URLs argument required"
|
|
222
222
|
$stderr.puts ""
|
|
223
|
-
$stderr.puts "Run 'exa-
|
|
223
|
+
$stderr.puts "Run 'exa-ai get-contents --help' for usage information."
|
|
224
224
|
exit 1
|
|
225
225
|
end
|
|
226
226
|
|
|
@@ -243,6 +243,7 @@ begin
|
|
|
243
243
|
# Format and output
|
|
244
244
|
output = Exa::CLI::Formatters::ContentsFormatter.format(result, output_format)
|
|
245
245
|
puts output
|
|
246
|
+
$stdout.flush
|
|
246
247
|
rescue Exa::ConfigurationError => e
|
|
247
248
|
$stderr.puts "Configuration error: #{e.message}"
|
|
248
249
|
exit 1
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
|
|
6
|
+
VALID_FORMATS = Exa::Constants::Websets::IMPORT_FORMATS
|
|
7
|
+
VALID_ENTITY_TYPES = Exa::Constants::Websets::ENTITY_TYPES
|
|
8
|
+
|
|
9
|
+
# Recursively convert hash keys from strings to symbols
|
|
10
|
+
def deep_symbolize_keys(obj)
|
|
11
|
+
case obj
|
|
12
|
+
when Hash
|
|
13
|
+
obj.each_with_object({}) do |(key, value), result|
|
|
14
|
+
result[key.to_sym] = deep_symbolize_keys(value)
|
|
15
|
+
end
|
|
16
|
+
when Array
|
|
17
|
+
obj.map { |item| deep_symbolize_keys(item) }
|
|
18
|
+
else
|
|
19
|
+
obj
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Parse JSON string or load from file (supports @file.json syntax)
|
|
24
|
+
def parse_json_or_file(value)
|
|
25
|
+
json_data = if value.start_with?("@")
|
|
26
|
+
file_path = value[1..]
|
|
27
|
+
JSON.parse(File.read(file_path))
|
|
28
|
+
else
|
|
29
|
+
JSON.parse(value)
|
|
30
|
+
end
|
|
31
|
+
deep_symbolize_keys(json_data)
|
|
32
|
+
rescue JSON::ParserError => e
|
|
33
|
+
$stderr.puts "Error: Invalid JSON: #{e.message}"
|
|
34
|
+
exit 1
|
|
35
|
+
rescue Errno::ENOENT => e
|
|
36
|
+
$stderr.puts "Error: File not found: #{e.message}"
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Parse command-line arguments
|
|
41
|
+
def parse_args(argv)
|
|
42
|
+
# Check for help first
|
|
43
|
+
if argv.include?("--help") || argv.include?("-h")
|
|
44
|
+
puts <<~HELP
|
|
45
|
+
Usage: exa-ai import-create FILE [OPTIONS]
|
|
46
|
+
|
|
47
|
+
Create a new import and upload external data file
|
|
48
|
+
|
|
49
|
+
Arguments:
|
|
50
|
+
FILE Path to the CSV file to upload
|
|
51
|
+
|
|
52
|
+
Required:
|
|
53
|
+
--count N Number of entities in the import
|
|
54
|
+
--title TEXT Display title
|
|
55
|
+
--format TYPE Format type (default: csv, options: #{VALID_FORMATS.join(', ')})
|
|
56
|
+
--entity-type TYPE Entity type (options: #{VALID_ENTITY_TYPES.join(', ')})
|
|
57
|
+
|
|
58
|
+
Options:
|
|
59
|
+
--csv-identifier N CSV column identifier (0-indexed)
|
|
60
|
+
--metadata JSON Custom metadata (supports @file.json)
|
|
61
|
+
--quiet Suppress normal output (only show errors)
|
|
62
|
+
--api-key KEY Exa API key (or set EXA_API_KEY env var)
|
|
63
|
+
--output-format FMT Output format: json, pretty, or text (default: json)
|
|
64
|
+
--help, -h Show this help message
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
exa-ai import-create data.csv --count 100 --title "My Companies" --format csv --entity-type company
|
|
68
|
+
exa-ai import-create companies.csv --count 50 --title "Tech Startups" --format csv --entity-type company --csv-identifier 0
|
|
69
|
+
exa-ai import-create import.csv --count 100 --title "Import" --format csv --entity-type company --metadata '{"source":"crm"}' --quiet
|
|
70
|
+
HELP
|
|
71
|
+
exit 0
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# First argument should be the file path
|
|
75
|
+
if argv.empty? || argv[0].start_with?("--")
|
|
76
|
+
$stderr.puts "Error: FILE argument is required"
|
|
77
|
+
$stderr.puts "Run 'exa-ai import-create --help' for usage information"
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
args = {
|
|
82
|
+
file_path: argv[0],
|
|
83
|
+
output_format: "json",
|
|
84
|
+
api_key: nil,
|
|
85
|
+
format: "csv",
|
|
86
|
+
quiet: false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
i = 1 # Start after the file path
|
|
90
|
+
while i < argv.length
|
|
91
|
+
arg = argv[i]
|
|
92
|
+
case arg
|
|
93
|
+
when "--count"
|
|
94
|
+
args[:count] = argv[i + 1].to_i
|
|
95
|
+
i += 2
|
|
96
|
+
when "--title"
|
|
97
|
+
args[:title] = argv[i + 1]
|
|
98
|
+
i += 2
|
|
99
|
+
when "--format"
|
|
100
|
+
args[:format] = argv[i + 1]
|
|
101
|
+
i += 2
|
|
102
|
+
when "--entity-type"
|
|
103
|
+
args[:entity_type] = argv[i + 1]
|
|
104
|
+
i += 2
|
|
105
|
+
when "--csv-identifier"
|
|
106
|
+
args[:csv_identifier] = argv[i + 1].to_i
|
|
107
|
+
i += 2
|
|
108
|
+
when "--metadata"
|
|
109
|
+
args[:metadata] = parse_json_or_file(argv[i + 1])
|
|
110
|
+
i += 2
|
|
111
|
+
when "--quiet"
|
|
112
|
+
args[:quiet] = true
|
|
113
|
+
i += 1
|
|
114
|
+
when "--api-key"
|
|
115
|
+
args[:api_key] = argv[i + 1]
|
|
116
|
+
i += 2
|
|
117
|
+
when "--output-format"
|
|
118
|
+
args[:output_format] = argv[i + 1]
|
|
119
|
+
i += 2
|
|
120
|
+
else
|
|
121
|
+
$stderr.puts "Unknown option: #{arg}"
|
|
122
|
+
exit 1
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
args
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Main execution
|
|
130
|
+
begin
|
|
131
|
+
args = parse_args(ARGV)
|
|
132
|
+
|
|
133
|
+
# Validate required parameters
|
|
134
|
+
unless args[:count]
|
|
135
|
+
$stderr.puts "Error: --count is required"
|
|
136
|
+
$stderr.puts "Run 'exa-ai import-create --help' for usage information"
|
|
137
|
+
exit 1
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
unless args[:title]
|
|
141
|
+
$stderr.puts "Error: --title is required"
|
|
142
|
+
$stderr.puts "Run 'exa-ai import-create --help' for usage information"
|
|
143
|
+
exit 1
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
unless args[:entity_type]
|
|
147
|
+
$stderr.puts "Error: --entity-type is required"
|
|
148
|
+
$stderr.puts "Run 'exa-ai import-create --help' for usage information"
|
|
149
|
+
exit 1
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Validate format
|
|
153
|
+
unless VALID_FORMATS.include?(args[:format])
|
|
154
|
+
$stderr.puts "Error: format must be one of: #{VALID_FORMATS.join(', ')}"
|
|
155
|
+
exit 1
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Validate entity type
|
|
159
|
+
unless VALID_ENTITY_TYPES.include?(args[:entity_type])
|
|
160
|
+
$stderr.puts "Error: entity-type must be one of: #{VALID_ENTITY_TYPES.join(', ')}"
|
|
161
|
+
exit 1
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Validate file exists
|
|
165
|
+
unless File.exist?(args[:file_path])
|
|
166
|
+
$stderr.puts "Error: File not found: #{args[:file_path]}"
|
|
167
|
+
exit 1
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Resolve API key
|
|
171
|
+
api_key = Exa::CLI::Base.resolve_api_key(args[:api_key])
|
|
172
|
+
|
|
173
|
+
# Resolve output format
|
|
174
|
+
output_format = Exa::CLI::Base.resolve_output_format(args[:output_format])
|
|
175
|
+
|
|
176
|
+
# Build client
|
|
177
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
178
|
+
|
|
179
|
+
# Prepare import parameters
|
|
180
|
+
import_params = {
|
|
181
|
+
file_path: args[:file_path],
|
|
182
|
+
count: args[:count],
|
|
183
|
+
title: args[:title],
|
|
184
|
+
format: args[:format],
|
|
185
|
+
entity: { type: args[:entity_type] }
|
|
186
|
+
}
|
|
187
|
+
import_params[:metadata] = args[:metadata] if args[:metadata]
|
|
188
|
+
|
|
189
|
+
# Add CSV config if csv_identifier provided
|
|
190
|
+
if args[:csv_identifier]
|
|
191
|
+
import_params[:csv] = { identifier: args[:csv_identifier] }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Show upload message unless quiet
|
|
195
|
+
unless args[:quiet]
|
|
196
|
+
file_size = File.size(args[:file_path])
|
|
197
|
+
$stderr.puts "Uploading #{args[:file_path]} (#{file_size} bytes)..."
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Upload import
|
|
201
|
+
import = client.upload_import(**import_params)
|
|
202
|
+
|
|
203
|
+
# Show success message unless quiet
|
|
204
|
+
unless args[:quiet]
|
|
205
|
+
$stderr.puts "Upload complete. Import ID: #{import.id}"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Format and output result (unless quiet)
|
|
209
|
+
unless args[:quiet]
|
|
210
|
+
output = Exa::CLI::Formatters::ImportFormatter.format(import, output_format)
|
|
211
|
+
puts output
|
|
212
|
+
end
|
|
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,92 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
|
|
6
|
+
# Parse arguments
|
|
7
|
+
import_id = nil
|
|
8
|
+
api_key = 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 import-delete <import_id> [OPTIONS]
|
|
22
|
+
|
|
23
|
+
Delete an import
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
import_id ID of the import to delete (required)
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--api-key KEY Exa API key (or set EXA_API_KEY env var)
|
|
30
|
+
--output-format FMT Output format: json, pretty, or text (default: json)
|
|
31
|
+
--help, -h Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
exa-ai import-delete imp_123
|
|
35
|
+
exa-ai import-delete imp_123 --output-format text
|
|
36
|
+
HELP
|
|
37
|
+
exit 0
|
|
38
|
+
else
|
|
39
|
+
# First positional argument is import_id
|
|
40
|
+
if import_id.nil?
|
|
41
|
+
import_id = arg
|
|
42
|
+
else
|
|
43
|
+
$stderr.puts "Unknown option: #{arg}"
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Validate
|
|
50
|
+
if import_id.nil?
|
|
51
|
+
$stderr.puts "Error: import_id argument is required"
|
|
52
|
+
$stderr.puts "Usage: exa-ai import-delete <import_id> [OPTIONS]"
|
|
53
|
+
$stderr.puts "Try 'exa-ai import-delete --help' for more information"
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
# Resolve API key and format
|
|
59
|
+
api_key = Exa::CLI::Base.resolve_api_key(api_key)
|
|
60
|
+
output_format = Exa::CLI::Base.resolve_output_format(output_format)
|
|
61
|
+
|
|
62
|
+
# Build client
|
|
63
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
64
|
+
|
|
65
|
+
# Delete import
|
|
66
|
+
import = client.delete_import(import_id)
|
|
67
|
+
|
|
68
|
+
# Format and output
|
|
69
|
+
output = Exa::CLI::Formatters::ImportFormatter.format(import, output_format)
|
|
70
|
+
puts output
|
|
71
|
+
|
|
72
|
+
rescue Exa::NotFound => e
|
|
73
|
+
$stderr.puts "Import not found: #{e.message}"
|
|
74
|
+
exit 1
|
|
75
|
+
rescue Exa::Unauthorized => e
|
|
76
|
+
$stderr.puts "Authentication error: #{e.message}"
|
|
77
|
+
$stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
|
|
78
|
+
exit 1
|
|
79
|
+
rescue Exa::ClientError => e
|
|
80
|
+
$stderr.puts "Client error: #{e.message}"
|
|
81
|
+
exit 1
|
|
82
|
+
rescue Exa::ServerError => e
|
|
83
|
+
$stderr.puts "Server error: #{e.message}"
|
|
84
|
+
exit 1
|
|
85
|
+
rescue Exa::Error => e
|
|
86
|
+
$stderr.puts "Error: #{e.message}"
|
|
87
|
+
exit 1
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
$stderr.puts "Unexpected error: #{e.message}"
|
|
90
|
+
$stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
|
|
91
|
+
exit 1
|
|
92
|
+
end
|