agdata 0.1.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.
Potentially problematic release.
This version of agdata might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/README.md +24 -0
- data/agdata.gemspec +24 -0
- data/bin/agdata +4 -0
- data/lib/agdata_cli.rb +169 -0
- metadata +58 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c8d6174a7bf364de8b95f144a7262f719c122afc0af6e0c217219487dba5aead
|
|
4
|
+
data.tar.gz: 4d9b04203887168afdfeee1360a17a96e257e487186d362dc805e2cd900c9c31
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8d331a764b05f724c6e399c37518b752a3992fe51fd9b8206d30f12867e16154e5535c52f6ece1190be9383b9e55c8ae58e4d0d82d7c3292c2337119f32c9ca3
|
|
7
|
+
data.tar.gz: 2ed3631a0ef9a854deebe7e96dba6277af7a4f3385e75d6267a6d66c1efc2c5e562079aa2d186b4202c822bb69cbea24b6ec4e3ee8a5578ea3f974af2501bddb
|
data/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# agdata
|
|
2
|
+
|
|
3
|
+
CLI for https://agdata.shveik.dev (the same code as `ag-cli`, which covers all five products).
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
gem install agdata # then: agdata endpoints
|
|
7
|
+
agdata endpoints
|
|
8
|
+
agdata docs
|
|
9
|
+
agdata quote <endpoint> '{...}'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Commands: endpoints, docs, pricing, openapi, health, quote, call. It never holds keys and never signs a payment.
|
|
13
|
+
|
|
14
|
+
## The shveik.dev agent products
|
|
15
|
+
|
|
16
|
+
Pay-per-call services for AI agents, paid in stablecoins with x402 or MPP, no accounts or API keys. Built by [shveik.dev](https://shveik.dev).
|
|
17
|
+
|
|
18
|
+
- [agdata](https://agdata.shveik.dev): fresh web data (any URL as Markdown, search, maps, reviews, jobs, social)
|
|
19
|
+
- [agproxy](https://agproxy.shveik.dev): residential proxy packs and a raw fetch
|
|
20
|
+
- [agvps](https://agvps.shveik.dev): Linux servers by the month
|
|
21
|
+
- [agmail](https://agmail.shveik.dev): receive-only agent inboxes
|
|
22
|
+
- [agpay](https://agpay.shveik.dev): custodial hold between two agents
|
|
23
|
+
|
|
24
|
+
Docs for agents: `/agents.md` on each site. Homepage: https://shveik.dev
|
data/agdata.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Gem::Specification.new do |spec|
|
|
2
|
+
spec.name = 'agdata'
|
|
3
|
+
spec.version = '0.1.1'
|
|
4
|
+
spec.summary = 'CLI for agdata.shveik.dev (one of the shveik.dev agent products; same code as ag-cli)'
|
|
5
|
+
spec.description = 'Zero-dependency CLI for agdata.shveik.dev, one of the shveik.dev agent products: list endpoints, show 402 quotes, send payment-signed calls. Same code as ag-cli.'
|
|
6
|
+
spec.authors = ['shveik.dev']
|
|
7
|
+
spec.homepage = 'https://agdata.shveik.dev'
|
|
8
|
+
spec.license = 'MIT'
|
|
9
|
+
spec.required_ruby_version = '>= 2.7'
|
|
10
|
+
spec.files = ['agdata.gemspec', 'README.md', 'bin/agdata', 'lib/agdata_cli.rb']
|
|
11
|
+
spec.bindir = 'bin'
|
|
12
|
+
spec.executables = ['agdata']
|
|
13
|
+
spec.require_paths = ['lib']
|
|
14
|
+
spec.metadata = {
|
|
15
|
+
'homepage_uri' => 'https://agdata.shveik.dev',
|
|
16
|
+
'documentation_uri' => 'https://agdata.shveik.dev/agents.md',
|
|
17
|
+
'shveik_dev' => 'https://shveik.dev',
|
|
18
|
+
'agdata' => 'https://agdata.shveik.dev',
|
|
19
|
+
'agproxy' => 'https://agproxy.shveik.dev',
|
|
20
|
+
'agvps' => 'https://agvps.shveik.dev',
|
|
21
|
+
'agmail' => 'https://agmail.shveik.dev',
|
|
22
|
+
'agpay' => 'https://agpay.shveik.dev'
|
|
23
|
+
}
|
|
24
|
+
end
|
data/bin/agdata
ADDED
data/lib/agdata_cli.rb
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# ag: one small CLI for the shveik.dev agent products (agdata, agproxy, agvps, agmail, agpay).
|
|
2
|
+
# Zero dependencies, Ruby 2.7+ stdlib only. It lists endpoints, shows quotes and sends calls you have
|
|
3
|
+
# signed elsewhere; it never holds keys and never signs a payment.
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'openssl'
|
|
7
|
+
require 'timeout'
|
|
8
|
+
require 'uri'
|
|
9
|
+
|
|
10
|
+
module AgdataCli
|
|
11
|
+
VERSION = '0.1.1'
|
|
12
|
+
|
|
13
|
+
PRODUCTS = { 'data' => 'agdata', 'proxy' => 'agproxy', 'vps' => 'agvps', 'mail' => 'agmail', 'pay' => 'agpay' }.freeze
|
|
14
|
+
DEFAULT_PRODUCT = "agdata" # a product-named gem (agvps, agpay, ...) sets this
|
|
15
|
+
|
|
16
|
+
USAGE = <<~TEXT
|
|
17
|
+
ag <product> <command> [args]
|
|
18
|
+
|
|
19
|
+
products data proxy vps mail pay (or the full name: agdata, agproxy, ...)
|
|
20
|
+
commands endpoints list the API endpoints (JSON)
|
|
21
|
+
docs | pricing | openapi print the agent guide, the price list, the OpenAPI document
|
|
22
|
+
health liveness check
|
|
23
|
+
quote <endpoint> [json] send the call unpaid and print the 402 quote (exit 3)
|
|
24
|
+
call <endpoint> <json> --payment <signature>
|
|
25
|
+
send a call whose payment you signed elsewhere
|
|
26
|
+
(or set AG_PAYMENT); the CLI never holds keys
|
|
27
|
+
|
|
28
|
+
options --origin <url> override the service origin (or AG_ORIGIN)
|
|
29
|
+
|
|
30
|
+
example ag data endpoints
|
|
31
|
+
ag data quote unblock '{"url":"https://example.com"}'
|
|
32
|
+
exit 0 ok, 1 error, 2 usage, 3 payment required (quote printed)
|
|
33
|
+
TEXT
|
|
34
|
+
|
|
35
|
+
def self.fail_with(message, code = 1)
|
|
36
|
+
$stderr.write(message + "\n")
|
|
37
|
+
exit(code)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.present(value)
|
|
41
|
+
value.nil? || value.empty? ? nil : value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.take_option(args, name)
|
|
45
|
+
at = args.index(name)
|
|
46
|
+
return nil if at.nil?
|
|
47
|
+
fail_with("#{name} needs a value", 2) if at + 1 >= args.length
|
|
48
|
+
value = args[at + 1]
|
|
49
|
+
args.slice!(at, 2)
|
|
50
|
+
value
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.show(text)
|
|
54
|
+
$stdout.write(JSON.pretty_generate(JSON.parse(text)) + "\n")
|
|
55
|
+
rescue JSON::ParserError
|
|
56
|
+
$stdout.write(text.end_with?("\n") ? text : text + "\n")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns [status, response, text]; HTTP error statuses are returned, not raised.
|
|
60
|
+
def self.request(url, method = 'GET', body = nil, headers = {})
|
|
61
|
+
uri = URI.parse(url)
|
|
62
|
+
10.times do
|
|
63
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
64
|
+
http.use_ssl = uri.scheme == 'https'
|
|
65
|
+
http.open_timeout = 180
|
|
66
|
+
http.read_timeout = 180
|
|
67
|
+
req = method == 'POST' ? Net::HTTP::Post.new(uri.request_uri) : Net::HTTP::Get.new(uri.request_uri)
|
|
68
|
+
headers.each { |key, value| req[key] = value }
|
|
69
|
+
req.body = body unless body.nil?
|
|
70
|
+
response = http.request(req)
|
|
71
|
+
if response.is_a?(Net::HTTPRedirection) && response['location']
|
|
72
|
+
code = response.code.to_i
|
|
73
|
+
uri = URI.join(uri.to_s, response['location'])
|
|
74
|
+
if [301, 302, 303].include?(code)
|
|
75
|
+
method = 'GET'
|
|
76
|
+
body = nil
|
|
77
|
+
end
|
|
78
|
+
next
|
|
79
|
+
end
|
|
80
|
+
text = response.body.to_s.dup.force_encoding(Encoding::UTF_8).scrub
|
|
81
|
+
return [response.code.to_i, response, text]
|
|
82
|
+
end
|
|
83
|
+
fail_with('request failed: too many redirects')
|
|
84
|
+
rescue SocketError, SystemCallError, IOError, Timeout::Error, OpenSSL::SSL::SSLError, URI::Error => error
|
|
85
|
+
fail_with("request failed: #{error.message}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.get(origin, path)
|
|
89
|
+
status, _, text = request(origin + path)
|
|
90
|
+
fail_with("HTTP #{status} #{path}\n#{text}") if status >= 400
|
|
91
|
+
show(text)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.endpoint_path(name)
|
|
95
|
+
name.start_with?('/') ? name : "/x402/#{name}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.paid_request(origin, endpoint, body, extra_headers)
|
|
99
|
+
headers = { 'content-type' => 'application/json', 'user-agent' => "ag-cli/#{VERSION}" }.merge(extra_headers)
|
|
100
|
+
request(origin + endpoint_path(endpoint), 'POST', body, headers)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.show_quote(status, response, text)
|
|
104
|
+
$stdout.write("HTTP #{status}\n")
|
|
105
|
+
required = present(response['payment-required'])
|
|
106
|
+
authenticate = present(response['www-authenticate'])
|
|
107
|
+
if required
|
|
108
|
+
show(required.unpack1('m').dup.force_encoding(Encoding::UTF_8).scrub)
|
|
109
|
+
elsif authenticate
|
|
110
|
+
$stdout.write("WWW-Authenticate: #{authenticate}\n")
|
|
111
|
+
else
|
|
112
|
+
show(text)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.main(argv = nil)
|
|
117
|
+
args = (argv || ARGV).dup
|
|
118
|
+
origin_option = present(take_option(args, '--origin'))
|
|
119
|
+
payment = present(take_option(args, '--payment')) || present(ENV['AG_PAYMENT'])
|
|
120
|
+
full_names = PRODUCTS.values
|
|
121
|
+
if DEFAULT_PRODUCT && (args.empty? || (!PRODUCTS.key?(args[0]) && !full_names.include?(args[0]) && !%w[products help -h --help].include?(args[0])))
|
|
122
|
+
args.unshift(DEFAULT_PRODUCT)
|
|
123
|
+
end
|
|
124
|
+
if args.empty? || %w[help -h --help].include?(args[0])
|
|
125
|
+
$stdout.write(USAGE)
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
if args[0] == 'products'
|
|
129
|
+
PRODUCTS.each { |short, full| $stdout.write("#{short}\t#{full}\thttps://#{full}.shveik.dev\n") }
|
|
130
|
+
return
|
|
131
|
+
end
|
|
132
|
+
product = PRODUCTS[args[0]] || (full_names.include?(args[0]) ? args[0] : nil)
|
|
133
|
+
fail_with(%(unknown product "#{args[0]}"\n\n#{USAGE}), 2) unless product
|
|
134
|
+
origin = (origin_option || present(ENV['AG_ORIGIN']) || "https://#{product}.shveik.dev").sub(%r{/+\z}, '')
|
|
135
|
+
command = args.length > 1 ? args[1] : ''
|
|
136
|
+
rest = args.drop(2)
|
|
137
|
+
case command
|
|
138
|
+
when 'endpoints'
|
|
139
|
+
get(origin, '/v1')
|
|
140
|
+
when 'docs'
|
|
141
|
+
get(origin, '/agents.md')
|
|
142
|
+
when 'pricing'
|
|
143
|
+
get(origin, '/pricing.md')
|
|
144
|
+
when 'openapi'
|
|
145
|
+
get(origin, '/openapi.json')
|
|
146
|
+
when 'health'
|
|
147
|
+
get(origin, '/health')
|
|
148
|
+
when 'quote'
|
|
149
|
+
fail_with('quote needs an endpoint name or path', 2) if rest.empty?
|
|
150
|
+
status, response, text = paid_request(origin, rest[0], rest.length > 1 ? rest[1] : '{}', {})
|
|
151
|
+
show_quote(status, response, text)
|
|
152
|
+
exit(status == 402 ? 3 : status < 400 ? 0 : 1)
|
|
153
|
+
when 'call'
|
|
154
|
+
fail_with('call needs an endpoint and a JSON body', 2) if rest.length < 2
|
|
155
|
+
fail_with('a signed payment is required: --payment <signature> or AG_PAYMENT (sign the quote with your own wallet)', 2) unless payment
|
|
156
|
+
status, response, text = paid_request(origin, rest[0], rest[1], { 'PAYMENT-SIGNATURE' => payment })
|
|
157
|
+
if status == 402
|
|
158
|
+
show_quote(status, response, text)
|
|
159
|
+
exit(3)
|
|
160
|
+
end
|
|
161
|
+
show(text)
|
|
162
|
+
exit(status < 400 ? 0 : 1)
|
|
163
|
+
else
|
|
164
|
+
fail_with(%(unknown command "#{command}"\n\n#{USAGE}), 2)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
AgCli.main if __FILE__ == $PROGRAM_NAME
|
metadata
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: agdata
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- shveik.dev
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 'Zero-dependency CLI for agdata.shveik.dev, one of the shveik.dev agent
|
|
14
|
+
products: list endpoints, show 402 quotes, send payment-signed calls. Same code
|
|
15
|
+
as ag-cli.'
|
|
16
|
+
email:
|
|
17
|
+
executables:
|
|
18
|
+
- agdata
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- README.md
|
|
23
|
+
- agdata.gemspec
|
|
24
|
+
- bin/agdata
|
|
25
|
+
- lib/agdata_cli.rb
|
|
26
|
+
homepage: https://agdata.shveik.dev
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
metadata:
|
|
30
|
+
homepage_uri: https://agdata.shveik.dev
|
|
31
|
+
documentation_uri: https://agdata.shveik.dev/agents.md
|
|
32
|
+
shveik_dev: https://shveik.dev
|
|
33
|
+
agdata: https://agdata.shveik.dev
|
|
34
|
+
agproxy: https://agproxy.shveik.dev
|
|
35
|
+
agvps: https://agvps.shveik.dev
|
|
36
|
+
agmail: https://agmail.shveik.dev
|
|
37
|
+
agpay: https://agpay.shveik.dev
|
|
38
|
+
post_install_message:
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.7'
|
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements: []
|
|
53
|
+
rubygems_version: 3.5.22
|
|
54
|
+
signing_key:
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: CLI for agdata.shveik.dev (one of the shveik.dev agent products; same code
|
|
57
|
+
as ag-cli)
|
|
58
|
+
test_files: []
|