@k2b/cloud 0.6.0-rc.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 (540) hide show
  1. package/README.md +98 -0
  2. package/package.json +128 -0
  3. package/public/logo.svg +1 -0
  4. package/scripts/app-favicon.test.ts +49 -0
  5. package/scripts/app-favicon.ts +51 -0
  6. package/scripts/build.ts +205 -0
  7. package/scripts/preload.ts +164 -0
  8. package/scripts/sync-recovery-smoke.ts +200 -0
  9. package/src/_internal/app-favicon.ts +2 -0
  10. package/src/_internal/app-presentation.ts +61 -0
  11. package/src/_internal/app-workspace-state.ts +24 -0
  12. package/src/_internal/bounded-json.ts +47 -0
  13. package/src/_internal/build-metadata.ts +9 -0
  14. package/src/_internal/capabilities.ts +1118 -0
  15. package/src/_internal/define-app.ts +801 -0
  16. package/src/_internal/heartbeat.ts +167 -0
  17. package/src/_internal/help-catalog.ts +241 -0
  18. package/src/_internal/help.ts +95 -0
  19. package/src/_internal/nats-connection.ts +34 -0
  20. package/src/_internal/page-responses.ts +40 -0
  21. package/src/_internal/process-sync.ts +117 -0
  22. package/src/_internal/readiness.ts +3 -0
  23. package/src/_internal/registry-validation.ts +136 -0
  24. package/src/_internal/registry-watch.ts +52 -0
  25. package/src/_internal/registry.ts +338 -0
  26. package/src/_internal/runtime-compatibility.ts +64 -0
  27. package/src/_internal/runtime-context.ts +84 -0
  28. package/src/_internal/runtime-watcher.ts +78 -0
  29. package/src/_internal/static-assets.ts +86 -0
  30. package/src/_internal/status-preserving-ssr.ts +49 -0
  31. package/src/access/PermissionEditor.tsx +486 -0
  32. package/src/access/ResourceApiKeys.tsx +271 -0
  33. package/src/access/messages.ts +121 -0
  34. package/src/access/ui.ts +16 -0
  35. package/src/account/Avatar.tsx +28 -0
  36. package/src/account/EntitySearch.tsx +363 -0
  37. package/src/account/avatar-upload-dialog.tsx +197 -0
  38. package/src/account/avatar-upload.ts +149 -0
  39. package/src/account/ui.ts +3 -0
  40. package/src/ai/admin.ts +47 -0
  41. package/src/ai/approval-routes.ts +130 -0
  42. package/src/ai/approvals.ts +139 -0
  43. package/src/ai/assistant-models.ts +61 -0
  44. package/src/ai/attachments.ts +40 -0
  45. package/src/ai/browser.ts +92 -0
  46. package/src/ai/capabilities.ts +714 -0
  47. package/src/ai/capability-execution.ts +151 -0
  48. package/src/ai/chat/blocks.render.test.tsx +1108 -0
  49. package/src/ai/chat/blocks.tsx +730 -0
  50. package/src/ai/chat/builtin-tools.tsx +428 -0
  51. package/src/ai/chat/capability-table.tsx +87 -0
  52. package/src/ai/chat/composer-adapter.ts +169 -0
  53. package/src/ai/chat/file-tools.tsx +81 -0
  54. package/src/ai/chat/message-actions.tsx +370 -0
  55. package/src/ai/chat/message-utils.ts +479 -0
  56. package/src/ai/chat/messages.ts +43 -0
  57. package/src/ai/chat/presentation.tsx +395 -0
  58. package/src/ai/chat/primitives.tsx +3 -0
  59. package/src/ai/chat/tool-disclosure.tsx +41 -0
  60. package/src/ai/chat/turn-navigator-utils.ts +28 -0
  61. package/src/ai/chat/turn-navigator.tsx +164 -0
  62. package/src/ai/chat/user-message.tsx +149 -0
  63. package/src/ai/chat/visual-tools.tsx +435 -0
  64. package/src/ai/chat/web-tools.tsx +202 -0
  65. package/src/ai/chat-task-contracts.ts +113 -0
  66. package/src/ai/chat-tasks.ts +952 -0
  67. package/src/ai/client/controller.ts +1211 -0
  68. package/src/ai/client/file-source.ts +95 -0
  69. package/src/ai/client/live-connection.ts +140 -0
  70. package/src/ai/client/projection.ts +178 -0
  71. package/src/ai/client/transport.ts +111 -0
  72. package/src/ai/compaction.ts +182 -0
  73. package/src/ai/credentials.ts +124 -0
  74. package/src/ai/default-tool-contracts.ts +108 -0
  75. package/src/ai/default-tools.ts +127 -0
  76. package/src/ai/enrich.ts +280 -0
  77. package/src/ai/executor.ts +1624 -0
  78. package/src/ai/fetch-file-tool.ts +313 -0
  79. package/src/ai/file-context.ts +92 -0
  80. package/src/ai/file-media-type.ts +28 -0
  81. package/src/ai/file-mount.ts +16 -0
  82. package/src/ai/file-tools.ts +566 -0
  83. package/src/ai/files-store.ts +390 -0
  84. package/src/ai/firecrawl-tools.ts +305 -0
  85. package/src/ai/grids-skill.ts +126 -0
  86. package/src/ai/http.ts +265 -0
  87. package/src/ai/index.ts +337 -0
  88. package/src/ai/kit-skill.ts +22 -0
  89. package/src/ai/limits.ts +5 -0
  90. package/src/ai/live-events.ts +289 -0
  91. package/src/ai/live-messages.ts +45 -0
  92. package/src/ai/live-outbox.ts +102 -0
  93. package/src/ai/live-routes.ts +412 -0
  94. package/src/ai/live.ts +2 -0
  95. package/src/ai/maintenance.ts +302 -0
  96. package/src/ai/markdown-pdf-tool.ts +95 -0
  97. package/src/ai/memories.ts +605 -0
  98. package/src/ai/memory-learning-runs.ts +222 -0
  99. package/src/ai/memory-learning.ts +619 -0
  100. package/src/ai/memory-tool.ts +120 -0
  101. package/src/ai/memory-workflow-evidence.ts +158 -0
  102. package/src/ai/migrate.ts +2082 -0
  103. package/src/ai/model-access-migrate.ts +30 -0
  104. package/src/ai/model-access.ts +191 -0
  105. package/src/ai/personal-agent.ts +6 -0
  106. package/src/ai/prefs.ts +72 -0
  107. package/src/ai/project-tool.ts +94 -0
  108. package/src/ai/projects-routes.ts +277 -0
  109. package/src/ai/projects.ts +902 -0
  110. package/src/ai/protocol.ts +279 -0
  111. package/src/ai/provider.ts +73 -0
  112. package/src/ai/public-projection.ts +25 -0
  113. package/src/ai/resource-markers.ts +31 -0
  114. package/src/ai/resource-refs.ts +70 -0
  115. package/src/ai/routes.ts +1131 -0
  116. package/src/ai/runtime-tools.ts +39 -0
  117. package/src/ai/runtime.ts +544 -0
  118. package/src/ai/settings.ts +529 -0
  119. package/src/ai/short-id.ts +45 -0
  120. package/src/ai/skill-catalog.ts +73 -0
  121. package/src/ai/skill-format.ts +145 -0
  122. package/src/ai/skill-seeds.ts +430 -0
  123. package/src/ai/skill-tool.ts +81 -0
  124. package/src/ai/skills-routes.ts +143 -0
  125. package/src/ai/skills.ts +934 -0
  126. package/src/ai/solid.ts +27 -0
  127. package/src/ai/store.ts +3113 -0
  128. package/src/ai/stream.ts +308 -0
  129. package/src/ai/structured-runs.ts +66 -0
  130. package/src/ai/structured.ts +186 -0
  131. package/src/ai/system-prompt.ts +170 -0
  132. package/src/ai/task-prompt.ts +22 -0
  133. package/src/ai/timeline.ts +148 -0
  134. package/src/ai/tool-audit.ts +177 -0
  135. package/src/ai/tool-result-budget.ts +16 -0
  136. package/src/ai/tools.ts +155 -0
  137. package/src/ai/types.ts +967 -0
  138. package/src/ai/ui.tsx +39 -0
  139. package/src/ai/usage-migrate.ts +81 -0
  140. package/src/ai/usage.ts +315 -0
  141. package/src/ai/validate.ts +54 -0
  142. package/src/ai/vision-tool.ts +79 -0
  143. package/src/api/accounts-entities.ts +174 -0
  144. package/src/api/admin-ai-projects.ts +74 -0
  145. package/src/api/admin-ai-skills.ts +76 -0
  146. package/src/api/admin-ai-usage.ts +42 -0
  147. package/src/api/admin-core-settings.ts +508 -0
  148. package/src/api/admin-identity.ts +148 -0
  149. package/src/api/admin-lifecycle.ts +206 -0
  150. package/src/api/admin-linux-identities.ts +66 -0
  151. package/src/api/announcements.ts +131 -0
  152. package/src/api/app-approval.ts +129 -0
  153. package/src/api/apps.ts +51 -0
  154. package/src/api/auth/schemas.ts +54 -0
  155. package/src/api/auth.ts +370 -0
  156. package/src/api/capabilities.ts +658 -0
  157. package/src/api/help-messages.ts +19 -0
  158. package/src/api/help.ts +78 -0
  159. package/src/api/index.ts +108 -0
  160. package/src/api/mcp.ts +744 -0
  161. package/src/api/me-rail.ts +45 -0
  162. package/src/api/me.ts +753 -0
  163. package/src/api/search/schemas.ts +79 -0
  164. package/src/api/search.bench.ts +185 -0
  165. package/src/api/search.ts +407 -0
  166. package/src/api/sync-ops.ts +79 -0
  167. package/src/api/widgets.ts +143 -0
  168. package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
  169. package/src/browser/CloudResourceSearch.tsx +791 -0
  170. package/src/browser/app-approval-vault.ts +305 -0
  171. package/src/browser/app-approval.ts +234 -0
  172. package/src/browser/live-websocket.ts +268 -0
  173. package/src/browser/locale-preference.ts +25 -0
  174. package/src/browser/notification-target.ts +27 -0
  175. package/src/browser/notification-timeout.ts +14 -0
  176. package/src/browser/notifications.ts +181 -0
  177. package/src/browser/resource-clipboard.ts +70 -0
  178. package/src/browser/resource-picker.tsx +39 -0
  179. package/src/browser/resource-search-messages.ts +55 -0
  180. package/src/browser/resource-search.ts +27 -0
  181. package/src/capabilities/client.ts +149 -0
  182. package/src/capabilities/response.ts +39 -0
  183. package/src/capabilities/server.ts +215 -0
  184. package/src/capabilities/signals.ts +4 -0
  185. package/src/capabilities/testing.ts +12 -0
  186. package/src/capabilities/types.ts +32 -0
  187. package/src/cli/access.ts +499 -0
  188. package/src/cli/account.ts +382 -0
  189. package/src/cli/admin/account-administration.ts +45 -0
  190. package/src/cli/admin/account-categories.ts +49 -0
  191. package/src/cli/admin/ai-usage.ts +118 -0
  192. package/src/cli/admin/app-sign-in.ts +49 -0
  193. package/src/cli/admin/data.ts +343 -0
  194. package/src/cli/admin/documentation.ts +34 -0
  195. package/src/cli/admin/gateway.ts +155 -0
  196. package/src/cli/admin/index.ts +61 -0
  197. package/src/cli/admin/instance.ts +250 -0
  198. package/src/cli/admin/jobs.ts +223 -0
  199. package/src/cli/admin/legal.ts +122 -0
  200. package/src/cli/admin/linux.ts +53 -0
  201. package/src/cli/admin/logs.ts +324 -0
  202. package/src/cli/admin/metrics.ts +142 -0
  203. package/src/cli/admin/nats.ts +167 -0
  204. package/src/cli/admin/notifications.ts +516 -0
  205. package/src/cli/admin/shared.ts +113 -0
  206. package/src/cli/admin/sync.ts +188 -0
  207. package/src/cli/admin/telemetry.ts +314 -0
  208. package/src/cli/admin/webhooks.ts +222 -0
  209. package/src/cli/admin/workflows.ts +367 -0
  210. package/src/cli/apps.ts +40 -0
  211. package/src/cli/capabilities.ts +239 -0
  212. package/src/cli/commands.ts +604 -0
  213. package/src/cli/index.ts +61 -0
  214. package/src/cli/locale.ts +31 -0
  215. package/src/cli/output.ts +58 -0
  216. package/src/clients/core.ts +20 -0
  217. package/src/config/env.ts +54 -0
  218. package/src/config/index.ts +6 -0
  219. package/src/contracts/account-categories.ts +62 -0
  220. package/src/contracts/announcements.ts +121 -0
  221. package/src/contracts/app-approval.ts +197 -0
  222. package/src/contracts/app.ts +162 -0
  223. package/src/contracts/capabilities.ts +484 -0
  224. package/src/contracts/index.ts +14 -0
  225. package/src/contracts/notification-live.ts +66 -0
  226. package/src/contracts/notification-types.ts +235 -0
  227. package/src/contracts/posix.ts +69 -0
  228. package/src/contracts/profile.ts +85 -0
  229. package/src/contracts/rail-preferences.ts +53 -0
  230. package/src/contracts/registry.ts +110 -0
  231. package/src/contracts/resource-clipboard.ts +33 -0
  232. package/src/contracts/settings-types.ts +98 -0
  233. package/src/contracts/shared.ts +408 -0
  234. package/src/contracts/user-notifications.ts +101 -0
  235. package/src/contracts/widgets.ts +183 -0
  236. package/src/desktop/index.ts +704 -0
  237. package/src/desktop/solid.tsx +938 -0
  238. package/src/index.ts +58 -0
  239. package/src/server/actor.ts +41 -0
  240. package/src/server/api/index.ts +2 -0
  241. package/src/server/api/respond.ts +96 -0
  242. package/src/server/api-client.ts +54 -0
  243. package/src/server/app-context.ts +39 -0
  244. package/src/server/help.ts +187 -0
  245. package/src/server/index.ts +89 -0
  246. package/src/server/locale.ts +55 -0
  247. package/src/server/middleware/auth.ts +386 -0
  248. package/src/server/middleware/index.ts +28 -0
  249. package/src/server/middleware/invocation.ts +55 -0
  250. package/src/server/middleware/middleware.ts +65 -0
  251. package/src/server/middleware/openapi.ts +126 -0
  252. package/src/server/middleware/rate-limit.ts +125 -0
  253. package/src/server/middleware/request-logger.ts +41 -0
  254. package/src/server/middleware/route-template.ts +46 -0
  255. package/src/server/middleware/runtime.ts +22 -0
  256. package/src/server/middleware/settings.ts +55 -0
  257. package/src/server/middleware/validator.ts +49 -0
  258. package/src/server/middleware/workload.ts +22 -0
  259. package/src/server/ratelimit.ts +151 -0
  260. package/src/server/request-cookies.ts +18 -0
  261. package/src/server/services/access.ts +644 -0
  262. package/src/server/services/freeipa/client.ts +137 -0
  263. package/src/server/services/freeipa/index.ts +9 -0
  264. package/src/server/services/freeipa/session.ts +123 -0
  265. package/src/server/services/freeipa/test-certificates.ts +44 -0
  266. package/src/server/services/freeipa/tls.ts +44 -0
  267. package/src/server/services/freeipa/transport.ts +116 -0
  268. package/src/server/services/freeipa/util.ts +59 -0
  269. package/src/server/services/geo.ts +198 -0
  270. package/src/server/services/index.ts +48 -0
  271. package/src/server/services/pagination.ts +22 -0
  272. package/src/server/services/services.ts +13 -0
  273. package/src/server/time.ts +33 -0
  274. package/src/services/account-category-policy.ts +51 -0
  275. package/src/services/account-lifecycle/audit.ts +41 -0
  276. package/src/services/account-lifecycle/index.ts +940 -0
  277. package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
  278. package/src/services/account-lifecycle/notification-sender.ts +13 -0
  279. package/src/services/account-lifecycle/scheduler.ts +506 -0
  280. package/src/services/account-model.ts +22 -0
  281. package/src/services/accounts/app.ts +1671 -0
  282. package/src/services/accounts/authz.ts +44 -0
  283. package/src/services/accounts/avatar.ts +120 -0
  284. package/src/services/accounts/base-group.ts +11 -0
  285. package/src/services/accounts/base-user.ts +46 -0
  286. package/src/services/accounts/duplicate-emails.ts +82 -0
  287. package/src/services/accounts/entities.ts +622 -0
  288. package/src/services/accounts/group-sql.ts +106 -0
  289. package/src/services/accounts/groups.ts +272 -0
  290. package/src/services/accounts/index.ts +14 -0
  291. package/src/services/accounts/ipa-data.ts +66 -0
  292. package/src/services/accounts/lifecycle.ts +2 -0
  293. package/src/services/accounts/local-groups.ts +499 -0
  294. package/src/services/accounts/model.ts +129 -0
  295. package/src/services/accounts/notification-sender.ts +31 -0
  296. package/src/services/accounts/posix.ts +386 -0
  297. package/src/services/accounts/request-policy.ts +11 -0
  298. package/src/services/accounts/switching.ts +126 -0
  299. package/src/services/accounts/users.ts +745 -0
  300. package/src/services/announcements/index.ts +224 -0
  301. package/src/services/app-approval.ts +498 -0
  302. package/src/services/audit/index.ts +438 -0
  303. package/src/services/auth-flows/index.ts +15 -0
  304. package/src/services/auth-flows/ipa.ts +197 -0
  305. package/src/services/auth-flows/magic-link.ts +185 -0
  306. package/src/services/auth-flows/notification-sender.ts +10 -0
  307. package/src/services/auth-flows/password-reset.ts +268 -0
  308. package/src/services/auth-flows/proxy-return.ts +49 -0
  309. package/src/services/document-extraction.ts +148 -0
  310. package/src/services/freeipa-config.ts +179 -0
  311. package/src/services/gateway.ts +186 -0
  312. package/src/services/identity/constants.ts +18 -0
  313. package/src/services/identity/index.ts +52 -0
  314. package/src/services/identity/invocation-actor.ts +120 -0
  315. package/src/services/identity/invocation-authority.ts +53 -0
  316. package/src/services/identity/invocation-operations.ts +15 -0
  317. package/src/services/identity/invocation-token.ts +276 -0
  318. package/src/services/identity/key-config.ts +41 -0
  319. package/src/services/identity/key-ring.ts +530 -0
  320. package/src/services/identity/metrics.ts +29 -0
  321. package/src/services/identity/routes.ts +18 -0
  322. package/src/services/identity/runtime-config.ts +88 -0
  323. package/src/services/identity/session-token.ts +152 -0
  324. package/src/services/identity/signer-refresh.ts +44 -0
  325. package/src/services/identity/workload-auth.ts +64 -0
  326. package/src/services/index.ts +187 -0
  327. package/src/services/ipa/auth.ts +216 -0
  328. package/src/services/ipa/connection.ts +57 -0
  329. package/src/services/ipa/effective-groups.ts +70 -0
  330. package/src/services/ipa/groups.ts +722 -0
  331. package/src/services/ipa/guard.ts +17 -0
  332. package/src/services/ipa/index.ts +17 -0
  333. package/src/services/ipa/posix.ts +19 -0
  334. package/src/services/ipa/profile.ts +132 -0
  335. package/src/services/ipa/runtime.ts +28 -0
  336. package/src/services/ipa/search.ts +154 -0
  337. package/src/services/ipa/service-account.ts +15 -0
  338. package/src/services/ipa/sync-planning.ts +100 -0
  339. package/src/services/ipa/sync.ts +862 -0
  340. package/src/services/ipa/users.ts +806 -0
  341. package/src/services/legal-consent.ts +76 -0
  342. package/src/services/logging/index.ts +477 -0
  343. package/src/services/logging/redaction.ts +20 -0
  344. package/src/services/logging/trace.ts +1231 -0
  345. package/src/services/mandates/index.ts +855 -0
  346. package/src/services/mandates/policy.ts +85 -0
  347. package/src/services/network-security.ts +31 -0
  348. package/src/services/notifications/batches.ts +722 -0
  349. package/src/services/notifications/browser.ts +252 -0
  350. package/src/services/notifications/catalog.ts +113 -0
  351. package/src/services/notifications/channels.ts +104 -0
  352. package/src/services/notifications/dispatcher.ts +139 -0
  353. package/src/services/notifications/email.ts +118 -0
  354. package/src/services/notifications/index.ts +633 -0
  355. package/src/services/notifications/live.ts +50 -0
  356. package/src/services/notifications/observability.ts +407 -0
  357. package/src/services/notifications/platform.ts +421 -0
  358. package/src/services/notifications/runtime.ts +120 -0
  359. package/src/services/notifications/user.ts +248 -0
  360. package/src/services/notifications/web-push-transport.ts +135 -0
  361. package/src/services/oauth-tokens.ts +209 -0
  362. package/src/services/outbox.ts +187 -0
  363. package/src/services/pdf/gotenberg.ts +290 -0
  364. package/src/services/pdf/index.ts +43 -0
  365. package/src/services/pdf/markdown.ts +209 -0
  366. package/src/services/pdf/template-preview.ts +122 -0
  367. package/src/services/postgres.ts +76 -0
  368. package/src/services/providers/index.ts +27 -0
  369. package/src/services/providers/local/auth.ts +51 -0
  370. package/src/services/providers/local/index.ts +4 -0
  371. package/src/services/providers/local/users.ts +263 -0
  372. package/src/services/rail-preferences.ts +27 -0
  373. package/src/services/runtime-lifecycle.ts +191 -0
  374. package/src/services/secrets.ts +10 -0
  375. package/src/services/service-account-credentials.ts +891 -0
  376. package/src/services/service-accounts.ts +184 -0
  377. package/src/services/session/index.ts +236 -0
  378. package/src/services/session/test-fixture.ts +18 -0
  379. package/src/services/session/user.ts +144 -0
  380. package/src/services/settings/api.ts +61 -0
  381. package/src/services/settings/app.ts +86 -0
  382. package/src/services/settings/core-settings.ts +796 -0
  383. package/src/services/settings/crypto.ts +69 -0
  384. package/src/services/settings/defaults.ts +374 -0
  385. package/src/services/settings/index.ts +215 -0
  386. package/src/services/settings/namespace.ts +9 -0
  387. package/src/services/settings/snapshot.ts +49 -0
  388. package/src/services/settings/store.ts +241 -0
  389. package/src/services/settings/templates.ts +4 -0
  390. package/src/services/sync-ops.ts +403 -0
  391. package/src/services/topic-cursor.ts +22 -0
  392. package/src/services/weather/forecast.ts +323 -0
  393. package/src/services/weather/geo.ts +119 -0
  394. package/src/services/weather/index.ts +67 -0
  395. package/src/services/weather/location.ts +24 -0
  396. package/src/services/weather/locations.ts +133 -0
  397. package/src/services/weather/migrate.ts +45 -0
  398. package/src/services/weather/settings.ts +66 -0
  399. package/src/services/weather/short-id.ts +57 -0
  400. package/src/services/weather/types.ts +61 -0
  401. package/src/services/weather/ui.ts +50 -0
  402. package/src/services/webauthn.ts +385 -0
  403. package/src/shared/account-action-notice.ts +70 -0
  404. package/src/shared/account-display.ts +15 -0
  405. package/src/shared/account-session.ts +15 -0
  406. package/src/shared/ai-platform-prompt.ts +134 -0
  407. package/src/shared/ai-usage.ts +49 -0
  408. package/src/shared/app-presentation.ts +48 -0
  409. package/src/shared/app-url.ts +12 -0
  410. package/src/shared/branding.ts +14 -0
  411. package/src/shared/capability-messages.ts +70 -0
  412. package/src/shared/email-html.ts +54 -0
  413. package/src/shared/format.ts +91 -0
  414. package/src/shared/help.ts +68 -0
  415. package/src/shared/icons.ts +400 -0
  416. package/src/shared/index.ts +54 -0
  417. package/src/shared/locale.ts +48 -0
  418. package/src/shared/login-method.ts +44 -0
  419. package/src/shared/markdown/client.ts +130 -0
  420. package/src/shared/markdown/extensions/code.ts +105 -0
  421. package/src/shared/markdown/extensions/guided-help.ts +100 -0
  422. package/src/shared/markdown/extensions/images.ts +79 -0
  423. package/src/shared/markdown/extensions/info-blocks.ts +97 -0
  424. package/src/shared/markdown/extensions/katex.ts +120 -0
  425. package/src/shared/markdown/extensions/links.ts +43 -0
  426. package/src/shared/markdown/extensions/mark.ts +48 -0
  427. package/src/shared/markdown/extensions/sub-sup.ts +60 -0
  428. package/src/shared/markdown/extensions/tables.ts +109 -0
  429. package/src/shared/markdown/extensions/task-list.ts +53 -0
  430. package/src/shared/markdown/formula.ts +1187 -0
  431. package/src/shared/markdown/index.ts +193 -0
  432. package/src/shared/markdown/shared.ts +36 -0
  433. package/src/shared/mock-cover.ts +130 -0
  434. package/src/shared/network-address.ts +106 -0
  435. package/src/shared/redirect.ts +56 -0
  436. package/src/shared/template-rendering.ts +140 -0
  437. package/src/shared/theme.ts +68 -0
  438. package/src/shared/time.ts +13 -0
  439. package/src/ssr/AdminLayout.tsx +53 -0
  440. package/src/ssr/AdminSidebar.tsx +50 -0
  441. package/src/ssr/AppLaunchpad.island.tsx +236 -0
  442. package/src/ssr/AppWorkspaceController.island.tsx +21 -0
  443. package/src/ssr/BrowserNotifications.island.tsx +135 -0
  444. package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
  445. package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
  446. package/src/ssr/GlobalSearchDialog.tsx +35 -0
  447. package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
  448. package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
  449. package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
  450. package/src/ssr/Layout.render.test.tsx +148 -0
  451. package/src/ssr/Layout.tsx +292 -0
  452. package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
  453. package/src/ssr/LayoutFooter.tsx +22 -0
  454. package/src/ssr/LayoutHeader.tsx +81 -0
  455. package/src/ssr/LayoutHelp.tsx +869 -0
  456. package/src/ssr/LayoutPreferences.island.tsx +36 -0
  457. package/src/ssr/LayoutRail.tsx +58 -0
  458. package/src/ssr/MinimalLayout.tsx +46 -0
  459. package/src/ssr/PageError.tsx +29 -0
  460. package/src/ssr/ProfilePreferences.island.tsx +92 -0
  461. package/src/ssr/RailApps.island.tsx +61 -0
  462. package/src/ssr/RailEditor.tsx +293 -0
  463. package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
  464. package/src/ssr/TimezoneCookie.island.tsx +23 -0
  465. package/src/ssr/admin-active-link.ts +47 -0
  466. package/src/ssr/admin-navigation.ts +78 -0
  467. package/src/ssr/app-appearance.ts +23 -0
  468. package/src/ssr/app-navigation.ts +41 -0
  469. package/src/ssr/help-messages.ts +104 -0
  470. package/src/ssr/index.ts +14 -0
  471. package/src/ssr/islands/SearchBar.island.tsx +80 -0
  472. package/src/ssr/islands/index.ts +16 -0
  473. package/src/ssr/layout-context.ts +17 -0
  474. package/src/ssr/layout-help-markdown.ts +9 -0
  475. package/src/ssr/layout-help-navigation.ts +24 -0
  476. package/src/ssr/layout-help-url.ts +5 -0
  477. package/src/ssr/layout-runtime.ts +22 -0
  478. package/src/ssr/page-error-messages.ts +25 -0
  479. package/src/ssr/platform-messages.ts +135 -0
  480. package/src/ssr/preference-controller.ts +44 -0
  481. package/src/ssr/profile-preferences-messages.ts +29 -0
  482. package/src/ssr/rail-context.ts +21 -0
  483. package/src/ssr/rail-messages.ts +66 -0
  484. package/src/ssr/rail-navigation.ts +60 -0
  485. package/src/ssr/runtime.ts +31 -0
  486. package/src/ssr/url-filter.ts +127 -0
  487. package/src/styles/base-popover.css +36 -0
  488. package/src/styles/effects.css +851 -0
  489. package/src/styles/global.css +268 -0
  490. package/src/styles/input.css +58 -0
  491. package/src/styles/tokens.css +230 -0
  492. package/src/styles/utilities-buttons.css +405 -0
  493. package/src/styles/utilities-data.css +33 -0
  494. package/src/styles/utilities-feedback.css +232 -0
  495. package/src/styles/utilities-layout.css +603 -0
  496. package/src/styles/utilities-markdown-editor.css +525 -0
  497. package/src/styles/utilities-markdown-table.css +234 -0
  498. package/src/styles/utilities-navigation.css +1054 -0
  499. package/src/types/ambient.d.ts +24 -0
  500. package/src/workflows/ai/index.ts +17 -0
  501. package/src/workflows/ai/runtime.ts +286 -0
  502. package/src/workflows/ai/store.ts +314 -0
  503. package/src/workflows/ai/types.ts +111 -0
  504. package/src/workflows/ai-actions.ts +269 -0
  505. package/src/workflows/authoring.ts +88 -0
  506. package/src/workflows/builtins.ts +193 -0
  507. package/src/workflows/contracts.ts +280 -0
  508. package/src/workflows/definition.ts +334 -0
  509. package/src/workflows/editor.ts +113 -0
  510. package/src/workflows/index.ts +71 -0
  511. package/src/workflows/language/binder.ts +45 -0
  512. package/src/workflows/language/canonical.ts +29 -0
  513. package/src/workflows/language/compiler.ts +396 -0
  514. package/src/workflows/language/expressions.ts +34 -0
  515. package/src/workflows/language/index.ts +20 -0
  516. package/src/workflows/language/references.ts +55 -0
  517. package/src/workflows/language/schema.ts +134 -0
  518. package/src/workflows/language/strict-yaml.ts +131 -0
  519. package/src/workflows/manifest.ts +24 -0
  520. package/src/workflows/module.ts +12 -0
  521. package/src/workflows/runtime/coordinator.ts +115 -0
  522. package/src/workflows/runtime/executor.ts +1003 -0
  523. package/src/workflows/runtime/index.ts +60 -0
  524. package/src/workflows/runtime/instant.ts +8 -0
  525. package/src/workflows/runtime/ports.ts +213 -0
  526. package/src/workflows/runtime/schedule.ts +203 -0
  527. package/src/workflows/runtime/trigger.ts +37 -0
  528. package/src/workflows/runtime/wake.ts +13 -0
  529. package/src/workflows/store/actions.ts +438 -0
  530. package/src/workflows/store/budget.ts +145 -0
  531. package/src/workflows/store/definitions.ts +345 -0
  532. package/src/workflows/store/events.ts +383 -0
  533. package/src/workflows/store/index.ts +107 -0
  534. package/src/workflows/store/observability.ts +594 -0
  535. package/src/workflows/store/runs.ts +975 -0
  536. package/src/workflows/store/transaction.ts +23 -0
  537. package/src/workflows/store/worker.ts +439 -0
  538. package/src/workflows/testing/conformance.ts +72 -0
  539. package/src/workflows/testing/index.ts +11 -0
  540. package/src/workflows/testing/process-fixtures.ts +275 -0
@@ -0,0 +1,235 @@
1
+ import type { output, ZodType } from "zod";
2
+ import { canonicalLocale, localeFallbackChain } from "../shared/locale";
3
+
4
+ export type NotificationRecipientKind = "user" | "email";
5
+
6
+ /** Deployment channel packages extend this registry through module augmentation. */
7
+ export interface NotificationChannelRegistry {
8
+ email: true;
9
+ browser: true;
10
+ }
11
+
12
+ export type NotificationChannelId = Extract<keyof NotificationChannelRegistry, string>;
13
+
14
+ const NOTIFICATION_TARGET_ORIGIN = "https://cloud.invalid";
15
+
16
+ export const isSafeNotificationTargetHref = (value: string): value is `/${string}` => {
17
+ if (!value.startsWith("/") || value.startsWith("//") || /[\\\u0000-\u001f\u007f]/.test(value)) return false;
18
+ try {
19
+ const target = new URL(value, NOTIFICATION_TARGET_ORIGIN);
20
+ return target.origin === NOTIFICATION_TARGET_ORIGIN && `${target.pathname}${target.search}${target.hash}` === value;
21
+ } catch {
22
+ return false;
23
+ }
24
+ };
25
+
26
+ export const validateNotificationTargetHref = (value: string): `/${string}` => {
27
+ if (!isSafeNotificationTargetHref(value)) throw new Error("Notification targetHref must be a canonical same-origin absolute path");
28
+ return value;
29
+ };
30
+
31
+ export type NotificationPresentation = {
32
+ title: string;
33
+ body?: string;
34
+ /** Same-origin absolute path. Validated again before persistence and navigation. */
35
+ targetHref?: `/${string}`;
36
+ };
37
+
38
+ export type EmailNotificationPresentation = {
39
+ subject: string;
40
+ content?: string;
41
+ rawHtml?: string;
42
+ };
43
+
44
+ export type NotificationRenderContext = {
45
+ /** Canonical locale selected by the sender for this rendered notification. */
46
+ locale: string;
47
+ };
48
+
49
+ export type NotificationDeliveryPolicy = {
50
+ recommended?: readonly NotificationChannelId[];
51
+ required?: readonly NotificationChannelId[];
52
+ };
53
+
54
+ export type NotificationDefinitionPresentationTranslation = {
55
+ label?: string;
56
+ description?: string;
57
+ };
58
+
59
+ export type NotificationDefinitionPresentationCatalog = {
60
+ /** Locale of the complete `label` and `description` declaration. */
61
+ baseLocale: string;
62
+ /** Partial presentation overlays with exact -> ancestor -> base fallback. */
63
+ translations: Readonly<Record<string, NotificationDefinitionPresentationTranslation>>;
64
+ };
65
+
66
+ export type NotificationDefinitionInput<R extends NotificationRecipientKind, S extends ZodType> = {
67
+ recipient: R;
68
+ label: string;
69
+ description: string;
70
+ presentation?: NotificationDefinitionPresentationCatalog;
71
+ data: S;
72
+ delivery?: NotificationDeliveryPolicy;
73
+ render: (data: output<S>, context: NotificationRenderContext) => NotificationPresentation | Promise<NotificationPresentation>;
74
+ email?: (data: output<S>, context: NotificationRenderContext) => EmailNotificationPresentation | Promise<EmailNotificationPresentation>;
75
+ };
76
+
77
+ const notificationDefinitionMarker = Symbol("cloud.notification-definition");
78
+
79
+ export type NotificationDefinition<R extends NotificationRecipientKind = NotificationRecipientKind, S extends ZodType = ZodType> = Readonly<
80
+ NotificationDefinitionInput<R, S> & {
81
+ readonly [notificationDefinitionMarker]: true;
82
+ }
83
+ >;
84
+
85
+ type AnyNotificationDefinition = Readonly<{
86
+ recipient: NotificationRecipientKind;
87
+ label: string;
88
+ description: string;
89
+ presentation?: NotificationDefinitionPresentationCatalog;
90
+ data: ZodType;
91
+ delivery?: NotificationDeliveryPolicy;
92
+ render: (...args: never[]) => NotificationPresentation | Promise<NotificationPresentation>;
93
+ email?: (...args: never[]) => EmailNotificationPresentation | Promise<EmailNotificationPresentation>;
94
+ readonly [notificationDefinitionMarker]: true;
95
+ }>;
96
+
97
+ export type NotificationDefinitionMap = Record<string, AnyNotificationDefinition>;
98
+
99
+ export type BoundNotificationDefinition<
100
+ AppId extends string = string,
101
+ Key extends string = string,
102
+ R extends NotificationRecipientKind = NotificationRecipientKind,
103
+ S extends ZodType = ZodType,
104
+ > = NotificationDefinition<R, S> & {
105
+ readonly appId: AppId;
106
+ readonly key: Key;
107
+ readonly id: `${AppId}.${Key}`;
108
+ };
109
+
110
+ export type AnyBoundNotificationDefinition = AnyNotificationDefinition & {
111
+ readonly appId: string;
112
+ readonly key: string;
113
+ readonly id: `${string}.${string}`;
114
+ };
115
+
116
+ export type BoundNotificationMap<AppId extends string, N extends NotificationDefinitionMap> = {
117
+ readonly [K in keyof N]: N[K] extends NotificationDefinition<infer R, infer S>
118
+ ? BoundNotificationDefinition<AppId, Extract<K, string>, R, S>
119
+ : never;
120
+ };
121
+
122
+ export type NotificationRecipient<R extends NotificationRecipientKind> = R extends "user" ? { userId: string } : { email: string };
123
+
124
+ export type NotificationSendInput<D extends AnyBoundNotificationDefinition> =
125
+ D extends BoundNotificationDefinition<string, string, infer R, infer S>
126
+ ? {
127
+ recipient: NotificationRecipient<R>;
128
+ data: output<S>;
129
+ idempotencyKey: string;
130
+ sentBy?: string;
131
+ /** Locale for final human-facing notification and email content. */
132
+ locale?: string;
133
+ }
134
+ : never;
135
+
136
+ const hasDuplicates = (values: readonly string[]): boolean => new Set(values).size !== values.length;
137
+
138
+ const requiredPresentationText = (value: string, field: string): string => {
139
+ const normalized = value.trim();
140
+ if (!normalized) throw new Error(`Notification presentation ${field} is required`);
141
+ return normalized;
142
+ };
143
+
144
+ const compileDefinitionPresentation = (
145
+ catalog: NotificationDefinitionPresentationCatalog | undefined,
146
+ ): NotificationDefinitionPresentationCatalog | undefined => {
147
+ if (!catalog) return undefined;
148
+ const baseLocale = canonicalLocale(catalog.baseLocale);
149
+ if (!baseLocale) throw new Error("Notification presentation baseLocale must be a valid BCP 47 locale");
150
+
151
+ const translations: Record<string, NotificationDefinitionPresentationTranslation> = {};
152
+ for (const [locale, translation] of Object.entries(catalog.translations)) {
153
+ const canonical = canonicalLocale(locale);
154
+ if (!canonical) throw new Error(`Notification presentation locale ${JSON.stringify(locale)} is not a valid BCP 47 locale`);
155
+ if (canonical === baseLocale) throw new Error(`Notification presentation translations must not repeat base locale ${baseLocale}`);
156
+ if (translations[canonical]) throw new Error(`Notification presentation locale ${canonical} is duplicated after canonicalization`);
157
+ translations[canonical] = Object.freeze({
158
+ ...(translation.label !== undefined ? { label: requiredPresentationText(translation.label, `${canonical}.label`) } : {}),
159
+ ...(translation.description !== undefined
160
+ ? { description: requiredPresentationText(translation.description, `${canonical}.description`) }
161
+ : {}),
162
+ });
163
+ }
164
+ return Object.freeze({ baseLocale, translations: Object.freeze(translations) });
165
+ };
166
+
167
+ export const resolveNotificationDefinitionPresentation = (
168
+ definition: {
169
+ label: string;
170
+ description: string;
171
+ presentation?: NotificationDefinitionPresentationCatalog | null;
172
+ },
173
+ requestedLocale?: string | null,
174
+ ): { label: string; description: string } => {
175
+ const catalog = definition.presentation;
176
+ if (!catalog || !requestedLocale) return { label: definition.label, description: definition.description };
177
+ const translations = new Map(Object.entries(catalog.translations));
178
+ return localeFallbackChain(requestedLocale, catalog.baseLocale)
179
+ .filter((locale) => locale !== catalog.baseLocale)
180
+ .reverse()
181
+ .reduce((resolved, locale) => ({ ...resolved, ...translations.get(locale) }), {
182
+ label: definition.label,
183
+ description: definition.description,
184
+ });
185
+ };
186
+
187
+ export const notification = <const R extends NotificationRecipientKind, const S extends ZodType>(
188
+ input: NotificationDefinitionInput<R, S>,
189
+ ): NotificationDefinition<R, S> => {
190
+ const label = input.label.trim();
191
+ const description = input.description.trim();
192
+ if (!label) throw new Error("Notification label is required");
193
+ if (!description) throw new Error(`Notification "${label}" description is required`);
194
+
195
+ const recommended = [...(input.delivery?.recommended ?? [])];
196
+ const required = [...(input.delivery?.required ?? [])];
197
+ if (hasDuplicates(recommended) || hasDuplicates(required)) {
198
+ throw new Error(`Notification "${label}" contains duplicate delivery channels`);
199
+ }
200
+ const overlappingChannel = recommended.find((channel) => required.includes(channel));
201
+ if (overlappingChannel) {
202
+ throw new Error(`Notification "${label}" cannot recommend and require the "${overlappingChannel}" channel`);
203
+ }
204
+ if (input.recipient === "email" && !required.includes("email")) {
205
+ throw new Error(`Email-recipient notification "${label}" must require the email channel`);
206
+ }
207
+ const presentation = compileDefinitionPresentation(input.presentation);
208
+
209
+ const definition: NotificationDefinition<R, S> = {
210
+ ...input,
211
+ label,
212
+ description,
213
+ ...(presentation ? { presentation } : {}),
214
+ delivery: Object.freeze({ recommended: Object.freeze(recommended), required: Object.freeze(required) }),
215
+ [notificationDefinitionMarker]: true,
216
+ };
217
+ return Object.freeze(definition);
218
+ };
219
+
220
+ export const bindNotificationDefinitions = <const AppId extends string, const N extends NotificationDefinitionMap>(
221
+ appId: AppId,
222
+ definitions?: N,
223
+ ): BoundNotificationMap<AppId, N> => {
224
+ const result: Record<string, AnyBoundNotificationDefinition> = {};
225
+ for (const [key, definition] of Object.entries(definitions ?? {})) {
226
+ if (!/^[a-z][a-zA-Z0-9]*$/.test(key)) {
227
+ throw new Error(`Notification key "${key}" must be lower camelCase`);
228
+ }
229
+ result[key] = Object.freeze({ ...definition, appId, key, id: `${appId}.${key}` });
230
+ }
231
+
232
+ // Object iteration erases each heterogeneous definition's schema. The map
233
+ // is rebuilt one-to-one without changing values, so callers safely retain N.
234
+ return result as BoundNotificationMap<AppId, N>;
235
+ };
@@ -0,0 +1,69 @@
1
+ import { z } from "zod";
2
+
3
+ // Match the platform's INTEGER storage. Zero is never allocated to a Cloud account.
4
+ export const PosixNumberSchema = z.number().int().min(1).max(2_147_483_647);
5
+ export const PosixPathSchema = z
6
+ .string()
7
+ .min(2)
8
+ .max(4095)
9
+ .refine(
10
+ (value) =>
11
+ value.startsWith("/") &&
12
+ !/[\s:\x00-\x1f\x7f]/.test(value) &&
13
+ !value
14
+ .slice(1)
15
+ .split("/")
16
+ .some((part) => part === "." || part === ".." || part === ""),
17
+ "Use an absolute path without whitespace, colons or relative segments",
18
+ );
19
+
20
+ export const PosixIdentitySchema = z.object({
21
+ userId: z.uuid(),
22
+ managedBy: z.enum(["local", "ipa"]),
23
+ uidNumber: PosixNumberSchema.nullable(),
24
+ primaryGidNumber: PosixNumberSchema.nullable(),
25
+ homeDirectory: z.string().nullable(),
26
+ loginShell: z.string().nullable(),
27
+ });
28
+ export type PosixIdentity = z.infer<typeof PosixIdentitySchema>;
29
+
30
+ export const LinuxIdentityConfigurationSchema = z
31
+ .object({
32
+ enabled: z.boolean(),
33
+ rangeStart: z.number().int().min(0).max(2_147_483_647),
34
+ rangeEnd: z.number().int().min(0).max(2_147_483_647),
35
+ homeTemplate: z.string().min(1).max(4095),
36
+ loginShell: PosixPathSchema,
37
+ })
38
+ .strict()
39
+ .superRefine((value, ctx) => {
40
+ if (value.enabled && (value.rangeStart < 1000 || value.rangeEnd < value.rangeStart)) {
41
+ ctx.addIssue({
42
+ code: "custom",
43
+ path: ["rangeStart"],
44
+ message: "Choose a reserved range above system accounts (at least 1000), with end >= start",
45
+ });
46
+ }
47
+ if (
48
+ !value.homeTemplate.includes("{username}") ||
49
+ !PosixPathSchema.safeParse(value.homeTemplate.replaceAll("{username}", "user")).success ||
50
+ /[{}]/.test(value.homeTemplate.replaceAll("{username}", "user"))
51
+ ) {
52
+ ctx.addIssue({
53
+ code: "custom",
54
+ path: ["homeTemplate"],
55
+ message: "Use an absolute home path containing {username}; no other placeholders are supported",
56
+ });
57
+ }
58
+ });
59
+ export type LinuxIdentityConfiguration = z.infer<typeof LinuxIdentityConfigurationSchema>;
60
+ export const DEFAULT_LINUX_IDENTITY_CONFIGURATION: LinuxIdentityConfiguration = {
61
+ enabled: false,
62
+ rangeStart: 0,
63
+ rangeEnd: 0,
64
+ homeTemplate: "/home/{username}",
65
+ loginShell: "/bin/bash",
66
+ };
67
+
68
+ export const PosixOverridesSchema = z.object({ homeDirectory: PosixPathSchema, loginShell: PosixPathSchema }).strict();
69
+ export const isPosixName = (value: string): boolean => /^[a-z_][a-z0-9_-]{0,31}$/.test(value);
@@ -0,0 +1,85 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Schemas for account profile / password / request inputs. Live in core
5
+ * contracts so both the core `/api/me` router and the accounts admin app can
6
+ * consume the same shapes without cross-app imports.
7
+ */
8
+
9
+ const SSH_PUBLIC_KEY_PATTERN = /^(ssh-(rsa|ed25519|dss)|ecdsa-sha2-nistp(256|384|521))\s+[A-Za-z0-9+/=]+(?:\s+.+)?$/;
10
+
11
+ /**
12
+ * IPA-only self-service fields. Exposed so an admin schema can compose the
13
+ * same inner shape without extending the refined self-service schema.
14
+ */
15
+ export const IpaProfileFieldsSchema = z.object({
16
+ phone: z.string().optional(),
17
+ address: z
18
+ .object({
19
+ street: z.string().optional(),
20
+ postalCode: z.string().optional(),
21
+ city: z.string().optional(),
22
+ state: z.string().optional(),
23
+ })
24
+ .optional(),
25
+ sshPublicKeys: z.array(z.string().regex(SSH_PUBLIC_KEY_PATTERN, "Invalid SSH public key format")).optional(),
26
+ });
27
+
28
+ export const UpdateProfileSchema = z
29
+ .object({
30
+ givenname: z.string().min(1).optional(),
31
+ sn: z.string().min(1).optional(),
32
+ displayName: z.string().min(1).optional(),
33
+ ipa: IpaProfileFieldsSchema.optional(),
34
+ })
35
+ .refine((data) => data.givenname !== undefined || data.sn !== undefined || data.displayName !== undefined || data.ipa !== undefined, {
36
+ message: "At least one profile field must be provided",
37
+ });
38
+
39
+ export const MAX_AVATAR_DATA_URL_LENGTH = 65_536;
40
+ export const MAX_AVATAR_BYTES = 48 * 1024;
41
+
42
+ export const AvatarDataUrlSchema = z
43
+ .string()
44
+ .max(MAX_AVATAR_DATA_URL_LENGTH)
45
+ .regex(/^data:image\/(?:png|jpeg|webp);base64,[A-Za-z0-9+/]+={0,2}$/, "Avatar must be a PNG, JPEG, or WebP data URL");
46
+
47
+ export const UpdateAvatarSchema = z.object({
48
+ dataUrl: AvatarDataUrlSchema,
49
+ });
50
+ export type UpdateAvatar = z.infer<typeof UpdateAvatarSchema>;
51
+
52
+ export const UpdateAvatarResponseSchema = z.object({
53
+ message: z.string(),
54
+ avatarHash: z.string(),
55
+ });
56
+ export type UpdateAvatarResponse = z.infer<typeof UpdateAvatarResponseSchema>;
57
+
58
+ export const ChangePasswordSchema = z
59
+ .object({
60
+ currentPassword: z.string().min(1),
61
+ newPassword: z.string().min(8),
62
+ confirmPassword: z.string().min(1),
63
+ })
64
+ .refine((data) => data.newPassword === data.confirmPassword, {
65
+ message: "Passwords do not match",
66
+ path: ["confirmPassword"],
67
+ });
68
+
69
+ export const ChangeExpiredPasswordSchema = z
70
+ .object({
71
+ username: z.string().min(1),
72
+ currentPassword: z.string().min(1),
73
+ newPassword: z.string().min(8),
74
+ confirmPassword: z.string().min(1),
75
+ })
76
+ .refine((data) => data.newPassword === data.confirmPassword, {
77
+ message: "Passwords do not match",
78
+ path: ["confirmPassword"],
79
+ });
80
+
81
+ export const CreateAccountRequestSchema = z.object({
82
+ phone: z.string().optional().describe("Optional phone number for the request"),
83
+ comment: z.string().optional().describe("Why do you need a FreeIPA account?"),
84
+ acceptedAgb: z.literal(true).describe("Must accept terms of service"),
85
+ });
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+
3
+ /** Rail settings travel with each SSR page: budget the complete snapshot at 16 KiB. */
4
+ export const RAIL_PREFERENCES_MAX_BYTES = 16 * 1024;
5
+ const id = z.string().trim().min(1).max(120);
6
+
7
+ export const isRailShortcutHref = (value: string): boolean => {
8
+ if (/[\u0000-\u0020\u007f\\]/.test(value)) return false;
9
+ if (value.startsWith("/") && !value.startsWith("//")) return true;
10
+ try {
11
+ const url = new URL(value);
12
+ return (url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password;
13
+ } catch {
14
+ return false;
15
+ }
16
+ };
17
+
18
+ export const RailShortcutSchema = z.discriminatedUnion("kind", [
19
+ z.object({ id, kind: z.literal("app"), appId: id }).strict(),
20
+ z
21
+ .object({
22
+ id,
23
+ kind: z.literal("link"),
24
+ title: z.string().trim().min(1).max(80),
25
+ href: z.string().trim().min(1).max(2000).refine(isRailShortcutHref, "Use an absolute path or an HTTP(S) URL"),
26
+ icon: z
27
+ .string()
28
+ .regex(/^ti ti-[a-z0-9-]+$/)
29
+ .max(120),
30
+ })
31
+ .strict(),
32
+ ]);
33
+ export type RailShortcut = z.infer<typeof RailShortcutSchema>;
34
+
35
+ export const RailPreferencesSchema = z
36
+ .object({
37
+ revision: z.number().int().nonnegative().max(2_147_483_647),
38
+ visibility: z.record(id, z.boolean()),
39
+ shortcuts: z.array(RailShortcutSchema),
40
+ })
41
+ .strict()
42
+ .superRefine((value, ctx) => {
43
+ if (new TextEncoder().encode(JSON.stringify({ ...value, revision: 2_147_483_647 })).byteLength > RAIL_PREFERENCES_MAX_BYTES) {
44
+ ctx.addIssue({ code: "custom", message: "App bar settings exceed the 16 KiB page budget" });
45
+ }
46
+ if (new Set(value.shortcuts.map((entry) => entry.id)).size !== value.shortcuts.length) {
47
+ ctx.addIssue({ code: "custom", message: "Shortcut IDs must be unique" });
48
+ }
49
+ const apps = value.shortcuts.flatMap((entry) => (entry.kind === "app" ? [entry.appId] : []));
50
+ if (new Set(apps).size !== apps.length) ctx.addIssue({ code: "custom", message: "An app can only be pinned once" });
51
+ });
52
+ export type RailPreferences = z.infer<typeof RailPreferencesSchema>;
53
+ export const defaultRailPreferences = (): RailPreferences => ({ revision: 0, visibility: {}, shortcuts: [] });
@@ -0,0 +1,110 @@
1
+ import type { HelpManifest } from "../shared/help";
2
+ import type { AppAdminNavigationGroup, AppAppearance, AppAppearanceColor, AppPresentationCatalog } from "./app";
3
+ import type { CapabilityManifest, CapabilityPresentationCatalog } from "./capabilities";
4
+ import type { Role } from "./shared";
5
+ import type { DashboardWidgetPresentation } from "./widgets";
6
+
7
+ /**
8
+ * App-registry entry type. Populated internally by `defineApp()` + the
9
+ * heartbeat runtime. Values are validated when read because Redis may still
10
+ * contain records written by older or interrupted runtimes.
11
+ */
12
+
13
+ export type AppRegistryNav = {
14
+ href: string;
15
+ match?: string;
16
+ section: "primary" | "more" | "hidden";
17
+ requiresAuth?: boolean;
18
+ requiresRoles?: Role[];
19
+ adminHref?: string;
20
+ };
21
+
22
+ export type AppRegistryCapabilitySummary = {
23
+ protocolVersion: number;
24
+ manifestHash: string;
25
+ };
26
+
27
+ /** Validated Capability manifest joined to its matching live app lease. */
28
+ export type CapabilityRegistryEntry = {
29
+ appId: string;
30
+ appName: string;
31
+ appIcon: string;
32
+ appAccent?: AppAppearanceColor;
33
+ appDescription: string;
34
+ endpoint: string;
35
+ manifest: CapabilityManifest;
36
+ presentation?: CapabilityPresentationCatalog;
37
+ };
38
+
39
+ export type AppRegistryHelpSummary = HelpManifest;
40
+
41
+ export type HelpRegistryDocument = {
42
+ id: string;
43
+ title: string;
44
+ icon?: string;
45
+ description?: string;
46
+ order: number;
47
+ markdown: string;
48
+ /** Precomputed by the app. Older registry entries may omit it during rolling upgrades. */
49
+ searchText?: string;
50
+ };
51
+
52
+ /** Full Markdown lives separately so normal app discovery stays small. */
53
+ export type HelpRegistryEntry = {
54
+ appId: string;
55
+ appName: string;
56
+ appIcon: string;
57
+ manifestHash: string;
58
+ /** Base language that owns the complete logical article set. Absent on legacy registrations. */
59
+ baseLocale?: string;
60
+ /** Partial localized article variants stored in the same bounded logical registration. */
61
+ documentsByLocale?: Readonly<Record<string, readonly HelpRegistryDocument[]>>;
62
+ documents: readonly HelpRegistryDocument[];
63
+ };
64
+
65
+ export type AppRegistryLegalLink = {
66
+ label: string;
67
+ href: string;
68
+ icon?: string;
69
+ };
70
+
71
+ export type AppRegistryWidget = {
72
+ id: string;
73
+ /** Absolute path on the app's HTTP service, e.g. "/api/quotes/widget/random". */
74
+ path: string;
75
+ presentation?: DashboardWidgetPresentation;
76
+ };
77
+
78
+ export type AppRuntimeMetadata = {
79
+ release: string;
80
+ syncVersion: string;
81
+ };
82
+
83
+ export type AppRegistryEntry = {
84
+ id: string;
85
+ name: string;
86
+ icon: string;
87
+ description: string;
88
+ presentation?: AppPresentationCatalog;
89
+ appearance?: AppAppearance;
90
+ baseUrl: string;
91
+ /** Build metadata reported by the running app. Missing on older app releases. */
92
+ runtime?: AppRuntimeMetadata;
93
+ /** Process start time (epoch ms) reported by the running app; the registry cannot know it. */
94
+ startedAt?: number;
95
+ /**
96
+ * Top-level URL prefixes the gateway routes to this app. The gateway
97
+ * builds a prefix-trie from these strings, no derivation or heuristics.
98
+ */
99
+ routes: readonly string[];
100
+ nav?: AppRegistryNav;
101
+ adminNav?: AppAdminNavigationGroup[];
102
+ capabilities?: AppRegistryCapabilitySummary;
103
+ help?: AppRegistryHelpSummary;
104
+ legalLinks?: AppRegistryLegalLink[];
105
+ widgets?: AppRegistryWidget[];
106
+ /** Setting keys declared by this app. Used by admin tooling to avoid treating live app-owned settings as legacy. */
107
+ settingKeys?: readonly string[];
108
+ /** Gateway-relative URL where this app serves its OpenAPI JSON spec. */
109
+ openapi?: string;
110
+ };
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ import { type CloudResourceRef, CloudResourceRefSchema } from "./capabilities";
3
+
4
+ export const CLOUD_RESOURCE_CLIPBOARD_MIME_TYPE = "application/vnd.k2b.cloud-resource-ref+json";
5
+ export const CLOUD_RESOURCE_CLIPBOARD_WEB_FORMAT = `web ${CLOUD_RESOURCE_CLIPBOARD_MIME_TYPE}`;
6
+ export const CLOUD_RESOURCE_CLIPBOARD_MAX_BYTES = 4_096;
7
+
8
+ export const CloudResourceClipboardPayloadSchema = z
9
+ .object({
10
+ version: z.literal(1),
11
+ cloudUrl: z
12
+ .string()
13
+ .url()
14
+ .refine((value) => new URL(value).origin === value, "Expected the canonical Cloud origin"),
15
+ ref: CloudResourceRefSchema,
16
+ })
17
+ .strict();
18
+
19
+ export type CloudResourceClipboardPayload = z.infer<typeof CloudResourceClipboardPayloadSchema>;
20
+
21
+ export const serializeCloudResourceClipboard = (input: { cloudUrl: string; ref: CloudResourceRef }): string =>
22
+ JSON.stringify(CloudResourceClipboardPayloadSchema.parse({ version: 1, ...input }));
23
+
24
+ export const parseCloudResourceClipboard = (value: string, cloudUrl: string): CloudResourceRef | null => {
25
+ if (new TextEncoder().encode(value).byteLength > CLOUD_RESOURCE_CLIPBOARD_MAX_BYTES) return null;
26
+
27
+ try {
28
+ const payload = CloudResourceClipboardPayloadSchema.safeParse(JSON.parse(value));
29
+ return payload.success && payload.data.cloudUrl === cloudUrl ? payload.data.ref : null;
30
+ } catch {
31
+ return null;
32
+ }
33
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Type-level scaffolding for `defineApp({ settings: { ... } })`.
3
+ *
4
+ * These types let TypeScript derive a per-app `Settings` shape from the literal
5
+ * settings declaration in defineApp. The derived shape is:
6
+ *
7
+ * 1. **Flat** — `{ "files.filegate_url": string, "freeipa.enable": boolean, ... }`
8
+ * (used by the typed async API: `app.settings.get("files.filegate_url")`)
9
+ *
10
+ * 2. **Nested + readonly** — `{ files: { filegate_url: string }, freeipa: { enable: boolean } }`
11
+ * (used by the per-request snapshot exposed via `c.get("settings")`)
12
+ *
13
+ * No runtime in this file — pure TypeScript.
14
+ */
15
+ import type { SettingOption } from "../services/settings/defaults";
16
+
17
+ // ── Setting definition shape (what users write inside defineApp.settings) ───
18
+
19
+ type EnvResolver = () => unknown;
20
+
21
+ export type AppSettingPresentationTranslation = {
22
+ label?: string;
23
+ description?: string;
24
+ placeholder?: string;
25
+ options?: Readonly<Record<string, string>>;
26
+ };
27
+
28
+ export type AppSettingPresentation = {
29
+ /** Partial overlays; the complete fields use the application's presentation base locale. */
30
+ translations: Readonly<Record<string, AppSettingPresentationTranslation>>;
31
+ };
32
+
33
+ type CommonDef = {
34
+ label?: string;
35
+ description?: string;
36
+ presentation?: AppSettingPresentation;
37
+ envBootstrap?: EnvResolver;
38
+ envFallback?: EnvResolver;
39
+ };
40
+
41
+ type StringLikeKind = "string" | "text" | "email" | "url" | "secret" | "image" | "cron" | "timezone" | "template";
42
+
43
+ export type AppSettingDef =
44
+ | (CommonDef & { kind: StringLikeKind; default: string; placeholder?: string; templateVars?: readonly string[] })
45
+ | (CommonDef & { kind: "boolean"; default: boolean })
46
+ | (CommonDef & { kind: "number"; default: number; min?: number; max?: number; placeholder?: string })
47
+ | (CommonDef & { kind: "enum"; default: string; options: ReadonlyArray<SettingOption> })
48
+ | (CommonDef & { kind: "string_list"; default: readonly string[]; placeholder?: string })
49
+ | (CommonDef & { kind: "number_list"; default: readonly number[]; placeholder?: string });
50
+
51
+ /** A map of setting-key → definition. The key strings are dotted paths like "files.filegate_url". */
52
+ export type AppSettingsMap = Record<string, AppSettingDef>;
53
+
54
+ // ── Type-level transforms ───────────────────────────────────────────────────
55
+
56
+ /** Map a setting `kind` literal to the runtime value type returned by reads. */
57
+ export type KindToType<K extends string> = K extends "boolean"
58
+ ? boolean
59
+ : K extends "number"
60
+ ? number
61
+ : K extends "string_list"
62
+ ? string[]
63
+ : K extends "number_list"
64
+ ? number[]
65
+ : string;
66
+
67
+ /**
68
+ * Derive a flat `{ key: value }` map from a SettingsMap.
69
+ *
70
+ * Example:
71
+ * SettingsFlat<{ "app.name": { kind: "string"; default: "" } }>
72
+ * = { readonly "app.name": string }
73
+ */
74
+ export type SettingsFlat<S extends Record<string, { kind: string }>> = {
75
+ readonly [K in keyof S]: KindToType<S[K]["kind"]>;
76
+ };
77
+
78
+ /**
79
+ * Recursively un-flatten dotted-key maps into nested objects.
80
+ *
81
+ * Example:
82
+ * Unflatten<{ "files.filegate_url": string; "files.base_homes": string; "app.name": string }>
83
+ * = { readonly files: { readonly filegate_url: string; readonly base_homes: string }, readonly app: { readonly name: string } }
84
+ */
85
+ export type Unflatten<T extends Record<string, unknown>> = {
86
+ readonly [K in Extract<keyof T, string> as K extends `${infer Head}.${string}` ? Head : K]: K extends `${infer Head}.${string}`
87
+ ? Unflatten<{ [P in Extract<keyof T, `${Head}.${string}`> as P extends `${Head}.${infer R}` ? R : never]: T[P] }>
88
+ : T[K];
89
+ };
90
+
91
+ /**
92
+ * Final per-app Settings shape: nested, readonly, derived from the App type.
93
+ *
94
+ * Used by `AppContext<App>` to type `c.get("settings")` correctly per-app.
95
+ */
96
+ export type AppSettings<App> = App extends { readonly _settings: infer S extends Record<string, { kind: string }> }
97
+ ? Unflatten<SettingsFlat<S>>
98
+ : never;