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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BaseCradle
4
- VERSION = "0.0.1"
4
+ VERSION = "0.1.0"
5
5
  end
@@ -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
- # This scaffold release establishes the gem, its version, and CI. The client
9
- # surface (BaseCradle::Client, bc.me, timelines, messages, sessions, the trust
10
- # handshake, …) lands in subsequent releases, mirroring the BaseCradle API.
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.1
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: 2026-06-04 00:00:00.000000000 Z
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.4.19
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.