ordainer 0.1.0 → 0.1.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 +37 -1
- data/README.md +31 -1
- data/lib/ordainer/deny_response.rb +118 -9
- data/lib/ordainer/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53bc08e22508421e545a73555380d25329bc5c7cffb96f6476b479a416eb7586
|
|
4
|
+
data.tar.gz: 90543ec827c7accd06f17893821df1051ccd8b2faa5f6d4bcbc1351158518fab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76c013a98e2d18e2d4306c975660b95a06c4aa1484a99311a3fd2f0a5175dfd394cde60915da49222026174cd56609f7b055f058b37d25396e0cdc8680b8b46c
|
|
7
|
+
data.tar.gz: c4deb82535a6f9cb7134358e609a966cf17a76b00d85257bbcd52b5e5470d9ade3a7884d686561c63e3736446837a3a430927ad31a7d59ca016d3588688e26de
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1 (2026-10-18)
|
|
4
|
+
- Fixed (QA P95F-N4): `Ordainer.deny_response` no longer raises `FloatDomainError` when the server's `resets_at` parses but
|
|
5
|
+
gives a non-finite number of seconds (for example a fraction hundreds of digits long); there is no `Retry-After` header
|
|
6
|
+
then, as for an unknown reset time. Status, code and body are unchanged.
|
|
7
|
+
- Changed (PM final wording, Oct 18; replaces the P95R-N3 text below): Error messages reworded; codes, statuses and fields unchanged. Match on error.code, not the message. Same bytes as the Node SDK. `503 ordainer_unavailable`: "This call wasn't run. The service couldn't finish a safety check it does before each call, so it stopped instead of going ahead without it. Nothing is wrong on your side. Please try again in a minute. (ordainer_unavailable)".
|
|
8
|
+
`429 test_limit_reached`: "This call wasn't run. This service's test calls for the month are used up. They reset on
|
|
9
|
+
November 1 (UTC). Nothing is wrong on your side, and trying again won't help until they reset. If you need it sooner, contact
|
|
10
|
+
the service's team. (test_limit_reached)". The reset day is `resets_at` in UTC as "<Month D>" (full English month, no
|
|
11
|
+
leading zero, no year; no locale APIs), and the sentence appears only when `resets_at` is a valid ISO-8601 date-time
|
|
12
|
+
String (`YYYY-MM-DDTHH:MM:SS`, optional fraction, `Z` or `±HH:MM`); nil, empty, numbers, garbage or trailing text leave
|
|
13
|
+
it out (closes P95R-N4 for this message; `plan_limit_reached` is unchanged). `403 plan_denied` with `tool_not_in_plan`:
|
|
14
|
+
"This call wasn't run because your plan doesn't include "<tool>". If you need it, ask whoever manages your plan.
|
|
15
|
+
(tool_not_in_plan)", where `<tool>` is the tool passed to `decide` / `decide!` / the Rails guard (no extra request),
|
|
16
|
+
quoted only when it matches `\A[A-Za-z0-9][A-Za-z0-9_.:/-]{0,63}\z` and has no `//` and no leading `scheme:` (`\A[A-Za-z][A-Za-z0-9+.-]*:`, so nothing URL-like is echoed), otherwise "this action" without quotes. Any other
|
|
17
|
+
plan deny: "This call wasn't run because it isn't allowed on your account right now. If you need it, contact the
|
|
18
|
+
service's team. (<reason>)", with the bracket only when the reason matches `\A[a-z][a-z_]{0,39}\z`. Status, `code`,
|
|
19
|
+
`reason`, `resets_at`, `request_id`, headers and whether the call runs are unchanged.
|
|
20
|
+
- Changed (P95R-N3, Content & DevRel Addendum 3, same bytes as the Node SDK): three more caller messages drop our name
|
|
21
|
+
and our terms. `503 ordainer_unavailable`: "This call wasn't run. The service couldn't complete a check it needs before
|
|
22
|
+
running it, and it's set to stop rather than go ahead without one. Please try again in a moment. (ordainer_unavailable)".
|
|
23
|
+
`429 test_limit_reached`: "This call wasn't run. The service's test allowance for this month is used up. It resets at
|
|
24
|
+
{resets_at}. It isn't something you did, and retrying won't help until the allowance resets. (test_limit_reached)" (no
|
|
25
|
+
"It resets at" sentence when `resets_at` is nil or empty; an empty value used to render "They reset at ."). `403
|
|
26
|
+
plan_denied`: "This call wasn't run because this agent's plan doesn't allow it. ({reason})", with the bracketed reason
|
|
27
|
+
only when it matches `\A[a-z][a-z_]{0,39}\z` (the same check as the `plan_check_rejected` message); otherwise it ends at
|
|
28
|
+
"allow it.". Before, the Ruby 403 message never named the reason. Only `message` (and the raised errors' `message`)
|
|
29
|
+
changes: status, `code` (still `ordainer_unavailable`), `reason`, `resets_at`, `request_id`, headers and whether the call
|
|
30
|
+
runs are unchanged.
|
|
31
|
+
- Changed (T18-P40, same strings as the Node SDK): the caller's `500 plan_check_rejected` message says what kind of setup
|
|
32
|
+
issue it is (a key isn't recognized, has been turned off, or is missing; anything else) and ends with the API's code for
|
|
33
|
+
the service's team. The `429 rate_limited` message no longer names us: "The service is getting too many requests on this
|
|
34
|
+
key right now, so this call was held back. Please wait a moment and try again. (rate_limited)". A `429 concurrency_limited`
|
|
35
|
+
now gets its own: "The service is handling too many calls on this key at once, so this call was held back. Please wait a moment and try again. (concurrency_limited)" (the `code` field stays `rate_limited`). Only `message` changes:
|
|
36
|
+
status, `code`, headers (`Retry-After`, `X-Request-Id`), `retry_after`, `rejected?` / `rate_limited?` and whether the call
|
|
37
|
+
runs are unchanged. The 403 billing message is unchanged.
|
|
38
|
+
|
|
39
|
+
## 0.1.0 (2026-10-14)
|
|
4
40
|
- First version: Ordainer::Client#decide, Decision, typed errors, retries, fail_mode, optional Rails guard.
|
data/README.md
CHANGED
|
@@ -4,7 +4,17 @@ Ask Ordainer before your tool runs: one `decide` call checks the caller's plan a
|
|
|
4
4
|
Ruby 3.0+, standard library only (net/http, json), no runtime dependencies. Mirrors the Node SDK
|
|
5
5
|
(`packages/ordainer-node`, 1.2.2).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install from [RubyGems](https://rubygems.org/gems/ordainer):
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
gem install ordainer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
or in your Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "ordainer", "~> 0.1"
|
|
17
|
+
```
|
|
8
18
|
|
|
9
19
|
```ruby
|
|
10
20
|
require "ordainer"
|
|
@@ -47,6 +57,26 @@ end
|
|
|
47
57
|
`fail_mode` (`rejected?`). 403 `subscription_inactive` follows `billing_lapse` instead. 429
|
|
48
58
|
`safety_limit_reached` lets the call run (`safety_limited`).
|
|
49
59
|
- Only timeouts, network errors and 503 are retried, never other 5xx or 4xx.
|
|
60
|
+
- What the caller reads (`Ordainer.deny_response`, same text as the Node SDK): a block a retry can't fix answers
|
|
61
|
+
`500 plan_check_rejected` with plain words for the cause and the API's code last, e.g. "This call wasn't run. This is a
|
|
62
|
+
setup issue on the service's side: one of its keys isn't recognized. It isn't something you did, and retrying won't help
|
|
63
|
+
until the service fixes the key. (For the service's team: unknown_key.)" A `429 rate_limited` answers "The service is
|
|
64
|
+
getting too many requests on this key right now, so this call was held back. Please wait a moment and try again.
|
|
65
|
+
(rate_limited)" with `Retry-After`; a `429 concurrency_limited` answers "The service is handling too many calls on this key at once, so this call was held back. Please wait a moment and try again. (concurrency_limited)" (the `code` field
|
|
66
|
+
stays `rate_limited`). Billing blocks (403) keep the Legal-approved text.
|
|
67
|
+
- The same text as the Node SDK for the other blocks too (PM final wording, Oct 18). Error messages can be reworded;
|
|
68
|
+
codes, statuses and fields stay the same, so match on `code`, not the message. An outage under `fail_mode: :closed`
|
|
69
|
+
answers `503 ordainer_unavailable` with `Retry-After: 5`: "This call wasn't run. The service couldn't finish a safety check it does before each call, so it stopped instead of going ahead without it. Nothing is wrong on your side. Please try again in a minute. (ordainer_unavailable)".
|
|
70
|
+
A used-up test allowance answers `429 test_limit_reached`: "This call wasn't run. This service's test calls for the
|
|
71
|
+
month are used up. They reset on November 1 (UTC). Nothing is wrong on your side, and trying again won't help until
|
|
72
|
+
they reset. If you need it sooner, contact the service's team. (test_limit_reached)" (the day is `resets_at` in UTC as
|
|
73
|
+
"<Month D>"; no "They reset on" sentence unless `resets_at` is a valid ISO-8601 date-time). A `tool_not_in_plan` deny
|
|
74
|
+
answers `403 plan_denied`: "This call wasn't run because your plan doesn't include "research.run". If you need it, ask
|
|
75
|
+
whoever manages your plan. (tool_not_in_plan)", naming the tool you passed to `decide` (or the Rails guard) in quotes
|
|
76
|
+
only when it matches `\A[A-Za-z0-9][A-Za-z0-9_.:/-]{0,63}\z` and has no `//` and no leading `scheme:` (`\A[A-Za-z][A-Za-z0-9+.-]*:`, so nothing URL-like is echoed), otherwise "this action". Any other deny answers "This
|
|
77
|
+
call wasn't run because it isn't allowed on your account right now. If you need it, contact the service's team.
|
|
78
|
+
(reason)", with the reason in brackets only when it looks like an API code (lowercase letters and `_`, up to 40
|
|
79
|
+
characters); the reason is also its own `reason` field.
|
|
50
80
|
|
|
51
81
|
## Decision
|
|
52
82
|
|
|
@@ -9,6 +9,34 @@ module Ordainer
|
|
|
9
9
|
TEST_LIMIT_REASON = "test_limit_reached"
|
|
10
10
|
# Legal-approved (Oct 3; tech/billing-lapse-scope.md item 2), verbatim, same as the Node SDK.
|
|
11
11
|
BILLING_BLOCKED_MESSAGE = "This action isn't available right now. This is a setup issue on the service's side; retrying won't help."
|
|
12
|
+
# Caller-facing text for a 429 rate_limited / concurrency_limited block, same as the Node SDK (Content & DevRel, Oct 18):
|
|
13
|
+
# no product name. Only the message differs; the code field stays rate_limited for both, and status, Retry-After and
|
|
14
|
+
# retry_after are unchanged.
|
|
15
|
+
RATE_LIMITED_MESSAGE = "The service is getting too many requests on this key right now, so this call was held back. Please wait a moment and try again. (rate_limited)"
|
|
16
|
+
CONCURRENCY_LIMITED_MESSAGE = "The service is handling too many calls on this key at once, so this call was held back. Please wait a moment and try again. (concurrency_limited)"
|
|
17
|
+
# T18-P40 (Content & DevRel final wording, Oct 18; same strings as the Node SDK's rejectedMessage()): the caller-facing
|
|
18
|
+
# sentence for a block a retry can't fix (500 plan_check_rejected). Plain words first, the API's own code last for the
|
|
19
|
+
# service's team; the code is echoed only if it looks like an API code (lowercase letters and _, <= 40 chars).
|
|
20
|
+
# too_many_failed_auth stays with "isn't recognized": the API's "couldn't check this key" variant has the same status,
|
|
21
|
+
# code, limit_scope and headers; only its English message differs.
|
|
22
|
+
KEY_NOT_RECOGNIZED_CODES = %w[unknown_key old_key_format too_many_failed_auth].freeze
|
|
23
|
+
REJECTED_TAIL = "It isn't something you did, and retrying won't help until the service"
|
|
24
|
+
# P95R-N3 (Content & DevRel, Oct 18, content/persona-2-2026-10-18/p40-sdk-key-messages.md Addendum 3; same bytes as
|
|
25
|
+
# the Node SDK): the 503 fail-closed outage, the 429 test allowance and the 403 plan deny. No product name and none of
|
|
26
|
+
# our terms. Only the message changes: status, code (ordainer_unavailable is kept), headers and fields are unchanged.
|
|
27
|
+
# Text replaced by the PM's final wording (Oct 18); codes and fields are as before.
|
|
28
|
+
UNAVAILABLE_MESSAGE = "This call wasn't run. The service couldn't finish a safety check it does before each call, so it stopped instead of going ahead without it. Nothing is wrong on your side. Please try again in a minute. (#{UNAVAILABLE_CODE})"
|
|
29
|
+
API_CODE_SHAPE = /\A[a-z][a-z_]{0,39}\z/
|
|
30
|
+
# PM final wording (Oct 18): tool_not_in_plan names the tool sent in decide(), in double quotes, only when it looks
|
|
31
|
+
# like a plain tool name; otherwise "this action". Same check and bytes as the Node SDK's TOOL_NAME_SHAPE.
|
|
32
|
+
TOOL_NAME_SHAPE = /\A[A-Za-z0-9][A-Za-z0-9_.:\/-]{0,63}\z/
|
|
33
|
+
# QA P95F-N3: nothing URL-like is echoed either: no "//" anywhere and no leading "scheme:" (RFC 3986 shape). Same as Node.
|
|
34
|
+
TOOL_SCHEME_PREFIX = /\A[A-Za-z][A-Za-z0-9+.-]*:/
|
|
35
|
+
# Addendum 3 amendment (Oct 18, CoS): the reset day as "<Month D>" in UTC (full English month, no leading zero, no
|
|
36
|
+
# year). Only for a String that is a valid ISO-8601 date-time (YYYY-MM-DDTHH:MM:SS, optional .fraction, then Z or
|
|
37
|
+
# +HH:MM/-HH:MM). Fixed month list and integer UTC math only (no Time.parse, no locale), same bytes as Node.
|
|
38
|
+
MONTHS = %w[January February March April May June July August September October November December].freeze
|
|
39
|
+
ISO_DATE_TIME = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|([+-])(\d{2}):(\d{2}))\z/
|
|
12
40
|
|
|
13
41
|
module_function
|
|
14
42
|
|
|
@@ -21,28 +49,26 @@ module Ordainer
|
|
|
21
49
|
retry_after = (d.retry_after_sec || 1).to_s
|
|
22
50
|
return resp(429, { "Retry-After" => retry_after }.merge(rid),
|
|
23
51
|
code: "rate_limited",
|
|
24
|
-
message:
|
|
52
|
+
message: d.reason == "concurrency_limited" ? CONCURRENCY_LIMITED_MESSAGE : RATE_LIMITED_MESSAGE,
|
|
25
53
|
request_id: d.request_id, retry_after: retry_after.to_i)
|
|
26
54
|
end
|
|
27
55
|
if d.rejected || (d.fail_closed && d.billing_inactive)
|
|
28
56
|
billing = d.billing_inactive
|
|
29
57
|
return resp(billing ? 403 : 500, rid,
|
|
30
58
|
code: REJECTED_CODE,
|
|
31
|
-
message: billing ? BILLING_BLOCKED_MESSAGE :
|
|
59
|
+
message: billing ? BILLING_BLOCKED_MESSAGE : rejected_message(d.error&.code),
|
|
32
60
|
request_id: d.request_id)
|
|
33
61
|
end
|
|
34
62
|
if d.fail_closed
|
|
35
63
|
return resp(503, { "Retry-After" => "5" }.merge(rid),
|
|
36
64
|
code: UNAVAILABLE_CODE,
|
|
37
|
-
message:
|
|
65
|
+
message: UNAVAILABLE_MESSAGE,
|
|
38
66
|
request_id: d.request_id)
|
|
39
67
|
end
|
|
40
68
|
if d.reason == TEST_LIMIT_REASON
|
|
41
69
|
return resp(429, retry_after_header(d.resets_at, now).merge(rid),
|
|
42
70
|
code: TEST_LIMIT_REASON,
|
|
43
|
-
message:
|
|
44
|
-
(d.resets_at ? " They reset at #{d.resets_at}." : "") +
|
|
45
|
-
" This is a sandbox limit on test keys, not a plan limit and not an outage. Live keys are not affected. This call was not run.",
|
|
71
|
+
message: test_limit_message(d.resets_at),
|
|
46
72
|
request_id: d.request_id, reason: d.reason, resets_at: d.resets_at)
|
|
47
73
|
end
|
|
48
74
|
if d.reason == "quota_exceeded"
|
|
@@ -53,20 +79,103 @@ module Ordainer
|
|
|
53
79
|
" Retrying before then will not succeed. This call was not run.",
|
|
54
80
|
request_id: d.request_id, reason: d.reason, remaining: d.remaining, limit: d.limit, resets_at: d.resets_at)
|
|
55
81
|
end
|
|
56
|
-
# Node
|
|
82
|
+
# Same as Node: the reason in brackets at the end, only when it looks like an API code.
|
|
57
83
|
resp(403, rid,
|
|
58
84
|
code: "plan_denied",
|
|
59
|
-
message:
|
|
85
|
+
message: plan_denied_message(d.reason, d.tool),
|
|
60
86
|
request_id: d.request_id, reason: d.reason)
|
|
61
87
|
end
|
|
62
88
|
|
|
89
|
+
def rejected_message(code)
|
|
90
|
+
msg =
|
|
91
|
+
if code == "missing_key"
|
|
92
|
+
"This call wasn't run. This is a setup issue on the service's side: a key it needs is missing. #{REJECTED_TAIL} adds the key."
|
|
93
|
+
elsif code == "revoked_key"
|
|
94
|
+
"This call wasn't run. This is a setup issue on the service's side: one of its keys has been turned off. #{REJECTED_TAIL} switches to a new key."
|
|
95
|
+
elsif KEY_NOT_RECOGNIZED_CODES.include?(code)
|
|
96
|
+
"This call wasn't run. This is a setup issue on the service's side: one of its keys isn't recognized. #{REJECTED_TAIL} fixes the key."
|
|
97
|
+
else
|
|
98
|
+
"This call wasn't run because of a setup issue on the service's side. #{REJECTED_TAIL} fixes it."
|
|
99
|
+
end
|
|
100
|
+
code.is_a?(String) && code.match?(API_CODE_SHAPE) ? "#{msg} (For the service's team: #{code}.)" : msg
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Same rules as Node's resetDayUtc(): nil for anything that isn't a valid ISO-8601 date-time String.
|
|
104
|
+
def reset_day_utc(v)
|
|
105
|
+
return nil unless v.is_a?(String) && v.valid_encoding?
|
|
106
|
+
|
|
107
|
+
m = ISO_DATE_TIME.match(v)
|
|
108
|
+
return nil unless m
|
|
109
|
+
|
|
110
|
+
y, mo, d, h, mi, s = m.captures[0, 6].map(&:to_i)
|
|
111
|
+
leap = (y % 4).zero? && !(y % 100).zero? || (y % 400).zero?
|
|
112
|
+
dim = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
113
|
+
return nil if mo < 1 || mo > 12 || d < 1 || d > dim[mo - 1] || h > 23 || mi > 59 || s > 59
|
|
114
|
+
|
|
115
|
+
off = 0
|
|
116
|
+
if m[7]
|
|
117
|
+
oh = m[8].to_i
|
|
118
|
+
om = m[9].to_i
|
|
119
|
+
return nil if oh > 23 || om > 59
|
|
120
|
+
|
|
121
|
+
off = (m[7] == "-" ? -1 : 1) * (oh * 60 + om)
|
|
122
|
+
end
|
|
123
|
+
# Minutes from local midnight, shifted to UTC; at most one day either way.
|
|
124
|
+
mins = h * 60 + mi - off
|
|
125
|
+
uy, um, ud = y, mo, d
|
|
126
|
+
if mins.negative?
|
|
127
|
+
ud -= 1
|
|
128
|
+
if ud < 1
|
|
129
|
+
um -= 1
|
|
130
|
+
if um < 1
|
|
131
|
+
um = 12
|
|
132
|
+
uy -= 1
|
|
133
|
+
end
|
|
134
|
+
ud = dim[um - 1] # February only from March 1 (same year)
|
|
135
|
+
end
|
|
136
|
+
elsif mins >= 1440
|
|
137
|
+
ud += 1
|
|
138
|
+
if ud > dim[mo - 1]
|
|
139
|
+
ud = 1
|
|
140
|
+
um += 1
|
|
141
|
+
um = 1 if um > 12
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
"#{MONTHS[um - 1]} #{ud}"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def test_limit_message(resets_at)
|
|
148
|
+
day = reset_day_utc(resets_at)
|
|
149
|
+
"This call wasn't run. This service's test calls for the month are used up." +
|
|
150
|
+
(day ? " They reset on #{day} (UTC)." : "") +
|
|
151
|
+
" Nothing is wrong on your side, and trying again won't help until they reset. If you need it sooner, contact the service's team. (test_limit_reached)"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def showable_tool?(tool)
|
|
155
|
+
tool.is_a?(String) && tool.valid_encoding? && tool.match?(TOOL_NAME_SHAPE) && !tool.include?("//") &&
|
|
156
|
+
!tool.match?(TOOL_SCHEME_PREFIX)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def plan_denied_message(reason, tool = nil)
|
|
160
|
+
if reason == "tool_not_in_plan"
|
|
161
|
+
what = showable_tool?(tool) ? "\"#{tool}\"" : "this action"
|
|
162
|
+
return "This call wasn't run because your plan doesn't include #{what}. If you need it, ask whoever manages your plan. (tool_not_in_plan)"
|
|
163
|
+
end
|
|
164
|
+
msg = "This call wasn't run because it isn't allowed on your account right now. If you need it, contact the service's team."
|
|
165
|
+
reason.is_a?(String) && reason.valid_encoding? && reason.match?(API_CODE_SHAPE) ? "#{msg} (#{reason})" : msg
|
|
166
|
+
end
|
|
167
|
+
|
|
63
168
|
def resp(status, headers, **error)
|
|
64
169
|
{ status: status, headers: headers, body: { error: error } }
|
|
65
170
|
end
|
|
66
171
|
|
|
67
172
|
def retry_after_header(resets_at, now)
|
|
68
173
|
t = Time.iso8601(resets_at.to_s) rescue nil
|
|
69
|
-
|
|
174
|
+
return {} unless t
|
|
175
|
+
|
|
176
|
+
# QA P95F-N4: a huge fraction can make the difference NaN/Infinity; no header then, never a FloatDomainError.
|
|
177
|
+
secs = (t - now).to_f
|
|
178
|
+
secs.finite? ? { "Retry-After" => [1, secs.ceil].max.to_s } : {}
|
|
70
179
|
end
|
|
71
180
|
private_class_method :resp, :retry_after_header
|
|
72
181
|
end
|
data/lib/ordainer/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ordainer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ordainer
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-10-18 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
executables: []
|
|
13
13
|
extensions: []
|