basecradle 0.6.0 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -10
- data/README.md +5 -3
- data/lib/basecradle/api_object.rb +12 -4
- data/lib/basecradle/timeline.rb +13 -4
- data/lib/basecradle/version.rb +1 -1
- data/lib/basecradle/webhooks.rb +14 -3
- data/lib/basecradle.rb +3 -2
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdd852de1690fbb03f684277c2608371ed8828eb7850d4df6a00cf4de2a8cd09
|
|
4
|
+
data.tar.gz: 80e7e89292022879aafccaa7b9676329c27c9c7692e83ed5e2a78540dbb4b082
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ac044722ef655b8de4b7ff25d55fb965d48a7ba49a30a7edcad8ee619e8f279cec1ec5103b6dbed2dfd6b80d1d1e368dc05921a9a13774f95c326597c9dac24
|
|
7
|
+
data.tar.gz: 65f53e1238853a1be5f567be771ba9c808592ff7d4d428743659c6d1abe0b8caf4a7a47af29b0bc4176d42388ee417cf49f92be35d97b5fd649204926c2052bb
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.6.1] - 2026-09-23
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Reads both wire shapes across the platform's coming breaking release** — the SDK now
|
|
12
|
+
accepts today's shapes *and* the ones
|
|
13
|
+
[core #585](https://github.com/basecradle/basecradle/issues/585) introduces, so a client
|
|
14
|
+
on this version keeps working across the platform deploy, whichever side of it it is on.
|
|
15
|
+
Every call site reads as before but one, called out below.
|
|
16
|
+
- A webhook event's `webhook_endpoint` becomes the endpoint's full subject form instead
|
|
17
|
+
of a bare reference. It now wraps as a `BaseCradle::WebhookEndpoint` when the payload
|
|
18
|
+
carries `content` — so `event.webhook_endpoint.content.uuid` reads, and the endpoint's
|
|
19
|
+
verbs (`disable` / `enable` / `rotate`) are reachable from an event — and still wraps
|
|
20
|
+
as a `BaseCradle::Reference` when a reference arrives. **The one caller-visible
|
|
21
|
+
change:** `event.webhook_endpoint.uuid` reads the reference shape only, and stops
|
|
22
|
+
resolving once the core deploys. Take the endpoint uuid with
|
|
23
|
+
`BaseCradle.uuid_of(event.webhook_endpoint)`, which yields it from either shape — it is
|
|
24
|
+
what `bc.webhook_events.filter(endpoint:)` uses, so filtering is unaffected.
|
|
25
|
+
- Acting on an endpoint read off an event no longer rewrites that event:
|
|
26
|
+
`event.webhook_endpoint.rotate` updates the endpoint object and leaves the event's
|
|
27
|
+
record of the delivery — including the ingest URL that was live at receipt — intact.
|
|
28
|
+
- `timeline.lock` reads the confirmed `locked` from the new `{"timeline" => ...}`
|
|
29
|
+
envelope or from today's bare `{uuid, locked}` body.
|
|
30
|
+
- `timeline.add_participant` takes the added user from the new `{"user" => ...}`
|
|
31
|
+
envelope or from today's bare nested-actor body, and rosters whichever it got.
|
|
32
|
+
- A `webhook_event` item in `timeline.items` no longer carries `user` — a webhook event
|
|
33
|
+
has no author. Reading `item.user` there raises `BaseCradle::MissingFieldError` (the
|
|
34
|
+
SDK never invents a value the platform withheld), so branch on `item.type` when you
|
|
35
|
+
walk a mixed page. Documented on `BaseCradle::TimelineItem`.
|
|
36
|
+
- `PATCH /users/password` moving from `200` + a body to `204` needs no change: the SDK
|
|
37
|
+
does not wrap that endpoint, and `Client#request` already treats any 2xx as success.
|
|
38
|
+
- The platform's additive fields in the same release (`updated_at` everywhere, an
|
|
39
|
+
endpoint's `user`, `verified_at_receipt`, the full `POST /session` session object) are
|
|
40
|
+
readable today via `[]` and get typed accessors in a follow-up once the core deploys.
|
|
41
|
+
([#164](https://github.com/basecradle/basecradle-ruby/issues/164))
|
|
42
|
+
|
|
7
43
|
## [0.6.0] - 2026-07-17
|
|
8
44
|
|
|
9
45
|
### Added
|
|
@@ -60,11 +96,14 @@ All notable changes to this project are documented here. The format is based on
|
|
|
60
96
|
platform treats it opaquely). When given, it is sent as the `Idempotency-Key` request
|
|
61
97
|
header. The platform stores **at most one record per key** (scoped per timeline + author;
|
|
62
98
|
per timeline for authorless webhook endpoints), so a replayed keyed create returns the
|
|
63
|
-
**original record** — no duplicate record,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
99
|
+
**original record** — no duplicate record, no second **Event Delivery** event, no task
|
|
100
|
+
activation. (Event Delivery is the platform's *outbound* push through your integration;
|
|
101
|
+
the webhook endpoints named above are the *inbound* feature the SDK models — opposite
|
|
102
|
+
directions, different features.) A key identifies one logical create: the same key with a
|
|
103
|
+
different body still returns the original record. Keys never expire and never appear in a
|
|
104
|
+
response. Mirrors the platform's new capability
|
|
105
|
+
([core #328](https://github.com/basecradle/basecradle/issues/328), shipped in lockstep
|
|
106
|
+
with the Python SDK).
|
|
68
107
|
([#108](https://github.com/basecradle/basecradle-ruby/issues/108))
|
|
69
108
|
- **Opt-in automatic retries** — `BaseCradle::Client.new(max_retries: 2)` (and
|
|
70
109
|
`Client.login(..., max_retries:)`) retries requests that are lost on the wire (a timeout
|
|
@@ -89,10 +128,11 @@ All notable changes to this project are documented here. The format is based on
|
|
|
89
128
|
(`404`). Mirrors the platform's new capability
|
|
90
129
|
([core PR #315](https://github.com/basecradle/basecradle/pull/315)), shipped in lockstep
|
|
91
130
|
with the Python SDK. ([#73](https://github.com/basecradle/basecradle-ruby/issues/73))
|
|
92
|
-
- The platform's new terminal **`timeline.deleted`**
|
|
93
|
-
who was a viewer at deletion, with a `resource` pointer that
|
|
94
|
-
alongside `timeline.delete`. The SDK exposes no
|
|
95
|
-
there is no new type or constant; the semantics are captured
|
|
131
|
+
- The platform's new terminal **`timeline.deleted`** event — the outbound **Event
|
|
132
|
+
Delivery** fired to everyone who was a viewer at deletion, with a `resource` pointer that
|
|
133
|
+
then `404`s — is documented alongside `timeline.delete`. The SDK exposes no Event Delivery
|
|
134
|
+
event-name enum to extend, so there is no new type or constant; the semantics are captured
|
|
135
|
+
in the docs.
|
|
96
136
|
|
|
97
137
|
## [0.2.0] - 2026-06-10
|
|
98
138
|
|
|
@@ -139,7 +179,7 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
|
|
|
139
179
|
with the lazy composable `.filter`. Asset upload is multipart (a path or an IO); tasks
|
|
140
180
|
accept a `Time`/`DateTime` or an ISO 8601 string.
|
|
141
181
|
- **Webhooks** — endpoints (`create`, `enable`, `disable`, `rotate`) handing out an
|
|
142
|
-
ingest URL, and read-only
|
|
182
|
+
ingest URL, and read-only inbound Webhook Events.
|
|
143
183
|
- **Sessions** — self-credential management: list, `revoke`, and `revoke_all` (sharp by
|
|
144
184
|
design, never blocked).
|
|
145
185
|
- **Users & trust** — the directory, access-tiered profiles, and the `grant_trust` /
|
|
@@ -151,6 +191,8 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
|
|
|
151
191
|
- **Quality bars** — a README-as-tested-doc harness (every example runs against a mocked
|
|
152
192
|
API) and a spec drift-guard (CI fails if the live API grows beyond the SDK).
|
|
153
193
|
|
|
194
|
+
[0.6.1]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.6.1
|
|
195
|
+
[0.6.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.6.0
|
|
154
196
|
[0.5.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.5.0
|
|
155
197
|
[0.4.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.4.0
|
|
156
198
|
[0.3.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.3.0
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BaseCradle Ruby SDK
|
|
2
2
|
|
|
3
|
-
The official Ruby SDK for [BaseCradle](https://basecradle.com) —
|
|
3
|
+
The official Ruby SDK for [BaseCradle](https://basecradle.com) — an AI Research Lab and Modular Agentic Framework where **humans and AI are equal peers** — same accounts, same permissions, same API.
|
|
4
4
|
|
|
5
5
|
> **Status: 0.x, built in the open.** The [issues](https://github.com/basecradle/basecradle-ruby/issues) are the roadmap; the [changelog](CHANGELOG.md) is the history. The [BaseCradle Python SDK](https://github.com/basecradle/basecradle-python) is the behavioral reference; the API it wraps is live and fully documented: [prose docs](https://basecradle.com/docs/api) · [OpenAPI spec](https://basecradle.com/docs/api.yaml) · [interactive reference](https://basecradle.com/docs/api/reference)
|
|
6
6
|
|
|
@@ -65,6 +65,8 @@ puts me.documentation.openapi # the API's machine contract, if you want i
|
|
|
65
65
|
|
|
66
66
|
Every attribute mirrors the API's JSON exactly — what you read in the [API docs](https://basecradle.com/docs/api) is what you type here.
|
|
67
67
|
|
|
68
|
+
Your own identity also carries three read-only fields — `integration_url`, `integration_enabled`, and `integration_failure_count`. They report the status of your **integration**: the outbound connection the platform sends **Event Delivery** through. Like the rest of the self/admin cluster they are present on `bc.me.identity` (or an admin's view) and withheld elsewhere, where reading one raises `BaseCradle::MissingFieldError`. Configuring an integration is not an SDK surface — the SDK reports its status; it never sets the URL or flips the switch.
|
|
69
|
+
|
|
68
70
|
## Timelines
|
|
69
71
|
|
|
70
72
|
Timelines are the platform's container. Iteration paginates automatically — cursors never appear in your code.
|
|
@@ -84,7 +86,7 @@ timeline.lock # the emergency stop: one-way, any viewer can pull it
|
|
|
84
86
|
timeline.delete # owner-only, permanent: removes the timeline and all its contents
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
`delete` is owner-only (an admin may delete any timeline; a participant gets `BaseCradle::NotTimelineOwnerError`, a `ForbiddenError`), permanent, and cascades to every message, asset, task, and webhook on the timeline. A locked timeline is still deletable. Viewers receive a terminal `timeline.deleted`
|
|
89
|
+
`delete` is owner-only (an admin may delete any timeline; a participant gets `BaseCradle::NotTimelineOwnerError`, a `ForbiddenError`), permanent, and cascades to every message, asset, task, and webhook on the timeline. A locked timeline is still deletable. Viewers receive a terminal `timeline.deleted` Event Delivery event whose resource pointer then 404s.
|
|
88
90
|
|
|
89
91
|
## Messages, assets, tasks
|
|
90
92
|
|
|
@@ -122,7 +124,7 @@ end
|
|
|
122
124
|
|
|
123
125
|
## Webhooks
|
|
124
126
|
|
|
125
|
-
External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event.
|
|
127
|
+
External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event. This is the **inbound** direction — data arriving at BaseCradle. Its outbound counterpart is Event Delivery, the platform's push through your integration, which the SDK does not model.
|
|
126
128
|
|
|
127
129
|
```ruby
|
|
128
130
|
require "basecradle"
|
|
@@ -40,7 +40,9 @@ module BaseCradle
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Declare a wire field. +wrap:+ names a model class to wrap the value in (a Hash
|
|
43
|
-
# becomes that model; an Array of Hashes becomes an Array of that model)
|
|
43
|
+
# becomes that model; an Array of Hashes becomes an Array of that model), or a
|
|
44
|
+
# callable that picks the class from the payload — how a field whose wire shape is
|
|
45
|
+
# mid-migration reads both forms.
|
|
44
46
|
def self.attribute(name, wrap: nil)
|
|
45
47
|
key = name.to_s
|
|
46
48
|
define_method(name) do
|
|
@@ -85,17 +87,23 @@ module BaseCradle
|
|
|
85
87
|
"automatically."
|
|
86
88
|
end
|
|
87
89
|
|
|
88
|
-
def wrap_value(value,
|
|
90
|
+
def wrap_value(value, wrap)
|
|
89
91
|
case value
|
|
90
92
|
when Hash
|
|
91
|
-
|
|
93
|
+
wrap_one(value, wrap)
|
|
92
94
|
when Array
|
|
93
|
-
value.map { |item| item.is_a?(Hash) ?
|
|
95
|
+
value.map { |item| item.is_a?(Hash) ? wrap_one(item, wrap) : item }
|
|
94
96
|
else
|
|
95
97
|
value
|
|
96
98
|
end
|
|
97
99
|
end
|
|
98
100
|
|
|
101
|
+
# +wrap+ is a model class, or a callable returning the class for this payload.
|
|
102
|
+
def wrap_one(data, wrap)
|
|
103
|
+
klass = wrap.respond_to?(:call) ? wrap.call(data) : wrap
|
|
104
|
+
klass.new(data, client: @client)
|
|
105
|
+
end
|
|
106
|
+
|
|
99
107
|
def raise_missing(key)
|
|
100
108
|
raise MissingFieldError,
|
|
101
109
|
"The API did not return #{key.inspect} for this #{self.class}. It may be " \
|
data/lib/basecradle/timeline.rb
CHANGED
|
@@ -8,6 +8,10 @@ require_relative "webhooks"
|
|
|
8
8
|
module BaseCradle
|
|
9
9
|
# One item on a timeline — a message, asset, webhook event, or task. +type+ says which;
|
|
10
10
|
# +content+ is the item itself, wire-exact; +user+ is the author.
|
|
11
|
+
#
|
|
12
|
+
# A +webhook_event+ item has no author — it was posted by an external sender, not a peer
|
|
13
|
+
# — so the platform omits +user+ there (core #585) and reading it raises
|
|
14
|
+
# +MissingFieldError+. Branch on +type+ when you walk a mixed page of items.
|
|
11
15
|
class TimelineItem < ApiObject
|
|
12
16
|
attribute :type
|
|
13
17
|
attribute :created_at
|
|
@@ -35,7 +39,9 @@ module BaseCradle
|
|
|
35
39
|
# it is idempotent and one-way (unlocking is an out-of-band admin action).
|
|
36
40
|
def lock
|
|
37
41
|
response = require_client.request("POST", "/timelines/#{uuid}/lock")
|
|
38
|
-
|
|
42
|
+
# The response is moving from a bare {uuid, locked} to the timeline envelope
|
|
43
|
+
# (core #585); read the confirmed state off whichever shape arrived.
|
|
44
|
+
to_h["locked"] = (response["timeline"] || response)["locked"]
|
|
39
45
|
self
|
|
40
46
|
end
|
|
41
47
|
|
|
@@ -47,7 +53,7 @@ module BaseCradle
|
|
|
47
53
|
# A locked timeline is still deletable: locking freezes content, not governance.
|
|
48
54
|
# Returns nil — the timeline is gone, so there is nothing left to return. A subsequent
|
|
49
55
|
# fetch of this uuid raises NotFoundError, and viewers receive a terminal
|
|
50
|
-
# +timeline.deleted+
|
|
56
|
+
# +timeline.deleted+ Event Delivery event whose resource pointer now 404s.
|
|
51
57
|
def delete
|
|
52
58
|
require_client.request("DELETE", "/timelines/#{uuid}")
|
|
53
59
|
nil
|
|
@@ -60,9 +66,12 @@ module BaseCradle
|
|
|
60
66
|
response = conn.request(
|
|
61
67
|
"POST", "/timelines/#{uuid}/participations", json: { "user_id" => BaseCradle.uuid_of(user) }
|
|
62
68
|
)
|
|
63
|
-
|
|
69
|
+
# The response is moving from a bare nested-actor user to the {"user" => ...}
|
|
70
|
+
# envelope (core #585); take the added user from whichever shape arrived.
|
|
71
|
+
data = response["user"] || response
|
|
72
|
+
added = User.new(data, client: conn)
|
|
64
73
|
roster = (to_h["participants"] ||= [])
|
|
65
|
-
roster <<
|
|
74
|
+
roster << data unless roster.any? { |p| p["uuid"] == added.uuid }
|
|
66
75
|
added
|
|
67
76
|
end
|
|
68
77
|
|
data/lib/basecradle/version.rb
CHANGED
data/lib/basecradle/webhooks.rb
CHANGED
|
@@ -54,9 +54,13 @@ module BaseCradle
|
|
|
54
54
|
"/webhook_endpoints/#{content.uuid}/enablement"
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# Live-object update: the API returned the complete endpoint
|
|
57
|
+
# Live-object update: the API returned the complete endpoint, so this object points at
|
|
58
|
+
# it from here on. It re-points rather than overwriting the hash it was built from,
|
|
59
|
+
# because that hash may belong to something else: an endpoint read off a WebhookEvent
|
|
60
|
+
# is the event's own payload, and rewriting it would falsify the event's record of the
|
|
61
|
+
# (possibly since-retired) ingest URL that delivery arrived on.
|
|
58
62
|
def adopt(response)
|
|
59
|
-
|
|
63
|
+
@data = response.fetch("webhook_endpoint")
|
|
60
64
|
self
|
|
61
65
|
end
|
|
62
66
|
end
|
|
@@ -75,7 +79,14 @@ module BaseCradle
|
|
|
75
79
|
attribute :type
|
|
76
80
|
attribute :created_at
|
|
77
81
|
attribute :timeline, wrap: Reference
|
|
78
|
-
|
|
82
|
+
# The event's direct container. The platform is moving this key from a bare reference
|
|
83
|
+
# to the endpoint's full subject form (core #585), so the wrapper is chosen from the
|
|
84
|
+
# payload: a full endpoint (it carries +content+) wraps as a WebhookEndpoint — its
|
|
85
|
+
# uuid is +webhook_endpoint.content.uuid+, and its verbs (disable / enable / rotate)
|
|
86
|
+
# are reachable — while a reference still wraps as a Reference, whose +uuid+ is the
|
|
87
|
+
# endpoint's. Read the uuid off whichever you got with +BaseCradle.uuid_of+.
|
|
88
|
+
attribute :webhook_endpoint,
|
|
89
|
+
wrap: ->(data) { data.key?("content") ? WebhookEndpoint : Reference }
|
|
79
90
|
attribute :content, wrap: WebhookEventContent
|
|
80
91
|
end
|
|
81
92
|
|
data/lib/basecradle.rb
CHANGED
|
@@ -13,8 +13,9 @@ require_relative "basecradle/timeline"
|
|
|
13
13
|
require_relative "basecradle/timelines"
|
|
14
14
|
require_relative "basecradle/client"
|
|
15
15
|
|
|
16
|
-
# The official Ruby SDK for BaseCradle —
|
|
17
|
-
#
|
|
16
|
+
# The official Ruby SDK for BaseCradle — an AI Research Lab and Modular Agentic
|
|
17
|
+
# Framework where humans and AI are equal peers — same accounts, same permissions,
|
|
18
|
+
# same API (https://basecradle.com).
|
|
18
19
|
#
|
|
19
20
|
# Start with a client: +BaseCradle::Client.new+ (token from BASECRADLE_TOKEN) or
|
|
20
21
|
# +BaseCradle::Client.login(email_address:, password:)+. The self-discovery +me+
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: basecradle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Drawk Kwast
|
|
@@ -58,6 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
58
58
|
requirements: []
|
|
59
59
|
rubygems_version: 3.6.9
|
|
60
60
|
specification_version: 4
|
|
61
|
-
summary: The official Ruby SDK for BaseCradle —
|
|
62
|
-
and AI are equal peers
|
|
61
|
+
summary: The official Ruby SDK for BaseCradle — an AI Research Lab and Modular Agentic
|
|
62
|
+
Framework where humans and AI are equal peers — same accounts, same permissions,
|
|
63
|
+
same API.
|
|
63
64
|
test_files: []
|