@k2b/cloud 0.41.0 → 0.42.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 (227) hide show
  1. package/package.json +6 -5
  2. package/src/_internal/bounded-json.ts +14 -2
  3. package/src/_internal/capabilities.ts +87 -8
  4. package/src/_internal/capability-reviews.ts +6 -0
  5. package/src/_internal/capability-timeout.ts +71 -0
  6. package/src/_internal/define-app.ts +145 -89
  7. package/src/_internal/http-drain.ts +56 -0
  8. package/src/_internal/registry-validation.ts +3 -0
  9. package/src/_internal/runtime-context.ts +1 -0
  10. package/src/_internal/status-preserving-ssr.ts +2 -0
  11. package/src/access/PermissionEditor.tsx +16 -17
  12. package/src/access/PrincipalPicker.tsx +45 -10
  13. package/src/access/messages.ts +8 -0
  14. package/src/access/principal-label.ts +18 -0
  15. package/src/access/ui.ts +1 -0
  16. package/src/account/EntitySearch.tsx +5 -4
  17. package/src/ai/action-review.ts +2 -1
  18. package/src/ai/app-skill-store.ts +1 -1
  19. package/src/ai/capabilities.ts +21 -3
  20. package/src/ai/capability-execution.ts +14 -12
  21. package/src/ai/chat/action-card.ts +7 -1
  22. package/src/ai/chat/action-messages.ts +14 -0
  23. package/src/ai/chat/action-question.browser-harness.tsx +12 -0
  24. package/src/ai/chat/action-question.tsx +202 -22
  25. package/src/ai/chat/action-text.ts +1 -1
  26. package/src/ai/chat-tasks.ts +2 -1
  27. package/src/ai/client/controller.ts +150 -32
  28. package/src/ai/client/transport.ts +14 -2
  29. package/src/ai/code-capability-routes.ts +27 -23
  30. package/src/ai/code-runtime-tools.ts +14 -16
  31. package/src/ai/code-source-tools.ts +16 -18
  32. package/src/ai/executor.ts +56 -21
  33. package/src/ai/fetch-file-tool.ts +67 -76
  34. package/src/ai/http.ts +1 -0
  35. package/src/ai/migrate.ts +13 -0
  36. package/src/ai/open-questions.ts +4 -8
  37. package/src/ai/projects.ts +1 -2
  38. package/src/ai/protocol.ts +2 -2
  39. package/src/ai/recent-resources.ts +12 -5
  40. package/src/ai/review-content.ts +77 -0
  41. package/src/ai/routes.ts +69 -31
  42. package/src/ai/runtime.ts +1 -0
  43. package/src/ai/skill-tool.ts +17 -6
  44. package/src/ai/skills-routes.ts +72 -27
  45. package/src/ai/skills.ts +139 -60
  46. package/src/ai/store.ts +168 -33
  47. package/src/ai/types.ts +10 -3
  48. package/src/ai/usage.ts +2 -2
  49. package/src/api/admin-core-settings.ts +15 -2
  50. package/src/api/admin-geo.ts +54 -0
  51. package/src/api/admin-linux-identities.ts +6 -3
  52. package/src/api/admin-outgoing-mail.ts +29 -15
  53. package/src/api/approvals-loader.ts +3 -3
  54. package/src/api/approvals.ts +51 -0
  55. package/src/api/apps.ts +4 -2
  56. package/src/api/capabilities.ts +258 -181
  57. package/src/api/capability-streams.ts +21 -13
  58. package/src/api/cli-plugins.ts +4 -1
  59. package/src/api/geo-glyphs.ts +11 -0
  60. package/src/api/geo.ts +128 -0
  61. package/src/api/help.ts +12 -4
  62. package/src/api/index.ts +5 -0
  63. package/src/api/mcp.ts +46 -38
  64. package/src/api/me-panel-consent.ts +8 -5
  65. package/src/api/search.ts +21 -14
  66. package/src/api/sync-ops.ts +19 -13
  67. package/src/api/widgets.ts +42 -18
  68. package/src/approvals/Approvals.tsx +5 -3
  69. package/src/browser/CloudResourceSearch.browser-harness.tsx +53 -1
  70. package/src/browser/CloudResourceSearch.tsx +3 -13
  71. package/src/browser/PanelLeaveNotice.tsx +18 -0
  72. package/src/browser/navigation-search.ts +78 -25
  73. package/src/browser/panel-leave.ts +69 -0
  74. package/src/browser/panel-messages.ts +12 -0
  75. package/src/browser/panel-slot.ts +173 -24
  76. package/src/browser/panel.ts +3 -0
  77. package/src/browser/resource-search-messages.ts +2 -0
  78. package/src/capabilities/client.ts +28 -1
  79. package/src/capabilities/executions.ts +22 -3
  80. package/src/capabilities/geo-client.ts +57 -0
  81. package/src/capabilities/response.ts +2 -1
  82. package/src/capabilities/review-storage.ts +24 -0
  83. package/src/capabilities/reviews.ts +17 -3
  84. package/src/capabilities/server.ts +36 -16
  85. package/src/cli/access.ts +15 -23
  86. package/src/cli/admin/linux.ts +13 -2
  87. package/src/cli/admin/outgoing-mail.ts +26 -4
  88. package/src/cli/capabilities.ts +19 -0
  89. package/src/config/env.ts +0 -6
  90. package/src/contracts/app-access.ts +48 -0
  91. package/src/contracts/app.ts +4 -1
  92. package/src/contracts/approvals.ts +2 -2
  93. package/src/contracts/avatar.ts +186 -0
  94. package/src/contracts/capabilities.ts +148 -54
  95. package/src/contracts/geo-admin.ts +127 -0
  96. package/src/contracts/geo-map.ts +19 -0
  97. package/src/contracts/geo-route.ts +168 -0
  98. package/src/contracts/geo.ts +248 -0
  99. package/src/contracts/index.ts +4 -0
  100. package/src/contracts/outgoing-mail.ts +17 -4
  101. package/src/contracts/registry.ts +2 -0
  102. package/src/events/app-access.ts +68 -0
  103. package/src/events/live-delivery.ts +128 -0
  104. package/src/events/live.ts +14 -130
  105. package/src/geo/CloudMap.tsx +95 -0
  106. package/src/geo/map-messages.ts +17 -0
  107. package/src/geo/ui.ts +1 -0
  108. package/src/index.ts +2 -0
  109. package/src/server/client-ip.ts +36 -0
  110. package/src/server/index.ts +3 -4
  111. package/src/server/middleware/app-access.ts +312 -0
  112. package/src/server/middleware/auth.ts +1 -1
  113. package/src/server/middleware/invocation.ts +11 -0
  114. package/src/server/middleware/rate-limit.ts +4 -4
  115. package/src/server/services/access.ts +13 -9
  116. package/src/server/services/index.ts +0 -2
  117. package/src/server/services/services.ts +0 -2
  118. package/src/services/accounts/posix.ts +21 -7
  119. package/src/services/allowed-network-fetch.ts +16 -10
  120. package/src/services/app-access-metrics.ts +24 -0
  121. package/src/services/app-access.ts +123 -0
  122. package/src/services/app-discovery.ts +27 -0
  123. package/src/services/approvals/content.ts +40 -0
  124. package/src/services/approvals/index.ts +14 -6
  125. package/src/services/approvals/list.ts +6 -11
  126. package/src/services/approvals/question.ts +11 -7
  127. package/src/services/downstream-budget.ts +87 -0
  128. package/src/services/geo/admin.ts +262 -0
  129. package/src/services/geo/bucket.ts +67 -0
  130. package/src/services/geo/cache.ts +57 -0
  131. package/src/services/geo/costs.ts +5 -0
  132. package/src/services/geo/errors.ts +41 -0
  133. package/src/services/geo/fixtures/README.md +17 -0
  134. package/src/services/geo/fixtures/along.json +355 -0
  135. package/src/services/geo/fixtures/autocomplete.json +218 -0
  136. package/src/services/geo/fixtures/geocode.json +129 -0
  137. package/src/services/geo/fixtures/places.json +355 -0
  138. package/src/services/geo/fixtures/reverse.json +61 -0
  139. package/src/services/geo/fixtures/routing.json +57 -0
  140. package/src/services/geo/geoapify-route.ts +93 -0
  141. package/src/services/geo/geoapify.ts +281 -0
  142. package/src/services/geo/ledger.ts +122 -0
  143. package/src/services/geo/map.ts +160 -0
  144. package/src/services/geo/migrate.ts +25 -0
  145. package/src/services/geo/polyline.ts +184 -0
  146. package/src/services/geo/route-chain.ts +145 -0
  147. package/src/services/geo/route-costs.ts +9 -0
  148. package/src/services/geo/route-geometry.ts +39 -0
  149. package/src/services/geo/route-plan.ts +172 -0
  150. package/src/services/geo/route-service.ts +433 -0
  151. package/src/services/geo/service.ts +300 -0
  152. package/src/services/geo/settings.ts +49 -0
  153. package/src/services/help/index.ts +16 -7
  154. package/src/services/help/types.ts +6 -1
  155. package/src/services/identity/invocation-actor.ts +20 -2
  156. package/src/services/identity/issue-invocation.ts +67 -0
  157. package/src/services/identity/key-ring.ts +5 -3
  158. package/src/services/identity/runtime-config.ts +23 -0
  159. package/src/services/index.ts +9 -0
  160. package/src/services/mandates/index.ts +40 -3
  161. package/src/services/network-security.ts +6 -3
  162. package/src/services/network-transport.ts +99 -0
  163. package/src/services/notifications/catalog.ts +3 -2
  164. package/src/services/notifications/runtime.ts +1 -0
  165. package/src/services/notifications/web-push-transport.ts +45 -38
  166. package/src/services/outgoing-mail/dispatcher.ts +6 -1
  167. package/src/services/outgoing-mail/index.ts +55 -1
  168. package/src/services/outgoing-mail/messages.ts +82 -3
  169. package/src/services/outgoing-mail/resolve-host.ts +2 -2
  170. package/src/services/outgoing-mail/retention.ts +13 -1
  171. package/src/services/outgoing-mail/store.ts +36 -6
  172. package/src/services/outgoing-mail/test-send.ts +34 -4
  173. package/src/services/outgoing-mail/transport.ts +1 -1
  174. package/src/services/pdf/gotenberg.ts +11 -38
  175. package/src/services/pdf/template-preview.ts +1 -1
  176. package/src/services/public-http.ts +28 -40
  177. package/src/services/settings/app.ts +4 -1
  178. package/src/services/settings/core-settings.ts +88 -0
  179. package/src/services/settings/defaults.ts +8 -2
  180. package/src/services/settings/errors.ts +11 -0
  181. package/src/services/settings/geo-budget.ts +40 -0
  182. package/src/services/settings/index.ts +4 -3
  183. package/src/services/settings/store.ts +25 -17
  184. package/src/services/weather/geo.ts +29 -111
  185. package/src/services/weather/index.ts +4 -3
  186. package/src/services/weather/locations.ts +8 -5
  187. package/src/services/weather/migrate.ts +1 -0
  188. package/src/services/weather/settings.ts +0 -16
  189. package/src/shared/__snapshots__/avatar-art.test.ts.snap +5 -0
  190. package/src/shared/avatar-art.ts +400 -0
  191. package/src/shared/index.ts +2 -0
  192. package/src/shared/network-address.ts +22 -3
  193. package/src/shared/template-rendering.ts +36 -4
  194. package/src/shared-text/documents.ts +1 -1
  195. package/src/shared-text/index.ts +2 -0
  196. package/src/shared-text/log.ts +51 -3
  197. package/src/shared-text/snapshots.ts +340 -0
  198. package/src/ssr/AdminLayout.tsx +4 -2
  199. package/src/ssr/CloudShell.island.tsx +6 -0
  200. package/src/ssr/Layout.tsx +65 -15
  201. package/src/ssr/LayoutHelp.browser-harness.tsx +1 -1
  202. package/src/ssr/LayoutHelp.tsx +3 -2
  203. package/src/ssr/PanelRuntime.island.tsx +11 -7
  204. package/src/ssr/PwaLayout.tsx +2 -2
  205. package/src/ssr/RailApps.tsx +6 -0
  206. package/src/ssr/ShellRuntime.island.tsx +23 -7
  207. package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +151 -0
  208. package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +6 -6
  209. package/src/ssr/admin-navigation.ts +1 -0
  210. package/src/ssr/app-badges.ts +6 -1
  211. package/src/ssr/compact-shell.browser-harness.ts +44 -5
  212. package/src/ssr/help.ts +3 -2
  213. package/src/ssr/index.ts +1 -1
  214. package/src/ssr/layout-context.ts +2 -0
  215. package/src/ssr/mobile-menu-history.ts +4 -0
  216. package/src/ssr/page-swap-fixture/Column.island.tsx +15 -0
  217. package/src/ssr/page-swap-fixture/ProviderProbe.island.tsx +31 -0
  218. package/src/ssr/page-swap.browser-harness.tsx +177 -0
  219. package/src/ssr/page-swap.ts +28 -0
  220. package/src/ssr/page-trail-runtime.ts +32 -13
  221. package/src/ssr/panel-consent.ts +2 -2
  222. package/src/ssr/panel-slot.browser-harness.tsx +3 -0
  223. package/src/ssr/platform-messages.ts +8 -0
  224. package/src/ssr/runtime.ts +17 -0
  225. package/src/ssr/shell-data.tsx +10 -2
  226. package/src/styles/global.css +70 -6
  227. package/src/server/services/geo.ts +0 -215
@@ -17,15 +17,18 @@ import { type AuthContext, auth, jsonResponse, requiresAdmin } from "../server";
17
17
  import { noTransform } from "../server/middleware/no-transform";
18
18
  import { audit } from "../services/audit";
19
19
  import { outgoingMailLog } from "../services/outgoing-mail/admin";
20
- import { MAIL_RETENTION_KEYS, readMailRetention } from "../services/outgoing-mail/retention";
20
+ import { MAIL_RETENTION_KEYS, readMailRetentionSettings } from "../services/outgoing-mail/retention";
21
21
  import { type MailAuditContext, OutgoingMailError, outgoingMailStore } from "../services/outgoing-mail/store";
22
22
  import { outgoingMailTest } from "../services/outgoing-mail/test-send";
23
23
  import * as settings from "../services/settings";
24
24
 
25
- const retentionInput = MailRetentionSchema.refine((value) => value.recordDays >= value.contentDays, {
26
- message: "Record retention must be at least content retention.",
27
- path: ["recordDays"],
28
- });
25
+ const retentionView = MailRetentionSchema.extend({ unreadable: z.record(z.string(), z.boolean()) });
26
+ const retentionInput = MailRetentionSchema.partial()
27
+ .extend({ replaceUnreadable: z.array(z.enum(MAIL_RETENTION_KEYS)).optional() })
28
+ .refine((value) => value.recordDays === undefined || value.contentDays === undefined || value.recordDays >= value.contentDays, {
29
+ message: "Record retention must be at least content retention.",
30
+ path: ["recordDays"],
31
+ });
29
32
 
30
33
  const validate = <Target extends "param" | "json" | "query", Schema extends z.ZodType>(
31
34
  target: Target,
@@ -113,9 +116,9 @@ export const createAdminOutgoingMailRoutes = (authenticate: MiddlewareHandler<Au
113
116
  summary: "Read outgoing mail retention",
114
117
  tags: ["Outgoing mail"],
115
118
  ...requiresAdmin,
116
- responses: { 200: jsonResponse(MailRetentionSchema, "Effective retention in days") },
119
+ responses: { 200: jsonResponse(retentionView, "Effective retention in days") },
117
120
  }),
118
- async (c) => c.json(await readMailRetention()),
121
+ async (c) => c.json(await readMailRetentionSettings()),
119
122
  )
120
123
  .put(
121
124
  "/retention",
@@ -124,19 +127,30 @@ export const createAdminOutgoingMailRoutes = (authenticate: MiddlewareHandler<Au
124
127
  tags: ["Outgoing mail"],
125
128
  ...requiresAdmin,
126
129
  responses: {
127
- 200: jsonResponse(MailRetentionSchema, "Updated retention in days"),
130
+ 200: jsonResponse(retentionView, "Updated retention in days"),
128
131
  400: jsonResponse(z.object({ code: z.literal("bad_input"), message: z.string() }), "Invalid retention"),
132
+ 409: jsonResponse(
133
+ z.object({ code: z.literal("setting_unreadable"), message: z.string() }),
134
+ "Stored period needs explicit replacement",
135
+ ),
129
136
  },
130
137
  }),
131
138
  validate("json", retentionInput, "bad_input"),
132
139
  async (c) => {
133
140
  const next = c.req.valid("json");
134
- const old = await readMailRetention();
135
141
  await sql.begin(async (tx) => {
136
- // complete value from explicit admin input; nothing is merged over a read (#1303)
137
- await settings.set(MAIL_RETENTION_KEYS[0], next.contentDays, tx, { replaceUnreadable: true });
138
- // complete value from explicit admin input; nothing is merged over a read (#1303)
139
- await settings.set(MAIL_RETENTION_KEYS[1], next.recordDays, tx, { replaceUnreadable: true });
142
+ await tx`SELECT pg_advisory_xact_lock(hashtextextended('outgoing-mail.retention-settings', 0))`;
143
+ const old = await readMailRetentionSettings(tx);
144
+ if ((next.recordDays ?? old.recordDays) < (next.contentDays ?? old.contentDays))
145
+ throw new OutgoingMailError("bad_input", "Record retention must be at least content retention.");
146
+ if (next.contentDays !== undefined)
147
+ await settings.set(MAIL_RETENTION_KEYS[0], next.contentDays, tx, {
148
+ replaceUnreadable: next.replaceUnreadable?.includes(MAIL_RETENTION_KEYS[0]) === true,
149
+ });
150
+ if (next.recordDays !== undefined)
151
+ await settings.set(MAIL_RETENTION_KEYS[1], next.recordDays, tx, {
152
+ replaceUnreadable: next.replaceUnreadable?.includes(MAIL_RETENTION_KEYS[1]) === true,
153
+ });
140
154
  await audit.record(
141
155
  {
142
156
  ...context(c),
@@ -149,7 +163,7 @@ export const createAdminOutgoingMailRoutes = (authenticate: MiddlewareHandler<Au
149
163
  );
150
164
  });
151
165
  await settings.invalidateSettingsCache(MAIL_RETENTION_KEYS);
152
- return c.json(next);
166
+ return c.json(await readMailRetentionSettings());
153
167
  },
154
168
  )
155
169
  .get(
@@ -245,7 +259,7 @@ export const createAdminOutgoingMailRoutes = (authenticate: MiddlewareHandler<Au
245
259
  .put(
246
260
  "/apps/:appId/log-access",
247
261
  describeRoute({
248
- summary: "Set application mail log access",
262
+ summary: "Replace application metadata and content mail log access",
249
263
  tags: ["Outgoing mail"],
250
264
  ...requiresAdmin,
251
265
  responses: {
@@ -3,7 +3,7 @@ import { HTTPException } from "hono/http-exception";
3
3
  import { listCapabilities } from "../_internal/registry";
4
4
  import { resolveAiChatRequestContext } from "../ai/request-context";
5
5
  import type { ApprovalListQuery, ApprovalPage } from "../contracts/approvals";
6
- import { CapabilityActionCallReviewSchema, capabilityActionPolicy } from "../contracts/capabilities";
6
+ import { CapabilityActionReviewPresentationSchema, capabilityActionPolicy } from "../contracts/capabilities";
7
7
  import type { AuthContext } from "../server/middleware/auth";
8
8
  import { listPending, presentPendingApproval } from "../services/approvals/list";
9
9
  import { logger } from "../services/logging";
@@ -16,8 +16,8 @@ export const loadPendingApprovals = async <T extends AuthContext>(c: Context<T>,
16
16
  const apps = getLocalizedRuntimeContext(c).apps;
17
17
  const needsPolicy = page.items.some((row) => {
18
18
  if (row.storedReview && "sentence" in row.storedReview) return false;
19
- const review = CapabilityActionCallReviewSchema.safeParse(row.details);
20
- return review.success && !review.data.ask;
19
+ const review = CapabilityActionReviewPresentationSchema.safeParse(row.details);
20
+ return review.success && "sentence" in review.data && !review.data.ask;
21
21
  });
22
22
  const capabilities = needsPolicy
23
23
  ? await listCapabilities().catch((error: unknown) => {
@@ -4,6 +4,7 @@ import { describeRoute } from "hono-openapi";
4
4
  import { z } from "zod";
5
5
  import { toAiActionFailureResponse } from "../ai/http";
6
6
  import { resolveAiChatRequestContext } from "../ai/request-context";
7
+ import { readReviewContent } from "../ai/review-content";
7
8
  import { submitAiTurnAction } from "../ai/runtime";
8
9
  import { aiConversations } from "../ai/store";
9
10
  import {
@@ -14,6 +15,7 @@ import {
14
15
  ApprovalListQuerySchema,
15
16
  ApprovalPageSchema,
16
17
  } from "../contracts/approvals";
18
+ import { CapabilityReviewContentSchema } from "../contracts/capabilities";
17
19
  import { ErrorResponseSchema } from "../contracts/shared";
18
20
  import { rateLimit } from "../server";
19
21
  import { respond } from "../server/api";
@@ -23,6 +25,7 @@ import { jsonResponse, requiresAuth } from "../server/middleware/openapi";
23
25
  import { v } from "../server/middleware/validator";
24
26
  import { ok } from "../server/services";
25
27
  import { approvalDecisionChannel, countPending } from "../services/approvals";
28
+ import { readApprovalContent } from "../services/approvals/content";
26
29
  import { decodeApprovalCursor } from "../services/approvals/cursor";
27
30
  import { loadPendingApprovals } from "./approvals-loader";
28
31
 
@@ -30,6 +33,54 @@ export const approvalRoutes = new Hono<AuthContext>()
30
33
  .use("*", noTransform())
31
34
  .use(rateLimit())
32
35
  .use(auth.requireRole("authenticated"))
36
+ .get(
37
+ "/:id/content",
38
+ describeRoute({
39
+ tags: ["Approvals"],
40
+ summary: "Read one complete pending approval snapshot",
41
+ ...requiresAuth,
42
+ responses: {
43
+ 200: jsonResponse(CapabilityReviewContentSchema, "Complete reviewed content"),
44
+ 404: jsonResponse(ErrorResponseSchema, "Snapshot unavailable"),
45
+ },
46
+ }),
47
+ v("param", z.object({ id: z.uuid() })),
48
+ v("query", z.object({ digest: z.string().regex(/^[a-f0-9]{64}$/) }).strict()),
49
+ async (c) => {
50
+ const ctx = await resolveAiChatRequestContext(c);
51
+ if (ctx instanceof Response) return ctx;
52
+ const content = await readApprovalContent({
53
+ ...c.req.valid("param"),
54
+ ...c.req.valid("query"),
55
+ approverUserId: ctx.ownerUserId,
56
+ signal: c.req.raw.signal,
57
+ });
58
+ c.header("Cache-Control", "private, no-store");
59
+ return content ? c.json(content) : c.json({ message: "This review is no longer available." }, 404);
60
+ },
61
+ )
62
+ .get(
63
+ "/conversations/:conversationId/turns/:turnId/reviews/:callId",
64
+ describeRoute({
65
+ tags: ["Approvals"],
66
+ summary: "Read one historical conversation review snapshot",
67
+ ...requiresAuth,
68
+ responses: {
69
+ 200: jsonResponse(CapabilityReviewContentSchema, "Complete reviewed content"),
70
+ 404: jsonResponse(ErrorResponseSchema, "Snapshot unavailable"),
71
+ },
72
+ }),
73
+ v("param", z.object({ conversationId: z.uuid(), turnId: z.uuid(), callId: z.string().min(1).max(200) })),
74
+ v("query", z.object({ digest: z.string().regex(/^[a-f0-9]{64}$/) }).strict()),
75
+ async (c) => {
76
+ const ctx = await resolveAiChatRequestContext(c);
77
+ if (ctx instanceof Response) return ctx;
78
+ const location = c.req.valid("param");
79
+ c.header("Cache-Control", "private, no-store");
80
+ const content = await readReviewContent(location, c.req.valid("query").digest, ctx.ownerUserId, c.req.raw.signal);
81
+ return content ? c.json(content) : c.json({ message: "This review is no longer available." }, 404);
82
+ },
83
+ )
33
84
  .get(
34
85
  "/",
35
86
  describeRoute({
package/src/api/apps.ts CHANGED
@@ -3,6 +3,8 @@ import { describeRoute } from "hono-openapi";
3
3
  import { z } from "zod";
4
4
  import { ErrorResponseSchema } from "../contracts";
5
5
  import { type AuthContext, auth, jsonResponse, requiresAuth, v } from "../server";
6
+ import { appAccessAuthority } from "../services/app-access";
7
+ import { discoverableApps } from "../services/app-discovery";
6
8
  import { visibleNavigationApps } from "../ssr/app-navigation";
7
9
  import { getLocalizedRuntimeContext } from "../ssr/runtime";
8
10
 
@@ -35,9 +37,9 @@ export const appDiscoveryRoutes = new Hono<AuthContext>().use(auth.requireRole("
35
37
  },
36
38
  }),
37
39
  v("query", AppListQuerySchema),
38
- (c) => {
40
+ async (c) => {
39
41
  const search = c.req.valid("query").search?.toLowerCase();
40
- const items = visibleNavigationApps(getLocalizedRuntimeContext(c).apps, c.get("user"))
42
+ const items = visibleNavigationApps(await discoverableApps(appAccessAuthority(c), getLocalizedRuntimeContext(c).apps), c.get("user"))
41
43
  .map((app) => ({
42
44
  id: app.id,
43
45
  name: app.name,