@k9o/core 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.
package/src/index.ts CHANGED
@@ -50,7 +50,6 @@ export type {
50
50
  StorageBucket, CreateBucketOptions, UpdateBucketOptions, StorageObject, UploadOptions, SignedUrlOptions,
51
51
  SignedUrl, RemovedObject, DeletedBucket,
52
52
  } from "./storage-buckets.ts";
53
- import { makeMetadataNamespace } from "./metadata.ts";
54
53
  import { makeMigrationsNamespace } from "./migrations.ts";
55
54
  import { makeFunctionsNamespace } from "./functions.ts";
56
55
  import { makePoliciesNamespace } from "./policies.ts";
@@ -83,6 +82,9 @@ export {
83
82
  export {
84
83
  validateBaseUrl,
85
84
  resolveBaseUrl,
85
+ parseApiHost,
86
+ DEFAULT_BASE_URL,
87
+ type ApiHost,
86
88
  validateSlug,
87
89
  projectPath,
88
90
  orgPath,
@@ -109,23 +111,21 @@ export type {
109
111
  StorageLocation, StorageLocationProvider, StorageLocationCustodyMode, StorageLocationProviderParams,
110
112
  CreateStorageLocationInput, UpdateStorageLocationInput, RetiredStorageLocation, StorageLocationTestResult,
111
113
  } from "./storage-locations.ts";
112
- export {
113
- METADATA_OPERATORS,
114
- type MetadataSlice, type MetadataOperator, type MetadataSurface,
115
- type MetadataWhereClause, type MetadataQueryRecord, type MetadataQueryResult,
116
- type MetadataQueryBuilder,
117
- } from "./metadata.ts";
118
114
  export type {
119
- Migration, AppliedMigration, PendingMigration,
115
+ Migration, AppliedMigration, AppliedMigrationRow, SkippedMigrationRow, ApplyMigrationsResult, ApplyPendingResult,
116
+ PendingMigration, PendingMigrationStatus, QueuedMigration,
120
117
  Operation, ColumnSpec, IndexSpec, ColumnReferences,
121
118
  MigrationPolicy, CollectionPolicy, StorageBucketPolicy, RelationshipSpec, ActionRule,
122
119
  } from "./migrations.ts";
123
120
  export type { FunctionRecord, DeployFunctionInput } from "./functions.ts";
124
- export type { PolicyBundle, CreatePolicyInput, UpdatePolicyInput, ValidatePolicyResult } from "./policies.ts";
121
+ export type {
122
+ PolicyBundle, PolicyDocument, PolicyPublishResult, PublishPolicyOptions, CreatePolicyInput, UpdatePolicyInput, ValidatePolicyResult,
123
+ } from "./policies.ts";
125
124
  export type { AuditEvent, AuditQueryOpts } from "./audit.ts";
126
125
  export type { Org } from "./orgs.ts";
127
126
  export type {
128
127
  OrgMember,
128
+ OrgInviteResult,
129
129
  OrgGroup,
130
130
  OrgRoleGrant,
131
131
  OrgAuditEvent,
@@ -134,12 +134,16 @@ export type {
134
134
  SmtpTestEmailResult,
135
135
  OrgIdpProvider,
136
136
  CreateOrgIdpInput,
137
+ UpdateOrgIdpInput,
138
+ OrgIdpProviderKind,
139
+ OrgDefaultLlm,
137
140
  } from "./org.ts";
138
141
  export type {
139
142
  MyProfile,
140
143
  MySession,
144
+ UpdateMyProfileInput,
141
145
  MyApiToken,
142
- MyNotification,
146
+ CreateMyApiTokenInput,
143
147
  } from "./me.ts";
144
148
  export type { ExtensionSlug, Extension } from "./extensions-bridge.ts";
145
149
  export type { GateClient } from "@k9o/gate";
@@ -188,6 +192,7 @@ export {
188
192
  type LLMInvokeParams,
189
193
  type LLMInvokeInput,
190
194
  type LLMResponse,
195
+ type LLMUsage,
191
196
  type LLMStreamChunk,
192
197
  } from "./compute/llms.ts";
193
198
 
@@ -203,9 +208,7 @@ export {
203
208
  type RealtimeFilter,
204
209
  type RealtimeEvent,
205
210
  type RealtimeSubscription,
206
- type RealtimeTransportKind,
207
211
  type SubscribeOptions,
208
- type ReplayInput,
209
212
  } from "./realtime.ts";
210
213
 
211
214
  /**
@@ -222,9 +225,6 @@ export type K9oClient = {
222
225
  * `client.kibo.vault.secrets` (credential pre-create): this namespace only registers a caller-supplied
223
226
  * `credentialRef` string, never creates or resolves one. */
224
227
  storage: ReturnType<typeof makeStorageNamespace>;
225
- /** Cross-surface metadata query — `client.metadata.from(...).where(...).execute()`. Client-side
226
- * only until the gateway's Inc-2 route lands; see `metadata.ts` for status. */
227
- metadata: ReturnType<typeof makeMetadataNamespace>;
228
228
  /** Migration lifecycle — `client.migrations.apply()` etc. */
229
229
  migrations: ReturnType<typeof makeMigrationsNamespace>;
230
230
  /** Function deploy + invoke — `client.functions.deploy()` etc. */
@@ -245,7 +245,7 @@ export type K9oClient = {
245
245
  compute: ReturnType<typeof makeComputeNamespace>;
246
246
  /** Platform AI primitives — `client.ai.embed({ model?, input })`. */
247
247
  ai: ReturnType<typeof makeAiNamespace>;
248
- /** Realtime subscriptions — `client.realtime.subscribe()` / `.replay()`. */
248
+ /** Realtime subscriptions — `client.realtime.subscribe()` (SSE). */
249
249
  realtime: ReturnType<typeof makeRealtimeNamespace>;
250
250
  /** Gate (Identity-Aware Kernel) — composed from `@k9o/gate`. Available when projectSlug is bound on the client. */
251
251
  readonly gate: GateClient;
@@ -278,7 +278,6 @@ export function createClient(options: CreateClientOptions): K9oClient {
278
278
  from: makeFromFactory(config),
279
279
  projects: makeProjectsNamespace(config),
280
280
  storage: makeStorageNamespace(config),
281
- metadata: makeMetadataNamespace(config),
282
281
  migrations: makeMigrationsNamespace(config),
283
282
  functions: makeFunctionsNamespace(config),
284
283
  policies: makePoliciesNamespace(config),
package/src/me.ts CHANGED
@@ -7,14 +7,10 @@
7
7
  *
8
8
  * Wire mapping (gateway uses platform-plane / cross-org `/me/...` routes):
9
9
  * - `client.me.profile.get/update` → GET/PATCH /me
10
- * - `client.me.sessions.list/revoke` → GET/DELETE /me/sessions
11
- * - `client.me.apiTokens.list/create/delete` → GET/POST/DELETE /me/api-tokens (not available yet)
12
- * - `client.me.notifications.list/markRead` → GET/PATCH /me/notifications (not available yet)
13
- * - `client.me.auditLog.query` → GET /me/audit-log (not available yet)
14
- * - `client.me.dataExport.request` → POST /me/data-export (not available yet)
15
- *
16
- * Profile, sessions and MFA setup are served by the existing gateway routes
17
- * `me.ts` + `mfa.ts`.
10
+ * - `client.me.sessions.list` → GET /me/sessions
11
+ * - `client.me.sessions.revoke` → POST /auth/sessions/<sessionId>/revoke
12
+ * - `client.me.sessions.revokeAll` → POST /me/sessions/revoke-all
13
+ * - `client.me.apiTokens.list/create/delete` → GET/POST/DELETE /me/api-tokens
18
14
  */
19
15
 
20
16
  import { request, type HttpClientConfig } from "./http.ts";
@@ -22,48 +18,82 @@ import { orgPath } from "./url-grammar.ts";
22
18
  import type { Envelope } from "./envelope.ts";
23
19
 
24
20
  export type MyProfile = {
25
- /** `u_<base62>`. */
21
+ /** `usr_<base62>`. */
26
22
  id: string;
27
23
  email: string;
28
- displayName: string | null;
24
+ displayName: string;
25
+ mfaEnabled: boolean;
26
+ verifiedAt: string | null;
27
+ /** Set while the account is in its deletion grace period. */
28
+ deletedAt: string | null;
29
+ deletionRequestedAt: string | null;
30
+ deletionScheduledFor: string | null;
31
+ avatarUrl: string | null;
32
+ /** BCP 47 language tag, or `null` when unset. */
33
+ locale: string | null;
34
+ /** IANA time zone name, or `null` when unset. */
35
+ timezone: string | null;
29
36
  /** ISO 8601. */
30
37
  createdAt: string;
31
- /** Last activity timestamp. */
32
- lastActiveAt: string | null;
38
+ /** ISO 8601. */
39
+ updatedAt: string;
33
40
  };
34
41
 
35
42
  export type MySession = {
36
43
  /** `sess_<base62>`. */
37
44
  id: string;
38
- /** Initial sign-in time. */
39
- createdAt: string;
40
- /** Most recent activity time. */
41
- lastSeenAt: string;
45
+ userId: string;
46
+ /** Sign-in time. */
47
+ issuedAt: string;
48
+ expiresAt: string;
49
+ revokedAt: string | null;
50
+ mfaVerifiedAt: string | null;
42
51
  userAgent: string | null;
43
52
  ipAddress: string | null;
53
+ /** Always `null` for now. */
54
+ geoCountry: string | null;
44
55
  /** Whether this session is the one issuing the current request. */
45
- current: boolean;
56
+ isCurrentSession: boolean;
57
+ };
58
+
59
+ /** Fields `profile.update` may change; `null` clears `locale` and `timezone`. */
60
+ export type UpdateMyProfileInput = {
61
+ displayName?: string | undefined;
62
+ /** BCP 47 language tag, for example `de-DE`. */
63
+ locale?: string | null | undefined;
64
+ /** IANA time zone name, for example `Europe/Berlin`. */
65
+ timezone?: string | null | undefined;
46
66
  };
47
67
 
48
68
  export type MyApiToken = {
49
- /** `tok_<base62>`. */
69
+ /** `pat_<base62>`. */
50
70
  id: string;
51
- name: string;
71
+ userId: string;
72
+ orgId: string;
73
+ label: string;
74
+ scopeManifest: CreateMyApiTokenInput["scopeManifest"];
75
+ ttlSeconds: number;
52
76
  /** ISO 8601. */
53
77
  createdAt: string;
54
- /** ISO 8601 or null for non-expiring tokens. */
55
- expiresAt: string | null;
56
- /** Last 4 chars for display. */
57
- suffix: string;
58
- scope: string;
78
+ /** ISO 8601. */
79
+ expiresAt: string;
80
+ lastUsedAt: string | null;
81
+ revokedReason: string | null;
82
+ status: "active" | "revoked" | "expired";
59
83
  };
60
84
 
61
- export type MyNotification = {
62
- id: string;
63
- type: string;
64
- payload: Record<string, unknown>;
65
- createdAt: string;
66
- readAt: string | null;
85
+ /** Body of POST /me/api-tokens. Each `scopeManifest` field narrows the token; `null` or absent leaves it unrestricted. */
86
+ export type CreateMyApiTokenInput = {
87
+ label: string;
88
+ scopeManifest: {
89
+ operations?: ReadonlyArray<"read" | "write" | "admin"> | null | undefined;
90
+ projects?: ReadonlyArray<string> | null | undefined;
91
+ resourceClasses?: ReadonlyArray<string> | null | undefined;
92
+ collections?: Record<string, unknown> | null | undefined;
93
+ cidrs?: ReadonlyArray<string> | null | undefined;
94
+ };
95
+ /** Token lifetime in seconds (60 to 31536000). */
96
+ ttlSeconds?: number | undefined;
67
97
  };
68
98
 
69
99
  export function makeMeNamespace(config: HttpClientConfig) {
@@ -74,8 +104,8 @@ export function makeMeNamespace(config: HttpClientConfig) {
74
104
  return request(config, { method: "GET", path: orgPath("me") });
75
105
  },
76
106
 
77
- /** PATCH /me — update mutable profile fields (displayName). */
78
- async update(patch: { displayName?: string | null }): Promise<Envelope<MyProfile>> {
107
+ /** PATCH /me — update mutable profile fields (displayName, locale, timezone). */
108
+ async update(patch: UpdateMyProfileInput): Promise<Envelope<MyProfile>> {
79
109
  return request(config, { method: "PATCH", path: orgPath("me"), body: patch });
80
110
  },
81
111
  },
@@ -86,76 +116,30 @@ export function makeMeNamespace(config: HttpClientConfig) {
86
116
  return request(config, { method: "GET", path: orgPath("me/sessions") });
87
117
  },
88
118
 
89
- /** DELETE /me/sessions/<sessionId> — revoke a specific session. */
119
+ /** POST /auth/sessions/<sessionId>/revoke — revoke a specific session. */
90
120
  async revoke(sessionId: string): Promise<Envelope<{ revoked: boolean }>> {
91
- return request(config, { method: "DELETE", path: orgPath(`me/sessions/${encodeURIComponent(sessionId)}`) });
121
+ return request(config, { method: "POST", path: orgPath(`auth/sessions/${encodeURIComponent(sessionId)}/revoke`) });
92
122
  },
93
123
 
94
- /** DELETE /me/sessions (no path) — revoke all sessions except the current one. */
124
+ /** POST /me/sessions/revoke-all — revoke all of the operator's sessions except the current one. */
95
125
  async revokeAll(): Promise<Envelope<{ revoked: number }>> {
96
- return request(config, { method: "DELETE", path: orgPath("me/sessions") });
126
+ return request(config, { method: "POST", path: orgPath("me/sessions/revoke-all") });
97
127
  },
98
128
  },
99
129
 
100
- /**
101
- * API tokens for personal scripting. The gateway endpoints are not available yet,
102
- * so these methods return K9O_NOT_FOUND until they ship.
103
- */
130
+ /** Personal access tokens for scripting. */
104
131
  apiTokens: {
105
132
  async list(): Promise<Envelope<ReadonlyArray<MyApiToken>>> {
106
133
  return request(config, { method: "GET", path: orgPath("me/api-tokens") });
107
134
  },
108
135
 
109
- async create(input: { name: string; expiresInDays?: number | undefined; scope?: string | undefined }): Promise<Envelope<MyApiToken & { token: string }>> {
136
+ async create(input: CreateMyApiTokenInput): Promise<Envelope<MyApiToken & { plaintext: string }>> {
110
137
  return request(config, { method: "POST", path: orgPath("me/api-tokens"), body: input });
111
138
  },
112
139
 
113
- async delete(tokenId: string): Promise<Envelope<{ deleted: boolean }>> {
140
+ async delete(tokenId: string): Promise<Envelope<MyApiToken>> {
114
141
  return request(config, { method: "DELETE", path: orgPath(`me/api-tokens/${encodeURIComponent(tokenId)}`) });
115
142
  },
116
143
  },
117
-
118
- /**
119
- * Notifications — operator-targeted (account-level events). Forward-
120
- * cycle substrate; methods route correctly when gateway ships.
121
- */
122
- notifications: {
123
- async list(opts?: { unreadOnly?: boolean | undefined; limit?: number | undefined; cursor?: string | undefined }): Promise<Envelope<ReadonlyArray<MyNotification>>> {
124
- return request(config, { method: "GET", path: orgPath("me/notifications"), query: opts });
125
- },
126
-
127
- async markRead(notificationId: string): Promise<Envelope<MyNotification>> {
128
- return request(config, { method: "PATCH", path: orgPath(`me/notifications/${encodeURIComponent(notificationId)}`), body: { read: true } });
129
- },
130
-
131
- async markAllRead(): Promise<Envelope<{ marked: number }>> {
132
- return request(config, { method: "PATCH", path: orgPath("me/notifications"), body: { read: true } });
133
- },
134
- },
135
-
136
- /**
137
- * Operator-self audit log — actions performed BY this operator across
138
- * orgs. Distinct from `client.audit` (project-plane) and
139
- * `client.org.auditLog` (org-plane).
140
- */
141
- auditLog: {
142
- async query(opts?: { since?: string | undefined; until?: string | undefined; type?: string | undefined; limit?: number | undefined; cursor?: string | undefined }): Promise<Envelope<ReadonlyArray<{ id: string; type: string; occurredAt: string; payload: Record<string, unknown> }>>> {
143
- return request(config, { method: "GET", path: orgPath("me/audit-log"), query: opts });
144
- },
145
- },
146
-
147
- /**
148
- * Personal data export per GDPR-style requests. Returns a job id
149
- * the operator polls for completion.
150
- */
151
- dataExport: {
152
- async request(input?: { format?: "json" | "csv" }): Promise<Envelope<{ jobId: string; status: "queued" | "in-progress" | "ready" }>> {
153
- return request(config, { method: "POST", path: orgPath("me/data-export"), body: input ?? {} });
154
- },
155
-
156
- async status(jobId: string): Promise<Envelope<{ jobId: string; status: "queued" | "in-progress" | "ready" | "failed"; downloadUrl: string | null }>> {
157
- return request(config, { method: "GET", path: orgPath(`me/data-export/${encodeURIComponent(jobId)}`) });
158
- },
159
- },
160
144
  };
161
145
  }
package/src/migrations.ts CHANGED
@@ -152,25 +152,74 @@ export type Migration = {
152
152
  };
153
153
 
154
154
  /**
155
- * Applied-migration record returned by `list` + `get`.
155
+ * Applied-migration record returned by `list` + `get`. `get` also carries `opPayload`
156
+ * (the migration body as submitted, or `null` for old migrations).
156
157
  */
157
158
  export type AppliedMigration = {
158
159
  version: number;
159
160
  slug: string;
160
161
  checksum: string;
161
162
  appliedAt: string;
163
+ noPolicy: boolean;
164
+ affectedCollections: ReadonlyArray<string>;
165
+ opPayload?: unknown;
162
166
  };
163
167
 
168
+ /** A migration that `apply` or `applyPending` just applied. Carries no `appliedAt`. */
169
+ export type AppliedMigrationRow = {
170
+ version: number;
171
+ slug: string;
172
+ checksum: string;
173
+ };
174
+
175
+ /** A migration `apply` left alone, e.g. `already_applied`. */
176
+ export type SkippedMigrationRow = {
177
+ version: number;
178
+ slug: string;
179
+ reason: string;
180
+ };
181
+
182
+ /** Result of `apply`. */
183
+ export type ApplyMigrationsResult = {
184
+ applied: ReadonlyArray<AppliedMigrationRow>;
185
+ skipped: ReadonlyArray<SkippedMigrationRow>;
186
+ };
187
+
188
+ /** Result of `applyPending`: `pendingId` is the queued row that was applied. */
189
+ export type ApplyPendingResult = ApplyMigrationsResult & {
190
+ pendingId: string;
191
+ };
192
+
193
+ export type PendingMigrationStatus = "queued" | "pending_approval" | "applied" | "rejected" | "cancelled";
194
+
164
195
  /**
165
196
  * Pending migration record returned by `listPending`.
166
197
  */
167
198
  export type PendingMigration = {
168
- pendingId: string;
199
+ id: string;
169
200
  version: number;
170
201
  slug: string;
171
202
  checksum: string;
172
- submittedAt: string;
173
- submittedBy: string;
203
+ status: PendingMigrationStatus;
204
+ affectedCollections: ReadonlyArray<string>;
205
+ /** Approval that gates a `pending_approval` row, else `null`. */
206
+ approvalId: string | null;
207
+ queuedAt: string;
208
+ queuedBy: string;
209
+ decidedAt: string | null;
210
+ decidedBy: string | null;
211
+ decisionReason: string | null;
212
+ };
213
+
214
+ /** One row of the `queue` answer. */
215
+ export type QueuedMigration = {
216
+ /** The pending id to pass to `applyPending`, `rejectPending` or `deletePending`. */
217
+ id: string;
218
+ version: number;
219
+ slug: string;
220
+ status: "queued" | "pending_approval";
221
+ approvalId: string | null;
222
+ affectedCollections: ReadonlyArray<string>;
174
223
  };
175
224
 
176
225
  function requireProjectSlug(config: HttpClientConfig): string {
@@ -183,7 +232,7 @@ function requireProjectSlug(config: HttpClientConfig): string {
183
232
  export function makeMigrationsNamespace(config: HttpClientConfig) {
184
233
  return {
185
234
  /** Apply migrations immediately (PROJECT_ADMIN minimum). */
186
- async apply(input: { migrations: ReadonlyArray<Migration> }): Promise<Envelope<{ applied: ReadonlyArray<AppliedMigration> }>> {
235
+ async apply(input: { migrations: ReadonlyArray<Migration> }): Promise<Envelope<ApplyMigrationsResult>> {
187
236
  const slug = requireProjectSlug(config);
188
237
  return request(config, {
189
238
  method: "POST",
@@ -211,7 +260,7 @@ export function makeMigrationsNamespace(config: HttpClientConfig) {
211
260
  },
212
261
 
213
262
  /** Queue migrations for maker-checker approval. */
214
- async queue(input: { migrations: ReadonlyArray<Migration> }): Promise<Envelope<{ pending: ReadonlyArray<PendingMigration> }>> {
263
+ async queue(input: { migrations: ReadonlyArray<Migration> }): Promise<Envelope<ReadonlyArray<QueuedMigration>>> {
215
264
  const slug = requireProjectSlug(config);
216
265
  return request(config, {
217
266
  method: "POST",
@@ -220,12 +269,13 @@ export function makeMigrationsNamespace(config: HttpClientConfig) {
220
269
  });
221
270
  },
222
271
 
223
- /** List pending (queued, not yet approved) migrations. */
224
- async listPending(): Promise<Envelope<ReadonlyArray<PendingMigration>>> {
272
+ /** List pending migrations: `queued` ones by default, or those with the given `status`. */
273
+ async listPending(opts?: { status?: PendingMigrationStatus | undefined }): Promise<Envelope<ReadonlyArray<PendingMigration>>> {
225
274
  const slug = requireProjectSlug(config);
226
275
  return request(config, {
227
276
  method: "GET",
228
277
  path: projectPath(slug, "migrations/pending"),
278
+ query: opts,
229
279
  });
230
280
  },
231
281
 
@@ -235,7 +285,7 @@ export function makeMigrationsNamespace(config: HttpClientConfig) {
235
285
  * migration awaiting a distinct-admin's review, is refused here and
236
286
  * needs its own approval flow, not this call).
237
287
  */
238
- async applyPending(pendingId: string): Promise<Envelope<AppliedMigration>> {
288
+ async applyPending(pendingId: string): Promise<Envelope<ApplyPendingResult>> {
239
289
  const slug = requireProjectSlug(config);
240
290
  return request(config, {
241
291
  method: "POST",
@@ -244,7 +294,7 @@ export function makeMigrationsNamespace(config: HttpClientConfig) {
244
294
  },
245
295
 
246
296
  /** Reject a pending migration (transitions to rejected; does not apply). */
247
- async rejectPending(pendingId: string, input?: { reason?: string }): Promise<Envelope<{ rejected: boolean }>> {
297
+ async rejectPending(pendingId: string, input?: { reason?: string }): Promise<Envelope<{ pendingId: string; status: "rejected" }>> {
248
298
  const slug = requireProjectSlug(config);
249
299
  return request(config, {
250
300
  method: "POST",
@@ -254,7 +304,7 @@ export function makeMigrationsNamespace(config: HttpClientConfig) {
254
304
  },
255
305
 
256
306
  /** Delete a pending migration from the queue. */
257
- async deletePending(pendingId: string): Promise<Envelope<{ deleted: boolean }>> {
307
+ async deletePending(pendingId: string): Promise<Envelope<{ pendingId: string; status: "cancelled" }>> {
258
308
  const slug = requireProjectSlug(config);
259
309
  return request(config, {
260
310
  method: "DELETE",