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.

Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +24 -0
  3. data/agdata.gemspec +24 -0
  4. data/bin/agdata +4 -0
  5. data/lib/agdata_cli.rb +169 -0
  6. 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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'agdata_cli'
3
+
4
+ AgdataCli.main(ARGV)
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: []