exploremyprofile 0.1.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 +7 -0
- data/exe/explore +13 -0
- data/lib/explore/cli.rb +349 -0
- data/lib/explore/version.rb +9 -0
- data/lib/explore.rb +4 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fbb95d414e36bede8a58108431a36c0a439a4bb56e92afbf6a62f866021a5a74
|
|
4
|
+
data.tar.gz: 3e5a41439c7d2e4cb7e640d632aa178731b4dd5ac9a19e872b1aec44982b6cad
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7a46d662af6e8926a4d591ed975a748b9c34bc81c3b8567f1a77b274b085d79bb8b4f7555d7ccfa5dc58449884dd6a8b402a90f5aee5b16a546335af506102cc
|
|
7
|
+
data.tar.gz: e552159bd69ebc39cb17a99c83b2be30fd231ac82fd1cc96f953d3adc742928d158d469da83d328bb4f30699a4365ce2081f08b9ad8bfea6e70a03ffc46d3ca4
|
data/exe/explore
ADDED
data/lib/explore/cli.rb
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module Explore
|
|
7
|
+
module Cli
|
|
8
|
+
class Error < StandardError
|
|
9
|
+
attr_reader :exit_code
|
|
10
|
+
|
|
11
|
+
def initialize(message, exit_code: 1)
|
|
12
|
+
super(message)
|
|
13
|
+
@exit_code = exit_code
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Client
|
|
18
|
+
OWNER_ONLY_PATH_PREFIXES = [
|
|
19
|
+
"/api/agent/v1/drafts",
|
|
20
|
+
"/api/agent/v1/onboarding/status",
|
|
21
|
+
"/api/agent/v1/manifest/next_actions",
|
|
22
|
+
"/api/agent/v1/notion/sync_status",
|
|
23
|
+
"/api/agent/v1/publish/preview",
|
|
24
|
+
"/api/agent/v1/content/create_draft",
|
|
25
|
+
"/api/agent/v1/content/propose_update"
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
def initialize(base_url:, api_key: nil)
|
|
29
|
+
@base_url = base_url
|
|
30
|
+
@api_key = api_key
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get(path, params: {})
|
|
34
|
+
request(Net::HTTP::Get, path, params:)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def post(path, params: {}, json: nil)
|
|
38
|
+
request(Net::HTTP::Post, path, params:, json:)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def patch(path, params: {}, json: nil)
|
|
42
|
+
request(Net::HTTP::Patch, path, params:, json:)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
attr_reader :base_url, :api_key
|
|
48
|
+
|
|
49
|
+
def request(klass, path, params:, json: nil)
|
|
50
|
+
uri = URI.join(base_url, path)
|
|
51
|
+
uri.query = URI.encode_www_form(params.compact) if params.compact.any?
|
|
52
|
+
|
|
53
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
54
|
+
http.use_ssl = uri.scheme == "https"
|
|
55
|
+
|
|
56
|
+
request = klass.new(uri)
|
|
57
|
+
request["Accept"] = "application/json"
|
|
58
|
+
request["Content-Type"] = "application/json" if json
|
|
59
|
+
request["X-API-Key"] = api_key if api_key.to_s != ""
|
|
60
|
+
request.body = JSON.generate(json) if json
|
|
61
|
+
|
|
62
|
+
response = http.request(request)
|
|
63
|
+
parsed = response.body.to_s.empty? ? {} : JSON.parse(response.body)
|
|
64
|
+
return parsed if response.code.to_i.between?(200, 299)
|
|
65
|
+
|
|
66
|
+
message = error_message_for(response:, parsed:, path:)
|
|
67
|
+
raise Error.new(message, exit_code: 1)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def error_message_for(response:, parsed:, path:)
|
|
71
|
+
return owner_only_unauthorized_message if response.code.to_i == 401 && owner_only_path?(path)
|
|
72
|
+
|
|
73
|
+
parsed.dig("error", "message") || "Request failed"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def owner_only_path?(path)
|
|
77
|
+
OWNER_ONLY_PATH_PREFIXES.any? { |prefix| path.start_with?(prefix) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def owner_only_unauthorized_message
|
|
81
|
+
"Unauthorized: this is an owner-only command. Use a signed-in session or pass EXPLORE_API_KEY/--api-key. " \
|
|
82
|
+
"The key is app-level, not account-specific, and lives in Rails credentials at api.v1_key."
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Runner
|
|
87
|
+
DEFAULT_BASE_URL = "http://127.0.0.1:3000".freeze
|
|
88
|
+
|
|
89
|
+
def initialize(argv:, env:, stdout:, stderr:, client_class: Client)
|
|
90
|
+
@argv = argv.dup
|
|
91
|
+
@env = env
|
|
92
|
+
@stdout = stdout
|
|
93
|
+
@stderr = stderr
|
|
94
|
+
@client_class = client_class
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def call
|
|
98
|
+
command = argv.shift
|
|
99
|
+
return emit_version if version_flag?(command)
|
|
100
|
+
raise Error.new(usage, exit_code: 2) if blank_value?(command)
|
|
101
|
+
|
|
102
|
+
case command
|
|
103
|
+
when "version"
|
|
104
|
+
emit_version
|
|
105
|
+
when "whoami"
|
|
106
|
+
options = parse_options(argv, require_input: false)
|
|
107
|
+
response = client(options).get("/api/agent/v1/profile", params: scope_params(options))
|
|
108
|
+
emit(output_for_whoami(response, options))
|
|
109
|
+
when "profile"
|
|
110
|
+
execute_read(argv.shift, "/api/agent/v1/profile")
|
|
111
|
+
when "content"
|
|
112
|
+
subcommand = argv.shift
|
|
113
|
+
return execute_read(subcommand, "/api/agent/v1/content") if subcommand == "list"
|
|
114
|
+
return execute_create_draft if subcommand == "create-draft"
|
|
115
|
+
return execute_propose_update if subcommand == "propose-update"
|
|
116
|
+
raise Error.new(usage, exit_code: 2)
|
|
117
|
+
when "drafts"
|
|
118
|
+
subcommand = argv.shift
|
|
119
|
+
return execute_read(subcommand, "/api/agent/v1/drafts") if subcommand == "list"
|
|
120
|
+
return execute_draft_inspect if subcommand == "inspect"
|
|
121
|
+
return execute_draft_apply if subcommand == "apply"
|
|
122
|
+
return execute_draft_apply_preview if subcommand == "apply-preview"
|
|
123
|
+
return execute_draft_update if subcommand == "update"
|
|
124
|
+
return execute_draft_archive if subcommand == "archive"
|
|
125
|
+
raise Error.new(usage, exit_code: 2)
|
|
126
|
+
when "onboarding"
|
|
127
|
+
execute_read(argv.shift, "/api/agent/v1/onboarding/status")
|
|
128
|
+
when "manifest"
|
|
129
|
+
execute_read(argv.shift, "/api/agent/v1/manifest/next_actions")
|
|
130
|
+
when "notion"
|
|
131
|
+
execute_read(argv.shift, "/api/agent/v1/notion/sync_status")
|
|
132
|
+
when "publish"
|
|
133
|
+
execute_read(argv.shift, "/api/agent/v1/publish/preview")
|
|
134
|
+
else
|
|
135
|
+
raise Error.new(usage, exit_code: 2)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
0
|
|
139
|
+
rescue Error => e
|
|
140
|
+
stderr.puts(e.message)
|
|
141
|
+
e.exit_code
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
attr_reader :argv, :env, :stdout, :stderr, :client_class
|
|
147
|
+
|
|
148
|
+
def execute_read(subcommand, path)
|
|
149
|
+
expected = {
|
|
150
|
+
"/api/agent/v1/profile" => "inspect",
|
|
151
|
+
"/api/agent/v1/onboarding/status" => "status",
|
|
152
|
+
"/api/agent/v1/manifest/next_actions" => "next-actions",
|
|
153
|
+
"/api/agent/v1/notion/sync_status" => "sync-status",
|
|
154
|
+
"/api/agent/v1/publish/preview" => "preview",
|
|
155
|
+
"/api/agent/v1/content" => "list",
|
|
156
|
+
"/api/agent/v1/drafts" => "list"
|
|
157
|
+
}.fetch(path)
|
|
158
|
+
raise Error.new(usage, exit_code: 2) unless subcommand == expected
|
|
159
|
+
|
|
160
|
+
options = parse_options(argv, require_input: false)
|
|
161
|
+
response = client(options).get(path, params: scope_params(options))
|
|
162
|
+
emit(format_output(response, options))
|
|
163
|
+
0
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def execute_propose_update
|
|
167
|
+
options = parse_options(argv, require_input: true)
|
|
168
|
+
input_path = options.fetch(:input)
|
|
169
|
+
payload = JSON.parse(File.read(input_path))
|
|
170
|
+
response = client(options).post(
|
|
171
|
+
"/api/agent/v1/content/propose_update",
|
|
172
|
+
params: scope_params(options),
|
|
173
|
+
json: payload
|
|
174
|
+
)
|
|
175
|
+
emit(format_output(response, options))
|
|
176
|
+
0
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def execute_create_draft
|
|
180
|
+
options = parse_options(argv, require_input: true)
|
|
181
|
+
input_path = options.fetch(:input)
|
|
182
|
+
payload = JSON.parse(File.read(input_path))
|
|
183
|
+
response = client(options).post(
|
|
184
|
+
"/api/agent/v1/content/create_draft",
|
|
185
|
+
params: scope_params(options),
|
|
186
|
+
json: payload
|
|
187
|
+
)
|
|
188
|
+
emit(format_output(response, options))
|
|
189
|
+
0
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def execute_draft_inspect
|
|
193
|
+
options = parse_options(argv, require_input: false, require_draft_id: true)
|
|
194
|
+
response = client(options).get(
|
|
195
|
+
"/api/agent/v1/drafts/#{options.fetch(:draft_id)}",
|
|
196
|
+
params: scope_params(options)
|
|
197
|
+
)
|
|
198
|
+
emit(format_output(response, options))
|
|
199
|
+
0
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def execute_draft_archive
|
|
203
|
+
options = parse_options(argv, require_input: false, require_draft_id: true)
|
|
204
|
+
response = client(options).post(
|
|
205
|
+
"/api/agent/v1/drafts/#{options.fetch(:draft_id)}/archive",
|
|
206
|
+
params: scope_params(options)
|
|
207
|
+
)
|
|
208
|
+
emit(format_output(response, options))
|
|
209
|
+
0
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def execute_draft_apply
|
|
213
|
+
options = parse_options(argv, require_input: false, require_draft_id: true)
|
|
214
|
+
response = client(options).post(
|
|
215
|
+
"/api/agent/v1/drafts/#{options.fetch(:draft_id)}/apply",
|
|
216
|
+
params: scope_params(options)
|
|
217
|
+
)
|
|
218
|
+
emit(format_output(response, options))
|
|
219
|
+
0
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def execute_draft_apply_preview
|
|
223
|
+
options = parse_options(argv, require_input: false, require_draft_id: true)
|
|
224
|
+
response = client(options).get(
|
|
225
|
+
"/api/agent/v1/drafts/#{options.fetch(:draft_id)}/apply_preview",
|
|
226
|
+
params: scope_params(options)
|
|
227
|
+
)
|
|
228
|
+
emit(format_output(response, options))
|
|
229
|
+
0
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def execute_draft_update
|
|
233
|
+
options = parse_options(argv, require_input: true, require_draft_id: true)
|
|
234
|
+
payload = JSON.parse(File.read(options.fetch(:input)))
|
|
235
|
+
response = client(options).patch(
|
|
236
|
+
"/api/agent/v1/drafts/#{options.fetch(:draft_id)}",
|
|
237
|
+
params: scope_params(options),
|
|
238
|
+
json: payload
|
|
239
|
+
)
|
|
240
|
+
emit(format_output(response, options))
|
|
241
|
+
0
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def parse_options(arguments, require_input:, require_draft_id: false)
|
|
245
|
+
options = {
|
|
246
|
+
base_url: env.fetch("EXPLORE_BASE_URL", DEFAULT_BASE_URL),
|
|
247
|
+
api_key: env["EXPLORE_API_KEY"],
|
|
248
|
+
account: env["EXPLORE_ACCOUNT"],
|
|
249
|
+
slug: env["EXPLORE_SLUG"],
|
|
250
|
+
json: false
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
parser = OptionParser.new do |opts|
|
|
254
|
+
opts.on("--base-url URL") { |value| options[:base_url] = value }
|
|
255
|
+
opts.on("--api-key TOKEN") { |value| options[:api_key] = value }
|
|
256
|
+
opts.on("--account ACCOUNT") { |value| options[:account] = value }
|
|
257
|
+
opts.on("--slug SLUG") { |value| options[:slug] = value }
|
|
258
|
+
opts.on("--draft ID") { |value| options[:draft_id] = value }
|
|
259
|
+
opts.on("--input PATH") { |value| options[:input] = value }
|
|
260
|
+
opts.on("--json") { options[:json] = true }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
parser.parse!(arguments)
|
|
264
|
+
raise Error.new("Missing --input", exit_code: 2) if require_input && blank_value?(options[:input])
|
|
265
|
+
raise Error.new("Missing --draft", exit_code: 2) if require_draft_id && blank_value?(options[:draft_id])
|
|
266
|
+
|
|
267
|
+
options
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def client(options)
|
|
271
|
+
client_class.new(base_url: options.fetch(:base_url), api_key: options[:api_key])
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def scope_params(options)
|
|
275
|
+
if present_value?(options[:slug])
|
|
276
|
+
{ slug: options[:slug] }
|
|
277
|
+
elsif options[:account].to_s.match?(/\A\d+\z/)
|
|
278
|
+
{ account_id: options[:account] }
|
|
279
|
+
elsif present_value?(options[:account])
|
|
280
|
+
{ account: options[:account] }
|
|
281
|
+
else
|
|
282
|
+
{}
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def format_output(response, options)
|
|
287
|
+
return JSON.pretty_generate(response) if options[:json]
|
|
288
|
+
|
|
289
|
+
JSON.pretty_generate(response)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def output_for_whoami(response, options)
|
|
293
|
+
return JSON.pretty_generate(response) if options[:json]
|
|
294
|
+
|
|
295
|
+
[
|
|
296
|
+
"Account: #{response.dig("profile", "name")}",
|
|
297
|
+
"Slug: #{response["account_slug"]}",
|
|
298
|
+
"Context: #{response.dig("context", "mode")}",
|
|
299
|
+
"Profile: #{response.dig("profile", "public_url")}"
|
|
300
|
+
].join("\n")
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def emit(message)
|
|
304
|
+
stdout.puts(message)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def emit_version
|
|
308
|
+
stdout.puts(Explore::VERSION)
|
|
309
|
+
0
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def blank_value?(value)
|
|
313
|
+
value.nil? || value.respond_to?(:empty?) && value.empty?
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def present_value?(value)
|
|
317
|
+
!blank_value?(value)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def version_flag?(value)
|
|
321
|
+
%w[--version -v].include?(value)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def usage
|
|
325
|
+
<<~TEXT
|
|
326
|
+
Usage:
|
|
327
|
+
explore version
|
|
328
|
+
explore whoami [--account <id-or-slug>] [--slug <slug>] [--json]
|
|
329
|
+
explore profile inspect [--account <id-or-slug>] [--slug <slug>] [--json]
|
|
330
|
+
explore content list [--account <id-or-slug>] [--slug <slug>] [--json]
|
|
331
|
+
explore content create-draft --account <id-or-slug> --input <file.json> [--json]
|
|
332
|
+
explore drafts list --account <id-or-slug> [--json]
|
|
333
|
+
explore drafts inspect --account <id-or-slug> --draft <id> [--json]
|
|
334
|
+
explore drafts apply --account <id-or-slug> --draft <id> [--json]
|
|
335
|
+
explore drafts apply-preview --account <id-or-slug> --draft <id> [--json]
|
|
336
|
+
explore drafts update --account <id-or-slug> --draft <id> --input <file.json> [--json]
|
|
337
|
+
explore drafts archive --account <id-or-slug> --draft <id> [--json]
|
|
338
|
+
explore onboarding status [--account <id-or-slug>] [--json]
|
|
339
|
+
explore manifest next-actions [--account <id-or-slug>] [--json]
|
|
340
|
+
explore notion sync-status [--account <id-or-slug>] [--json]
|
|
341
|
+
explore publish preview [--account <id-or-slug>] [--json]
|
|
342
|
+
explore content propose-update --account <id-or-slug> --input <file.json> [--json]
|
|
343
|
+
TEXT
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
CLI = Cli
|
|
349
|
+
end
|
data/lib/explore.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: exploremyprofile
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Johnny Butler
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Explore provides a terminal-friendly CLI for public-safe profile inspection
|
|
13
|
+
and owner draft workflows.
|
|
14
|
+
email:
|
|
15
|
+
- johnnybutler@example.com
|
|
16
|
+
executables:
|
|
17
|
+
- explore
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- exe/explore
|
|
22
|
+
- lib/explore.rb
|
|
23
|
+
- lib/explore/cli.rb
|
|
24
|
+
- lib/explore/version.rb
|
|
25
|
+
homepage: https://exploremyprofile.com/agent-setup
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata:
|
|
29
|
+
homepage_uri: https://exploremyprofile.com/agent-setup
|
|
30
|
+
documentation_uri: https://exploremyprofile.com/agents
|
|
31
|
+
source_code_uri: https://github.com/johnnybutler7/johnnybutler-com
|
|
32
|
+
rubygems_mfa_required: 'true'
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 3.2.0
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubygems_version: 4.0.6
|
|
48
|
+
specification_version: 4
|
|
49
|
+
summary: Terminal CLI for the Explore agent API
|
|
50
|
+
test_files: []
|