@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,324 @@
1
+ /**
2
+ * Structured application logs — listing, aggregation, and the correlation
3
+ * helpers (`problems`, `explain`) that turn one entry into context.
4
+ */
5
+ import { arg, type CloudCliContext, type CloudCliTableColumn, command, confirmFlag, flag, paginationFlags } from "../index";
6
+ import type { GatewayHealth } from "./gateway";
7
+ import {
8
+ apiGet,
9
+ type Pagination,
10
+ pageQuery,
11
+ parseLookbackHours,
12
+ printJsonOrTable,
13
+ queryString,
14
+ safeCollect,
15
+ sleep,
16
+ sortByTimeDesc,
17
+ truncate,
18
+ } from "./shared";
19
+ import type { TelemetryEvent } from "./telemetry";
20
+
21
+ export type LogEntry = {
22
+ id: string;
23
+ level: "debug" | "info" | "warn" | "error";
24
+ source: string;
25
+ message: string;
26
+ metadata: Record<string, unknown> | null;
27
+ createdAt: string;
28
+ };
29
+
30
+ export type LogSummary = {
31
+ total: number;
32
+ errors24h: number;
33
+ warnings24h: number;
34
+ total24h: number;
35
+ sources: number;
36
+ lastErrorAt: string | null;
37
+ };
38
+
39
+ export type LogStatsResponse = {
40
+ groupBy: "source" | "level";
41
+ items: { key: string; count: number }[];
42
+ };
43
+
44
+ export const PG_BIGINT_MAX = 9_223_372_036_854_775_807n;
45
+
46
+ export const parseLogId = (value: string): string => {
47
+ if (!/^[1-9]\d*$/.test(value)) throw new Error("Log id must be a positive integer.");
48
+ if (BigInt(value) > PG_BIGINT_MAX) throw new Error("Log id must fit into a Postgres BIGINT.");
49
+ return value;
50
+ };
51
+
52
+ export const logRows = (entries: LogEntry[], messageLength = 120) =>
53
+ entries.map((entry) => ({
54
+ time: entry.createdAt,
55
+ level: entry.level,
56
+ source: entry.source,
57
+ message: truncate(entry.message, messageLength),
58
+ metadata: entry.metadata ? "yes" : "",
59
+ id: entry.id,
60
+ }));
61
+
62
+ export const logColumns = [
63
+ { key: "time" },
64
+ { key: "level" },
65
+ { key: "source" },
66
+ { key: "message" },
67
+ { key: "metadata" },
68
+ { key: "id" },
69
+ ] satisfies CloudCliTableColumn<ReturnType<typeof logRows>[number]>[];
70
+
71
+ export const getLogs = async (
72
+ ctx: CloudCliContext,
73
+ params: {
74
+ search?: string;
75
+ source?: string;
76
+ level?: "debug" | "info" | "warn" | "error";
77
+ sinceHours?: number;
78
+ page?: number;
79
+ perPage?: number;
80
+ },
81
+ ): Promise<{ entries: LogEntry[]; pagination: Pagination }> =>
82
+ apiGet(
83
+ ctx,
84
+ `/api/logging${queryString({
85
+ search: params.search,
86
+ source: params.source,
87
+ level: params.level,
88
+ since_hours: params.sinceHours,
89
+ ...pageQuery(params),
90
+ })}`,
91
+ );
92
+
93
+ export const trimLogMessages = <T extends { entries: LogEntry[] }>(result: T, messageLength: number): T => ({
94
+ ...result,
95
+ entries: result.entries.map((entry) => ({ ...entry, message: truncate(entry.message, messageLength) })),
96
+ });
97
+
98
+ export const logCommands = [
99
+ command("logs list", {
100
+ summary: "List log entries with source, level, and free-text filters",
101
+ flags: {
102
+ search: flag.string({ aliases: ["q"], description: "Free-text log search" }),
103
+ source: flag.string({ description: "Log source filter" }),
104
+ level: flag.enum(["debug", "info", "warn", "error"], { description: "Log level filter" }),
105
+ since: flag.string({ description: "Lookback window like 30m, 6h, or 7d" }),
106
+ messageLength: flag.int({ name: "message-length", default: 140, min: 40, max: 1000, description: "Table message preview length" }),
107
+ ...paginationFlags({ defaultPerPage: 50, maxPerPage: 100 }),
108
+ },
109
+ run: async ({ ctx, flags }) => {
110
+ const result = await getLogs(ctx, { ...flags, sinceHours: flags.since ? parseLookbackHours(flags.since) : undefined });
111
+ printJsonOrTable(ctx, result, logRows(result.entries, flags.messageLength), logColumns);
112
+ },
113
+ }),
114
+ command("logs summary", {
115
+ summary: "Show log volume and recent error summary",
116
+ run: async ({ ctx }) => {
117
+ const result = await apiGet<LogSummary>(ctx, "/api/logging/summary");
118
+ printJsonOrTable(
119
+ ctx,
120
+ result,
121
+ [result],
122
+ [{ key: "total" }, { key: "total24h" }, { key: "errors24h" }, { key: "warnings24h" }, { key: "sources" }, { key: "lastErrorAt" }],
123
+ );
124
+ },
125
+ }),
126
+ command("logs stats", {
127
+ summary: "Group log volume by source or level",
128
+ flags: {
129
+ groupBy: flag.enum(["source", "level"], { name: "group-by", default: "source", description: "Stats dimension" }),
130
+ since: flag.string({ default: "24h", description: "Lookback window like 30m, 6h, or 7d" }),
131
+ limit: flag.int({ default: 50, min: 1, max: 200, description: "Maximum groups" }),
132
+ },
133
+ run: async ({ ctx, flags }) => {
134
+ const result = await apiGet<LogStatsResponse>(
135
+ ctx,
136
+ `/api/logging/stats${queryString({
137
+ group_by: flags.groupBy,
138
+ since_hours: parseLookbackHours(flags.since),
139
+ limit: flags.limit,
140
+ })}`,
141
+ );
142
+ printJsonOrTable(ctx, result, result.items, [
143
+ { key: "key", label: flags.groupBy === "level" ? "level" : "source" },
144
+ { key: "count" },
145
+ ]);
146
+ },
147
+ }),
148
+ command("logs errors", {
149
+ summary: "List recent error logs for incident debugging",
150
+ flags: {
151
+ search: flag.string({ aliases: ["q"], description: "Free-text log search" }),
152
+ source: flag.string({ description: "Log source filter" }),
153
+ since: flag.string({ default: "24h", description: "Lookback window like 30m, 6h, or 7d" }),
154
+ messageLength: flag.int({ name: "message-length", default: 180, min: 40, max: 1000, description: "Table message preview length" }),
155
+ ...paginationFlags({ defaultPerPage: 25, maxPerPage: 100 }),
156
+ },
157
+ run: async ({ ctx, flags }) => {
158
+ const result = await getLogs(ctx, { ...flags, level: "error", sinceHours: parseLookbackHours(flags.since) });
159
+ printJsonOrTable(ctx, result, logRows(result.entries, flags.messageLength), logColumns);
160
+ },
161
+ }),
162
+ command("logs problems", {
163
+ summary: "List recent warn and error logs together",
164
+ flags: {
165
+ search: flag.string({ aliases: ["q"], description: "Free-text log search" }),
166
+ source: flag.string({ description: "Log source filter" }),
167
+ since: flag.string({ default: "24h", description: "Lookback window like 30m, 6h, or 7d" }),
168
+ limit: flag.int({ default: 50, min: 1, max: 100, description: "Maximum combined rows" }),
169
+ messageLength: flag.int({ name: "message-length", default: 160, min: 40, max: 1000, description: "Table message preview length" }),
170
+ },
171
+ run: async ({ ctx, flags }) => {
172
+ const perPage = flags.limit ?? 50;
173
+ const sinceHours = parseLookbackHours(flags.since);
174
+ const [errors, warnings] = await Promise.all([
175
+ getLogs(ctx, { search: flags.search, source: flags.source, level: "error", sinceHours, perPage }),
176
+ getLogs(ctx, { search: flags.search, source: flags.source, level: "warn", sinceHours, perPage }),
177
+ ]);
178
+ const entries = sortByTimeDesc([...errors.entries, ...warnings.entries]).slice(0, perPage);
179
+ const result = { entries, totals: { errors: errors.pagination.total, warnings: warnings.pagination.total } };
180
+ printJsonOrTable(ctx, result, logRows(entries, flags.messageLength), logColumns);
181
+ },
182
+ }),
183
+ command("logs show", {
184
+ summary: "Show one log entry with full message and metadata",
185
+ args: { id: arg.required({ valueLabel: "id" }) },
186
+ run: async ({ ctx, args }) => {
187
+ const id = parseLogId(args.id);
188
+ const result = await apiGet<LogEntry>(ctx, `/api/logging/${id}`);
189
+ if (ctx.options.output === "json") {
190
+ ctx.json(result);
191
+ return;
192
+ }
193
+ const metadata = result.metadata ? `\nmetadata:\n${JSON.stringify(result.metadata, null, 2)}` : "";
194
+ ctx.print(`[${result.createdAt}] ${result.level} ${result.source} #${result.id}\n${result.message}${metadata}`);
195
+ },
196
+ }),
197
+ command("logs explain", {
198
+ summary: "Collect one log entry with nearby diagnostic context",
199
+ args: { id: arg.required({ valueLabel: "id" }) },
200
+ flags: {
201
+ since: flag.string({ default: "24h", description: "Context lookback window like 30m, 6h, or 7d" }),
202
+ limit: flag.int({ default: 20, min: 1, max: 50, description: "Context rows per section" }),
203
+ messageLength: flag.int({ name: "message-length", default: 500, min: 40, max: 5000, description: "Context log message length" }),
204
+ },
205
+ run: async ({ ctx, args, flags }) => {
206
+ const id = parseLogId(args.id);
207
+ const log = await apiGet<LogEntry>(ctx, `/api/logging/${id}`);
208
+ const sinceHours = parseLookbackHours(flags.since);
209
+ const limit = flags.limit ?? 20;
210
+ const [sameSource, recentProblems, telemetryErrors, appHealth] = await Promise.all([
211
+ safeCollect("same source logs", () => getLogs(ctx, { source: log.source, sinceHours, perPage: limit })),
212
+ safeCollect("recent warn/error logs", async () => {
213
+ const [errors, warnings] = await Promise.all([
214
+ getLogs(ctx, { level: "error", sinceHours, perPage: limit }),
215
+ getLogs(ctx, { level: "warn", sinceHours, perPage: limit }),
216
+ ]);
217
+ return sortByTimeDesc([...errors.entries, ...warnings.entries]).slice(0, limit);
218
+ }),
219
+ safeCollect("telemetry errors", () =>
220
+ apiGet<{ items: TelemetryEvent[]; total: number }>(
221
+ ctx,
222
+ `/api/gateway/telemetry/events${queryString({ errors: "1", hours: sinceHours, page: 1, per_page: limit })}`,
223
+ ),
224
+ ),
225
+ safeCollect("app health", async () => {
226
+ const health = await apiGet<GatewayHealth>(ctx, "/api/gateway/health");
227
+ return health.apps.find((app) => app.id === log.source || app.name === log.source) ?? null;
228
+ }),
229
+ ]);
230
+ const trimEntry = (entry: LogEntry) => ({ ...entry, message: truncate(entry.message, flags.messageLength ?? 500) });
231
+ const bundle = {
232
+ log,
233
+ lookbackHours: sinceHours,
234
+ sameSource:
235
+ sameSource.ok && flags.messageLength
236
+ ? { ...sameSource, data: trimLogMessages(sameSource.data, flags.messageLength) }
237
+ : sameSource,
238
+ recentProblems: recentProblems.ok ? { ...recentProblems, data: recentProblems.data.map(trimEntry) } : recentProblems,
239
+ telemetryErrors,
240
+ appHealth,
241
+ };
242
+ if (ctx.options.output === "json") {
243
+ ctx.json(bundle);
244
+ return;
245
+ }
246
+ const lines = [
247
+ `[${log.createdAt}] ${log.level} ${log.source} #${log.id}`,
248
+ log.message,
249
+ "",
250
+ `same-source logs: ${sameSource.ok ? sameSource.data.entries.length : `unavailable (${sameSource.error})`}`,
251
+ `recent problems: ${recentProblems.ok ? recentProblems.data.length : `unavailable (${recentProblems.error})`}`,
252
+ `telemetry errors: ${telemetryErrors.ok ? telemetryErrors.data.items.length : `unavailable (${telemetryErrors.error})`}`,
253
+ `app health: ${appHealth.ok ? (appHealth.data ? `${appHealth.data.status} (${appHealth.data.id})` : "no matching app") : `unavailable (${appHealth.error})`}`,
254
+ ];
255
+ ctx.print(lines.join("\n"));
256
+ },
257
+ }),
258
+ command("logs tail", {
259
+ summary: "Show latest logs, optionally filtered",
260
+ flags: {
261
+ search: flag.string({ aliases: ["q"], description: "Free-text log search" }),
262
+ source: flag.string({ description: "Log source filter" }),
263
+ level: flag.enum(["debug", "info", "warn", "error"], { description: "Log level filter" }),
264
+ since: flag.string({ description: "Lookback window like 30m, 6h, or 7d" }),
265
+ lines: flag.int({ default: 20, min: 1, max: 100, description: "Rows to show" }),
266
+ follow: flag.boolean({ aliases: ["f"], description: "Poll and print new rows until interrupted" }),
267
+ interval: flag.int({ default: 2, min: 1, max: 60, description: "Follow poll interval in seconds" }),
268
+ messageLength: flag.int({ name: "message-length", default: 180, min: 40, max: 1000, description: "Table message preview length" }),
269
+ },
270
+ run: async ({ ctx, flags }) => {
271
+ const load = () =>
272
+ getLogs(ctx, {
273
+ search: flags.search,
274
+ source: flags.source,
275
+ level: flags.level,
276
+ sinceHours: flags.since ? parseLookbackHours(flags.since) : undefined,
277
+ perPage: flags.lines ?? 20,
278
+ });
279
+ const result = await load();
280
+ printJsonOrTable(ctx, result, logRows(result.entries, flags.messageLength), logColumns);
281
+ if (!flags.follow) return;
282
+ const seen = new Set(result.entries.map((entry) => entry.id));
283
+ while (true) {
284
+ await sleep((flags.interval ?? 2) * 1000);
285
+ const next = await load();
286
+ const fresh = next.entries.filter((entry) => !seen.has(entry.id)).reverse();
287
+ for (const entry of fresh) seen.add(entry.id);
288
+ if (fresh.length === 0) continue;
289
+ if (ctx.options.output === "json") {
290
+ for (const entry of fresh) ctx.json(entry);
291
+ } else {
292
+ ctx.table(logRows(fresh, flags.messageLength), logColumns);
293
+ }
294
+ }
295
+ },
296
+ }),
297
+ command("logs sources", {
298
+ summary: "List log sources",
299
+ run: async ({ ctx }) => {
300
+ const result = await apiGet<{ sources: string[] }>(ctx, "/api/logging/sources");
301
+ printJsonOrTable(
302
+ ctx,
303
+ result,
304
+ result.sources.map((source) => ({ source })),
305
+ [{ key: "source" }],
306
+ );
307
+ },
308
+ }),
309
+ command("logs cleanup", {
310
+ summary: "Delete old retained logs",
311
+ flags: {
312
+ days: flag.int({ default: 30, min: 1, description: "Delete logs older than this many days" }),
313
+ yes: confirmFlag("Confirm log cleanup"),
314
+ },
315
+ run: async ({ ctx, flags }) => {
316
+ if (!flags.yes) throw new Error("Refusing to clean logs without --yes.");
317
+ const result = await ctx.readJson<{ deleted: number }>(
318
+ await ctx.fetch(`/api/logging/cleanup${queryString({ days: flags.days })}`, { method: "DELETE" }),
319
+ );
320
+ if (ctx.options.output === "json") ctx.json(result);
321
+ else ctx.print(`Deleted ${result.deleted} log entries.`);
322
+ },
323
+ }),
324
+ ];
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Prometheus metrics snapshot, catalogue, and scrape tokens.
3
+ */
4
+ import { arg, command, confirmFlag, flag } from "../index";
5
+ import { parseExpiresAt } from "./notifications";
6
+ import { apiGet, apiJson, formatMs, printJsonOrTable, truncate } from "./shared";
7
+
8
+ export type MetricsToken = {
9
+ id: string;
10
+ name: string;
11
+ tokenPrefix: string;
12
+ expiresAt: string | null;
13
+ lastUsedAt: string | null;
14
+ createdAt: string;
15
+ };
16
+
17
+ export type MetricsCollector = {
18
+ id: string;
19
+ name: string;
20
+ description: string;
21
+ status: "ok" | "error";
22
+ series: number;
23
+ durationMs: number;
24
+ lastRunAt: string;
25
+ error: string | null;
26
+ metricNames: string[];
27
+ };
28
+
29
+ export const metricsCommands = [
30
+ command("metrics status", {
31
+ summary: "Show metric collector status",
32
+ run: async ({ ctx }) => {
33
+ const result = await apiGet<{ generatedAt: string; series: number; collectors: MetricsCollector[] }>(
34
+ ctx,
35
+ "/api/gateway/metrics/snapshot",
36
+ );
37
+ const rows = result.collectors.map((collector) => ({
38
+ id: collector.id,
39
+ status: collector.status,
40
+ series: collector.series,
41
+ duration: formatMs(collector.durationMs),
42
+ metrics: collector.metricNames.length,
43
+ error: truncate(collector.error, 60),
44
+ }));
45
+ printJsonOrTable(ctx, result, rows, [
46
+ { key: "id" },
47
+ { key: "status" },
48
+ { key: "series" },
49
+ { key: "duration" },
50
+ { key: "metrics" },
51
+ { key: "error" },
52
+ ]);
53
+ },
54
+ }),
55
+ command("metrics read", {
56
+ summary: "Read raw Prometheus metrics",
57
+ run: async ({ ctx }) => {
58
+ const response = await ctx.fetch("/metrics");
59
+ if (!response.ok) {
60
+ const text = await response.text().catch(() => "");
61
+ throw new Error(`${response.status} ${text.trim() || response.statusText}`);
62
+ }
63
+ ctx.print((await response.text()).trimEnd());
64
+ },
65
+ }),
66
+ command("metrics catalogue", {
67
+ summary: "List exposed metric names by collector",
68
+ flags: {
69
+ search: flag.string({ aliases: ["q"], description: "Search metric or collector text" }),
70
+ category: flag.string({ description: "Collector id or name filter" }),
71
+ },
72
+ run: async ({ ctx, flags }) => {
73
+ const result = await apiGet<{ generatedAt: string; series: number; collectors: MetricsCollector[] }>(
74
+ ctx,
75
+ "/api/gateway/metrics/snapshot",
76
+ );
77
+ const search = flags.search?.toLowerCase();
78
+ const category = flags.category?.toLowerCase();
79
+ const rows = result.collectors
80
+ .filter((collector) => !category || collector.id.toLowerCase() === category || collector.name.toLowerCase() === category)
81
+ .flatMap((collector) =>
82
+ collector.metricNames.map((metric) => ({
83
+ collector: collector.id,
84
+ name: metric,
85
+ status: collector.status,
86
+ series: collector.series,
87
+ description: truncate(collector.description, 90),
88
+ })),
89
+ )
90
+ .filter((row) => {
91
+ if (!search) return true;
92
+ return `${row.collector} ${row.name} ${row.description}`.toLowerCase().includes(search);
93
+ })
94
+ .sort((a, b) => a.collector.localeCompare(b.collector) || a.name.localeCompare(b.name));
95
+ printJsonOrTable(ctx, { generatedAt: result.generatedAt, items: rows }, rows, [
96
+ { key: "collector" },
97
+ { key: "name" },
98
+ { key: "status" },
99
+ { key: "series" },
100
+ { key: "description" },
101
+ ]);
102
+ },
103
+ }),
104
+ command("metrics tokens list", {
105
+ summary: "List metrics bearer tokens",
106
+ run: async ({ ctx }) => {
107
+ const result = await apiGet<{ items: MetricsToken[] }>(ctx, "/api/gateway/metrics/tokens");
108
+ const rows = result.items.map((token) => ({
109
+ name: token.name,
110
+ prefix: token.tokenPrefix,
111
+ expires: token.expiresAt ?? "never",
112
+ lastUsed: token.lastUsedAt ?? "never",
113
+ id: token.id,
114
+ }));
115
+ printJsonOrTable(ctx, result, rows, [{ key: "name" }, { key: "prefix" }, { key: "expires" }, { key: "lastUsed" }, { key: "id" }]);
116
+ },
117
+ }),
118
+ command("metrics tokens create", {
119
+ summary: "Create a metrics bearer token",
120
+ args: { name: arg.required({ valueLabel: "name" }) },
121
+ flags: { expiresAt: flag.string({ name: "expires-at", description: "ISO expiry timestamp, never, or null" }) },
122
+ run: async ({ ctx, args, flags }) => {
123
+ const result = await apiJson<{ token: string; credential: MetricsToken }>(ctx, "POST", "/api/gateway/metrics/tokens", {
124
+ name: args.name,
125
+ expiresAt: parseExpiresAt(flags.expiresAt) ?? null,
126
+ });
127
+ if (ctx.options.output === "json") ctx.json(result);
128
+ else ctx.print(`Token: ${result.token}\nPrefix: ${result.credential.tokenPrefix}\nStore this token now. It cannot be shown again.`);
129
+ },
130
+ }),
131
+ command("metrics tokens revoke", {
132
+ summary: "Revoke a metrics bearer token",
133
+ args: { id: arg.required({ valueLabel: "id" }) },
134
+ flags: { yes: confirmFlag("Confirm metrics token revocation") },
135
+ run: async ({ ctx, args, flags }) => {
136
+ if (!flags.yes) throw new Error("Refusing to revoke a metrics token without --yes.");
137
+ const result = await apiJson<{ message: string }>(ctx, "DELETE", `/api/gateway/metrics/tokens/${encodeURIComponent(args.id)}`);
138
+ if (ctx.options.output === "json") ctx.json(result);
139
+ else ctx.print(result.message);
140
+ },
141
+ }),
142
+ ];
@@ -0,0 +1,167 @@
1
+ /** Read-only NATS operations; machine output retains snapshot completeness. */
2
+ import { arg, type CloudCliContext, command, flag } from "../index";
3
+ import { apiGet, formatBytes, queryString } from "./shared";
4
+
5
+ type Replica = { name: string; current: boolean | null; offline: boolean | null; lag: number | null };
6
+ type Cluster = { name: string | null; leader: string | null; replicas: Replica[] };
7
+ type Node = {
8
+ id: string;
9
+ name: string;
10
+ version: string;
11
+ jetstreamEnabled: boolean;
12
+ metadataStatus: string;
13
+ processMemory: number | null;
14
+ storage: number | null;
15
+ meta: Cluster | null;
16
+ };
17
+ type Stream = {
18
+ name: string;
19
+ kind: string;
20
+ storage: string;
21
+ messages: number;
22
+ bytes: number;
23
+ consumers: number;
24
+ replicas: number;
25
+ deadLetter: boolean;
26
+ replicationStatus: string | null;
27
+ hasDeadLetters: boolean;
28
+ cluster: Cluster | null;
29
+ sync: { namespace: string; owner: string; kind: string; id: string } | null;
30
+ };
31
+ type Consumer = { name: string; pending: number; ackPending: number; redelivered: number; cluster: Cluster | null };
32
+ export type NatsSnapshot = {
33
+ sampledAt: string;
34
+ cluster: { status: string; issue?: string; nodes: Node[] };
35
+ inventory: {
36
+ status: string;
37
+ issue?: string;
38
+ streams: Stream[];
39
+ total: number;
40
+ matchedTotal: number | null;
41
+ scannedTotal: number;
42
+ accountTotal: number | null;
43
+ offset: number;
44
+ limit: number;
45
+ nextOffset: number | null;
46
+ consumers: Consumer[];
47
+ consumerTotal: number | null;
48
+ consumerStream: string | null;
49
+ consumerOffset: number;
50
+ consumerNextOffset: number | null;
51
+ };
52
+ };
53
+ const read = (ctx: CloudCliContext, params: Parameters<typeof queryString>[0] = {}) =>
54
+ apiGet<NatsSnapshot>(ctx, `/api/gateway/nats${queryString(params)}`);
55
+ const machine = (ctx: CloudCliContext, result: NatsSnapshot, section: "nodes" | "streams" | "consumers") => {
56
+ if (ctx.options.output === "json") {
57
+ ctx.json(result);
58
+ return true;
59
+ }
60
+ if (ctx.options.output === "jsonl") {
61
+ const { nodes: _nodes, ...cluster } = result.cluster;
62
+ const { streams: _streams, consumers: _consumers, ...inventory } = result.inventory;
63
+ ctx.jsonLine({ type: "snapshot", sampledAt: result.sampledAt, cluster, inventory });
64
+ for (const item of section === "nodes" ? result.cluster.nodes : result.inventory[section]) ctx.jsonLine({ type: section, ...item });
65
+ return true;
66
+ }
67
+ ctx.print(`Snapshot: ${result.sampledAt}; cluster: ${result.cluster.status}; inventory: ${result.inventory.status}`);
68
+ if (result.cluster.issue) ctx.error(result.cluster.issue);
69
+ if (result.inventory.issue) ctx.error(result.inventory.issue);
70
+ return false;
71
+ };
72
+ export const natsCommands = [
73
+ command("nats status", {
74
+ summary: "Inspect NATS nodes and account inventory completeness",
75
+ run: async ({ ctx }) => {
76
+ const result = await read(ctx);
77
+ if (machine(ctx, result, "nodes")) return;
78
+ ctx.table(
79
+ result.cluster.nodes.map((node) => ({
80
+ name: node.name,
81
+ version: node.version,
82
+ jetstream: node.jetstreamEnabled ? "enabled" : "disabled",
83
+ metadataStatus: node.metadataStatus,
84
+ metadataLeader: node.meta?.leader ?? "unknown",
85
+ memory: formatBytes(node.processMemory),
86
+ storage: formatBytes(node.storage),
87
+ })),
88
+ [
89
+ { key: "name" },
90
+ { key: "version" },
91
+ { key: "jetstream" },
92
+ { key: "metadataStatus" },
93
+ { key: "metadataLeader" },
94
+ { key: "memory" },
95
+ { key: "storage" },
96
+ ],
97
+ );
98
+ },
99
+ }),
100
+ command("nats streams list", {
101
+ summary: "Find NATS streams and buckets; filters apply before pagination",
102
+ flags: {
103
+ app: flag.string({ description: "Exact Sync owner" }),
104
+ namespace: flag.string({ description: "Exact Sync namespace" }),
105
+ resource: flag.string({ description: "Search stream name or Sync resource ID" }),
106
+ problems: flag.boolean({ description: "Only dead-letter or replication problems" }),
107
+ offset: flag.int({ min: 0, default: 0, description: "Filtered result offset" }),
108
+ limit: flag.int({ min: 1, max: 100, default: 50, description: "Rows per page" }),
109
+ },
110
+ run: async ({ ctx, flags }) => {
111
+ const result = await read(ctx, flags);
112
+ if (machine(ctx, result, "streams")) return;
113
+ ctx.table(
114
+ result.inventory.streams.map((stream) => ({
115
+ name: stream.name,
116
+ kind: stream.kind,
117
+ namespace: stream.sync?.namespace ?? "-",
118
+ app: stream.sync?.owner ?? "-",
119
+ resource: stream.sync?.id ?? "-",
120
+ messages: stream.messages,
121
+ storage: formatBytes(stream.bytes),
122
+ replicas: stream.replicas,
123
+ replication: stream.replicationStatus ?? "-",
124
+ deadLetters: stream.hasDeadLetters ? "yes" : "no",
125
+ consumers: stream.consumers,
126
+ })),
127
+ [
128
+ { key: "name" },
129
+ { key: "kind" },
130
+ { key: "namespace" },
131
+ { key: "app" },
132
+ { key: "resource" },
133
+ { key: "messages" },
134
+ { key: "storage" },
135
+ { key: "replicas" },
136
+ { key: "replication" },
137
+ { key: "deadLetters" },
138
+ { key: "consumers" },
139
+ ],
140
+ );
141
+ ctx.print(
142
+ `Matches: ${result.inventory.matchedTotal ?? `${result.inventory.total}+ (incomplete)`}; next offset: ${result.inventory.nextOffset ?? "none"}`,
143
+ );
144
+ },
145
+ }),
146
+ command("nats consumers list", {
147
+ summary: "Inspect consumers and delivery backlog for one NATS stream",
148
+ args: { stream: arg.required({ valueLabel: "stream" }) },
149
+ flags: { offset: flag.int({ min: 0, default: 0, description: "Consumer result offset" }) },
150
+ run: async ({ ctx, args, flags }) => {
151
+ if (!/^[A-Za-z0-9_-]+$/.test(args.stream)) throw new Error("Invalid NATS stream name.");
152
+ const result = await read(ctx, { stream: args.stream, consumerOffset: flags.offset });
153
+ if (machine(ctx, result, "consumers")) return;
154
+ ctx.table(
155
+ result.inventory.consumers.map((consumer) => ({
156
+ name: consumer.name,
157
+ pending: consumer.pending,
158
+ ackPending: consumer.ackPending,
159
+ redelivered: consumer.redelivered,
160
+ leader: consumer.cluster?.leader ?? "-",
161
+ })),
162
+ [{ key: "name" }, { key: "pending" }, { key: "ackPending" }, { key: "redelivered" }, { key: "leader" }],
163
+ );
164
+ ctx.print(`Consumers: ${result.inventory.consumerTotal ?? "unknown"}; next offset: ${result.inventory.consumerNextOffset ?? "none"}`);
165
+ },
166
+ }),
167
+ ];