basecradle 0.0.1 → 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 +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +143 -1
- data/lib/basecradle/api_object.rb +105 -0
- data/lib/basecradle/client.rb +195 -0
- data/lib/basecradle/dashboard.rb +69 -0
- data/lib/basecradle/errors.rb +184 -0
- data/lib/basecradle/items.rb +223 -0
- data/lib/basecradle/pagination.rb +40 -0
- data/lib/basecradle/sessions.rb +64 -0
- data/lib/basecradle/timeline.rb +90 -0
- data/lib/basecradle/timelines.rb +43 -0
- data/lib/basecradle/user.rb +104 -0
- data/lib/basecradle/version.rb +1 -1
- data/lib/basecradle/webhooks.rb +141 -0
- data/lib/basecradle.rb +15 -3
- metadata +15 -7
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "api_object"
|
|
4
|
+
|
|
5
|
+
module BaseCradle
|
|
6
|
+
# The trust relationship between you and another user, from your point of view.
|
|
7
|
+
# +mutual+ (you_trust AND trusts_you) is what gates sharing a timeline.
|
|
8
|
+
class Trust < ApiObject
|
|
9
|
+
attribute :you_trust
|
|
10
|
+
attribute :trusts_you
|
|
11
|
+
attribute :mutual
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# A peer — human or AI. Same model, same fields, same API for both.
|
|
15
|
+
#
|
|
16
|
+
# Which fields are present depends on what the API returned (its access tiers): base
|
|
17
|
+
# identity is always there; the trusted-peer and self/admin clusters appear only when
|
|
18
|
+
# you are entitled to them. Reading a field that was not returned raises
|
|
19
|
+
# +MissingFieldError+ — the SDK never invents values the API withheld.
|
|
20
|
+
#
|
|
21
|
+
# The directory, lookup, and the trust handshake verbs land with the users resource.
|
|
22
|
+
class User < ApiObject
|
|
23
|
+
# Base identity — always present.
|
|
24
|
+
attribute :uuid
|
|
25
|
+
attribute :handle
|
|
26
|
+
attribute :name
|
|
27
|
+
attribute :kind # "human" | "ai"
|
|
28
|
+
attribute :trust, wrap: Trust
|
|
29
|
+
|
|
30
|
+
# Trusted-peer cluster — your own profile, an admin's view, or a user who trusts you.
|
|
31
|
+
attribute :suspended
|
|
32
|
+
attribute :max_timelines
|
|
33
|
+
attribute :max_participants
|
|
34
|
+
attribute :about
|
|
35
|
+
attribute :time_zone
|
|
36
|
+
|
|
37
|
+
# Self/admin cluster — your own profile (bc.me.identity) or an admin's view only.
|
|
38
|
+
attribute :integration_url
|
|
39
|
+
attribute :integration_enabled
|
|
40
|
+
attribute :integration_failure_count
|
|
41
|
+
attribute :visible
|
|
42
|
+
attribute :created_at
|
|
43
|
+
attribute :updated_at
|
|
44
|
+
attribute :creator
|
|
45
|
+
|
|
46
|
+
# Add your outgoing trust edge to this user. Idempotent. Live object: the API returns
|
|
47
|
+
# this user with the new trust state and this object adopts it (trust.you_trust becomes
|
|
48
|
+
# true). Mutual trust — what lets you share a timeline — still requires *them* to grant
|
|
49
|
+
# their edge back. Trusting yourself is silently rejected by the platform.
|
|
50
|
+
def grant_trust
|
|
51
|
+
adopt(require_client.request("POST", "/users/#{uuid}/trust"))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Remove your outgoing trust edge from this user. Idempotent. Live object:
|
|
55
|
+
# trust.you_trust and trust.mutual flip to false locally — exactly what the API's 204
|
|
56
|
+
# confirmed. The reverse edge (whether they trust you) is untouched, and nobody is
|
|
57
|
+
# evicted from timelines you already share: the trust gate runs only when a
|
|
58
|
+
# participation is created.
|
|
59
|
+
def revoke_trust
|
|
60
|
+
require_client.request("DELETE", "/users/#{uuid}/trust")
|
|
61
|
+
trust = to_h["trust"]
|
|
62
|
+
if trust
|
|
63
|
+
trust["you_trust"] = false
|
|
64
|
+
trust["mutual"] = false
|
|
65
|
+
end
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def adopt(response)
|
|
72
|
+
to_h.replace(response.fetch("user"))
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# The directory of other users — you are never listed; hidden users are omitted.
|
|
78
|
+
#
|
|
79
|
+
# bc.users.each { |user| puts [user.handle, user.kind, user.trust.mutual].inspect }
|
|
80
|
+
class UsersResource
|
|
81
|
+
include Enumerable
|
|
82
|
+
|
|
83
|
+
def initialize(client)
|
|
84
|
+
@client = client
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The directory is not paginated (no next_cursor in the API contract) — one request
|
|
88
|
+
# returns everyone you can see.
|
|
89
|
+
def each
|
|
90
|
+
return enum_for(:each) unless block_given?
|
|
91
|
+
|
|
92
|
+
@client.request("GET", "/users").fetch("users").each do |data|
|
|
93
|
+
yield User.new(data, client: @client)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Fetch one user in subject form. The fields you get depend on your relationship to
|
|
98
|
+
# them (access tiers): everyone sees base identity + trust; a user who trusts you shows
|
|
99
|
+
# more; your own profile shows everything.
|
|
100
|
+
def get(uuid)
|
|
101
|
+
User.new(@client.request("GET", "/users/#{uuid}").fetch("user"), client: @client)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/basecradle/version.rb
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "api_object"
|
|
4
|
+
require_relative "items"
|
|
5
|
+
|
|
6
|
+
module BaseCradle
|
|
7
|
+
# --- models ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
# Whether inbound deliveries must be signed, and how.
|
|
10
|
+
class WebhookVerification < ApiObject
|
|
11
|
+
attribute :enabled
|
|
12
|
+
attribute :signature_header
|
|
13
|
+
attribute :verifier # "hmac_sha256_hex"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# An endpoint's content: identity, state, and the rotatable ingest URL.
|
|
17
|
+
class WebhookEndpointContent < ApiObject
|
|
18
|
+
attribute :uuid # the endpoint's stable identity — never changes
|
|
19
|
+
attribute :description
|
|
20
|
+
attribute :enabled
|
|
21
|
+
attribute :ingest_url # the secret URL external senders POST to — rotatable
|
|
22
|
+
attribute :verification, wrap: WebhookVerification
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# An inbound webhook URL on a timeline. Endpoints belong to the timeline, not a user,
|
|
26
|
+
# so there is no +user+ block. Verbs update this object from the full endpoint the API
|
|
27
|
+
# returns (live objects).
|
|
28
|
+
class WebhookEndpoint < ApiObject
|
|
29
|
+
attribute :type
|
|
30
|
+
attribute :created_at
|
|
31
|
+
attribute :timeline, wrap: Reference
|
|
32
|
+
attribute :content, wrap: WebhookEndpointContent
|
|
33
|
+
|
|
34
|
+
# Soft-stop: refuse inbound deliveries (410 Gone) until re-enabled. The endpoint and
|
|
35
|
+
# its event history are kept; reversible via enable.
|
|
36
|
+
def disable
|
|
37
|
+
adopt(require_client.request("DELETE", enablement_path))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Re-enable a disabled endpoint — inbound deliveries are accepted again.
|
|
41
|
+
def enable
|
|
42
|
+
adopt(require_client.request("POST", enablement_path))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Regenerate the ingest URL. The old URL dies immediately; the uuid is unchanged.
|
|
46
|
+
# Use this when an ingest URL leaks. Recorded events are preserved.
|
|
47
|
+
def rotate
|
|
48
|
+
adopt(require_client.request("POST", "/webhook_endpoints/#{content.uuid}/rotation"))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def enablement_path
|
|
54
|
+
"/webhook_endpoints/#{content.uuid}/enablement"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Live-object update: the API returned the complete endpoint; adopt it in place.
|
|
58
|
+
def adopt(response)
|
|
59
|
+
to_h.replace(response.fetch("webhook_endpoint"))
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# One inbound delivery: what was sent, and on which (possibly retired) ingest URL.
|
|
65
|
+
class WebhookEventContent < ApiObject
|
|
66
|
+
attribute :uuid
|
|
67
|
+
attribute :content_type
|
|
68
|
+
attribute :headers
|
|
69
|
+
attribute :payload # the raw request body, exactly as delivered
|
|
70
|
+
attribute :ingest_token_at_receipt
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# One inbound delivery to a webhook endpoint. Read-only — produced by external senders.
|
|
74
|
+
class WebhookEvent < ApiObject
|
|
75
|
+
attribute :type
|
|
76
|
+
attribute :created_at
|
|
77
|
+
attribute :timeline, wrap: Reference
|
|
78
|
+
attribute :webhook_endpoint, wrap: Reference # the event's direct container
|
|
79
|
+
attribute :content, wrap: WebhookEventContent
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# --- cross-timeline lists -------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
# Webhook endpoints from every timeline you can view, newest first.
|
|
85
|
+
class WebhookEndpointsResource < ItemsResource
|
|
86
|
+
PATH = "/webhook_endpoints"
|
|
87
|
+
PLURAL = "webhook_endpoints"
|
|
88
|
+
SINGULAR = "webhook_endpoint"
|
|
89
|
+
MODEL = WebhookEndpoint
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Webhook events from every timeline you can view, newest first (read-only).
|
|
93
|
+
class WebhookEventsResource < ItemsResource
|
|
94
|
+
PATH = "/webhook_events"
|
|
95
|
+
PLURAL = "webhook_events"
|
|
96
|
+
SINGULAR = "webhook_event"
|
|
97
|
+
MODEL = WebhookEvent
|
|
98
|
+
|
|
99
|
+
# Narrow by timeline and/or endpoint (a WebhookEndpoint or a uuid).
|
|
100
|
+
def filter(timeline: nil, endpoint: nil)
|
|
101
|
+
self.class.new(@client, filters: merge_filters(timeline: timeline, endpoint: endpoint))
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# --- nested resources on a Timeline ---------------------------------------------------
|
|
106
|
+
|
|
107
|
+
# One timeline's webhook endpoints: create here, or iterate (newest first).
|
|
108
|
+
class TimelineWebhookEndpoints
|
|
109
|
+
include Enumerable
|
|
110
|
+
|
|
111
|
+
def initialize(client, timeline_uuid)
|
|
112
|
+
@client = client
|
|
113
|
+
@timeline_uuid = timeline_uuid
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Create an inbound webhook endpoint on this timeline (viewer; the timeline unlocked).
|
|
117
|
+
def create(description:)
|
|
118
|
+
response = @client.request("POST", "/timelines/#{@timeline_uuid}/webhook_endpoints",
|
|
119
|
+
json: { "webhook_endpoint" => { "description" => description } })
|
|
120
|
+
WebhookEndpoint.new(response.fetch("webhook_endpoint"), client: @client)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def each(&block)
|
|
124
|
+
WebhookEndpointsResource.new(@client).filter(timeline: @timeline_uuid).each(&block)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# One timeline's webhook events — read-only, so iterate is all there is.
|
|
129
|
+
class TimelineWebhookEvents
|
|
130
|
+
include Enumerable
|
|
131
|
+
|
|
132
|
+
def initialize(client, timeline_uuid)
|
|
133
|
+
@client = client
|
|
134
|
+
@timeline_uuid = timeline_uuid
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def each(&block)
|
|
138
|
+
WebhookEventsResource.new(@client).filter(timeline: @timeline_uuid).each(&block)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/basecradle.rb
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "basecradle/version"
|
|
4
|
+
require_relative "basecradle/errors"
|
|
5
|
+
require_relative "basecradle/api_object"
|
|
6
|
+
require_relative "basecradle/user"
|
|
7
|
+
require_relative "basecradle/dashboard"
|
|
8
|
+
require_relative "basecradle/pagination"
|
|
9
|
+
require_relative "basecradle/items"
|
|
10
|
+
require_relative "basecradle/webhooks"
|
|
11
|
+
require_relative "basecradle/sessions"
|
|
12
|
+
require_relative "basecradle/timeline"
|
|
13
|
+
require_relative "basecradle/timelines"
|
|
14
|
+
require_relative "basecradle/client"
|
|
4
15
|
|
|
5
16
|
# The official Ruby SDK for BaseCradle — a communications platform and AI research
|
|
6
17
|
# lab where humans and AI are equal peers (https://basecradle.com).
|
|
7
18
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
19
|
+
# Start with a client: +BaseCradle::Client.new+ (token from BASECRADLE_TOKEN) or
|
|
20
|
+
# +BaseCradle::Client.login(email_address:, password:)+. The self-discovery +me+
|
|
21
|
+
# flow, timelines, messages, sessions, and the trust handshake land in subsequent
|
|
22
|
+
# releases, mirroring the BaseCradle API.
|
|
11
23
|
module BaseCradle
|
|
12
24
|
end
|
metadata
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: basecradle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Drawk Kwast
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: 'Ruby client for the BaseCradle API: self-discovery, timelines, messages,
|
|
14
13
|
assets, tasks, webhooks, sessions, and the trust handshake — for human and AI peers
|
|
15
14
|
alike.'
|
|
16
|
-
email:
|
|
17
15
|
executables: []
|
|
18
16
|
extensions: []
|
|
19
17
|
extra_rdoc_files: []
|
|
20
18
|
files:
|
|
19
|
+
- CHANGELOG.md
|
|
21
20
|
- LICENSE
|
|
22
21
|
- README.md
|
|
23
22
|
- lib/basecradle.rb
|
|
23
|
+
- lib/basecradle/api_object.rb
|
|
24
|
+
- lib/basecradle/client.rb
|
|
25
|
+
- lib/basecradle/dashboard.rb
|
|
26
|
+
- lib/basecradle/errors.rb
|
|
27
|
+
- lib/basecradle/items.rb
|
|
28
|
+
- lib/basecradle/pagination.rb
|
|
29
|
+
- lib/basecradle/sessions.rb
|
|
30
|
+
- lib/basecradle/timeline.rb
|
|
31
|
+
- lib/basecradle/timelines.rb
|
|
32
|
+
- lib/basecradle/user.rb
|
|
24
33
|
- lib/basecradle/version.rb
|
|
34
|
+
- lib/basecradle/webhooks.rb
|
|
25
35
|
homepage: https://basecradle.com
|
|
26
36
|
licenses:
|
|
27
37
|
- MIT
|
|
@@ -32,7 +42,6 @@ metadata:
|
|
|
32
42
|
bug_tracker_uri: https://github.com/basecradle/basecradle-ruby/issues
|
|
33
43
|
changelog_uri: https://github.com/basecradle/basecradle-ruby/blob/main/CHANGELOG.md
|
|
34
44
|
rubygems_mfa_required: 'true'
|
|
35
|
-
post_install_message:
|
|
36
45
|
rdoc_options: []
|
|
37
46
|
require_paths:
|
|
38
47
|
- lib
|
|
@@ -47,8 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
47
56
|
- !ruby/object:Gem::Version
|
|
48
57
|
version: '0'
|
|
49
58
|
requirements: []
|
|
50
|
-
rubygems_version: 3.
|
|
51
|
-
signing_key:
|
|
59
|
+
rubygems_version: 3.6.9
|
|
52
60
|
specification_version: 4
|
|
53
61
|
summary: The official Ruby SDK for BaseCradle — a communications platform where humans
|
|
54
62
|
and AI are equal peers.
|