@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,305 @@
1
+ import { z } from "zod";
2
+ import { AppDevicePublicKeySchema } from "../contracts/app-approval";
3
+ import type { AppApprovalKey } from "./app-approval";
4
+
5
+ const keyGuards = new WeakMap<CryptoKey, () => void>();
6
+ export function assertVaultKey(key: CryptoKey) {
7
+ keyGuards.get(key)?.();
8
+ }
9
+ const bytes = (length: number) => crypto.getRandomValues(new Uint8Array(length));
10
+ const encode = (value: Uint8Array<ArrayBuffer>) => btoa(String.fromCharCode(...value));
11
+ const decode = (value: string) => Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
12
+ const text = new TextEncoder();
13
+ const base64 = (length: number) =>
14
+ z
15
+ .string()
16
+ .max(length * 2)
17
+ .refine((s) => {
18
+ try {
19
+ return decode(s).length === length && encode(decode(s)) === s;
20
+ } catch {
21
+ return false;
22
+ }
23
+ });
24
+ const blobSchema = z.object({ iv: base64(12), data: z.string().max(2_000_000) }).strict();
25
+ const pinSchema = z
26
+ .object({ type: z.literal("pin"), salt: base64(16), kdf: z.literal("argon2id-64m-t3-p1"), wrapped: blobSchema })
27
+ .strict();
28
+ const passkeySchema = z
29
+ .object({ type: z.literal("passkey"), credentialId: z.string().min(1).max(2048), salt: base64(32), wrapped: blobSchema })
30
+ .strict();
31
+ const methodSchema = z.discriminatedUnion("type", [pinSchema, passkeySchema]);
32
+ const configSchema = z
33
+ .object({ version: z.literal(1), id: base64(16), methods: z.array(methodSchema).min(1).max(2) })
34
+ .strict()
35
+ .refine((c) => new Set(c.methods.map((m) => m.type)).size === c.methods.length);
36
+ export type AppVaultConfig = z.infer<typeof configSchema>;
37
+ export type AppVaultBlob = z.infer<typeof blobSchema>;
38
+ export type AppVaultMethod = AppVaultConfig["methods"][number];
39
+ export type AppVaultSession = ReturnType<typeof session>;
40
+ export class AppVaultError extends Error {
41
+ constructor(public readonly code: "LOCKED" | "INVALID" | "UNSUPPORTED") {
42
+ super(`App vault: ${code}`);
43
+ }
44
+ }
45
+ const invalid = () => new AppVaultError("INVALID");
46
+ const aes = (raw: Uint8Array<ArrayBuffer>) => crypto.subtle.importKey("raw", raw, "AES-GCM", false, ["encrypt", "decrypt"]);
47
+ async function encrypt(key: CryptoKey, data: Uint8Array<ArrayBuffer>, context: string): Promise<AppVaultBlob> {
48
+ const iv = bytes(12);
49
+ return {
50
+ iv: encode(iv),
51
+ data: encode(
52
+ new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: text.encode(context), tagLength: 128 }, key, data)),
53
+ ),
54
+ };
55
+ }
56
+ async function decrypt(key: CryptoKey, value: AppVaultBlob, context: string) {
57
+ const b = blobSchema.parse(value);
58
+ try {
59
+ return new Uint8Array(
60
+ await crypto.subtle.decrypt(
61
+ { name: "AES-GCM", iv: decode(b.iv), additionalData: text.encode(context), tagLength: 128 },
62
+ key,
63
+ decode(b.data),
64
+ ),
65
+ );
66
+ } catch {
67
+ throw invalid();
68
+ }
69
+ }
70
+ async function pinKey(pin: string, salt: string) {
71
+ if (!/^[0-9]{6}$/.test(pin)) throw invalid();
72
+ const { argon2id } = await import("hash-wasm");
73
+ // Based on RFC 9106's memory-constrained profile: 64 MiB, three passes; one browser lane.
74
+ const raw = await argon2id({
75
+ password: pin,
76
+ salt: decode(salt),
77
+ parallelism: 1,
78
+ iterations: 3,
79
+ memorySize: 65536,
80
+ hashLength: 32,
81
+ outputType: "binary",
82
+ });
83
+ try {
84
+ return await aes(new Uint8Array(raw));
85
+ } finally {
86
+ raw.fill(0);
87
+ }
88
+ }
89
+ function wrapContext(id: string, method: Omit<z.infer<typeof pinSchema>, "wrapped"> | Omit<z.infer<typeof passkeySchema>, "wrapped">) {
90
+ return JSON.stringify(["cloud-login-vault", 1, id, method.type, method.salt, method.type === "pin" ? method.kdf : method.credentialId]);
91
+ }
92
+ function methodContext(id: string, method: AppVaultMethod) {
93
+ return wrapContext(
94
+ id,
95
+ method.type === "pin"
96
+ ? { type: "pin", salt: method.salt, kdf: method.kdf }
97
+ : { type: "passkey", salt: method.salt, credentialId: method.credentialId },
98
+ );
99
+ }
100
+ function extension(credential: Credential | null): { id: string; enabled: boolean; output?: Uint8Array<ArrayBuffer> } {
101
+ if (!(credential instanceof PublicKeyCredential)) throw invalid();
102
+ const result: unknown = credential.getClientExtensionResults();
103
+ const parsed = z
104
+ .object({
105
+ prf: z.object({ enabled: z.boolean().optional(), results: z.object({ first: z.instanceof(ArrayBuffer) }).optional() }).optional(),
106
+ })
107
+ .parse(result);
108
+ const first = parsed.prf?.results?.first;
109
+ if (first && first.byteLength !== 32) throw invalid();
110
+ return {
111
+ id: encode(new Uint8Array(credential.rawId)),
112
+ enabled: parsed.prf?.enabled === true,
113
+ output: first ? new Uint8Array(first) : undefined,
114
+ };
115
+ }
116
+ async function passkeyKey(credentialId: string, salt: string, signal?: AbortSignal) {
117
+ const credential = await navigator.credentials.get({
118
+ publicKey: {
119
+ challenge: bytes(32),
120
+ rpId: location.hostname,
121
+ allowCredentials: [{ type: "public-key", id: decode(credentialId) }],
122
+ userVerification: "required",
123
+ extensions: { prf: { eval: { first: decode(salt) } } },
124
+ },
125
+ signal,
126
+ });
127
+ const result = extension(credential);
128
+ if (result.id !== credentialId || !result.output) throw new AppVaultError("UNSUPPORTED");
129
+ return derivePasskeyKey(result.output);
130
+ }
131
+ async function derivePasskeyKey(output: Uint8Array<ArrayBuffer>) {
132
+ try {
133
+ const ikm = await crypto.subtle.importKey("raw", output, "HKDF", false, ["deriveKey"]);
134
+ return await crypto.subtle.deriveKey(
135
+ { name: "HKDF", hash: "SHA-256", salt: text.encode(location.origin), info: text.encode("cloud-login-vault-wrap-v1") },
136
+ ikm,
137
+ { name: "AES-GCM", length: 256 },
138
+ false,
139
+ ["encrypt", "decrypt"],
140
+ );
141
+ } finally {
142
+ output.fill(0);
143
+ }
144
+ }
145
+ async function capability(): Promise<"supported" | "unknown" | "unsupported"> {
146
+ if (
147
+ !globalThis.isSecureContext ||
148
+ !globalThis.PublicKeyCredential ||
149
+ !navigator.credentials ||
150
+ /^(?:[0-9]+\.){3}[0-9]+$|^\[/.test(location.hostname)
151
+ )
152
+ return "unsupported";
153
+ if (!PublicKeyCredential.getClientCapabilities) return "unknown";
154
+ try {
155
+ const caps = await PublicKeyCredential.getClientCapabilities();
156
+ const prf = caps["extension:prf"];
157
+ return prf === true ? "supported" : prf === false ? "unsupported" : "unknown";
158
+ } catch {
159
+ return "unknown";
160
+ }
161
+ }
162
+ function session(id: string, raw: Uint8Array<ArrayBuffer>, key: CryptoKey) {
163
+ let live = true;
164
+ const imported = new Map<string, AppApprovalKey>();
165
+ const materials = new Map<CryptoKey, Uint8Array<ArrayBuffer>>();
166
+ const check = () => {
167
+ if (!live) throw new AppVaultError("LOCKED");
168
+ };
169
+ const importPrivate = async (material: Uint8Array<ArrayBuffer>, publicKey: AppApprovalKey["publicKey"]): Promise<AppApprovalKey> => {
170
+ check();
171
+ const cacheId = encode(material);
172
+ const cached = imported.get(cacheId);
173
+ if (cached && JSON.stringify(cached.publicKey) === JSON.stringify(publicKey)) return cached;
174
+ const privateKey = await crypto.subtle.importKey("pkcs8", material, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
175
+ const pub = await crypto.subtle.importKey("jwk", publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
176
+ const challenge = bytes(32);
177
+ const proof = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, privateKey, challenge);
178
+ if (!(await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, pub, proof, challenge))) throw invalid();
179
+ check();
180
+ materials.set(privateKey, material.slice());
181
+ keyGuards.set(privateKey, check);
182
+ const value = { privateKey, publicKey };
183
+ imported.set(cacheId, value);
184
+ return value;
185
+ };
186
+ return {
187
+ id,
188
+ check,
189
+ lock() {
190
+ live = false;
191
+ raw.fill(0);
192
+ for (const material of materials.values()) material.fill(0);
193
+ materials.clear();
194
+ imported.clear();
195
+ },
196
+ async createKey() {
197
+ check();
198
+ const pair = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
199
+ const jwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
200
+ const publicKey = AppDevicePublicKeySchema.parse({ kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y });
201
+ const material = new Uint8Array(await crypto.subtle.exportKey("pkcs8", pair.privateKey));
202
+ try {
203
+ return await importPrivate(material, publicKey);
204
+ } finally {
205
+ material.fill(0);
206
+ }
207
+ },
208
+ async sealRecord<T extends { key: AppApprovalKey }>(record: T, context: string) {
209
+ check();
210
+ const material = materials.get(record.key.privateKey);
211
+ if (!material) throw invalid();
212
+ const plain = text.encode(JSON.stringify({ ...record, key: { privateKey: encode(material), publicKey: record.key.publicKey } }));
213
+ try {
214
+ const blob = await encrypt(key, plain, JSON.stringify(["cloud-login-record", 1, id, context]));
215
+ check();
216
+ return blob;
217
+ } finally {
218
+ plain.fill(0);
219
+ }
220
+ },
221
+ async openRecord<T>(blob: AppVaultBlob, context: string, validate: (value: unknown) => T): Promise<T> {
222
+ check();
223
+ const plain = await decrypt(key, blob, JSON.stringify(["cloud-login-record", 1, id, context]));
224
+ try {
225
+ const parsed = z
226
+ .object({ key: z.object({ privateKey: z.string().max(512), publicKey: AppDevicePublicKeySchema }).strict() })
227
+ .passthrough()
228
+ .parse(JSON.parse(new TextDecoder().decode(plain)));
229
+ const material = decode(parsed.key.privateKey);
230
+ try {
231
+ const result = validate({ ...parsed, key: await importPrivate(material, parsed.key.publicKey) });
232
+ check();
233
+ return result;
234
+ } finally {
235
+ material.fill(0);
236
+ }
237
+ } finally {
238
+ plain.fill(0);
239
+ }
240
+ },
241
+ async pin(pin: string): Promise<AppVaultMethod> {
242
+ check();
243
+ const method = { type: "pin" as const, salt: encode(bytes(16)), kdf: "argon2id-64m-t3-p1" as const };
244
+ const wrapping = await pinKey(pin, method.salt);
245
+ check();
246
+ const wrapped = await encrypt(wrapping, raw, wrapContext(id, method));
247
+ check();
248
+ return { ...method, wrapped };
249
+ },
250
+ async passkey(signal?: AbortSignal): Promise<AppVaultMethod> {
251
+ check();
252
+ const salt = encode(bytes(32));
253
+ const credential = await navigator.credentials.create({
254
+ publicKey: {
255
+ challenge: bytes(32),
256
+ rp: { id: location.hostname, name: "Cloud Login" },
257
+ user: { id: bytes(32), name: "Cloud Login", displayName: "Cloud Login" },
258
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
259
+ authenticatorSelection: { residentKey: "required", userVerification: "required" },
260
+ attestation: "none",
261
+ extensions: { prf: { eval: { first: decode(salt) } } },
262
+ },
263
+ signal,
264
+ });
265
+ const result = extension(credential);
266
+ if (!result.enabled) {
267
+ result.output?.fill(0);
268
+ throw new AppVaultError("UNSUPPORTED");
269
+ }
270
+ const method = { type: "passkey" as const, credentialId: result.id, salt };
271
+ // Some providers evaluate PRF during creation; others require authentication.
272
+ const wrapping = result.output ? await derivePasskeyKey(result.output) : await passkeyKey(method.credentialId, method.salt, signal);
273
+ check();
274
+ const wrapped = await encrypt(wrapping, raw, wrapContext(id, method));
275
+ check();
276
+ return { ...method, wrapped };
277
+ },
278
+ };
279
+ }
280
+ async function create() {
281
+ const raw = bytes(32);
282
+ return session(encode(bytes(16)), raw, await aes(raw));
283
+ }
284
+ async function unlock(config: AppVaultConfig, type: "pin" | "passkey", pin?: string, signal?: AbortSignal) {
285
+ const value = configSchema.parse(config);
286
+ const method = value.methods.find((m) => m.type === type);
287
+ if (!method) throw invalid();
288
+ const wrapping =
289
+ method.type === "pin" ? await pinKey(pin ?? "", method.salt) : await passkeyKey(method.credentialId, method.salt, signal);
290
+ const raw = await decrypt(wrapping, method.wrapped, methodContext(value.id, method));
291
+ if (raw.length !== 32) {
292
+ raw.fill(0);
293
+ throw invalid();
294
+ }
295
+ try {
296
+ signal?.throwIfAborted();
297
+ const key = await aes(raw);
298
+ signal?.throwIfAborted();
299
+ return session(value.id, raw, key);
300
+ } catch (error) {
301
+ raw.fill(0);
302
+ throw error;
303
+ }
304
+ }
305
+ export const appApprovalVault = { create, unlock, capability, parseConfig: (value: unknown) => configSchema.parse(value) };
@@ -0,0 +1,234 @@
1
+ import { appApprovalVault, assertVaultKey } from "./app-approval-vault";
2
+
3
+ export { type AppVaultBlob, type AppVaultConfig, AppVaultError, type AppVaultMethod, type AppVaultSession } from "./app-approval-vault";
4
+
5
+ import { z } from "zod";
6
+ import {
7
+ APP_APPROVAL_LIMITS,
8
+ APP_APPROVAL_PATH,
9
+ APP_APPROVAL_PROTOCOL,
10
+ AppDeviceCommandSchema,
11
+ AppDeviceProofSchema,
12
+ type AppDevicePublicKey,
13
+ AppDevicePublicKeySchema,
14
+ AppDeviceResponseSchema,
15
+ AppPairingClaimResultSchema,
16
+ AppPairingClaimSchema,
17
+ type AppPairingPayload,
18
+ AppPairingPayloadSchema,
19
+ AppPairingResultSchema,
20
+ type AppPendingLogin,
21
+ AppPendingLoginSchema,
22
+ appDeviceProofMessage,
23
+ appPairingLink,
24
+ appPairingProofMessage,
25
+ parseAppPairingLink,
26
+ } from "../contracts/app-approval";
27
+
28
+ export interface AppApprovalKey {
29
+ privateKey: CryptoKey;
30
+ publicKey: AppDevicePublicKey;
31
+ }
32
+ export interface AppApprovalDevice {
33
+ issuer: string;
34
+ deviceId: string;
35
+ key: AppApprovalKey;
36
+ }
37
+
38
+ /** No remote error body or credential material is retained in an error. */
39
+ export class AppApprovalClientError extends Error {
40
+ constructor(
41
+ public readonly code: "HTTP" | "INVALID_RESPONSE" | "NETWORK" | "INVALID_INPUT",
42
+ public readonly status?: number,
43
+ ) {
44
+ super(`App approval: ${code}`);
45
+ }
46
+ }
47
+
48
+ const origin = (input: string): string => {
49
+ const url = new URL(input);
50
+ if (
51
+ url.origin !== input ||
52
+ url.username ||
53
+ url.password ||
54
+ (url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)))
55
+ ) {
56
+ throw new AppApprovalClientError("INVALID_INPUT");
57
+ }
58
+ return url.origin;
59
+ };
60
+
61
+ const createKey = async (): Promise<AppApprovalKey> => {
62
+ const keys = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"]);
63
+ const jwk = await crypto.subtle.exportKey("jwk", keys.publicKey);
64
+ return { privateKey: keys.privateKey, publicKey: AppDevicePublicKeySchema.parse({ kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }) };
65
+ };
66
+
67
+ const sign = async (key: AppApprovalKey, message: string) => {
68
+ assertVaultKey(key.privateKey);
69
+ if (key.privateKey.extractable || key.privateKey.type !== "private") throw new AppApprovalClientError("INVALID_INPUT");
70
+ const signature = new Uint8Array(
71
+ await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, key.privateKey, new TextEncoder().encode(message)),
72
+ );
73
+ assertVaultKey(key.privateKey);
74
+ return btoa(String.fromCharCode(...signature))
75
+ .replaceAll("+", "-")
76
+ .replaceAll("/", "_")
77
+ .replaceAll("=", "");
78
+ };
79
+
80
+ const InfoSchema = z.object({
81
+ protocol: z.literal(APP_APPROVAL_PROTOCOL),
82
+ issuer: z.string(),
83
+ api: z.string(),
84
+ appOrigin: z.string(),
85
+ algorithm: z.literal("ES256"),
86
+ limits: z.object(Object.fromEntries(Object.entries(APP_APPROVAL_LIMITS).map(([key, value]) => [key, z.literal(value)]))),
87
+ });
88
+
89
+ /** Call only after the user explicitly trusts the issuer from a pairing link.
90
+ * Connect validates discovery before returning a client. */
91
+ const connect = async (options: {
92
+ issuer: string;
93
+ authenticatorOrigin: string;
94
+ signal?: AbortSignal;
95
+ fetch?: (url: string, init: RequestInit) => Promise<Response>;
96
+ }) => {
97
+ const issuer = origin(options.issuer);
98
+ const appOrigin = origin(options.authenticatorOrigin);
99
+ const fetcher = options.fetch ?? globalThis.fetch;
100
+ const request = async <T>(path: string, schema: z.ZodType<T>, body?: unknown, signal?: AbortSignal): Promise<T> => {
101
+ const serialized = body === undefined ? undefined : JSON.stringify(body);
102
+ if (serialized && new TextEncoder().encode(serialized).length > APP_APPROVAL_LIMITS.bodyBytes)
103
+ throw new AppApprovalClientError("INVALID_INPUT");
104
+ let response: Response;
105
+ const timeout = AbortSignal.timeout(APP_APPROVAL_LIMITS.proofSeconds * 1000);
106
+ const requestSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;
107
+ try {
108
+ response = await fetcher(`${issuer}${APP_APPROVAL_PATH}${path}`, {
109
+ method: serialized === undefined ? "GET" : "POST",
110
+ body: serialized,
111
+ headers: serialized === undefined ? undefined : { "Content-Type": "application/json" },
112
+ credentials: "omit",
113
+ redirect: "error",
114
+ cache: "no-store",
115
+ referrerPolicy: "no-referrer",
116
+ signal: requestSignal,
117
+ });
118
+ } catch {
119
+ signal?.throwIfAborted();
120
+ throw new AppApprovalClientError("NETWORK");
121
+ }
122
+ if (!response.ok) {
123
+ await response.body?.cancel();
124
+ throw new AppApprovalClientError("HTTP", response.status);
125
+ }
126
+ // The response budget matches the bounded v1 request/pending-list budget.
127
+ const reader = response.body?.getReader();
128
+ if (!reader) throw new AppApprovalClientError("INVALID_RESPONSE");
129
+ const decoder = new TextDecoder();
130
+ let text = "",
131
+ bytes = 0;
132
+ try {
133
+ while (true) {
134
+ const chunk = await reader.read();
135
+ if (chunk.done) break;
136
+ bytes += chunk.value.length;
137
+ if (bytes > APP_APPROVAL_LIMITS.bodyBytes) {
138
+ await reader.cancel();
139
+ throw new AppApprovalClientError("INVALID_RESPONSE");
140
+ }
141
+ text += decoder.decode(chunk.value, { stream: true });
142
+ }
143
+ text += decoder.decode();
144
+ return schema.parse(JSON.parse(text));
145
+ } catch {
146
+ signal?.throwIfAborted();
147
+ throw new AppApprovalClientError("INVALID_RESPONSE");
148
+ } finally {
149
+ reader.releaseLock();
150
+ }
151
+ };
152
+ const info = await request("/info", InfoSchema, undefined, options.signal);
153
+ if (info.issuer !== issuer || info.appOrigin !== appOrigin || info.api !== `${issuer}${APP_APPROVAL_PATH}`)
154
+ throw new AppApprovalClientError("INVALID_RESPONSE");
155
+
156
+ const pairing = (payload: AppPairingPayload) => {
157
+ const parsed = AppPairingPayloadSchema.parse(payload);
158
+ if (parsed.issuer !== issuer || Date.parse(parsed.expiresAt) <= Date.now()) throw new AppApprovalClientError("INVALID_INPUT");
159
+ return parsed;
160
+ };
161
+ const command = async (device: AppApprovalDevice, input: z.infer<typeof AppDeviceCommandSchema>, signal?: AbortSignal) => {
162
+ if (device.issuer !== issuer) throw new AppApprovalClientError("INVALID_INPUT");
163
+ const now = Math.floor(Date.now() / 1000);
164
+ const proof = AppDeviceProofSchema.parse({
165
+ issuer,
166
+ deviceId: device.deviceId,
167
+ jti: crypto.randomUUID(),
168
+ issuedAt: now,
169
+ expiresAt: now + APP_APPROVAL_LIMITS.proofSeconds,
170
+ command: AppDeviceCommandSchema.parse(input),
171
+ });
172
+ return request("/device", AppDeviceResponseSchema, { proof, signature: await sign(device.key, appDeviceProofMessage(proof)) }, signal);
173
+ };
174
+ return {
175
+ issuer,
176
+ info,
177
+ claim: async (payload: AppPairingPayload, key: AppApprovalKey, name: string, signal?: AbortSignal) => {
178
+ const p = pairing(payload);
179
+ const input = AppPairingClaimSchema.omit({ signature: true }).parse({
180
+ pairingId: p.pairingId,
181
+ secret: p.secret,
182
+ publicKey: key.publicKey,
183
+ name,
184
+ });
185
+ return request(
186
+ "/pairings/claim",
187
+ AppPairingClaimResultSchema,
188
+ { ...input, signature: await sign(key, appPairingProofMessage(issuer, input)) },
189
+ signal,
190
+ );
191
+ },
192
+ pairingResult: (payload: AppPairingPayload, key: AppApprovalKey, signal?: AbortSignal) => {
193
+ const p = pairing(payload);
194
+ return request(
195
+ "/pairings/result",
196
+ AppPairingResultSchema,
197
+ { pairingId: p.pairingId, secret: p.secret, publicKey: AppDevicePublicKeySchema.parse(key.publicKey) },
198
+ signal,
199
+ );
200
+ },
201
+ pending: async (device: AppApprovalDevice, signal?: AbortSignal) => {
202
+ const result = await command(device, { operation: "pending" }, signal);
203
+ if (!("requests" in result)) throw new AppApprovalClientError("INVALID_RESPONSE");
204
+ return result;
205
+ },
206
+ decide: async (device: AppApprovalDevice, login: AppPendingLogin, decision: "approve" | "deny", signal?: AbortSignal) => {
207
+ login = AppPendingLoginSchema.parse(login);
208
+ if (Date.parse(login.expiresAt) <= Date.now()) throw new AppApprovalClientError("INVALID_INPUT");
209
+ const result = await command(
210
+ device,
211
+ { operation: "decide", requestId: login.requestId, challenge: login.challenge, comparison: login.comparison, decision },
212
+ signal,
213
+ );
214
+ if (!("state" in result) || result.state !== (decision === "approve" ? "approved" : "denied"))
215
+ throw new AppApprovalClientError("INVALID_RESPONSE");
216
+ return result;
217
+ },
218
+ revoke: async (device: AppApprovalDevice, signal?: AbortSignal) => {
219
+ const result = await command(device, { operation: "revoke" }, signal);
220
+ if (!("state" in result) || result.state !== "revoked") throw new AppApprovalClientError("INVALID_RESPONSE");
221
+ return result;
222
+ },
223
+ };
224
+ };
225
+
226
+ /** Browser-only SDK. Never polls, approves, retries mutations, or reads clipboard automatically. */
227
+ export const appApproval = {
228
+ vault: appApprovalVault,
229
+ createPairingLink: appPairingLink,
230
+ parsePairingLink: parseAppPairingLink,
231
+ createKey,
232
+ connect,
233
+ limits: APP_APPROVAL_LIMITS,
234
+ };