@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,250 @@
1
+ /**
2
+ * Instance-wide entry points: `status` for a quick verdict and `diagnose`
3
+ * for the bounded, agent-oriented troubleshooting bundle.
4
+ */
5
+ import { command, flag } from "../index";
6
+ import type { PostgresDiagnostics, RedisDiagnostics } from "./data";
7
+ import type { GatewayHealth } from "./gateway";
8
+ import type { BackgroundJobRow } from "./jobs";
9
+ import { getLogs, type LogSummary, trimLogMessages } from "./logs";
10
+ import type { NatsSnapshot } from "./nats";
11
+ import type { SyncOverview } from "./sync";
12
+ import type { MetricsCollector } from "./metrics";
13
+ import { apiGet, formatBytes, parseLookbackHours, printJsonOrTable, queryString, safeCollect, skippedCollect, truncate } from "./shared";
14
+ import type { TelemetryEvent, TelemetryRouteRow } from "./telemetry";
15
+ import { diagnoseRange } from "./telemetry";
16
+
17
+ export const DIAGNOSE_SECTIONS = ["health", "logs", "telemetry", "jobs", "postgres", "redis", "metrics", "sync", "nats"] as const;
18
+ export type DiagnoseSection = (typeof DIAGNOSE_SECTIONS)[number];
19
+
20
+ export const parseDiagnoseSections = (value: string | undefined, label: string): Set<DiagnoseSection> | null => {
21
+ if (!value) return null;
22
+ const sections = new Set<DiagnoseSection>();
23
+ for (const raw of value.split(",")) {
24
+ const section = raw.trim();
25
+ if (!section) continue;
26
+ if (!DIAGNOSE_SECTIONS.includes(section as DiagnoseSection)) {
27
+ throw new Error(`${label} must contain only: ${DIAGNOSE_SECTIONS.join(", ")}.`);
28
+ }
29
+ sections.add(section as DiagnoseSection);
30
+ }
31
+ return sections;
32
+ };
33
+
34
+ export const instanceCommands = [
35
+ command("status", {
36
+ summary: "Show gateway and app health",
37
+ run: async ({ ctx }) => {
38
+ const health = await apiGet<GatewayHealth>(ctx, "/api/gateway/health");
39
+ const row = {
40
+ status: health.status,
41
+ apps: health.summary.apps,
42
+ healthy: health.summary.healthy,
43
+ degraded: health.summary.degraded,
44
+ offline: health.summary.offline,
45
+ routes: health.summary.routes,
46
+ requests: health.summary.requests,
47
+ errors: health.summary.errors,
48
+ };
49
+ printJsonOrTable(
50
+ ctx,
51
+ health,
52
+ [row],
53
+ [
54
+ { key: "status" },
55
+ { key: "apps" },
56
+ { key: "healthy" },
57
+ { key: "degraded" },
58
+ { key: "offline" },
59
+ { key: "routes" },
60
+ { key: "requests" },
61
+ { key: "errors" },
62
+ ],
63
+ );
64
+ },
65
+ }),
66
+ command("diagnose", {
67
+ summary: "Collect a bounded diagnostics bundle for agents",
68
+ flags: {
69
+ since: flag.string({ default: "24h", description: "Lookback window like 30m, 6h, or 7d" }),
70
+ logLimit: flag.int({ name: "log-limit", default: 20, min: 1, max: 50, description: "Recent error/warn logs per level" }),
71
+ include: flag.string({ description: "Comma-separated sections: health,logs,telemetry,jobs,postgres,redis,metrics,sync,nats" }),
72
+ skip: flag.string({ description: "Comma-separated sections to skip" }),
73
+ messageLength: flag.int({
74
+ name: "message-length",
75
+ default: 500,
76
+ min: 40,
77
+ max: 5000,
78
+ description: "Log message length in JSON bundle",
79
+ }),
80
+ fullLogs: flag.boolean({ name: "full-logs", description: "Do not trim log messages in the JSON bundle" }),
81
+ },
82
+ run: async ({ ctx, flags }) => {
83
+ const hours = parseLookbackHours(flags.since);
84
+ const logLimit = flags.logLimit ?? 20;
85
+ const include = parseDiagnoseSections(flags.include, "--include");
86
+ const skip = parseDiagnoseSections(flags.skip, "--skip");
87
+ const shouldCollect = (section: DiagnoseSection) => (!include || include.has(section)) && !skip?.has(section);
88
+ const [
89
+ health,
90
+ logSummary,
91
+ logErrors,
92
+ logWarnings,
93
+ telemetrySummary,
94
+ telemetryErrors,
95
+ failingRoutes,
96
+ jobs,
97
+ postgres,
98
+ redis,
99
+ metrics,
100
+ sync,
101
+ nats,
102
+ ] = await Promise.all([
103
+ shouldCollect("health")
104
+ ? safeCollect("gateway health", () => apiGet<GatewayHealth>(ctx, "/api/gateway/health"))
105
+ : skippedCollect("gateway health"),
106
+ shouldCollect("logs")
107
+ ? safeCollect("log summary", () => apiGet<LogSummary>(ctx, "/api/logging/summary"))
108
+ : skippedCollect("log summary"),
109
+ shouldCollect("logs")
110
+ ? safeCollect("error logs", () => getLogs(ctx, { level: "error", sinceHours: hours, perPage: logLimit }))
111
+ : skippedCollect("error logs"),
112
+ shouldCollect("logs")
113
+ ? safeCollect("warning logs", () => getLogs(ctx, { level: "warn", sinceHours: hours, perPage: logLimit }))
114
+ : skippedCollect("warning logs"),
115
+ shouldCollect("telemetry")
116
+ ? safeCollect("telemetry summary", () =>
117
+ apiGet<Record<string, number | null>>(ctx, `/api/gateway/telemetry/summary${queryString({ hours })}`),
118
+ )
119
+ : skippedCollect("telemetry summary"),
120
+ shouldCollect("telemetry")
121
+ ? safeCollect("telemetry errors", () =>
122
+ apiGet<{ items: TelemetryEvent[]; total: number }>(
123
+ ctx,
124
+ `/api/gateway/telemetry/events${queryString({ errors: "1", hours, page: 1, per_page: Math.min(logLimit, 50) })}`,
125
+ ),
126
+ )
127
+ : skippedCollect("telemetry errors"),
128
+ // The single most useful line in the bundle: a total request count
129
+ // says nothing when one route dominates traffic, whereas a ranked
130
+ // list names the endpoints actually failing.
131
+ shouldCollect("telemetry")
132
+ ? safeCollect("failing routes", () =>
133
+ apiGet<{ items: TelemetryRouteRow[] }>(
134
+ ctx,
135
+ `/api/gateway/telemetry/routes${queryString({ range: diagnoseRange(hours), sort: "errorRate", errors: "1", limit: 10 })}`,
136
+ ),
137
+ )
138
+ : skippedCollect("failing routes"),
139
+ shouldCollect("jobs")
140
+ ? safeCollect("background jobs", () =>
141
+ apiGet<{ items: BackgroundJobRow[] }>(ctx, `/api/gateway/jobs${queryString({ health: "failed" })}`),
142
+ )
143
+ : skippedCollect("background jobs"),
144
+ shouldCollect("postgres")
145
+ ? safeCollect("postgres", () => apiGet<PostgresDiagnostics>(ctx, "/api/gateway/data/postgres"))
146
+ : skippedCollect("postgres"),
147
+ shouldCollect("redis")
148
+ ? safeCollect("redis", () => apiGet<RedisDiagnostics>(ctx, "/api/gateway/data/redis"))
149
+ : skippedCollect("redis"),
150
+ shouldCollect("metrics")
151
+ ? safeCollect("metrics", () =>
152
+ apiGet<{ generatedAt: string; series: number; collectors: MetricsCollector[] }>(ctx, "/api/gateway/metrics/snapshot"),
153
+ )
154
+ : skippedCollect("metrics"),
155
+ shouldCollect("sync")
156
+ ? safeCollect("sync", async () => {
157
+ const snapshot = await apiGet<SyncOverview>(ctx, "/api/gateway/sync?problems=true");
158
+ // Support bundles need failure context, not user payload previews.
159
+ return { ...snapshot, deadLetters: snapshot.deadLetters.map(({ dataPreview: _preview, ...entry }) => entry) };
160
+ })
161
+ : skippedCollect("sync"),
162
+ shouldCollect("nats")
163
+ ? safeCollect("nats", () => apiGet<NatsSnapshot>(ctx, "/api/gateway/nats?problems=true&limit=20"))
164
+ : skippedCollect("nats"),
165
+ ]);
166
+ const bundle = {
167
+ generatedAt: new Date().toISOString(),
168
+ lookbackHours: hours,
169
+ health,
170
+ logs: {
171
+ summary: logSummary,
172
+ errors:
173
+ logErrors.ok && !flags.fullLogs
174
+ ? { ...logErrors, data: trimLogMessages(logErrors.data, flags.messageLength ?? 500) }
175
+ : logErrors,
176
+ warnings:
177
+ logWarnings.ok && !flags.fullLogs
178
+ ? { ...logWarnings, data: trimLogMessages(logWarnings.data, flags.messageLength ?? 500) }
179
+ : logWarnings,
180
+ },
181
+ telemetry: { summary: telemetrySummary, errors: telemetryErrors, failingRoutes },
182
+ jobs,
183
+ postgres,
184
+ redis,
185
+ metrics,
186
+ sync,
187
+ nats,
188
+ };
189
+ if (ctx.options.output === "json") {
190
+ ctx.json(bundle);
191
+ return;
192
+ }
193
+
194
+ const lines = ["Cloud admin diagnose", `lookback: ${hours}h`, ""];
195
+ if (health.ok) {
196
+ lines.push(
197
+ `gateway: ${health.data.status}`,
198
+ `apps: ${health.data.summary.healthy ?? 0} healthy, ${health.data.summary.degraded ?? 0} degraded, ${health.data.summary.offline ?? 0} offline`,
199
+ );
200
+ } else lines.push(`gateway: unavailable (${health.error})`);
201
+ if (logSummary.ok) {
202
+ lines.push(
203
+ `logs: ${logSummary.data.errors24h} errors / ${logSummary.data.warnings24h} warnings in 24h, ${logSummary.data.total} retained`,
204
+ );
205
+ } else lines.push(`logs: unavailable (${logSummary.error})`);
206
+ if (telemetrySummary.ok) {
207
+ lines.push(`telemetry: ${telemetrySummary.data.requests ?? 0} requests, ${telemetrySummary.data.errors ?? 0} errors`);
208
+ } else lines.push(`telemetry: unavailable (${telemetrySummary.error})`);
209
+ if (failingRoutes.ok) {
210
+ const worst = failingRoutes.data.items.slice(0, 3);
211
+ lines.push(
212
+ worst.length === 0
213
+ ? "failing routes: none"
214
+ : `failing routes: ${worst.map((row) => `${row.route} ${row.errors}/${row.requests}`).join(", ")}`,
215
+ );
216
+ } else if (!("skipped" in failingRoutes)) lines.push(`failing routes: unavailable (${failingRoutes.error})`);
217
+ if (jobs.ok)
218
+ lines.push(`failing jobs: ${jobs.data.items.length === 0 ? "none" : jobs.data.items.map((row) => row.source).join(", ")}`);
219
+ else if (!("skipped" in jobs)) lines.push(`failing jobs: unavailable (${jobs.error})`);
220
+ if (postgres.ok)
221
+ lines.push(
222
+ `postgres: ${postgres.data.tables} tables, ${formatBytes(postgres.data.totalBytes)}, ${postgres.data.warnings.length} warnings`,
223
+ );
224
+ else lines.push(`postgres: unavailable (${postgres.error})`);
225
+ if (redis.ok) lines.push(`redis: ${redis.data.dbSize} keys, ${redis.data.warnings.length} warnings`);
226
+ else lines.push(`redis: unavailable (${redis.error})`);
227
+ if (metrics.ok) {
228
+ const failed = metrics.data.collectors.filter((collector) => collector.status !== "ok");
229
+ lines.push(`metrics: ${metrics.data.series} series, ${failed.length} failed collectors`);
230
+ } else lines.push(`metrics: unavailable (${metrics.error})`);
231
+ if (sync.ok)
232
+ lines.push(
233
+ `sync: ${sync.data.resources.length} resources needing attention; ${sync.data.deadLetters.length}${sync.data.truncatedStores.length ? "+" : ""} sampled dead letters; ${sync.data.apps.filter((app) => app.status !== "ok").length} unavailable apps`,
234
+ );
235
+ else if (!("skipped" in sync)) lines.push(`sync: unavailable (${sync.error})`);
236
+ if (nats.ok)
237
+ lines.push(
238
+ `nats: cluster ${nats.data.cluster.status}; inventory ${nats.data.inventory.status}; ${nats.data.inventory.matchedTotal ?? "unknown"} streams needing attention`,
239
+ );
240
+ else if (!("skipped" in nats)) lines.push(`nats: unavailable (${nats.error})`);
241
+ if (logErrors.ok && logErrors.data.entries.length > 0) {
242
+ lines.push("", "recent errors:");
243
+ for (const entry of logErrors.data.entries.slice(0, 5)) {
244
+ lines.push(`- [${entry.createdAt}] ${entry.source}: ${truncate(entry.message, 140)} (#${entry.id})`);
245
+ }
246
+ }
247
+ ctx.print(lines.join("\n"));
248
+ },
249
+ }),
250
+ ];
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Background jobs and scheduled work, read through the trace spans every
3
+ * Cloud job records. Until now this was the one observability domain with no
4
+ * CLI at all — visible in the admin UI, invisible to scripts and agents.
5
+ *
6
+ * Read-only. Triggering a schedule stays a deliberate action in the admin UI.
7
+ */
8
+ import { arg, command, flag, paginationFlags } from "../index";
9
+ import { apiGet, formatMs, pageQuery, printJsonOrTable, queryString, truncate } from "./shared";
10
+
11
+ export type BackgroundJobTrace = {
12
+ runs: number;
13
+ failed: number;
14
+ running: number;
15
+ stuck: number;
16
+ anomalous: number;
17
+ latestStatus: string | null;
18
+ latestStartedAt: string | null;
19
+ latestEndedAt: string | null;
20
+ avgDurationMs: number | null;
21
+ categories: string[];
22
+ };
23
+
24
+ export type BackgroundJobRow = {
25
+ kind: "schedule" | "trace";
26
+ source: string;
27
+ label?: string | null;
28
+ appId?: string | null;
29
+ cron?: string | null;
30
+ trace: BackgroundJobTrace | null;
31
+ };
32
+
33
+ export type TraceSpan = {
34
+ traceId: string;
35
+ spanId: string;
36
+ source: string;
37
+ appId: string | null;
38
+ category: string | null;
39
+ status: string;
40
+ /** Failure detail; the field is `statusMessage`, not `error`. */
41
+ statusMessage: string | null;
42
+ /** Structured per-run metadata (counts, ids); shape is job-specific. */
43
+ summary: Record<string, unknown> | null;
44
+ eventCount: number;
45
+ startedAt: string | null;
46
+ endedAt: string | null;
47
+ durationMs: number | null;
48
+ };
49
+
50
+ export type TraceEvent = {
51
+ name: string;
52
+ severity: string | null;
53
+ occurredAt: string | null;
54
+ body: string | null;
55
+ attributes: Record<string, unknown> | null;
56
+ };
57
+
58
+ const HEALTH_VALUES = ["all", "failed", "stuck", "running", "healthy"] as const;
59
+ const WINDOW_VALUES = ["10m", "1h", "12h", "24h", "7d", "30d"] as const;
60
+
61
+ const windowFlag = () => flag.enum(WINDOW_VALUES, { default: "24h", description: "Lookback window for run statistics" });
62
+ const TYPE_VALUES = ["all", "job", "schedule", "backfill", "ai", "http", "notification", "sync", "custom"] as const;
63
+
64
+ /** `traceId:spanId`, the identifier `jobs runs` prints and `jobs show` takes. */
65
+ const runKey = (span: { traceId: string; spanId: string }): string => `${span.traceId}:${span.spanId}`;
66
+
67
+ const jobRows = (items: BackgroundJobRow[]) =>
68
+ items.map((row) => ({
69
+ source: row.source,
70
+ kind: row.kind,
71
+ app: row.appId ?? "-",
72
+ schedule: row.cron ?? "-",
73
+ runs: row.trace?.runs ?? 0,
74
+ failed: row.trace?.failed ?? 0,
75
+ running: row.trace?.running ?? 0,
76
+ stuck: row.trace?.stuck ?? 0,
77
+ latest: row.trace?.latestStatus ?? "-",
78
+ avgMs: formatMs(row.trace?.avgDurationMs ?? null),
79
+ }));
80
+
81
+ export const jobCommands = [
82
+ command("jobs list", {
83
+ summary: "List background jobs and schedules with their run health",
84
+ description:
85
+ "Joins registered schedules with recorded trace runs over the selected window. `--health failed` means the MOST RECENT run of a source failed, i.e. currently unhealthy — it does not list every source that has ever failed. `--health stuck` lists sources with spans left open past the abandonment threshold; those are not running, nothing is working on them. Use `jobs runs --source <id>` for run history.",
86
+ examples: [
87
+ "cld admin jobs list --json",
88
+ "cld admin jobs list --health stuck",
89
+ "cld admin jobs list --health failed --window 7d",
90
+ "cld admin jobs list --type backfill",
91
+ "cld admin jobs list --type schedule --search mail",
92
+ ],
93
+ flags: {
94
+ search: flag.string({ aliases: ["q"], description: "Free-text match on source, label, or app" }),
95
+ type: flag.enum(TYPE_VALUES, { default: "all", description: "Trace category" }),
96
+ health: flag.enum(HEALTH_VALUES, { default: "all", description: "Health of the source in this window" }),
97
+ window: windowFlag(),
98
+ },
99
+ run: async ({ ctx, flags }) => {
100
+ const raw = await apiGet<{ items: BackgroundJobRow[] }>(
101
+ ctx,
102
+ `/api/gateway/jobs${queryString({ search: flags.search, type: flags.type, health: flags.health, window: flags.window })}`,
103
+ );
104
+ printJsonOrTable(ctx, raw, jobRows(raw.items), [
105
+ { key: "source", label: "Source" },
106
+ { key: "kind", label: "Kind" },
107
+ { key: "app", label: "App" },
108
+ { key: "schedule", label: "Cron" },
109
+ { key: "runs", label: "Runs" },
110
+ { key: "failed", label: "Failed" },
111
+ { key: "running", label: "Running" },
112
+ { key: "stuck", label: "Stuck" },
113
+ { key: "latest", label: "Latest" },
114
+ { key: "avgMs", label: "Avg" },
115
+ ]);
116
+ },
117
+ }),
118
+
119
+ command("jobs stats", {
120
+ summary: "Show aggregate run counts across all background jobs",
121
+ description:
122
+ "Running counts spans in flight now; stuck counts spans left open past the abandonment threshold; anomalous counts finished runs longer than that threshold, which are excluded from the duration percentiles so those describe real runs.",
123
+ examples: ["cld admin jobs stats --json", "cld admin jobs stats --window 7d"],
124
+ flags: { source: flag.string({ description: "Restrict to one job source" }), window: windowFlag() },
125
+ run: async ({ ctx, flags }) => {
126
+ const raw = await apiGet<Record<string, number>>(
127
+ ctx,
128
+ `/api/gateway/jobs/stats${queryString({ source: flags.source, window: flags.window })}`,
129
+ );
130
+ printJsonOrTable(
131
+ ctx,
132
+ raw,
133
+ [raw],
134
+ [
135
+ { key: "runs", label: "Runs" },
136
+ { key: "sources", label: "Sources" },
137
+ { key: "running", label: "Running" },
138
+ { key: "stuck", label: "Stuck" },
139
+ { key: "anomalous", label: "Anomalous" },
140
+ { key: "succeeded", label: "Succeeded" },
141
+ { key: "failed", label: "Failed" },
142
+ ],
143
+ );
144
+ },
145
+ }),
146
+
147
+ command("jobs runs", {
148
+ summary: "List individual job runs, newest first",
149
+ description: "Scope with --source to one job. The printed run id is `<traceId>:<spanId>` and is what `jobs show` expects.",
150
+ examples: ["cld admin jobs runs --source gateway:telemetry:cleanup --json", "cld admin jobs runs --per-page 10"],
151
+ flags: {
152
+ source: flag.string({ description: "Restrict to one job source" }),
153
+ window: windowFlag(),
154
+ ...paginationFlags({ defaultPerPage: 25, maxPerPage: 200 }),
155
+ },
156
+ run: async ({ ctx, flags }) => {
157
+ const raw = await apiGet<{ items: TraceSpan[] }>(
158
+ ctx,
159
+ `/api/gateway/jobs/runs${queryString({ source: flags.source, window: flags.window, ...pageQuery(flags) })}`,
160
+ );
161
+ printJsonOrTable(
162
+ ctx,
163
+ raw,
164
+ raw.items.map((span) => ({
165
+ run: runKey(span),
166
+ source: span.source,
167
+ status: span.status,
168
+ startedAt: span.startedAt ?? "-",
169
+ durationMs: formatMs(span.durationMs),
170
+ error: truncate(span.statusMessage, 60) ?? "-",
171
+ })),
172
+ [
173
+ { key: "run", label: "Run" },
174
+ { key: "source", label: "Source" },
175
+ { key: "status", label: "Status" },
176
+ { key: "startedAt", label: "Started" },
177
+ { key: "durationMs", label: "Duration" },
178
+ { key: "error", label: "Error" },
179
+ ],
180
+ );
181
+ },
182
+ }),
183
+
184
+ command("jobs show", {
185
+ summary: "Show one job run with its recorded events",
186
+ description: "Events are the closest thing a background job has to a log. Take the run id from `jobs runs`.",
187
+ examples: ["cld admin jobs show <traceId>:<spanId> --json"],
188
+ args: { run: arg.required({ valueLabel: "run" }) },
189
+ run: async ({ ctx, args }) => {
190
+ const raw = await apiGet<{ span: TraceSpan | null; events: TraceEvent[] }>(
191
+ ctx,
192
+ `/api/gateway/jobs/runs/${encodeURIComponent(args.run)}`,
193
+ );
194
+ if (ctx.options.output === "json") {
195
+ ctx.json(raw);
196
+ return;
197
+ }
198
+ if (!raw.span) {
199
+ ctx.print("Run not found.");
200
+ return;
201
+ }
202
+ ctx.print(`${raw.span.source} — ${raw.span.status}`);
203
+ ctx.print(`started: ${raw.span.startedAt ?? "-"} duration: ${formatMs(raw.span.durationMs)}`);
204
+ if (raw.span.summary) ctx.print(`summary: ${JSON.stringify(raw.span.summary)}`);
205
+ if (raw.span.statusMessage) ctx.print(`status message: ${raw.span.statusMessage}`);
206
+ ctx.print("");
207
+ ctx.table(
208
+ raw.events.map((event) => ({
209
+ occurredAt: event.occurredAt ?? "-",
210
+ severity: event.severity ?? "-",
211
+ name: event.name,
212
+ body: truncate(event.body, 80) ?? "-",
213
+ })),
214
+ [
215
+ { key: "occurredAt", label: "At" },
216
+ { key: "severity", label: "Level" },
217
+ { key: "name", label: "Event" },
218
+ { key: "body", label: "Detail" },
219
+ ],
220
+ );
221
+ },
222
+ }),
223
+ ];
@@ -0,0 +1,122 @@
1
+ import { arg, type CloudCliContext, cliText, command, confirmFlag, flag, printRows, printStructured, readCliInput } from "../index";
2
+ import { apiGet } from "./shared";
3
+
4
+ const LEGAL_KINDS = ["terms", "privacy", "imprint"] as const;
5
+ type LegalKind = (typeof LEGAL_KINDS)[number];
6
+ type LegalMode = "local" | "external";
7
+
8
+ type LegalDocument = {
9
+ kind: LegalKind;
10
+ path: string;
11
+ mode: LegalMode;
12
+ content: string;
13
+ url: string;
14
+ };
15
+
16
+ const legalApiPath = "/api/admin/core/settings";
17
+
18
+ const parseKind = (value: string): LegalKind => {
19
+ if (LEGAL_KINDS.includes(value as LegalKind)) return value as LegalKind;
20
+ throw new Error(`Document must be one of: ${LEGAL_KINDS.join(", ")}.`);
21
+ };
22
+
23
+ const listDocuments = async (ctx: CloudCliContext): Promise<LegalDocument[]> =>
24
+ (await apiGet<{ items: LegalDocument[] }>(ctx, `${legalApiPath}/legal`)).items;
25
+
26
+ const getDocument = async (ctx: CloudCliContext, kind: LegalKind): Promise<LegalDocument> => {
27
+ const document = (await listDocuments(ctx)).find((item) => item.kind === kind);
28
+ if (!document) throw new Error(`Legal document "${kind}" was not found.`);
29
+ return document;
30
+ };
31
+
32
+ const saveSettings = async (ctx: CloudCliContext, body: { updates?: Record<string, unknown>; resets?: string[] }): Promise<void> => {
33
+ const response = await ctx.fetch(legalApiPath, {
34
+ method: "PUT",
35
+ headers: { "Content-Type": "application/json" },
36
+ body: JSON.stringify(body),
37
+ });
38
+ if (response.ok) return;
39
+ await ctx.readJson(response);
40
+ throw new Error(`Settings request failed with ${response.status}.`);
41
+ };
42
+
43
+ const printDocument = (ctx: CloudCliContext, document: LegalDocument) => {
44
+ if (printStructured(ctx, document)) return;
45
+ ctx.print(
46
+ [
47
+ `${document.kind} (${document.mode})`,
48
+ `Path: ${document.path}`,
49
+ ...(document.mode === "external" ? [`URL: ${document.url || "-"}`] : ["", document.content || "No local content configured."]),
50
+ ].join("\n"),
51
+ );
52
+ };
53
+
54
+ export const legalCommands = [
55
+ command("legal list", {
56
+ summary: "List Terms, Privacy, and Imprint configuration",
57
+ run: async ({ ctx }) => {
58
+ const items = await listDocuments(ctx);
59
+ printRows(
60
+ ctx,
61
+ items,
62
+ items.map((document) => ({
63
+ document: document.kind,
64
+ source: document.mode,
65
+ target: document.mode === "external" ? document.url : document.path,
66
+ })),
67
+ [{ key: "document" }, { key: "source" }, { key: "target" }],
68
+ );
69
+ },
70
+ }),
71
+ command("legal get", {
72
+ summary: "Show one legal document configuration",
73
+ args: { document: arg.required({ valueLabel: "terms|privacy|imprint" }) },
74
+ run: async ({ ctx, args }) => printDocument(ctx, await getDocument(ctx, parseKind(args.document))),
75
+ }),
76
+ command("legal set", {
77
+ summary: "Publish local Markdown or redirect to an external URL",
78
+ args: { document: arg.required({ valueLabel: "terms|privacy|imprint" }) },
79
+ flags: {
80
+ content: flag.input({ description: "Local Markdown; pass a value, --content-file, or --stdin" }),
81
+ url: flag.string({ description: "External URL" }),
82
+ },
83
+ examples: ["cld admin legal set terms --content-file ./terms.md", "cld admin legal set privacy --url https://example.org/privacy"],
84
+ run: async ({ ctx, args, flags }) => {
85
+ const kind = parseKind(args.document);
86
+ const contentProvided = flags.content.provided;
87
+ const urlProvided = flags.url !== undefined;
88
+ if (contentProvided === urlProvided) throw new Error("Pass exactly one of --content, --content-file, --stdin, or --url.");
89
+
90
+ const mode: LegalMode = contentProvided ? "local" : "external";
91
+ const value = contentProvided ? await readCliInput(flags.content, { label: `${kind} Markdown`, required: true }) : flags.url?.trim();
92
+ if (mode === "external" && !value) throw new Error("--url must not be empty.");
93
+
94
+ await saveSettings(ctx, {
95
+ updates: {
96
+ [`legal.${kind}.mode`]: mode,
97
+ [`legal.${kind}.${mode === "local" ? "content" : "url"}`]: value ?? "",
98
+ },
99
+ });
100
+ const result = { document: kind, mode };
101
+ if (!printStructured(ctx, result)) {
102
+ ctx.print(cliText(ctx, { en: `Updated ${kind}.`, de: `${kind} wurde aktualisiert.` }));
103
+ }
104
+ },
105
+ }),
106
+ command("legal reset", {
107
+ summary: "Reset one legal document to its defaults",
108
+ args: { document: arg.required({ valueLabel: "terms|privacy|imprint" }) },
109
+ flags: { yes: confirmFlag("Reset the legal document") },
110
+ run: async ({ ctx, args, flags }) => {
111
+ const kind = parseKind(args.document);
112
+ if (!flags.yes) throw new Error("Refusing to reset without --yes.");
113
+ await saveSettings(ctx, {
114
+ resets: [`legal.${kind}.mode`, `legal.${kind}.content`, `legal.${kind}.url`],
115
+ });
116
+ const result = { document: kind, reset: true };
117
+ if (!printStructured(ctx, result)) {
118
+ ctx.print(cliText(ctx, { en: `Reset ${kind}.`, de: `${kind} wurde zurückgesetzt.` }));
119
+ }
120
+ },
121
+ }),
122
+ ];
@@ -0,0 +1,53 @@
1
+ import { LinuxIdentityConfigurationSchema, type LinuxIdentityConfiguration } from "../../contracts/posix";
2
+ import { cliText, command, confirmFlag, flag, printStructured, readCliInput } from "../index";
3
+ import { apiGet, apiJson, queryString } from "./shared";
4
+
5
+ const path = "/api/admin/core/linux-identities";
6
+
7
+ export const linuxCommands = [
8
+ command("linux preview", {
9
+ summary: "Inspect one page of Linux identities without changing accounts",
10
+ flags: {
11
+ after: flag.string({ description: "Continue after the previous page's nextCursor" }),
12
+ search: flag.string({ description: "Filter by username substring" }),
13
+ scope: flag.enum(["ready", "all"] as const, { description: "Show eligible accounts missing Linux attributes or all accounts (default)" }),
14
+ },
15
+ async run({ ctx, flags }) {
16
+ const result = await apiGet<unknown>(ctx, `${path}${queryString({ after: flags.after, search: flags.search, scope: flags.scope })}`);
17
+ if (!printStructured(ctx, result)) ctx.print(JSON.stringify(result, null, 2));
18
+ },
19
+ }),
20
+ command("linux config get", {
21
+ summary: "Export the complete Linux identity configuration",
22
+ async run({ ctx }) {
23
+ const { config } = await apiGet<{ config: LinuxIdentityConfiguration }>(ctx, path);
24
+ if (!printStructured(ctx, config)) ctx.print(JSON.stringify(config, null, 2));
25
+ },
26
+ }),
27
+ command("linux config set", {
28
+ summary: "Replace the complete Linux identity configuration",
29
+ flags: {
30
+ config: flag.input({ required: true, description: "Configuration JSON: --config, --config-file, or --stdin" }),
31
+ rangeReserved: flag.boolean({ name: "range-reserved", description: "Confirm the range is reserved across all connected systems" }),
32
+ yes: confirmFlag("Confirm replacing Linux identity configuration"),
33
+ },
34
+ async run({ ctx, flags }) {
35
+ if (!flags.yes)
36
+ throw new Error(
37
+ cliText(ctx, { en: "Refusing to change configuration without --yes.", de: "Konfiguration wird ohne --yes nicht geändert." }),
38
+ );
39
+ const input = await readCliInput(flags.config, { label: "Linux configuration", required: true });
40
+ if (input === undefined) throw new Error("Missing Linux configuration.");
41
+ const config = LinuxIdentityConfigurationSchema.parse(JSON.parse(input));
42
+ if (config.enabled && !flags.rangeReserved)
43
+ throw new Error(
44
+ cliText(ctx, {
45
+ en: "Enabling Linux identities requires --range-reserved.",
46
+ de: "Zum Aktivieren ist --range-reserved erforderlich.",
47
+ }),
48
+ );
49
+ const result = await apiJson<unknown>(ctx, "PUT", `${path}/configuration`, { config, rangeReserved: flags.rangeReserved });
50
+ if (!printStructured(ctx, result)) ctx.print(JSON.stringify(result, null, 2));
51
+ },
52
+ }),
53
+ ];