k9o 1.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.
data/README.md ADDED
@@ -0,0 +1,546 @@
1
+ # k9o
2
+
3
+ The Kilimanjaro platform SDK for Ruby.
4
+
5
+ Documentation: https://kilimanjaro.dev. Platform API: https://api.k9o.io.
6
+
7
+ ## Contents
8
+
9
+ - [Install](#install)
10
+ - [Quick start](#quick-start)
11
+ - [Authentication and project context](#authentication-and-project-context)
12
+ - [Running queries](#running-queries)
13
+ - [Handling errors](#handling-errors)
14
+ - [Appendix](#appendix)
15
+ - [License](#license)
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ gem install k9o
21
+ ```
22
+
23
+ Or add it to your `Gemfile`:
24
+
25
+ ```ruby
26
+ gem "k9o"
27
+ ```
28
+
29
+ Requires Ruby 3.2 or newer. See [requirements](#requirements) for details.
30
+
31
+ ## Quick start
32
+
33
+ The examples use a `tasks` collection with `title` and `done` fields.
34
+
35
+ ### Create the client
36
+
37
+ ```ruby
38
+ require "k9o"
39
+
40
+ k9o = K9o.create_client(
41
+ org_slug: "acme",
42
+ project_slug: "task-app",
43
+ api_key: ENV.fetch("K9O_API_KEY")
44
+ )
45
+ ```
46
+
47
+ ### Add a task
48
+
49
+ ```ruby
50
+ new_task = { "title" => "Write the README", "done" => false }
51
+ created = k9o.from("tasks").insert(new_task)
52
+ puts "created: #{created.data['title']}"
53
+ ```
54
+
55
+ ```text
56
+ created: Write the README
57
+ ```
58
+
59
+ ### List tasks
60
+
61
+ ```ruby
62
+ response = k9o.from("tasks").select.eq("done", false).limit(10).execute
63
+ response.data.each do |task|
64
+ puts "#{task['title']} #{task['done']}"
65
+ end
66
+ ```
67
+
68
+ ```text
69
+ Write the README false
70
+ ```
71
+
72
+ ### Update and delete a task
73
+
74
+ Both calls need an `eq("id", ...)` filter.
75
+
76
+ ```ruby
77
+ task_id = created.data["id"]
78
+
79
+ patch = { "done" => true }
80
+ k9o.from("tasks").update(patch).eq("id", task_id).execute
81
+
82
+ k9o.from("tasks").delete.eq("id", task_id).execute
83
+ ```
84
+
85
+ ## Authentication and project context
86
+
87
+ The client needs a key and, for project calls, an org and project.
88
+
89
+ | Option | What it does |
90
+ |---|---|
91
+ | `api_key:` | Bearer token sent with every request. |
92
+ | `org_slug:` | Organization. On the default host the SDK uses `https://<org_slug>.api.k9o.io`. |
93
+ | `project_slug:` | Project. Required by every project-scoped namespace. |
94
+
95
+ See [client options](#client-options) for every option.
96
+
97
+ ### Choose a host
98
+
99
+ Pick one of two forms.
100
+
101
+ - Default platform host: pass the org slug, project slug and key.
102
+ - Custom or self-hosted host: pass `base_url`, project slug and key, and no org slug.
103
+
104
+ ```ruby
105
+ k9o = K9o.create_client(
106
+ base_url: "https://k9o.internal.example.com",
107
+ project_slug: "task-app",
108
+ api_key: ENV.fetch("K9O_API_KEY")
109
+ )
110
+ ```
111
+
112
+ The base URL rules are listed under [regional hosts](#regional-hosts).
113
+
114
+ ## Running queries
115
+
116
+ Chain filters on `select`, then call `execute`. The result carries rows in `data` and details in `meta`.
117
+
118
+ | Call | What it does |
119
+ |---|---|
120
+ | `eq(column, value)` | Keep rows where the column equals the value. |
121
+ | `limit(n)` | Return at most `n` rows. |
122
+ | `cursor(token)` | Continue from `meta.cursor.next`. |
123
+
124
+ See [filters](#filters) and [paging](#paging) for all calls, and [response metadata](#response-metadata) for every `meta` field.
125
+
126
+ A sub-object the server did not send is `nil`, so use `&.` when it may be absent.
127
+
128
+ ```ruby
129
+ request_id = response.meta.context.request_id
130
+ total = response.meta.cursor.total_count
131
+ puts "#{request_id} #{total}"
132
+ ```
133
+
134
+ ```text
135
+ req_01H... 1
136
+ ```
137
+
138
+ ## Handling errors
139
+
140
+ Failures raise `K9o::KError`, a subclass of `StandardError`. Branch on `code`.
141
+
142
+ ```ruby
143
+ begin
144
+ k9o.from("missing").select.execute
145
+ rescue K9o::KError => error
146
+ puts "#{error.code} #{error.status} #{error.request_id}"
147
+ puts error.message
148
+ end
149
+ ```
150
+
151
+ ```text
152
+ K9O_NOT_FOUND 404 req_01H...
153
+ Collection not found
154
+ ```
155
+
156
+ - See [error attributes](#error-attributes) for every field.
157
+ - See [error codes](#error-codes) for the codes the SDK raises.
158
+ - See [retries](#retries) for automatic retry rules.
159
+
160
+
161
+ ## Appendix
162
+
163
+ ### Requirements
164
+
165
+ | Requirement | Version |
166
+ |---|---|
167
+ | `Ruby` | 3.2 or newer |
168
+ | `Runtime gems` | None (uses `Net::HTTP`, `json` and `uri` from the standard library) |
169
+
170
+ ### Client options
171
+
172
+ | Option | What it does |
173
+ |---|---|
174
+ | `api_key:` | Bearer token sent with every request. |
175
+ | `org_slug:` | Organization. On the default host the SDK uses `https://<org_slug>.api.k9o.io`. |
176
+ | `project_slug:` | Project. Required by every project-scoped namespace. |
177
+ | `base_url:` | Scheme and host only. Defaults to `https://api.k9o.io`. |
178
+ | `default_headers:` | Extra headers added to every request. |
179
+ | `http_client:` | A `Net::HTTP`-compatible object used instead of the default connection. |
180
+
181
+ `org_slug` and `project_slug` are also sent as the `x-k9o-org-slug` and `x-k9o-project-slug` headers.
182
+
183
+ ### Methods by namespace
184
+
185
+ Every method is called on the client, for example `k9o.projects.list`. Return values are `Envelope` objects.
186
+
187
+
188
+
189
+ #### Data
190
+
191
+ `k9o.from(collection)` reads and writes rows in one collection of the active project.
192
+
193
+ | Method | What it does |
194
+ |---|---|
195
+ | `select` | Start a query. Finish with `execute`. |
196
+ | `insert(record)` | Add one row and return it. |
197
+ | `update(patch)` | Change one row. Needs `eq("id", ...)`. |
198
+ | `delete` | Remove one row. Needs `eq("id", ...)`. |
199
+
200
+ ##### Filters
201
+
202
+ Chain filters before `execute`. Values are matched against one column.
203
+
204
+ | Method | What it does |
205
+ |---|---|
206
+ | `eq(column, value)` | Equal to value. |
207
+ | `neq(column, value)` | Not equal to value. |
208
+ | `gt(column, value)` | Greater than value. |
209
+ | `gte(column, value)` | Greater than or equal. |
210
+ | `lt(column, value)` | Less than value. |
211
+ | `lte(column, value)` | Less than or equal. |
212
+ | `in_(column, values)` | Equal to any value in the list. |
213
+ | `not_in(column, values)` | Equal to none of the values. |
214
+ | `like(column, pattern)` | Case-sensitive pattern match. |
215
+ | `ilike(column, pattern)` | Case-insensitive pattern match. |
216
+ | `is(column, value)` | Match `null`, `true` or `false`. |
217
+ | `filter(column, op, value)` | Generic form with an explicit operator name. |
218
+
219
+ ##### Paging
220
+
221
+ | Method | What it does |
222
+ |---|---|
223
+ | `limit(n)` | Maximum rows to return. |
224
+ | `cursor(token)` | Continue from `meta.cursor.next`. |
225
+ | `count(mode)` | Row count mode: `none`, `exact` or `estimated`. |
226
+
227
+ #### Projects
228
+
229
+ `k9o.projects` manages projects in the active organization.
230
+
231
+ | Method | What it does |
232
+ |---|---|
233
+ | `k9o.projects.list` | List projects you can access. |
234
+ | `k9o.projects.create` | Create a project from `slug` and `name`. |
235
+ | `k9o.projects.get` | Read a project by slug. |
236
+ | `k9o.projects.update` | Change a project's `name`. |
237
+
238
+ #### Gate (end-user sign-in)
239
+
240
+ `k9o.gate` signs in the active project's end users and needs `project_slug`.
241
+
242
+ | Method | What it does |
243
+ |---|---|
244
+ | `k9o.gate.sign_up` | Create an end user from email and password. |
245
+ | `k9o.gate.sign_in` | Sign in with email and password. |
246
+ | `k9o.gate.sign_out` | Revoke the session's refresh token. |
247
+ | `k9o.gate.whoami` | Return the signed-in identity. |
248
+ | `k9o.gate.refresh_token` | Exchange a refresh token for a new access token. |
249
+ | `k9o.gate.verify_session` | Check that the current session is valid. |
250
+ | `k9o.gate.hosted` | Return the hosted sign-in `signInUrl` and `rpId`. |
251
+
252
+ ```ruby
253
+ session = k9o.gate.sign_in(email: "ada@example.com", password: "correct-horse")
254
+ user = session.data["user"]
255
+ puts user["email"]
256
+ ```
257
+
258
+ ```text
259
+ ada@example.com
260
+ ```
261
+
262
+ #### Storage
263
+
264
+ `k9o.storage` manages buckets and the objects inside them.
265
+
266
+ ##### Buckets
267
+
268
+ | Method | What it does |
269
+ |---|---|
270
+ | `k9o.storage.create_bucket` | Create a bucket by name. |
271
+ | `k9o.storage.get_bucket` | Read one bucket. |
272
+ | `k9o.storage.list_buckets` | List buckets. |
273
+ | `k9o.storage.update_bucket` | Change bucket settings. |
274
+ | `k9o.storage.delete_bucket` | Delete a bucket. Pass `empty` to purge its objects first. |
275
+
276
+ ##### Objects
277
+
278
+ `k9o.storage.from(bucket)` returns a handle for one bucket.
279
+
280
+ | Method | What it does |
281
+ |---|---|
282
+ | `k9o.storage.from(bucket).upload` | Store bytes at a path. Existing paths are kept unless `upsert` is true. |
283
+ | `k9o.storage.from(bucket).create_signed_url` | Create a time-limited download link. |
284
+ | `k9o.storage.from(bucket).list` | List objects, optionally under a `prefix`. |
285
+ | `k9o.storage.from(bucket).remove` | Delete the object at a path. |
286
+
287
+ `expires_in` is required, in seconds from 1 to 604800.
288
+
289
+ ```ruby
290
+ avatars = k9o.storage.from("avatars")
291
+ avatars.upload("ada.png", "...image bytes...", content_type: "image/png")
292
+ signed = avatars.create_signed_url("ada.png", expires_in: 300)
293
+ ```
294
+
295
+ #### Functions
296
+
297
+ `k9o.functions` deploys and runs server-side functions.
298
+
299
+ | Method | What it does |
300
+ |---|---|
301
+ | `k9o.functions.deploy` | Deploy a function from `name`, `runtime` and `code`. |
302
+ | `k9o.functions.list` | List functions. |
303
+ | `k9o.functions.get` | Read one function by name. |
304
+ | `k9o.functions.delete` | Remove a function. |
305
+ | `k9o.functions.invoke` | Run a function with a payload. |
306
+
307
+ #### Migrations
308
+
309
+ `k9o.migrations` changes the project's schema.
310
+
311
+ | Method | What it does |
312
+ |---|---|
313
+ | `k9o.migrations.apply` | Apply migrations now. |
314
+ | `k9o.migrations.list` | List applied migrations. |
315
+ | `k9o.migrations.get` | Read one migration by version. |
316
+ | `k9o.migrations.queue` | Queue migrations for approval. |
317
+ | `k9o.migrations.list_pending` | List queued migrations. |
318
+ | `k9o.migrations.apply_pending` | Apply one queued migration. |
319
+ | `k9o.migrations.reject_pending` | Reject one queued migration. |
320
+ | `k9o.migrations.delete_pending` | Delete one queued migration. |
321
+
322
+ #### Policies
323
+
324
+ `k9o.policies` manages access policy bundles.
325
+
326
+ | Method | What it does |
327
+ |---|---|
328
+ | `k9o.policies.current` | Read the published bundle. |
329
+ | `k9o.policies.list` | List bundles. |
330
+ | `k9o.policies.create` | Create a bundle from `name` and `rules`. |
331
+ | `k9o.policies.validate` | Check rules without saving. |
332
+ | `k9o.policies.get` | Read one bundle. |
333
+ | `k9o.policies.update` | Change a bundle. |
334
+ | `k9o.policies.delete` | Remove a bundle. |
335
+ | `k9o.policies.publish` | Make a bundle current. |
336
+
337
+ #### Audit
338
+
339
+ `k9o.audit` reads the project's audit events.
340
+
341
+ | Method | What it does |
342
+ |---|---|
343
+ | `k9o.audit.query` | Search events by time range, type and more. |
344
+
345
+ #### AI embeddings
346
+
347
+ `k9o.ai` creates text embeddings.
348
+
349
+ | Method | What it does |
350
+ |---|---|
351
+ | `k9o.ai.embed(input:, model: nil)` | Embed one string or a list of strings. |
352
+
353
+ #### Language models
354
+
355
+ `k9o.compute.llms` lists and calls hosted models.
356
+
357
+ | Method | What it does |
358
+ |---|---|
359
+ | `k9o.compute.llms.list` | List available models. |
360
+ | `k9o.compute.llms.read` | Read one model. |
361
+ | `k9o.compute.llms.invoke` | Send a prompt and get the reply. |
362
+ | `k9o.compute.llms.invoke_stream` | Stream the reply token by token (returns an `Enumerator`). |
363
+
364
+ #### Realtime
365
+
366
+ `k9o.realtime` delivers live events for a topic.
367
+
368
+ | Method | What it does |
369
+ |---|---|
370
+ | `k9o.realtime.subscribe` | Stream live events (returns an `Enumerator`). |
371
+ | `k9o.realtime.replay` | Read past events from a sequence number. |
372
+ | `k9o.realtime.subscribe_poll` | Poll for events from a sequence number. |
373
+
374
+ `K9o::Realtime::Topic` builds topic names, for example `Topic.collection("tasks")`.
375
+
376
+ #### Studio
377
+
378
+ `k9o.studio` manages workflows and dashboards.
379
+
380
+ ##### Workflows
381
+
382
+ | Method | What it does |
383
+ |---|---|
384
+ | `k9o.studio.workflows.list` | List workflows. |
385
+ | `k9o.studio.workflows.create` | Create a workflow. |
386
+ | `k9o.studio.workflows.read` | Read one workflow. |
387
+ | `k9o.studio.workflows.update` | Change a workflow. |
388
+ | `k9o.studio.workflows.delete` | Delete a workflow. |
389
+ | `k9o.studio.workflows.trigger` | Start a run. |
390
+ | `k9o.studio.workflows.runs.list` | List a workflow's runs. |
391
+ | `k9o.studio.workflows.runs.read` | Read one run. |
392
+ | `k9o.studio.workflows.runs.cancel` | Cancel a run. |
393
+
394
+ ##### Dashboards
395
+
396
+ | Method | What it does |
397
+ |---|---|
398
+ | `k9o.studio.dashboards.list` | List dashboards. |
399
+ | `k9o.studio.dashboards.create` | Create a dashboard. |
400
+ | `k9o.studio.dashboards.read` | Read one dashboard. |
401
+ | `k9o.studio.dashboards.update` | Change a dashboard. |
402
+ | `k9o.studio.dashboards.delete` | Delete a dashboard. |
403
+ | `k9o.studio.dashboards.pin` | Pin to the workspace. |
404
+ | `k9o.studio.dashboards.unpin` | Remove from the workspace. |
405
+
406
+ #### Current user
407
+
408
+ `k9o.me` covers the signed-in account.
409
+
410
+ | Method | What it does |
411
+ |---|---|
412
+ | `k9o.me.profile.get` | Read the profile. |
413
+ | `k9o.me.profile.update` | Change the display name. |
414
+ | `k9o.me.sessions.list` | List active sessions. |
415
+ | `k9o.me.sessions.revoke` | End one session. |
416
+ | `k9o.me.sessions.revoke_all` | End all sessions. |
417
+ | `k9o.me.api_tokens.list` | List API tokens. |
418
+ | `k9o.me.api_tokens.create` | Create an API token. |
419
+ | `k9o.me.api_tokens.delete` | Delete an API token. |
420
+ | `k9o.me.notifications.list` | List notifications. |
421
+ | `k9o.me.notifications.mark_read` | Mark one as read. |
422
+ | `k9o.me.notifications.mark_all_read` | Mark all as read. |
423
+ | `k9o.me.audit_log.query` | Search your own audit events. |
424
+ | `k9o.me.data_export.request` | Request a data export. |
425
+ | `k9o.me.data_export.status` | Check an export job. |
426
+
427
+ #### Organization
428
+
429
+ `k9o.orgs` lists organizations and `k9o.org` administers the active one.
430
+
431
+ ##### Organizations
432
+
433
+ | Method | What it does |
434
+ |---|---|
435
+ | `k9o.orgs.list` | List your organizations. |
436
+ | `k9o.orgs.get` | Read one by slug. |
437
+ | `k9o.orgs.current` | Read the active organization. |
438
+ | `k9o.orgs.restore` | Restore a deleted organization. |
439
+
440
+ ##### Administration
441
+
442
+ | Method | What it does |
443
+ |---|---|
444
+ | `k9o.org.get` | Read the active organization. |
445
+ | `k9o.org.members.list` | List members. |
446
+ | `k9o.org.members.invite` | Invite a member by email and role. |
447
+ | `k9o.org.members.get` | Read one member. |
448
+ | `k9o.org.members.remove` | Remove a member. |
449
+ | `k9o.org.groups.list` | List groups. |
450
+ | `k9o.org.groups.create` | Create a group. |
451
+ | `k9o.org.groups.get` | Read one group. |
452
+ | `k9o.org.groups.delete` | Delete a group. |
453
+ | `k9o.org.audit_log.query` | Search organization audit events. |
454
+ | `k9o.org.gate.roles.list` | List role grants. |
455
+ | `k9o.org.gate.roles.grant` | Grant a role to a user. |
456
+ | `k9o.org.gate.roles.get` | Read one grant. |
457
+ | `k9o.org.gate.roles.revoke` | Revoke a grant. |
458
+ | `k9o.org.gate.idp_providers.list` | List identity providers. |
459
+ | `k9o.org.gate.idp_providers.create` | Add an identity provider. |
460
+ | `k9o.org.gate.idp_providers.get` | Read one provider. |
461
+ | `k9o.org.gate.idp_providers.configure` | Change a provider. |
462
+ | `k9o.org.gate.idp_providers.delete` | Remove a provider. |
463
+ | `k9o.org.smtp.get` | Read the mail server settings. |
464
+ | `k9o.org.smtp.set` | Save the mail server settings. |
465
+ | `k9o.org.smtp.test` | Send a test email. |
466
+ | `k9o.org.smtp.unset` | Remove the mail server settings. |
467
+ | `k9o.org.default_llm.get` | Read the default model. |
468
+ | `k9o.org.default_llm.set` | Set the default model. |
469
+
470
+ #### Vault secrets
471
+
472
+ `k9o.kibo.vault.secrets` stores and reveals secrets in the project's vault.
473
+
474
+ | Method | What it does |
475
+ |---|---|
476
+ | `k9o.kibo.vault.secrets.list` | List secrets by prefix. |
477
+ | `k9o.kibo.vault.secrets.get` | Read a secret's details. |
478
+ | `k9o.kibo.vault.secrets.create` | Store a new secret. |
479
+ | `k9o.kibo.vault.secrets.reveal_challenge` | Get a one-time challenge for revealing. |
480
+ | `k9o.kibo.vault.secrets.reveal` | Reveal the value using the challenge. |
481
+ | `k9o.kibo.vault.secrets.update` | Replace the value. |
482
+ | `k9o.kibo.vault.secrets.rotate` | Rotate the secret. |
483
+ | `k9o.kibo.vault.secrets.move` | Move to a new path. |
484
+ | `k9o.kibo.vault.secrets.delete` | Delete the secret. |
485
+
486
+ ### Response metadata
487
+
488
+ | Field | What it holds |
489
+ |---|---|
490
+ | `meta.cursor` | Pagination: `next` and `total_count`. |
491
+ | `meta.context` | Request tracing: `request_id`. |
492
+ | `meta.timing` | Server time: `total_ms`. |
493
+ | `meta.warnings` | List of non-fatal warnings, each with `code` and `message`. |
494
+ | `meta.idempotency` | `replayed` flag for repeated requests. |
495
+ | `meta.subscription` | `is_initial` flag for streamed events. |
496
+ | `meta.cost` | Usage cost details as a hash. |
497
+
498
+ ### Error attributes
499
+
500
+ | Attribute | Meaning |
501
+ |---|---|
502
+ | `code` | Error code such as `K9O_NOT_FOUND`. |
503
+ | `message` | Human-readable explanation. |
504
+ | `recommendation` | Suggested next step, or `nil`. |
505
+ | `status` | HTTP status, or `nil` if no request was made. |
506
+ | `request_id` | Server trace id. Quote it when contacting support. |
507
+ | `context` | Read-only details for the error. |
508
+
509
+ The server may send codes not listed here, so keep a fallback branch.
510
+
511
+ ### Error codes
512
+
513
+ | Code | When |
514
+ |---|---|
515
+ | `K9O_INVALID_BASE_URL` | `base_url` has a path, query or bad scheme. |
516
+ | `K9O_MISSING_PROJECT_CONTEXT` | A project call was made without `project_slug`. |
517
+ | `K9O_INVALID_FIELD_VALUE` | An argument failed validation before sending. |
518
+ | `K9O_UPSTREAM_UNAVAILABLE` | Network failure reaching the gateway. |
519
+ | `K9O_INTERNAL_ERROR` | The response could not be read. |
520
+
521
+ `error.to_s` reads like `[K9O_NOT_FOUND] Collection not found (status=404)`.
522
+
523
+ ### Retries
524
+
525
+ - GET, HEAD, OPTIONS and DELETE are retried on network errors.
526
+ - They are also retried on 429, 502, 503 and 504.
527
+ - The default is 3 retries with exponential backoff.
528
+ - Other methods are never retried.
529
+
530
+ ### Regional hosts
531
+
532
+ - Organization hosts such as `https://acme.api.k9o.io` are region-free.
533
+ - The SDK sends one credential-free `HEAD` request to learn the region.
534
+ - Later requests go to the regional host.
535
+ - Any other base URL is used exactly as given.
536
+
537
+ ### Related packages
538
+
539
+ | Package | What it is |
540
+ |---|---|
541
+ | `@k9o/core` | TypeScript SDK |
542
+ | `@k9o/cli` | Command-line tool |
543
+
544
+ ## License
545
+
546
+ Apache-2.0 © Formelo Inc. See `LICENSE` and `NOTICE`.
data/k9o.gemspec ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/k9o/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'k9o'
7
+ spec.version = K9o::VERSION
8
+ spec.authors = ['Kilimanjaro']
9
+ spec.email = ['engineering@kilimanjaro.dev']
10
+
11
+ spec.summary = 'Kilimanjaro platform SDK for Ruby'
12
+ spec.description = 'Idiomatic Ruby SDK — create_client + data DSL + projects + KError. snake_case + raise K9o::KError.'
13
+ spec.homepage = 'https://kilimanjaro.dev'
14
+ spec.license = 'Apache-2.0'
15
+
16
+ spec.required_ruby_version = '>= 3.2.0'
17
+
18
+ # Platform gems: the publish pipeline sets K9O_GEM_PLATFORM (e.g. arm64-darwin,
19
+ # x86_64-linux) when building a gem that bundles the kibo ZK native lib for that
20
+ # platform. Unset → the pure-Ruby gem (the universal fallback; `k9o.kibo` is
21
+ # simply unavailable where no platform gem matches).
22
+ if (gem_plat = ENV['K9O_GEM_PLATFORM']) && !gem_plat.empty?
23
+ spec.platform = gem_plat
24
+ end
25
+
26
+ spec.metadata = {
27
+ 'homepage_uri' => 'https://kilimanjaro.dev',
28
+ 'source_code_uri' => 'https://github.com/k9o-dev/sdk-ruby',
29
+ 'changelog_uri' => 'https://github.com/k9o-dev/sdk-ruby/blob/main/CHANGELOG.md'
30
+ }
31
+
32
+ # Ruby sources + the CI-injected kibo native lib (one per platform gem; the
33
+ # generated uniffi binding under lib/k9o/_ffi/ is picked up by the .rb glob).
34
+ spec.files = Dir.glob('lib/**/*.rb') +
35
+ Dir.glob('lib/k9o/_ffi/*.{dylib,so,bundle,dll}') +
36
+ Dir.glob('sig/**/*.rbs') +
37
+ %w[README.md CHANGELOG.md LICENSE NOTICE k9o.gemspec]
38
+ spec.require_paths = ['lib']
39
+
40
+ spec.add_development_dependency 'minitest', '~> 5.20'
41
+ end
data/lib/k9o/ai.rb ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module K9o
4
+ # AI namespace — platform AI primitives.
5
+ # k9o.ai.embed(...)
6
+ #
7
+ # Wire (project-plane):
8
+ # POST /@<slug>/ai/embeddings body { model?, input: String | [String] }
9
+ # -> { data: { embeddings: [[Float]], model:, dimensions: }, meta }
10
+ #
11
+ # Bounds are enforced by the gateway, not here. The SDK sends `input`
12
+ # exactly as given and never puts it in an error.
13
+ class Ai
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ # POST /@<slug>/ai/embeddings — embed one string or an array of strings.
19
+ # `model` is omitted from the body when nil (server default).
20
+ def embed(input:, model: nil)
21
+ slug = Studio.require_project_slug(@config, 'ai')
22
+ body = { 'input' => input }
23
+ body['model'] = model unless model.nil?
24
+ HttpTransport.request(config: @config, method: 'POST',
25
+ path: UrlGrammar.project_path(slug, 'ai/embeddings'), body: body)
26
+ end
27
+ end
28
+ end
data/lib/k9o/audit.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module K9o
4
+ # Project-plane audit log namespace — k9o.audit.*.
5
+ # Distinct from k9o.org.audit_log + k9o.me.audit_log.
6
+ class Audit
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ # Query the audit log (reverse-chronological; filters AND-compose).
12
+ # Accepts keyword filters: since/until/type/actor_id/target_kind/
13
+ # target_id/limit/cursor (wire camelCase mapping applied here).
14
+ def query(since: nil, until_: nil, type: nil, actor_id: nil,
15
+ target_kind: nil, target_id: nil, limit: nil, cursor: nil)
16
+ slug = Studio.require_project_slug(@config, 'audit')
17
+ q = {}
18
+ q['since'] = since if since
19
+ q['until'] = until_ if until_
20
+ q['type'] = type if type
21
+ q['actorId'] = actor_id if actor_id
22
+ q['targetKind'] = target_kind if target_kind
23
+ q['targetId'] = target_id if target_id
24
+ q['limit'] = limit if limit
25
+ q['cursor'] = cursor if cursor
26
+ HttpTransport.request(config: @config, method: 'GET', path: UrlGrammar.project_path(slug, 'audit-log'), query: q)
27
+ end
28
+ end
29
+ end