@stacksjs/ts-cloud 0.7.56 → 0.7.57

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 (467) hide show
  1. package/dist/alerts/alerts.test.d.ts +1 -0
  2. package/dist/alerts/delivery.d.ts +45 -0
  3. package/dist/alerts/evaluator.d.ts +12 -0
  4. package/dist/alerts/health.d.ts +34 -0
  5. package/dist/alerts/index.d.ts +6 -0
  6. package/dist/alerts/model.d.ts +170 -0
  7. package/dist/alerts/store.d.ts +90 -0
  8. package/dist/alerts/telemetry.d.ts +3 -0
  9. package/dist/api/client.d.ts +197 -0
  10. package/dist/api/handler.d.ts +18 -0
  11. package/dist/api/handler.test.d.ts +1 -0
  12. package/dist/api/index.d.ts +6 -0
  13. package/dist/api/openapi.d.ts +2 -0
  14. package/dist/api/service.d.ts +28 -0
  15. package/dist/api/types.d.ts +119 -0
  16. package/dist/auth/encryption.d.ts +3 -0
  17. package/dist/auth/index.d.ts +8 -0
  18. package/dist/auth/mailer.d.ts +9 -0
  19. package/dist/auth/oidc.d.ts +36 -0
  20. package/dist/auth/oidc.test.d.ts +1 -0
  21. package/dist/auth/store.d.ts +121 -0
  22. package/dist/auth/store.test.d.ts +1 -0
  23. package/dist/auth/totp.d.ts +11 -0
  24. package/dist/auth/totp.test.d.ts +1 -0
  25. package/dist/auth/types.d.ts +138 -0
  26. package/dist/automation/index.d.ts +2 -0
  27. package/dist/automation/store.d.ts +46 -0
  28. package/dist/automation/store.test.d.ts +1 -0
  29. package/dist/automation/types.d.ts +66 -0
  30. package/dist/aws/acm.d.ts +0 -4
  31. package/dist/aws/application-autoscaling.d.ts +0 -5
  32. package/dist/aws/backup.d.ts +42 -0
  33. package/dist/aws/bedrock.d.ts +0 -5
  34. package/dist/aws/client.d.ts +3 -6
  35. package/dist/aws/cloudformation.d.ts +0 -4
  36. package/dist/aws/cloudfront-media.test.d.ts +1 -0
  37. package/dist/aws/cloudfront-origin.test.d.ts +1 -0
  38. package/dist/aws/cloudfront.d.ts +87 -3
  39. package/dist/aws/cloudwatch-logs.d.ts +2 -4
  40. package/dist/aws/cloudwatch.d.ts +1 -1
  41. package/dist/aws/comprehend.d.ts +0 -5
  42. package/dist/aws/connect.d.ts +0 -4
  43. package/dist/aws/cost-explorer-cache.d.ts +1 -15
  44. package/dist/aws/cost-explorer.d.ts +18 -9
  45. package/dist/aws/cost-explorer.test.d.ts +1 -0
  46. package/dist/aws/dynamodb.d.ts +0 -4
  47. package/dist/aws/ec2.d.ts +0 -4
  48. package/dist/aws/ecr.d.ts +0 -4
  49. package/dist/aws/ecs.d.ts +17 -7
  50. package/dist/aws/efs.d.ts +0 -4
  51. package/dist/aws/elasticache.d.ts +0 -4
  52. package/dist/aws/elbv2.d.ts +0 -7
  53. package/dist/aws/email.d.ts +0 -10
  54. package/dist/aws/eventbridge.d.ts +0 -4
  55. package/dist/aws/iam.d.ts +1 -5
  56. package/dist/aws/imap-server.d.ts +0 -7
  57. package/dist/aws/index.d.ts +4 -3
  58. package/dist/aws/index.js +31 -31
  59. package/dist/aws/kendra.d.ts +0 -5
  60. package/dist/aws/lambda.d.ts +3 -5
  61. package/dist/aws/opensearch.d.ts +0 -4
  62. package/dist/aws/personalize.d.ts +0 -5
  63. package/dist/aws/polly.d.ts +0 -5
  64. package/dist/aws/rds.d.ts +59 -4
  65. package/dist/aws/rekognition.d.ts +0 -5
  66. package/dist/aws/route53-domains.d.ts +0 -4
  67. package/dist/aws/route53.d.ts +0 -4
  68. package/dist/aws/s3.d.ts +10 -6
  69. package/dist/aws/scheduler.d.ts +0 -4
  70. package/dist/aws/secrets-manager.d.ts +0 -4
  71. package/dist/aws/setup-sms.d.ts +0 -14
  72. package/dist/aws/sms.d.ts +0 -10
  73. package/dist/aws/smtp-server.d.ts +0 -11
  74. package/dist/aws/sns.d.ts +0 -4
  75. package/dist/aws/sqs.d.ts +0 -4
  76. package/dist/aws/ssm.d.ts +0 -4
  77. package/dist/aws/sts.d.ts +0 -4
  78. package/dist/aws/support.d.ts +0 -4
  79. package/dist/aws/textract.d.ts +0 -5
  80. package/dist/aws/transcribe.d.ts +0 -4
  81. package/dist/aws/translate.d.ts +0 -5
  82. package/dist/aws/voice.d.ts +0 -11
  83. package/dist/aws/xray.d.ts +1 -0
  84. package/dist/backups/aws-database-source.d.ts +19 -0
  85. package/dist/backups/aws-database-source.test.d.ts +1 -0
  86. package/dist/backups/aws-infrastructure-source.d.ts +43 -0
  87. package/dist/backups/aws-infrastructure-source.test.d.ts +1 -0
  88. package/dist/backups/control-plane-source.d.ts +16 -0
  89. package/dist/backups/control-plane-source.test.d.ts +1 -0
  90. package/dist/backups/docker-volume-source.d.ts +31 -0
  91. package/dist/backups/docker-volume-source.test.d.ts +1 -0
  92. package/dist/backups/filesystem-source.d.ts +34 -0
  93. package/dist/backups/filesystem-source.test.d.ts +1 -0
  94. package/dist/backups/index.d.ts +10 -0
  95. package/dist/backups/logical-database-source.d.ts +16 -0
  96. package/dist/backups/logical-database-source.test.d.ts +1 -0
  97. package/dist/backups/model.d.ts +121 -0
  98. package/dist/backups/s3-destination.d.ts +85 -0
  99. package/dist/backups/s3-destination.test.d.ts +1 -0
  100. package/dist/backups/service.d.ts +80 -0
  101. package/dist/backups/service.test.d.ts +1 -0
  102. package/dist/backups/store.d.ts +30 -0
  103. package/dist/backups/store.test.d.ts +1 -0
  104. package/dist/bin/cli.js +3360 -1802
  105. package/dist/{chunk-tjjgajbh.js → chunk-01d86gt1.js} +301 -9
  106. package/dist/{chunk-73vcqg6z.js → chunk-0z43x8re.js} +7 -7
  107. package/dist/{chunk-tnztxpcb.js → chunk-32e7ya18.js} +61 -49
  108. package/dist/{chunk-qpj3edwz.js → chunk-4cjrg98a.js} +3 -7
  109. package/dist/{chunk-hsk6fe6x.js → chunk-50jpda9q.js} +2 -2
  110. package/dist/{chunk-93hjhs78.js → chunk-703nkybg.js} +1 -1
  111. package/dist/{chunk-d5vqwj8s.js → chunk-9m6b4m74.js} +755 -720
  112. package/dist/chunk-e05phx5f.js +10 -0
  113. package/dist/{chunk-f29w04w5.js → chunk-exbcsab2.js} +10 -4
  114. package/dist/{chunk-eq08r166.js → chunk-f55nkpvb.js} +2 -2
  115. package/dist/{chunk-3bfv1z4v.js → chunk-fv1bj3yt.js} +63 -56
  116. package/dist/chunk-fw3q88wk.js +48641 -0
  117. package/dist/{chunk-3knnr7wh.js → chunk-he9a874b.js} +170 -2
  118. package/dist/{chunk-zn0nxxa8.js → chunk-hpv68b4a.js} +1070 -1070
  119. package/dist/chunk-k114qwg9.js +12 -0
  120. package/dist/{chunk-r16e02bc.js → chunk-vdk53yyq.js} +3900 -4101
  121. package/dist/{chunk-jp2a8yad.js → chunk-vvkd46k1.js} +15 -13
  122. package/dist/{chunk-vd87cpvn.js → chunk-wj3s95p9.js} +62 -46
  123. package/dist/{chunk-mj1tmhte.js → chunk-x9s0cqfx.js} +1 -1
  124. package/dist/{chunk-3rsfns7x.js → chunk-ybcz6sxc.js} +2048 -387
  125. package/dist/{chunk-arsh1g5h.js → chunk-zqtpg06c.js} +14 -11
  126. package/dist/compose/index.d.ts +6 -0
  127. package/dist/compose/parser.d.ts +13 -0
  128. package/dist/compose/parser.test.d.ts +1 -0
  129. package/dist/compose/runtime.d.ts +10 -0
  130. package/dist/compose/service.d.ts +18 -0
  131. package/dist/compose/store.d.ts +59 -0
  132. package/dist/compose/store.test.d.ts +1 -0
  133. package/dist/compose/templates.d.ts +17 -0
  134. package/dist/compose/types.d.ts +164 -0
  135. package/dist/configuration/backends.d.ts +66 -0
  136. package/dist/configuration/backends.test.d.ts +1 -0
  137. package/dist/configuration/dotenv.d.ts +14 -0
  138. package/dist/configuration/dotenv.test.d.ts +1 -0
  139. package/dist/configuration/index.d.ts +6 -0
  140. package/dist/configuration/migrate.d.ts +10 -0
  141. package/dist/configuration/migrate.test.d.ts +1 -0
  142. package/dist/configuration/model.d.ts +50 -0
  143. package/dist/configuration/service.d.ts +144 -0
  144. package/dist/configuration/service.test.d.ts +1 -0
  145. package/dist/configuration/store.d.ts +33 -0
  146. package/dist/configuration/store.test.d.ts +1 -0
  147. package/dist/control-plane/authorization.d.ts +24 -0
  148. package/dist/control-plane/authorization.test.d.ts +1 -0
  149. package/dist/control-plane/index.d.ts +8 -0
  150. package/dist/control-plane/migrations.d.ts +7 -0
  151. package/dist/control-plane/search.d.ts +25 -0
  152. package/dist/control-plane/store.d.ts +117 -0
  153. package/dist/control-plane/store.test.d.ts +1 -0
  154. package/dist/control-plane/types.d.ts +381 -0
  155. package/dist/cost/reporting.d.ts +24 -0
  156. package/dist/cost/reporting.test.d.ts +1 -0
  157. package/dist/cost/resource-inventory.d.ts +32 -0
  158. package/dist/cost/resource-inventory.test.d.ts +1 -0
  159. package/dist/cost/resource-optimization.d.ts +59 -0
  160. package/dist/cost/resource-optimization.test.d.ts +1 -0
  161. package/dist/data-services/adapters.d.ts +39 -0
  162. package/dist/data-services/aws-transports.d.ts +29 -0
  163. package/dist/data-services/aws-transports.test.d.ts +1 -0
  164. package/dist/data-services/capabilities.d.ts +2 -0
  165. package/dist/data-services/data-services.test.d.ts +1 -0
  166. package/dist/data-services/docker-transport.d.ts +55 -0
  167. package/dist/data-services/docker-transport.test.d.ts +1 -0
  168. package/dist/data-services/index.d.ts +8 -0
  169. package/dist/data-services/model.d.ts +79 -0
  170. package/dist/data-services/secrets.d.ts +13 -0
  171. package/dist/data-services/secrets.test.d.ts +1 -0
  172. package/dist/data-services/service.d.ts +51 -0
  173. package/dist/data-services/store.d.ts +17 -0
  174. package/dist/deploy/container-image.d.ts +28 -0
  175. package/dist/deploy/container-image.test.d.ts +1 -0
  176. package/dist/deploy/dashboard-audit.test.d.ts +1 -0
  177. package/dist/deploy/dashboard-auth.d.ts +4 -23
  178. package/dist/deploy/dashboard-backups.integration.test.d.ts +1 -0
  179. package/dist/deploy/dashboard-configuration.integration.test.d.ts +1 -0
  180. package/dist/deploy/dashboard-control-plane.d.ts +37 -0
  181. package/dist/deploy/dashboard-control-plane.test.d.ts +1 -0
  182. package/dist/deploy/dashboard-data-services.integration.test.d.ts +1 -0
  183. package/dist/deploy/dashboard-environment-scope.test.d.ts +1 -0
  184. package/dist/deploy/dashboard-guard.d.ts +12 -0
  185. package/dist/deploy/dashboard-guard.test.d.ts +1 -0
  186. package/dist/deploy/dashboard-identities.d.ts +15 -0
  187. package/dist/deploy/dashboard-identities.test.d.ts +1 -0
  188. package/dist/deploy/dashboard-jobs.integration.test.d.ts +1 -0
  189. package/dist/deploy/dashboard-login-page.d.ts +5 -5
  190. package/dist/deploy/dashboard-login-page.test.d.ts +1 -0
  191. package/dist/deploy/dashboard-oidc.integration.test.d.ts +1 -0
  192. package/dist/deploy/dashboard-onboarding.integration.test.d.ts +1 -0
  193. package/dist/deploy/dashboard-policy.d.ts +4 -2
  194. package/dist/deploy/dashboard-queue-worker.integration.test.d.ts +1 -0
  195. package/dist/deploy/dashboard-release.integration.test.d.ts +1 -0
  196. package/dist/deploy/dashboard-route-manifest.d.ts +14 -0
  197. package/dist/deploy/dashboard-route-manifest.test.d.ts +1 -0
  198. package/dist/deploy/dashboard-security.integration.test.d.ts +1 -0
  199. package/dist/deploy/dashboard-session.d.ts +3 -14
  200. package/dist/deploy/dashboard-source.integration.test.d.ts +1 -0
  201. package/dist/deploy/dashboard-users.d.ts +3 -0
  202. package/dist/deploy/fullstack-container.d.ts +87 -0
  203. package/dist/deploy/fullstack-container.test.d.ts +1 -0
  204. package/dist/deploy/index.d.ts +7 -2
  205. package/dist/deploy/index.js +65 -25
  206. package/dist/deploy/local-dashboard-server.d.ts +30 -0
  207. package/dist/deploy/quick-deploy.d.ts +1 -1
  208. package/dist/deploy/serverless-operations.d.ts +1 -0
  209. package/dist/deploy/site-target.d.ts +16 -0
  210. package/dist/deploy/static-api-origin.d.ts +107 -0
  211. package/dist/deploy/static-api-origin.test.d.ts +1 -0
  212. package/dist/deploy/static-site-helper.d.ts +0 -16
  213. package/dist/deploy/telemetry-access.d.ts +5 -0
  214. package/dist/deploy/telemetry-access.test.d.ts +1 -0
  215. package/dist/deploy/telemetry-collection.d.ts +44 -0
  216. package/dist/deploy/telemetry-collection.test.d.ts +1 -0
  217. package/dist/dns/index.js +3 -3
  218. package/dist/dns/route53-adapter.d.ts +1 -1
  219. package/dist/drivers/hetzner/cloud-init.d.ts +1 -1
  220. package/dist/drivers/index.d.ts +2 -2
  221. package/dist/drivers/index.js +6 -6
  222. package/dist/drivers/shared/notifications.d.ts +1 -1
  223. package/dist/drivers/shared/rpx-gateway.d.ts +1 -0
  224. package/dist/fleet/fleet.test.d.ts +1 -0
  225. package/dist/fleet/index.d.ts +144 -0
  226. package/dist/fleet/queue.d.ts +3 -0
  227. package/dist/fleet/ssh-driver.d.ts +34 -0
  228. package/dist/index.d.ts +28 -4
  229. package/dist/index.js +1132 -424
  230. package/dist/jobs/adapters.d.ts +23 -0
  231. package/dist/jobs/index.d.ts +7 -0
  232. package/dist/jobs/jobs.test.d.ts +1 -0
  233. package/dist/jobs/model.d.ts +100 -0
  234. package/dist/jobs/provider-adapters.d.ts +49 -0
  235. package/dist/jobs/reconcile.d.ts +19 -0
  236. package/dist/jobs/schedule.d.ts +9 -0
  237. package/dist/jobs/service.d.ts +30 -0
  238. package/dist/jobs/store.d.ts +48 -0
  239. package/dist/maintenance/drivers.d.ts +117 -0
  240. package/dist/maintenance/index.d.ts +6 -0
  241. package/dist/maintenance/maintenance.test.d.ts +1 -0
  242. package/dist/maintenance/manifest.d.ts +15 -0
  243. package/dist/maintenance/queue.d.ts +9 -0
  244. package/dist/maintenance/service.d.ts +31 -0
  245. package/dist/maintenance/store.d.ts +84 -0
  246. package/dist/maintenance/types.d.ts +192 -0
  247. package/dist/media.d.ts +62 -0
  248. package/dist/media.test.d.ts +1 -0
  249. package/dist/object-storage/index.d.ts +0 -24
  250. package/dist/onboarding/archive.d.ts +12 -0
  251. package/dist/onboarding/artifact-store.d.ts +22 -0
  252. package/dist/onboarding/detection.d.ts +8 -0
  253. package/dist/onboarding/index.d.ts +9 -0
  254. package/dist/onboarding/migrations.d.ts +3 -0
  255. package/dist/onboarding/onboarding.test.d.ts +1 -0
  256. package/dist/onboarding/plan.d.ts +3 -0
  257. package/dist/onboarding/registry.d.ts +41 -0
  258. package/dist/onboarding/service.d.ts +16 -0
  259. package/dist/onboarding/store.d.ts +30 -0
  260. package/dist/onboarding/types.d.ts +204 -0
  261. package/dist/placement/builds.d.ts +20 -0
  262. package/dist/placement/drivers.d.ts +54 -0
  263. package/dist/placement/index.d.ts +6 -0
  264. package/dist/placement/placement.test.d.ts +1 -0
  265. package/dist/placement/queue.d.ts +4 -0
  266. package/dist/placement/service.d.ts +34 -0
  267. package/dist/placement/store.d.ts +49 -0
  268. package/dist/placement/types.d.ts +109 -0
  269. package/dist/preview/index.d.ts +3 -0
  270. package/dist/preview/service.d.ts +45 -0
  271. package/dist/preview/service.test.d.ts +1 -0
  272. package/dist/preview/store.d.ts +47 -0
  273. package/dist/preview/store.test.d.ts +1 -0
  274. package/dist/preview/types.d.ts +99 -0
  275. package/dist/push/apns.d.ts +0 -26
  276. package/dist/push/fcm.d.ts +0 -23
  277. package/dist/push/index.d.ts +2 -2
  278. package/dist/push/index.js +1 -1
  279. package/dist/queue/deployment-handlers.d.ts +27 -0
  280. package/dist/queue/deployment-handlers.test.d.ts +1 -0
  281. package/dist/queue/index.d.ts +4 -0
  282. package/dist/queue/queue.d.ts +57 -0
  283. package/dist/queue/queue.test.d.ts +1 -0
  284. package/dist/queue/types.d.ts +93 -0
  285. package/dist/queue/worker.d.ts +32 -0
  286. package/dist/queue/worker.test.d.ts +1 -0
  287. package/dist/regions/drivers.d.ts +139 -0
  288. package/dist/regions/index.d.ts +5 -0
  289. package/dist/regions/queue.d.ts +4 -0
  290. package/dist/regions/regions.test.d.ts +1 -0
  291. package/dist/regions/service.d.ts +23 -0
  292. package/dist/regions/store.d.ts +41 -0
  293. package/dist/regions/types.d.ts +132 -0
  294. package/dist/release/index.d.ts +6 -0
  295. package/dist/release/queue.d.ts +15 -0
  296. package/dist/release/queue.test.d.ts +1 -0
  297. package/dist/release/runtime.d.ts +35 -0
  298. package/dist/release/runtime.test.d.ts +1 -0
  299. package/dist/release/service.d.ts +28 -0
  300. package/dist/release/store.d.ts +88 -0
  301. package/dist/release/store.test.d.ts +1 -0
  302. package/dist/release/strategy.d.ts +8 -0
  303. package/dist/release/types.d.ts +103 -0
  304. package/dist/runtime/adapters/aws.d.ts +5 -0
  305. package/dist/runtime/adapters/docker.d.ts +53 -0
  306. package/dist/runtime/adapters/systemd.d.ts +26 -0
  307. package/dist/runtime/index.d.ts +9 -0
  308. package/dist/runtime/inventory.d.ts +6 -0
  309. package/dist/runtime/model.d.ts +130 -0
  310. package/dist/runtime/normalize.d.ts +5 -0
  311. package/dist/runtime/operations.d.ts +113 -0
  312. package/dist/runtime/service.d.ts +32 -0
  313. package/dist/runtime/streams.d.ts +49 -0
  314. package/dist/security/artifacts.d.ts +105 -0
  315. package/dist/security/artifacts.test.d.ts +1 -0
  316. package/dist/security/container-release.d.ts +35 -0
  317. package/dist/security/container-release.test.d.ts +1 -0
  318. package/dist/security/index.d.ts +8 -0
  319. package/dist/security/integration.d.ts +41 -0
  320. package/dist/security/integration.test.d.ts +1 -0
  321. package/dist/security/posture-store.d.ts +80 -0
  322. package/dist/security/posture-store.test.d.ts +1 -0
  323. package/dist/security/pre-deploy-scanner.d.ts +0 -4
  324. package/dist/security/scanners.d.ts +81 -0
  325. package/dist/security/scanners.test.d.ts +1 -0
  326. package/dist/security/types.d.ts +150 -0
  327. package/dist/source/git-workspace.d.ts +26 -0
  328. package/dist/source/git-workspace.test.d.ts +1 -0
  329. package/dist/source/index.d.ts +6 -0
  330. package/dist/source/providers.d.ts +199 -0
  331. package/dist/source/providers.test.d.ts +1 -0
  332. package/dist/source/service.d.ts +18 -0
  333. package/dist/source/store.d.ts +149 -0
  334. package/dist/source/store.test.d.ts +1 -0
  335. package/dist/source/types.d.ts +191 -0
  336. package/dist/source/webhooks.d.ts +33 -0
  337. package/dist/source/webhooks.test.d.ts +1 -0
  338. package/dist/ssl/acme-client.d.ts +0 -6
  339. package/dist/storage/compose.d.ts +4 -0
  340. package/dist/storage/compose.test.d.ts +1 -0
  341. package/dist/storage/drivers.d.ts +107 -0
  342. package/dist/storage/drivers.test.d.ts +1 -0
  343. package/dist/storage/index.d.ts +5 -0
  344. package/dist/storage/model.d.ts +112 -0
  345. package/dist/storage/service.d.ts +95 -0
  346. package/dist/storage/storage.test.d.ts +1 -0
  347. package/dist/storage/store.d.ts +40 -0
  348. package/dist/telemetry/correlation.test.d.ts +1 -0
  349. package/dist/telemetry/index.d.ts +4 -0
  350. package/dist/telemetry/model.d.ts +145 -0
  351. package/dist/telemetry/policy.d.ts +8 -0
  352. package/dist/telemetry/policy.test.d.ts +1 -0
  353. package/dist/telemetry/redaction.d.ts +9 -0
  354. package/dist/telemetry/store.d.ts +34 -0
  355. package/dist/telemetry/store.test.d.ts +1 -0
  356. package/dist/ui/404.html +1506 -17
  357. package/dist/ui/accept-invitation.html +1163 -0
  358. package/dist/ui/{server/backups.html → access-denied.html} +344 -55
  359. package/dist/ui/account/automation.html +1669 -0
  360. package/dist/ui/account/security.html +1646 -0
  361. package/dist/ui/applications/compose.html +1590 -0
  362. package/dist/ui/applications/new.html +1868 -0
  363. package/dist/ui/data/backups.html +1757 -0
  364. package/dist/ui/data/services.html +1723 -0
  365. package/dist/ui/data/volumes.html +1639 -0
  366. package/dist/ui/index.html +392 -29
  367. package/dist/ui/integrations.html +1865 -0
  368. package/dist/ui/operations/alerts.html +1610 -0
  369. package/dist/ui/operations/configuration.html +1746 -0
  370. package/dist/ui/operations/jobs.html +1709 -0
  371. package/dist/ui/operations/maintenance.html +1540 -0
  372. package/dist/ui/operations/observability.html +1742 -0
  373. package/dist/ui/{serverless/scheduler.html → operations/previews.html} +447 -46
  374. package/dist/ui/operations/queue.html +1615 -0
  375. package/dist/ui/{server/workers.html → operations/regions.html} +413 -49
  376. package/dist/ui/operations/releases.html +1596 -0
  377. package/dist/ui/operations/workloads.html +1775 -0
  378. package/dist/ui/security.html +1767 -0
  379. package/dist/ui/server/actions.html +446 -57
  380. package/dist/ui/server/activity.html +346 -1
  381. package/dist/ui/server/capacity.html +1542 -0
  382. package/dist/ui/server/database.html +443 -36
  383. package/dist/ui/server/deployments.html +439 -53
  384. package/dist/ui/server/diagnostics.html +346 -1
  385. package/dist/ui/server/firewall.html +476 -50
  386. package/dist/ui/server/fleet.html +1577 -0
  387. package/dist/ui/server/logs.html +389 -25
  388. package/dist/ui/server/metrics.html +346 -1
  389. package/dist/ui/server/security.html +346 -1
  390. package/dist/ui/server/services.html +421 -46
  391. package/dist/ui/server/sites.html +476 -95
  392. package/dist/ui/server/ssh-keys.html +476 -46
  393. package/dist/ui/server/team.html +700 -77
  394. package/dist/ui/server/terminal.html +444 -54
  395. package/dist/ui/serverless/alarms.html +430 -42
  396. package/dist/ui/serverless/assets.html +421 -46
  397. package/dist/ui/serverless/cost.html +346 -1
  398. package/dist/ui/serverless/data.html +422 -47
  399. package/dist/ui/serverless/deployments.html +421 -46
  400. package/dist/ui/serverless/firewall.html +346 -1
  401. package/dist/ui/serverless/functions.html +423 -48
  402. package/dist/ui/serverless/logs.html +389 -25
  403. package/dist/ui/serverless/metrics.html +346 -1
  404. package/dist/ui/serverless/queues.html +448 -62
  405. package/dist/ui/serverless/secrets.html +448 -42
  406. package/dist/ui/serverless/traces.html +350 -5
  407. package/dist/ui/serverless.html +434 -58
  408. package/dist/ui-src/pages/404.stx +18 -0
  409. package/dist/ui-src/pages/accept-invitation.stx +50 -0
  410. package/dist/ui-src/pages/access-denied.stx +18 -0
  411. package/dist/ui-src/pages/account/automation.stx +187 -0
  412. package/dist/ui-src/pages/account/security.stx +249 -0
  413. package/dist/ui-src/pages/applications/compose.stx +38 -0
  414. package/dist/ui-src/pages/applications/new.stx +76 -0
  415. package/dist/ui-src/pages/data/backups.stx +214 -0
  416. package/dist/ui-src/pages/data/services.stx +257 -0
  417. package/dist/ui-src/pages/data/volumes.stx +40 -0
  418. package/dist/ui-src/pages/index.stx +24 -10
  419. package/dist/ui-src/pages/integrations.stx +186 -0
  420. package/dist/ui-src/pages/operations/alerts.stx +40 -0
  421. package/dist/ui-src/pages/operations/configuration.stx +164 -0
  422. package/dist/ui-src/pages/operations/jobs.stx +238 -0
  423. package/dist/ui-src/pages/operations/maintenance.stx +15 -0
  424. package/dist/ui-src/pages/operations/observability.stx +76 -0
  425. package/dist/ui-src/pages/operations/previews.stx +62 -0
  426. package/dist/ui-src/pages/operations/queue.stx +158 -0
  427. package/dist/ui-src/pages/operations/regions.stx +12 -0
  428. package/dist/ui-src/pages/operations/releases.stx +39 -0
  429. package/dist/ui-src/pages/operations/workloads.stx +151 -0
  430. package/dist/ui-src/pages/partials/head.stx +148 -15
  431. package/dist/ui-src/pages/partials/nav.stx +491 -74
  432. package/dist/ui-src/pages/partials/op-confirm.stx +6 -6
  433. package/dist/ui-src/pages/security.stx +264 -0
  434. package/dist/ui-src/pages/server/actions.stx +17 -26
  435. package/dist/ui-src/pages/server/capacity.stx +16 -0
  436. package/dist/ui-src/pages/server/database.stx +31 -16
  437. package/dist/ui-src/pages/server/deployments.stx +28 -30
  438. package/dist/ui-src/pages/server/firewall.stx +25 -30
  439. package/dist/ui-src/pages/server/fleet.stx +10 -0
  440. package/dist/ui-src/pages/server/logs.stx +11 -9
  441. package/dist/ui-src/pages/server/metrics.stx +8 -16
  442. package/dist/ui-src/pages/server/services.stx +19 -28
  443. package/dist/ui-src/pages/server/sites.stx +75 -72
  444. package/dist/ui-src/pages/server/ssh-keys.stx +24 -30
  445. package/dist/ui-src/pages/server/team.stx +286 -149
  446. package/dist/ui-src/pages/server/terminal.stx +6 -28
  447. package/dist/ui-src/pages/serverless/alarms.stx +17 -31
  448. package/dist/ui-src/pages/serverless/assets.stx +13 -22
  449. package/dist/ui-src/pages/serverless/cost.stx +15 -3
  450. package/dist/ui-src/pages/serverless/data.stx +12 -25
  451. package/dist/ui-src/pages/serverless/deployments.stx +14 -23
  452. package/dist/ui-src/pages/serverless/firewall.stx +5 -1
  453. package/dist/ui-src/pages/serverless/functions.stx +20 -26
  454. package/dist/ui-src/pages/serverless/logs.stx +11 -10
  455. package/dist/ui-src/pages/serverless/metrics.stx +18 -17
  456. package/dist/ui-src/pages/serverless/queues.stx +34 -42
  457. package/dist/ui-src/pages/serverless/secrets.stx +31 -34
  458. package/dist/ui-src/pages/serverless/traces.stx +2 -6
  459. package/dist/ui-src/pages/serverless.stx +40 -74
  460. package/package.json +11 -11
  461. package/dist/chunk-b17vf1g1.js +0 -13787
  462. package/dist/chunk-b82pbxyp.js +0 -1572
  463. package/dist/chunk-pqfzdg68.js +0 -12
  464. package/dist/chunk-z58fqnjm.js +0 -10
  465. package/dist/ui-src/pages/server/backups.stx +0 -91
  466. package/dist/ui-src/pages/server/workers.stx +0 -99
  467. package/dist/ui-src/pages/serverless/scheduler.stx +0 -89
@@ -7,18 +7,31 @@ const server = __d?.server ?? { name: 'acme-production-app' }
7
7
  // Members reach this page for their own sites; box-level CLI hints and the
8
8
  // server crumb are not theirs to act on.
9
9
  const isMember = (__d?.viewer?.role ?? 'admin') === 'member'
10
+ const viewerCapabilities = __d?.viewer?.capabilities ?? []
11
+ const canRollback = !isMember && viewerCapabilities.includes('deployments:rollback')
10
12
  const initialDeps = __d?.serverDeploymentsDetail ?? []
11
13
  const deploymentChart = renderDeploymentSparkline(initialDeps)
12
14
  const deploymentsEmptyReason = __d?.deploymentsEmptyReason ?? 'No deployment history has been recorded yet.'
13
15
  </script>
14
16
  <script client>
17
+ import { useOpConfirm } from '../../functions/useOpConfirm.ts'
18
+
15
19
  // Seeded from build-time data (stx server→client bridge), then live-refreshed.
16
20
  const deps = state(initialDeps)
17
- const status = state('all')
18
- const site = state('all')
21
+ const initialFilters = new URLSearchParams(location.search)
22
+ const status = state(initialFilters.get('status') || 'all')
23
+ const site = state(initialFilters.get('site') || 'all')
19
24
  const sites = derived(() => [...new Set(deps().map(d => d.site).filter(Boolean))])
20
25
  const filtered = derived(() => deps().filter(d =>
21
26
  (status() === 'all' || d.status === status()) && (site() === 'all' || d.site === site())))
27
+ function setFilter(name, value, signal) {
28
+ signal.set(value)
29
+ const url = new URL(location.href)
30
+ if (value === 'all') url.searchParams.delete(name)
31
+ else url.searchParams.set(name, value)
32
+ history.replaceState(null, '', url)
33
+ window.dispatchEvent(new Event('ts-cloud:filters-changed'))
34
+ }
22
35
  function tone(s) { return s === 'success' ? 'ok' : (s === 'failed' ? 'bad' : 'warn') }
23
36
  async function refresh() {
24
37
  try { const r = await fetch('/api/dashboard-data'); if (!r.ok) return; const x = await r.json(); if (Array.isArray(x.serverDeploymentsDetail)) deps.set(x.serverDeploymentsDetail) } catch {}
@@ -26,27 +39,12 @@ async function refresh() {
26
39
  onMount(() => { refresh() })
27
40
 
28
41
  // Reactive operation runner (rollback) - typed-confirm bar + output.
29
- const opOutput = state('')
30
- const opShown = state(false)
31
- const pending = state(null)
32
- const typed = state('')
33
- const confirmTok = derived(() => { const p = pending(); return p ? p.confirm : '' })
34
- const confirmVerb = derived(() => { const p = pending(); return p ? p.verb : '' })
35
- const canRun = derived(() => { const p = pending(); return !!p && typed() === p.confirm })
36
- function askOp(operation, confirm, verb, to) { pending.set({ operation, confirm, verb, to: to || null }); typed.set('') }
37
- function cancelOp() { pending.set(null) }
38
- async function runOp() {
39
- const p = pending()
40
- if (!p || typed() !== p.confirm) return
41
- pending.set(null); typed.set('')
42
- opShown.set(true); opOutput.set('Running ' + p.operation + '...')
43
- try {
44
- const payload = p.to ? { operation: p.operation, confirm: p.confirm, to: p.to } : { operation: p.operation, confirm: p.confirm }
45
- const res = await fetch('/api/server/operations/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
46
- const body = await res.json()
47
- opOutput.set((body.ok ? 'OK ' : 'FAILED ') + (body.command || p.operation) + '\n\n' + (body.stdout || '') + (body.stderr ? '\n' + body.stderr : '') + (body.error ? '\n' + body.error : ''))
48
- } catch (e) { opOutput.set('FAILED ' + p.operation + '\n\n' + ((e && e.message) || e)) }
49
- }
42
+ const { opOutput, opShown, pending, typed, busy, confirmTok, confirmVerb, confirmDanger, confirmLabel, canRun, askOp, cancelOp, runOp } = useOpConfirm(async p => {
43
+ const payload = p.to ? { operation: p.operation, confirm: p.apiConfirm, to: p.to } : { operation: p.operation, confirm: p.apiConfirm }
44
+ const res = await fetch('/api/server/operations/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
45
+ const body = await res.json()
46
+ return (body.ok ? 'OK ' : 'FAILED ') + (body.command || p.operation) + '\n\n' + (body.stdout || '') + (body.stderr ? '\n' + body.stderr : '') + (body.error ? '\n' + body.error : '')
47
+ })
50
48
  </script>
51
49
  <!DOCTYPE html>
52
50
  <html lang="en" data-theme="dark">
@@ -74,13 +72,13 @@ async function runOp() {
74
72
  </div>
75
73
  <div class="filters">
76
74
  <div class="segmented" role="group" aria-label="Filter deployment status">
77
- <button type="button" :class="{ active: status() === 'all' }" @click="status.set('all')">All</button>
78
- <button type="button" :class="{ active: status() === 'success' }" @click="status.set('success')">Success</button>
79
- <button type="button" :class="{ active: status() === 'failed' }" @click="status.set('failed')">Failed</button>
75
+ <button type="button" :class="{ active: status() === 'all' }" @click="setFilter('status', 'all', status)">All</button>
76
+ <button type="button" :class="{ active: status() === 'success' }" @click="setFilter('status', 'success', status)">Success</button>
77
+ <button type="button" :class="{ active: status() === 'failed' }" @click="setFilter('status', 'failed', status)">Failed</button>
80
78
  </div>
81
79
  <label class="select-filter">
82
80
  <span>Site</span>
83
- <select :value="site()" @change="site.set($event.target.value)">
81
+ <select :value="site()" @change="setFilter('site', $event.target.value, site)">
84
82
  <option value="all">All sites</option>
85
83
  <template :for="s in sites()">
86
84
  <option :value="s">{{ s }}</option>
@@ -89,7 +87,7 @@ async function runOp() {
89
87
  </label>
90
88
  </div>
91
89
  <table>
92
- <thead><tr><th>Build</th><th>Site</th><th>Branch</th><th>Status</th><th>Steps</th><th>By</th><th>Took</th><th>When</th><th>Action</th></tr></thead>
90
+ <thead><tr><th scope="col">Build</th><th scope="col">Site</th><th scope="col">Branch</th><th scope="col">Status</th><th scope="col">Steps</th><th scope="col">By</th><th scope="col">Took</th><th scope="col">When</th><th scope="col">Action</th></tr></thead>
93
91
  <tbody>
94
92
  <template :for="d in filtered()">
95
93
  <tr>
@@ -101,7 +99,7 @@ async function runOp() {
101
99
  <td style="color:var(--txt2)">{{ d.by }}</td>
102
100
  <td class="mono">{{ d.took }}</td>
103
101
  <td style="color:var(--txt3)">{{ d.when }}</td>
104
- <td class="table-actions"><button class="btn danger sm" type="button" @click="askOp('rollback:' + d.site, d.site, 'roll back', d.release)">Roll back</button></td>
102
+ <td class="table-actions">@if (canRollback)<button class="btn danger sm" type="button" :aria-label="'Roll back ' + d.site + ' deployment ' + d.sha" @click="askOp('rollback:' + d.site, d.site, 'roll back', d.release)">Roll back</button>@else<span class="mono">read only</span>@endif</td>
105
103
  </tr>
106
104
  </template>
107
105
  </tbody>
@@ -110,7 +108,7 @@ async function runOp() {
110
108
  <div class="compact empty" @show="deps().length > 0 && filtered().length === 0"><strong>No matches</strong><span>Try a different status or site filter.</span></div>
111
109
  @include('../partials/op-confirm')
112
110
  @if (isMember)
113
- <p class="note">Roll back one of your sites to any prior release.</p>
111
+ <p class="note">Deployment history is visible in this scope. Rollback requires an organization administrator because it changes server-wide release state.</p>
114
112
  @else
115
113
  <p class="note">Roll back a site to any prior release, or inspect a box with <span class="mono">cloud server:logs {{ server.name }}</span>.</p>
116
114
  @endif
@@ -6,6 +6,9 @@ const initialAlwaysOpen = __d?.firewall?.alwaysOpen ?? [22, 80, 443]
6
6
  const initialPorts = __d?.firewall?.ports ?? [8080, 6379]
7
7
  </script>
8
8
  <script client>
9
+ import { useOpConfirm } from '../../functions/useOpConfirm.ts'
10
+ import { requestJson } from '../../functions/requestJson.ts'
11
+
9
12
  // Reactive host firewall (UFW) management - list + allow + remove, via signals +
10
13
  // directives. Seeded from build-time data (stx server→client bridge); load()
11
14
  // reconciles against the live cloud config, and each mutation applies UFW live.
@@ -31,36 +34,33 @@ async function load() {
31
34
  } catch (e) { setMessage((e && e.message) || 'Could not read firewall config.', 'bad') }
32
35
  }
33
36
  async function allowPort() {
37
+ if (busy()) return
34
38
  const port = Number(newPort())
35
39
  if (!Number.isInteger(port) || port < 1 || port > 65535) { setMessage('Port must be a whole number between 1 and 65535.', 'bad'); return }
36
40
  if (reserved.includes(port)) { setMessage('Ports 22, 80, and 443 are always open, so there is nothing to add.', 'bad'); return }
37
- setMessage('Allowing port ' + port + '...', '')
38
- const res = await fetch('/api/firewall', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ port }) })
39
- const data = await res.json().catch(() => ({}))
40
- if (!res.ok || data.ok === false) { setMessage(data.error || 'Could not allow port.', 'bad'); return }
41
- ports.set(data.ports || []); newPort.set('')
42
- setMessage('Allowed port ' + port + ' in cloud config.' + applyLine(data.apply), 'ok')
41
+ busy.set(true); setMessage('Allowing port ' + port + '...', '')
42
+ try {
43
+ const data = await requestJson('/api/firewall', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ port }) })
44
+ ports.set(data.ports || []); newPort.set('')
45
+ setMessage('Allowed port ' + port + ' in cloud config.' + applyLine(data.apply), 'ok')
46
+ } catch (error) { setMessage(error?.message || 'Could not allow port.', 'bad') }
47
+ finally { busy.set(false) }
43
48
  }
44
49
  async function removePort(port) {
45
50
  setMessage('Removing port ' + port + '...', '')
46
- const res = await fetch('/api/firewall', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ port }) })
47
- const data = await res.json().catch(() => ({}))
48
- if (!res.ok || data.ok === false) { setMessage(data.error || 'Could not remove port.', 'bad'); return }
49
- ports.set(data.ports || [])
50
- setMessage('Removed port ' + port + ' from cloud config.' + applyLine(data.apply), 'ok')
51
+ try {
52
+ const data = await requestJson('/api/firewall', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ port }) })
53
+ ports.set(data.ports || [])
54
+ setMessage('Removed port ' + port + ' from cloud config.' + applyLine(data.apply), 'ok')
55
+ } catch (error) { setMessage(error?.message || 'Could not remove port.', 'bad') }
51
56
  }
52
57
 
53
- // Remove runs through a typed-confirm bar: type the port number to confirm.
54
- const pending = state(null); const typed = state('')
55
- const confirmTok = derived(() => { const p = pending(); return p ? String(p) : '' })
56
- const canRun = derived(() => { const p = pending(); return p !== null && typed() === String(p) })
57
- function askRemove(port) { pending.set(port); typed.set('') }
58
- function cancelRemove() { pending.set(null); typed.set('') }
59
- async function runRemove() {
60
- const p = pending(); if (p === null || typed() !== String(p)) return
61
- pending.set(null); typed.set('')
62
- await removePort(p)
63
- }
58
+ // Remove runs through the shared typed-confirm lifecycle.
59
+ const { opOutput, opShown, pending, typed, busy, confirmTok, confirmVerb, confirmDanger, confirmLabel, canRun, askOp, cancelOp, runOp } = useOpConfirm(async p => {
60
+ await removePort(Number(p.port))
61
+ return message()
62
+ })
63
+ function askRemove(port) { askOp('firewall:remove:' + port, String(port), 'remove port ' + port, null, String(port), { port, danger: true, label: 'Remove port' }) }
64
64
  onMount(() => { load() })
65
65
  </script>
66
66
  <!DOCTYPE html>
@@ -100,7 +100,7 @@ onMount(() => { load() })
100
100
  <input id="fw-port" type="number" min="1" max="65535" autocomplete="off" placeholder="8080" :value="newPort()" @input="newPort.set($event.target.value)" required>
101
101
  </div>
102
102
  <div class="form-actions">
103
- <button class="btn" type="submit">Allow</button>
103
+ <button class="btn" type="submit" :disabled="busy()">Allow</button>
104
104
  <span class="msg" :class="messageKind()" role="status">{{ message() }}</span>
105
105
  </div>
106
106
  </form>
@@ -113,19 +113,14 @@ onMount(() => { load() })
113
113
  <div class="port-grid" @show="ports().length > 0">
114
114
  <template :for="p in ports()">
115
115
  <div class="port-card">
116
- <div class="top"><span class="tag">tcp</span><button type="button" class="btn danger sm" @click="askRemove(p)">Remove</button></div>
116
+ <div class="top"><span class="tag">tcp</span><button type="button" class="btn danger sm" :aria-label="'Remove firewall port ' + p" @click="askRemove(p)">Remove</button></div>
117
117
  <div class="listen mono">{{ p }}</div>
118
118
  <div class="proc">Allowed via cloud config</div>
119
119
  </div>
120
120
  </template>
121
121
  </div>
122
122
  <div class="compact empty" @show="ports().length === 0"><strong>No extra ports configured</strong><span>Only 22, 80, and 443 are open. Allow a port above to update <span class="mono">infrastructure.compute.firewall</span>.</span></div>
123
- <div class="op-confirm panel" @show="pending() !== null">
124
- <span>Type <b class="mono">{{ confirmTok() }}</b> to remove this port:</span>
125
- <input class="op-confirm-input" :value="typed()" @input="typed.set($event.target.value)" @keydown.enter="runRemove()" placeholder="confirm" autocomplete="off">
126
- <button class="btn sm" :disabled="!canRun()" @click="runRemove()">Run</button>
127
- <button class="btn ghost sm" @click="cancelRemove()">Cancel</button>
128
- </div>
123
+ @include('../partials/op-confirm')
129
124
  <p class="note">Ports are declarative: this page edits <span class="mono">infrastructure.compute.firewall</span> in cloud config and applies <span class="mono">ufw allow</span> / <span class="mono">ufw delete allow</span> live on the box. The full config is reconciled again on the next <span class="mono">cloud deploy</span>.</p>
130
125
  </div>
131
126
  </div>
@@ -0,0 +1,10 @@
1
+ <script client>
2
+ import { usePolling } from '../../functions/usePolling.ts'
3
+ const servers=state([]),operations=state([]),selected=state(null),search=state(''),notice=state(''),error=state(''),busy=state(false),adding=state(false)
4
+ async function api(url,options){const response=await fetch(url,options),body=await response.json();if(!response.ok||body.ok===false)throw new Error(body.error||'Request failed.');return body}async function load(){const data=await api('/api/fleet');servers.set(data.servers||[]);operations.set(data.operations||[]);if(selected())selected.set((data.servers||[]).find(v=>v.id===selected().id)||null)}const{pollingState,pollingError,lastUpdatedAt,refresh}=usePolling(load,15000)
5
+ async function mutate(url,value){busy.set(true);error.set('');try{const result=await api(url,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(value)});notice.set(result.operation?`Queued ${result.operation.id}.`:'Fleet state updated.');await refresh();return result}catch(cause){error.set(cause.message)}finally{busy.set(false)}}async function add(event){event.preventDefault();const value=Object.fromEntries(new FormData(event.currentTarget));value.roles=String(value.roles||'application').split(',').map(v=>v.trim()).filter(Boolean);value.labels=Object.fromEntries(String(value.labels||'').split(',').map(v=>v.split('=')).filter(v=>v[0]&&v[1]));const result=await mutate('/api/fleet',value);if(result){event.currentTarget.reset();adding.set(false);selected.set(result.server)}}async function action(name,extra={}){if(selected())await mutate('/api/fleet/action',{id:selected().id,action:name,...extra})}async function archive(){const name=prompt(`Type ${selected().name} to archive it without terminating provider infrastructure.`);if(name===selected().name)await action('archive',{confirmation:name})}function filtered(){const q=search().toLowerCase();return servers().filter(v=>!q||`${v.name} ${v.provider} ${v.region} ${v.roles.join(' ')} ${Object.values(v.labels).join(' ')}`.toLowerCase().includes(q))}function tone(v){return v==='ready'?'ok':['degraded','unreachable'].includes(v)?'bad':'warn'}function bytes(v){return v==null?'—':`${(v/1073741824).toFixed(1)} GB`}
6
+ </script><!DOCTYPE html><html lang="en" data-theme="dark"><head>@include('../partials/head',{title:'ts-cloud · Fleet inventory'})</head><body>@include('../partials/nav',{active:'fleet',env:'production'})
7
+ <main class="fleet-wrap wrap" id="main-content"><div class="crumbs">Infrastructure <span class="sep">/</span> Fleet inventory</div><header class="page"><div><h1>Fleet inventory</h1><p>Provider-neutral server identity, trust, validation, capacity, roles, heartbeat, and maintenance state.</p></div><div class="header-actions"><span class="pill" :class="pollingState()==='live'?'ok':'warn'">{{ pollingState() }}</span><small>{{ lastUpdatedAt()?.toLocaleTimeString()||'connecting' }}</small><button class="btn sm" @click="adding.set(!adding())">{{ adding()?'Close':'Add server' }}</button></div></header><p class="notice" role="status" @show="!!notice()">{{ notice() }}</p><p class="bad notice" role="alert" @show="!!error()||!!pollingError()">{{ error()||pollingError() }}</p>
8
+ <section class="summary"><article class="panel"><b>Servers</b><strong>{{ servers().length }}</strong></article><article class="panel"><b>Ready</b><strong>{{ servers().filter(v=>v.status==='ready').length }}</strong></article><article class="panel"><b>Unreachable</b><strong>{{ servers().filter(v=>v.status==='unreachable').length }}</strong></article><article class="panel"><b>Draining</b><strong>{{ servers().filter(v=>['draining','drained'].includes(v.status)).length }}</strong></article></section>
9
+ <form class="add-server panel" @show="adding()" @submit="add($event)"><div class="wide"><h2>Provision or connect existing</h2><p class="note">Enrollment only records identity and trust inputs. It never mutates an existing host until a reviewed bootstrap plan is confirmed.</p></div><label>Name<input name="name" required pattern="[a-z0-9][a-z0-9-]{1,62}"></label><label>Provider<select name="provider"><option>aws</option><option>hetzner</option><option>ssh</option></select></label><label>Provider ID<input name="providerId" placeholder="i-… / server id / external id"></label><label>Region / zone<input name="region" placeholder="us-west-2"></label><label>Endpoint<input name="endpoint" required placeholder="server.example.com"></label><label>SSH user<input name="sshUser" value="deploy" required></label><label>Credential reference<input name="credentialRef" required value="secret://fleet/agent"></label><label>Roles<input name="roles" value="application" placeholder="application,build"></label><label class="wide">Labels<input name="labels" placeholder="pool=production,arch=arm64"></label><button class="btn wide" type="submit">Enroll without mutation</button></form>
10
+ <section class="filters panel"><input type="search" aria-label="Search fleet" placeholder="Search provider, region, role, or label" @input="search.set($event.currentTarget.value)"></section><div class="fleet-grid" :class="{detail:!!selected()}"><section class="server-cards"><template :for="item in filtered()"><article class="panel server-card"><div><span class="pill" :class="tone(item.status)">{{ item.status }}</span><span class="pill">{{ item.provider }}</span><h2>{{ item.name }}</h2><p>{{ item.endpoint }} · {{ item.region||'external' }}</p></div><div class="facts"><span>{{ item.roles.join(', ') }}</span><span>{{ item.capacity.cpu||0 }} CPU · {{ bytes(item.capacity.memoryBytes) }}</span><span>heartbeat {{ item.heartbeatAt||'never' }}</span><span>trust {{ item.trustState }}</span></div><button class="btn ghost sm" @click="selected.set(item)">Inspect</button></article></template></section><aside class="detail-panel panel" @show="!!selected()"><div class="detail-head"><h2>{{ selected()?.name }}</h2><button class="btn ghost sm" @click="selected.set(null)">Close</button></div><div class="actions"><button class="btn sm" @click="action('test')">Test & pin key</button><button class="btn sm" @click="action('validate')" :disabled="selected()?.trustState!=='pinned'">Validate</button><button class="btn ghost sm" @click="action('bootstrap')">Preview bootstrap</button><button class="btn ghost sm" @click="action('bootstrap',{confirmed:true})" :disabled="!selected()?.validation?.valid">Apply bootstrap</button><button class="btn ghost sm" @click="action('drain')">Drain</button><button class="btn ghost sm" @click="action('uncordon')">Uncordon</button><button class="btn danger sm" @click="archive()">Archive only</button></div><p class="notice warn" @show="selected()?.trustState==='rotation_pending'">Host key changed. Review <code>{{ selected()?.pendingHostKey }}</code> before continuing. <button class="btn sm" @click="action('review_host_key',{fingerprint:selected().pendingHostKey})">Accept rotation</button></p><section><h3>Validation</h3><template :for="finding in selected()?.validation?.findings||[]"><p class="finding" :class="finding.severity==='error'?'bad-text':''"><b>{{ finding.code }}</b> {{ finding.message }}<small>{{ finding.remediation }}</small></p></template><pre>{{ JSON.stringify(selected()?.validation?.facts||{},null,2) }}</pre></section></aside></div></main><style>.fleet-wrap{padding-bottom:5rem}.header-actions,.actions{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}.summary{display:grid;grid-template-columns:repeat(4,1fr);gap:1rem;margin:1rem 0}.summary article{display:grid;gap:.4rem}.summary strong{font-size:1.8rem}.add-server{display:grid;grid-template-columns:repeat(2,1fr);gap:1rem}.add-server label{display:grid;gap:.3rem}.wide{grid-column:1/-1}.filters{margin:1rem 0}.filters input{width:100%}.fleet-grid.detail{display:grid;grid-template-columns:1fr 430px;gap:1rem}.server-cards{display:grid;gap:1rem}.server-card{display:grid;grid-template-columns:1fr 1fr auto;gap:1rem;align-items:center}.facts{display:grid;gap:.35rem}.detail-panel{position:sticky;top:1rem;align-self:start}.detail-head{display:flex;justify-content:space-between}.finding{display:grid;border-bottom:1px solid var(--border);padding:.5rem 0}.bad-text{color:var(--danger)}pre{white-space:pre-wrap;max-height:20rem;overflow:auto}@media(max-width:900px){.fleet-grid.detail{grid-template-columns:1fr}.detail-panel{position:static}.server-card{grid-template-columns:1fr}.summary{grid-template-columns:repeat(2,1fr)}}@media(max-width:560px){.add-server,.summary{grid-template-columns:1fr}.wide{grid-column:auto}}</style></body></html>
@@ -9,6 +9,8 @@ const initialLogs = __d?.serverLogs ?? []
9
9
  const emptyReason = __d?.serverLogsEmptyReason ?? 'No server logs are available yet.'
10
10
  </script>
11
11
  <script client>
12
+ import { usePolling } from '../../functions/usePolling.ts'
13
+
12
14
  // Reactive log viewer: seeded from build-time data (stx server→client bridge),
13
15
  // then filters by level + source and auto-refreshes - all via signals +
14
16
  // directives (no manual DOM).
@@ -19,15 +21,13 @@ const sources = derived(() => [...new Set(logs().map(l => l.source).filter(Boole
19
21
  const filtered = derived(() => logs().filter(l =>
20
22
  (level() === 'all' || l.level === level()) && (source() === 'all' || l.source === source())))
21
23
  function tone(l) { return l === 'error' ? 'bad' : (l === 'warn' ? 'warn' : 'ok') }
22
- async function refresh() {
23
- try {
24
- const res = await fetch('/api/dashboard-data')
25
- if (!res.ok) return
26
- const data = await res.json()
27
- if (Array.isArray(data.serverLogs)) logs.set(data.serverLogs)
28
- } catch {}
29
- }
30
- onMount(() => { refresh(); const id = setInterval(refresh, 8000); return () => clearInterval(id) })
24
+ const { pollingState, pollingError, pollingLabel } = usePolling(async () => {
25
+ const res = await fetch('/api/dashboard-data')
26
+ if (!res.ok) throw new Error('Log refresh failed with HTTP ' + res.status + '.')
27
+ const data = await res.json()
28
+ if (!Array.isArray(data.serverLogs)) throw new Error('Log refresh returned an invalid payload.')
29
+ logs.set(data.serverLogs)
30
+ }, 8000)
31
31
  </script>
32
32
  <!DOCTYPE html>
33
33
  <html lang="en" data-theme="dark">
@@ -53,6 +53,7 @@ onMount(() => { refresh(); const id = setInterval(refresh, 8000); return () => c
53
53
  </div>
54
54
  <div class="cmds header-actions"><span class="accent cmd">cloud server:logs {{ server.name }}</span></div>
55
55
  @endif
56
+ <div class="header-actions"><span class="pill" :class="{ ok: pollingState() === 'live', warn: pollingState() !== 'live' }">{{ pollingLabel() }}</span></div>
56
57
  </header>
57
58
 
58
59
  <div class="section">
@@ -89,6 +90,7 @@ onMount(() => { refresh(); const id = setInterval(refresh, 8000); return () => c
89
90
  </div>
90
91
  <div class="compact empty" @show="logs().length > 0 && filtered().length === 0"><strong>No matching logs</strong><span>Try a different level or source filter.</span></div>
91
92
  <div class="empty" @show="logs().length === 0"><strong>No logs available</strong><span>{{ emptyReason }}</span></div>
93
+ <p class="bad note" role="status" @show="pollingState() === 'stale'">Showing the last successful snapshot. {{ pollingError() }}</p>
92
94
 
93
95
  @if (isMember)
94
96
  <p class="note">Live <span class="mono">journalctl</span> for your sites' services, <b>queue workers</b> and daemons. Auto-refreshes every 8s.</p>
@@ -3,20 +3,13 @@ import { renderResourceBars } from '../../src/charts.ts'
3
3
 
4
4
  // ts-cloud server health view. Real data is injected at build time via
5
5
  // TSCLOUD_DASHBOARD_DATA (see deploy/dashboard-data-server.ts); the sample
6
- // values below render the design when nothing is injected.
6
+ // No fallback samples: missing data must remain visibly unavailable.
7
7
  let __d = null
8
8
  try { __d = JSON.parse(process.env.TSCLOUD_DASHBOARD_DATA || '') } catch { __d = null }
9
9
 
10
- const server = __d?.server ?? {
11
- name: 'acme-production-app', provider: 'Hetzner', region: 'fsn1',
12
- os: 'Ubuntu 24.04', uptime: '14d 6h', ip: '203.0.113.42',
13
- }
14
- const m = __d?.systemMetrics ?? {
15
- load: 0.42, cpus: 4,
16
- memUsedMb: 2480, memTotalMb: 7860,
17
- diskUsedPct: 38, diskUsedGb: 19, diskTotalGb: 50,
18
- }
19
- const live = __d ? !!__d?._serverReachable && !__d?.metricsUnavailable : true
10
+ const server = __d?.server ?? { name: 'Server', provider: 'unavailable', region: 'unavailable', os: 'unavailable', uptime: 'unavailable' }
11
+ const m = __d?.systemMetrics ?? { load: 0, cpus: 0, memUsedMb: 0, memTotalMb: 0, diskUsedPct: 0, diskUsedGb: 0, diskTotalGb: 0 }
12
+ const live = !!__d && !!__d?._serverReachable && !__d?.metricsUnavailable
20
13
 
21
14
  // ── Derived percentages ───────────────────────────────────────────────
22
15
  const memPct = live ? Math.min(100, Math.round((m.memUsedMb / Math.max(1, m.memTotalMb)) * 100)) : 0
@@ -63,10 +56,9 @@ const resourceChart = renderResourceBars([
63
56
  { label: 'Disk', value: diskPct, detail: live ? `${m.diskUsedGb} / ${m.diskTotalGb} GB` : unavailableNote },
64
57
  ], { disabled: !live })
65
58
 
66
- // ── Load trend (sample only - not part of the live metrics payload) ────
67
- const loadTrend = __d?.loadTrend ?? [22, 28, 31, 26, 40, 38, 33, 45, 41, 30, 27, 35, 48, 52, 39, 33, 29, 44, 50, 47, 36, 31, 28, 34]
59
+ const loadTrend = __d?.loadTrend ?? []
68
60
  const maxLoad = Math.max(...loadTrend, 1)
69
- const trendPeak = Math.max(...loadTrend)
61
+ const trendPeak = loadTrend.length ? Math.max(...loadTrend) : 0
70
62
  function barPct(v) { return Math.round((v / maxLoad) * 100) }
71
63
  function barTone(v) { const r = v / maxLoad; return r >= 0.9 ? 'peak' : r >= 0.7 ? 'hot' : '' }
72
64
  </script>
@@ -165,7 +157,7 @@ function barTone(v) { const r = v / maxLoad; return r >= 0.9 ? 'peak' : r >= 0.7
165
157
  <div class="section">
166
158
  <h2>CPU load trend</h2>
167
159
  <div class="chart panel">
168
- @if (live)
160
+ @if (live && loadTrend.length)
169
161
  <div class="spark">
170
162
  @foreach (loadTrend as v)
171
163
  <i class="{{ barTone(v) }}" style="height: {{ barPct(v) }}%"></i>
@@ -174,7 +166,7 @@ function barTone(v) { const r = v / maxLoad; return r >= 0.9 ? 'peak' : r >= 0.7
174
166
  <div class="xaxis"><span>-24 snapshots</span><span>peak {{ trendPeak }}% · now</span></div>
175
167
  <p class="note">% of CPU capacity · refresh with <span class="mono">cloud server:monitoring {{ server.name }}</span></p>
176
168
  @else
177
- <div class="compact empty"><strong>No live trend</strong><span>{{ unavailableNote }}</span></div>
169
+ <div class="compact empty"><strong>No historical trend</strong><span>Open <a href="/operations/observability">Observability</a> for persisted host metrics, source freshness, timezone, and gaps.</span></div>
178
170
  @endif
179
171
  </div>
180
172
  </div>
@@ -12,29 +12,16 @@ const services = __d?.servicesDetail ?? [
12
12
  function tone(s) { return s === 'running' ? 'ok' : (s === 'stopped' || s === 'failed' ? 'bad' : 'warn') }
13
13
  </script>
14
14
  <script client>
15
+ import { useOpConfirm } from '../../functions/useOpConfirm.ts'
16
+
15
17
  // Reactive server-operation runner: a typed-confirmation bar + output, driven by
16
18
  // signals + directives (no manual DOM). Buttons call askOp(...) to stage an op.
17
- const opOutput = state('')
18
- const opShown = state(false)
19
- const pending = state(null)
20
- const typed = state('')
21
- const confirmTok = derived(() => { const p = pending(); return p ? p.confirm : '' })
22
- const confirmVerb = derived(() => { const p = pending(); return p ? p.verb : '' })
23
- const canRun = derived(() => { const p = pending(); return !!p && typed() === p.confirm })
24
- function askOp(operation, confirm, verb, to) { pending.set({ operation, confirm, verb, to: to || null }); typed.set('') }
25
- function cancelOp() { pending.set(null) }
26
- async function runOp() {
27
- const p = pending()
28
- if (!p || typed() !== p.confirm) return
29
- pending.set(null); typed.set('')
30
- opShown.set(true); opOutput.set('Running ' + p.operation + '...')
31
- try {
32
- const payload = p.to ? { operation: p.operation, confirm: p.confirm, to: p.to } : { operation: p.operation, confirm: p.confirm }
33
- const res = await fetch('/api/server/operations/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
34
- const body = await res.json()
35
- opOutput.set((body.ok ? 'OK ' : 'FAILED ') + (body.command || p.operation) + '\n\n' + (body.stdout || '') + (body.stderr ? '\n' + body.stderr : '') + (body.error ? '\n' + body.error : ''))
36
- } catch (e) { opOutput.set('FAILED ' + p.operation + '\n\n' + ((e && e.message) || e)) }
37
- }
19
+ const { opOutput, opShown, pending, typed, busy, confirmTok, confirmVerb, confirmDanger, confirmLabel, canRun, askOp, cancelOp, runOp } = useOpConfirm(async p => {
20
+ const payload = p.to ? { operation: p.operation, confirm: p.apiConfirm, to: p.to } : { operation: p.operation, confirm: p.apiConfirm }
21
+ const res = await fetch('/api/server/operations/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
22
+ const body = await res.json()
23
+ return (body.ok ? 'OK ' : 'FAILED ') + (body.command || p.operation) + '\n\n' + (body.stdout || '') + (body.stderr ? '\n' + body.stderr : '') + (body.error ? '\n' + body.error : '')
24
+ })
38
25
  </script>
39
26
  <!DOCTYPE html>
40
27
  <html lang="en" data-theme="dark">
@@ -49,8 +36,9 @@ async function runOp() {
49
36
  </header>
50
37
  <div class="section">
51
38
  <div class="panel">
39
+ @if (services.length)
52
40
  <table>
53
- <thead><tr><th>Service</th><th>Status</th><th>Uptime</th><th>Memory</th><th>Autostart</th><th>Manage</th></tr></thead>
41
+ <thead><tr><th scope="col">Service</th><th scope="col">Status</th><th scope="col">Uptime</th><th scope="col">Memory</th><th scope="col">Autostart</th><th scope="col">Manage</th></tr></thead>
54
42
  <tbody>
55
43
  @foreach (services as s)
56
44
  <tr>
@@ -61,18 +49,21 @@ async function runOp() {
61
49
  <td>@if (s.auto)<span class="ok pill">enabled</span>@else<span class="pill warn">disabled</span>@endif</td>
62
50
  <td>
63
51
  <div class="action-runner">
64
- <button class="btn ghost sm" type="button" @click="askOp('restart:{{ s.name }}', '{{ s.name }}', 'restart')">Restart</button>
65
- <button class="btn ghost sm" type="button" @click="askOp('reload:{{ s.name }}', '{{ s.name }}', 'reload')">Reload</button>
66
- <button class="btn ghost sm" type="button" @click="askOp('start:{{ s.name }}', '{{ s.name }}', 'start')">Start</button>
67
- <button class="btn danger sm" type="button" @click="askOp('stop:{{ s.name }}', '{{ s.name }}', 'stop')">Stop</button>
68
- <button class="btn ghost sm" type="button" @click="askOp('enable:{{ s.name }}', '{{ s.name }}', 'enable')">Enable</button>
69
- <button class="btn ghost sm" type="button" @click="askOp('disable:{{ s.name }}', '{{ s.name }}', 'disable')">Disable</button>
52
+ <button class="btn ghost sm" type="button" aria-label="Restart {{ s.name }}" @click="askOp('restart:{{ s.name }}', 'restart {{ s.name }}', 'restart {{ s.name }}', null, '{{ s.name }}')">Restart</button>
53
+ <button class="btn ghost sm" type="button" aria-label="Reload {{ s.name }}" @click="askOp('reload:{{ s.name }}', 'reload {{ s.name }}', 'reload {{ s.name }}', null, '{{ s.name }}')">Reload</button>
54
+ <button class="btn ghost sm" type="button" aria-label="Start {{ s.name }}" @click="askOp('start:{{ s.name }}', 'start {{ s.name }}', 'start {{ s.name }}', null, '{{ s.name }}')">Start</button>
55
+ <button class="btn danger sm" type="button" aria-label="Stop {{ s.name }}" @click="askOp('stop:{{ s.name }}', 'stop {{ s.name }}', 'stop {{ s.name }}', null, '{{ s.name }}')">Stop</button>
56
+ <button class="btn ghost sm" type="button" aria-label="Enable {{ s.name }}" @click="askOp('enable:{{ s.name }}', 'enable {{ s.name }}', 'enable {{ s.name }}', null, '{{ s.name }}')">Enable</button>
57
+ <button class="btn ghost sm" type="button" aria-label="Disable {{ s.name }}" @click="askOp('disable:{{ s.name }}', 'disable {{ s.name }}', 'disable {{ s.name }}', null, '{{ s.name }}')">Disable</button>
70
58
  </div>
71
59
  </td>
72
60
  </tr>
73
61
  @endforeach
74
62
  </tbody>
75
63
  </table>
64
+ @else
65
+ <div class="empty"><strong>No managed services</strong><span>Services appear here after they are configured and discovered on the server.</span></div>
66
+ @endif
76
67
  @include('../partials/op-confirm')
77
68
  <p class="note">Install software with <span class="mono">cloud server:recipe {{ server.name }} &lt;recipe&gt;</span>.</p>
78
69
  </div>