k9o 1.2.0 → 1.4.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 +55 -1
- data/README.md +359 -68
- data/lib/k9o/ai.rb +15 -0
- data/lib/k9o/approvals.rb +159 -0
- data/lib/k9o/audit.rb +12 -14
- data/lib/k9o/client.rb +11 -7
- data/lib/k9o/compute.rb +32 -15
- data/lib/k9o/data.rb +67 -1
- data/lib/k9o/functions.rb +48 -9
- data/lib/k9o/gate.rb +9 -18
- data/lib/k9o/http_transport.rb +10 -5
- data/lib/k9o/kibo.rb +3 -3
- data/lib/k9o/me.rb +27 -87
- data/lib/k9o/org.rb +81 -68
- data/lib/k9o/orgs.rb +1 -0
- data/lib/k9o/policies.rb +25 -6
- data/lib/k9o/projects.rb +43 -8
- data/lib/k9o/query.rb +174 -0
- data/lib/k9o/realtime.rb +13 -35
- data/lib/k9o/region.rb +20 -18
- data/lib/k9o/schema.rb +35 -0
- data/lib/k9o/streams.rb +94 -0
- data/lib/k9o/studio.rb +21 -11
- data/lib/k9o/url_grammar.rb +46 -20
- data/lib/k9o/vault.rb +76 -80
- data/lib/k9o/version.rb +1 -1
- data/lib/k9o.rb +4 -0
- data/sig/k9o/ai.rbs +4 -0
- data/sig/k9o/approvals.rbs +49 -0
- data/sig/k9o/gate.rbs +7 -6
- data/sig/k9o/query.rbs +26 -0
- data/sig/k9o/region.rbs +14 -3
- metadata +8 -2
data/lib/k9o/vault.rb
CHANGED
|
@@ -4,45 +4,33 @@ require 'base64'
|
|
|
4
4
|
require 'uri'
|
|
5
5
|
|
|
6
6
|
module K9o
|
|
7
|
-
# `k9o.kibo.vault.secrets.*`
|
|
7
|
+
# `k9o.kibo.vault.secrets.*` - the typed secrets surface
|
|
8
8
|
# (`services/gateway/src/routes/vault-secrets.ts`). Full family: list / get /
|
|
9
9
|
# create / reveal_challenge / reveal / update / rotate / move / delete.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# - The wire `value` field is base64 in BOTH directions, contradicting the
|
|
22
|
-
# OpenAPI's plain-language "the plaintext to seal" description (reads
|
|
23
|
-
# like it should be raw). A raw non-base64 `value` on create/update is
|
|
24
|
-
# rejected as unparseable. `create`/`update` take plaintext as a normal
|
|
25
|
-
# Ruby String and base64-encode it here; `reveal` base64-decodes the
|
|
26
|
-
# wire `value` back to plaintext before returning it — callers never
|
|
27
|
-
# see base64 on either side.
|
|
28
|
-
# - `update`/`rotate`/`move` responses are LIVE-VERIFIED NARROWER than (or
|
|
29
|
-
# entirely undocumented vs.) the OpenAPI's claimed `SecretSummary` — see
|
|
30
|
-
# the comment on each method below.
|
|
11
|
+
# - `region` is OPTIONAL on every named-secret operation: the gateway
|
|
12
|
+
# ignores any caller-supplied region and uses its own. It is sent on the
|
|
13
|
+
# query string only when the caller passes one (never by `get`, which
|
|
14
|
+
# reads the list route).
|
|
15
|
+
# - The wire `value` field is base64 in BOTH directions. `create`/`update`
|
|
16
|
+
# take plaintext as a normal Ruby String and base64-encode it here;
|
|
17
|
+
# `reveal` base64-decodes the wire `value` back to plaintext before
|
|
18
|
+
# returning it - callers never see base64 on either side.
|
|
19
|
+
# - `update`/`rotate`/`move` responses are narrower than a full summary -
|
|
20
|
+
# see the comment on each method below.
|
|
31
21
|
#
|
|
32
22
|
# `data` is returned as the raw wire Hash (camelCase string keys), matching
|
|
33
|
-
# this package's existing convention
|
|
34
|
-
#
|
|
35
|
-
# camelCase<->snake_case response transform anywhere in this gem); `reveal`
|
|
36
|
-
# is the one exception, where the `value` key is base64-decoded in place
|
|
37
|
-
# (mirroring the TS reference's own `value`-only transform).
|
|
23
|
+
# this package's existing convention; `reveal` is the one exception, where
|
|
24
|
+
# the `value` key is base64-decoded in place.
|
|
38
25
|
class VaultSecrets
|
|
26
|
+
MAX_GET_PAGES = 20
|
|
27
|
+
|
|
39
28
|
def initialize(config)
|
|
40
29
|
@config = config
|
|
41
30
|
end
|
|
42
31
|
|
|
43
32
|
# List secrets (metadata only — never lists ciphertext).
|
|
44
33
|
# `GET /@<project_slug>/vault/secrets`.
|
|
45
|
-
# The one secrets operation that does NOT need `region` (live-verified).
|
|
46
34
|
def list(prefix: nil, cursor: nil, limit: nil)
|
|
47
35
|
slug = require_project_slug
|
|
48
36
|
q = {}
|
|
@@ -56,40 +44,42 @@ module K9o
|
|
|
56
44
|
)
|
|
57
45
|
end
|
|
58
46
|
|
|
59
|
-
# Read a secret's metadata (never its value
|
|
47
|
+
# Read a secret's metadata (never its value - use `reveal_challenge` +
|
|
60
48
|
# `reveal` for that).
|
|
61
|
-
# `GET /@<project_slug>/vault/secrets/<name>?region=<region>`.
|
|
62
49
|
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
# `GET /@<project>/vault/secrets/<name>` is a reveal and always demands an
|
|
51
|
+
# `X-K9O-Reveal-Challenge` header, so metadata is read from the list route:
|
|
52
|
+
# list with the exact name as `prefix` (limit 100, following `nextCursor`
|
|
53
|
+
# for up to MAX_GET_PAGES pages) and return the secret whose `name` equals
|
|
54
|
+
# the argument. Raises `KError` `K9O_NOT_FOUND` (status 404) when none
|
|
55
|
+
# matches. `region` is accepted for symmetry and not sent.
|
|
56
|
+
def get(name, region: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
57
|
+
cursor = nil
|
|
58
|
+
MAX_GET_PAGES.times do
|
|
59
|
+
env = list(prefix: name, cursor: cursor, limit: 100)
|
|
60
|
+
data = env.data || {}
|
|
61
|
+
secret = (data['secrets'] || []).find { |s| s['name'] == name }
|
|
62
|
+
return Envelope.new(data: secret, meta: env.meta) if secret
|
|
63
|
+
|
|
64
|
+
cursor = data['nextCursor']
|
|
65
|
+
break if cursor.nil? || cursor.to_s.empty?
|
|
66
|
+
end
|
|
67
|
+
raise KError.new(
|
|
68
|
+
code: 'K9O_NOT_FOUND',
|
|
69
|
+
message: "No secret named '#{name}' in this project.",
|
|
70
|
+
context: { 'name' => name },
|
|
71
|
+
status: 404
|
|
77
72
|
)
|
|
78
73
|
end
|
|
79
74
|
|
|
80
75
|
# Create a secret under the caller's own auth.
|
|
81
|
-
# `POST /@<project_slug>/vault/secrets/<name
|
|
82
|
-
# `sec_<base62>` summary.
|
|
83
|
-
#
|
|
84
|
-
# `region` scopes the vault WRITE itself (residency guard) and is
|
|
85
|
-
# REQUIRED — live-verified against staging, not documented in the route's
|
|
86
|
-
# OpenAPI schema.
|
|
76
|
+
# `POST /@<project_slug>/vault/secrets/<name>` (+ `region` when given) -> the
|
|
77
|
+
# full `sec_<base62>` summary.
|
|
87
78
|
#
|
|
88
79
|
# `visibility` (`{'kind' => 'private'}` or
|
|
89
80
|
# `{'kind' => 'shared', 'principals' => [...], 'roles' => [...]}`) and
|
|
90
|
-
# `metadata` (`Hash[String, String]`) are passed through verbatim
|
|
91
|
-
|
|
92
|
-
def create(name:, value:, region:, visibility: nil, metadata: nil)
|
|
81
|
+
# `metadata` (`Hash[String, String]`) are passed through verbatim.
|
|
82
|
+
def create(name:, value:, region: nil, visibility: nil, metadata: nil)
|
|
93
83
|
slug = require_project_slug
|
|
94
84
|
body = { 'value' => Base64.strict_encode64(value) }
|
|
95
85
|
body['visibility'] = visibility if visibility
|
|
@@ -97,34 +87,34 @@ module K9o
|
|
|
97
87
|
HttpTransport.request(
|
|
98
88
|
config: @config, method: 'POST',
|
|
99
89
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
100
|
-
query:
|
|
90
|
+
query: region_query(region),
|
|
101
91
|
body: body
|
|
102
92
|
)
|
|
103
93
|
end
|
|
104
94
|
|
|
105
95
|
# Mint a single-use reveal challenge — present it as
|
|
106
96
|
# `X-K9O-Reveal-Challenge` on the immediately-following `reveal` call.
|
|
107
|
-
# `POST .../<name>?op=reveal-challenge
|
|
108
|
-
def reveal_challenge(name, region:)
|
|
97
|
+
# `POST .../<name>?op=reveal-challenge`.
|
|
98
|
+
def reveal_challenge(name, region: nil)
|
|
109
99
|
slug = require_project_slug
|
|
110
100
|
HttpTransport.request(
|
|
111
101
|
config: @config, method: 'POST',
|
|
112
102
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
113
|
-
query:
|
|
103
|
+
query: region_query(region, 'op' => 'reveal-challenge')
|
|
114
104
|
)
|
|
115
105
|
end
|
|
116
106
|
|
|
117
107
|
# Reveal a secret's plaintext value using a single-use challenge from
|
|
118
|
-
# `reveal_challenge`. `GET .../<name
|
|
108
|
+
# `reveal_challenge`. `GET .../<name>` with
|
|
119
109
|
# `X-K9O-Reveal-Challenge`. Base64-decodes the wire's `value` back to
|
|
120
|
-
# plaintext (
|
|
121
|
-
#
|
|
122
|
-
def reveal(name,
|
|
110
|
+
# plaintext (the reveal response's `value` is base64, symmetric with
|
|
111
|
+
# `create`'s encode) before returning it to the caller.
|
|
112
|
+
def reveal(name, challenge:, region: nil)
|
|
123
113
|
slug = require_project_slug
|
|
124
114
|
envelope = HttpTransport.request(
|
|
125
115
|
config: @config, method: 'GET',
|
|
126
116
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
127
|
-
query:
|
|
117
|
+
query: region_query(region),
|
|
128
118
|
headers: { 'X-K9O-Reveal-Challenge' => challenge }
|
|
129
119
|
)
|
|
130
120
|
data = envelope.data
|
|
@@ -135,67 +125,73 @@ module K9o
|
|
|
135
125
|
end
|
|
136
126
|
|
|
137
127
|
# Update a secret's value (bumps version) — owner-only.
|
|
138
|
-
# `PUT .../<name
|
|
128
|
+
# `PUT .../<name>`.
|
|
139
129
|
#
|
|
140
|
-
#
|
|
130
|
+
# Narrower than a full summary: the gateway returns only
|
|
141
131
|
# `{id, name, version}` on a real PUT, not the full summary the OpenAPI
|
|
142
132
|
# schema claims — call `get` afterward if the full summary is needed.
|
|
143
|
-
def update(name,
|
|
133
|
+
def update(name, value:, region: nil, metadata: nil)
|
|
144
134
|
slug = require_project_slug
|
|
145
135
|
body = { 'value' => Base64.strict_encode64(value) }
|
|
146
136
|
body['metadata'] = metadata if metadata
|
|
147
137
|
HttpTransport.request(
|
|
148
138
|
config: @config, method: 'PUT',
|
|
149
139
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
150
|
-
query:
|
|
140
|
+
query: region_query(region),
|
|
151
141
|
body: body
|
|
152
142
|
)
|
|
153
143
|
end
|
|
154
144
|
|
|
155
145
|
# Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content
|
|
156
|
-
# unchanged) — owner-only. `POST .../<name>?op=rotate
|
|
146
|
+
# unchanged) — owner-only. `POST .../<name>?op=rotate`.
|
|
157
147
|
#
|
|
158
|
-
#
|
|
148
|
+
# Response shape: `{id, name, version, rotatedAt,
|
|
159
149
|
# ledgerSequenceNumber}` — the route's OpenAPI schema doesn't document
|
|
160
150
|
# this response at all.
|
|
161
|
-
def rotate(name, region:)
|
|
151
|
+
def rotate(name, region: nil)
|
|
162
152
|
slug = require_project_slug
|
|
163
153
|
HttpTransport.request(
|
|
164
154
|
config: @config, method: 'POST',
|
|
165
155
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
166
|
-
query:
|
|
156
|
+
query: region_query(region, 'op' => 'rotate')
|
|
167
157
|
)
|
|
168
158
|
end
|
|
169
159
|
|
|
170
160
|
# Move (rename) a secret's hierarchical path — owner-only.
|
|
171
|
-
# `POST .../<name>?op=move
|
|
161
|
+
# `POST .../<name>?op=move` with `{newPath}`.
|
|
172
162
|
#
|
|
173
|
-
#
|
|
163
|
+
# Response shape: `{id, name, event}` (observed
|
|
174
164
|
# `event: "SECRET_PATH_CHANGED"`; treat as a plain String, not a closed
|
|
175
165
|
# enum — only one sample was observed).
|
|
176
|
-
def move(name,
|
|
166
|
+
def move(name, new_path:, region: nil)
|
|
177
167
|
slug = require_project_slug
|
|
178
168
|
HttpTransport.request(
|
|
179
169
|
config: @config, method: 'POST',
|
|
180
170
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
181
|
-
query:
|
|
171
|
+
query: region_query(region, 'op' => 'move'),
|
|
182
172
|
body: { 'newPath' => new_path }
|
|
183
173
|
)
|
|
184
174
|
end
|
|
185
175
|
|
|
186
|
-
# Retire a secret
|
|
187
|
-
#
|
|
188
|
-
def delete(name, region:)
|
|
176
|
+
# Retire a secret - owner-only. `DELETE .../<name>`; any 2xx is success
|
|
177
|
+
# (204 No Content maps to `data: nil`).
|
|
178
|
+
def delete(name, region: nil)
|
|
189
179
|
slug = require_project_slug
|
|
190
180
|
HttpTransport.request(
|
|
191
181
|
config: @config, method: 'DELETE',
|
|
192
182
|
path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
|
|
193
|
-
query:
|
|
183
|
+
query: region_query(region)
|
|
194
184
|
)
|
|
195
185
|
end
|
|
196
186
|
|
|
197
187
|
private
|
|
198
188
|
|
|
189
|
+
def region_query(region, extra = {})
|
|
190
|
+
q = extra.dup
|
|
191
|
+
q['region'] = region if region
|
|
192
|
+
q
|
|
193
|
+
end
|
|
194
|
+
|
|
199
195
|
def require_project_slug
|
|
200
196
|
Studio.require_project_slug(@config, 'kibo.vault.secrets')
|
|
201
197
|
end
|
|
@@ -206,8 +202,8 @@ module K9o
|
|
|
206
202
|
end
|
|
207
203
|
|
|
208
204
|
# `k9o.kibo.vault.*` — the Kibo vault surface (member-plane, project-scoped).
|
|
209
|
-
# Currently just `secrets
|
|
210
|
-
# (
|
|
205
|
+
# Currently just `secrets` ; a `zk_deks` accessor
|
|
206
|
+
# (`POST vault/zk-deks` / `GET vault/zk-deks/<id>`) would
|
|
211
207
|
# nest here alongside it if/when that surface is built for Ruby — out of
|
|
212
208
|
# scope for this wrapper, which only covers the vault-secrets audit gap.
|
|
213
209
|
class KiboVault
|
|
@@ -220,7 +216,7 @@ module K9o
|
|
|
220
216
|
|
|
221
217
|
# `k9o.kibo.*` — per-client namespace root for the Kibo vault REST surface.
|
|
222
218
|
# Distinct from the module-level `K9o::Kibo` (native ZK `encrypt`/`decrypt`
|
|
223
|
-
# FFI bindings,
|
|
219
|
+
# FFI bindings, called directly as
|
|
224
220
|
# `K9o::Kibo.encrypt`/`.decrypt` since that surface needs no per-client HTTP
|
|
225
221
|
# config): this class exists so `client.kibo.vault.secrets.*` is reachable
|
|
226
222
|
# per client instance, mirroring the TS reference's
|
data/lib/k9o/version.rb
CHANGED
data/lib/k9o.rb
CHANGED
|
@@ -29,6 +29,7 @@ require_relative 'k9o/url_grammar'
|
|
|
29
29
|
require_relative 'k9o/region'
|
|
30
30
|
require_relative 'k9o/http_transport'
|
|
31
31
|
require_relative 'k9o/data'
|
|
32
|
+
require_relative 'k9o/query'
|
|
32
33
|
require_relative 'k9o/projects'
|
|
33
34
|
require_relative 'k9o/gate'
|
|
34
35
|
require_relative 'k9o/studio'
|
|
@@ -36,6 +37,9 @@ require_relative 'k9o/compute'
|
|
|
36
37
|
require_relative 'k9o/ai'
|
|
37
38
|
require_relative 'k9o/storage'
|
|
38
39
|
require_relative 'k9o/realtime'
|
|
40
|
+
require_relative 'k9o/streams'
|
|
41
|
+
require_relative 'k9o/approvals'
|
|
42
|
+
require_relative 'k9o/schema'
|
|
39
43
|
require_relative 'k9o/migrations'
|
|
40
44
|
require_relative 'k9o/functions'
|
|
41
45
|
require_relative 'k9o/policies'
|
data/sig/k9o/ai.rbs
CHANGED
|
@@ -8,6 +8,10 @@ module K9o
|
|
|
8
8
|
# { "embeddings" => Array[Array[Float]], "model" => String, "dimensions" => Integer }.
|
|
9
9
|
def initialize: (untyped config) -> void
|
|
10
10
|
def embed: (input: (String | Array[String]), ?model: String?) -> Envelope
|
|
11
|
+
|
|
12
|
+
# Only the fields given are sent. Returns an Envelope whose `data` is
|
|
13
|
+
# { "modelId" => String, "content" => String, "finishReason" => String, "usage" => Hash, "usageType" => String }.
|
|
14
|
+
def generate: (String prompt, ?model: String?, ?max_tokens: Integer?, ?temperature: Numeric?, ?usage_type: String?) -> Envelope
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
class K9oClient
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# RBS signatures for `client.approvals`, `client.approval_policies` and `client.schema`.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
module ApprovalChecks
|
|
5
|
+
def self.invalid: (String message, ?Hash[Symbol, untyped] context) -> KError
|
|
6
|
+
def self.check_limit: (untyped limit) -> void
|
|
7
|
+
def self.id_segment: (untyped value, String what) -> String
|
|
8
|
+
def self.reason!: (untyped reason) -> String
|
|
9
|
+
def self.predicate!: (untyped value, String field) -> Hash[untyped, untyped]
|
|
10
|
+
def self.enum!: (untyped value, Array[String] allowed, String field) -> String
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Approvals
|
|
14
|
+
def initialize: (untyped config) -> void
|
|
15
|
+
# Not paginated: only `limit` (1 to 200, default 50) caps the result.
|
|
16
|
+
def list: (?status: (String | Symbol)?, ?limit: Integer?) -> Envelope
|
|
17
|
+
def get: (String id) -> Envelope
|
|
18
|
+
# `status` on the answer stays "pending_approval" while more approvers are needed.
|
|
19
|
+
def approve: (String id, ?reason: String?) -> Envelope
|
|
20
|
+
def reject: (String id, reason: String) -> Envelope
|
|
21
|
+
# Failed rows are skipped without a report: compare `affectedIds` with what you intended.
|
|
22
|
+
def bulk: (action: (String | Symbol), criteria: Hash[(String | Symbol), untyped], ?reason: String?) -> Envelope
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ApprovalPolicies
|
|
26
|
+
COLLECTION_RE: Regexp
|
|
27
|
+
|
|
28
|
+
def initialize: (untyped config) -> void
|
|
29
|
+
def list: () -> Envelope
|
|
30
|
+
def create: (collection: String, operation: (String | Symbol), action: (String | Symbol), predicate: Hash[(String | Symbol), untyped]) -> Envelope
|
|
31
|
+
def delete: (String id) -> Envelope
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class SchemaCollections
|
|
35
|
+
def initialize: (untyped config) -> void
|
|
36
|
+
def list: (?limit: Integer?, ?cursor: String?) -> Envelope
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Schema
|
|
40
|
+
attr_reader collections: SchemaCollections
|
|
41
|
+
def initialize: (untyped config) -> void
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class K9oClient
|
|
45
|
+
attr_reader approvals: Approvals
|
|
46
|
+
attr_reader approval_policies: ApprovalPolicies
|
|
47
|
+
attr_reader schema: Schema
|
|
48
|
+
end
|
|
49
|
+
end
|
data/sig/k9o/gate.rbs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
# RBS signatures for the `client.gate` namespace
|
|
1
|
+
# RBS signatures for the `client.gate` namespace.
|
|
2
2
|
|
|
3
3
|
module K9o
|
|
4
4
|
class Gate
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
def
|
|
5
|
+
def sign_up: (email: String, password: String, ?display_name: String?) -> Envelope
|
|
6
|
+
def sign_in: (email: String, password: String) -> Envelope
|
|
7
|
+
# The refresh token is required: the gateway answers 400 without it.
|
|
8
|
+
def sign_out: (refresh_token: String) -> Envelope
|
|
9
|
+
def whoami: () -> Envelope
|
|
10
|
+
def refresh_token: (refresh_token: String) -> Envelope
|
|
10
11
|
end
|
|
11
12
|
end
|
data/sig/k9o/query.rbs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# RBS signatures for `client.query`, `client.search` and the select builder's search methods.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
module Query
|
|
5
|
+
type value = String | Integer | Float | bool | nil
|
|
6
|
+
|
|
7
|
+
# Rewrites :name placeholders to $n; returns [sql, positional values].
|
|
8
|
+
def self.translate_named_params: (String sql, Hash[(String | Symbol), untyped] named) -> [String, Array[value]]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module QueryMethods
|
|
12
|
+
# An Array binds positionally to $1, $2, ...; a Hash binds :name placeholders. `data` is the array of rows.
|
|
13
|
+
def query: (String sql, ?(Array[untyped] | Hash[(String | Symbol), untyped])? params) -> Envelope
|
|
14
|
+
|
|
15
|
+
def search: (String collection, column: String, q: String, ?language: String?, ?type: String?, ?limit: Integer?, ?cursor: String?) -> Envelope
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class K9oClient
|
|
19
|
+
include QueryMethods
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class SelectBuilder
|
|
23
|
+
def text_search: (String column, String query, ?language: String, ?type: (String | Symbol)) -> self
|
|
24
|
+
def vector_search: (String column, Array[Numeric] embedding, ?metric: (String | Symbol), ?limit: Integer?) -> self
|
|
25
|
+
end
|
|
26
|
+
end
|
data/sig/k9o/region.rbs
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module K9o
|
|
4
4
|
module Region
|
|
5
|
-
REGION_FREE_ORG_HOST: Regexp
|
|
6
|
-
REGION_CODE: Regexp
|
|
7
|
-
|
|
8
5
|
# The regional origin `location` may name for `host`, or nil.
|
|
9
6
|
def self.origin_from_location: (String host, String location, String base) -> String?
|
|
10
7
|
# The origin requests go to: cached regional origin, else the configured base URL. Never raises.
|
|
@@ -17,3 +14,17 @@ module K9o
|
|
|
17
14
|
attr_accessor regional_origin: String?
|
|
18
15
|
end
|
|
19
16
|
end
|
|
17
|
+
|
|
18
|
+
module K9o
|
|
19
|
+
module UrlGrammar
|
|
20
|
+
# The org, family, region and apex of a gateway host (`acme.api-de01.corp.internal`), or nil for any other shape.
|
|
21
|
+
class ApiHost < Struct[untyped]
|
|
22
|
+
attr_reader org_slug: String?
|
|
23
|
+
attr_reader family: String
|
|
24
|
+
attr_reader region: String?
|
|
25
|
+
attr_reader apex: String
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.parse_api_host: (String host) -> ApiHost?
|
|
29
|
+
end
|
|
30
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: k9o
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kilimanjaro
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- k9o.gemspec
|
|
40
40
|
- lib/k9o.rb
|
|
41
41
|
- lib/k9o/ai.rb
|
|
42
|
+
- lib/k9o/approvals.rb
|
|
42
43
|
- lib/k9o/audit.rb
|
|
43
44
|
- lib/k9o/barbican.rb
|
|
44
45
|
- lib/k9o/client.rb
|
|
@@ -57,16 +58,21 @@ files:
|
|
|
57
58
|
- lib/k9o/orgs.rb
|
|
58
59
|
- lib/k9o/policies.rb
|
|
59
60
|
- lib/k9o/projects.rb
|
|
61
|
+
- lib/k9o/query.rb
|
|
60
62
|
- lib/k9o/realtime.rb
|
|
61
63
|
- lib/k9o/region.rb
|
|
64
|
+
- lib/k9o/schema.rb
|
|
62
65
|
- lib/k9o/storage.rb
|
|
66
|
+
- lib/k9o/streams.rb
|
|
63
67
|
- lib/k9o/studio.rb
|
|
64
68
|
- lib/k9o/url_grammar.rb
|
|
65
69
|
- lib/k9o/vault.rb
|
|
66
70
|
- lib/k9o/version.rb
|
|
67
71
|
- sig/k9o/ai.rbs
|
|
72
|
+
- sig/k9o/approvals.rbs
|
|
68
73
|
- sig/k9o/gate.rbs
|
|
69
74
|
- sig/k9o/migrations.rbs
|
|
75
|
+
- sig/k9o/query.rbs
|
|
70
76
|
- sig/k9o/region.rbs
|
|
71
77
|
- sig/k9o/storage.rbs
|
|
72
78
|
homepage: https://kilimanjaro.dev
|