exa-ai 0.6.0 → 0.7.1
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 +105 -0
- data/exe/exa-ai-answer +8 -2
- data/exe/exa-ai-enrichment-create +1 -11
- data/exe/exa-ai-import-create +19 -1
- data/exe/exa-ai-search +64 -201
- data/exe/exa-ai-webset-create +25 -6
- data/exe/exa-ai-webset-item-list +18 -4
- data/exe/exa-ai-webset-search-create +19 -6
- data/exe/exa-ai-webset-search-get +1 -1
- data/lib/exa/cli/formatters/answer_formatter.rb +22 -14
- data/lib/exa/cli/formatters/enrichment_formatter.rb +54 -2
- data/lib/exa/cli/formatters/import_formatter.rb +70 -2
- data/lib/exa/cli/formatters/monitor_formatter.rb +65 -2
- data/lib/exa/cli/formatters/monitor_run_formatter.rb +53 -2
- data/lib/exa/cli/formatters/webset_formatter.rb +61 -1
- data/lib/exa/cli/formatters/webset_item_formatter.rb +67 -9
- data/lib/exa/cli/formatters/webset_search_formatter.rb +57 -0
- data/lib/exa/cli/search_parser.rb +152 -0
- data/lib/exa/client.rb +6 -29
- data/lib/exa/constants/websets.rb +1 -1
- data/lib/exa/resources/webset_item_collection.rb +33 -0
- data/lib/exa/services/websets/create_validator.rb +15 -0
- data/lib/exa/services/websets/list_items.rb +9 -3
- data/lib/exa/version.rb +1 -1
- data/lib/exa.rb +2 -0
- metadata +18 -1
data/exe/exa-ai-webset-item-list
CHANGED
|
@@ -7,6 +7,8 @@ require "exa-ai"
|
|
|
7
7
|
webset_id = nil
|
|
8
8
|
api_key = nil
|
|
9
9
|
output_format = "json"
|
|
10
|
+
limit = nil
|
|
11
|
+
cursor = nil
|
|
10
12
|
|
|
11
13
|
args = ARGV.dup
|
|
12
14
|
while args.any?
|
|
@@ -16,6 +18,10 @@ while args.any?
|
|
|
16
18
|
api_key = args.shift
|
|
17
19
|
when "--output-format"
|
|
18
20
|
output_format = args.shift
|
|
21
|
+
when "--limit"
|
|
22
|
+
limit = args.shift&.to_i
|
|
23
|
+
when "--cursor"
|
|
24
|
+
cursor = args.shift
|
|
19
25
|
when "--help", "-h"
|
|
20
26
|
puts <<~HELP
|
|
21
27
|
Usage: exa-ai webset-item-list <webset_id> [OPTIONS]
|
|
@@ -26,14 +32,17 @@ while args.any?
|
|
|
26
32
|
webset_id ID of the webset (required)
|
|
27
33
|
|
|
28
34
|
Options:
|
|
35
|
+
--limit N Maximum number of items to return (default: 20)
|
|
36
|
+
--cursor CURSOR Cursor for pagination (use nextCursor from previous response)
|
|
29
37
|
--api-key KEY Exa API key (or set EXA_API_KEY env var)
|
|
30
|
-
--output-format FMT Output format: json, pretty, or
|
|
38
|
+
--output-format FMT Output format: json, pretty, text, or toon (default: json)
|
|
31
39
|
--help, -h Show this help message
|
|
32
40
|
|
|
33
41
|
Examples:
|
|
34
42
|
exa-ai webset-item-list ws_123
|
|
43
|
+
exa-ai webset-item-list ws_123 --limit 10
|
|
44
|
+
exa-ai webset-item-list ws_123 --limit 5 --cursor "abc123"
|
|
35
45
|
exa-ai webset-item-list ws_123 --output-format pretty
|
|
36
|
-
exa-ai webset-item-list ws_123 --output-format text
|
|
37
46
|
HELP
|
|
38
47
|
exit 0
|
|
39
48
|
else
|
|
@@ -63,11 +72,16 @@ begin
|
|
|
63
72
|
# Build client
|
|
64
73
|
client = Exa::CLI::Base.build_client(api_key)
|
|
65
74
|
|
|
75
|
+
# Build list params
|
|
76
|
+
list_params = {}
|
|
77
|
+
list_params[:limit] = limit if limit
|
|
78
|
+
list_params[:cursor] = cursor if cursor
|
|
79
|
+
|
|
66
80
|
# List items
|
|
67
|
-
|
|
81
|
+
collection = client.list_items(webset_id: webset_id, **list_params)
|
|
68
82
|
|
|
69
83
|
# Format and output
|
|
70
|
-
output = Exa::CLI::Formatters::WebsetItemFormatter.format_collection(
|
|
84
|
+
output = Exa::CLI::Formatters::WebsetItemFormatter.format_collection(collection, output_format)
|
|
71
85
|
puts output
|
|
72
86
|
$stdout.flush
|
|
73
87
|
|
|
@@ -104,11 +104,20 @@ def parse_args(argv)
|
|
|
104
104
|
--entity TYPE Entity type: person, company, article, research_paper, custom
|
|
105
105
|
--entity-description TXT Description for custom entity type (required with --entity custom)
|
|
106
106
|
--criteria JSON Search criteria array (supports @file.json)
|
|
107
|
+
Format: [{"description":"criterion 1"},{"description":"criterion 2"}]
|
|
107
108
|
--exclude JSON Items to exclude from results (supports @file.json)
|
|
109
|
+
Format: [{"source":"import|webset","id":"..."}]
|
|
108
110
|
--scope JSON Limit search to specific sources (supports @file.json)
|
|
111
|
+
Format: [{"source":"import|webset","id":"..."}]
|
|
112
|
+
Filters this search to only items from these sources
|
|
113
|
+
With relationship (hop search):
|
|
114
|
+
[{"source":"webset","id":"ws_123",
|
|
115
|
+
"relationship":{"definition":"investors of","limit":3}}]
|
|
109
116
|
--recall Estimate total available results
|
|
110
|
-
--behavior TYPE "override" or "append" (
|
|
117
|
+
--behavior TYPE "override" (replace items) or "append" (add items)
|
|
118
|
+
Default: override when scope is present, append otherwise
|
|
111
119
|
--metadata JSON Custom metadata (supports @file.json)
|
|
120
|
+
Format: {"key":"value"}
|
|
112
121
|
--api-key KEY Exa API key (or set EXA_API_KEY env var)
|
|
113
122
|
--output-format FMT Output format: json, pretty, or text (default: json)
|
|
114
123
|
--help, -h Show this help message
|
|
@@ -121,11 +130,15 @@ def parse_args(argv)
|
|
|
121
130
|
exa-ai webset-search-create ws_123 --query "tech CEOs" --entity person
|
|
122
131
|
exa-ai webset-search-create ws_123 --query "Silicon Valley firms" --entity company
|
|
123
132
|
|
|
124
|
-
#
|
|
125
|
-
exa-ai webset-search-create ws_123 --query "
|
|
126
|
-
--
|
|
133
|
+
# Scoped search (filter to specific import)
|
|
134
|
+
exa-ai webset-search-create ws_123 --query "CTOs" \\
|
|
135
|
+
--scope '[{"source":"import","id":"import_abc"}]'
|
|
127
136
|
|
|
128
|
-
#
|
|
137
|
+
# Hop search (find investors of companies in webset)
|
|
138
|
+
exa-ai webset-search-create ws_123 --query "investors" \\
|
|
139
|
+
--scope '[{"source":"webset","id":"ws_companies","relationship":{"definition":"investors of","limit":5}}]'
|
|
140
|
+
|
|
141
|
+
# Search with criteria and behavior
|
|
129
142
|
exa-ai webset-search-create ws_123 --query "machine learning" --count 50
|
|
130
143
|
exa-ai webset-search-create ws_123 --query "research" --behavior append --recall
|
|
131
144
|
HELP
|
|
@@ -203,7 +216,7 @@ begin
|
|
|
203
216
|
search = client.create_webset_search(webset_id: args[:webset_id], **search_params)
|
|
204
217
|
|
|
205
218
|
# Format and output result
|
|
206
|
-
output = Exa::CLI::Formatters::
|
|
219
|
+
output = Exa::CLI::Formatters::WebsetSearchFormatter.format(search, output_format)
|
|
207
220
|
puts output
|
|
208
221
|
$stdout.flush
|
|
209
222
|
|
|
@@ -78,7 +78,7 @@ begin
|
|
|
78
78
|
search = client.get_webset_search(webset_id: webset_id, id: search_id)
|
|
79
79
|
|
|
80
80
|
# Format and output
|
|
81
|
-
output = Exa::CLI::Formatters::
|
|
81
|
+
output = Exa::CLI::Formatters::WebsetSearchFormatter.format(search, output_format)
|
|
82
82
|
puts output
|
|
83
83
|
$stdout.flush
|
|
84
84
|
|
|
@@ -4,24 +4,30 @@ module Exa
|
|
|
4
4
|
module CLI
|
|
5
5
|
module Formatters
|
|
6
6
|
class AnswerFormatter
|
|
7
|
-
def self.format(result, format)
|
|
7
|
+
def self.format(result, format, skip_citations: false)
|
|
8
8
|
case format
|
|
9
9
|
when "json"
|
|
10
|
-
|
|
10
|
+
format_json(result, skip_citations: skip_citations)
|
|
11
11
|
when "pretty"
|
|
12
|
-
format_pretty(result)
|
|
12
|
+
format_pretty(result, skip_citations: skip_citations)
|
|
13
13
|
when "text"
|
|
14
14
|
format_text(result)
|
|
15
15
|
when "toon"
|
|
16
16
|
Exa::CLI::Base.encode_as_toon(result.to_h)
|
|
17
17
|
else
|
|
18
|
-
|
|
18
|
+
format_json(result, skip_citations: skip_citations)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
def self.
|
|
24
|
+
def self.format_json(result, skip_citations: false)
|
|
25
|
+
hash = result.to_h
|
|
26
|
+
hash.delete(:citations) if skip_citations
|
|
27
|
+
JSON.pretty_generate(hash)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.format_pretty(result, skip_citations: false)
|
|
25
31
|
output = []
|
|
26
32
|
output << "Answer:"
|
|
27
33
|
output << "-" * 60
|
|
@@ -34,15 +40,17 @@ module Exa
|
|
|
34
40
|
end
|
|
35
41
|
output << ""
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
unless skip_citations
|
|
44
|
+
if result.citations && !result.citations.empty?
|
|
45
|
+
output << "Citations:"
|
|
46
|
+
output << "-" * 60
|
|
47
|
+
result.citations.each_with_index do |citation, idx|
|
|
48
|
+
output << "[#{idx + 1}] #{citation['title']}"
|
|
49
|
+
output << " URL: #{citation['url']}"
|
|
50
|
+
output << " Author: #{citation['author']}" if citation['author']
|
|
51
|
+
output << " Date: #{citation['publishedDate']}" if citation['publishedDate']
|
|
52
|
+
output << ""
|
|
53
|
+
end
|
|
46
54
|
end
|
|
47
55
|
end
|
|
48
56
|
|
|
@@ -9,7 +9,7 @@ module Exa
|
|
|
9
9
|
when "json"
|
|
10
10
|
JSON.generate(enrichment.to_h)
|
|
11
11
|
when "pretty"
|
|
12
|
-
|
|
12
|
+
format_as_pretty(enrichment)
|
|
13
13
|
when "text"
|
|
14
14
|
format_as_text(enrichment)
|
|
15
15
|
when "toon"
|
|
@@ -24,7 +24,7 @@ module Exa
|
|
|
24
24
|
when "json"
|
|
25
25
|
JSON.generate(collection.to_h)
|
|
26
26
|
when "pretty"
|
|
27
|
-
|
|
27
|
+
format_collection_as_pretty(collection)
|
|
28
28
|
when "text"
|
|
29
29
|
format_collection_as_text(collection)
|
|
30
30
|
when "toon"
|
|
@@ -34,6 +34,31 @@ module Exa
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.format_as_pretty(enrichment)
|
|
38
|
+
lines = []
|
|
39
|
+
lines << "Enrichment ID: #{enrichment.id}"
|
|
40
|
+
lines << "Webset ID: #{enrichment.webset_id}" if enrichment.webset_id
|
|
41
|
+
lines << "Status: #{enrichment.status}"
|
|
42
|
+
lines << "Title: #{enrichment.title}" if enrichment.title
|
|
43
|
+
lines << "Description: #{enrichment.description}" if enrichment.description
|
|
44
|
+
lines << "Format: #{enrichment.format}" if enrichment.format
|
|
45
|
+
|
|
46
|
+
if enrichment.options && !enrichment.options.empty?
|
|
47
|
+
lines << ""
|
|
48
|
+
lines << "Options (#{enrichment.options.length}):"
|
|
49
|
+
enrichment.options.each do |option|
|
|
50
|
+
lines << " • #{option['label']}" if option['label']
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
lines << ""
|
|
55
|
+
lines << "Created: #{enrichment.created_at}" if enrichment.created_at
|
|
56
|
+
lines << "Updated: #{enrichment.updated_at}" if enrichment.updated_at
|
|
57
|
+
|
|
58
|
+
lines.join("\n")
|
|
59
|
+
end
|
|
60
|
+
private_class_method :format_as_pretty
|
|
61
|
+
|
|
37
62
|
def self.format_as_text(enrichment)
|
|
38
63
|
lines = []
|
|
39
64
|
lines << "Enrichment: #{enrichment.id}"
|
|
@@ -57,6 +82,33 @@ module Exa
|
|
|
57
82
|
end
|
|
58
83
|
private_class_method :format_as_text
|
|
59
84
|
|
|
85
|
+
def self.format_collection_as_pretty(collection)
|
|
86
|
+
lines = []
|
|
87
|
+
lines << "Enrichments (#{collection.data.length} items)"
|
|
88
|
+
lines << ""
|
|
89
|
+
|
|
90
|
+
collection.data.each_with_index do |enr, idx|
|
|
91
|
+
lines << "" if idx > 0 # Blank line between enrichments
|
|
92
|
+
|
|
93
|
+
lines << "Enrichment ID: #{enr['id']}"
|
|
94
|
+
lines << "Webset ID: #{enr['websetId']}" if enr['websetId']
|
|
95
|
+
lines << "Status: #{enr['status']}"
|
|
96
|
+
lines << "Title: #{enr['title']}" if enr['title']
|
|
97
|
+
lines << "Description: #{enr['description']}" if enr['description']
|
|
98
|
+
lines << "Format: #{enr['format']}" if enr['format']
|
|
99
|
+
lines << "Created: #{enr['createdAt']}" if enr['createdAt']
|
|
100
|
+
lines << "Updated: #{enr['updatedAt']}" if enr['updatedAt']
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if collection.has_more
|
|
104
|
+
lines << ""
|
|
105
|
+
lines << "Next Cursor: #{collection.next_cursor}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
lines.join("\n")
|
|
109
|
+
end
|
|
110
|
+
private_class_method :format_collection_as_pretty
|
|
111
|
+
|
|
60
112
|
def self.format_collection_as_text(collection)
|
|
61
113
|
lines = ["Enrichments (#{collection.data.length} items):"]
|
|
62
114
|
collection.data.each do |enr|
|
|
@@ -9,7 +9,7 @@ module Exa
|
|
|
9
9
|
when "json"
|
|
10
10
|
JSON.generate(import.to_h)
|
|
11
11
|
when "pretty"
|
|
12
|
-
|
|
12
|
+
format_as_pretty(import)
|
|
13
13
|
when "text"
|
|
14
14
|
format_as_text(import)
|
|
15
15
|
when "toon"
|
|
@@ -24,7 +24,7 @@ module Exa
|
|
|
24
24
|
when "json"
|
|
25
25
|
JSON.generate(collection.to_h)
|
|
26
26
|
when "pretty"
|
|
27
|
-
|
|
27
|
+
format_collection_as_pretty(collection)
|
|
28
28
|
when "text"
|
|
29
29
|
format_collection_as_text(collection)
|
|
30
30
|
when "toon"
|
|
@@ -34,6 +34,43 @@ module Exa
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.format_as_pretty(import)
|
|
38
|
+
lines = []
|
|
39
|
+
lines << "Import ID: #{import.id}"
|
|
40
|
+
lines << "Status: #{import.status}"
|
|
41
|
+
lines << "Title: #{import.title}" if import.title
|
|
42
|
+
lines << "Format: #{import.format}" if import.format
|
|
43
|
+
|
|
44
|
+
if import.entity
|
|
45
|
+
entity_type = import.entity['type'] || import.entity[:type]
|
|
46
|
+
lines << "Entity Type: #{entity_type}" if entity_type
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
lines << "Count: #{import.count}" if import.count
|
|
50
|
+
|
|
51
|
+
if import.failed?
|
|
52
|
+
lines << ""
|
|
53
|
+
lines << "Failure:"
|
|
54
|
+
lines << " Reason: #{import.failed_reason}" if import.failed_reason
|
|
55
|
+
lines << " Message: #{import.failed_message}" if import.failed_message
|
|
56
|
+
lines << " Failed At: #{import.failed_at}" if import.failed_at
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if import.upload_url
|
|
60
|
+
lines << ""
|
|
61
|
+
lines << "Upload:"
|
|
62
|
+
lines << " URL: #{import.upload_url}"
|
|
63
|
+
lines << " Valid Until: #{import.upload_valid_until}" if import.upload_valid_until
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
lines << ""
|
|
67
|
+
lines << "Created: #{import.created_at}" if import.created_at
|
|
68
|
+
lines << "Updated: #{import.updated_at}" if import.updated_at
|
|
69
|
+
|
|
70
|
+
lines.join("\n")
|
|
71
|
+
end
|
|
72
|
+
private_class_method :format_as_pretty
|
|
73
|
+
|
|
37
74
|
def self.format_as_text(import)
|
|
38
75
|
lines = []
|
|
39
76
|
lines << "Import: #{import.id}"
|
|
@@ -68,6 +105,37 @@ module Exa
|
|
|
68
105
|
end
|
|
69
106
|
private_class_method :format_as_text
|
|
70
107
|
|
|
108
|
+
def self.format_collection_as_pretty(collection)
|
|
109
|
+
lines = []
|
|
110
|
+
lines << "Imports (#{collection.data.length} items)"
|
|
111
|
+
lines << ""
|
|
112
|
+
|
|
113
|
+
collection.data.each_with_index do |imp, idx|
|
|
114
|
+
lines << "" if idx > 0 # Blank line between imports
|
|
115
|
+
|
|
116
|
+
lines << "Import ID: #{imp.id}"
|
|
117
|
+
lines << "Status: #{imp.status}"
|
|
118
|
+
lines << "Title: #{imp.title}" if imp.title
|
|
119
|
+
lines << "Format: #{imp.format}" if imp.format
|
|
120
|
+
lines << "Entity Type: #{imp.entity['type']}" if imp.entity && imp.entity['type']
|
|
121
|
+
lines << "Count: #{imp.count}" if imp.count
|
|
122
|
+
lines << "Created: #{imp.created_at}" if imp.created_at
|
|
123
|
+
lines << "Updated: #{imp.updated_at}" if imp.updated_at
|
|
124
|
+
|
|
125
|
+
if imp.status == 'failed'
|
|
126
|
+
lines << "Failed Reason: #{imp.failed_reason}" if imp.failed_reason
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if collection.has_more
|
|
131
|
+
lines << ""
|
|
132
|
+
lines << "Next Cursor: #{collection.next_cursor}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
lines.join("\n")
|
|
136
|
+
end
|
|
137
|
+
private_class_method :format_collection_as_pretty
|
|
138
|
+
|
|
71
139
|
def self.format_collection_as_text(collection)
|
|
72
140
|
lines = ["Imports (#{collection.data.length} items):"]
|
|
73
141
|
collection.data.each do |imp|
|
|
@@ -9,7 +9,7 @@ module Exa
|
|
|
9
9
|
when "json"
|
|
10
10
|
JSON.generate(monitor.to_h)
|
|
11
11
|
when "pretty"
|
|
12
|
-
|
|
12
|
+
format_as_pretty(monitor)
|
|
13
13
|
when "text"
|
|
14
14
|
format_as_text(monitor)
|
|
15
15
|
when "toon"
|
|
@@ -24,7 +24,7 @@ module Exa
|
|
|
24
24
|
when "json"
|
|
25
25
|
JSON.generate(collection.to_h)
|
|
26
26
|
when "pretty"
|
|
27
|
-
|
|
27
|
+
format_collection_as_pretty(collection)
|
|
28
28
|
when "text"
|
|
29
29
|
format_collection_as_text(collection)
|
|
30
30
|
when "toon"
|
|
@@ -34,6 +34,35 @@ module Exa
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.format_as_pretty(monitor)
|
|
38
|
+
lines = []
|
|
39
|
+
lines << "Monitor ID: #{monitor.id}"
|
|
40
|
+
lines << "Webset ID: #{monitor.webset_id}" if monitor.webset_id
|
|
41
|
+
lines << "Status: #{monitor.status}"
|
|
42
|
+
|
|
43
|
+
if monitor.cadence
|
|
44
|
+
lines << ""
|
|
45
|
+
lines << "Cadence:"
|
|
46
|
+
lines << " Cron: #{monitor.cadence['cron']}" if monitor.cadence['cron']
|
|
47
|
+
lines << " Timezone: #{monitor.cadence['timezone']}" if monitor.cadence['timezone']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if monitor.behavior
|
|
51
|
+
lines << ""
|
|
52
|
+
lines << "Behavior:"
|
|
53
|
+
lines << " Type: #{monitor.behavior['type']}" if monitor.behavior['type']
|
|
54
|
+
lines << " Query: #{monitor.behavior['query']}" if monitor.behavior['query']
|
|
55
|
+
lines << " Count: #{monitor.behavior['count']}" if monitor.behavior['count']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
lines << ""
|
|
59
|
+
lines << "Created: #{monitor.created_at}" if monitor.created_at
|
|
60
|
+
lines << "Updated: #{monitor.updated_at}" if monitor.updated_at
|
|
61
|
+
|
|
62
|
+
lines.join("\n")
|
|
63
|
+
end
|
|
64
|
+
private_class_method :format_as_pretty
|
|
65
|
+
|
|
37
66
|
def self.format_as_text(monitor)
|
|
38
67
|
lines = []
|
|
39
68
|
lines << "Monitor: #{monitor.id}"
|
|
@@ -60,6 +89,40 @@ module Exa
|
|
|
60
89
|
end
|
|
61
90
|
private_class_method :format_as_text
|
|
62
91
|
|
|
92
|
+
def self.format_collection_as_pretty(collection)
|
|
93
|
+
lines = []
|
|
94
|
+
lines << "Monitors (#{collection.data.length} items)"
|
|
95
|
+
lines << ""
|
|
96
|
+
|
|
97
|
+
collection.data.each_with_index do |mon, idx|
|
|
98
|
+
lines << "" if idx > 0 # Blank line between monitors
|
|
99
|
+
|
|
100
|
+
lines << "Monitor ID: #{mon['id']}"
|
|
101
|
+
lines << "Webset ID: #{mon['websetId']}" if mon['websetId']
|
|
102
|
+
lines << "Status: #{mon['status']}"
|
|
103
|
+
|
|
104
|
+
if mon['cadence']
|
|
105
|
+
lines << "Cron: #{mon['cadence']['cron']}" if mon['cadence']['cron']
|
|
106
|
+
lines << "Timezone: #{mon['cadence']['timezone']}" if mon['cadence']['timezone']
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if mon['behavior']
|
|
110
|
+
lines << "Query: #{mon['behavior']['query']}" if mon['behavior']['query']
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
lines << "Created: #{mon['createdAt']}" if mon['createdAt']
|
|
114
|
+
lines << "Updated: #{mon['updatedAt']}" if mon['updatedAt']
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if collection.has_more
|
|
118
|
+
lines << ""
|
|
119
|
+
lines << "Next Cursor: #{collection.next_cursor}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
lines.join("\n")
|
|
123
|
+
end
|
|
124
|
+
private_class_method :format_collection_as_pretty
|
|
125
|
+
|
|
63
126
|
def self.format_collection_as_text(collection)
|
|
64
127
|
lines = ["Monitors (#{collection.data.length} items):"]
|
|
65
128
|
collection.data.each do |mon|
|
|
@@ -9,7 +9,7 @@ module Exa
|
|
|
9
9
|
when "json"
|
|
10
10
|
JSON.generate(monitor_run.to_h)
|
|
11
11
|
when "pretty"
|
|
12
|
-
|
|
12
|
+
format_as_pretty(monitor_run)
|
|
13
13
|
when "text"
|
|
14
14
|
format_as_text(monitor_run)
|
|
15
15
|
when "toon"
|
|
@@ -24,7 +24,7 @@ module Exa
|
|
|
24
24
|
when "json"
|
|
25
25
|
JSON.generate(collection.to_h)
|
|
26
26
|
when "pretty"
|
|
27
|
-
|
|
27
|
+
format_collection_as_pretty(collection)
|
|
28
28
|
when "text"
|
|
29
29
|
format_collection_as_text(collection)
|
|
30
30
|
when "toon"
|
|
@@ -34,6 +34,27 @@ module Exa
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.format_as_pretty(monitor_run)
|
|
38
|
+
lines = []
|
|
39
|
+
lines << "Monitor Run ID: #{monitor_run.id}"
|
|
40
|
+
lines << "Monitor ID: #{monitor_run.monitor_id}" if monitor_run.monitor_id
|
|
41
|
+
lines << "Status: #{monitor_run.status}"
|
|
42
|
+
|
|
43
|
+
lines << ""
|
|
44
|
+
lines << "Created: #{monitor_run.created_at}" if monitor_run.created_at
|
|
45
|
+
lines << "Updated: #{monitor_run.updated_at}" if monitor_run.updated_at
|
|
46
|
+
lines << "Completed: #{monitor_run.completed_at}" if monitor_run.completed_at
|
|
47
|
+
|
|
48
|
+
if monitor_run.failed?
|
|
49
|
+
lines << ""
|
|
50
|
+
lines << "Failed: #{monitor_run.failed_at}" if monitor_run.failed_at
|
|
51
|
+
lines << "Reason: #{monitor_run.failed_reason}" if monitor_run.failed_reason
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
lines.join("\n")
|
|
55
|
+
end
|
|
56
|
+
private_class_method :format_as_pretty
|
|
57
|
+
|
|
37
58
|
def self.format_as_text(monitor_run)
|
|
38
59
|
lines = []
|
|
39
60
|
lines << "Monitor Run: #{monitor_run.id}"
|
|
@@ -53,6 +74,36 @@ module Exa
|
|
|
53
74
|
end
|
|
54
75
|
private_class_method :format_as_text
|
|
55
76
|
|
|
77
|
+
def self.format_collection_as_pretty(collection)
|
|
78
|
+
lines = []
|
|
79
|
+
lines << "Monitor Runs (#{collection.data.length} items)"
|
|
80
|
+
lines << ""
|
|
81
|
+
|
|
82
|
+
collection.data.each_with_index do |run, idx|
|
|
83
|
+
lines << "" if idx > 0 # Blank line between runs
|
|
84
|
+
|
|
85
|
+
lines << "Monitor Run ID: #{run['id']}"
|
|
86
|
+
lines << "Monitor ID: #{run['monitorId']}" if run['monitorId']
|
|
87
|
+
lines << "Status: #{run['status']}"
|
|
88
|
+
lines << "Created: #{run['createdAt']}" if run['createdAt']
|
|
89
|
+
lines << "Updated: #{run['updatedAt']}" if run['updatedAt']
|
|
90
|
+
lines << "Completed: #{run['completedAt']}" if run['completedAt']
|
|
91
|
+
|
|
92
|
+
if run['status'] == 'failed'
|
|
93
|
+
lines << "Failed: #{run['failedAt']}" if run['failedAt']
|
|
94
|
+
lines << "Reason: #{run['failedReason']}" if run['failedReason']
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if collection.has_more
|
|
99
|
+
lines << ""
|
|
100
|
+
lines << "Next Cursor: #{collection.next_cursor}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
lines.join("\n")
|
|
104
|
+
end
|
|
105
|
+
private_class_method :format_collection_as_pretty
|
|
106
|
+
|
|
56
107
|
def self.format_collection_as_text(collection)
|
|
57
108
|
lines = ["Monitor Runs (#{collection.data.length} items):"]
|
|
58
109
|
collection.data.each do |run|
|
|
@@ -24,7 +24,7 @@ module Exa
|
|
|
24
24
|
when "json"
|
|
25
25
|
JSON.generate(collection.to_h)
|
|
26
26
|
when "pretty"
|
|
27
|
-
|
|
27
|
+
format_collection_as_pretty(collection)
|
|
28
28
|
when "text"
|
|
29
29
|
format_collection_as_text(collection)
|
|
30
30
|
when "toon"
|
|
@@ -66,6 +66,66 @@ module Exa
|
|
|
66
66
|
lines.join("\n")
|
|
67
67
|
end
|
|
68
68
|
private_class_method :format_collection_as_text
|
|
69
|
+
|
|
70
|
+
def self.format_collection_as_pretty(collection)
|
|
71
|
+
lines = []
|
|
72
|
+
|
|
73
|
+
# Header with count and pagination info
|
|
74
|
+
header = "Websets (#{collection.data.length} items)"
|
|
75
|
+
header += " - Page #{collection.has_more ? '1 of many' : '1 of 1'}" if collection.data.any?
|
|
76
|
+
lines << header
|
|
77
|
+
|
|
78
|
+
if collection.has_more
|
|
79
|
+
lines << "Next Cursor: #{collection.next_cursor}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
lines << ""
|
|
83
|
+
|
|
84
|
+
# Format each webset
|
|
85
|
+
collection.data.each_with_index do |ws, idx|
|
|
86
|
+
lines << "" if idx > 0 # Blank line between websets
|
|
87
|
+
|
|
88
|
+
lines << "Webset ID: #{ws['id']}"
|
|
89
|
+
lines << "Status: #{ws['status']}"
|
|
90
|
+
lines << "Title: #{ws['title']}" if ws['title']
|
|
91
|
+
lines << "External ID: #{ws['externalId']}" if ws['externalId']
|
|
92
|
+
lines << "Created: #{ws['createdAt']}" if ws['createdAt']
|
|
93
|
+
lines << "Updated: #{ws['updatedAt']}" if ws['updatedAt']
|
|
94
|
+
|
|
95
|
+
# Searches
|
|
96
|
+
if ws['searches'] && !ws['searches'].empty?
|
|
97
|
+
lines << ""
|
|
98
|
+
lines << "Searches (#{ws['searches'].length}):"
|
|
99
|
+
ws['searches'].each do |search|
|
|
100
|
+
status_indicator = case search['status']
|
|
101
|
+
when 'completed' then '✓'
|
|
102
|
+
when 'running' then '→'
|
|
103
|
+
when 'failed' then '✗'
|
|
104
|
+
else '•'
|
|
105
|
+
end
|
|
106
|
+
lines << " #{status_indicator} #{search['query']} (#{search['status']})" if search['query']
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Enrichments
|
|
111
|
+
if ws['enrichments'] && !ws['enrichments'].empty?
|
|
112
|
+
lines << "Enrichments: #{ws['enrichments'].length}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Monitors
|
|
116
|
+
if ws['monitors'] && !ws['monitors'].empty?
|
|
117
|
+
lines << "Monitors: #{ws['monitors'].length}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Imports
|
|
121
|
+
if ws['imports'] && !ws['imports'].empty?
|
|
122
|
+
lines << "Imports: #{ws['imports'].length}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
lines.join("\n")
|
|
127
|
+
end
|
|
128
|
+
private_class_method :format_collection_as_pretty
|
|
69
129
|
end
|
|
70
130
|
end
|
|
71
131
|
end
|