immersivecommons 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/LICENSE +21 -0
- data/README.md +67 -0
- data/lib/immersivecommons/client.rb +252 -0
- data/lib/immersivecommons/generated.rb +291 -0
- data/lib/immersivecommons/openapi.json +2951 -0
- data/lib/immersivecommons/version.rb +5 -0
- data/lib/immersivecommons.rb +11 -0
- metadata +58 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 64529458b7a3d2d3702de67e334dfa5bc67ad9a1d18dff908c529414caa32ebf
|
|
4
|
+
data.tar.gz: 699bba540214da82cd7aedc674e90afae0f2798b5c6ed68d5ee7d0356f8e1f85
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 15feca82dd9b11b815209fe3e784ea7563538fda457c3a9eac5a20dec79d35c141a31735c50d2b0d4588565cabf67b7158c61379ede3dd109fd386a007627e8c
|
|
7
|
+
data.tar.gz: 22b1954531b9781c575da9f38196189443e13ff912cfeb2a5b2bd32406a18bf0af441f52361a6059f63093076ee67da4503ef4c16e487939031e5eb56d0892df
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Immersive Commons
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# immersivecommons (Ruby) — Immersive Commons client
|
|
2
|
+
|
|
3
|
+
Thin, spec-derived Ruby client for the [Immersive Commons](https://www.immersivecommons.com) Agent REST API. Immersive Commons is Floor 10 of Frontier Tower, a members-run AI builder space in San Francisco — this gem gives Ruby agents and services typed access to events & RSVPs, the members directory, resource booking, research Q&A, the donor wall, and RFC 8628 device-code token minting.
|
|
4
|
+
|
|
5
|
+
- **Homepage:** <https://www.immersivecommons.com>
|
|
6
|
+
- **Developer docs:** <https://www.immersivecommons.com/developers>
|
|
7
|
+
- **OpenAPI spec:** <https://www.immersivecommons.com/openapi.json> (vendored here as `lib/immersivecommons/openapi.json`)
|
|
8
|
+
- **Siblings:** [`@immersivecommons/sdk`](https://www.npmjs.com/package/@immersivecommons/sdk) (npm), [`immersivecommons`](https://pypi.org/project/immersivecommons/) (PyPI), [`ic-go`](https://pkg.go.dev/github.com/immersive-commons/ic-go) (Go)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
gem install immersivecommons
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Zero runtime dependencies — stdlib `net/http` only. Ruby >= 3.0.
|
|
17
|
+
|
|
18
|
+
## Use
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require "immersivecommons"
|
|
22
|
+
|
|
23
|
+
# Public reads need no token.
|
|
24
|
+
ic = ImmersiveCommons::Client.new
|
|
25
|
+
events = ic.list_upcoming_events(limit: 5)
|
|
26
|
+
puts events["count"]
|
|
27
|
+
|
|
28
|
+
# Authenticated calls take an agent bearer token (agt_...).
|
|
29
|
+
authed = ImmersiveCommons::Client.new(token: "agt_your_token")
|
|
30
|
+
puts authed.setup_check
|
|
31
|
+
|
|
32
|
+
# Writes that the spec marks idempotent accept an Idempotency-Key.
|
|
33
|
+
receipt = authed.rsvp_to_event("https://luma.com/your-event",
|
|
34
|
+
email: "you@example.com",
|
|
35
|
+
name: "Your Name",
|
|
36
|
+
idempotency_key: "rsvp-key-1")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Minting a token (device-code grant)
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
res = ic.authorize(["events:rsvp"], client_name: "my-agent",
|
|
43
|
+
on_prompt: ->(start) {
|
|
44
|
+
puts "Visit #{start['verify_url']} and enter code #{start['user_code']}"
|
|
45
|
+
})
|
|
46
|
+
ic.token = res["token"]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A human approves the grant in the browser; the loop polls until `completed`. Pass `sandbox: true` to `Client.new` (or `authorize`) to mint a sandbox token whose writes return simulated receipts.
|
|
50
|
+
|
|
51
|
+
## How it derives from the spec
|
|
52
|
+
|
|
53
|
+
`lib/immersivecommons/generated.rb` is emitted from the vendored `openapi.json` by `scripts/generate.py`: an operations table (operationId → method, path, query params, scopes, auth, idempotency, sandbox behavior). The transport is table-driven — one generic `call(operation_id, ...)` builds the URL, attaches the bearer token, and adds an `Idempotency-Key` only on operations the spec marks idempotent. The named methods are facades over that one call, so they cannot drift from the spec. Errors raise `ImmersiveCommons::ApiError` with `status`, `error_kind`, and `retry_after_seconds`.
|
|
54
|
+
|
|
55
|
+
23 operations are in the table; the 4 Clerk-cookie-only operations (`tailAgentEvents`, `getMyTier`, `requestTier`, `cancelTierRequest`) have no facade methods — agents use the equivalent MCP tools instead. `batchPublicReads` is reachable via the generic `call`.
|
|
56
|
+
|
|
57
|
+
## Test
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
ruby -Ilib test/test_client.rb
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Mock transport, no network, no live writes.
|
|
64
|
+
|
|
65
|
+
## Source of truth
|
|
66
|
+
|
|
67
|
+
This gem is generated from and mirrored out of the [immersivecommons.com](https://www.immersivecommons.com) site repo; skills and sibling SDKs live in [immersive-commons/ic-skills](https://github.com/immersive-commons/ic-skills). MIT license.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Thin, table-driven client for the Immersive Commons Agent REST API.
|
|
4
|
+
#
|
|
5
|
+
# `call` is the whole transport: it looks the operation up in the generated
|
|
6
|
+
# OPERATIONS table, attaches auth + Idempotency-Key when the spec allows, and
|
|
7
|
+
# sends the request. The named methods are typed facades over `call` — they
|
|
8
|
+
# add no routing of their own, so they cannot drift from the spec. Zero
|
|
9
|
+
# dependencies (stdlib net/http only).
|
|
10
|
+
|
|
11
|
+
require "json"
|
|
12
|
+
require "net/http"
|
|
13
|
+
require "timeout"
|
|
14
|
+
require "uri"
|
|
15
|
+
|
|
16
|
+
module ImmersiveCommons
|
|
17
|
+
# Raised on a non-2xx response. Carries the parsed error body + metadata.
|
|
18
|
+
class ApiError < StandardError
|
|
19
|
+
attr_reader :status, :operation_id, :body, :error_kind, :retry_after_seconds
|
|
20
|
+
|
|
21
|
+
def initialize(status, operation_id, body)
|
|
22
|
+
@status = status
|
|
23
|
+
@operation_id = operation_id
|
|
24
|
+
@body = body.is_a?(Hash) ? body : {}
|
|
25
|
+
@error_kind = @body["error_kind"].is_a?(String) ? @body["error_kind"] : nil
|
|
26
|
+
ra = @body["retry_after_seconds"]
|
|
27
|
+
@retry_after_seconds = ra.is_a?(Integer) ? ra : nil
|
|
28
|
+
|
|
29
|
+
err = @body["error"]
|
|
30
|
+
msg = if err.is_a?(String)
|
|
31
|
+
err
|
|
32
|
+
elsif err.is_a?(Hash)
|
|
33
|
+
[err["code"], err["message"]].compact.join(": ")
|
|
34
|
+
end
|
|
35
|
+
msg = @body["message"] if (msg.nil? || msg.empty?) && @body["message"].is_a?(String)
|
|
36
|
+
text = "#{operation_id} failed (HTTP #{status})"
|
|
37
|
+
text += ": #{msg}" if msg && !msg.empty?
|
|
38
|
+
super(text)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Client
|
|
43
|
+
attr_reader :base_url, :sandbox, :user_agent
|
|
44
|
+
attr_accessor :token
|
|
45
|
+
|
|
46
|
+
# transport: an object responding to call(method, url, headers, body) ->
|
|
47
|
+
# [status, headers, body_text]. Injectable for tests; defaults to Net::HTTP.
|
|
48
|
+
def initialize(token: nil, base_url: DEFAULT_BASE_URL, sandbox: false,
|
|
49
|
+
timeout: 30, transport: nil, user_agent: nil)
|
|
50
|
+
@base_url = base_url.sub(%r{/+\z}, "")
|
|
51
|
+
@token = token
|
|
52
|
+
# Documentation flag only: a sandbox token already behaves sandbox
|
|
53
|
+
# server-side. This just makes authorize request one by default.
|
|
54
|
+
@sandbox = sandbox
|
|
55
|
+
@user_agent = user_agent || "immersivecommons-ruby/#{API_VERSION}"
|
|
56
|
+
@transport = transport || default_transport(timeout)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------- generic
|
|
60
|
+
|
|
61
|
+
def call(operation_id, query: nil, body: nil, idempotency_key: nil, token: nil)
|
|
62
|
+
spec = OPERATIONS[operation_id]
|
|
63
|
+
raise ArgumentError, "Unknown operationId: #{operation_id}" unless spec
|
|
64
|
+
|
|
65
|
+
headers = { "accept" => "application/json", "user-agent" => @user_agent }
|
|
66
|
+
tok = token || @token
|
|
67
|
+
headers["authorization"] = "Bearer #{tok}" if tok
|
|
68
|
+
|
|
69
|
+
data = nil
|
|
70
|
+
if spec[:has_body] && !body.nil?
|
|
71
|
+
headers["content-type"] = "application/json"
|
|
72
|
+
data = JSON.generate(body)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if idempotency_key
|
|
76
|
+
unless spec[:idempotent]
|
|
77
|
+
raise ArgumentError, "#{operation_id} does not accept an Idempotency-Key"
|
|
78
|
+
end
|
|
79
|
+
headers["idempotency-key"] = idempotency_key
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
url = @base_url + spec[:path] + query_string(query)
|
|
83
|
+
status, _resp_headers, text = @transport.call(spec[:method], url, headers, data)
|
|
84
|
+
parsed = begin
|
|
85
|
+
text && !text.empty? ? JSON.parse(text) : nil
|
|
86
|
+
rescue JSON::ParserError
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
unless (200..299).cover?(status)
|
|
90
|
+
raise ApiError.new(status, operation_id, parsed || { "error" => text })
|
|
91
|
+
end
|
|
92
|
+
parsed
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# ------------------------------------------------------------------ reads
|
|
96
|
+
|
|
97
|
+
def list_upcoming_events(limit: nil)
|
|
98
|
+
call("listUpcomingEvents", query: limit.nil? ? nil : { "limit" => limit })
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def get_event_by_luma_url(luma)
|
|
102
|
+
call("getEventByLumaUrl", query: { "luma" => luma })
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def search_directory(q: nil, limit: nil)
|
|
106
|
+
call("searchDirectory", query: { "q" => q, "limit" => limit })
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def list_resources
|
|
110
|
+
call("listResources")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def get_my_activity(limit: nil)
|
|
114
|
+
call("getMyActivity", query: limit.nil? ? nil : { "limit" => limit })
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def get_my_leaderboard_status
|
|
118
|
+
call("getMyLeaderboardStatus")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def get_donor_wall(limit: nil)
|
|
122
|
+
call("getDonorWall", query: limit.nil? ? nil : { "limit" => limit })
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def setup_check
|
|
126
|
+
call("setupCheck")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# ----------------------------------------------------------------- writes
|
|
130
|
+
|
|
131
|
+
def rsvp_to_event(event_url, email:, name: nil, idempotency_key: nil)
|
|
132
|
+
body = { "event_url" => event_url, "email" => email }
|
|
133
|
+
body["name"] = name unless name.nil?
|
|
134
|
+
call("rsvpToEvent", body: body, idempotency_key: idempotency_key)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def request_event(event, idempotency_key: nil)
|
|
138
|
+
call("requestEvent", body: event, idempotency_key: idempotency_key)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def book_resource(resource_id, start_iso:, end_iso:, email:, purpose: nil, idempotency_key: nil)
|
|
142
|
+
body = { "resource_id" => resource_id, "start_iso" => start_iso,
|
|
143
|
+
"end_iso" => end_iso, "email" => email }
|
|
144
|
+
body["purpose"] = purpose unless purpose.nil?
|
|
145
|
+
call("bookResource", body: body, idempotency_key: idempotency_key)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def set_leaderboard_optin(opt_in)
|
|
149
|
+
call("setLeaderboardOptIn", body: { "optIn" => opt_in })
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def ask_research(q, k: nil, sources: nil, synthesize: nil, model: nil)
|
|
153
|
+
body = { "q" => q }
|
|
154
|
+
body["k"] = k unless k.nil?
|
|
155
|
+
body["sources"] = sources.to_a unless sources.nil?
|
|
156
|
+
body["synthesize"] = synthesize unless synthesize.nil?
|
|
157
|
+
body["model"] = model unless model.nil?
|
|
158
|
+
call("askResearch", body: body)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def submit_highlight_pending(story, idempotency_key: nil)
|
|
162
|
+
call("submitHighlightPending", body: story, idempotency_key: idempotency_key)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def submit_feedback(kind, message, **extra)
|
|
166
|
+
body = { "kind" => kind, "message" => message }
|
|
167
|
+
extra.each { |k, v| body[k.to_s] = v }
|
|
168
|
+
call("submitFeedback", body: body)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def revoke_own_token
|
|
172
|
+
call("revokeOwnToken")
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# --------------------------------------------------------- device-code auth
|
|
176
|
+
|
|
177
|
+
def start_signup(scopes, client_name: nil, sandbox: nil)
|
|
178
|
+
body = { "scopes" => scopes.to_a }
|
|
179
|
+
body["client_name"] = client_name unless client_name.nil?
|
|
180
|
+
want = sandbox.nil? ? @sandbox : sandbox
|
|
181
|
+
body["sandbox"] = true if want
|
|
182
|
+
call("startSignup", body: body)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def poll_signup(device_code)
|
|
186
|
+
call("pollSignup", query: { "device_code" => device_code })
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Runs the full RFC 8628 device-code grant and returns the minted token as
|
|
190
|
+
# { "token", "granted_scopes", "tier", "sandbox" }. Does not store the
|
|
191
|
+
# token on the client — assign client.token yourself if you want to.
|
|
192
|
+
# on_prompt receives the start response (user_code, verify_url).
|
|
193
|
+
# sleeper is injectable for tests.
|
|
194
|
+
def authorize(scopes, client_name: "immersivecommons-ruby", sandbox: nil,
|
|
195
|
+
on_prompt: nil, timeout_seconds: nil, sleeper: ->(s) { sleep(s) })
|
|
196
|
+
start = start_signup(scopes, client_name: client_name, sandbox: sandbox)
|
|
197
|
+
on_prompt&.call(start)
|
|
198
|
+
interval = [start["interval"].to_i, 1].max
|
|
199
|
+
deadline = monotonic_now + (timeout_seconds || start["expires_in"] || 900)
|
|
200
|
+
loop do
|
|
201
|
+
raise Timeout::Error, "Device-code grant timed out before approval." if monotonic_now > deadline
|
|
202
|
+
|
|
203
|
+
sleeper.call(interval)
|
|
204
|
+
poll = poll_signup(start.fetch("device_code"))
|
|
205
|
+
case poll["status"]
|
|
206
|
+
when "completed"
|
|
207
|
+
unless poll["agent_token"]
|
|
208
|
+
raise ApiError.new(500, "pollSignup", { "error" => "completed without token" })
|
|
209
|
+
end
|
|
210
|
+
return {
|
|
211
|
+
"token" => poll["agent_token"],
|
|
212
|
+
"granted_scopes" => poll["granted_scopes"] || [],
|
|
213
|
+
"tier" => poll["tier"],
|
|
214
|
+
"sandbox" => !!poll["sandbox"]
|
|
215
|
+
}
|
|
216
|
+
when "cancelled"
|
|
217
|
+
raise "Device-code grant cancelled: #{poll['reason']}".strip
|
|
218
|
+
end
|
|
219
|
+
# pending -> keep polling
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
private
|
|
224
|
+
|
|
225
|
+
def monotonic_now
|
|
226
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def query_string(query)
|
|
230
|
+
return "" if query.nil?
|
|
231
|
+
|
|
232
|
+
pairs = query.reject { |_k, v| v.nil? }
|
|
233
|
+
return "" if pairs.empty?
|
|
234
|
+
|
|
235
|
+
"?" + URI.encode_www_form(pairs)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def default_transport(timeout)
|
|
239
|
+
lambda do |method, url, headers, body|
|
|
240
|
+
uri = URI.parse(url)
|
|
241
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
242
|
+
http.use_ssl = uri.scheme == "https"
|
|
243
|
+
http.open_timeout = timeout
|
|
244
|
+
http.read_timeout = timeout
|
|
245
|
+
req = Net::HTTP.const_get(method.capitalize).new(uri, headers)
|
|
246
|
+
req.body = body if body
|
|
247
|
+
resp = http.request(req)
|
|
248
|
+
[resp.code.to_i, resp.each_header.to_h, resp.body || ""]
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# AUTO-GENERATED from openapi.json by scripts/generate.py — DO NOT EDIT.
|
|
4
|
+
# Spec version: 2026-07-16. Regenerate with: python scripts/generate.py
|
|
5
|
+
|
|
6
|
+
module ImmersiveCommons
|
|
7
|
+
API_VERSION = "2026-07-16"
|
|
8
|
+
DEFAULT_BASE_URL = "https://www.immersivecommons.com"
|
|
9
|
+
|
|
10
|
+
# operationId => operation spec (method, path, query, scopes, auth,
|
|
11
|
+
# idempotency, sandbox). The client transport is table-driven off this
|
|
12
|
+
# hash, so no method can describe a route the spec does not.
|
|
13
|
+
OPERATIONS = {
|
|
14
|
+
"listUpcomingEvents" => {
|
|
15
|
+
method: "GET",
|
|
16
|
+
path: "/api/events/upcoming",
|
|
17
|
+
query: [{ name: "limit", required: false }],
|
|
18
|
+
scopes: ["events:read_upcoming"],
|
|
19
|
+
requires_auth: false,
|
|
20
|
+
bearer_reachable: true,
|
|
21
|
+
browser_session: false,
|
|
22
|
+
idempotent: false,
|
|
23
|
+
has_body: false,
|
|
24
|
+
sandbox: nil
|
|
25
|
+
}.freeze,
|
|
26
|
+
"getEventByLumaUrl" => {
|
|
27
|
+
method: "GET",
|
|
28
|
+
path: "/api/events/get",
|
|
29
|
+
query: [{ name: "luma", required: true }],
|
|
30
|
+
scopes: [],
|
|
31
|
+
requires_auth: false,
|
|
32
|
+
bearer_reachable: true,
|
|
33
|
+
browser_session: false,
|
|
34
|
+
idempotent: false,
|
|
35
|
+
has_body: false,
|
|
36
|
+
sandbox: nil
|
|
37
|
+
}.freeze,
|
|
38
|
+
"batchPublicReads" => {
|
|
39
|
+
method: "POST",
|
|
40
|
+
path: "/api/batch",
|
|
41
|
+
query: [],
|
|
42
|
+
scopes: [],
|
|
43
|
+
requires_auth: false,
|
|
44
|
+
bearer_reachable: true,
|
|
45
|
+
browser_session: false,
|
|
46
|
+
idempotent: false,
|
|
47
|
+
has_body: true,
|
|
48
|
+
sandbox: "real"
|
|
49
|
+
}.freeze,
|
|
50
|
+
"tailAgentEvents" => {
|
|
51
|
+
method: "GET",
|
|
52
|
+
path: "/api/events/next",
|
|
53
|
+
query: [{ name: "since", required: false }, { name: "types", required: false }, { name: "limit", required: false }],
|
|
54
|
+
scopes: [],
|
|
55
|
+
requires_auth: true,
|
|
56
|
+
bearer_reachable: false,
|
|
57
|
+
browser_session: true,
|
|
58
|
+
idempotent: false,
|
|
59
|
+
has_body: false,
|
|
60
|
+
sandbox: nil
|
|
61
|
+
}.freeze,
|
|
62
|
+
"rsvpToEvent" => {
|
|
63
|
+
method: "POST",
|
|
64
|
+
path: "/api/events/rsvp",
|
|
65
|
+
query: [],
|
|
66
|
+
scopes: ["events:rsvp"],
|
|
67
|
+
requires_auth: true,
|
|
68
|
+
bearer_reachable: true,
|
|
69
|
+
browser_session: false,
|
|
70
|
+
idempotent: true,
|
|
71
|
+
has_body: true,
|
|
72
|
+
sandbox: "simulated"
|
|
73
|
+
}.freeze,
|
|
74
|
+
"requestEvent" => {
|
|
75
|
+
method: "POST",
|
|
76
|
+
path: "/api/events/request",
|
|
77
|
+
query: [],
|
|
78
|
+
scopes: ["events:request"],
|
|
79
|
+
requires_auth: true,
|
|
80
|
+
bearer_reachable: true,
|
|
81
|
+
browser_session: false,
|
|
82
|
+
idempotent: true,
|
|
83
|
+
has_body: true,
|
|
84
|
+
sandbox: "simulated"
|
|
85
|
+
}.freeze,
|
|
86
|
+
"searchDirectory" => {
|
|
87
|
+
method: "GET",
|
|
88
|
+
path: "/api/directory/search",
|
|
89
|
+
query: [{ name: "q", required: false }, { name: "limit", required: false }],
|
|
90
|
+
scopes: ["directory:search"],
|
|
91
|
+
requires_auth: true,
|
|
92
|
+
bearer_reachable: true,
|
|
93
|
+
browser_session: false,
|
|
94
|
+
idempotent: false,
|
|
95
|
+
has_body: false,
|
|
96
|
+
sandbox: nil
|
|
97
|
+
}.freeze,
|
|
98
|
+
"listResources" => {
|
|
99
|
+
method: "GET",
|
|
100
|
+
path: "/api/resources/list",
|
|
101
|
+
query: [],
|
|
102
|
+
scopes: ["resources:read"],
|
|
103
|
+
requires_auth: true,
|
|
104
|
+
bearer_reachable: true,
|
|
105
|
+
browser_session: false,
|
|
106
|
+
idempotent: false,
|
|
107
|
+
has_body: false,
|
|
108
|
+
sandbox: nil
|
|
109
|
+
}.freeze,
|
|
110
|
+
"bookResource" => {
|
|
111
|
+
method: "POST",
|
|
112
|
+
path: "/api/resources/book",
|
|
113
|
+
query: [],
|
|
114
|
+
scopes: ["resources:book"],
|
|
115
|
+
requires_auth: true,
|
|
116
|
+
bearer_reachable: true,
|
|
117
|
+
browser_session: false,
|
|
118
|
+
idempotent: true,
|
|
119
|
+
has_body: true,
|
|
120
|
+
sandbox: "simulated"
|
|
121
|
+
}.freeze,
|
|
122
|
+
"getMyActivity" => {
|
|
123
|
+
method: "GET",
|
|
124
|
+
path: "/api/activity/me",
|
|
125
|
+
query: [{ name: "limit", required: false }],
|
|
126
|
+
scopes: ["membership:read"],
|
|
127
|
+
requires_auth: true,
|
|
128
|
+
bearer_reachable: true,
|
|
129
|
+
browser_session: false,
|
|
130
|
+
idempotent: false,
|
|
131
|
+
has_body: false,
|
|
132
|
+
sandbox: nil
|
|
133
|
+
}.freeze,
|
|
134
|
+
"getMyLeaderboardStatus" => {
|
|
135
|
+
method: "GET",
|
|
136
|
+
path: "/api/leaderboard/me",
|
|
137
|
+
query: [],
|
|
138
|
+
scopes: ["leaderboard:manage"],
|
|
139
|
+
requires_auth: true,
|
|
140
|
+
bearer_reachable: true,
|
|
141
|
+
browser_session: false,
|
|
142
|
+
idempotent: false,
|
|
143
|
+
has_body: false,
|
|
144
|
+
sandbox: nil
|
|
145
|
+
}.freeze,
|
|
146
|
+
"setLeaderboardOptIn" => {
|
|
147
|
+
method: "POST",
|
|
148
|
+
path: "/api/leaderboard/optin",
|
|
149
|
+
query: [],
|
|
150
|
+
scopes: ["leaderboard:manage"],
|
|
151
|
+
requires_auth: true,
|
|
152
|
+
bearer_reachable: true,
|
|
153
|
+
browser_session: false,
|
|
154
|
+
idempotent: false,
|
|
155
|
+
has_body: true,
|
|
156
|
+
sandbox: "simulated"
|
|
157
|
+
}.freeze,
|
|
158
|
+
"askResearch" => {
|
|
159
|
+
method: "POST",
|
|
160
|
+
path: "/api/research/ask",
|
|
161
|
+
query: [],
|
|
162
|
+
scopes: ["research:query"],
|
|
163
|
+
requires_auth: true,
|
|
164
|
+
bearer_reachable: true,
|
|
165
|
+
browser_session: false,
|
|
166
|
+
idempotent: false,
|
|
167
|
+
has_body: true,
|
|
168
|
+
sandbox: "real"
|
|
169
|
+
}.freeze,
|
|
170
|
+
"submitHighlightPending" => {
|
|
171
|
+
method: "POST",
|
|
172
|
+
path: "/api/ingest/highlights/pending",
|
|
173
|
+
query: [],
|
|
174
|
+
scopes: ["events:submit_recap"],
|
|
175
|
+
requires_auth: true,
|
|
176
|
+
bearer_reachable: true,
|
|
177
|
+
browser_session: false,
|
|
178
|
+
idempotent: true,
|
|
179
|
+
has_body: true,
|
|
180
|
+
sandbox: "simulated"
|
|
181
|
+
}.freeze,
|
|
182
|
+
"submitFeedback" => {
|
|
183
|
+
method: "POST",
|
|
184
|
+
path: "/api/agent/feedback",
|
|
185
|
+
query: [],
|
|
186
|
+
scopes: ["feedback:submit"],
|
|
187
|
+
requires_auth: false,
|
|
188
|
+
bearer_reachable: true,
|
|
189
|
+
browser_session: false,
|
|
190
|
+
idempotent: false,
|
|
191
|
+
has_body: true,
|
|
192
|
+
sandbox: "real"
|
|
193
|
+
}.freeze,
|
|
194
|
+
"revokeOwnToken" => {
|
|
195
|
+
method: "POST",
|
|
196
|
+
path: "/api/agent/token/revoke",
|
|
197
|
+
query: [],
|
|
198
|
+
scopes: [],
|
|
199
|
+
requires_auth: true,
|
|
200
|
+
bearer_reachable: true,
|
|
201
|
+
browser_session: false,
|
|
202
|
+
idempotent: false,
|
|
203
|
+
has_body: false,
|
|
204
|
+
sandbox: "real"
|
|
205
|
+
}.freeze,
|
|
206
|
+
"setupCheck" => {
|
|
207
|
+
method: "GET",
|
|
208
|
+
path: "/api/agent/setup-check",
|
|
209
|
+
query: [],
|
|
210
|
+
scopes: [],
|
|
211
|
+
requires_auth: false,
|
|
212
|
+
bearer_reachable: true,
|
|
213
|
+
browser_session: false,
|
|
214
|
+
idempotent: false,
|
|
215
|
+
has_body: false,
|
|
216
|
+
sandbox: nil
|
|
217
|
+
}.freeze,
|
|
218
|
+
"startSignup" => {
|
|
219
|
+
method: "POST",
|
|
220
|
+
path: "/api/agent/signup/start",
|
|
221
|
+
query: [],
|
|
222
|
+
scopes: [],
|
|
223
|
+
requires_auth: false,
|
|
224
|
+
bearer_reachable: true,
|
|
225
|
+
browser_session: false,
|
|
226
|
+
idempotent: false,
|
|
227
|
+
has_body: true,
|
|
228
|
+
sandbox: nil
|
|
229
|
+
}.freeze,
|
|
230
|
+
"pollSignup" => {
|
|
231
|
+
method: "GET",
|
|
232
|
+
path: "/api/agent/signup/poll",
|
|
233
|
+
query: [{ name: "device_code", required: false }],
|
|
234
|
+
scopes: [],
|
|
235
|
+
requires_auth: false,
|
|
236
|
+
bearer_reachable: true,
|
|
237
|
+
browser_session: false,
|
|
238
|
+
idempotent: false,
|
|
239
|
+
has_body: false,
|
|
240
|
+
sandbox: nil
|
|
241
|
+
}.freeze,
|
|
242
|
+
"getDonorWall" => {
|
|
243
|
+
method: "GET",
|
|
244
|
+
path: "/api/floor10/donations",
|
|
245
|
+
query: [{ name: "limit", required: false }],
|
|
246
|
+
scopes: [],
|
|
247
|
+
requires_auth: false,
|
|
248
|
+
bearer_reachable: true,
|
|
249
|
+
browser_session: false,
|
|
250
|
+
idempotent: false,
|
|
251
|
+
has_body: false,
|
|
252
|
+
sandbox: nil
|
|
253
|
+
}.freeze,
|
|
254
|
+
"getMyTier" => {
|
|
255
|
+
method: "GET",
|
|
256
|
+
path: "/api/tier/me",
|
|
257
|
+
query: [],
|
|
258
|
+
scopes: [],
|
|
259
|
+
requires_auth: true,
|
|
260
|
+
bearer_reachable: false,
|
|
261
|
+
browser_session: true,
|
|
262
|
+
idempotent: false,
|
|
263
|
+
has_body: false,
|
|
264
|
+
sandbox: nil
|
|
265
|
+
}.freeze,
|
|
266
|
+
"requestTier" => {
|
|
267
|
+
method: "POST",
|
|
268
|
+
path: "/api/tier/request",
|
|
269
|
+
query: [],
|
|
270
|
+
scopes: [],
|
|
271
|
+
requires_auth: true,
|
|
272
|
+
bearer_reachable: false,
|
|
273
|
+
browser_session: true,
|
|
274
|
+
idempotent: false,
|
|
275
|
+
has_body: true,
|
|
276
|
+
sandbox: nil
|
|
277
|
+
}.freeze,
|
|
278
|
+
"cancelTierRequest" => {
|
|
279
|
+
method: "DELETE",
|
|
280
|
+
path: "/api/tier/request",
|
|
281
|
+
query: [],
|
|
282
|
+
scopes: [],
|
|
283
|
+
requires_auth: true,
|
|
284
|
+
bearer_reachable: false,
|
|
285
|
+
browser_session: true,
|
|
286
|
+
idempotent: false,
|
|
287
|
+
has_body: false,
|
|
288
|
+
sandbox: nil
|
|
289
|
+
}.freeze,
|
|
290
|
+
}.freeze
|
|
291
|
+
end
|