k9o 1.1.1 → 1.3.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 +39 -0
- data/README.md +91 -51
- data/lib/k9o/audit.rb +12 -14
- data/lib/k9o/client.rb +3 -5
- data/lib/k9o/compute.rb +32 -15
- 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/realtime.rb +13 -35
- data/lib/k9o/region.rb +20 -18
- 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/sig/k9o/gate.rbs +7 -6
- data/sig/k9o/region.rbs +14 -3
- 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: fddde5f475c121ba44d0e069107845e30e0104613b717edb809813d152e2882a
|
|
4
|
+
data.tar.gz: af7564a794aa6c40150a09bf38800dad60e092afb26dfdf8e918524124b4a68c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c3735d8f97f9a0eacc9fa02dcf897ac0182056c606179b7cdcc104cc32ebba9ddb14b433c7522e30e4a10deadbd8b13022de1db262f4abccb2912b9edb05ac4a
|
|
7
|
+
data.tar.gz: f4f833edfa3f6fc6ea2d75efccfb85f09c902e0e2fb79cc43c8edad7db02bcd3817832087227005944fb4714f198ac4b0dd6f88134248698c9d2b4e3674aa1a0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog — k9o (Ruby)
|
|
2
2
|
|
|
3
|
+
## 1.3.0 — 2026-10-26
|
|
4
|
+
|
|
5
|
+
Every method now matches the gateway.
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- `projects.create` takes `display_name:` (and optional `slug:`, `region:`, `custody_mode:`, ...);
|
|
10
|
+
`projects.get` and `projects.update` use the projects routes.
|
|
11
|
+
- `functions.deploy` takes `name:`, `runtime:`, `tier:` (`gateway` or `sandbox`), `artifact_key:`, `version:` and `description:`.
|
|
12
|
+
Inline code is gone.
|
|
13
|
+
- `functions.invoke` sends the payload as `{payload}`.
|
|
14
|
+
- `policies.create`, `update` and `validate` take a `policy:` document (plus optional `rules:`, `editor_mode:`);
|
|
15
|
+
`policies.list` takes only `status:`.
|
|
16
|
+
- `audit.query` filters are `event_type:`, `actor_id:`, `after:`, `before:`, `limit:` and `cursor:`.
|
|
17
|
+
- Org roles, identity providers and default model use the current routes; `smtp.set` uses `password_ref:` and `from_address:`;
|
|
18
|
+
`smtp.test` takes `to:` and `subject:`.
|
|
19
|
+
- `me.api_tokens.create` takes `label:` and `scope_manifest:` (and `ttl_seconds:`); `me.sessions.revoke` and `revoke_all` use new routes.
|
|
20
|
+
- `compute.llms.invoke` settings are sent flat, and the result has `content` and `usage`.
|
|
21
|
+
- `studio.workflows.trigger` sends the context as `triggerContext`.
|
|
22
|
+
- `gate.sign_out` requires the refresh token.
|
|
23
|
+
- The API host comes from the base URL: any `<org>.api.<apex>` host works and no apex is hardcoded.
|
|
24
|
+
`https://api.k9o.io` is only the default.
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
- `gate.verify_session`, `gate.hosted`, `realtime.replay` and `realtime.subscribe_poll`.
|
|
29
|
+
- `me.notifications`, `me.audit_log` and `me.data_export`.
|
|
30
|
+
- `org.groups.create`, `org.groups.delete`, `org.members.get` and `org.smtp.get`.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- `functions.upload_artifact(bytes, kind)` uploads a build and returns the `artifactKey` for `deploy`.
|
|
35
|
+
- `UrlGrammar.parse_api_host` reads the org, region and apex from any gateway host.
|
|
36
|
+
- A contract test checks every method against the gateway's API description.
|
|
37
|
+
|
|
38
|
+
## 1.2.0 — 2026-10-25
|
|
39
|
+
|
|
40
|
+
- The README now introduces the Kilimanjaro products (Keep, Pulse, Gate, Kibo). No change to the public API.
|
|
41
|
+
|
|
3
42
|
## 1.1.1 — 2026-10-25
|
|
4
43
|
|
|
5
44
|
- Documentation links corrected: the documentation hub is https://docs.kilimanjaro.dev, with the website, platform API and console listed in a Links section. Package metadata points at the same places.
|
data/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
The Kilimanjaro platform SDK for Ruby.
|
|
4
4
|
|
|
5
|
+
Kilimanjaro is the AI-native foundation for building critical applications.
|
|
6
|
+
Its products are:
|
|
7
|
+
|
|
8
|
+
- Keep: AI Security and Governance
|
|
9
|
+
- Pulse: Data Intelligence
|
|
10
|
+
- Gate: Customer Identity and Access Management
|
|
11
|
+
- Kibo: Keys and Secrets Management
|
|
12
|
+
|
|
5
13
|
## Contents
|
|
6
14
|
|
|
7
15
|
- [Links](#links)
|
|
@@ -95,27 +103,28 @@ The client needs a key and, for project calls, an org and project.
|
|
|
95
103
|
| Option | What it does |
|
|
96
104
|
|---|---|
|
|
97
105
|
| `api_key:` | Bearer token sent with every request. |
|
|
98
|
-
| `org_slug:` | Organization.
|
|
106
|
+
| `org_slug:` | Organization. The SDK builds `https://<org_slug>.<host>` from the platform host. |
|
|
99
107
|
| `project_slug:` | Project. Required by every project-scoped namespace. |
|
|
100
108
|
|
|
101
109
|
See [client options](#client-options) for every option.
|
|
102
110
|
|
|
103
111
|
### Choose a host
|
|
104
112
|
|
|
105
|
-
Pick one of two forms.
|
|
113
|
+
The API host always comes from the base URL. Pick one of two forms.
|
|
106
114
|
|
|
107
|
-
- Default
|
|
108
|
-
-
|
|
115
|
+
- Default public host: pass the org slug, project slug and key.
|
|
116
|
+
- Any other platform, internal or air-gapped host: pass its `base_url` as well.
|
|
109
117
|
|
|
110
118
|
```ruby
|
|
111
119
|
k9o = K9o.create_client(
|
|
112
|
-
base_url: "https://
|
|
120
|
+
base_url: "https://api.corp.internal",
|
|
121
|
+
org_slug: "acme",
|
|
113
122
|
project_slug: "task-app",
|
|
114
123
|
api_key: ENV.fetch("K9O_API_KEY")
|
|
115
124
|
)
|
|
116
125
|
```
|
|
117
126
|
|
|
118
|
-
|
|
127
|
+
This client talks to `https://acme.api.corp.internal`. See [regional hosts](#regional-hosts) for the rules.
|
|
119
128
|
|
|
120
129
|
## Running queries
|
|
121
130
|
|
|
@@ -177,9 +186,9 @@ Collection not found
|
|
|
177
186
|
| Option | What it does |
|
|
178
187
|
|---|---|
|
|
179
188
|
| `api_key:` | Bearer token sent with every request. |
|
|
180
|
-
| `org_slug:` | Organization.
|
|
189
|
+
| `org_slug:` | Organization. Prefixed to a platform host (`api.<apex>`) to form the org host. |
|
|
181
190
|
| `project_slug:` | Project. Required by every project-scoped namespace. |
|
|
182
|
-
| `base_url:` | Scheme and host only. Defaults to `https://api.k9o.io`. |
|
|
191
|
+
| `base_url:` | Scheme and host only. Defaults to the public host `https://api.k9o.io`. |
|
|
183
192
|
| `default_headers:` | Extra headers added to every request. |
|
|
184
193
|
| `http_client:` | A `Net::HTTP`-compatible object used instead of the default connection. |
|
|
185
194
|
|
|
@@ -234,9 +243,18 @@ Chain filters before `execute`. Values are matched against one column.
|
|
|
234
243
|
| Method | What it does |
|
|
235
244
|
|---|---|
|
|
236
245
|
| `k9o.projects.list` | List projects you can access. |
|
|
237
|
-
| `k9o.projects.create` | Create a project from `slug`
|
|
246
|
+
| `k9o.projects.create` | Create a project from `display_name:` (optional `slug:`, `region:`, `custody_mode:`, more). |
|
|
238
247
|
| `k9o.projects.get` | Read a project by slug. |
|
|
239
|
-
| `k9o.projects.update` | Change a project
|
|
248
|
+
| `k9o.projects.update` | Change a project by slug: `display_name:`, `slug:` (rename), `description:`, `region:`, more. |
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
project = k9o.projects.create(display_name: "Task app", slug: "task-app")
|
|
252
|
+
puts project.data["slug"]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```text
|
|
256
|
+
task-app
|
|
257
|
+
```
|
|
240
258
|
|
|
241
259
|
#### Gate (end-user sign-in)
|
|
242
260
|
|
|
@@ -246,11 +264,9 @@ Chain filters before `execute`. Values are matched against one column.
|
|
|
246
264
|
|---|---|
|
|
247
265
|
| `k9o.gate.sign_up` | Create an end user from email and password. |
|
|
248
266
|
| `k9o.gate.sign_in` | Sign in with email and password. |
|
|
249
|
-
| `k9o.gate.sign_out` | Revoke
|
|
267
|
+
| `k9o.gate.sign_out` | Revoke a session. Requires `refresh_token:`. |
|
|
250
268
|
| `k9o.gate.whoami` | Return the signed-in identity. |
|
|
251
269
|
| `k9o.gate.refresh_token` | Exchange a refresh token for a new access token. |
|
|
252
|
-
| `k9o.gate.verify_session` | Check that the current session is valid. |
|
|
253
|
-
| `k9o.gate.hosted` | Return the hosted sign-in `signInUrl` and `rpId`. |
|
|
254
270
|
|
|
255
271
|
```ruby
|
|
256
272
|
session = k9o.gate.sign_in(email: "ada@example.com", password: "correct-horse")
|
|
@@ -301,11 +317,32 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
301
317
|
|
|
302
318
|
| Method | What it does |
|
|
303
319
|
|---|---|
|
|
304
|
-
| `k9o.functions.
|
|
305
|
-
| `k9o.functions.
|
|
320
|
+
| `k9o.functions.upload_artifact` | Upload the build (`bytes`, `kind` of `"wasm"` or `"tar"`). Returns `artifactKey`. |
|
|
321
|
+
| `k9o.functions.deploy` | Deploy from `name:`, `runtime:`, `tier:`, `artifact_key:`, `version:`, optional `description:`. |
|
|
322
|
+
| `k9o.functions.list` | List functions. Options: `status:`, `limit:`, `cursor:`. |
|
|
306
323
|
| `k9o.functions.get` | Read one function by name. |
|
|
307
324
|
| `k9o.functions.delete` | Remove a function. |
|
|
308
|
-
| `k9o.functions.invoke` | Run a function
|
|
325
|
+
| `k9o.functions.invoke` | Run a function. The `payload` argument is sent as `{payload}`. |
|
|
326
|
+
|
|
327
|
+
`tier:` is `"gateway"` (WebAssembly, `"wasm"`) or `"sandbox"` (OCI image archive, `"tar"`).
|
|
328
|
+
|
|
329
|
+
```ruby
|
|
330
|
+
artifact = k9o.functions.upload_artifact(File.binread("hello.wasm"), "wasm")
|
|
331
|
+
|
|
332
|
+
k9o.functions.deploy(
|
|
333
|
+
name: "hello",
|
|
334
|
+
runtime: "bun",
|
|
335
|
+
tier: "gateway",
|
|
336
|
+
artifact_key: artifact.data["artifactKey"],
|
|
337
|
+
version: "1.0.0"
|
|
338
|
+
)
|
|
339
|
+
result = k9o.functions.invoke("hello", { "name" => "Ada" })
|
|
340
|
+
puts result.data["greeting"]
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
```text
|
|
344
|
+
Hello, Ada
|
|
345
|
+
```
|
|
309
346
|
|
|
310
347
|
#### Migrations
|
|
311
348
|
|
|
@@ -329,11 +366,11 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
329
366
|
| Method | What it does |
|
|
330
367
|
|---|---|
|
|
331
368
|
| `k9o.policies.current` | Read the published bundle. |
|
|
332
|
-
| `k9o.policies.list` | List bundles. |
|
|
333
|
-
| `k9o.policies.create` | Create a bundle from `
|
|
334
|
-
| `k9o.policies.validate` | Check rules without saving. |
|
|
369
|
+
| `k9o.policies.list` | List bundles. Only option: `status:`. |
|
|
370
|
+
| `k9o.policies.create` | Create a bundle from a `policy:` document (optional `rules:`, `editor_mode:`). |
|
|
371
|
+
| `k9o.policies.validate` | Check a `policy:` document (and optional `rules:`) without saving. |
|
|
335
372
|
| `k9o.policies.get` | Read one bundle. |
|
|
336
|
-
| `k9o.policies.update` |
|
|
373
|
+
| `k9o.policies.update` | Replace a bundle's `policy:` (optional `rules:`, `editor_mode:`). |
|
|
337
374
|
| `k9o.policies.delete` | Remove a bundle. |
|
|
338
375
|
| `k9o.policies.publish` | Make a bundle current. |
|
|
339
376
|
|
|
@@ -343,7 +380,9 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
343
380
|
|
|
344
381
|
| Method | What it does |
|
|
345
382
|
|---|---|
|
|
346
|
-
| `k9o.audit.query` |
|
|
383
|
+
| `k9o.audit.query` | Filter by `event_type:`, `actor_id:`, `after:`, `before:`, `limit:`, `cursor:`. |
|
|
384
|
+
|
|
385
|
+
`after:` and `before:` are ISO 8601 timestamps.
|
|
347
386
|
|
|
348
387
|
#### AI embeddings
|
|
349
388
|
|
|
@@ -362,7 +401,18 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
362
401
|
| `k9o.compute.llms.list` | List available models. |
|
|
363
402
|
| `k9o.compute.llms.read` | Read one model. |
|
|
364
403
|
| `k9o.compute.llms.invoke` | Send a prompt and get the reply. |
|
|
365
|
-
| `k9o.compute.llms.invoke_stream` | Stream the reply
|
|
404
|
+
| `k9o.compute.llms.invoke_stream` | Stream the reply chunk by chunk (returns an `Enumerator`). |
|
|
405
|
+
|
|
406
|
+
Pass settings in `params:` (`max_tokens`, `temperature`, `usage_type`). The result `data` has `content` and `usage`.
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
reply = k9o.compute.llms.invoke("llama-3-8b", prompt: "Say hi", params: { max_tokens: 20 })
|
|
410
|
+
puts "#{reply.data['content']} #{reply.data['usage']['totalTokens']}"
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
Hi there! 14
|
|
415
|
+
```
|
|
366
416
|
|
|
367
417
|
#### Realtime
|
|
368
418
|
|
|
@@ -370,9 +420,7 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
370
420
|
|
|
371
421
|
| Method | What it does |
|
|
372
422
|
|---|---|
|
|
373
|
-
| `k9o.realtime.subscribe` | Stream live events (returns an `Enumerator`). |
|
|
374
|
-
| `k9o.realtime.replay` | Read past events from a sequence number. |
|
|
375
|
-
| `k9o.realtime.subscribe_poll` | Poll for events from a sequence number. |
|
|
423
|
+
| `k9o.realtime.subscribe` | Stream live events over SSE (returns an `Enumerator`). |
|
|
376
424
|
|
|
377
425
|
`K9o::Realtime::Topic` builds topic names, for example `Topic.collection("tasks")`.
|
|
378
426
|
|
|
@@ -389,7 +437,7 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
389
437
|
| `k9o.studio.workflows.read` | Read one workflow. |
|
|
390
438
|
| `k9o.studio.workflows.update` | Change a workflow. |
|
|
391
439
|
| `k9o.studio.workflows.delete` | Delete a workflow. |
|
|
392
|
-
| `k9o.studio.workflows.trigger` | Start a run. |
|
|
440
|
+
| `k9o.studio.workflows.trigger` | Start a run, with an optional context hash. |
|
|
393
441
|
| `k9o.studio.workflows.runs.list` | List a workflow's runs. |
|
|
394
442
|
| `k9o.studio.workflows.runs.read` | Read one run. |
|
|
395
443
|
| `k9o.studio.workflows.runs.cancel` | Cancel a run. |
|
|
@@ -413,19 +461,13 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
413
461
|
| Method | What it does |
|
|
414
462
|
|---|---|
|
|
415
463
|
| `k9o.me.profile.get` | Read the profile. |
|
|
416
|
-
| `k9o.me.profile.update` | Change
|
|
464
|
+
| `k9o.me.profile.update` | Change `display_name:`, `locale:` or `timezone:` (only those given). |
|
|
417
465
|
| `k9o.me.sessions.list` | List active sessions. |
|
|
418
466
|
| `k9o.me.sessions.revoke` | End one session. |
|
|
419
467
|
| `k9o.me.sessions.revoke_all` | End all sessions. |
|
|
420
468
|
| `k9o.me.api_tokens.list` | List API tokens. |
|
|
421
|
-
| `k9o.me.api_tokens.create` | Create
|
|
469
|
+
| `k9o.me.api_tokens.create` | Create a token from `label:` and `scope_manifest:` (optional `ttl_seconds:`). |
|
|
422
470
|
| `k9o.me.api_tokens.delete` | Delete an API token. |
|
|
423
|
-
| `k9o.me.notifications.list` | List notifications. |
|
|
424
|
-
| `k9o.me.notifications.mark_read` | Mark one as read. |
|
|
425
|
-
| `k9o.me.notifications.mark_all_read` | Mark all as read. |
|
|
426
|
-
| `k9o.me.audit_log.query` | Search your own audit events. |
|
|
427
|
-
| `k9o.me.data_export.request` | Request a data export. |
|
|
428
|
-
| `k9o.me.data_export.status` | Check an export job. |
|
|
429
471
|
|
|
430
472
|
#### Organization
|
|
431
473
|
|
|
@@ -445,30 +487,26 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
445
487
|
| Method | What it does |
|
|
446
488
|
|---|---|
|
|
447
489
|
| `k9o.org.get` | Read the active organization. |
|
|
448
|
-
| `k9o.org.members.list` | List members. |
|
|
490
|
+
| `k9o.org.members.list` | List members. Options: `user_id:`, `role:`, `acceptance_status:`, `status:`, `state:`. |
|
|
449
491
|
| `k9o.org.members.invite` | Invite a member by email and role. |
|
|
450
|
-
| `k9o.org.members.get` | Read one member. |
|
|
451
492
|
| `k9o.org.members.remove` | Remove a member. |
|
|
452
|
-
| `k9o.org.groups.list` | List groups
|
|
453
|
-
| `k9o.org.groups.create` | Create a group. |
|
|
493
|
+
| `k9o.org.groups.list` | List groups, optionally for one `user_id:`. |
|
|
454
494
|
| `k9o.org.groups.get` | Read one group. |
|
|
455
|
-
| `k9o.org.
|
|
456
|
-
| `k9o.org.audit_log.query` | Search organization audit events. |
|
|
495
|
+
| `k9o.org.audit_log.query` | Search organization audit events by `type:` and `limit:`. |
|
|
457
496
|
| `k9o.org.gate.roles.list` | List role grants. |
|
|
458
|
-
| `k9o.org.gate.roles.grant` | Grant a role to a user. |
|
|
497
|
+
| `k9o.org.gate.roles.grant` | Grant a role to a user (`user_id:`, `role:`, optional `decision_reason:`). |
|
|
459
498
|
| `k9o.org.gate.roles.get` | Read one grant. |
|
|
460
499
|
| `k9o.org.gate.roles.revoke` | Revoke a grant. |
|
|
461
500
|
| `k9o.org.gate.idp_providers.list` | List identity providers. |
|
|
462
|
-
| `k9o.org.gate.idp_providers.create` | Add
|
|
501
|
+
| `k9o.org.gate.idp_providers.create` | Add a provider: `idp_provider:`, `display_name:`, `client_id:`, `client_secret_ref:`, `tier_gating:`. |
|
|
463
502
|
| `k9o.org.gate.idp_providers.get` | Read one provider. |
|
|
464
503
|
| `k9o.org.gate.idp_providers.configure` | Change a provider. |
|
|
465
504
|
| `k9o.org.gate.idp_providers.delete` | Remove a provider. |
|
|
466
|
-
| `k9o.org.smtp.
|
|
467
|
-
| `k9o.org.smtp.
|
|
468
|
-
| `k9o.org.smtp.test` | Send a test email. |
|
|
505
|
+
| `k9o.org.smtp.set` | Save the mail server: `host:`, `port:`, `username:`, `password_ref:`, `from_address:`. |
|
|
506
|
+
| `k9o.org.smtp.test` | Send a test email to `to:`, with an optional `subject:`. |
|
|
469
507
|
| `k9o.org.smtp.unset` | Remove the mail server settings. |
|
|
470
508
|
| `k9o.org.default_llm.get` | Read the default model. |
|
|
471
|
-
| `k9o.org.default_llm.set` | Set the default model. |
|
|
509
|
+
| `k9o.org.default_llm.set` | Set the default model from a model id. |
|
|
472
510
|
|
|
473
511
|
#### Vault secrets
|
|
474
512
|
|
|
@@ -477,7 +515,7 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
477
515
|
| Method | What it does |
|
|
478
516
|
|---|---|
|
|
479
517
|
| `k9o.kibo.vault.secrets.list` | List secrets by prefix. |
|
|
480
|
-
| `k9o.kibo.vault.secrets.get` | Read a secret
|
|
518
|
+
| `k9o.kibo.vault.secrets.get` | Read a secret by name (`region:` is required). |
|
|
481
519
|
| `k9o.kibo.vault.secrets.create` | Store a new secret. |
|
|
482
520
|
| `k9o.kibo.vault.secrets.reveal_challenge` | Get a one-time challenge for revealing. |
|
|
483
521
|
| `k9o.kibo.vault.secrets.reveal` | Reveal the value using the challenge. |
|
|
@@ -532,10 +570,12 @@ The server may send codes not listed here, so keep a fallback branch.
|
|
|
532
570
|
|
|
533
571
|
### Regional hosts
|
|
534
572
|
|
|
535
|
-
-
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
-
|
|
573
|
+
- A platform host is `api.<apex>` or `api-staging.<apex>`, on any apex.
|
|
574
|
+
- With `org_slug`, the SDK builds the org host `<org_slug>.<host>`.
|
|
575
|
+
- A region-free org host gets one credential-free `HEAD` request to learn its region.
|
|
576
|
+
- Later requests go to the regional host, which must share the org, family and apex.
|
|
577
|
+
- Any other base URL (internal, air-gapped, localhost) is used exactly as given.
|
|
578
|
+
- `https://api.k9o.io` is only the default when `base_url:` is omitted.
|
|
539
579
|
|
|
540
580
|
### Related packages
|
|
541
581
|
|
data/lib/k9o/audit.rb
CHANGED
|
@@ -2,27 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module K9o
|
|
4
4
|
# Project-plane audit log namespace — k9o.audit.*.
|
|
5
|
-
# Distinct from k9o.org.audit_log
|
|
5
|
+
# Distinct from k9o.org.audit_log.
|
|
6
6
|
class Audit
|
|
7
7
|
def initialize(config)
|
|
8
8
|
@config = config
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
# GET /@<slug>/audit-log — reverse-chronological; filters AND-compose. Requires the AUDIT_READER role.
|
|
12
|
+
# event_type: audit-event name prefix (e.g. 'data.write.access') actor_id: filter by actor
|
|
13
|
+
# after / before: ISO 8601 bounds (events after / before this instant)
|
|
14
|
+
# limit: page size (bounded server-side) cursor: from the prior response's meta.cursor.next
|
|
15
|
+
def query(event_type: nil, actor_id: nil, after: nil, before: nil, limit: nil, cursor: nil)
|
|
16
16
|
slug = Studio.require_project_slug(@config, 'audit')
|
|
17
17
|
q = {}
|
|
18
|
-
q['
|
|
19
|
-
q['
|
|
20
|
-
q['
|
|
21
|
-
q['
|
|
22
|
-
q['
|
|
23
|
-
q['
|
|
24
|
-
q['limit'] = limit if limit
|
|
25
|
-
q['cursor'] = cursor if cursor
|
|
18
|
+
q['eventType'] = event_type unless event_type.nil?
|
|
19
|
+
q['actorId'] = actor_id unless actor_id.nil?
|
|
20
|
+
q['after'] = after unless after.nil?
|
|
21
|
+
q['before'] = before unless before.nil?
|
|
22
|
+
q['limit'] = limit unless limit.nil?
|
|
23
|
+
q['cursor'] = cursor unless cursor.nil?
|
|
26
24
|
HttpTransport.request(config: @config, method: 'GET', path: UrlGrammar.project_path(slug, 'audit-log'), query: q)
|
|
27
25
|
end
|
|
28
26
|
end
|
data/lib/k9o/client.rb
CHANGED
|
@@ -36,17 +36,15 @@ module K9o
|
|
|
36
36
|
|
|
37
37
|
# Factory — construct a K9oClient bound to the supplied options.
|
|
38
38
|
#
|
|
39
|
-
# Per spec sample:
|
|
40
39
|
# k9o = K9o.create_client(
|
|
41
40
|
# api_key: ENV["K9O_API_KEY"],
|
|
42
41
|
# org_slug: "acme",
|
|
43
42
|
# project_slug: "member-portal",
|
|
44
43
|
# )
|
|
45
44
|
#
|
|
46
|
-
# base_url is OPTIONAL. When omitted it
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# https://<org_slug>.api.k9o.io is composed. Any other host passes through.
|
|
45
|
+
# base_url is OPTIONAL. When omitted it defaults to the public-cloud platform host (a convenience only, never used
|
|
46
|
+
# to parse a host). When the host is a platform host (+api.<apex>+ or +api-staging.<apex>+, any apex) and org_slug
|
|
47
|
+
# is set, the org host +<org_slug>.<host>+ is composed. Any other host passes through unchanged.
|
|
50
48
|
def self.create_client(base_url: nil, api_key: nil, org_slug: nil, project_slug: nil, default_headers: {}, http_client: nil)
|
|
51
49
|
normalised = UrlGrammar.resolve_base_url(base_url, org_slug)
|
|
52
50
|
config = HttpClientConfig.new(
|
data/lib/k9o/compute.rb
CHANGED
|
@@ -4,30 +4,37 @@ module K9o
|
|
|
4
4
|
# Compute namespace (Compute LLMs registry).
|
|
5
5
|
# k9o.compute.llms.*
|
|
6
6
|
class Compute
|
|
7
|
-
#
|
|
7
|
+
# The curated model ids (closed enum).
|
|
8
8
|
LLM_MODEL_IDS = %w[
|
|
9
9
|
mistral-7b mistral-8x7b llama-3-8b llama-3-70b
|
|
10
10
|
claude-3-5-sonnet gpt-4o gpt-4o-mini
|
|
11
11
|
text-embedding-3-small text-embedding-3-large
|
|
12
12
|
].freeze
|
|
13
13
|
|
|
14
|
+
# Inference parameters the gateway reads (top-level body fields next to +prompt+). Snake_case spellings map to
|
|
15
|
+
# the gateway's.
|
|
16
|
+
PARAM_FIELDS = {
|
|
17
|
+
'max_tokens' => 'maxTokens', 'maxTokens' => 'maxTokens',
|
|
18
|
+
'temperature' => 'temperature',
|
|
19
|
+
'usage_type' => 'usageType', 'usageType' => 'usageType'
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
14
22
|
attr_reader :llms
|
|
15
23
|
|
|
16
24
|
def initialize(config)
|
|
17
25
|
@llms = Llms.new(config)
|
|
18
26
|
end
|
|
19
27
|
|
|
20
|
-
# k9o.compute.llms
|
|
28
|
+
# k9o.compute.llms.*
|
|
21
29
|
class Llms
|
|
22
30
|
def initialize(config)
|
|
23
31
|
@config = config
|
|
24
32
|
end
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
# GET /@<slug>/compute/llms
|
|
35
|
+
def list
|
|
27
36
|
slug = Studio.require_project_slug(@config, 'compute.llms')
|
|
28
|
-
|
|
29
|
-
HttpTransport.request(config: @config, method: 'GET',
|
|
30
|
-
path: UrlGrammar.project_path(slug, 'compute/llms'), query: q)
|
|
37
|
+
HttpTransport.request(config: @config, method: 'GET', path: UrlGrammar.project_path(slug, 'compute/llms'))
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def read(model_id)
|
|
@@ -36,29 +43,39 @@ module K9o
|
|
|
36
43
|
path: UrlGrammar.project_path(slug, "compute/llms/#{enc(model_id)}"))
|
|
37
44
|
end
|
|
38
45
|
|
|
39
|
-
# Synchronous inference
|
|
46
|
+
# Synchronous inference: POST /@<slug>/compute/llms/<id>/invoke { prompt, maxTokens?, temperature?,
|
|
47
|
+
# usageType? }. +params+ takes max_tokens / temperature / usage_type (the gateway's spellings also work).
|
|
48
|
+
# The envelope's data is { 'modelId', 'content', 'finishReason', 'usage' => { 'promptTokens',
|
|
49
|
+
# 'completionTokens', 'totalTokens' }, 'usageType' }.
|
|
40
50
|
def invoke(model_id, prompt:, params: nil)
|
|
41
51
|
slug = Studio.require_project_slug(@config, 'compute.llms')
|
|
42
|
-
body = { 'prompt' => prompt }
|
|
43
|
-
body['params'] = params if params
|
|
44
52
|
HttpTransport.request(config: @config, method: 'POST',
|
|
45
|
-
path: UrlGrammar.project_path(slug, "compute/llms/#{enc(model_id)}/invoke"),
|
|
53
|
+
path: UrlGrammar.project_path(slug, "compute/llms/#{enc(model_id)}/invoke"),
|
|
54
|
+
body: invoke_body(prompt, params))
|
|
46
55
|
end
|
|
47
56
|
|
|
48
|
-
# Streaming inference (SSE per-token). Returns an
|
|
49
|
-
# Enumerator of chunk hashes (Ruby idiom). Consume with
|
|
57
|
+
# Streaming inference (SSE per-token). Returns an Enumerator of chunk hashes. Consume with
|
|
50
58
|
# `.each { |chunk| ... }`.
|
|
51
59
|
def invoke_stream(model_id, prompt:, params: nil)
|
|
52
60
|
slug = Studio.require_project_slug(@config, 'compute.llms')
|
|
53
|
-
body = { 'prompt' => prompt }
|
|
54
|
-
body['params'] = params if params
|
|
55
61
|
HttpTransport.stream_sse(config: @config, method: 'POST',
|
|
56
62
|
path: UrlGrammar.project_path(slug, "compute/llms/#{enc(model_id)}/invoke-stream"),
|
|
57
|
-
body:
|
|
63
|
+
body: invoke_body(prompt, params))
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
private
|
|
61
67
|
|
|
68
|
+
def invoke_body(prompt, params)
|
|
69
|
+
body = { 'prompt' => prompt }
|
|
70
|
+
(params || {}).each do |key, value|
|
|
71
|
+
wire = PARAM_FIELDS[key.to_s]
|
|
72
|
+
raise ArgumentError, "unknown inference parameter: #{key}" if wire.nil?
|
|
73
|
+
|
|
74
|
+
body[wire] = value unless value.nil?
|
|
75
|
+
end
|
|
76
|
+
body
|
|
77
|
+
end
|
|
78
|
+
|
|
62
79
|
def enc(s)
|
|
63
80
|
::URI.encode_www_form_component(s)
|
|
64
81
|
end
|
data/lib/k9o/functions.rb
CHANGED
|
@@ -2,22 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
module K9o
|
|
4
4
|
# Function lifecycle namespace — k9o.functions.*.
|
|
5
|
+
#
|
|
6
|
+
# Upload the function's bytes first (+upload_artifact+ returns the +artifactKey+), then +deploy+ references that
|
|
7
|
+
# artifact. Deploy requires PROJECT_ADMIN and returns with status +deploying+; the function is ready when status
|
|
8
|
+
# flips to +active+.
|
|
5
9
|
class Functions
|
|
10
|
+
ARTIFACT_CONTENT_TYPES = { 'wasm' => 'application/wasm', 'tar' => 'application/x-tar' }.freeze
|
|
11
|
+
|
|
6
12
|
def initialize(config)
|
|
7
13
|
@config = config
|
|
8
14
|
end
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
# POST /@<slug>/functions/artifacts with the raw bytes: a WebAssembly module for the gateway tier (up to 16 MiB,
|
|
17
|
+
# +kind+ 'wasm') or an OCI image archive for the sandbox tier (up to 256 MiB, +kind+ 'tar'). Uploading the same
|
|
18
|
+
# bytes again returns the same key. The envelope's data is { 'artifactKey' => String }.
|
|
19
|
+
def upload_artifact(bytes, kind)
|
|
20
|
+
content_type = ARTIFACT_CONTENT_TYPES[kind.to_s]
|
|
21
|
+
raise ArgumentError, "kind must be 'wasm' or 'tar'" if content_type.nil?
|
|
22
|
+
|
|
23
|
+
bytes = bytes.read if bytes.respond_to?(:read)
|
|
24
|
+
raise ArgumentError, 'bytes must be a String or an IO' unless bytes.is_a?(String)
|
|
25
|
+
|
|
26
|
+
slug = require_project_slug
|
|
27
|
+
HttpTransport.request(config: @config, method: 'POST',
|
|
28
|
+
path: UrlGrammar.project_path(slug, 'functions/artifacts'),
|
|
29
|
+
raw_body: bytes, headers: { 'content-type' => content_type })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# POST /@<slug>/functions — deploy or redeploy from an uploaded artifact.
|
|
33
|
+
# runtime: bun python go java kotlin dotnet swift php ruby rust tinygo zig assemblyscript
|
|
34
|
+
# tier: 'gateway' (WebAssembly) or 'sandbox' (OCI image archive)
|
|
35
|
+
# artifact_key: the key +upload_artifact+ returned; version: label for this deploy (1 to 64 characters)
|
|
36
|
+
def deploy(name:, runtime:, tier:, artifact_key:, version:, description: nil)
|
|
11
37
|
slug = require_project_slug
|
|
12
|
-
body = { 'name' => name, 'runtime' => runtime, '
|
|
13
|
-
|
|
14
|
-
body['
|
|
38
|
+
body = { 'name' => name, 'runtime' => runtime, 'tier' => tier, 'artifactKey' => artifact_key,
|
|
39
|
+
'version' => version }
|
|
40
|
+
body['description'] = description unless description.nil?
|
|
15
41
|
HttpTransport.request(config: @config, method: 'POST', path: UrlGrammar.project_path(slug, 'functions'), body: body)
|
|
16
42
|
end
|
|
17
43
|
|
|
18
|
-
|
|
44
|
+
# GET /@<slug>/functions (cursor pagination).
|
|
45
|
+
def list(status: nil, limit: nil, cursor: nil)
|
|
19
46
|
slug = require_project_slug
|
|
20
|
-
q =
|
|
47
|
+
q = {}
|
|
48
|
+
q['status'] = status unless status.nil?
|
|
49
|
+
q['limit'] = limit unless limit.nil?
|
|
50
|
+
q['cursor'] = cursor unless cursor.nil?
|
|
21
51
|
HttpTransport.request(config: @config, method: 'GET', path: UrlGrammar.project_path(slug, 'functions'), query: q)
|
|
22
52
|
end
|
|
23
53
|
|
|
@@ -26,16 +56,25 @@ module K9o
|
|
|
26
56
|
HttpTransport.request(config: @config, method: 'GET', path: UrlGrammar.project_path(slug, "functions/#{enc(name)}"))
|
|
27
57
|
end
|
|
28
58
|
|
|
59
|
+
# Retire a function; its name enters a 24h cooldown.
|
|
29
60
|
def delete(name)
|
|
30
61
|
slug = require_project_slug
|
|
31
62
|
HttpTransport.request(config: @config, method: 'DELETE', path: UrlGrammar.project_path(slug, "functions/#{enc(name)}"))
|
|
32
63
|
end
|
|
33
64
|
|
|
34
|
-
#
|
|
35
|
-
|
|
65
|
+
# POST /@<slug>/functions/<name>/invoke. +payload+ is sent as the +payload+ field of the request body and passed
|
|
66
|
+
# to the function runtime verbatim. Optional controls: +background+ (run asynchronously), +delegation+
|
|
67
|
+
# ({ 'token' => ..., 'invokeSessionId' => ... }), +on_behalf_of+, +confined_to+.
|
|
68
|
+
def invoke(name, payload = nil, background: nil, delegation: nil, on_behalf_of: nil, confined_to: nil)
|
|
36
69
|
slug = require_project_slug
|
|
70
|
+
body = {}
|
|
71
|
+
body['payload'] = payload unless payload.nil?
|
|
72
|
+
body['background'] = background unless background.nil?
|
|
73
|
+
body['delegation'] = delegation unless delegation.nil?
|
|
74
|
+
body['onBehalfOf'] = on_behalf_of unless on_behalf_of.nil?
|
|
75
|
+
body['confinedTo'] = confined_to unless confined_to.nil?
|
|
37
76
|
HttpTransport.request(config: @config, method: 'POST',
|
|
38
|
-
path: UrlGrammar.project_path(slug, "functions/#{enc(name)}/invoke"), body:
|
|
77
|
+
path: UrlGrammar.project_path(slug, "functions/#{enc(name)}/invoke"), body: body)
|
|
39
78
|
end
|
|
40
79
|
|
|
41
80
|
private
|
data/lib/k9o/gate.rb
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module K9o
|
|
4
|
-
# Gate (Identity-Aware Kernel) namespace. Project-scoped.
|
|
4
|
+
# Gate (Identity-Aware Kernel) namespace. Project-scoped; the gateway closes each body to the fields shown.
|
|
5
5
|
class Gate
|
|
6
6
|
def initialize(config)
|
|
7
7
|
@config = config
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
# POST /@<slug>/gate/signup { email, password, displayName? }
|
|
10
11
|
def sign_up(email:, password:, display_name: nil)
|
|
11
12
|
body = { 'email' => email, 'password' => password }
|
|
12
13
|
body['displayName'] = display_name if display_name
|
|
13
14
|
HttpTransport.request(config: @config, method: 'POST', path: gate_path('signup'), body: body)
|
|
14
15
|
end
|
|
15
16
|
|
|
17
|
+
# POST /@<slug>/gate/signin { email, password }
|
|
16
18
|
def sign_in(email:, password:)
|
|
17
19
|
HttpTransport.request(
|
|
18
20
|
config: @config, method: 'POST', path: gate_path('signin'),
|
|
@@ -20,15 +22,18 @@ module K9o
|
|
|
20
22
|
)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
HttpTransport.request(config: @config, method: 'POST', path: gate_path('signout'),
|
|
25
|
+
# POST /@<slug>/gate/signout { refreshToken } — the refresh token is required (the gateway answers 400 without it).
|
|
26
|
+
def sign_out(refresh_token:)
|
|
27
|
+
HttpTransport.request(config: @config, method: 'POST', path: gate_path('signout'),
|
|
28
|
+
body: { 'refreshToken' => refresh_token })
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
# GET /@<slug>/gate/whoami
|
|
28
32
|
def whoami
|
|
29
33
|
HttpTransport.request(config: @config, method: 'GET', path: gate_path('whoami'))
|
|
30
34
|
end
|
|
31
35
|
|
|
36
|
+
# POST /@<slug>/gate/refresh { refreshToken }
|
|
32
37
|
def refresh_token(refresh_token:)
|
|
33
38
|
HttpTransport.request(
|
|
34
39
|
config: @config, method: 'POST', path: gate_path('refresh'),
|
|
@@ -36,20 +41,6 @@ module K9o
|
|
|
36
41
|
)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
def verify_session
|
|
40
|
-
HttpTransport.request(config: @config, method: 'GET', path: gate_path('verify'))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# The project's hosted sign-in URL and WebAuthn rpId:
|
|
44
|
-
# GET /@<slug>/gate/hosted. Unauthenticated and cacheable; no credential is
|
|
45
|
-
# sent even when an api_key is configured. Returns an Envelope whose `data`
|
|
46
|
-
# is { 'signInUrl' => String, 'rpId' => String }. Take both values as given:
|
|
47
|
-
# an unknown org, a deleted project and a non-Gate project all answer the
|
|
48
|
-
# same 404 K9O_PROJECT_NOT_FOUND.
|
|
49
|
-
def hosted
|
|
50
|
-
HttpTransport.request(config: @config, method: 'GET', path: gate_path('hosted'), authenticated: false)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
44
|
private
|
|
54
45
|
|
|
55
46
|
def gate_path(action)
|