@caelo-cms/provisioning 0.10.60 → 0.10.62

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 (286) hide show
  1. package/caddy/Caddyfile.production +7 -0
  2. package/caddy/Caddyfile.staging +7 -0
  3. package/dist/admin-internal-answer.d.ts +12 -0
  4. package/dist/admin-internal-answer.d.ts.map +1 -0
  5. package/dist/admin-internal-answer.js +32 -0
  6. package/dist/admin-internal-answer.js.map +1 -0
  7. package/dist/admin-internal-google.d.ts +4 -0
  8. package/dist/admin-internal-google.d.ts.map +1 -0
  9. package/dist/admin-internal-google.js +161 -0
  10. package/dist/admin-internal-google.js.map +1 -0
  11. package/dist/admin-internal-self-hosted.d.ts +30 -0
  12. package/dist/admin-internal-self-hosted.d.ts.map +1 -0
  13. package/dist/admin-internal-self-hosted.js +92 -0
  14. package/dist/admin-internal-self-hosted.js.map +1 -0
  15. package/dist/admin-internal.d.ts +33 -12
  16. package/dist/admin-internal.d.ts.map +1 -1
  17. package/dist/admin-internal.js +27 -159
  18. package/dist/admin-internal.js.map +1 -1
  19. package/dist/agent/command.d.ts +7 -0
  20. package/dist/agent/command.d.ts.map +1 -1
  21. package/dist/agent/command.js +1 -0
  22. package/dist/agent/command.js.map +1 -1
  23. package/dist/agent/lock.d.ts +13 -1
  24. package/dist/agent/lock.d.ts.map +1 -1
  25. package/dist/agent/lock.js +62 -5
  26. package/dist/agent/lock.js.map +1 -1
  27. package/dist/agent/run.d.ts +5 -1
  28. package/dist/agent/run.d.ts.map +1 -1
  29. package/dist/agent/run.js +8 -1
  30. package/dist/agent/run.js.map +1 -1
  31. package/dist/agent-db.js +192 -90
  32. package/dist/caddy.d.ts.map +1 -1
  33. package/dist/caddy.js +11 -1
  34. package/dist/caddy.js.map +1 -1
  35. package/dist/cli-routing.js +1 -1
  36. package/dist/cli-routing.js.map +1 -1
  37. package/dist/cli.js +95 -38
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compose.d.ts +7 -0
  40. package/dist/compose.d.ts.map +1 -1
  41. package/dist/compose.js +5 -0
  42. package/dist/compose.js.map +1 -1
  43. package/dist/converge-job-gcp.d.ts.map +1 -1
  44. package/dist/converge-job-gcp.js +10 -7
  45. package/dist/converge-job-gcp.js.map +1 -1
  46. package/dist/converge-job-preflight.d.ts +21 -0
  47. package/dist/converge-job-preflight.d.ts.map +1 -0
  48. package/dist/converge-job-preflight.js +111 -0
  49. package/dist/converge-job-preflight.js.map +1 -0
  50. package/dist/converge-job.d.ts +8 -2
  51. package/dist/converge-job.d.ts.map +1 -1
  52. package/dist/converge-job.js +14 -0
  53. package/dist/converge-job.js.map +1 -1
  54. package/dist/gateway-credentials.d.ts +5 -3
  55. package/dist/gateway-credentials.d.ts.map +1 -1
  56. package/dist/gateway-credentials.js +5 -8
  57. package/dist/gateway-credentials.js.map +1 -1
  58. package/dist/gcloud.d.ts +29 -0
  59. package/dist/gcloud.d.ts.map +1 -1
  60. package/dist/gcloud.js +66 -0
  61. package/dist/gcloud.js.map +1 -1
  62. package/dist/infra-contract.d.ts +1 -1
  63. package/dist/infra-contract.d.ts.map +1 -1
  64. package/dist/infra-contract.js +10 -1
  65. package/dist/infra-contract.js.map +1 -1
  66. package/dist/install-job.d.ts +3 -1
  67. package/dist/install-job.d.ts.map +1 -1
  68. package/dist/install-job.js.map +1 -1
  69. package/dist/install-state.d.ts +10 -0
  70. package/dist/install-state.d.ts.map +1 -1
  71. package/dist/install-state.js +12 -0
  72. package/dist/install-state.js.map +1 -1
  73. package/dist/lifecycle.d.ts +12 -0
  74. package/dist/lifecycle.d.ts.map +1 -1
  75. package/dist/lifecycle.js +113 -28
  76. package/dist/lifecycle.js.map +1 -1
  77. package/dist/mcp-iap.d.ts +2 -0
  78. package/dist/mcp-iap.d.ts.map +1 -1
  79. package/dist/mcp-iap.js +50 -17
  80. package/dist/mcp-iap.js.map +1 -1
  81. package/dist/operator-access.d.ts +1 -2
  82. package/dist/operator-access.d.ts.map +1 -1
  83. package/dist/operator-access.js +1 -2
  84. package/dist/operator-access.js.map +1 -1
  85. package/dist/owner-reset.d.ts +16 -19
  86. package/dist/owner-reset.d.ts.map +1 -1
  87. package/dist/owner-reset.js +92 -19
  88. package/dist/owner-reset.js.map +1 -1
  89. package/dist/preview-plan.d.ts +71 -0
  90. package/dist/preview-plan.d.ts.map +1 -0
  91. package/dist/preview-plan.js +168 -0
  92. package/dist/preview-plan.js.map +1 -0
  93. package/dist/public-role-isolation.d.ts +2 -0
  94. package/dist/public-role-isolation.d.ts.map +1 -1
  95. package/dist/public-role-isolation.js +9 -3
  96. package/dist/public-role-isolation.js.map +1 -1
  97. package/dist/release-contract.d.ts.map +1 -1
  98. package/dist/release-contract.js +4 -0
  99. package/dist/release-contract.js.map +1 -1
  100. package/dist/self-hosted-internal-secret.d.ts +27 -0
  101. package/dist/self-hosted-internal-secret.d.ts.map +1 -0
  102. package/dist/self-hosted-internal-secret.js +69 -0
  103. package/dist/self-hosted-internal-secret.js.map +1 -0
  104. package/dist/self-hosted-release.d.ts +3 -1
  105. package/dist/self-hosted-release.d.ts.map +1 -1
  106. package/dist/self-hosted-release.js +6 -2
  107. package/dist/self-hosted-release.js.map +1 -1
  108. package/dist/self-hosted-upgrade.d.ts +13 -1
  109. package/dist/self-hosted-upgrade.d.ts.map +1 -1
  110. package/dist/self-hosted-upgrade.js +26 -2
  111. package/dist/self-hosted-upgrade.js.map +1 -1
  112. package/dist/stack-converge.d.ts +5 -2
  113. package/dist/stack-converge.d.ts.map +1 -1
  114. package/dist/stack-converge.js +73 -8
  115. package/dist/stack-converge.js.map +1 -1
  116. package/dist/staging-admin.d.ts +4 -1
  117. package/dist/staging-admin.d.ts.map +1 -1
  118. package/dist/staging-admin.js.map +1 -1
  119. package/dist/state-backend.d.ts +3 -2
  120. package/dist/state-backend.d.ts.map +1 -1
  121. package/dist/state-backend.js +3 -2
  122. package/dist/state-backend.js.map +1 -1
  123. package/dist/state-command.d.ts +17 -0
  124. package/dist/state-command.d.ts.map +1 -1
  125. package/dist/state-command.js +34 -5
  126. package/dist/state-command.js.map +1 -1
  127. package/dist/state-migrate.d.ts +14 -3
  128. package/dist/state-migrate.d.ts.map +1 -1
  129. package/dist/state-migrate.js +31 -7
  130. package/dist/state-migrate.js.map +1 -1
  131. package/dist/state-pulumi.d.ts +0 -2
  132. package/dist/state-pulumi.d.ts.map +1 -1
  133. package/dist/state-pulumi.js +19 -20
  134. package/dist/state-pulumi.js.map +1 -1
  135. package/dist/static-security-headers.d.ts +86 -0
  136. package/dist/static-security-headers.d.ts.map +1 -0
  137. package/dist/static-security-headers.js +126 -0
  138. package/dist/static-security-headers.js.map +1 -0
  139. package/dist/static-svg-gcp.d.ts +28 -0
  140. package/dist/static-svg-gcp.d.ts.map +1 -0
  141. package/dist/static-svg-gcp.js +106 -0
  142. package/dist/static-svg-gcp.js.map +1 -0
  143. package/dist/upgrade/app-roll.d.ts +3 -0
  144. package/dist/upgrade/app-roll.d.ts.map +1 -1
  145. package/dist/upgrade/app-roll.js +12 -5
  146. package/dist/upgrade/app-roll.js.map +1 -1
  147. package/dist/upgrade/backup.d.ts +2 -0
  148. package/dist/upgrade/backup.d.ts.map +1 -1
  149. package/dist/upgrade/backup.js +5 -4
  150. package/dist/upgrade/backup.js.map +1 -1
  151. package/dist/upgrade/converge.d.ts +7 -0
  152. package/dist/upgrade/converge.d.ts.map +1 -1
  153. package/dist/upgrade/converge.js +19 -7
  154. package/dist/upgrade/converge.js.map +1 -1
  155. package/dist/upgrade/gcp-lookup.d.ts +38 -3
  156. package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
  157. package/dist/upgrade/gcp-lookup.js +31 -9
  158. package/dist/upgrade/gcp-lookup.js.map +1 -1
  159. package/dist/upgrade/health.d.ts +31 -5
  160. package/dist/upgrade/health.d.ts.map +1 -1
  161. package/dist/upgrade/health.js +114 -25
  162. package/dist/upgrade/health.js.map +1 -1
  163. package/dist/upgrade/release-images.d.ts +17 -0
  164. package/dist/upgrade/release-images.d.ts.map +1 -1
  165. package/dist/upgrade/release-images.js +23 -12
  166. package/dist/upgrade/release-images.js.map +1 -1
  167. package/dist/upgrade/reporter.d.ts +2 -0
  168. package/dist/upgrade/reporter.d.ts.map +1 -1
  169. package/dist/upgrade/reporter.js +1 -0
  170. package/dist/upgrade/reporter.js.map +1 -1
  171. package/dist/upgrade/upgrade.d.ts +11 -1
  172. package/dist/upgrade/upgrade.d.ts.map +1 -1
  173. package/dist/upgrade/upgrade.js +5 -2
  174. package/dist/upgrade/upgrade.js.map +1 -1
  175. package/dist/upgrader-gcp.d.ts +12 -2
  176. package/dist/upgrader-gcp.d.ts.map +1 -1
  177. package/dist/upgrader-gcp.js +22 -13
  178. package/dist/upgrader-gcp.js.map +1 -1
  179. package/dist/upgrader-iam-preflight.d.ts +16 -3
  180. package/dist/upgrader-iam-preflight.d.ts.map +1 -1
  181. package/dist/upgrader-iam-preflight.js +9 -4
  182. package/dist/upgrader-iam-preflight.js.map +1 -1
  183. package/dist/upgrader-job.d.ts.map +1 -1
  184. package/dist/upgrader-job.js +10 -8
  185. package/dist/upgrader-job.js.map +1 -1
  186. package/dist/upgrader-reporter.d.ts.map +1 -1
  187. package/dist/upgrader-reporter.js +1 -0
  188. package/dist/upgrader-reporter.js.map +1 -1
  189. package/dist/wizards/gcp-pulumi.d.ts +70 -23
  190. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  191. package/dist/wizards/gcp-pulumi.js +118 -39
  192. package/dist/wizards/gcp-pulumi.js.map +1 -1
  193. package/dist/wizards/gcp.d.ts.map +1 -1
  194. package/dist/wizards/gcp.js +67 -11
  195. package/dist/wizards/gcp.js.map +1 -1
  196. package/package.json +2 -2
  197. package/src/admin-internal-answer.ts +43 -0
  198. package/src/admin-internal-google.ts +187 -0
  199. package/src/admin-internal-self-hosted.test.ts +70 -0
  200. package/src/admin-internal-self-hosted.ts +128 -0
  201. package/src/admin-internal.ts +59 -174
  202. package/src/admin-memory.test.ts +1 -0
  203. package/src/agent/command.ts +3 -0
  204. package/src/agent/lock-config.test.ts +57 -2
  205. package/src/agent/lock.ts +68 -5
  206. package/src/agent/run.test.ts +18 -1
  207. package/src/agent/run.ts +17 -6
  208. package/src/caddy-redirects.test.ts +1 -0
  209. package/src/caddy.ts +12 -0
  210. package/src/cli-routing.ts +1 -1
  211. package/src/cli.ts +102 -44
  212. package/src/compose.ts +12 -0
  213. package/src/converge-job-gcp.ts +16 -11
  214. package/src/converge-job-preflight.test.ts +108 -0
  215. package/src/converge-job-preflight.ts +124 -0
  216. package/src/converge-job.test.ts +50 -0
  217. package/src/converge-job.ts +38 -2
  218. package/src/gateway-credentials.test.ts +4 -3
  219. package/src/gateway-credentials.ts +6 -10
  220. package/src/gateway-env-isolation.test.ts +1 -0
  221. package/src/gcloud.test.ts +93 -1
  222. package/src/gcloud.ts +82 -0
  223. package/src/index.test.ts +38 -1
  224. package/src/infra-contract.test.ts +4 -3
  225. package/src/infra-contract.ts +10 -1
  226. package/src/install-job.ts +5 -2
  227. package/src/install-state.ts +13 -0
  228. package/src/lifecycle.ts +134 -28
  229. package/src/mcp-iap.test.ts +66 -5
  230. package/src/mcp-iap.ts +55 -17
  231. package/src/operator-access.ts +1 -2
  232. package/src/owner-reset-providers.test.ts +370 -0
  233. package/src/owner-reset.test.ts +2 -2
  234. package/src/owner-reset.ts +108 -19
  235. package/src/preview-plan.test.ts +233 -0
  236. package/src/preview-plan.ts +226 -0
  237. package/src/public-role-isolation.test.ts +13 -2
  238. package/src/public-role-isolation.ts +14 -4
  239. package/src/release-contract.ts +4 -0
  240. package/src/self-hosted-compose.test.ts +25 -0
  241. package/src/self-hosted-internal-secret.test.ts +61 -0
  242. package/src/self-hosted-internal-secret.ts +80 -0
  243. package/src/self-hosted-release.ts +10 -3
  244. package/src/self-hosted-upgrade.test.ts +35 -1
  245. package/src/self-hosted-upgrade.ts +40 -3
  246. package/src/stack-contract.test.ts +1 -0
  247. package/src/stack-converge.test.ts +89 -5
  248. package/src/stack-converge.ts +81 -8
  249. package/src/staging-admin.test.ts +1 -1
  250. package/src/staging-admin.ts +2 -1
  251. package/src/state-backend.ts +3 -2
  252. package/src/state-command.test.ts +31 -0
  253. package/src/state-command.ts +35 -8
  254. package/src/state-migrate.test.ts +58 -7
  255. package/src/state-migrate.ts +48 -10
  256. package/src/state-pulumi.ts +20 -21
  257. package/src/static-security-headers.test.ts +124 -0
  258. package/src/static-security-headers.ts +154 -0
  259. package/src/static-svg-gcp.test.ts +133 -0
  260. package/src/static-svg-gcp.ts +140 -0
  261. package/src/upgrade/app-roll.ts +18 -7
  262. package/src/upgrade/backup.ts +14 -5
  263. package/src/upgrade/converge.ts +25 -7
  264. package/src/upgrade/gcp-lookup.test.ts +80 -1
  265. package/src/upgrade/gcp-lookup.ts +61 -11
  266. package/src/upgrade/health.test.ts +132 -8
  267. package/src/upgrade/health.ts +169 -31
  268. package/src/upgrade/release-images.test.ts +17 -0
  269. package/src/upgrade/release-images.ts +26 -14
  270. package/src/upgrade/reporter.ts +3 -0
  271. package/src/upgrade/upgrade.test.ts +137 -8
  272. package/src/upgrade/upgrade.ts +14 -2
  273. package/src/upgrader-gcp.ts +28 -13
  274. package/src/upgrader-iam-preflight.test.ts +6 -3
  275. package/src/upgrader-iam-preflight.ts +26 -5
  276. package/src/upgrader-job.ts +14 -6
  277. package/src/upgrader-reporter.ts +1 -0
  278. package/src/wizards/gcp-pulumi.test.ts +1 -1
  279. package/src/wizards/gcp-pulumi.ts +147 -38
  280. package/src/wizards/gcp.ts +89 -9
  281. package/src/wizards/stack-work-dir.test.ts +175 -0
  282. package/stacks/aws/index.ts +40 -0
  283. package/stacks/azure/index.ts +22 -1
  284. package/stacks/gcp/README.md +4 -0
  285. package/stacks/gcp/index.ts +6 -2
  286. package/stacks/gcp/Pulumi.production.yaml +0 -16
@@ -30,6 +30,13 @@
30
30
  # page's <link rel="icon" type> declares, not the host mime.types name.
31
31
  @ico path *.ico
32
32
  header @ico Content-Type "image/x-icon"
33
+ # #891 — nothing is content-sniffed, and an uploaded SVG opened directly
34
+ # runs no script on this origin. Keep the CSP in sync with
35
+ # SVG_DOCUMENT_CSP in @caelo-cms/shared (enforced by
36
+ # packages/provisioning/src/index.test.ts).
37
+ header X-Content-Type-Options "nosniff"
38
+ @svg path *.svg
39
+ header @svg Content-Security-Policy "default-src 'none'; img-src data:; style-src 'unsafe-inline'; font-src data:; sandbox"
33
40
  encode gzip
34
41
  try_files {path} {path}/index.html
35
42
  file_server
@@ -34,6 +34,13 @@
34
34
  # page's <link rel="icon" type> declares, not the host mime.types name.
35
35
  @ico path *.ico
36
36
  header @ico Content-Type "image/x-icon"
37
+ # #891 — nothing is content-sniffed, and an uploaded SVG opened directly
38
+ # runs no script on this origin. Keep the CSP in sync with
39
+ # SVG_DOCUMENT_CSP in @caelo-cms/shared (enforced by
40
+ # packages/provisioning/src/index.test.ts).
41
+ header X-Content-Type-Options "nosniff"
42
+ @svg path *.svg
43
+ header @svg Content-Security-Policy "default-src 'none'; img-src data:; style-src 'unsafe-inline'; font-src data:; sandbox"
37
44
  encode gzip
38
45
  header X-Robots-Tag "noindex, nofollow"
39
46
  try_files {path} {path}/index.html
@@ -0,0 +1,12 @@
1
+ /**
2
+ * How every admin-internal.ts transport reads the admin's HTTP answer, so a
3
+ * refusal reads the same whichever provider carried the request.
4
+ */
5
+ import type { AdminInternalCall, AdminInternalResult } from "./admin-internal.js";
6
+ /**
7
+ * The admin's answer as a result: a 404 is an admin older than the endpoint
8
+ * (fixed by `upgradeCommand`), a non-JSON body or `ok !== true` is a refusal
9
+ * carrying the admin's own message.
10
+ */
11
+ export declare function readAdminAnswer<T>(status: number, text: string, call: AdminInternalCall, upgradeCommand: string): AdminInternalResult<T>;
12
+ //# sourceMappingURL=admin-internal-answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-answer.d.ts","sourceRoot":"","sources":["../src/admin-internal-answer.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,iBAAiB,EACvB,cAAc,EAAE,MAAM,GACrB,mBAAmB,CAAC,CAAC,CAAC,CAuBxB"}
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * The admin's answer as a result: a 404 is an admin older than the endpoint
4
+ * (fixed by `upgradeCommand`), a non-JSON body or `ok !== true` is a refusal
5
+ * carrying the admin's own message.
6
+ */
7
+ export function readAdminAnswer(status, text, call, upgradeCommand) {
8
+ if (status === 404) {
9
+ return {
10
+ ok: false,
11
+ error: `the admin has no ${call.endpoint} yet (it runs a release from before ${call.introducedBy}). Run \`${upgradeCommand}\`, then run this command again.`,
12
+ };
13
+ }
14
+ let parsed;
15
+ try {
16
+ parsed = JSON.parse(text);
17
+ }
18
+ catch {
19
+ return {
20
+ ok: false,
21
+ error: `the admin answered HTTP ${status} with a non-JSON body: ${text.slice(0, 200)}`,
22
+ };
23
+ }
24
+ if (status < 200 || status > 299 || parsed.ok !== true) {
25
+ return {
26
+ ok: false,
27
+ error: `the admin refused (HTTP ${status}): ${parsed.error ?? text.slice(0, 300)}`,
28
+ };
29
+ }
30
+ return { ok: true, value: parsed };
31
+ }
32
+ //# sourceMappingURL=admin-internal-answer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-answer.js","sourceRoot":"","sources":["../src/admin-internal-answer.ts"],"names":[],"mappings":"AAAA,mCAAmC;AASnC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,IAAY,EACZ,IAAuB,EACvB,cAAsB;IAEtB,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,oBAAoB,IAAI,CAAC,QAAQ,uCAAuC,IAAI,CAAC,YAAY,YAAY,cAAc,kCAAkC;SAC7J,CAAC;IACJ,CAAC;IACD,IAAI,MAAkE,CAAC;IACvE,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,2BAA2B,MAAM,0BAA0B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;SACvF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,2BAA2B,MAAM,MAAM,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;SACnF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAsB,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AdminInternalAnswer, AdminInternalCall, AdminInternalDeps, GoogleAdminTarget } from "./admin-internal.js";
2
+ /** The admin call on a gcp / gcp-firebase install (see the file comment). */
3
+ export declare function callGoogleAdmin<T>(target: GoogleAdminTarget, call: AdminInternalCall, deps: AdminInternalDeps): Promise<AdminInternalAnswer<T>>;
4
+ //# sourceMappingURL=admin-internal-google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-google.d.ts","sourceRoot":"","sources":["../src/admin-internal-google.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EAEjB,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAW7B,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,CAAC,EACrC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAoEjC"}
@@ -0,0 +1,161 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * admin-internal.ts on Google Cloud, as the operator's own gcloud identity —
4
+ * the same path the Power-MCP shim takes (packages/mcp-server/src/ingress-auth.ts):
5
+ *
6
+ * 1. the admin's URL: on gcp-firebase the admin Cloud Run service's own
7
+ * URL (IAP sits on the service); on gcp `https://admin.<domain>` (IAP
8
+ * sits on the load balancer's admin backend, and the service's
9
+ * run.app URL accepts only load-balancer traffic);
10
+ * 2. the install's internal secret from Secret Manager (in memory only),
11
+ * which signs a 5-minute token for the endpoint's scope;
12
+ * 3. a JWT signed as `caelo-mcp@<project>` through the IAM Credentials
13
+ * API with `aud` = `<admin origin>/*` — the audience IAP's service-
14
+ * account JWT path checks on both resource kinds
15
+ * (cloud.google.com/iap/docs/authentication-howto). The admin's IAP
16
+ * lets that service account in, and every IAP-allowlisted operator may
17
+ * sign as it (`cms-provision upgrade` grants both), so the request
18
+ * passes IAP first, then Caelo's token.
19
+ */
20
+ import { findService } from "./admin-domain.js";
21
+ import { readAdminAnswer } from "./admin-internal-answer.js";
22
+ import { gcloud as defaultGcloud } from "./gcloud.js";
23
+ import { GCP_STACK_ENV, gcpSecretId, mcpIapServiceAccountEmail } from "./gcp-names.js";
24
+ import { mintInternalToken } from "./internal-token.js";
25
+ /** Signed JWTs live at most 1 hour; one call needs a few seconds. */
26
+ const IAP_JWT_LIFETIME_S = 600;
27
+ /** The admin call on a gcp / gcp-firebase install (see the file comment). */
28
+ export async function callGoogleAdmin(target, call, deps) {
29
+ const run = deps.run ?? defaultGcloud;
30
+ const doFetch = deps.fetch ?? fetch;
31
+ const admin = await adminUrl(run, target);
32
+ if (!admin.ok)
33
+ return admin;
34
+ const secretId = gcpSecretId(GCP_STACK_ENV, "internal-secret");
35
+ const secret = await run([
36
+ "secrets",
37
+ "versions",
38
+ "access",
39
+ "latest",
40
+ `--secret=${secretId}`,
41
+ `--project=${target.projectId}`,
42
+ ]);
43
+ const internalSecret = secret.stdout.trim();
44
+ if (!secret.ok || internalSecret.length < 32) {
45
+ return {
46
+ ok: false,
47
+ error: `read the install's internal secret (${secretId}) from Secret Manager: ${secret.stderr.trim() || "missing or too short"}. Your gcloud account needs secretmanager.versions.access on the project.`,
48
+ };
49
+ }
50
+ // Installs from before #678 may not have the IAM Credentials API on;
51
+ // enabling an enabled API is a no-op (codex review on #681).
52
+ const api = await run([
53
+ "services",
54
+ "enable",
55
+ "iamcredentials.googleapis.com",
56
+ `--project=${target.projectId}`,
57
+ ]);
58
+ if (!api.ok) {
59
+ return {
60
+ ok: false,
61
+ error: `enable the IAM Credentials API (iamcredentials.googleapis.com), needed to pass the admin's IAP: ${api.stderr.trim()}`,
62
+ };
63
+ }
64
+ const iap = await signIapJwt(run, doFetch, target.projectId, admin.value, deps.now);
65
+ if (!iap.ok)
66
+ return iap;
67
+ const token = await mintInternalToken(internalSecret, call.scope);
68
+ let res;
69
+ try {
70
+ res = await doFetch(`${admin.value}${call.endpoint}`, {
71
+ method: "POST",
72
+ headers: {
73
+ authorization: `Bearer ${iap.value}`,
74
+ "x-caelo-internal-token": token,
75
+ "content-type": "application/json",
76
+ },
77
+ body: JSON.stringify(call.body),
78
+ });
79
+ }
80
+ catch (e) {
81
+ return {
82
+ ok: false,
83
+ error: `reach the admin at ${admin.value}: ${String(e)}${unreachableHint(target)}`,
84
+ };
85
+ }
86
+ const text = await res.text();
87
+ if (res.headers.get("x-goog-iap-generated-response") === "true") {
88
+ return {
89
+ ok: false,
90
+ error: `Google IAP refused the request (HTTP ${res.status}: ${text.trim().slice(0, 200)}) — ${mcpIapServiceAccountEmail(target.projectId)} is not allowed on the admin's IAP yet. Run \`cms-provision upgrade\` (it allowlists that service account), then run this command again.`,
91
+ };
92
+ }
93
+ const answer = readAdminAnswer(res.status, text, call, "cms-provision upgrade");
94
+ return answer.ok ? { ok: true, value: { answer: answer.value, adminUrl: admin.value } } : answer;
95
+ }
96
+ /** Where the admin answers behind IAP (see the file comment, step 1). */
97
+ async function adminUrl(run, target) {
98
+ if (target.provider === "gcp")
99
+ return { ok: true, value: `https://admin.${target.domain}` };
100
+ const scope = [`--region=${target.region}`, `--project=${target.projectId}`];
101
+ const found = await findService(run, scope, "admin");
102
+ if (!found.ok)
103
+ return { ok: false, error: found.error };
104
+ const url = await run([
105
+ "run",
106
+ "services",
107
+ "describe",
108
+ found.service,
109
+ ...scope,
110
+ "--format=value(status.url)",
111
+ ]);
112
+ const value = url.stdout.trim();
113
+ if (!url.ok || !value) {
114
+ return { ok: false, error: `read the admin's URL: ${url.stderr.trim() || "empty"}` };
115
+ }
116
+ return { ok: true, value };
117
+ }
118
+ /** On gcp the admin's host must resolve to the load balancer and have its certificate. */
119
+ function unreachableHint(target) {
120
+ return target.provider === "gcp"
121
+ ? ` — admin.${target.domain} must resolve to the install's load balancer with an ACTIVE certificate (check \`dig +short admin.${target.domain}\`).`
122
+ : "";
123
+ }
124
+ /** A JWT signed as the install's MCP service account, for the admin's IAP. */
125
+ async function signIapJwt(run, doFetch, projectId, adminUrl, now) {
126
+ const access = await run(["auth", "print-access-token"]);
127
+ const accessToken = access.stdout.trim();
128
+ if (!access.ok || !accessToken) {
129
+ return {
130
+ ok: false,
131
+ error: `get an access token from gcloud: ${access.stderr.trim() || "empty"}. Run \`gcloud auth login\` with the account you provisioned with.`,
132
+ };
133
+ }
134
+ const serviceAccount = mcpIapServiceAccountEmail(projectId);
135
+ const iat = now ? now() : Math.floor(Date.now() / 1000);
136
+ const res = await doFetch(`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${encodeURIComponent(serviceAccount)}:signJwt`, {
137
+ method: "POST",
138
+ headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
139
+ body: JSON.stringify({
140
+ payload: JSON.stringify({
141
+ iss: serviceAccount,
142
+ sub: serviceAccount,
143
+ aud: `${new URL(adminUrl).origin}/*`,
144
+ iat,
145
+ exp: iat + IAP_JWT_LIFETIME_S,
146
+ }),
147
+ }),
148
+ });
149
+ if (!res.ok) {
150
+ const detail = (await res.text().catch(() => "")).slice(0, 300);
151
+ return {
152
+ ok: false,
153
+ error: `sign an IAP token as ${serviceAccount} (HTTP ${res.status}): ${detail} — your account needs roles/iam.serviceAccountTokenCreator on it; \`cms-provision upgrade\` grants it to everyone the admin's IAP lets in.`,
154
+ };
155
+ }
156
+ const { signedJwt } = (await res.json());
157
+ if (!signedJwt)
158
+ return { ok: false, error: "IAM signJwt answered without a signedJwt" };
159
+ return { ok: true, value: signedJwt };
160
+ }
161
+ //# sourceMappingURL=admin-internal-google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-google.js","sourceRoot":"","sources":["../src/admin-internal-google.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * admin-internal.ts on a self-hosted install: from the install's host into
3
+ * the admin container, never over the internet.
4
+ *
5
+ * The root of trust is the host: whoever may run `docker compose` for the
6
+ * install already controls its database, its files and its secrets, so
7
+ * reaching the admin's internal endpoints grants nothing new. The CLI signs
8
+ * a 5-minute token for the endpoint's scope with `.caelo/internal-secret.json`
9
+ * (the secret the Compose file gives the admin as `CAELO_INTERNAL_SECRET`)
10
+ * and runs one `fetch` inside the admin container (`docker compose exec`)
11
+ * against the admin's own port on 127.0.0.1 — the admin image has bun but
12
+ * no curl.
13
+ *
14
+ * Nothing secret travels in process arguments, which other users of the
15
+ * host can read in `ps`: the token and the request body go in on the exec's
16
+ * stdin, the answer comes back on its stdout. `docker compose exec` output
17
+ * is not part of the container's log (`docker compose logs` shows only the
18
+ * container's main process), so the answer is written nowhere.
19
+ */
20
+ import type { AdminInternalAnswer, AdminInternalCall, AdminInternalDeps, SelfHostedAdminTarget } from "./admin-internal.js";
21
+ /**
22
+ * What runs inside the admin container. It reads `{ endpoint, token, body }`
23
+ * from stdin, POSTs to the admin's own port and prints one JSON line:
24
+ * `{ status, text }`, or `{ unreachable }` when the admin does not answer.
25
+ * It holds no secret itself — it is visible in `ps` on the host.
26
+ */
27
+ export declare const IN_CONTAINER_FETCH = "(async () => {\n const out = (o) => process.stdout.write(JSON.stringify(o));\n const req = JSON.parse(await Bun.stdin.text());\n try {\n const res = await fetch(\"http://127.0.0.1:\" + process.env.PORT + req.endpoint, {\n method: \"POST\",\n headers: { \"x-caelo-internal-token\": req.token, \"content-type\": \"application/json\" },\n body: JSON.stringify(req.body),\n });\n out({ status: res.status, text: await res.text() });\n } catch (e) {\n out({ unreachable: String(e) });\n }\n})();";
28
+ /** The admin call on a self-hosted install (see the file comment). */
29
+ export declare function callSelfHostedAdmin<T>(target: SelfHostedAdminTarget, call: AdminInternalCall, deps: AdminInternalDeps): Promise<AdminInternalAnswer<T>>;
30
+ //# sourceMappingURL=admin-internal-self-hosted.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-self-hosted.d.ts","sourceRoot":"","sources":["../src/admin-internal-self-hosted.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,qBAAqB,CAAC;AAW7B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,+gBAazB,CAAC;AAEP,sEAAsE;AACtE,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CA0DjC"}
@@ -0,0 +1,92 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ import { readAdminAnswer } from "./admin-internal-answer.js";
3
+ import { mintInternalToken } from "./internal-token.js";
4
+ import { selfCommand } from "./self-command.js";
5
+ import { internalSecretPath, readInternalSecret } from "./self-hosted-internal-secret.js";
6
+ import { bunHostRunner } from "./self-hosted-release.js";
7
+ import { compose } from "./self-hosted-upgrade.js";
8
+ /** The admin's Compose service (compose.ts). */
9
+ const ADMIN_SERVICE = "caelo-admin";
10
+ /**
11
+ * What runs inside the admin container. It reads `{ endpoint, token, body }`
12
+ * from stdin, POSTs to the admin's own port and prints one JSON line:
13
+ * `{ status, text }`, or `{ unreachable }` when the admin does not answer.
14
+ * It holds no secret itself — it is visible in `ps` on the host.
15
+ */
16
+ export const IN_CONTAINER_FETCH = `(async () => {
17
+ const out = (o) => process.stdout.write(JSON.stringify(o));
18
+ const req = JSON.parse(await Bun.stdin.text());
19
+ try {
20
+ const res = await fetch("http://127.0.0.1:" + process.env.PORT + req.endpoint, {
21
+ method: "POST",
22
+ headers: { "x-caelo-internal-token": req.token, "content-type": "application/json" },
23
+ body: JSON.stringify(req.body),
24
+ });
25
+ out({ status: res.status, text: await res.text() });
26
+ } catch (e) {
27
+ out({ unreachable: String(e) });
28
+ }
29
+ })();`;
30
+ /** The admin call on a self-hosted install (see the file comment). */
31
+ export async function callSelfHostedAdmin(target, call, deps) {
32
+ const host = deps.host ?? bunHostRunner;
33
+ const restart = selfCommand("up");
34
+ let secret;
35
+ try {
36
+ secret = readInternalSecret(target.caeloDir);
37
+ }
38
+ catch (e) {
39
+ return { ok: false, error: e.message };
40
+ }
41
+ if (secret === null) {
42
+ return {
43
+ ok: false,
44
+ error: `${internalSecretPath(target.caeloDir)} does not exist. Run \`${restart}\` (it creates the secret and restarts the admin with it), then run this command again.`,
45
+ };
46
+ }
47
+ const token = await mintInternalToken(secret, call.scope);
48
+ const r = await host.run(compose(target.caeloDir, "exec", "-T", ADMIN_SERVICE, "bun", "-e", IN_CONTAINER_FETCH), { stdinText: JSON.stringify({ endpoint: call.endpoint, token, body: call.body }) });
49
+ if (r.exitCode !== 0) {
50
+ return {
51
+ ok: false,
52
+ error: `run a request inside the ${ADMIN_SERVICE} container (docker compose exec, exit ${r.exitCode}): ${tail(r.stderr) || "no output"}. The admin must be running — start it with \`${restart}\`, then run this command again.`,
53
+ };
54
+ }
55
+ let reply;
56
+ try {
57
+ reply = JSON.parse(r.stdout);
58
+ }
59
+ catch {
60
+ return {
61
+ ok: false,
62
+ error: `the request inside the ${ADMIN_SERVICE} container printed no answer: ${tail(r.stdout) || tail(r.stderr) || "empty"}`,
63
+ };
64
+ }
65
+ if (typeof reply.unreachable === "string") {
66
+ return {
67
+ ok: false,
68
+ error: `the admin did not answer on its own port inside the container (${reply.unreachable}). Check \`docker compose -f ${target.caeloDir}/docker-compose.yml logs ${ADMIN_SERVICE}\`, then run this command again.`,
69
+ };
70
+ }
71
+ if (typeof reply.status !== "number" || typeof reply.text !== "string") {
72
+ return {
73
+ ok: false,
74
+ error: `the request inside the ${ADMIN_SERVICE} container answered an unexpected shape`,
75
+ };
76
+ }
77
+ if (reply.status === 401) {
78
+ return {
79
+ ok: false,
80
+ error: `the admin refused the internal token (HTTP 401: ${reply.text.slice(0, 200)}) — the running admin does not hold the secret in ${internalSecretPath(target.caeloDir)} (it was started from an older Compose file). Run \`${restart}\` to restart it with that secret, then run this command again.`,
81
+ };
82
+ }
83
+ const answer = readAdminAnswer(reply.status, reply.text, call, "cms-provision upgrade");
84
+ return answer.ok
85
+ ? { ok: true, value: { answer: answer.value, adminUrl: target.adminUrl } }
86
+ : answer;
87
+ }
88
+ /** The last lines of a command's output, for an error message. */
89
+ function tail(text) {
90
+ return text.trim().split("\n").slice(-5).join("\n").slice(0, 500);
91
+ }
92
+ //# sourceMappingURL=admin-internal-self-hosted.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-internal-self-hosted.js","sourceRoot":"","sources":["../src/admin-internal-self-hosted.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,34 @@
1
- import { type GcloudResult } from "./gcloud.js";
2
- type Run = (args: string[]) => Promise<GcloudResult>;
3
- export interface AdminInternalTarget {
1
+ import type { GcloudResult } from "./gcloud.js";
2
+ import type { HostRunner } from "./self-hosted-release.js";
3
+ /** Which admin to call, and what the provider needs to reach it. */
4
+ export type AdminInternalTarget = GoogleAdminTarget | SelfHostedAdminTarget;
5
+ /** A Google Cloud install (IAP in front of the admin). */
6
+ export type GoogleAdminTarget = {
7
+ readonly provider: "gcp-firebase";
4
8
  readonly projectId: string;
5
9
  readonly region: string;
10
+ } | {
11
+ readonly provider: "gcp";
12
+ readonly projectId: string;
13
+ /** The install's domain; the admin answers on `admin.<domain>`. */
14
+ readonly domain: string;
15
+ };
16
+ /** A self-hosted install on this host. */
17
+ export interface SelfHostedAdminTarget {
18
+ readonly provider: "self-hosted";
19
+ /** The install's `.caelo/` directory (Compose file, internal secret). */
20
+ readonly caeloDir: string;
21
+ /** The admin's public origin, for the links the operator opens. */
22
+ readonly adminUrl: string;
6
23
  }
7
24
  export interface AdminInternalDeps {
8
- readonly run?: Run;
25
+ /** gcloud (Google providers). */
26
+ readonly run?: (args: string[]) => Promise<GcloudResult>;
9
27
  readonly fetch?: typeof fetch;
10
28
  /** Seconds since epoch (IAP JWT); tests pin it. */
11
29
  readonly now?: () => number;
30
+ /** `docker compose` on the install's host (self-hosted). */
31
+ readonly host?: HostRunner;
12
32
  }
13
33
  /** One endpoint call: where, under which token scope, with what body. */
14
34
  export interface AdminInternalCall {
@@ -27,15 +47,16 @@ export type AdminInternalResult<T> = {
27
47
  ok: false;
28
48
  error: string;
29
49
  };
50
+ /** What a transport hands back: the admin's answer and the URL it serves on. */
51
+ export type AdminInternalAnswer<T> = AdminInternalResult<{
52
+ answer: T;
53
+ adminUrl: string;
54
+ }>;
30
55
  /**
31
56
  * POST `call.body` to the endpoint and return the admin's JSON answer and
32
- * the admin URL it was reached at (to build links the operator opens).
33
- * Every failure names the fix: an IAP refusal or a pre-endpoint admin points
34
- * at `cms-provision upgrade`, a refusal carries the admin's own message.
57
+ * the admin URL the operator opens (to build links). Every failure names
58
+ * the fix: a gate refusal or a pre-endpoint admin points at the command that
59
+ * repairs it, a refusal carries the admin's own message.
35
60
  */
36
- export declare function callAdminInternal<T>(target: AdminInternalTarget, call: AdminInternalCall, deps?: AdminInternalDeps): Promise<AdminInternalResult<{
37
- answer: T;
38
- adminUrl: string;
39
- }>>;
40
- export {};
61
+ export declare function callAdminInternal<T>(target: AdminInternalTarget, call: AdminInternalCall, deps?: AdminInternalDeps): Promise<AdminInternalAnswer<T>>;
41
62
  //# sourceMappingURL=admin-internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin-internal.d.ts","sourceRoot":"","sources":["../src/admin-internal.ts"],"names":[],"mappings":"AAuBA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAIzE,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAKrD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,iBAAiB,EACvB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,mBAAmB,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAkG/D"}
1
+ {"version":3,"file":"admin-internal.d.ts","sourceRoot":"","sources":["../src/admin-internal.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG,qBAAqB,CAAC;AAE5E,0DAA0D;AAC1D,MAAM,MAAM,iBAAiB,GACzB;IACE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IACE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEN,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,mBAAmB,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1F;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,iBAAiB,EACvB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAIjC"}