@k2b/cloud 0.6.0-rc.0 → 0.7.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 (315) hide show
  1. package/README.md +2 -2
  2. package/package.json +30 -17
  3. package/scripts/browser-performance.ts +14 -0
  4. package/scripts/build-pdf-renderer.ts +39 -0
  5. package/scripts/build.ts +15 -35
  6. package/scripts/preload.ts +9 -12
  7. package/scripts/sync-recovery-smoke.ts +2 -1
  8. package/src/_internal/app-presentation.ts +14 -2
  9. package/src/_internal/capabilities.ts +108 -7
  10. package/src/_internal/capability-streams.ts +81 -0
  11. package/src/_internal/define-app.ts +62 -36
  12. package/src/_internal/heartbeat.ts +4 -0
  13. package/src/_internal/help-catalog.ts +27 -172
  14. package/src/_internal/help.ts +13 -76
  15. package/src/_internal/page-responses.ts +2 -0
  16. package/src/_internal/postgres-application-name.ts +30 -0
  17. package/src/_internal/process-identity.ts +15 -0
  18. package/src/_internal/process-sync.ts +1 -1
  19. package/src/_internal/registry-validation.ts +29 -25
  20. package/src/_internal/registry.ts +2 -53
  21. package/src/_internal/runtime-context.ts +5 -16
  22. package/src/_internal/server-timing.ts +13 -0
  23. package/src/_internal/status-preserving-ssr.ts +18 -6
  24. package/src/_internal/web-vitals-asset.ts +10 -0
  25. package/src/access/PermissionEditor.tsx +14 -153
  26. package/src/access/PrincipalPicker.tsx +104 -0
  27. package/src/access/ui.ts +2 -0
  28. package/src/ai/admin.ts +14 -13
  29. package/src/ai/assistant-models.ts +12 -1
  30. package/src/ai/audio-format.ts +34 -0
  31. package/src/ai/audio-tool.ts +90 -0
  32. package/src/ai/browser-code-contracts.ts +79 -0
  33. package/src/ai/browser.ts +2 -0
  34. package/src/ai/capabilities.ts +55 -36
  35. package/src/ai/capability-execution.ts +47 -12
  36. package/src/ai/chat/blocks.tsx +157 -95
  37. package/src/ai/chat/capability-result.ts +15 -0
  38. package/src/ai/chat/capability-table.tsx +2 -10
  39. package/src/ai/chat/composer-adapter.ts +48 -3
  40. package/src/ai/chat/message-actions.tsx +8 -7
  41. package/src/ai/chat/message-utils.ts +12 -1
  42. package/src/ai/chat/messages.ts +2 -0
  43. package/src/ai/chat/presentation.tsx +19 -48
  44. package/src/ai/chat/primitives.tsx +43 -1
  45. package/src/ai/chat/tool-disclosure.tsx +17 -2
  46. package/src/ai/chat/tool-groups.ts +52 -0
  47. package/src/ai/chat-quotas.ts +25 -0
  48. package/src/ai/chat-task-contracts.ts +9 -1
  49. package/src/ai/chat-tasks.ts +276 -43
  50. package/src/ai/client/controller.ts +110 -33
  51. package/src/ai/client/file-source.ts +12 -6
  52. package/src/ai/client/live-connection.ts +1 -0
  53. package/src/ai/client/projection.ts +22 -11
  54. package/src/ai/client/transport.ts +4 -0
  55. package/src/ai/code-capability-routes.ts +110 -0
  56. package/src/ai/code-capability-transport.ts +38 -0
  57. package/src/ai/code-mode-skill.ts +114 -0
  58. package/src/ai/code-runtime-tools.ts +166 -0
  59. package/src/ai/code-source-contracts.ts +202 -0
  60. package/src/ai/code-source-tools.ts +145 -0
  61. package/src/ai/compaction.ts +27 -6
  62. package/src/ai/data-analysis-skill.ts +10 -0
  63. package/src/ai/default-tools.ts +25 -2
  64. package/src/ai/dictation-runtime.ts +171 -0
  65. package/src/ai/dictations.ts +188 -0
  66. package/src/ai/draft-content.ts +34 -0
  67. package/src/ai/enrich.ts +6 -0
  68. package/src/ai/executor.ts +191 -31
  69. package/src/ai/file-content-version.ts +22 -0
  70. package/src/ai/file-context.ts +2 -1
  71. package/src/ai/file-media-type.ts +12 -0
  72. package/src/ai/file-reference-contracts.ts +15 -0
  73. package/src/ai/file-tools.ts +2 -1
  74. package/src/ai/files-store.ts +174 -71
  75. package/src/ai/fixtures/vision-encrypted.pdf +0 -0
  76. package/src/ai/grids-skill.ts +40 -10
  77. package/src/ai/http.ts +15 -8
  78. package/src/ai/index.ts +26 -2
  79. package/src/ai/inference-calls.ts +198 -0
  80. package/src/ai/live-events.ts +5 -1
  81. package/src/ai/memory-learning.ts +60 -28
  82. package/src/ai/message-queue.ts +167 -0
  83. package/src/ai/migrate.ts +187 -11
  84. package/src/ai/model-pricing.ts +43 -0
  85. package/src/ai/pdf-render-worker.ts +74 -0
  86. package/src/ai/pdf-render.fixture.ts +28 -0
  87. package/src/ai/pdf-render.ts +77 -0
  88. package/src/ai/projects-routes.ts +3 -3
  89. package/src/ai/projects.ts +20 -6
  90. package/src/ai/protocol.ts +3 -0
  91. package/src/ai/provider.ts +1 -8
  92. package/src/ai/quota-provider.ts +191 -0
  93. package/src/ai/quota-report.ts +152 -0
  94. package/src/ai/quotas-migrate.ts +45 -0
  95. package/src/ai/quotas.ts +168 -0
  96. package/src/ai/routes.ts +215 -11
  97. package/src/ai/run-timeout.ts +10 -0
  98. package/src/ai/runtime-tools.ts +3 -0
  99. package/src/ai/runtime.ts +55 -16
  100. package/src/ai/settings.ts +36 -8
  101. package/src/ai/short-id.ts +2 -2
  102. package/src/ai/skill-catalog.ts +0 -10
  103. package/src/ai/skill-search.ts +47 -0
  104. package/src/ai/skill-seeds.ts +105 -20
  105. package/src/ai/skill-tool.ts +16 -5
  106. package/src/ai/skills-routes.ts +32 -4
  107. package/src/ai/skills.ts +324 -155
  108. package/src/ai/store.ts +373 -191
  109. package/src/ai/stream.ts +5 -0
  110. package/src/ai/structured-runs.ts +4 -3
  111. package/src/ai/structured.ts +8 -1
  112. package/src/ai/system-prompt.ts +12 -2
  113. package/src/ai/timeline.ts +2 -5
  114. package/src/ai/todo-contracts.ts +20 -0
  115. package/src/ai/todo-tool.ts +16 -0
  116. package/src/ai/tool-audit.ts +30 -40
  117. package/src/ai/tools.ts +6 -0
  118. package/src/ai/transcription.ts +202 -0
  119. package/src/ai/turn-timing.ts +114 -0
  120. package/src/ai/types.ts +58 -10
  121. package/src/ai/ui.tsx +2 -0
  122. package/src/ai/usage.ts +118 -118
  123. package/src/ai/vision-tool.ts +39 -10
  124. package/src/api/admin-ai-projects.ts +2 -2
  125. package/src/api/admin-ai-quotas.ts +74 -0
  126. package/src/api/admin-ai-skills.ts +48 -5
  127. package/src/api/admin-ai-usage.ts +2 -2
  128. package/src/api/admin-core-settings.ts +25 -0
  129. package/src/api/admin-identity.ts +91 -51
  130. package/src/api/admin-rail.ts +58 -0
  131. package/src/api/announcements.ts +14 -0
  132. package/src/api/capabilities.ts +292 -43
  133. package/src/api/capability-streams.ts +262 -0
  134. package/src/api/help.ts +8 -44
  135. package/src/api/index.ts +6 -0
  136. package/src/api/mcp.ts +44 -26
  137. package/src/api/me-web-vitals.ts +35 -0
  138. package/src/api/me.ts +2 -0
  139. package/src/api/search/schemas.ts +26 -15
  140. package/src/api/search.bench.ts +1 -1
  141. package/src/api/search.ts +54 -17
  142. package/src/browser/CloudResourceSearch.tsx +640 -702
  143. package/src/browser/app-approval-vault.ts +19 -7
  144. package/src/browser/command-bridge.ts +109 -0
  145. package/src/browser/command-shortcuts.ts +76 -0
  146. package/src/browser/commands.ts +66 -0
  147. package/src/browser/navigation-search.ts +48 -0
  148. package/src/browser/notifications.ts +1 -1
  149. package/src/browser/resource-picker.tsx +5 -18
  150. package/src/browser/resource-search-dialog.ts +9 -0
  151. package/src/browser/resource-search-input.ts +46 -0
  152. package/src/browser/resource-search-messages.ts +76 -40
  153. package/src/browser/resource-search.ts +7 -0
  154. package/src/browser/search-bridge.ts +102 -0
  155. package/src/browser/search-commands.ts +59 -0
  156. package/src/browser/search.ts +13 -0
  157. package/src/browser/spotlight-position.ts +208 -0
  158. package/src/browser/testing.ts +5 -0
  159. package/src/browser/web-vitals.ts +25 -0
  160. package/src/capabilities/claims.ts +179 -0
  161. package/src/capabilities/client.ts +34 -0
  162. package/src/capabilities/command-link.ts +16 -0
  163. package/src/capabilities/executions.ts +298 -0
  164. package/src/capabilities/migrate.ts +81 -0
  165. package/src/capabilities/server.ts +114 -34
  166. package/src/capabilities/stream-body.ts +16 -0
  167. package/src/capabilities/streams.ts +28 -0
  168. package/src/cli/admin/ai-quotas.ts +161 -0
  169. package/src/cli/admin/ai-skills.ts +64 -0
  170. package/src/cli/admin/ai-usage.ts +53 -30
  171. package/src/cli/admin/app-credentials.ts +79 -0
  172. package/src/cli/admin/data.ts +17 -0
  173. package/src/cli/admin/index.ts +9 -0
  174. package/src/cli/capabilities.ts +29 -0
  175. package/src/cli/capability-streams.ts +25 -0
  176. package/src/cli/index.ts +3 -2
  177. package/src/config/env.ts +11 -8
  178. package/src/contracts/app.ts +23 -3
  179. package/src/contracts/capabilities.ts +68 -5
  180. package/src/contracts/capability-streams.ts +46 -0
  181. package/src/contracts/commands.ts +49 -0
  182. package/src/contracts/index.ts +6 -1
  183. package/src/contracts/rail-admin.ts +38 -0
  184. package/src/contracts/rail-preferences.ts +16 -0
  185. package/src/contracts/registry.ts +3 -27
  186. package/src/contracts/settings-types.ts +1 -1
  187. package/src/contracts/shared.ts +9 -5
  188. package/src/contracts/web-vitals.ts +27 -0
  189. package/src/desktop/solid.tsx +3 -3
  190. package/src/index.ts +0 -4
  191. package/src/server/index.ts +3 -0
  192. package/src/server/middleware/auth.ts +3 -2
  193. package/src/server/middleware/route-template.ts +9 -3
  194. package/src/server/middleware/runtime.ts +2 -1
  195. package/src/server/middleware/settings.ts +17 -11
  196. package/src/server/services/access-revision.ts +10 -0
  197. package/src/server/services/access.ts +1 -1
  198. package/src/server/services/index.ts +1 -0
  199. package/src/services/account-category-policy.ts +9 -0
  200. package/src/services/account-lifecycle/audit.ts +1 -1
  201. package/src/services/accounts/app.ts +12 -2
  202. package/src/services/accounts/email-write.ts +23 -0
  203. package/src/services/accounts/groups.ts +15 -4
  204. package/src/services/accounts/identities.ts +249 -0
  205. package/src/services/accounts/identity-reconciliation.ts +87 -0
  206. package/src/services/accounts/ipa-data.ts +2 -2
  207. package/src/services/accounts/local-groups.ts +2 -2
  208. package/src/services/accounts/posix.ts +45 -29
  209. package/src/services/accounts/users.ts +2 -2
  210. package/src/services/announcements/index.ts +69 -8
  211. package/src/services/audit/index.ts +1 -1
  212. package/src/services/cache-fill.ts +35 -0
  213. package/src/services/help/index.ts +109 -0
  214. package/src/services/help/maintenance.ts +25 -0
  215. package/src/services/help/store.ts +180 -0
  216. package/src/services/help/types.ts +46 -0
  217. package/src/services/identity/invocation-actor.ts +2 -2
  218. package/src/services/identity/key-config.ts +2 -1
  219. package/src/services/identity/key-ring.ts +1 -1
  220. package/src/services/index.ts +15 -0
  221. package/src/services/ipa/users.ts +268 -221
  222. package/src/services/legal-consent.ts +1 -1
  223. package/src/services/logging/index.ts +15 -13
  224. package/src/services/logging/trace.ts +2 -1
  225. package/src/services/logging/web-vitals.ts +81 -0
  226. package/src/services/mandates/index.ts +18 -4
  227. package/src/services/mandates/policy.ts +65 -2
  228. package/src/services/notifications/batches.ts +124 -57
  229. package/src/services/notifications/browser.ts +12 -0
  230. package/src/services/notifications/index.ts +0 -2
  231. package/src/services/notifications/platform.ts +0 -4
  232. package/src/services/oauth-tokens.ts +3 -3
  233. package/src/services/pdf/gotenberg.ts +107 -84
  234. package/src/services/pdf/index.ts +3 -0
  235. package/src/services/providers/local/users.ts +25 -16
  236. package/src/services/public-http.ts +101 -0
  237. package/src/services/rail-shortcuts.ts +95 -0
  238. package/src/services/rail-snapshot.ts +53 -0
  239. package/src/services/request-cache-redis.ts +28 -0
  240. package/src/services/session/index.ts +15 -2
  241. package/src/services/session/user.ts +62 -37
  242. package/src/services/settings/core-settings.ts +21 -28
  243. package/src/services/settings/defaults.ts +5 -0
  244. package/src/services/settings/store.ts +58 -84
  245. package/src/shared/ai-costs.ts +42 -0
  246. package/src/shared/ai-platform-prompt.ts +1 -1
  247. package/src/shared/ai-quotas.ts +136 -0
  248. package/src/shared/ai-usage.ts +6 -16
  249. package/src/shared/app-presentation.ts +21 -15
  250. package/src/shared/capability-messages.ts +14 -0
  251. package/src/shared/help.ts +6 -42
  252. package/src/shared/index.ts +6 -0
  253. package/src/ssr/AdminLayout.tsx +7 -5
  254. package/src/ssr/AdminSidebar.tsx +29 -15
  255. package/src/ssr/AppLaunchpad.island.tsx +24 -137
  256. package/src/ssr/AppLaunchpadPanel.tsx +181 -0
  257. package/src/ssr/BrowserPushRegistration.island.tsx +12 -0
  258. package/src/ssr/GlobalSearchDialog.tsx +167 -20
  259. package/src/ssr/GlobalSearchTrigger.island.tsx +33 -22
  260. package/src/ssr/Layout.tsx +17 -6
  261. package/src/ssr/LayoutBreadcrumbs.island.tsx +3 -5
  262. package/src/ssr/LayoutFooter.tsx +1 -1
  263. package/src/ssr/LayoutHeader.tsx +46 -14
  264. package/src/ssr/LayoutHelp.tsx +23 -22
  265. package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
  266. package/src/ssr/LayoutRail.tsx +17 -9
  267. package/src/ssr/MobileNavigation.tsx +71 -0
  268. package/src/ssr/MobileProfileActions.tsx +49 -0
  269. package/src/ssr/ProfilePreferences.island.tsx +3 -36
  270. package/src/ssr/RailApps.island.tsx +2 -2
  271. package/src/ssr/RailEditor.tsx +17 -2
  272. package/src/ssr/WorkspaceNavigation.island.tsx +7 -0
  273. package/src/ssr/WorkspaceNavigationProvider.tsx +16 -0
  274. package/src/ssr/admin-navigation.ts +7 -1
  275. package/src/ssr/help.ts +29 -0
  276. package/src/ssr/islands/SearchBar.tsx +67 -0
  277. package/src/ssr/islands/index.ts +3 -1
  278. package/src/ssr/layout-context.ts +2 -2
  279. package/src/ssr/layout-help-search.ts +15 -0
  280. package/src/ssr/mobile-menu-history.ts +70 -0
  281. package/src/ssr/platform-messages.ts +16 -8
  282. package/src/ssr/profile-actions.ts +42 -0
  283. package/src/ssr/rail-context.ts +8 -4
  284. package/src/ssr/rail-messages.ts +2 -0
  285. package/src/ssr/rail-navigation.ts +3 -3
  286. package/src/ssr/workspace-navigation.ts +75 -0
  287. package/src/styles/effects.css +6 -6
  288. package/src/styles/global.css +54 -0
  289. package/src/styles/input.css +43 -41
  290. package/src/styles/resource-search.css +505 -0
  291. package/src/styles/utilities-feedback.css +13 -0
  292. package/src/styles/utilities-layout.css +1 -5
  293. package/src/styles/utilities-navigation.css +3 -39
  294. package/src/types/ambient.d.ts +5 -0
  295. package/src/workflows/ai/runtime.ts +5 -1
  296. package/src/workflows/ai/store.ts +2 -0
  297. package/src/workflows/ai-actions.ts +16 -13
  298. package/src/workflows/definition.ts +10 -5
  299. package/src/workflows/store/actions.ts +44 -20
  300. package/src/workflows/store/index.ts +2 -0
  301. package/src/workflows/store/runs.ts +36 -0
  302. package/src/workflows/store/worker-pool.ts +55 -0
  303. package/src/workflows/store/worker-runtime.ts +90 -0
  304. package/scripts/app-favicon.test.ts +0 -49
  305. package/src/ai/chat/blocks.render.test.tsx +0 -1108
  306. package/src/ai/kit-skill.ts +0 -22
  307. package/src/ai/usage-migrate.ts +0 -81
  308. package/src/browser/CloudResourceSearch.behavior.test.tsx +0 -74
  309. package/src/browser/notification-target.ts +0 -27
  310. package/src/contracts/notification-live.ts +0 -66
  311. package/src/services/notifications/live.ts +0 -50
  312. package/src/ssr/BrowserNotifications.island.tsx +0 -135
  313. package/src/ssr/GlobalSearchDialog.behavior.test.tsx +0 -96
  314. package/src/ssr/Layout.render.test.tsx +0 -148
  315. package/src/ssr/islands/SearchBar.island.tsx +0 -80
@@ -1,7 +1,8 @@
1
1
  import { Hono, type MiddlewareHandler } from "hono";
2
2
  import { z } from "zod";
3
- import { AiSkillLastAdminError, aiSkills } from "../ai/skills";
4
- import { PrincipalSchema } from "../contracts/shared";
3
+ import { getBuiltinAiSkillTemplates } from "../ai/skill-seeds";
4
+ import { AiSkillInputError, AiSkillLastAdminError, AiSkillRevisionConflictError, aiSkills } from "../ai/skills";
5
+ import { AuthenticatedPrincipalSchema } from "../contracts/shared";
5
6
  import { type AuthContext, auth, err, fail, ok, respond, v } from "../server";
6
7
 
7
8
  const ListSkillsSchema = z
@@ -11,12 +12,11 @@ const ListSkillsSchema = z
11
12
  perPage: z.coerce.number().int().min(1).max(500).default(100),
12
13
  })
13
14
  .strict();
14
- const SkillAccessSchema = z.object({ principal: PrincipalSchema, permission: z.enum(["read", "write", "admin"]) }).strict();
15
+ const SkillAccessSchema = z.object({ principal: AuthenticatedPrincipalSchema, permission: z.enum(["read", "write", "admin"]) }).strict();
15
16
  const SkillAccessUpdateSchema = z.object({ permission: z.enum(["read", "write", "admin"]) }).strict();
16
17
 
17
18
  const notFound = (c: Parameters<typeof respond>[0], noun: string) => respond(c, fail(err.notFound(noun)));
18
- const conflict = (c: Parameters<typeof respond>[0], error: AiSkillLastAdminError) =>
19
- respond(c, { ok: false, error: error.message, status: 409 as const });
19
+ const conflict = (c: Parameters<typeof respond>[0], error: Error) => respond(c, { ok: false, error: error.message, status: 409 as const });
20
20
 
21
21
  export const createAdminAiSkillsRoutes = (authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin")) =>
22
22
  new Hono<AuthContext>()
@@ -33,6 +33,49 @@ export const createAdminAiSkillsRoutes = (authenticate: MiddlewareHandler<AuthCo
33
33
  }),
34
34
  );
35
35
  })
36
+ .get("/templates", (c) =>
37
+ respond(c, ok({ templates: getBuiltinAiSkillTemplates().map(({ key, version, name }) => ({ templateId: key, version, name })) })),
38
+ )
39
+ .post(
40
+ "/:skillId/template",
41
+ v(
42
+ "json",
43
+ z
44
+ .object({
45
+ templateId: z.string().min(1),
46
+ templateVersion: z.number().int().positive(),
47
+ expectedRevision: z.number().int().positive(),
48
+ mode: z.enum(["associate", "reset"]),
49
+ confirmed: z.literal(true),
50
+ })
51
+ .strict(),
52
+ ),
53
+ async (c) => {
54
+ const skill = await aiSkills.admin.getByShortId(c.req.param("skillId")!);
55
+ if (!skill) return notFound(c, "Skill");
56
+ try {
57
+ return (await aiSkills.admin.applyTemplate(skill.id, c.req.valid("json")))
58
+ ? respond(c, ok({ updated: true }))
59
+ : notFound(c, "Skill");
60
+ } catch (error) {
61
+ if (error instanceof AiSkillRevisionConflictError) return conflict(c, error);
62
+ if (error instanceof AiSkillInputError) return respond(c, fail(err.badInput(error.message)));
63
+ if (
64
+ typeof error === "object" &&
65
+ error !== null &&
66
+ (("code" in error && error.code === "23505") || ("errno" in error && error.errno === "23505"))
67
+ )
68
+ return conflict(c, new Error("A Skill with this name already exists."));
69
+ throw error;
70
+ }
71
+ },
72
+ )
73
+ .get("/:skillId/projects", async c => {
74
+ const skill = await aiSkills.admin.getByShortId(c.req.param("skillId")!);
75
+ if (!skill) return notFound(c, "Skill");
76
+ const projects = await aiSkills.admin.linkedProjects(skill.id,c.get("accessSubject") ?? null);
77
+ return respond(c,ok({ projects:projects.map(project=>({id:project.shortId,name:project.name})) }));
78
+ })
36
79
  .get("/:skillId/access", async (c) => {
37
80
  const skill = await aiSkills.admin.getByShortId(c.req.param("skillId")!);
38
81
  return skill ? respond(c, ok({ access: await aiSkills.admin.listAccess(skill.id) })) : notFound(c, "Skill");
@@ -2,8 +2,8 @@ import { Hono, type MiddlewareHandler } from "hono";
2
2
  import { describeRoute } from "hono-openapi";
3
3
  import { z } from "zod";
4
4
  import { aiUsage } from "../ai/usage";
5
+ import { type AuthContext, auth, err, fail, ok, respond, v } from "../server";
5
6
  import { AiUsageQuerySchema } from "../shared/ai-usage";
6
- import { auth, type AuthContext, v, respond, ok, fail, err } from "../server";
7
7
 
8
8
  export const createAdminAiUsageRoutes = (
9
9
  authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin"),
@@ -32,7 +32,7 @@ export const createAdminAiUsageRoutes = (
32
32
  .get(
33
33
  "/runs/:kind/:id",
34
34
  describeRoute({ summary: "Read an AI run and its stored error", tags: ["Admin AI usage"] }),
35
- v("param", z.object({ kind: z.enum(["chat", "background", "tool"]), id: z.uuid() })),
35
+ v("param", z.object({ kind: z.enum(["chat", "background"]), id: z.uuid() })),
36
36
  async (c) => {
37
37
  const { kind, id } = c.req.valid("param");
38
38
  const result = await service.detail(kind, id);
@@ -31,6 +31,8 @@ import { GotenbergRenderError, testGotenberg } from "../services/pdf";
31
31
  import * as settings from "../services/settings";
32
32
  import { SETTINGS_MAP, validateSettingValue } from "../services/settings/defaults";
33
33
 
34
+ import { invalidateSettingsCacheForAdmin } from "../services/settings/store";
35
+
34
36
  const BulkSaveSchema = z.union([
35
37
  z
36
38
  .object({
@@ -58,6 +60,7 @@ const AI_PROFILES_KEY = "ai.model_profiles_json";
58
60
  const AI_ENABLED_KEY = "ai.enabled";
59
61
  const AI_DEFAULT_MODEL_KEY = "ai.default_model_id";
60
62
  const AI_BACKGROUND_MODEL_KEY = "ai.background_model_id";
63
+ const AI_AUDIO_MODEL_KEY = "ai.audio_model_id";
61
64
  const AI_VISION_MODEL_KEY = "ai.vision_model_id";
62
65
  const AI_WORKFLOW_MODEL_KEY = "ai.workflow_model_id";
63
66
  const AI_CONFIGURATION_KEYS = new Set([
@@ -65,6 +68,7 @@ const AI_CONFIGURATION_KEYS = new Set([
65
68
  AI_DEFAULT_MODEL_KEY,
66
69
  AI_BACKGROUND_MODEL_KEY,
67
70
  AI_VISION_MODEL_KEY,
71
+ AI_AUDIO_MODEL_KEY,
68
72
  AI_WORKFLOW_MODEL_KEY,
69
73
  AI_PROFILES_KEY,
70
74
  ]);
@@ -116,6 +120,7 @@ const prepareAiSettingsMutation = async (
116
120
  currentDefaultModelId,
117
121
  currentBackgroundModelId,
118
122
  currentVisionModelId,
123
+ currentAudioModelId,
119
124
  currentWorkflowModelId,
120
125
  currentProfilesJson,
121
126
  ] = await Promise.all([
@@ -123,6 +128,7 @@ const prepareAiSettingsMutation = async (
123
128
  settings.get<string>(AI_DEFAULT_MODEL_KEY),
124
129
  settings.get<string>(AI_BACKGROUND_MODEL_KEY),
125
130
  settings.get<string>(AI_VISION_MODEL_KEY),
131
+ settings.get<string>(AI_AUDIO_MODEL_KEY),
126
132
  settings.get<string>(AI_WORKFLOW_MODEL_KEY),
127
133
  settings.get<string>(AI_PROFILES_KEY),
128
134
  ]);
@@ -162,6 +168,7 @@ const prepareAiSettingsMutation = async (
162
168
  enabled: nextEnabled,
163
169
  defaultModelId: String(valueAfterMutation(AI_DEFAULT_MODEL_KEY, currentDefaultModelId ?? "", updates, resets)),
164
170
  backgroundModelId: String(valueAfterMutation(AI_BACKGROUND_MODEL_KEY, currentBackgroundModelId ?? "", updates, resets)),
171
+ audioModelId: String(valueAfterMutation(AI_AUDIO_MODEL_KEY, currentAudioModelId ?? "", updates, resets)),
165
172
  visionModelId: String(valueAfterMutation(AI_VISION_MODEL_KEY, currentVisionModelId ?? "", updates, resets)),
166
173
  workflowModelId: String(valueAfterMutation(AI_WORKFLOW_MODEL_KEY, currentWorkflowModelId ?? "", updates, resets)),
167
174
  profiles: nextParsed.profiles,
@@ -184,6 +191,24 @@ const invalidateCommittedSettings = async (keys: readonly string[]): Promise<voi
184
191
  const liveSettingKeys = async () => (await listApps()).flatMap((app) => [...(app.settingKeys ?? [])]);
185
192
 
186
193
  const app = new Hono<AuthContext>()
194
+ .get("/web-vitals", auth.requireRole("admin"), async (c) =>
195
+ c.json({ enabled: (await settings.get("observability.web_vitals.enabled")) === true }),
196
+ )
197
+ .delete(
198
+ "/cache",
199
+ auth.requireRole("admin"),
200
+ describeRoute({
201
+ tags: ["Administration"],
202
+ summary: "Invalidate registered settings cache",
203
+ ...requiresAdmin,
204
+ responses: { 200: jsonResponse(z.object({ success: z.literal(true) }), "Cache invalidated") },
205
+ }),
206
+ async (c) => {
207
+ await invalidateSettingsCacheForAdmin(c.get("user"), await liveSettingKeys());
208
+ c.header("Cache-Control", "no-store");
209
+ return c.json({ success: true as const });
210
+ },
211
+ )
187
212
  .get(
188
213
  "/documentation",
189
214
  auth.requireRole("admin"),
@@ -1,7 +1,9 @@
1
1
  import { fail, ok } from "@k2b/stdlib";
2
2
  import type { MiddlewareHandler } from "hono";
3
3
  import { Hono } from "hono";
4
+ import { describeRoute } from "hono-openapi";
4
5
  import { z } from "zod";
6
+ import { listApps } from "../_internal/registry";
5
7
  import { CapabilityAppIdSchema } from "../contracts/capabilities";
6
8
  import { type AuthContext, auth, err, respond, v } from "../server";
7
9
  import { getIdentitySigningKeyStatus, identityMetrics, revokeIdentitySigningKey, rewrapIdentitySigningKeys } from "../services/identity";
@@ -40,6 +42,7 @@ const MandateListQuerySchema = z
40
42
  .strict();
41
43
 
42
44
  type IdentityAdminDependencies = {
45
+ apps: () => Promise<Array<{ id: string; name: string }>>;
43
46
  status: typeof getIdentitySigningKeyStatus;
44
47
  rewrap: typeof rewrapIdentitySigningKeys;
45
48
  revoke: typeof revokeIdentitySigningKey;
@@ -49,6 +52,7 @@ type IdentityAdminDependencies = {
49
52
  };
50
53
 
51
54
  const dependencies: IdentityAdminDependencies = {
55
+ apps: async () => (await listApps()).map(({ id, name }) => ({ id, name })),
52
56
  status: getIdentitySigningKeyStatus,
53
57
  rewrap: rewrapIdentitySigningKeys,
54
58
  revoke: revokeIdentitySigningKey,
@@ -63,60 +67,96 @@ export const createAdminIdentityRoutes = (
63
67
  ) =>
64
68
  new Hono<AuthContext>()
65
69
  .use(authenticate)
66
- .post("/workloads/:appId/credentials", v("param", WorkloadAppParamsSchema), v("json", WorkloadCredentialInputSchema), async (c) =>
67
- respond(
68
- c,
69
- async () => {
70
- const { appId } = c.req.valid("param");
71
- const input = c.req.valid("json");
72
- const actor = c.get("user");
73
- const account = await service.serviceAccounts.getOrCreateResourceBound({
74
- name: `${appId} workload`,
75
- appId,
76
- resourceType: "cloud.app",
77
- resourceId: appId,
78
- createdBy: actor.id,
79
- });
80
- if (!account.ok) return fail(account.error);
70
+ .use("*", async (c, next) => {
71
+ c.header("Cache-Control", "no-store");
72
+ await next();
73
+ })
74
+ .get(
75
+ "/workloads",
76
+ describeRoute({ tags: ["App credentials"], summary: "List registered applications for workload credentials" }),
77
+ async (c) => c.json(await service.apps()),
78
+ )
79
+ .get(
80
+ "/workloads/credentials",
81
+ describeRoute({ tags: ["App credentials"], summary: "List credential metadata across applications without secrets" }),
82
+ v("query", WorkloadCredentialListQuerySchema),
83
+ async (c) =>
84
+ c.json(
85
+ await service.credentials.listOverview({
86
+ pagination: c.req.valid("query"),
87
+ filter: { serviceAccountKind: "resource_bound", resourceType: "cloud.app" },
88
+ }),
89
+ ),
90
+ )
91
+ .post(
92
+ "/workloads/:appId/credentials",
93
+ describeRoute({ tags: ["App credentials"], summary: "Create an app-bound credential; return its token once" }),
94
+ v("param", WorkloadAppParamsSchema),
95
+ v("json", WorkloadCredentialInputSchema),
96
+ async (c) =>
97
+ respond(
98
+ c,
99
+ async () => {
100
+ const { appId } = c.req.valid("param");
101
+ const input = c.req.valid("json");
102
+ const actor = c.get("user");
103
+ const account = await service.serviceAccounts.getOrCreateResourceBound({
104
+ name: `${appId} workload`,
105
+ appId,
106
+ resourceType: "cloud.app",
107
+ resourceId: appId,
108
+ createdBy: actor.id,
109
+ });
110
+ if (!account.ok) return fail(account.error);
81
111
 
82
- // The raw token intentionally appears only in this one create response.
83
- return service.credentials.createResourceApiToken({
84
- serviceAccountId: account.data.id,
85
- actor,
86
- name: input.name,
87
- scopes: input.scopes,
88
- expiresAt: input.expiresAt,
89
- });
90
- },
91
- 201,
92
- ),
112
+ // The raw token intentionally appears only in this one create response.
113
+ return service.credentials.createResourceApiToken({
114
+ serviceAccountId: account.data.id,
115
+ actor,
116
+ name: input.name,
117
+ scopes: input.scopes,
118
+ expiresAt: input.expiresAt,
119
+ });
120
+ },
121
+ 201,
122
+ ),
93
123
  )
94
- .get("/workloads/:appId/credentials", v("param", WorkloadAppParamsSchema), v("query", WorkloadCredentialListQuerySchema), async (c) => {
95
- const { appId } = c.req.valid("param");
96
- return c.json(
97
- await service.credentials.listOverview({
98
- pagination: c.req.valid("query"),
99
- filter: { serviceAccountKind: "resource_bound", appId, resourceType: "cloud.app", resourceId: appId },
100
- }),
101
- );
102
- })
103
- .delete("/workloads/:appId/credentials/:credentialId", v("param", WorkloadCredentialParamsSchema), async (c) =>
104
- respond(c, async () => {
105
- const { appId, credentialId } = c.req.valid("param");
106
- const credential = await service.credentials.getOverview({ id: credentialId });
107
- if (
108
- !credential ||
109
- credential.owner.type !== "resource" ||
110
- credential.owner.appId !== appId ||
111
- credential.owner.resourceType !== "cloud.app" ||
112
- credential.owner.resourceId !== appId
113
- ) {
114
- return fail(err.notFound("Workload credential"));
115
- }
124
+ .get(
125
+ "/workloads/:appId/credentials",
126
+ describeRoute({ tags: ["App credentials"], summary: "List app credential metadata without secrets" }),
127
+ v("param", WorkloadAppParamsSchema),
128
+ v("query", WorkloadCredentialListQuerySchema),
129
+ async (c) => {
130
+ const { appId } = c.req.valid("param");
131
+ return c.json(
132
+ await service.credentials.listOverview({
133
+ pagination: c.req.valid("query"),
134
+ filter: { serviceAccountKind: "resource_bound", appId, resourceType: "cloud.app", resourceId: appId },
135
+ }),
136
+ );
137
+ },
138
+ )
139
+ .delete(
140
+ "/workloads/:appId/credentials/:credentialId",
141
+ describeRoute({ tags: ["App credentials"], summary: "Revoke one exact app credential" }),
142
+ v("param", WorkloadCredentialParamsSchema),
143
+ async (c) =>
144
+ respond(c, async () => {
145
+ const { appId, credentialId } = c.req.valid("param");
146
+ const credential = await service.credentials.getOverview({ id: credentialId });
147
+ if (
148
+ !credential ||
149
+ credential.owner.type !== "resource" ||
150
+ credential.owner.appId !== appId ||
151
+ credential.owner.resourceType !== "cloud.app" ||
152
+ credential.owner.resourceId !== appId
153
+ ) {
154
+ return fail(err.notFound("Workload credential"));
155
+ }
116
156
 
117
- const revoked = await service.credentials.revoke({ credentialId, actor: c.get("user") });
118
- return revoked.ok ? ok({ revoked: true as const }) : revoked;
119
- }),
157
+ const revoked = await service.credentials.revoke({ credentialId, actor: c.get("user") });
158
+ return revoked.ok ? ok({ revoked: true as const }) : revoked;
159
+ }),
120
160
  )
121
161
  .get("/mandates", v("query", MandateListQuerySchema), async (c) => {
122
162
  const query = c.req.valid("query");
@@ -0,0 +1,58 @@
1
+ import { Hono, type MiddlewareHandler } from "hono";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import { bodyLimit } from "hono/body-limit";
4
+ import { describeRoute } from "hono-openapi";
5
+ import { RailAdminSchema, RailAdminInputSchema } from "../contracts/rail-admin";
6
+ import { RAIL_PREFERENCES_MAX_BYTES } from "../contracts/rail-preferences";
7
+ import { type AuthContext, auth, jsonResponse, requiresAdmin, v } from "../server";
8
+ import { RailAdminError, railShortcuts } from "../services/rail-shortcuts";
9
+
10
+ export const createAdminRailRoutes = (service = railShortcuts, authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin")) =>
11
+ new Hono<AuthContext>()
12
+ .use("*", authenticate)
13
+ .onError((error, c) => {
14
+ if (error instanceof RailAdminError) return c.json({ message: error.message }, error.status);
15
+ if (error instanceof HTTPException) return error.getResponse();
16
+ throw error;
17
+ })
18
+ .use("*", async (c, next) => {
19
+ c.header("Cache-Control", "no-store");
20
+ await next();
21
+ })
22
+ .delete(
23
+ "/cache",
24
+ describeRoute({
25
+ tags: ["Admin app bar"],
26
+ summary: "Invalidate app bar caches for all users",
27
+ ...requiresAdmin,
28
+ responses: { 204: { description: "Caches invalidated; rebuilt on the next read" } },
29
+ }),
30
+ async (c) => {
31
+ await service.invalidateCache(c.get("user"));
32
+ return c.body(null, 204);
33
+ },
34
+ )
35
+ .get(
36
+ "/",
37
+ describeRoute({
38
+ tags: ["Admin app bar"],
39
+ summary: "Read global app bar shortcuts and audiences",
40
+ ...requiresAdmin,
41
+ responses: { 200: jsonResponse(RailAdminSchema, "Global app bar configuration") },
42
+ }),
43
+ async (c) => c.json(await service.list(c.get("user"))),
44
+ )
45
+ .put(
46
+ "/",
47
+ describeRoute({
48
+ tags: ["Admin app bar"],
49
+ summary: "Save global app bar shortcuts and audiences",
50
+ ...requiresAdmin,
51
+ responses: { 200: jsonResponse(RailAdminSchema, "Saved configuration"), 409: { description: "Configuration changed" } },
52
+ }),
53
+ bodyLimit({ maxSize: RAIL_PREFERENCES_MAX_BYTES }),
54
+ v("json", RailAdminInputSchema),
55
+ async (c) => c.json(await service.save(c.get("user"), c.req.valid("json"))),
56
+ );
57
+
58
+ export default createAdminRailRoutes();
@@ -44,6 +44,20 @@ export const announcementRoutes = new Hono<AuthContext>().get(
44
44
 
45
45
  export const adminAnnouncementRoutes = new Hono<AuthContext>()
46
46
  .use(auth.requireRole("admin"))
47
+ .delete(
48
+ "/cache",
49
+ describeRoute({
50
+ tags: ["Admin Announcements"],
51
+ summary: "Invalidate announcement cache",
52
+ ...requiresAdmin,
53
+ responses: { 200: jsonResponse(z.object({ success: z.literal(true) }), "Cache invalidated") },
54
+ }),
55
+ async (c) => {
56
+ await announcements.admin.invalidateCache(c.get("user"));
57
+ c.header("Cache-Control", "no-store");
58
+ return c.json({ success: true as const });
59
+ },
60
+ )
47
61
  .get(
48
62
  "/",
49
63
  describeRoute({