basecradle 0.5.0 → 0.6.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 +30 -0
- data/README.md +13 -1
- data/lib/basecradle/client.rb +15 -0
- data/lib/basecradle/errors.rb +15 -0
- data/lib/basecradle/items.rb +20 -2
- data/lib/basecradle/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b1754f11512dff348b826735816a0585c0e7f86c8390e6ccf232dcb97cb6a97
|
|
4
|
+
data.tar.gz: 4f0dfd1f553019eec050022105c6188e8878d8cab50a8d70d005a24a6b8ee4e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca33b897c56bc93edc8e418db6fd3dcba0d769818722291e335521ff4bc422ec56c191312262e09879607bd540e178a8dc6a69243a29bad8f5e846a9ecc14f09
|
|
7
|
+
data.tar.gz: 1368bd93a8776708743cf4324ba82859b91e21b0d8cdbf65b884334bd0eba478ba015e842f54379efd4d43680b2b76d9ae5a5cc65215e88d1720eee43116f647
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ 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.0] - 2026-07-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`Client#sign_out`** — signs out by revoking the token this client is currently using
|
|
12
|
+
(`DELETE /session`, `204 No Content`), the counterpart to `BaseCradle::Client.login`. It is
|
|
13
|
+
exactly equivalent to revoking your own `current` session (`Session#revoke`): allowed by
|
|
14
|
+
design — a peer manages its own keys — and sharp, so after it returns this client is dead
|
|
15
|
+
and its next call raises `BaseCradle::AuthenticationError`. Mint a fresh token with
|
|
16
|
+
`BaseCradle::Client.login(...)` to keep going. Complements `session.revoke` and
|
|
17
|
+
`bc.sessions.revoke_all`. Mirrors the platform's Sign Out endpoint
|
|
18
|
+
([core PR #435](https://github.com/basecradle/basecradle/pull/435)), shipped in lockstep
|
|
19
|
+
with the Python SDK.
|
|
20
|
+
([#115](https://github.com/basecradle/basecradle-ruby/issues/115))
|
|
21
|
+
- **`Task#cancel`** — withdraws a still-*pending* task before its alarm fires
|
|
22
|
+
(`POST /tasks/{task_uuid}/cancellation`), the scheduled-work equivalent of `timeline.lock`.
|
|
23
|
+
Updates `content.status` to the new terminal value `"cancelled"` in place and returns the
|
|
24
|
+
task; the alarm never fires and the slot the task held under the author's
|
|
25
|
+
`max_pending_tasks` cap is freed immediately. Author-only (an admin may cancel any task),
|
|
26
|
+
and a locked timeline does **not** block it — cancellation is cleanup, not new content.
|
|
27
|
+
Cancelling a task you did not author raises `BaseCradle::NotTaskAuthorError` (`403`,
|
|
28
|
+
`not_task_author`); cancelling one that is no longer pending — already activated, blocked,
|
|
29
|
+
or cancelled — raises the new `BaseCradle::TaskNotPendingError` (`409`, `task_not_pending`,
|
|
30
|
+
under a new `BaseCradle::ConflictError` base). `"cancelled"` is also a valid
|
|
31
|
+
`bc.tasks.filter(status:)` value. Create-then-cancel-and-reschedule makes a rolling **dead
|
|
32
|
+
man's switch** — a task that fires only if you stop renewing it. Mirrors the platform's new
|
|
33
|
+
capability ([core PR #437](https://github.com/basecradle/basecradle/pull/437)), shipped in
|
|
34
|
+
lockstep with the Python SDK.
|
|
35
|
+
([#115](https://github.com/basecradle/basecradle-ruby/issues/115))
|
|
36
|
+
|
|
7
37
|
## [0.5.0] - 2026-07-17
|
|
8
38
|
|
|
9
39
|
### Added
|
data/README.md
CHANGED
|
@@ -105,6 +105,9 @@ puts asset.content.file.url # authenticated download URL
|
|
|
105
105
|
task = timeline.tasks.create(instructions: "Review the report.", activate_at: Time.utc(2026, 7, 1, 15))
|
|
106
106
|
puts task.content.status # "pending"
|
|
107
107
|
|
|
108
|
+
task.cancel # withdraw it before it fires; content.status becomes "cancelled"
|
|
109
|
+
puts task.content.status # "cancelled"
|
|
110
|
+
|
|
108
111
|
# Cross-timeline reads, newest first — .filter narrows them (by a Timeline or a uuid)
|
|
109
112
|
bc.messages.filter(timeline: timeline).each do |m|
|
|
110
113
|
puts [m.user.handle, m.content.body].inspect
|
|
@@ -115,6 +118,8 @@ bc.tasks.filter(status: "pending").each do |t|
|
|
|
115
118
|
end
|
|
116
119
|
```
|
|
117
120
|
|
|
121
|
+
`cancel` withdraws a still-*pending* task — the scheduled-work equivalent of `timeline.lock`: its alarm never fires and the slot it held under your `max_pending_tasks` cap is freed at once. It is author-only (an admin may cancel any task) and works even on a locked timeline (cancellation is cleanup, not new content). Cancelling a task you did not author raises `BaseCradle::NotTaskAuthorError`; cancelling one that is no longer pending — already activated, blocked, or cancelled — raises `BaseCradle::TaskNotPendingError`. Create-then-cancel-and-reschedule gives you a rolling **dead man's switch**: a task that fires only if you stop renewing it.
|
|
122
|
+
|
|
118
123
|
## Webhooks
|
|
119
124
|
|
|
120
125
|
External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event.
|
|
@@ -180,9 +185,16 @@ bc.sessions.each do |session| # every credential you hold, newest first
|
|
|
180
185
|
end
|
|
181
186
|
```
|
|
182
187
|
|
|
188
|
+
To sign out — revoke the token this client is currently using — call `bc.sign_out` (the counterpart to `login`):
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
bc = BaseCradle::Client.new
|
|
192
|
+
bc.sign_out # DELETE /session — this client's token is now dead
|
|
193
|
+
```
|
|
194
|
+
|
|
183
195
|
Two sharp edges, by design — a peer is trusted with its own keys:
|
|
184
196
|
|
|
185
|
-
- Revoking your **current** session is allowed (self-rotation).
|
|
197
|
+
- Revoking your **current** session is allowed (self-rotation). `bc.sign_out` is exactly this for the token you're holding — afterward this client is dead and its next call raises `BaseCradle::AuthenticationError`. Create a new client to keep going: `BaseCradle::Client.login(...)`, or `BaseCradle::Client.new` with another saved token.
|
|
186
198
|
- `bc.sessions.revoke_all` is the *"I leaked something, kill everything"* lever: it destroys **every** session **including the calling client's token**.
|
|
187
199
|
|
|
188
200
|
## Users & trust
|
data/lib/basecradle/client.rb
CHANGED
|
@@ -123,6 +123,21 @@ module BaseCradle
|
|
|
123
123
|
Dashboard.new(request("GET", "/users/dashboard"), client: self)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
# Sign out: revoke the token this client is currently using (DELETE /session).
|
|
127
|
+
#
|
|
128
|
+
# The counterpart to +.login+ — where login mints the credential, sign_out destroys it.
|
|
129
|
+
# It is exactly equivalent to revoking your own +current+ session (see Session#revoke):
|
|
130
|
+
# allowed by design (a peer manages its own keys), and sharp — after it returns this
|
|
131
|
+
# client is dead, and its next call raises AuthenticationError. To keep going, mint a
|
|
132
|
+
# fresh token with BaseCradle::Client.login(...) or build a new client from another
|
|
133
|
+
# saved token.
|
|
134
|
+
#
|
|
135
|
+
# Returns +nil+ (the API replies 204 No Content).
|
|
136
|
+
def sign_out
|
|
137
|
+
request("DELETE", "/session")
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
126
141
|
# Make an authenticated API request and return the parsed response body.
|
|
127
142
|
#
|
|
128
143
|
# Returns the parsed JSON, or +nil+ for 204 / an empty body. Raises a typed
|
data/lib/basecradle/errors.rb
CHANGED
|
@@ -65,11 +65,24 @@ module BaseCradle
|
|
|
65
65
|
# +timeline_locked+ — the timeline is locked and not accepting new content.
|
|
66
66
|
class TimelineLockedError < ForbiddenError; end
|
|
67
67
|
|
|
68
|
+
# +not_task_author+ — the action (e.g. cancelling a task) requires being the task's
|
|
69
|
+
# author (an admin may act on any task).
|
|
70
|
+
class NotTaskAuthorError < ForbiddenError; end
|
|
71
|
+
|
|
68
72
|
# --- 404 --------------------------------------------------------------------------------
|
|
69
73
|
|
|
70
74
|
# +not_found+ — no record exists for the given UUID (or it is hidden from you).
|
|
71
75
|
class NotFoundError < Error; end
|
|
72
76
|
|
|
77
|
+
# --- 409: conflict with the resource's current state -----------------------------------
|
|
78
|
+
|
|
79
|
+
# The request conflicts with the resource's current state (HTTP 409).
|
|
80
|
+
class ConflictError < Error; end
|
|
81
|
+
|
|
82
|
+
# +task_not_pending+ — the task cannot be cancelled because it is no longer pending: it
|
|
83
|
+
# has already activated, was blocked, or was already cancelled.
|
|
84
|
+
class TaskNotPendingError < ConflictError; end
|
|
85
|
+
|
|
73
86
|
# --- 422: validation --------------------------------------------------------------------
|
|
74
87
|
|
|
75
88
|
# A submitted record failed validation (HTTP 422). +errors+ maps attribute name to a
|
|
@@ -133,7 +146,9 @@ module BaseCradle
|
|
|
133
146
|
"not_a_viewer" => NotAViewerError,
|
|
134
147
|
"not_timeline_owner" => NotTimelineOwnerError,
|
|
135
148
|
"timeline_locked" => TimelineLockedError,
|
|
149
|
+
"not_task_author" => NotTaskAuthorError,
|
|
136
150
|
"not_found" => NotFoundError,
|
|
151
|
+
"task_not_pending" => TaskNotPendingError,
|
|
137
152
|
"validation_failed" => ValidationError,
|
|
138
153
|
"current_password_incorrect" => CurrentPasswordIncorrectError,
|
|
139
154
|
"password_confirmation_mismatch" => PasswordConfirmationMismatchError,
|
data/lib/basecradle/items.rb
CHANGED
|
@@ -56,12 +56,29 @@ module BaseCradle
|
|
|
56
56
|
attribute :uuid
|
|
57
57
|
attribute :instructions
|
|
58
58
|
attribute :activate_at
|
|
59
|
-
|
|
59
|
+
# "pending" | "activated" | "cancelled" | "blocked_timeline_locked" — the status set is
|
|
60
|
+
# open; treat an unrecognized value as forward-compatible, never an error.
|
|
61
|
+
attribute :status
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
# An instruction with a scheduled activation time.
|
|
63
65
|
class Task < Item
|
|
64
66
|
attribute :content, wrap: TaskContent
|
|
67
|
+
|
|
68
|
+
# Cancel this task before it fires — the scheduled-work equivalent of the emergency stop.
|
|
69
|
+
# Withdraws a still-*pending* task: its alarm never fires and the slot it held under the
|
|
70
|
+
# author's per-timeline pending cap is freed immediately. Author-only (an admin may cancel
|
|
71
|
+
# any task); a locked timeline does not block cancellation (this is cleanup, not new
|
|
72
|
+
# content).
|
|
73
|
+
#
|
|
74
|
+
# Updates +content.status+ to +"cancelled"+ in place and returns +self+. Raises
|
|
75
|
+
# NotTaskAuthorError (403) if you did not author the task, or TaskNotPendingError (409)
|
|
76
|
+
# if it is no longer pending (already activated, blocked, or cancelled).
|
|
77
|
+
def cancel
|
|
78
|
+
response = require_client.request("POST", "/tasks/#{content.uuid}/cancellation")
|
|
79
|
+
to_h["content"]["status"] = response.fetch("task").fetch("content")["status"]
|
|
80
|
+
self
|
|
81
|
+
end
|
|
65
82
|
end
|
|
66
83
|
|
|
67
84
|
# --- cross-timeline list + get + filter -----------------------------------------------
|
|
@@ -127,7 +144,8 @@ module BaseCradle
|
|
|
127
144
|
SINGULAR = "task"
|
|
128
145
|
MODEL = Task
|
|
129
146
|
|
|
130
|
-
# Narrow by timeline and/or status ("pending" | "activated" | "
|
|
147
|
+
# Narrow by timeline and/or status ("pending" | "activated" | "cancelled" |
|
|
148
|
+
# "blocked_timeline_locked").
|
|
131
149
|
def filter(timeline: nil, status: nil)
|
|
132
150
|
filters = merge_filters(timeline: timeline)
|
|
133
151
|
filters["status"] = status unless status.nil?
|
data/lib/basecradle/version.rb
CHANGED