@k2b/cloud 0.47.0 → 0.49.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.
Files changed (240) hide show
  1. package/package.json +3 -3
  2. package/src/_internal/app-access-manager-sql.ts +10 -0
  3. package/src/_internal/app-access-resources.ts +2 -1
  4. package/src/_internal/app-capabilities.ts +73 -0
  5. package/src/_internal/app-event-catalog.ts +1 -1
  6. package/src/_internal/app-queries.ts +1 -1
  7. package/src/_internal/application-package.ts +2 -1
  8. package/src/_internal/bounded-json.ts +7 -1
  9. package/src/_internal/capabilities.ts +25 -2
  10. package/src/_internal/define-app.ts +33 -2
  11. package/src/_internal/heartbeat.ts +2 -0
  12. package/src/_internal/nats-connection.ts +10 -9
  13. package/src/_internal/process-sync.ts +6 -6
  14. package/src/_internal/public-surface.ts +76 -0
  15. package/src/_internal/purpose-name.ts +7 -0
  16. package/src/_internal/registry-validation.ts +1 -0
  17. package/src/_internal/registry.ts +49 -3
  18. package/src/_internal/workload-credential.ts +2 -1
  19. package/src/ai/app-skill-store.ts +2 -1
  20. package/src/ai/app-skills.ts +2 -1
  21. package/src/ai/approvals.ts +2 -1
  22. package/src/ai/chat-tasks.ts +2 -1
  23. package/src/ai/credentials.ts +2 -1
  24. package/src/ai/dictation-runtime.ts +1 -1
  25. package/src/ai/dictations.ts +1 -1
  26. package/src/ai/files-store.ts +2 -1
  27. package/src/ai/inference-calls.ts +2 -1
  28. package/src/ai/map-store.ts +1 -1
  29. package/src/ai/map-tool.ts +1 -1
  30. package/src/ai/memories.ts +1 -1
  31. package/src/ai/memory-learning-runs.ts +1 -1
  32. package/src/ai/memory-learning.ts +1 -1
  33. package/src/ai/memory-workflow-evidence.ts +1 -1
  34. package/src/ai/message-queue.ts +2 -1
  35. package/src/ai/migrate.ts +1 -1
  36. package/src/ai/model-access-migrate.ts +1 -1
  37. package/src/ai/model-access.ts +1 -1
  38. package/src/ai/model-catalog.ts +1 -1
  39. package/src/ai/model-delete.ts +1 -1
  40. package/src/ai/model-usage.ts +2 -1
  41. package/src/ai/open-questions.ts +1 -1
  42. package/src/ai/openai-api-slots.ts +2 -1
  43. package/src/ai/prefs.ts +2 -1
  44. package/src/ai/projects.ts +2 -1
  45. package/src/ai/provider-connections.ts +1 -1
  46. package/src/ai/quota-limits.ts +2 -1
  47. package/src/ai/quota-provider.ts +2 -1
  48. package/src/ai/quota-report.ts +2 -1
  49. package/src/ai/quotas-migrate.ts +2 -1
  50. package/src/ai/quotas.ts +2 -1
  51. package/src/ai/request-headers.ts +2 -1
  52. package/src/ai/review-content.ts +2 -1
  53. package/src/ai/skill-search.ts +2 -1
  54. package/src/ai/skills.ts +2 -1
  55. package/src/ai/store.ts +2 -1
  56. package/src/ai/structured-runs.ts +2 -1
  57. package/src/ai/tool-audit.ts +2 -1
  58. package/src/ai/turn-timing.ts +1 -1
  59. package/src/ai/usage.ts +2 -1
  60. package/src/ai/web-provenance.ts +2 -1
  61. package/src/api/admin-ai-quotas.ts +1 -1
  62. package/src/api/admin-ai.ts +1 -1
  63. package/src/api/admin-app-domains.ts +62 -0
  64. package/src/api/admin-app-keys.ts +47 -3
  65. package/src/api/admin-core-settings.ts +1 -1
  66. package/src/api/admin-outgoing-mail.ts +1 -1
  67. package/src/api/app-bootstrap.ts +153 -0
  68. package/src/api/app-events.ts +1 -1
  69. package/src/api/auth.ts +1 -1
  70. package/src/api/authenticate-app-request.ts +129 -0
  71. package/src/api/capabilities.ts +87 -6
  72. package/src/api/index.ts +7 -0
  73. package/src/api/me.ts +1 -1
  74. package/src/api/search.bench.ts +1 -1
  75. package/src/app-pwa/service.ts +2 -1
  76. package/src/app-pwa/transaction.ts +4 -2
  77. package/src/capabilities/claims.ts +2 -1
  78. package/src/capabilities/executions.ts +8 -4
  79. package/src/capabilities/geo-client.ts +1 -1
  80. package/src/capabilities/migrate.ts +3 -1
  81. package/src/capabilities/resource-readers.ts +2 -2
  82. package/src/capabilities/response.ts +2 -1
  83. package/src/capabilities/server-invoke.ts +73 -20
  84. package/src/capabilities/server.ts +2 -1
  85. package/src/cli/admin/app-domains.ts +140 -0
  86. package/src/cli/admin/app-keys.ts +25 -3
  87. package/src/cli/admin/index.ts +107 -88
  88. package/src/config/env.ts +10 -4
  89. package/src/contracts/app-domains.ts +91 -0
  90. package/src/contracts/app-status.ts +104 -0
  91. package/src/contracts/capabilities.ts +37 -2
  92. package/src/contracts/index.ts +1 -0
  93. package/src/contracts/outgoing-mail.ts +1 -1
  94. package/src/contracts/registry.ts +2 -0
  95. package/src/events/app-event-delivery.ts +1 -1
  96. package/src/events/live-delivery.ts +2 -1
  97. package/src/index.ts +1 -0
  98. package/src/runtime/bootstrap-client.ts +195 -0
  99. package/src/runtime/bootstrap-protocol.ts +67 -0
  100. package/src/runtime/clients.ts +6 -2
  101. package/src/runtime/connections.ts +9 -0
  102. package/src/server/app-key.ts +14 -0
  103. package/src/server/index.ts +2 -0
  104. package/src/server/ratelimit.ts +1 -1
  105. package/src/server/services/access.ts +2 -1
  106. package/src/services/account-category-policy.ts +1 -1
  107. package/src/services/account-lifecycle/audit.ts +1 -1
  108. package/src/services/account-lifecycle/index.ts +1 -1
  109. package/src/services/account-lifecycle/ipa-backfill.ts +2 -1
  110. package/src/services/accounts/app.ts +1 -1
  111. package/src/services/accounts/avatar.ts +1 -1
  112. package/src/services/accounts/base-group.ts +1 -1
  113. package/src/services/accounts/directory-shares.ts +1 -1
  114. package/src/services/accounts/duplicate-emails.ts +1 -1
  115. package/src/services/accounts/entities.ts +2 -1
  116. package/src/services/accounts/group-sql.ts +2 -1
  117. package/src/services/accounts/groups.ts +1 -1
  118. package/src/services/accounts/identities.ts +13 -11
  119. package/src/services/accounts/ipa-data.ts +1 -1
  120. package/src/services/accounts/local-groups.ts +2 -1
  121. package/src/services/accounts/machine-group-management.ts +2 -1
  122. package/src/services/accounts/posix.ts +13 -12
  123. package/src/services/accounts/request-policy.ts +2 -1
  124. package/src/services/accounts/switching.ts +1 -1
  125. package/src/services/accounts/users.ts +2 -1
  126. package/src/services/announcements/index.ts +1 -1
  127. package/src/services/app-access-management.ts +3 -9
  128. package/src/services/app-access.ts +2 -1
  129. package/src/services/app-admin-facts.ts +160 -0
  130. package/src/services/app-approval.ts +34 -30
  131. package/src/services/app-bootstrap.ts +217 -0
  132. package/src/services/app-details.ts +3 -2
  133. package/src/services/app-domain-check.ts +152 -0
  134. package/src/services/app-domains.ts +172 -0
  135. package/src/services/app-events/accept.ts +5 -5
  136. package/src/services/app-events/admin.ts +15 -15
  137. package/src/services/app-events/declarations.ts +6 -3
  138. package/src/services/app-events/deliver.ts +6 -6
  139. package/src/services/app-events.ts +3 -2
  140. package/src/services/app-keys.ts +45 -11
  141. package/src/services/approvals/content.ts +1 -1
  142. package/src/services/approvals/index.ts +2 -1
  143. package/src/services/approvals/list.ts +1 -1
  144. package/src/services/approvals/migrate.ts +1 -1
  145. package/src/services/audit/index.ts +2 -1
  146. package/src/services/auth-flows/ipa.ts +1 -1
  147. package/src/services/auth-flows/magic-link.ts +1 -1
  148. package/src/services/auth-flows/password-reset.ts +1 -1
  149. package/src/services/auth-flows/proxy-return.ts +1 -1
  150. package/src/services/downstream-budget.ts +2 -2
  151. package/src/services/geo/admin.ts +13 -12
  152. package/src/services/geo/identity.ts +46 -0
  153. package/src/services/geo/ledger.ts +118 -106
  154. package/src/services/geo/migrate.ts +2 -1
  155. package/src/services/geo/route-chain.ts +3 -3
  156. package/src/services/geo/service.ts +4 -24
  157. package/src/services/geoip/index.ts +5 -5
  158. package/src/services/geoip/store.ts +24 -21
  159. package/src/services/help/index.ts +2 -1
  160. package/src/services/help/store.ts +2 -1
  161. package/src/services/identity/app-request.ts +44 -0
  162. package/src/services/identity/constants.ts +1 -0
  163. package/src/services/identity/index.ts +1 -0
  164. package/src/services/identity/invocation-actor.ts +2 -1
  165. package/src/services/identity/invocation-token.ts +112 -0
  166. package/src/services/identity/key-ring.ts +2 -2
  167. package/src/services/identity/resource-readers.ts +1 -1
  168. package/src/services/identity/routes.ts +11 -3
  169. package/src/services/identity/runtime-config.ts +14 -3
  170. package/src/services/identity/workload-auth.ts +2 -0
  171. package/src/services/identity/workload-runtime.ts +3 -3
  172. package/src/services/index.ts +4 -0
  173. package/src/services/ipa/groups.ts +1 -1
  174. package/src/services/ipa/profile.ts +1 -1
  175. package/src/services/ipa/search.ts +1 -1
  176. package/src/services/ipa/sync.ts +1 -1
  177. package/src/services/ipa/users.ts +1 -1
  178. package/src/services/known-browsers.ts +2 -1
  179. package/src/services/legal-consent.ts +1 -1
  180. package/src/services/links/index.ts +16 -0
  181. package/src/services/links/runtime.ts +44 -0
  182. package/src/services/links/store.ts +105 -0
  183. package/src/services/logging/index.ts +13 -8
  184. package/src/services/logging/redaction.ts +6 -1
  185. package/src/services/logging/trace.ts +9 -9
  186. package/src/services/logging/web-vitals.ts +1 -1
  187. package/src/services/mandates/index.ts +2 -1
  188. package/src/services/notifications/batches.ts +1 -1
  189. package/src/services/notifications/browser.ts +1 -1
  190. package/src/services/notifications/catalog.ts +1 -1
  191. package/src/services/notifications/dispatcher.ts +1 -1
  192. package/src/services/notifications/index.ts +1 -1
  193. package/src/services/notifications/observability.ts +2 -1
  194. package/src/services/notifications/platform.ts +1 -1
  195. package/src/services/notifications/quiet.ts +1 -1
  196. package/src/services/notifications/user.ts +1 -1
  197. package/src/services/oauth-tokens.ts +1 -1
  198. package/src/services/outbox.ts +15 -14
  199. package/src/services/outgoing-mail/admin.ts +1 -1
  200. package/src/services/outgoing-mail/dispatcher.ts +1 -1
  201. package/src/services/outgoing-mail/drain.ts +2 -1
  202. package/src/services/outgoing-mail/enqueue.ts +2 -1
  203. package/src/services/outgoing-mail/messages.ts +2 -1
  204. package/src/services/outgoing-mail/retention.ts +1 -1
  205. package/src/services/outgoing-mail/send.ts +1 -1
  206. package/src/services/outgoing-mail/store.ts +2 -1
  207. package/src/services/panel-context-consent.ts +32 -28
  208. package/src/services/providers/local/auth.ts +1 -1
  209. package/src/services/providers/local/users.ts +1 -1
  210. package/src/services/pwa-devices.ts +2 -1
  211. package/src/services/rail-preferences.ts +16 -12
  212. package/src/services/rail-shortcuts.ts +12 -10
  213. package/src/services/request-cache-redis.ts +3 -3
  214. package/src/services/service-account-credentials.ts +2 -1
  215. package/src/services/service-accounts.ts +2 -1
  216. package/src/services/session/index.ts +2 -1
  217. package/src/services/session/user.ts +2 -1
  218. package/src/services/settings/core-settings.ts +20 -0
  219. package/src/services/settings/crypto.ts +2 -2
  220. package/src/services/settings/index.ts +1 -1
  221. package/src/services/settings/store.ts +6 -1
  222. package/src/services/settings/versions.ts +2 -1
  223. package/src/services/weather/forecast.ts +2 -1
  224. package/src/services/weather/locations.ts +1 -1
  225. package/src/services/weather/migrate.ts +2 -1
  226. package/src/services/weather/short-id.ts +2 -1
  227. package/src/services/webauthn.ts +1 -1
  228. package/src/ssr/admin-navigation.ts +0 -1
  229. package/src/ssr/compact-shell.browser-harness.ts +2 -0
  230. package/src/ssr/platform-messages.ts +0 -2
  231. package/src/start.ts +35 -14
  232. package/src/workflows/ai/store.ts +2 -1
  233. package/src/workflows/store/budget.ts +2 -1
  234. package/src/workflows/store/definitions.ts +2 -1
  235. package/src/workflows/store/events.ts +2 -1
  236. package/src/workflows/store/observability.ts +2 -1
  237. package/src/workflows/store/retention.ts +2 -1
  238. package/src/workflows/store/runs.ts +2 -1
  239. package/src/workflows/store/transaction.ts +2 -1
  240. package/src/workflows/store/worker.ts +2 -1
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import type { EphemeralConfig, MutexConfig } from "@k2b/sync";
3
3
  import type { AppAppearanceColor } from "../contracts/app";
4
+ import { appStatusIssues } from "../contracts/app-status";
4
5
  import { calendarSourceIssues } from "../contracts/calendar-source";
5
6
  import type { CapabilityManifest, CapabilityPresentationCatalog } from "../contracts/capabilities";
6
7
  import { discussionsIssues } from "../contracts/discussions";
@@ -16,6 +17,7 @@ import {
16
17
  WIDGET_TITLE_MAX_LENGTH,
17
18
  type WidgetSettingsDeclaration,
18
19
  } from "../contracts/widgets";
20
+ import type { PlatformBootstrapMetadata } from "../runtime/connections";
19
21
  import { resolveAppPresentations } from "../shared/app-presentation";
20
22
  import { compileCapabilityPresentation, parseCapabilityManifest } from "./capabilities";
21
23
  import { lazySync } from "./process-sync";
@@ -63,6 +65,8 @@ export type AppInstanceRecord = {
63
65
  hostname: string;
64
66
  startedAt: number;
65
67
  entries: Record<string, string>;
68
+ /** Missing on older instances means unknown, never env or successful bootstrap. */
69
+ bootstrap?: PlatformBootstrapMetadata;
66
70
  };
67
71
 
68
72
  // The id and owner are a cross-version contract: all replicas must use the same lock.
@@ -103,6 +107,8 @@ export type AppRegistryDetail = AppRegistryEntry & {
103
107
  updatedAt: number;
104
108
  expiresAt: number;
105
109
  version: string;
110
+ /** Per-process startup metadata for admin readers. null means an older/unknown producer. */
111
+ instances?: { instanceId: string; hostname: string; startedAt: number; bootstrap: PlatformBootstrapMetadata | null }[];
106
112
  };
107
113
 
108
114
  export type AppRegistryIssue = {
@@ -238,6 +244,7 @@ const reportLeftOutEntries = (sent: unknown, manifest: CapabilityManifest): void
238
244
  });
239
245
  if (groups.calendarSource !== undefined && !manifest.calendarSource) leftOut.push("calendarSource");
240
246
  if (groups.fileProvider !== undefined && !manifest.fileProvider) leftOut.push("fileProvider");
247
+ if (groups.appStatus !== undefined && !manifest.appStatus) leftOut.push("appStatus");
241
248
  if (groups.discussions !== undefined && !manifest.discussions) leftOut.push("discussions");
242
249
  if (leftOut.length === 0) return;
243
250
  console.warn(
@@ -348,6 +355,32 @@ const withValidDiscussions = (manifest: CapabilityManifest): CapabilityManifest
348
355
  return rest;
349
356
  };
350
357
 
358
+ const checkedAppStatus = new Map<string, { manifestHash: string; valid: boolean }>();
359
+ const withValidAppStatus = (manifest: CapabilityManifest): CapabilityManifest => {
360
+ if (!manifest.appStatus) return manifest;
361
+ const checked = checkedAppStatus.get(manifest.appId);
362
+ let valid = checked?.manifestHash === manifest.manifestHash ? checked.valid : undefined;
363
+ if (valid === undefined) {
364
+ const issues = appStatusIssues(manifest);
365
+ valid = issues.length === 0;
366
+ checkedAppStatus.set(manifest.appId, { manifestHash: manifest.manifestHash, valid });
367
+ if (!valid)
368
+ console.error(
369
+ JSON.stringify({
370
+ level: "error",
371
+ source: "capability-registry",
372
+ message: "Ignored an incompatible appStatus provider",
373
+ appId: manifest.appId,
374
+ manifestHash: manifest.manifestHash,
375
+ issues,
376
+ }),
377
+ );
378
+ }
379
+ if (valid) return manifest;
380
+ const { appStatus: _ignored, ...rest } = manifest;
381
+ return rest;
382
+ };
383
+
351
384
  /** Ignores record fields from a newer release; the endpoint always comes from the live app registry. */
352
385
  export const resolveLiveCapabilityRegistryEntry = (
353
386
  key: string,
@@ -362,7 +395,7 @@ export const resolveLiveCapabilityRegistryEntry = (
362
395
  if (!endpoint) return null;
363
396
  try {
364
397
  const read = parseCapabilityManifest(record.manifest, app.id);
365
- const supported = withValidDiscussions(withValidCalendarSource(withValidFileProvider(read)));
398
+ const supported = withValidAppStatus(withValidDiscussions(withValidCalendarSource(withValidFileProvider(read))));
366
399
  const manifest = supported.resourcePages
367
400
  ? {
368
401
  ...supported,
@@ -411,8 +444,21 @@ export const getCapability = async (appId: string): Promise<CapabilityRegistryEn
411
444
  * Same as `listApps` but returns registry metadata for admin observability.
412
445
  */
413
446
  export const listAppsDetailed = async (): Promise<AppRegistryDetail[]> => {
414
- const snapshot = await readAppRegistrySnapshot();
415
- return snapshot.apps;
447
+ const [snapshot, instances] = await Promise.all([
448
+ readAppRegistrySnapshot(),
449
+ appInstanceRegistry().snapshot({ prefix: APP_INSTANCE_PREFIX }),
450
+ ]);
451
+ return snapshot.apps.map((app) => ({
452
+ ...app,
453
+ instances: instances.entries
454
+ .filter(({ value }) => value.appId === app.id)
455
+ .map(({ value }) => ({
456
+ instanceId: value.instanceId,
457
+ hostname: value.hostname,
458
+ startedAt: value.startedAt,
459
+ bootstrap: value.bootstrap ?? null,
460
+ })),
461
+ }));
416
462
  };
417
463
 
418
464
  /**
@@ -1,4 +1,5 @@
1
1
  import { env } from "../config/env";
2
+ import { connections } from "../runtime/connections";
2
3
  import { getProcessApplicationId } from "./process-identity";
3
4
  import { getProcessNats } from "./process-sync";
4
5
  import {
@@ -50,7 +51,7 @@ export const createAppWorkloadCredential = (options: {
50
51
  const request =
51
52
  options.request ??
52
53
  (async (input: WorkloadRequest) => {
53
- const response = await getProcessNats().request(workloadSubject(env.SYNC_NAMESPACE), JSON.stringify(input), {
54
+ const response = await getProcessNats().request(workloadSubject(connections().nats.namespace), JSON.stringify(input), {
54
55
  timeout: WORKLOAD_REQUEST_TIMEOUT_MS,
55
56
  });
56
57
  return response.json<unknown>();
@@ -1,8 +1,9 @@
1
- import { type SQL, sql, type TransactionSQL } from "bun";
1
+ import type { SQL, TransactionSQL } from "bun";
2
2
  import { z } from "zod";
3
3
  import { appAudienceRoles } from "../_internal/app-roles";
4
4
  import { APP_REGISTRY_TTL_MS } from "../_internal/registry";
5
5
  import type { AppRegistryEntry } from "../contracts/registry";
6
+ import { sql } from "../runtime/clients";
6
7
  import { logger } from "../services/logging";
7
8
  import { toPgTextArray } from "../services/postgres";
8
9
  import { type AppSkillDefinition, parseStoredAppSkill } from "./app-skills";
@@ -1,5 +1,6 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
2
  import { z } from "zod";
3
+ import { sql } from "../runtime/clients";
3
4
  import { logger } from "../services/logging";
4
5
  import { AI_SKILL_CATALOG_MAX_CHARS, aiSkillCatalogChars } from "./skill-catalog";
5
6
  import { type AiSkillContent, canonicalHash, contentHash, validateSkillContent } from "./skill-content";
@@ -1,5 +1,6 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
2
  import type { AccessSubject } from "../contracts/shared";
3
+ import { sql } from "../runtime/clients";
3
4
  import type { RequestActor } from "../server/middleware/auth";
4
5
  import { type AuditActor, type AuditRecordParams, audit } from "../services/audit";
5
6
  import { toPgTextArray } from "../services/postgres";
@@ -1,7 +1,8 @@
1
1
  import type { Message } from "@k2b/nessi";
2
- import { type SQL, sql } from "bun";
2
+ import type { SQL } from "bun";
3
3
  import { getCapability } from "../_internal/registry";
4
4
  import { type CapabilityActionPolicy, capabilityActionPolicy } from "../contracts/capabilities";
5
+ import { sql } from "../runtime/clients";
5
6
  import { logger } from "../services/logging";
6
7
  import {
7
8
  createMandate,
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { decryptValue, encryptValue } from "../services/settings/crypto";
3
4
 
4
5
  type SqlClient = typeof sql;
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { lazySync } from "../_internal/process-sync";
2
+ import { sql } from "../runtime/clients";
3
3
  import { get as getUser } from "../services/accounts/users";
4
4
  import { logger, trace } from "../services/logging";
5
5
  import { superviseRuntimeTask } from "../services/runtime-lifecycle";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
2
+ import { sql } from "../runtime/clients";
3
3
  import { inspectAiAudio } from "./audio-format";
4
4
  import { createUniqueAiFileInTransaction } from "./files-store";
5
5
  import { AiSaveConversationDraftInputSchema } from "./http";
@@ -1,4 +1,5 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
+ import { sql } from "../runtime/clients";
2
3
  import { stripImageMetadata } from "../services/image-metadata";
3
4
  import { AiFileVersionConflict, AiFileWriteError, aiFileContentVersion } from "./file-content-version";
4
5
  import { isAiImage } from "./file-media-type";
@@ -1,4 +1,5 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
+ import { sql } from "../runtime/clients";
2
3
  import type { AccessSubject } from "../server/services/access";
3
4
  import { aiBudgetUnits, aiCostDecimal, aiCostUnits, aiDecimalUnits, hasBillableAiPricing } from "../shared/ai-costs";
4
5
  import { AiQuotaError, aiQuotas, quotaWindow } from "./quotas";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
2
+ import { sql } from "../runtime/clients";
3
3
  import { toPgTextArray } from "../services/postgres";
4
4
  import type { AiMap, AiMapCommand } from "./map-contracts";
5
5
  import { AiMapSchema, aiMapToolResult } from "./map-contracts";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import type { CapabilityCaller } from "../capabilities/server";
2
+ import { sql } from "../runtime/clients";
3
3
  import { resolveAiCapabilityActor } from "./capability-execution";
4
4
  import { AiMapToolInputSchema, AiMapToolResultSchema, aiMapToolResult } from "./map-contracts";
5
5
  import { executeStoredAiMap } from "./map-execution";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { type CloudResourceRef, CloudResourceRefSchema } from "../contracts/capabilities";
2
+ import { sql } from "../runtime/clients";
3
3
  import { logger } from "../services/logging";
4
4
  import { withAiShortId } from "./short-id";
5
5
 
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import type { CloudResourceRef } from "../contracts/capabilities";
2
+ import { sql } from "../runtime/clients";
3
3
  import type { AiMemoryKind } from "./memories";
4
4
  import { withAiShortId } from "./short-id";
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
- import { sql } from "bun";
3
2
  import { z } from "zod";
4
3
  import { CloudResourceRefSchema } from "../contracts/capabilities";
4
+ import { sql } from "../runtime/clients";
5
5
  import { coreSettings } from "../services";
6
6
  import { logger } from "../services/logging";
7
7
  import { normalizeLocale } from "../shared/locale";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { type CloudResourceRef, CloudResourceRefSchema } from "../contracts/capabilities";
2
+ import { sql } from "../runtime/clients";
3
3
  import { AI_MEMORY_LEARNING_DEFAULT_ENABLED } from "./prefs";
4
4
 
5
5
  const MAX_RECEIPT_RESOURCES = 20;
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { logger } from "../services/logging";
3
4
  import { aiChatAccessSubject, isAssistantChatTurn } from "./assistant-models";
4
5
  import { editAiDraftText } from "./draft-content";
package/src/ai/migrate.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { CapabilityActionReviewDataSchema } from "../contracts/capabilities";
2
+ import { sql } from "../runtime/clients";
3
3
  import { APPROVAL_MAX_WAIT_MS, approvalAssurance, approvalScopeFromToolName, approvals, migrateApprovals } from "../services/approvals";
4
4
  import { migrateChatTaskReasons } from "./chat-task-reasons-migrate";
5
5
  import { migrateAiMaps } from "./map-store";
@@ -1,4 +1,4 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
2
 
3
3
  export const migrateAiModelAccess = async (): Promise<void> => {
4
4
  await sql.begin(async (tx) => {
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import type { AccessEntry, Principal, ServiceAccountKind } from "../contracts/shared";
2
+ import { sql } from "../runtime/clients";
3
3
  import { type AccessSubject, buildAccessPrincipalCondition, createAccess } from "../server/services/access";
4
4
  import { toPgTextArray } from "../services/postgres";
5
5
  import type { AiModelAccessDraft } from "../shared/ai-models";
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
2
+ import { sql } from "../runtime/clients";
3
3
  import { ConfigConflict } from "../services/config-conflict";
4
4
  import { stripImageDataUrlMetadata } from "../services/image-metadata";
5
5
  import {
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
2
+ import { sql } from "../runtime/clients";
3
3
  import { recursiveGroupIdsSubquery } from "../services/accounts/group-sql";
4
4
  import { toPgTextArray } from "../services/postgres";
5
5
  import { type AiDeleteModelInput, type AiModel, type AiModelSlot, type AiProvider, aiReplacementProblem } from "../shared/ai-models";
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { toPgTextArray } from "../services/postgres";
3
4
  import type { AiModelUsage } from "../shared/ai-models";
4
5
  import { aiModelCatalog } from "./model-catalog";
@@ -1,7 +1,7 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
3
2
  import { APPROVALS_PAGE_SIZE } from "../contracts/approvals";
4
3
  import { CapabilityActionReviewPresentationSchema, CapabilitySemanticLinkSchema } from "../contracts/capabilities";
4
+ import { sql } from "../runtime/clients";
5
5
  import { openRequests } from "../services/approvals/content";
6
6
  import { logger } from "../services/logging";
7
7
  import { reviewShortText, toolActionCard, toolBlockReview } from "./chat/action-card";
@@ -1,4 +1,5 @@
1
- import { redis } from "bun";
1
+ import { redis } from "../runtime/clients";
2
+
2
3
  import type { AccessSubject } from "../server/services/access";
3
4
  import { logger } from "../services/logging";
4
5
 
package/src/ai/prefs.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import type { RequestActor } from "../server";
3
4
  import { type AiModelRequest, AiModelRequestSchema } from "../shared/ai-models";
4
5
 
@@ -1,6 +1,7 @@
1
- import { type SQL, type SQLQuery, sql } from "bun";
1
+ import type { SQL, SQLQuery } from "bun";
2
2
  import type { CloudResourceRef } from "../contracts/capabilities";
3
3
  import { AuthenticatedPrincipalSchema, type ServiceAccountKind } from "../contracts/shared";
4
+ import { sql } from "../runtime/clients";
4
5
  import {
5
6
  buildAccessPrincipalCondition,
6
7
  createAccess,
@@ -1,5 +1,5 @@
1
- import { sql } from "bun";
2
1
  import { z } from "zod";
2
+ import { sql } from "../runtime/clients";
3
3
  import type { AiProvider } from "../shared/ai-models";
4
4
  import { getAiCredential } from "./credentials";
5
5
  import { getAiRequestHeaders } from "./request-headers";
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { recursiveGroupIdsSubquery } from "../services/accounts/group-sql";
3
4
  import { toPgTextArray } from "../services/postgres";
4
5
  import { hasBillableAiPricing } from "../shared/ai-costs";
@@ -1,6 +1,7 @@
1
1
  import { setTimeout as delay } from "node:timers/promises";
2
2
  import { estimateTokens, type Provider } from "@k2b/nessi";
3
- import { sql } from "bun";
3
+ import { sql } from "../runtime/clients";
4
+
4
5
  import type { AccessSubject } from "../server/services/access";
5
6
  import { logger } from "../services/logging";
6
7
  import type { AiResolvedModelRef } from "../shared/ai-models";
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { resolveDisplayNames } from "../server/services/access";
3
4
  import { recursiveGroupIdsSubquery } from "../services/accounts/group-sql";
4
5
  import { toPgTextArray } from "../services/postgres";
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  export async function migrateAiQuotas() {
3
4
  await sql.begin(async (db) => {
4
5
  await db`CREATE TABLE IF NOT EXISTS ai.cost_config (
package/src/ai/quotas.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
+ import { sql } from "../runtime/clients";
2
3
  import { type AccessSubject, buildAccessPrincipalCondition } from "../server/services/access";
3
4
  import { hasBillableAiPricing } from "../shared/ai-costs";
4
5
  import { type AiQuotaConfig, AiQuotaConfigSchema, type AiQuotaIdentity, type AiQuotaSnapshot } from "../shared/ai-quotas";
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { decryptValue, encryptValue } from "../services/settings/crypto";
3
4
  import { AiRequestHeadersSchema, patchAiRequestHeaders } from "../shared/ai-request-options";
4
5
 
@@ -1,7 +1,8 @@
1
- import { type SQL, sql } from "bun";
1
+ import type { SQL } from "bun";
2
2
  import { capabilityRequestHash } from "../capabilities/claims";
3
3
  import { parseStoredReviewContent } from "../capabilities/review-storage";
4
4
  import { CAPABILITY_MAX_REVIEW_CONTENT_BYTES, type CapabilityActionReviewPresentation } from "../contracts/capabilities";
5
+ import { sql } from "../runtime/clients";
5
6
  import type { AiStoredActionReview } from "./action-review";
6
7
 
7
8
  export type ReviewContentLocation = { conversationId: string; turnId: string; callId: string };
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { toPgTextArray } from "../services/postgres";
3
4
 
4
5
  const BM25_INDEX = "ai.skills_search_bm25_idx";
package/src/ai/skills.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { type SQL, type SQLQuery, sql } from "bun";
1
+ import type { SQL, SQLQuery } from "bun";
2
2
  import { listApps } from "../_internal/registry";
3
3
  import { AuthenticatedPrincipalSchema, type ServiceAccountKind } from "../contracts/shared";
4
+ import { sql } from "../runtime/clients";
4
5
  import type { RequestAuthority } from "../server/middleware/auth";
5
6
  import {
6
7
  buildAccessPrincipalCondition,
package/src/ai/store.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import type { DoneReason, InboundEvent, LoopAggregate, Message, SessionStore, StoreEntry } from "@k2b/nessi";
2
2
  import type { Usage } from "@k2b/nessi/ai";
3
- import { type SQL, sql } from "bun";
3
+ import type { SQL } from "bun";
4
4
  import { capabilityRequestHash } from "../capabilities/claims";
5
5
  import { restoreReviewSnapshot, serializeReviewSnapshot } from "../capabilities/review-storage";
6
6
  import { type CapabilityActionReviewPresentation, CapabilityActionReviewPresentationSchema } from "../contracts/capabilities";
7
+ import { sql } from "../runtime/clients";
7
8
  import { logger } from "../services/logging";
8
9
  import { toPgTextArray } from "../services/postgres";
9
10
  import { AI_TOOL_CHECK_STALE_MS, type AiActionCheck, type AiStoredActionReview } from "./action-review";
@@ -1,5 +1,6 @@
1
1
  import type { Usage } from "@k2b/nessi";
2
- import { sql } from "bun";
2
+ import { sql } from "../runtime/clients";
3
+
3
4
  import { logger } from "../services/logging";
4
5
 
5
6
  export type AiUsageAttribution = {
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import type { ApprovalDecisionChannel } from "../services/approvals";
3
4
 
4
5
  /**
@@ -1,5 +1,5 @@
1
1
  import type { LoopAggregate, OutboundEvent } from "@k2b/nessi";
2
- import { sql } from "bun";
2
+ import { sql } from "../runtime/clients";
3
3
 
4
4
  export type TimingInterval = { start: number; end: number };
5
5
  const union = (intervals: TimingInterval[]): TimingInterval[] => {
package/src/ai/usage.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { sql } from "bun";
1
+ import { sql } from "../runtime/clients";
2
+
2
3
  import { type AiUsageQuery, AiUsageQuerySchema } from "../shared/ai-usage";
3
4
  import { aiQuotas } from "./quotas";
4
5
 
@@ -1,5 +1,6 @@
1
1
  import { domainToUnicode } from "node:url";
2
- import { sql } from "bun";
2
+ import { sql } from "../runtime/clients";
3
+
3
4
  import { toPgTextArray } from "../services/postgres";
4
5
 
5
6
  /** The longest address web_extract and fetch_file accept; a longer one can never be read, so it is never recorded. */
@@ -1,9 +1,9 @@
1
- import { sql } from "bun";
2
1
  import { Hono, type MiddlewareHandler } from "hono";
3
2
  import { AiBackgroundCostError, backgroundCostState, releaseBackgroundCostStop } from "../ai/inference-calls";
4
3
  import { quotaAdminConfig, quotaReport } from "../ai/quota-report";
5
4
  import { AiQuotaError, aiQuotas } from "../ai/quotas";
6
5
  import { readAiSettingsState } from "../ai/settings";
6
+ import { sql } from "../runtime/clients";
7
7
  import { type AuthContext, auth, v } from "../server";
8
8
  import { hasBillableAiPricing } from "../shared/ai-costs";
9
9
  import {
@@ -1,4 +1,3 @@
1
- import { sql } from "bun";
2
1
  import { Hono, type MiddlewareHandler } from "hono";
3
2
  import { z } from "zod";
4
3
  import { setAiCredential } from "../ai/credentials";
@@ -15,6 +14,7 @@ import {
15
14
  testAiProvider,
16
15
  } from "../ai/provider-connections";
17
16
  import { getAiRequestHeaders, setAiRequestHeaders } from "../ai/request-headers";
17
+ import { sql } from "../runtime/clients";
18
18
  import { type AuthContext, auth, v } from "../server";
19
19
  import { ConfigConflict } from "../services/config-conflict";
20
20
  import { ImageMetadataError } from "../services/image-metadata";
@@ -0,0 +1,62 @@
1
+ import { Hono, type MiddlewareHandler } from "hono";
2
+ import { describeRoute } from "hono-openapi";
3
+ import { z } from "zod";
4
+ import { type AuthContext, auth, respond, v } from "../server";
5
+ import { noTransform } from "../server/middleware/no-transform";
6
+ import { appDomains } from "../services/app-domains";
7
+ import { logger } from "../services/logging";
8
+
9
+ const appParams = z.object({ appId: z.string().regex(/^[a-z][a-z0-9-]{0,79}$/) });
10
+ const domainParams = appParams.extend({ host: z.string().max(1024) });
11
+ const host = z.string().max(1024);
12
+ const addInput = z.object({ host, redirectTo: host.optional() }).strict();
13
+ const setInput = z.object({ redirectTo: host.nullable() }).strict();
14
+ const describe = (summary: string) => describeRoute({ tags: ["App domains"], summary });
15
+
16
+ export const createAdminAppDomainRoutes = (
17
+ authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin"),
18
+ service: typeof appDomains = appDomains,
19
+ ) =>
20
+ new Hono<AuthContext>()
21
+ .use("/:appId/domains/*", noTransform())
22
+ .use("/:appId/domains/*", authenticate)
23
+ .use("/:appId/domains/*", async (c, next) => {
24
+ c.header("Cache-Control", "no-store");
25
+ if (c.req.method !== "GET") return auth.requireOAuthScope("admin")(c, next);
26
+ return next();
27
+ })
28
+ .get("/:appId/domains", describe("List assigned domains and their last check"), v("param", appParams), (c) =>
29
+ respond(c, () => service.list({ actor: c.get("user"), ...c.req.valid("param") })),
30
+ )
31
+ .post(
32
+ "/:appId/domains",
33
+ describe("Assign a domain and start its first check in the background"),
34
+ v("param", appParams),
35
+ v("json", addInput),
36
+ async (c) => {
37
+ const input = { actor: c.get("user"), ...c.req.valid("param"), ...c.req.valid("json") };
38
+ const result = await service.add(input);
39
+ if (result.ok)
40
+ void service.check({ ...input, host: result.data.host }).catch((error: unknown) => {
41
+ logger("core:app-domains").error("Initial domain check failed; retry through the domain check endpoint", {
42
+ host: result.data.host,
43
+ error: error instanceof Error ? error.message : String(error),
44
+ });
45
+ });
46
+ return respond(c, result, 201);
47
+ },
48
+ )
49
+ .patch("/:appId/domains/:host", describe("Change or clear the domain redirect"), v("param", domainParams), v("json", setInput), (c) =>
50
+ respond(c, () => service.set({ actor: c.get("user"), ...c.req.valid("param"), ...c.req.valid("json") })),
51
+ )
52
+ .delete("/:appId/domains/:host", describe("Remove an unreferenced domain assignment"), v("param", domainParams), (c) =>
53
+ respond(c, () => service.remove({ actor: c.get("user"), ...c.req.valid("param") })),
54
+ )
55
+ .post(
56
+ "/:appId/domains/:host/check",
57
+ describe("Check DNS, certificate and gateway reachability, at most once per 60 seconds"),
58
+ v("param", domainParams),
59
+ (c) => respond(c, () => service.check({ actor: c.get("user"), ...c.req.valid("param") })),
60
+ );
61
+
62
+ export default createAdminAppDomainRoutes();