@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,137 @@
1
+ import { getFreeIpaTls } from "./tls";
2
+ import { FreeIpaTransportError, isFreeIpaUpstreamStatus, readFreeIpaErrorBody, withFreeIpaResponse } from "./transport";
3
+
4
+ export type IpaRpcResult = {
5
+ result?: unknown;
6
+ count?: number;
7
+ truncated?: boolean;
8
+ summary?: string | null;
9
+ };
10
+
11
+ export type IpaRpcResponse = {
12
+ result: IpaRpcResult | null;
13
+ error: {
14
+ code: number;
15
+ message: string;
16
+ name: string;
17
+ kind: "invalid_response" | "rpc" | "session" | "upstream";
18
+ } | null;
19
+ id: number;
20
+ };
21
+
22
+ export const baseUrl = (url: string): string => `https://${url}`;
23
+
24
+ const isNoModificationError = (error: IpaRpcResponse["error"]): boolean =>
25
+ error?.code === 4202 && (error.message ?? "").toLowerCase().includes("no modifications to be performed");
26
+
27
+ export const call = async (config: {
28
+ url: string;
29
+ ipaSession: string;
30
+ method: string;
31
+ args?: unknown[];
32
+ options?: Record<string, unknown>;
33
+ signal?: AbortSignal;
34
+ }): Promise<IpaRpcResponse> => {
35
+ const tls = await getFreeIpaTls();
36
+ return withFreeIpaResponse(
37
+ `${baseUrl(config.url)}/ipa/session/json`,
38
+ {
39
+ method: "POST",
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ Referer: `${baseUrl(config.url)}/ipa`,
43
+ Accept: "application/json",
44
+ Cookie: `ipa_session=${config.ipaSession}`,
45
+ },
46
+ body: JSON.stringify({
47
+ method: config.method,
48
+ params: [config.args ?? [], { ...(config.options ?? {}), version: "2.251" }],
49
+ id: 0,
50
+ }),
51
+ tls,
52
+ },
53
+ async (res) => {
54
+ if (!res.ok || !res.headers.get("content-type")?.includes("json")) {
55
+ const body = await readFreeIpaErrorBody(res);
56
+ const text = body.text;
57
+ console.error("[freeipa:client] Non-JSON response", {
58
+ method: config.method,
59
+ status: res.status,
60
+ bodyTruncated: body.truncated,
61
+ });
62
+
63
+ if (res.status === 401 || text.includes("Invalid Authentication") || text.includes("GSSAPI Error")) {
64
+ return {
65
+ result: null,
66
+ error: {
67
+ code: 403,
68
+ message: "Your IPA session has expired or is invalid. Please log out and log in again to refresh your session.",
69
+ name: "SessionExpired",
70
+ kind: "session",
71
+ },
72
+ id: 0,
73
+ };
74
+ }
75
+
76
+ return {
77
+ result: null,
78
+ error: {
79
+ code: res.status,
80
+ message: "Non-JSON response from IPA",
81
+ name: "FetchError",
82
+ kind: isFreeIpaUpstreamStatus(res.status) ? "upstream" : "invalid_response",
83
+ },
84
+ id: 0,
85
+ };
86
+ }
87
+
88
+ const payload: unknown = await res.json().catch((error) => {
89
+ throw new FreeIpaTransportError("invalid_response", "FreeIPA returned invalid JSON", { cause: error });
90
+ });
91
+ if (typeof payload !== "object" || payload === null) {
92
+ throw new FreeIpaTransportError("invalid_response", "FreeIPA returned an invalid RPC response");
93
+ }
94
+ const response = payload as Partial<IpaRpcResponse>;
95
+ if (
96
+ !Object.hasOwn(response, "result") ||
97
+ !Object.hasOwn(response, "error") ||
98
+ typeof response.id !== "number" ||
99
+ !Number.isFinite(response.id) ||
100
+ (response.result === null) === (response.error === null) ||
101
+ (response.result !== null && (typeof response.result !== "object" || response.result === null || Array.isArray(response.result))) ||
102
+ (response.error !== null &&
103
+ response.error !== undefined &&
104
+ (typeof response.error !== "object" ||
105
+ response.error === null ||
106
+ typeof response.error.code !== "number" ||
107
+ typeof response.error.message !== "string" ||
108
+ typeof response.error.name !== "string"))
109
+ ) {
110
+ throw new FreeIpaTransportError("invalid_response", "FreeIPA returned an invalid RPC response");
111
+ }
112
+ const typedResponse = response as IpaRpcResponse;
113
+ if (typedResponse.error) {
114
+ if (isNoModificationError(typedResponse.error)) {
115
+ return {
116
+ result: {
117
+ result: null,
118
+ count: 0,
119
+ truncated: false,
120
+ summary: typedResponse.error.message,
121
+ },
122
+ error: null,
123
+ id: typedResponse.id,
124
+ };
125
+ }
126
+
127
+ console.error("[freeipa:client] RPC failed", {
128
+ method: config.method,
129
+ code: typedResponse.error.code,
130
+ message: typedResponse.error.message,
131
+ });
132
+ }
133
+ return typedResponse.error ? { ...typedResponse, error: { ...typedResponse.error, kind: "rpc" } } : typedResponse;
134
+ },
135
+ { signal: config.signal },
136
+ );
137
+ };
@@ -0,0 +1,9 @@
1
+ import * as client from "./client";
2
+ import * as session from "./session";
3
+ import * as util from "./util";
4
+
5
+ export const freeipa = {
6
+ client,
7
+ session,
8
+ util,
9
+ } as const;
@@ -0,0 +1,123 @@
1
+ import { createHash } from "node:crypto";
2
+ import { baseUrl, call } from "./client";
3
+ import { getFreeIpaTls, getFreeIpaTlsFingerprint } from "./tls";
4
+ import {
5
+ FreeIpaTransportError,
6
+ isFreeIpaUpstreamStatus,
7
+ readFreeIpaErrorBody,
8
+ upstreamFreeIpaError,
9
+ withFreeIpaResponse,
10
+ } from "./transport";
11
+
12
+ export type LoginResult = { status: "success"; session: string } | { status: "password_expired" } | { status: "failed" };
13
+
14
+ export const login = async (config: { url: string; username: string; password: string; signal?: AbortSignal }): Promise<LoginResult> => {
15
+ const tls = await getFreeIpaTls();
16
+ return withFreeIpaResponse(
17
+ `${baseUrl(config.url)}/ipa/session/login_password`,
18
+ {
19
+ method: "POST",
20
+ headers: {
21
+ "Content-Type": "application/x-www-form-urlencoded",
22
+ Referer: `${baseUrl(config.url)}/ipa`,
23
+ Accept: "text/plain",
24
+ },
25
+ body: new URLSearchParams({ user: config.username, password: config.password }),
26
+ redirect: "manual",
27
+ tls,
28
+ },
29
+ async (res): Promise<LoginResult> => {
30
+ const rejectionReason = res.headers.get("X-IPA-Rejection-Reason");
31
+ if (rejectionReason === "password-expired") {
32
+ await readFreeIpaErrorBody(res);
33
+ return { status: "password_expired" };
34
+ }
35
+
36
+ if (isFreeIpaUpstreamStatus(res.status)) {
37
+ await readFreeIpaErrorBody(res);
38
+ throw upstreamFreeIpaError(res.status);
39
+ }
40
+ if (!res.ok && res.status !== 303) {
41
+ await readFreeIpaErrorBody(res);
42
+ return { status: "failed" };
43
+ }
44
+
45
+ await res.body?.cancel().catch(() => undefined);
46
+ const cookies = res.headers.getSetCookie?.() ?? [];
47
+ for (const cookie of cookies) {
48
+ const match = cookie.match(/ipa_session=([^;]+)/);
49
+ if (match?.[1]) return { status: "success", session: match[1] };
50
+ }
51
+
52
+ const single = res.headers.get("set-cookie") ?? "";
53
+ const match = single.match(/ipa_session=([^;]+)/);
54
+ if (match?.[1]) return { status: "success", session: match[1] };
55
+ throw new FreeIpaTransportError("invalid_response", "FreeIPA login succeeded without a session cookie");
56
+ },
57
+ { signal: config.signal },
58
+ );
59
+ };
60
+
61
+ let svcSession: string | null = null;
62
+ let svcSessionPending: { key: string; promise: Promise<string> } | null = null;
63
+ let svcSessionKey: string | null = null;
64
+
65
+ const fingerprint = (value: string): string => createHash("sha256").update(value).digest("hex");
66
+
67
+ const openServiceSession = async (config: {
68
+ url: string;
69
+ serviceUser: string;
70
+ servicePassword: string;
71
+ signal?: AbortSignal;
72
+ }): Promise<string> => {
73
+ const result = await login({
74
+ url: config.url,
75
+ username: config.serviceUser,
76
+ password: config.servicePassword,
77
+ signal: config.signal,
78
+ });
79
+ if (result.status !== "success") {
80
+ console.error("[freeipa:session] Service account auth failed");
81
+ throw new Error("Failed to authenticate FreeIPA service account. Check freeipa.url/freeipa.service_user/freeipa.service_password.");
82
+ }
83
+ return result.session;
84
+ };
85
+
86
+ export const getServiceSession = async (config: {
87
+ url: string;
88
+ serviceUser: string;
89
+ servicePassword: string;
90
+ signal?: AbortSignal;
91
+ }): Promise<string> => {
92
+ // Include password and TLS fingerprints so credential or trust rotation
93
+ // cannot keep reusing a session opened under the previous configuration.
94
+ const currentKey = `${config.url}::${config.serviceUser}::${fingerprint(config.servicePassword)}::${await getFreeIpaTlsFingerprint()}`;
95
+ if (svcSessionKey !== currentKey) {
96
+ svcSession = null;
97
+ svcSessionKey = currentKey;
98
+ }
99
+
100
+ if (svcSession) {
101
+ const check = await call({ url: config.url, ipaSession: svcSession, method: "ping", args: [], signal: config.signal });
102
+ if (!check.error) return svcSession;
103
+ }
104
+ // An abortable caller must not share its signal with unrelated requests via
105
+ // the singleton in-flight login promise.
106
+ if (config.signal) {
107
+ const session = await openServiceSession(config);
108
+ if (svcSessionKey === currentKey) svcSession = session;
109
+ return session;
110
+ }
111
+ if (svcSessionPending?.key !== currentKey) {
112
+ const pending = openServiceSession(config)
113
+ .then((session) => {
114
+ if (svcSessionKey === currentKey) svcSession = session;
115
+ return session;
116
+ })
117
+ .finally(() => {
118
+ if (svcSessionPending?.promise === pending) svcSessionPending = null;
119
+ });
120
+ svcSessionPending = { key: currentKey, promise: pending };
121
+ }
122
+ return svcSessionPending.promise;
123
+ };
@@ -0,0 +1,44 @@
1
+ export const TEST_CA_CERT = `-----BEGIN CERTIFICATE-----
2
+ MIIBljCCATugAwIBAgIUaD/zikxyQaxGreCVzb9YufuusxIwCgYIKoZIzj0EAwIw
3
+ IDEeMBwGA1UEAwwVQ2xvdWQgRnJlZUlQQSBUZXN0IENBMB4XDTI2MDcyOTE1NTgx
4
+ MFoXDTM2MDcyNjE1NTgxMFowIDEeMBwGA1UEAwwVQ2xvdWQgRnJlZUlQQSBUZXN0
5
+ IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYZ7eU7/C71ilbpKa9k/FXObw
6
+ Vq4u/3rtpW/kSjsJaIVNPFp5oFBdHceL0UPgoxwpaJiJNHiwQHF/BBxaJ7HOAKNT
7
+ MFEwHQYDVR0OBBYEFOflw/p5xNlS0am+HytNLyXVEUndMB8GA1UdIwQYMBaAFOfl
8
+ w/p5xNlS0am+HytNLyXVEUndMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID
9
+ SQAwRgIhAJmRoNV5dxKvNOd2KoytvK/6ckRWN+Uu0ljc5y/g4izqAiEAsLCjxWvz
10
+ mgt1Z3uk+QkT9D1vHCuKSvcqv3aVn0DClxA=
11
+ -----END CERTIFICATE-----`;
12
+
13
+ export const TEST_SERVER_CERT = `-----BEGIN CERTIFICATE-----
14
+ MIIBpDCCAUmgAwIBAgIUNGe5IovnDTTKxjxwHBGVcnOaXaQwCgYIKoZIzj0EAwIw
15
+ IDEeMBwGA1UEAwwVQ2xvdWQgRnJlZUlQQSBUZXN0IENBMB4XDTI2MDcyOTE1NTgx
16
+ MFoXDTM2MDcyNjE1NTgxMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZI
17
+ zj0CAQYIKoZIzj0DAQcDQgAEolKc1EyyTH6MKX0GR7FhbXCN0O3G/D2riPJ045K+
18
+ qCL6m0Fso38eNxKbNENSkE1tX7j4uv6tcLmtHU/cKxjUg6NtMGswFAYDVR0RBA0w
19
+ C4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBT/iu3f
20
+ aYfd6r4xoPTkZujj/0rwWjAfBgNVHSMEGDAWgBTn5cP6ecTZUtGpvh8rTS8l1RFJ
21
+ 3TAKBggqhkjOPQQDAgNJADBGAiEAgsXqgbBm2RJaUZIs0IR2rbI+6S+1CtugVMHl
22
+ oxuRagcCIQCtoMZh0rZFlmUkH19BxrDvQe3JqCuQpd2HiZdvZTJhyQ==
23
+ -----END CERTIFICATE-----`;
24
+
25
+ export const TEST_SERVER_KEY = `-----BEGIN EC PARAMETERS-----
26
+ BggqhkjOPQMBBw==
27
+ -----END EC PARAMETERS-----
28
+ -----BEGIN EC PRIVATE KEY-----
29
+ MHcCAQEEIKFSLmqZO5KgG9hGbNhhJa0V2IMl39N2UHUj5VUh6DsmoAoGCCqGSM49
30
+ AwEHoUQDQgAEolKc1EyyTH6MKX0GR7FhbXCN0O3G/D2riPJ045K+qCL6m0Fso38e
31
+ NxKbNENSkE1tX7j4uv6tcLmtHU/cKxjUgw==
32
+ -----END EC PRIVATE KEY-----`;
33
+
34
+ export const TEST_WRONG_CA_CERT = `-----BEGIN CERTIFICATE-----
35
+ MIIBhDCCASugAwIBAgIUWM3XX235lciHJ/UKoajhrSx2dPwwCgYIKoZIzj0EAwIw
36
+ GDEWMBQGA1UEAwwNV3JvbmcgVGVzdCBDQTAeFw0yNjA3MjkxNTU4MTBaFw0zNjA3
37
+ MjYxNTU4MTBaMBgxFjAUBgNVBAMMDVdyb25nIFRlc3QgQ0EwWTATBgcqhkjOPQIB
38
+ BggqhkjOPQMBBwNCAAREOwLQCQYQI7zEa7GaSZag98eOF5KMckBPrDsDxS0Bb5lx
39
+ uIa3iWu9CI6Xkw+v+avpPF+Oz3QB7GXgCboKHFico1MwUTAdBgNVHQ4EFgQU1PRs
40
+ qLXtaHVxaKu0e0le1KpD6GYwHwYDVR0jBBgwFoAU1PRsqLXtaHVxaKu0e0le1KpD
41
+ 6GYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiA9vA9jI66Un7zQ
42
+ ycf0+6Ze0y5aSM9FLojGcVuzZb0iyAIgIC6ooFag9BuAZqcj/k07hjARi6UVOpEy
43
+ sIJIkokji38=
44
+ -----END CERTIFICATE-----`;
@@ -0,0 +1,44 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ /**
4
+ * TLS resolver slot for FreeIPA transport (`client.ts`, `session.ts`).
5
+ *
6
+ * The transport layer cannot directly read settings (would create a cycle:
7
+ * `services/freeipa-config.ts` already depends on `server/services/freeipa/`).
8
+ * Instead, the layer that owns settings registers a resolver via
9
+ * `setFreeIpaTlsResolver()` and the transport calls `getFreeIpaTls()` per fetch.
10
+ *
11
+ * Resolver is async — it reads through the Redis cache-aside settings layer.
12
+ * Returns Bun fetch `tls` options. Without a registered settings resolver,
13
+ * system trust remains explicitly verified.
14
+ * Resolution order is the resolver's concern (typically: ca_cert > allow_insecure > system trust).
15
+ */
16
+ // `BunFetchRequestInitTLS` is declared as a global (see bun-types/globals.d.ts)
17
+ // and not exported from the "bun" module. Use `Bun.TLSOptions` — fetch's `tls`
18
+ // option accepts it (the global extends it with `checkServerIdentity` which we
19
+ // don't need).
20
+ type FreeIpaTls = Bun.TLSOptions;
21
+
22
+ let resolver: (() => Promise<FreeIpaTls | undefined>) | null = null;
23
+
24
+ export const setFreeIpaTlsResolver = (fn: (() => Promise<FreeIpaTls | undefined>) | null): void => {
25
+ resolver = fn;
26
+ };
27
+
28
+ export const getFreeIpaTls = async (): Promise<FreeIpaTls> => {
29
+ return (await resolver?.()) ?? { rejectUnauthorized: true };
30
+ };
31
+
32
+ /**
33
+ * Stable fingerprint of the current TLS config — used as a cache-key suffix
34
+ * (e.g. for the cached service session) so that flipping `allow_insecure` or
35
+ * rotating `ca_cert` forces re-establishment of cached connections.
36
+ */
37
+ export const getFreeIpaTlsFingerprint = async (): Promise<string> => {
38
+ const tls = await getFreeIpaTls();
39
+ if (tls.ca && typeof tls.ca === "string" && tls.ca.length > 0) {
40
+ return `ca:${createHash("sha256").update(tls.ca).digest("hex")}`;
41
+ }
42
+ if (tls.rejectUnauthorized === false) return "insec";
43
+ return "sys";
44
+ };
@@ -0,0 +1,116 @@
1
+ export const FREEIPA_REQUEST_TIMEOUT_MS = 30_000;
2
+ export const FREEIPA_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
3
+
4
+ export type FreeIpaTransportErrorKind = "aborted" | "invalid_response" | "network" | "timeout" | "tls" | "upstream";
5
+
6
+ export class FreeIpaTransportError extends Error {
7
+ readonly kind: FreeIpaTransportErrorKind;
8
+ readonly status: number | null;
9
+
10
+ constructor(kind: FreeIpaTransportErrorKind, message: string, options: { cause?: unknown; status?: number } = {}) {
11
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
12
+ this.name = "FreeIpaTransportError";
13
+ this.kind = kind;
14
+ this.status = options.status ?? null;
15
+ }
16
+ }
17
+
18
+ const isTlsError = (error: unknown): boolean => {
19
+ const text = [
20
+ error instanceof Error ? error.message : String(error),
21
+ error instanceof Error && error.cause instanceof Error ? error.cause.message : "",
22
+ error instanceof Error && typeof error.cause === "object" && error.cause !== null && "code" in error.cause
23
+ ? String(error.cause.code)
24
+ : "",
25
+ ]
26
+ .join(" ")
27
+ .toLowerCase();
28
+
29
+ return /certificate|cert_|hostname|self[- ]signed|ssl|tls|unable to verify|unknown ca/.test(text);
30
+ };
31
+
32
+ export const withFreeIpaResponse = async <T>(
33
+ input: string | URL,
34
+ init: BunFetchRequestInit,
35
+ consume: (response: Response) => Promise<T>,
36
+ options: { signal?: AbortSignal; timeoutMs?: number } = {},
37
+ ): Promise<T> => {
38
+ const controller = new AbortController();
39
+ const timeoutMs = options.timeoutMs ?? FREEIPA_REQUEST_TIMEOUT_MS;
40
+ let timedOut = false;
41
+ let timeout!: ReturnType<typeof setTimeout>;
42
+ const timeoutFailure = new Promise<never>((_, reject) => {
43
+ timeout = setTimeout(() => {
44
+ timedOut = true;
45
+ controller.abort(new DOMException("FreeIPA request timed out", "TimeoutError"));
46
+ reject(new FreeIpaTransportError("timeout", `FreeIPA request timed out after ${timeoutMs} ms`));
47
+ }, timeoutMs);
48
+ });
49
+ let rejectCallerAbort: ((error: FreeIpaTransportError) => void) | null = null;
50
+ const callerAbortFailure = new Promise<never>((_, reject) => {
51
+ rejectCallerAbort = reject;
52
+ });
53
+ const abortFromCaller = () => {
54
+ controller.abort(options.signal?.reason);
55
+ rejectCallerAbort?.(new FreeIpaTransportError("aborted", "FreeIPA request was cancelled"));
56
+ };
57
+
58
+ if (options.signal?.aborted) abortFromCaller();
59
+ else options.signal?.addEventListener("abort", abortFromCaller, { once: true });
60
+
61
+ try {
62
+ const response = await Promise.race([fetch(input, { ...init, signal: controller.signal }), timeoutFailure, callerAbortFailure]);
63
+ return await Promise.race([consume(response), timeoutFailure, callerAbortFailure]);
64
+ } catch (error) {
65
+ if (error instanceof FreeIpaTransportError) throw error;
66
+ if (timedOut) {
67
+ throw new FreeIpaTransportError("timeout", `FreeIPA request timed out after ${timeoutMs} ms`, { cause: error });
68
+ }
69
+ if (options.signal?.aborted) {
70
+ throw new FreeIpaTransportError("aborted", "FreeIPA request was cancelled", { cause: error });
71
+ }
72
+ if (isTlsError(error)) {
73
+ throw new FreeIpaTransportError("tls", "FreeIPA TLS verification failed", { cause: error });
74
+ }
75
+ throw new FreeIpaTransportError("network", "Could not connect to FreeIPA", { cause: error });
76
+ } finally {
77
+ clearTimeout(timeout);
78
+ options.signal?.removeEventListener("abort", abortFromCaller);
79
+ }
80
+ };
81
+
82
+ export const readFreeIpaErrorBody = async (
83
+ response: Response,
84
+ maxBytes = FREEIPA_ERROR_BODY_LIMIT_BYTES,
85
+ ): Promise<{ text: string; truncated: boolean }> => {
86
+ if (!response.body) return { text: "", truncated: false };
87
+
88
+ const reader = response.body.getReader();
89
+ const decoder = new TextDecoder();
90
+ let bytesRead = 0;
91
+ let text = "";
92
+
93
+ try {
94
+ while (bytesRead <= maxBytes) {
95
+ const { done, value } = await reader.read();
96
+ if (done) return { text: text + decoder.decode(), truncated: false };
97
+ if (!value) continue;
98
+
99
+ const remaining = maxBytes - bytesRead;
100
+ if (value.byteLength > remaining) {
101
+ text += decoder.decode(value.subarray(0, Math.max(0, remaining)), { stream: true });
102
+ return { text: text + decoder.decode(), truncated: true };
103
+ }
104
+ bytesRead += value.byteLength;
105
+ text += decoder.decode(value, { stream: true });
106
+ }
107
+ return { text: text + decoder.decode(), truncated: true };
108
+ } finally {
109
+ await reader.cancel().catch(() => undefined);
110
+ }
111
+ };
112
+
113
+ export const isFreeIpaUpstreamStatus = (status: number): boolean => status === 408 || status === 429 || status >= 500;
114
+
115
+ export const upstreamFreeIpaError = (status: number): FreeIpaTransportError =>
116
+ new FreeIpaTransportError("upstream", `FreeIPA returned HTTP ${status}`, { status });
@@ -0,0 +1,59 @@
1
+ import { toPgTextArray as serializePgTextArray } from "../../../services/postgres";
2
+
3
+ export class IpaError extends Error {
4
+ constructor(
5
+ message: string,
6
+ public readonly code?: number,
7
+ public readonly data?: unknown,
8
+ ) {
9
+ super(message);
10
+ this.name = "IpaError";
11
+ }
12
+ }
13
+
14
+ export type DbRow = Record<string, unknown>;
15
+
16
+ export const str = (val: unknown): string => {
17
+ if (Array.isArray(val)) return String(val[0] ?? "");
18
+ return String(val ?? "");
19
+ };
20
+
21
+ export const num = (val: unknown): number | null => {
22
+ const raw = Array.isArray(val) ? val[0] : val;
23
+ const n = Number(raw);
24
+ return Number.isNaN(n) ? null : n;
25
+ };
26
+
27
+ export const parseGeneralizedTime = (val: unknown): Date | null => {
28
+ let raw = Array.isArray(val) ? val[0] : val;
29
+ if (raw && typeof raw === "object" && "__datetime__" in raw) {
30
+ raw = (raw as Record<string, unknown>).__datetime__;
31
+ }
32
+ const s = typeof raw === "string" ? raw : "";
33
+ if (!s || s.length < 14) return null;
34
+ const iso = `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}T${s.slice(8, 10)}:${s.slice(10, 12)}:${s.slice(12, 14)}Z`;
35
+ const d = new Date(iso);
36
+ return Number.isNaN(d.getTime()) ? null : d;
37
+ };
38
+
39
+ export const toGeneralizedTime = (date: Date): string => {
40
+ const y = date.getUTCFullYear();
41
+ const m = String(date.getUTCMonth() + 1).padStart(2, "0");
42
+ const d = String(date.getUTCDate()).padStart(2, "0");
43
+ const h = String(date.getUTCHours()).padStart(2, "0");
44
+ const min = String(date.getUTCMinutes()).padStart(2, "0");
45
+ const s = String(date.getUTCSeconds()).padStart(2, "0");
46
+ return `${y}${m}${d}${h}${min}${s}Z`;
47
+ };
48
+
49
+ export const escapeLike = (value: string): string => value.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
50
+
51
+ export const toExcludedGroupsSet = (groups: string[]): Set<string> => new Set(groups.map((group) => group.trim()).filter(Boolean));
52
+
53
+ export const mapIpaErrorCode = (code: number): 400 | 401 | 403 => {
54
+ if (code === 4001) return 401;
55
+ if (code === 4301) return 403;
56
+ return 400;
57
+ };
58
+
59
+ export const toPgTextArray = serializePgTextArray;