@homeflare/alchemy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cloudflare/client.d.ts +45 -0
  4. package/dist/cloudflare/client.d.ts.map +1 -0
  5. package/dist/cloudflare/index.d.ts +11 -0
  6. package/dist/cloudflare/index.d.ts.map +1 -0
  7. package/dist/cloudflare/index.js +129 -0
  8. package/dist/cloudflare/index.js.map +13 -0
  9. package/dist/cloudflare/lock-rules.d.ts +50 -0
  10. package/dist/cloudflare/lock-rules.d.ts.map +1 -0
  11. package/dist/cloudflare/providers.d.ts +19 -0
  12. package/dist/cloudflare/providers.d.ts.map +1 -0
  13. package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
  14. package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
  15. package/dist/forgejo/branch-protection-form.d.ts +19 -0
  16. package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
  17. package/dist/forgejo/branch-protection.d.ts +47 -0
  18. package/dist/forgejo/branch-protection.d.ts.map +1 -0
  19. package/dist/forgejo/client.d.ts +37 -0
  20. package/dist/forgejo/client.d.ts.map +1 -0
  21. package/dist/forgejo/index.d.ts +18 -0
  22. package/dist/forgejo/index.d.ts.map +1 -0
  23. package/dist/forgejo/index.js +479 -0
  24. package/dist/forgejo/index.js.map +21 -0
  25. package/dist/forgejo/org-actions-secrets.d.ts +45 -0
  26. package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
  27. package/dist/forgejo/org-label.d.ts +46 -0
  28. package/dist/forgejo/org-label.d.ts.map +1 -0
  29. package/dist/forgejo/org-team.d.ts +42 -0
  30. package/dist/forgejo/org-team.d.ts.map +1 -0
  31. package/dist/forgejo/repo-webhook-form.d.ts +27 -0
  32. package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
  33. package/dist/forgejo/repo-webhook.d.ts +46 -0
  34. package/dist/forgejo/repo-webhook.d.ts.map +1 -0
  35. package/dist/forgejo/repository.d.ts +45 -0
  36. package/dist/forgejo/repository.d.ts.map +1 -0
  37. package/dist/forgejo/resource.d.ts +83 -0
  38. package/dist/forgejo/resource.d.ts.map +1 -0
  39. package/dist/forgejo/team-member.d.ts +39 -0
  40. package/dist/forgejo/team-member.d.ts.map +1 -0
  41. package/dist/forgejo/values.d.ts +24 -0
  42. package/dist/forgejo/values.d.ts.map +1 -0
  43. package/dist/index.d.ts +18 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +9 -0
  47. package/dist/openbao/auth-role-form.d.ts +38 -0
  48. package/dist/openbao/auth-role-form.d.ts.map +1 -0
  49. package/dist/openbao/auth-role.d.ts +20 -0
  50. package/dist/openbao/auth-role.d.ts.map +1 -0
  51. package/dist/openbao/bao-address.d.ts +72 -0
  52. package/dist/openbao/bao-address.d.ts.map +1 -0
  53. package/dist/openbao/bao-http.d.ts +71 -0
  54. package/dist/openbao/bao-http.d.ts.map +1 -0
  55. package/dist/openbao/bao-status.d.ts +74 -0
  56. package/dist/openbao/bao-status.d.ts.map +1 -0
  57. package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
  58. package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
  59. package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
  60. package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
  61. package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
  62. package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
  63. package/dist/openbao/cloudflare-parity.d.ts +14 -0
  64. package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
  65. package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
  66. package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
  67. package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
  68. package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
  69. package/dist/openbao/cloudflare-policy.d.ts +65 -0
  70. package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
  71. package/dist/openbao/cloudflare-role-form.d.ts +110 -0
  72. package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
  73. package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
  74. package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
  75. package/dist/openbao/cloudflare-role.d.ts +35 -0
  76. package/dist/openbao/cloudflare-role.d.ts.map +1 -0
  77. package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
  78. package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
  79. package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
  80. package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
  81. package/dist/openbao/digest.d.ts +24 -0
  82. package/dist/openbao/digest.d.ts.map +1 -0
  83. package/dist/openbao/fake-bao.d.ts +40 -0
  84. package/dist/openbao/fake-bao.d.ts.map +1 -0
  85. package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
  86. package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
  87. package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
  88. package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
  89. package/dist/openbao/index.d.ts +18 -0
  90. package/dist/openbao/index.d.ts.map +1 -0
  91. package/dist/openbao/index.js +1158 -0
  92. package/dist/openbao/index.js.map +33 -0
  93. package/dist/openbao/mount-form.d.ts +60 -0
  94. package/dist/openbao/mount-form.d.ts.map +1 -0
  95. package/dist/openbao/mount-wire.d.ts +24 -0
  96. package/dist/openbao/mount-wire.d.ts.map +1 -0
  97. package/dist/openbao/mount.d.ts +20 -0
  98. package/dist/openbao/mount.d.ts.map +1 -0
  99. package/dist/openbao/pki-role-form.d.ts +118 -0
  100. package/dist/openbao/pki-role-form.d.ts.map +1 -0
  101. package/dist/openbao/pki-role.d.ts +50 -0
  102. package/dist/openbao/pki-role.d.ts.map +1 -0
  103. package/dist/openbao/policy-assembly.d.ts +13 -0
  104. package/dist/openbao/policy-assembly.d.ts.map +1 -0
  105. package/dist/openbao/policy-wire.d.ts +41 -0
  106. package/dist/openbao/policy-wire.d.ts.map +1 -0
  107. package/dist/openbao/policy.d.ts +83 -0
  108. package/dist/openbao/policy.d.ts.map +1 -0
  109. package/dist/openbao/proxmox-role-form.d.ts +92 -0
  110. package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
  111. package/dist/openbao/proxmox-role.d.ts +47 -0
  112. package/dist/openbao/proxmox-role.d.ts.map +1 -0
  113. package/dist/openbao/ssh-role-form.d.ts +99 -0
  114. package/dist/openbao/ssh-role-form.d.ts.map +1 -0
  115. package/dist/openbao/ssh-role.d.ts +43 -0
  116. package/dist/openbao/ssh-role.d.ts.map +1 -0
  117. package/dist/proxmox/acl.d.ts +72 -0
  118. package/dist/proxmox/acl.d.ts.map +1 -0
  119. package/dist/proxmox/api-token-form.d.ts +59 -0
  120. package/dist/proxmox/api-token-form.d.ts.map +1 -0
  121. package/dist/proxmox/api-token.d.ts +159 -0
  122. package/dist/proxmox/api-token.d.ts.map +1 -0
  123. package/dist/proxmox/backup-job.d.ts +112 -0
  124. package/dist/proxmox/backup-job.d.ts.map +1 -0
  125. package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
  126. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
  127. package/dist/proxmox/ceph-daemon.d.ts +162 -0
  128. package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
  129. package/dist/proxmox/ceph-flag.d.ts +131 -0
  130. package/dist/proxmox/ceph-flag.d.ts.map +1 -0
  131. package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
  132. package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
  133. package/dist/proxmox/ceph-fs.d.ts +124 -0
  134. package/dist/proxmox/ceph-fs.d.ts.map +1 -0
  135. package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
  136. package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
  137. package/dist/proxmox/ceph-osd-write.d.ts +36 -0
  138. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
  139. package/dist/proxmox/ceph-osd.d.ts +102 -0
  140. package/dist/proxmox/ceph-osd.d.ts.map +1 -0
  141. package/dist/proxmox/ceph-pool-form.d.ts +217 -0
  142. package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
  143. package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
  144. package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
  145. package/dist/proxmox/ceph-pool.d.ts +48 -0
  146. package/dist/proxmox/ceph-pool.d.ts.map +1 -0
  147. package/dist/proxmox/client.d.ts +64 -0
  148. package/dist/proxmox/client.d.ts.map +1 -0
  149. package/dist/proxmox/credentials.d.ts +153 -0
  150. package/dist/proxmox/credentials.d.ts.map +1 -0
  151. package/dist/proxmox/firewall-alias-form.d.ts +98 -0
  152. package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
  153. package/dist/proxmox/firewall-alias.d.ts +141 -0
  154. package/dist/proxmox/firewall-alias.d.ts.map +1 -0
  155. package/dist/proxmox/generated/pbs.d.ts +388 -0
  156. package/dist/proxmox/generated/pbs.d.ts.map +1 -0
  157. package/dist/proxmox/generated/pve.d.ts +4713 -0
  158. package/dist/proxmox/generated/pve.d.ts.map +1 -0
  159. package/dist/proxmox/group.d.ts +104 -0
  160. package/dist/proxmox/group.d.ts.map +1 -0
  161. package/dist/proxmox/ha-resource.d.ts +104 -0
  162. package/dist/proxmox/ha-resource.d.ts.map +1 -0
  163. package/dist/proxmox/ha-rule-form.d.ts +103 -0
  164. package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
  165. package/dist/proxmox/ha-rule.d.ts +156 -0
  166. package/dist/proxmox/ha-rule.d.ts.map +1 -0
  167. package/dist/proxmox/index.d.ts +43 -0
  168. package/dist/proxmox/index.d.ts.map +1 -0
  169. package/dist/proxmox/index.js +2322 -0
  170. package/dist/proxmox/index.js.map +70 -0
  171. package/dist/proxmox/lease-cache.d.ts +98 -0
  172. package/dist/proxmox/lease-cache.d.ts.map +1 -0
  173. package/dist/proxmox/lxc.d.ts +59 -0
  174. package/dist/proxmox/lxc.d.ts.map +1 -0
  175. package/dist/proxmox/members.d.ts +56 -0
  176. package/dist/proxmox/members.d.ts.map +1 -0
  177. package/dist/proxmox/metric-server-form.d.ts +53 -0
  178. package/dist/proxmox/metric-server-form.d.ts.map +1 -0
  179. package/dist/proxmox/metric-server-otel.d.ts +49 -0
  180. package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
  181. package/dist/proxmox/metric-server.d.ts +123 -0
  182. package/dist/proxmox/metric-server.d.ts.map +1 -0
  183. package/dist/proxmox/network-apply-read.d.ts +93 -0
  184. package/dist/proxmox/network-apply-read.d.ts.map +1 -0
  185. package/dist/proxmox/network-apply.d.ts +102 -0
  186. package/dist/proxmox/network-apply.d.ts.map +1 -0
  187. package/dist/proxmox/node-network-form.d.ts +128 -0
  188. package/dist/proxmox/node-network-form.d.ts.map +1 -0
  189. package/dist/proxmox/node-network.d.ts +173 -0
  190. package/dist/proxmox/node-network.d.ts.map +1 -0
  191. package/dist/proxmox/notification-target-form.d.ts +44 -0
  192. package/dist/proxmox/notification-target-form.d.ts.map +1 -0
  193. package/dist/proxmox/notification-target.d.ts +146 -0
  194. package/dist/proxmox/notification-target.d.ts.map +1 -0
  195. package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
  196. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
  197. package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
  198. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
  199. package/dist/proxmox/pbs-datastore.d.ts +140 -0
  200. package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
  201. package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
  202. package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
  203. package/dist/proxmox/pbs-prune-job.d.ts +168 -0
  204. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
  205. package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
  206. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
  207. package/dist/proxmox/pbs-sync-job.d.ts +246 -0
  208. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
  209. package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
  210. package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
  211. package/dist/proxmox/pbs-verify-job.d.ts +145 -0
  212. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
  213. package/dist/proxmox/pool.d.ts +42 -0
  214. package/dist/proxmox/pool.d.ts.map +1 -0
  215. package/dist/proxmox/qemu.d.ts +46 -0
  216. package/dist/proxmox/qemu.d.ts.map +1 -0
  217. package/dist/proxmox/replication-job-form.d.ts +71 -0
  218. package/dist/proxmox/replication-job-form.d.ts.map +1 -0
  219. package/dist/proxmox/replication-job.d.ts +162 -0
  220. package/dist/proxmox/replication-job.d.ts.map +1 -0
  221. package/dist/proxmox/resource.d.ts +157 -0
  222. package/dist/proxmox/resource.d.ts.map +1 -0
  223. package/dist/proxmox/role.d.ts +97 -0
  224. package/dist/proxmox/role.d.ts.map +1 -0
  225. package/dist/proxmox/sdn-apply-read.d.ts +80 -0
  226. package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
  227. package/dist/proxmox/sdn-apply.d.ts +63 -0
  228. package/dist/proxmox/sdn-apply.d.ts.map +1 -0
  229. package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
  230. package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
  231. package/dist/proxmox/sdn-subnet.d.ts +161 -0
  232. package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
  233. package/dist/proxmox/sdn-vnet.d.ts +113 -0
  234. package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
  235. package/dist/proxmox/sdn-zone.d.ts +126 -0
  236. package/dist/proxmox/sdn-zone.d.ts.map +1 -0
  237. package/dist/proxmox/storage.d.ts +120 -0
  238. package/dist/proxmox/storage.d.ts.map +1 -0
  239. package/dist/proxmox/user.d.ts +107 -0
  240. package/dist/proxmox/user.d.ts.map +1 -0
  241. package/dist/proxmox/values.d.ts +134 -0
  242. package/dist/proxmox/values.d.ts.map +1 -0
  243. package/dist/proxmox/zfs-pool-write.d.ts +76 -0
  244. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
  245. package/dist/proxmox/zfs-pool.d.ts +146 -0
  246. package/dist/proxmox/zfs-pool.d.ts.map +1 -0
  247. package/dist/talos/credentials.d.ts +30 -0
  248. package/dist/talos/credentials.d.ts.map +1 -0
  249. package/dist/talos/index.d.ts +15 -0
  250. package/dist/talos/index.d.ts.map +1 -0
  251. package/dist/talos/index.js +392 -0
  252. package/dist/talos/index.js.map +16 -0
  253. package/dist/talos/kubeconfig.d.ts +53 -0
  254. package/dist/talos/kubeconfig.d.ts.map +1 -0
  255. package/dist/talos/resource.d.ts +32 -0
  256. package/dist/talos/resource.d.ts.map +1 -0
  257. package/dist/talos/talos-bootstrap.d.ts +27 -0
  258. package/dist/talos/talos-bootstrap.d.ts.map +1 -0
  259. package/dist/talos/talos-cluster-health.d.ts +36 -0
  260. package/dist/talos/talos-cluster-health.d.ts.map +1 -0
  261. package/dist/talos/talos-machine-config.d.ts +41 -0
  262. package/dist/talos/talos-machine-config.d.ts.map +1 -0
  263. package/dist/talos/talosctl.d.ts +29 -0
  264. package/dist/talos/talosctl.d.ts.map +1 -0
  265. package/dist/talos/values.d.ts +20 -0
  266. package/dist/talos/values.d.ts.map +1 -0
  267. package/package.json +57 -0
  268. package/src/cloudflare/client.ts +76 -0
  269. package/src/cloudflare/index.ts +10 -0
  270. package/src/cloudflare/lock-rules.ts +60 -0
  271. package/src/cloudflare/providers.ts +23 -0
  272. package/src/cloudflare/r2-bucket-lock.ts +220 -0
  273. package/src/forgejo/branch-protection-form.ts +29 -0
  274. package/src/forgejo/branch-protection.ts +101 -0
  275. package/src/forgejo/client.ts +136 -0
  276. package/src/forgejo/index.ts +17 -0
  277. package/src/forgejo/org-actions-secrets.ts +99 -0
  278. package/src/forgejo/org-label.ts +100 -0
  279. package/src/forgejo/org-team.ts +105 -0
  280. package/src/forgejo/repo-webhook-form.ts +47 -0
  281. package/src/forgejo/repo-webhook.ts +118 -0
  282. package/src/forgejo/repository.ts +126 -0
  283. package/src/forgejo/resource.ts +171 -0
  284. package/src/forgejo/team-member.ts +97 -0
  285. package/src/forgejo/values.test.ts +48 -0
  286. package/src/forgejo/values.ts +61 -0
  287. package/src/index.ts +18 -0
  288. package/src/openbao/auth-role-form.ts +112 -0
  289. package/src/openbao/auth-role.ts +101 -0
  290. package/src/openbao/bao-address.test.ts +89 -0
  291. package/src/openbao/bao-address.ts +97 -0
  292. package/src/openbao/bao-http-gate.test.ts +88 -0
  293. package/src/openbao/bao-http-unix.test.ts +70 -0
  294. package/src/openbao/bao-http.test.ts +161 -0
  295. package/src/openbao/bao-http.ts +185 -0
  296. package/src/openbao/bao-status.test.ts +114 -0
  297. package/src/openbao/bao-status.ts +137 -0
  298. package/src/openbao/cloudflare-group-scope.test.ts +103 -0
  299. package/src/openbao/cloudflare-group-scope.ts +83 -0
  300. package/src/openbao/cloudflare-parity-catalog.ts +104 -0
  301. package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
  302. package/src/openbao/cloudflare-parity.ts +152 -0
  303. package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
  304. package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
  305. package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
  306. package/src/openbao/cloudflare-permission-groups.ts +299 -0
  307. package/src/openbao/cloudflare-policy.ts +180 -0
  308. package/src/openbao/cloudflare-role-form.test.ts +177 -0
  309. package/src/openbao/cloudflare-role-form.ts +183 -0
  310. package/src/openbao/cloudflare-role-wire.test.ts +132 -0
  311. package/src/openbao/cloudflare-role-wire.ts +41 -0
  312. package/src/openbao/cloudflare-role.ts +189 -0
  313. package/src/openbao/cloudflare-roles-config.ts +230 -0
  314. package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
  315. package/src/openbao/cloudflare-roles-expand.ts +231 -0
  316. package/src/openbao/digest.ts +26 -0
  317. package/src/openbao/fake-bao.ts +84 -0
  318. package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
  319. package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
  320. package/src/openbao/forgejo-bootstrap.ts +181 -0
  321. package/src/openbao/index.ts +17 -0
  322. package/src/openbao/mount-form.ts +147 -0
  323. package/src/openbao/mount-wire.test.ts +108 -0
  324. package/src/openbao/mount-wire.ts +79 -0
  325. package/src/openbao/mount.ts +132 -0
  326. package/src/openbao/pki-role-form.ts +248 -0
  327. package/src/openbao/pki-role.ts +202 -0
  328. package/src/openbao/policy-assembly.test.ts +28 -0
  329. package/src/openbao/policy-assembly.ts +13 -0
  330. package/src/openbao/policy-wire.test.ts +90 -0
  331. package/src/openbao/policy-wire.ts +53 -0
  332. package/src/openbao/policy.ts +212 -0
  333. package/src/openbao/proxmox-role-form.ts +174 -0
  334. package/src/openbao/proxmox-role.ts +214 -0
  335. package/src/openbao/ssh-role-form.ts +248 -0
  336. package/src/openbao/ssh-role.ts +240 -0
  337. package/src/proxmox/acl.ts +267 -0
  338. package/src/proxmox/api-token-form.ts +122 -0
  339. package/src/proxmox/api-token.ts +222 -0
  340. package/src/proxmox/backup-job.ts +213 -0
  341. package/src/proxmox/ceph-daemon-form.ts +84 -0
  342. package/src/proxmox/ceph-daemon.ts +243 -0
  343. package/src/proxmox/ceph-flag.ts +249 -0
  344. package/src/proxmox/ceph-fs-wire.ts +246 -0
  345. package/src/proxmox/ceph-fs.ts +249 -0
  346. package/src/proxmox/ceph-osd-tree.ts +193 -0
  347. package/src/proxmox/ceph-osd-write.ts +76 -0
  348. package/src/proxmox/ceph-osd.ts +244 -0
  349. package/src/proxmox/ceph-pool-form.ts +249 -0
  350. package/src/proxmox/ceph-pool-settle.ts +70 -0
  351. package/src/proxmox/ceph-pool.ts +212 -0
  352. package/src/proxmox/client.test.ts +185 -0
  353. package/src/proxmox/client.ts +240 -0
  354. package/src/proxmox/credentials.test.ts +144 -0
  355. package/src/proxmox/credentials.ts +254 -0
  356. package/src/proxmox/firewall-alias-form.ts +112 -0
  357. package/src/proxmox/firewall-alias.ts +208 -0
  358. package/src/proxmox/generated/pbs.ts +461 -0
  359. package/src/proxmox/generated/pve.ts +7344 -0
  360. package/src/proxmox/group.ts +199 -0
  361. package/src/proxmox/ha-resource.ts +169 -0
  362. package/src/proxmox/ha-rule-form.ts +185 -0
  363. package/src/proxmox/ha-rule.ts +240 -0
  364. package/src/proxmox/index.ts +42 -0
  365. package/src/proxmox/lease-cache.test.ts +163 -0
  366. package/src/proxmox/lease-cache.ts +131 -0
  367. package/src/proxmox/lxc.ts +117 -0
  368. package/src/proxmox/members.test.ts +54 -0
  369. package/src/proxmox/members.ts +174 -0
  370. package/src/proxmox/metric-server-form.ts +128 -0
  371. package/src/proxmox/metric-server-otel.ts +99 -0
  372. package/src/proxmox/metric-server.ts +214 -0
  373. package/src/proxmox/network-apply-read.ts +228 -0
  374. package/src/proxmox/network-apply.ts +244 -0
  375. package/src/proxmox/node-network-form.ts +213 -0
  376. package/src/proxmox/node-network.ts +243 -0
  377. package/src/proxmox/notification-target-form.ts +104 -0
  378. package/src/proxmox/notification-target.ts +222 -0
  379. package/src/proxmox/pbs-datastore-form.ts +248 -0
  380. package/src/proxmox/pbs-datastore-guard.ts +105 -0
  381. package/src/proxmox/pbs-datastore.ts +271 -0
  382. package/src/proxmox/pbs-prune-job-form.ts +208 -0
  383. package/src/proxmox/pbs-prune-job.ts +227 -0
  384. package/src/proxmox/pbs-sync-job-form.ts +234 -0
  385. package/src/proxmox/pbs-sync-job.ts +258 -0
  386. package/src/proxmox/pbs-verify-job-form.ts +131 -0
  387. package/src/proxmox/pbs-verify-job.ts +227 -0
  388. package/src/proxmox/pool.test.ts +42 -0
  389. package/src/proxmox/pool.ts +80 -0
  390. package/src/proxmox/qemu.ts +89 -0
  391. package/src/proxmox/replication-job-form.ts +89 -0
  392. package/src/proxmox/replication-job.ts +248 -0
  393. package/src/proxmox/resource.ts +257 -0
  394. package/src/proxmox/role.ts +161 -0
  395. package/src/proxmox/sdn-apply-read.test.ts +152 -0
  396. package/src/proxmox/sdn-apply-read.ts +189 -0
  397. package/src/proxmox/sdn-apply.ts +154 -0
  398. package/src/proxmox/sdn-subnet-form.ts +156 -0
  399. package/src/proxmox/sdn-subnet.ts +238 -0
  400. package/src/proxmox/sdn-vnet.ts +228 -0
  401. package/src/proxmox/sdn-zone.ts +246 -0
  402. package/src/proxmox/storage.ts +245 -0
  403. package/src/proxmox/user.ts +214 -0
  404. package/src/proxmox/values.test.ts +193 -0
  405. package/src/proxmox/values.ts +203 -0
  406. package/src/proxmox/zfs-pool-write.ts +130 -0
  407. package/src/proxmox/zfs-pool.ts +253 -0
  408. package/src/talos/credentials.ts +136 -0
  409. package/src/talos/index.ts +14 -0
  410. package/src/talos/kubeconfig.ts +170 -0
  411. package/src/talos/resource.ts +31 -0
  412. package/src/talos/talos-bootstrap.ts +109 -0
  413. package/src/talos/talos-cluster-health.ts +119 -0
  414. package/src/talos/talos-machine-config.ts +146 -0
  415. package/src/talos/talosctl.ts +94 -0
  416. package/src/talos/values.test.ts +80 -0
  417. package/src/talos/values.ts +70 -0
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/forgejo/branch-protection.ts", "../src/forgejo/branch-protection-form.ts", "../src/forgejo/resource.ts", "../src/forgejo/client.ts", "../src/forgejo/values.ts", "../src/forgejo/org-actions-secrets.ts", "../src/forgejo/org-label.ts", "../src/forgejo/org-team.ts", "../src/forgejo/repo-webhook.ts", "../src/forgejo/repo-webhook-form.ts", "../src/forgejo/repository.ts", "../src/forgejo/team-member.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * `Forgejo.BranchProtection` — one rule at `/repos/{owner}/{repo}/branch_protections/{name}`.\n *\n * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json`: create is POST collection;\n * read/update/delete use the branch/rule name in the path segment `{name}`. Response carries\n * `rule_name` (preferred) and deprecated `branch_name`.\n *\n * ⛔ `defaultRemovalPolicy: 'retain'` — dropping a protection rule from the stack must not silently\n * unprotect a branch; opt into `.pipe(RemovalPolicy.destroy())` to DELETE.\n *\n * ⛔ TOKEN NEEDS `write:repository` TO CREATE OR PATCH — get needs `read:repository`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { branchProtectionForm } from './branch-protection-form.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { bool, int, stringArray, text } from './values.ts';\n\nexport interface BranchProtectionProps {\n owner: string;\n repo: string;\n /** Branch or rule name — path key and `rule_name` on the wire. */\n branchName: string;\n requiredApprovals?: number;\n enablePush?: boolean;\n enableStatusCheck?: boolean;\n requireSignedCommits?: boolean;\n dismissStaleApprovals?: boolean;\n ignoreStaleApprovals?: boolean;\n applyToAdmins?: boolean;\n statusCheckContexts?: string[];\n}\n\nexport interface BranchProtectionAttributes {\n owner: string;\n repo: string;\n branchName: string;\n requiredApprovals: number;\n enablePush: boolean;\n enableStatusCheck: boolean;\n requireSignedCommits: boolean;\n dismissStaleApprovals: boolean;\n ignoreStaleApprovals: boolean;\n applyToAdmins: boolean;\n statusCheckContexts: string[];\n}\n\nexport interface ForgejoBranchProtection extends Resource<\n 'Forgejo.BranchProtection',\n BranchProtectionProps,\n BranchProtectionAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoBranchProtection = Resource<ForgejoBranchProtection>(\n 'Forgejo.BranchProtection',\n { defaultRemovalPolicy: 'retain' },\n);\n\nconst handlers = forgejoHandlers<BranchProtectionProps, BranchProtectionAttributes>({\n attributes: (live, props) => {\n const ruleName = text(live['rule_name'], text(live['branch_name']));\n if (ruleName === '') return undefined;\n return {\n applyToAdmins: bool(live['apply_to_admins']),\n branchName: props.branchName,\n dismissStaleApprovals: bool(live['dismiss_stale_approvals']),\n enablePush: bool(live['enable_push'], true),\n enableStatusCheck: bool(live['enable_status_check']),\n ignoreStaleApprovals: bool(live['ignore_stale_approvals']),\n owner: props.owner,\n repo: props.repo,\n requireSignedCommits: bool(live['require_signed_commits']),\n requiredApprovals: int(live['required_approvals']),\n statusCheckContexts: stringArray(live['status_check_contexts']),\n };\n },\n collection: (props) => `repos/${props.owner}/${props.repo}/branch_protections`,\n createForm: branchProtectionForm,\n matches: (attributes, props) =>\n attributes.requiredApprovals === (props.requiredApprovals ?? 0) &&\n attributes.enablePush === (props.enablePush ?? true) &&\n attributes.enableStatusCheck === (props.enableStatusCheck ?? false) &&\n attributes.requireSignedCommits === (props.requireSignedCommits ?? false) &&\n attributes.dismissStaleApprovals === (props.dismissStaleApprovals ?? false) &&\n attributes.ignoreStaleApprovals === (props.ignoreStaleApprovals ?? false) &&\n attributes.applyToAdmins === (props.applyToAdmins ?? false) &&\n (props.statusCheckContexts === undefined ||\n stringArray(props.statusCheckContexts).join('\\0') ===\n attributes.statusCheckContexts.join('\\0')),\n path: (props) => `repos/${props.owner}/${props.repo}/branch_protections/${props.branchName}`,\n updateForm: branchProtectionForm,\n});\n\nexport const ForgejoBranchProtectionProvider = () =>\n Provider.effect(\n ForgejoBranchProtection,\n Effect.succeed(ForgejoBranchProtection.Provider.of(handlers)),\n );\n",
6
+ "/**\n * Create/PATCH bodies for `Forgejo.BranchProtection` — extracted for the 250-line cap.\n *\n * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json` — create posts\n * `CreateBranchProtectionOption`; update sends `EditBranchProtectionOption` fields only.\n */\nimport type { BranchProtectionProps } from './branch-protection.ts';\n\nexport const branchProtectionForm = (props: BranchProtectionProps) => ({\n ...(props.applyToAdmins === undefined ? {} : { apply_to_admins: props.applyToAdmins }),\n ...(props.dismissStaleApprovals === undefined\n ? {}\n : { dismiss_stale_approvals: props.dismissStaleApprovals }),\n ...(props.enablePush === undefined ? {} : { enable_push: props.enablePush }),\n ...(props.enableStatusCheck === undefined\n ? {}\n : { enable_status_check: props.enableStatusCheck }),\n ...(props.ignoreStaleApprovals === undefined\n ? {}\n : { ignore_stale_approvals: props.ignoreStaleApprovals }),\n ...(props.requireSignedCommits === undefined\n ? {}\n : { require_signed_commits: props.requireSignedCommits }),\n ...(props.requiredApprovals === undefined ? {} : { required_approvals: props.requiredApprovals }),\n ...(props.statusCheckContexts === undefined\n ? {}\n : { status_check_contexts: props.statusCheckContexts }),\n rule_name: props.branchName,\n});\n",
7
+ "/**\n * One shape for every Forgejo object — the same four operations, different path and JSON body.\n *\n * ⛔ THE ALTERNATIVE IS COPYING THE SAME HANDLERS PER RESOURCE. Repositories, org labels, teams,\n * runners, branch protection and package settings are all create / read / update / delete over\n * `/api/v1` with JSON bodies. Hand-rolling the fourth copy is where someone drops the read-back\n * or the isResolved guard and the plan lies.\n *\n * ★ SO THE FOUR OPERATIONS LIVE HERE, ONCE. A new Forgejo object should be a spec plus ~forty\n * lines, not a hundred and thirty.\n *\n * ⚠️ GITEA ANSWERS WITH THE OBJECT DIRECTLY — NOT `{\"data\": ...}`. See client.ts. reconcile still\n * READS BACK after every write rather than trusting the status code alone.\n *\n * ★ WHY `Forgejo.Repository` DECLARES `defaultRemovalPolicy: 'retain'`, WRITTEN ONCE HERE.\n * Alchemy's own `GitHub.Repository` defaults to retain \"because their contents are\n * irreplaceable\". A git repository is the most irreplaceable object on this estate. `retain`\n * means the engine skips `provider.delete` when a resource is orphaned unless the caller opted\n * into `.pipe(RemovalPolicy.destroy())`. The `delete` handler is FULLY IMPLEMENTED anyway — a\n * stub that silently does nothing lies to whoever reads the plan.\n */\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/unstable/http/HttpClient';\nimport { ForgejoError, forgejo } from './client.ts';\n\n/**\n * ★ WHAT EVERY OPERATION HERE NEEDS FROM THE RUNTIME — and it is NOT what the first draft said.\n *\n * These resources declared `ChildProcessSpawner`, copied from house/proxmox, where it is there\n * because a PVE credential is minted by shelling out to `bao`. Nothing in this package shells\n * out: the token comes from `FORGEJO_TOKEN` in the environment. Declaring a service you never\n * use is not harmless — it makes a stack provide a layer for nothing, and it hides the one\n * service you DO need.\n *\n * What is actually required is `HttpClient`, for the reason in client.ts: Alchemy's own\n * providers call through Effect's client rather than the global `fetch`.\n */\nexport type ForgejoRequirements = HttpClient.HttpClient;\n\nexport type ForgejoSpec<Props extends object, Attributes> = {\n /** `repos/HomeFlare/homeflare`, `orgs/homeflare/labels/40` — one object. */\n readonly path: (props: Props) => string;\n /** `orgs/HomeFlare/repos`, `orgs/homeflare/labels` — where a new one is POSTed. */\n readonly collection: (props: Props) => string;\n readonly attributes: (live: Record<string, unknown>, props: Props) => Attributes | undefined;\n /** JSON body on create. ⚠️ Gitea takes JSON, not form encoding. */\n readonly createForm: (props: Props) => Record<string, unknown>;\n readonly updateForm?: (props: Props) => Record<string, unknown>;\n readonly matches: (attributes: Attributes, props: Props) => boolean;\n /**\n * When the stable key is not enough for the wire path — org labels are addressed by numeric id.\n * `locate` lists or searches; `wirePath` builds update/delete paths from the live row.\n */\n readonly locate?: (\n props: Props,\n ) => Effect.Effect<Record<string, unknown> | undefined, ForgejoError, ForgejoRequirements>;\n readonly wirePath?: (props: Props, live: Record<string, unknown>) => string;\n /**\n * When create is PUT upsert rather than POST — org actions secrets have no POST and no GET-by-name.\n * `before === undefined` means absent; the callback decides whether to write. Existence-only families\n * noop when `before` is set because the API never returns the value for comparison.\n */\n readonly upsert?: (\n props: Props,\n before: Record<string, unknown> | undefined,\n ) => Effect.Effect<void, ForgejoError, ForgejoRequirements>;\n};\n\n/** ⚠️ Only 404 folds to \"absent\". A 403 is a real failure — folding it would plan a create over an existing object. */\nconst absentOn404 = <A>(io: Effect.Effect<A, ForgejoError, ForgejoRequirements>) =>\n io.pipe(\n Effect.catchIf(\n (cause): cause is ForgejoError => cause instanceof ForgejoError && cause.status === 404,\n () => Effect.succeed(undefined as A),\n ),\n );\n\nexport const forgejoOperations = <Props extends object, Attributes>(\n spec: ForgejoSpec<Props, Attributes>,\n) => {\n const fetchLive = (props: Props) =>\n absentOn404(\n spec.locate !== undefined\n ? spec.locate(props)\n : forgejo<Record<string, unknown>>('GET', spec.path(props)),\n );\n\n const objectPath = (props: Props, live: Record<string, unknown>) =>\n spec.wirePath === undefined ? spec.path(props) : spec.wirePath(props, live);\n\n const read = (props: Props) =>\n fetchLive(props).pipe(\n Effect.map((data) => (data === undefined ? undefined : spec.attributes(data, props))),\n );\n\n return {\n read,\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* read(news);\n if (live === undefined) return { action: 'update' } as const;\n if (spec.matches(live, news)) return { action: 'noop' } as const;\n return spec.updateForm === undefined\n ? ({ action: 'replace' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props) =>\n Effect.gen(function* () {\n const before = yield* fetchLive(news);\n if (spec.upsert !== undefined) {\n yield* spec.upsert(news, before);\n } else if (before === undefined) {\n yield* forgejo('POST', spec.collection(news), spec.createForm(news));\n } else if (spec.updateForm !== undefined) {\n const mapped = spec.attributes(before, news);\n if (mapped === undefined || spec.matches(mapped, news)) {\n /* adoption or already converged — no PATCH */\n } else {\n const form = spec.updateForm(news);\n if (Object.keys(form).length > 0) {\n yield* forgejo('PATCH', objectPath(news, before), form);\n }\n }\n }\n const after = yield* read(news);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `${spec.path(news)}: the write returned no error but the object is still absent. ` +\n 'Gitea returns the object directly — read back rather than trusting the status code.',\n ),\n );\n }\n return after;\n }),\n\n destroy: (olds: Props) =>\n Effect.gen(function* () {\n const live = yield* fetchLive(olds);\n if (live === undefined) return;\n yield* forgejo('DELETE', objectPath(olds, live));\n }),\n };\n};\n\n/**\n * The five provider handlers for a spec'd Forgejo object, wired once.\n *\n * ⛔ IT STOPS AT THE HANDLERS AND DOES NOT RETURN THE LAYER — same reasoning as pveHandlers in\n * house/proxmox: wrapping `Provider.effect` here needs casts against Alchemy's `Props<R>`.\n *\n * ⚠️ `list` answers empty — Forgejo index endpoints return the whole org; adoption stays explicit.\n */\nexport const forgejoHandlers = <Props extends object, Attributes extends object>(\n spec: ForgejoSpec<Props, Attributes>,\n) => {\n const ops = forgejoOperations(spec);\n return {\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: Props }) => ops.read(olds),\n diff: ({ news, output }: { news: Input<Props>; output: Attributes | undefined }) =>\n ops.diff(news, output),\n reconcile: ({ news }: { news: Props }) => ops.reconcile(news),\n delete: ({ olds }: { olds: Props }) => ops.destroy(olds),\n };\n};\n",
8
+ "/**\n * One authenticated call to the Forgejo (Gitea) API at `/api/v1`.\n *\n * ★ PLAIN `fetch`, NO TLS ESCAPE HATCH — your Forgejo host serves a real certificate on :443,\n * and the local daemon on :3000 is loopback. There is no `rejectUnauthorized` switch to leave\n * on by accident elsewhere.\n *\n * ⚠️ GITEA DOES NOT WRAP ANSWERS IN `{\"data\": ...}` THE WAY PVE DOES. MEASURED 2026-09-13 on\n * the live daemon: `GET /api/v1/version` returns `{\"version\":\"16.0.3\"}`; `GET /api/v1/user`\n * returns the user object; `GET /api/v1/orgs/HomeFlare/repos` returns a JSON array. A caller\n * that unwraps a non-existent envelope sees `undefined` and calls a healthy object \"absent\".\n *\n * ⛔ THE TOKEN IS NEVER A PROP. Read `FORGEJO_TOKEN` from the environment at call time only.\n * Alchemy persists attributes unencrypted and this estate's state store is Postgres dumped\n * nightly — nothing stored may be a credential.\n */\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\n\nconst PATH_PREFIX = '/api/v1';\n\n/**\n * The Forgejo instance, from `FORGEJO_URL`. Trailing slash stripped.\n *\n * ⛔ NO DEFAULT HOSTNAME. This used to fall back to the author's own instance, which is\n * fine inside one estate and wrong in a published package: a consumer who forgets the\n * variable would silently point at someone else's server rather than being told.\n * ⚠️ Read at CALL time, never at module scope — a value captured at import is pinned to\n * whatever the first isolate saw.\n */\nexport const apiBase = (): string => {\n const raw = process.env['FORGEJO_URL'];\n if (raw === undefined || raw.trim() === '') {\n throw new Error(\n 'FORGEJO_URL is not set. Point it at your Forgejo instance, e.g. https://git.example.com',\n );\n }\n return raw.trim().replace(/\\/$/, '');\n};\n\nconst token = (): Effect.Effect<string, ForgejoError> =>\n Effect.gen(function* () {\n const raw = process.env['FORGEJO_TOKEN'];\n if (raw === undefined || raw.trim() === '') {\n return yield* Effect.fail(\n new ForgejoError(\n 0,\n 'GET',\n '',\n 'FORGEJO_TOKEN is unset. Mint a scoped token and export it — never store it in Alchemy props.',\n ),\n );\n }\n return raw.trim();\n });\n\nexport class ForgejoError extends Error {\n constructor(\n readonly status: number,\n readonly method: string,\n readonly path: string,\n detail: string,\n ) {\n super(`Forgejo ${method} ${path} -> ${String(status)}: ${detail}`);\n this.name = 'ForgejoError';\n }\n}\n\n/**\n * ⚠️ `Authorization: token <value>`, NOT `Bearer`. Gitea docs and MEASURED behaviour on this\n * estate: Bearer is treated as OAuth2 and rejected; `token` is the documented API-key form.\n */\nconst authorization = (value: string) => ({ Authorization: `token ${value}` });\n\n/** One JSON call. Returns the parsed body, or `undefined` when the response is empty (DELETE). */\nexport const forgejo = <T>(\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: string,\n body?: Record<string, unknown>,\n) =>\n Effect.gen(function* () {\n const credential = yield* token();\n const url = `${apiBase()}${PATH_PREFIX}/${path.replace(/^\\//, '')}`;\n /**\n * ★ THROUGH `HttpClient`, NOT THE GLOBAL `fetch` — the idiom Alchemy's own providers follow\n * (src/Hetzner/Providers.ts builds on `FetchHttpClient.layer`; there is no bare fetch in its\n * Docker, Kubernetes or GitHub providers). It buys a typed error channel, interruption when\n * a plan is cancelled, the runtime's tracing, and a client a test can replace without\n * monkey-patching a global. house/proxmox/src/resource.ts carries the same note.\n */\n const client = yield* HttpClient.HttpClient;\n /**\n * ⛔ THE CONTENT TYPE GOES ON `bodyText`, NOT IN THE HEADER MAP, AND PUTTING IT IN BOTH IS HOW\n * THIS PACKAGE SHIPPED UNABLE TO WRITE AT ALL.\n *\n * 🔴 THE BUG, MEASURED THE FIRST TIME A WRITE WAS EVER ATTEMPTED. The header map DID carry\n * `Content-Type: application/json` — and `bodyText(s)` with no second argument sets\n * `text/plain` and OVERWRITES it, because it is piped AFTER `setHeaders`. Forgejo answered\n * 422 {\"message\":\"[]: Unsupported Content-Type\"}\n * on `PATCH orgs/homeflare/labels/1`. Every create and every update in every family here was\n * broken, identically, from the day the client was written.\n *\n * ⛔ NOTHING CAUGHT IT BECAUSE NOTHING HAD EVER WRITTEN. The only credential on the shelf was\n * `mcp-read`, which answers 403 before a request body is ever parsed — so read, adopt, diff\n * and plan all worked perfectly and the write path had literally never executed. A provider\n * that has never performed its own writes is not a tested provider, whatever the plan says.\n *\n * ⚠️ `bodyJsonUnsafe` WOULD ALSO WORK. `bodyText` with an explicit type is used instead\n * because it makes the content type visible at the call site, which is the thing that was\n * invisible before.\n */\n const request = HttpClientRequest.make(method)(url).pipe(\n HttpClientRequest.setHeaders({ ...authorization(credential), Accept: 'application/json' }),\n body === undefined\n ? (self) => self\n : HttpClientRequest.bodyText(JSON.stringify(body), 'application/json'),\n );\n const response = yield* client\n .execute(request)\n .pipe(Effect.mapError((cause) => new ForgejoError(0, method, path, String(cause))));\n // ⛔ Status first, body once — a Gitea error body is JSON but a proxy's 502 is not.\n if (response.status < 200 || response.status >= 300) {\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ''));\n return yield* Effect.fail(\n new ForgejoError(response.status, method, path, detail.slice(0, 300)),\n );\n }\n // ⚠️ 204 AND AN EMPTY BODY ARE REAL: a label DELETE answers no content, and `json` on an empty\n // body fails. Read text once and parse only when there is something to parse.\n const text = yield* response.text.pipe(\n Effect.mapError((cause) => new ForgejoError(0, method, path, String(cause))),\n );\n // ⚠️ PUT/DELETE on actions secrets and hooks often answer 201/204 with no body — same trap as DELETE.\n return (text === '' ? undefined : JSON.parse(text)) as T;\n });\n",
9
+ "/**\n * Wire coercions shared across Forgejo resources.\n *\n * ★ GITEA/ FORGEJO JSON USES snake_case ON THE WIRE and camelCase in props — each resource\n * translates at the form boundary; these helpers only normalise values for comparison.\n */\n\nexport const text = (value: unknown, fallback = '') =>\n typeof value === 'string' ? value : fallback;\n\n/** Forgejo returns label colours without `#`; declarations may include it. */\nexport const color = (value: unknown) => text(value).replace(/^#/, '').toLowerCase();\n\nexport const bool = (value: unknown, fallback = false) =>\n value === undefined || value === null ? fallback : value === true || value === 1 || value === '1';\n\nexport const int = (value: unknown, fallback = 0) => {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.trunc(value);\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number.parseInt(value, 10);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n};\n\n/** Wire arrays compared in sorted order — Gitea reshuffles nothing but callers should not depend on order. */\nexport const stringArray = (value: unknown) =>\n Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === 'string').toSorted()\n : [];\n\nexport const stringRecord = (value: unknown): Record<string, string> => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return {};\n const out: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry === 'string') out[key] = entry;\n }\n return out;\n};\n\nexport const recordEqual = (left: Record<string, string>, right: Record<string, string>) => {\n const leftKeys = Object.keys(left).toSorted();\n const rightKeys = Object.keys(right).toSorted();\n if (leftKeys.length !== rightKeys.length) return false;\n return leftKeys.every((key, index) => rightKeys[index] === key && left[key] === right[key]);\n};\n\n/** Hook `config` without `secret` — the value must never land in Alchemy attributes. */\nexport const hookConfigPublic = (value: unknown) => {\n const config = stringRecord(value);\n const { secret: _secret, ...rest } = config;\n return rest;\n};\n\n/** Env key for a write-only org actions secret — value read at PUT time only, never stored. */\nexport const orgSecretEnvKey = (org: string, name: string) =>\n `FORGEJO_ORG_SECRET_${org}_${name}`.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();\n\n/** Env key for a write-only repo webhook HMAC secret — omitted from attributes and props. */\nexport const hookSecretEnvKey = (owner: string, repo: string, name: string) =>\n `FORGEJO_HOOK_SECRET_${owner}_${repo}_${name}`.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();\n",
10
+ "/**\n * `Forgejo.OrgSecret` — org-level Actions secret metadata at `/orgs/{org}/actions/secrets/{name}`.\n *\n * ★ CHOICE: AUTHORED WITH WRITE-ONLY VALUE VIA ENV, NOT REFUSED. The OpenAPI in\n * `house/mcp-servers/docs/api/upstream/forgejo.json` shows `Secret` returns only `name` and\n * `created_at` — never the `data` field — so attributes stay metadata-only. Create/update is PUT\n * with body `{ data }`, which does not fit POST/PATCH; this family uses the `upsert` seam on\n * `ForgejoSpec` instead of hand-rolling provider handlers.\n *\n * ⛔ THE SECRET VALUE IS NEVER A PROP OR AN ATTRIBUTE. It is read from `FORGEJO_ORG_SECRET_{org}_{name}`\n * (see orgSecretEnvKey) at PUT time when the name is absent from the list. Once present, reconcile\n * noops — the API cannot confirm the value matches, and re-PUT on every deploy would rotate\n * unintentionally. Rotate by destroy-then-create or a manual PUT outside Alchemy.\n *\n * ⚠️ PUT ANSWERS 201/204 WITH NO BODY — client.ts treats empty responses as `undefined`; read-back\n * uses the list endpoint, not the PUT response.\n *\n * ⛔ TOKEN NEEDS `write:organization` TO PUT — list needs `read:organization`.\n *\n * ⛔ THE FILE NAME IS PLURAL ON PURPOSE AND MUST STAY THAT WAY. `.gitignore:198` carries\n * `*-secret.*` — the rule that stops `client-secret.json` and friends being committed — and it\n * matches a SOURCE file called `org-secret.ts` just as happily. This family was first authored\n * under that name: it existed on disk, typechecked, passed oxlint, was imported by alchemy.run.ts\n * and appeared in NO `git status`. It would have been committed as a broken import for everyone\n * else, with the local tree green the whole time. `scripts/husky/ignored-imports.ts` is the gate\n * that now catches the class; this comment is for whoever is tempted to rename it back.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { ForgejoError, forgejo } from './client.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { orgSecretEnvKey, text } from './values.ts';\n\nexport interface OrgSecretProps {\n org: string;\n /** Secret name — path key; value lives in env, not here. */\n name: string;\n}\n\nexport interface OrgSecretAttributes {\n org: string;\n name: string;\n createdAt: string;\n}\n\nexport interface ForgejoOrgSecret extends Resource<\n 'Forgejo.OrgSecret',\n OrgSecretProps,\n OrgSecretAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoOrgSecret = Resource<ForgejoOrgSecret>('Forgejo.OrgSecret');\n\nconst secretData = (org: string, name: string) =>\n Effect.gen(function* () {\n const key = orgSecretEnvKey(org, name);\n const raw = process.env[key];\n if (raw === undefined || raw.trim() === '') {\n return yield* Effect.fail(\n new ForgejoError(\n 0,\n 'PUT',\n `orgs/${org}/actions/secrets/${name}`,\n `${key} is unset. Export the secret value there — never as an Alchemy prop.`,\n ),\n );\n }\n return raw.trim();\n });\n\nconst handlers = forgejoHandlers<OrgSecretProps, OrgSecretAttributes>({\n attributes: (live, props) => ({\n createdAt: text(live['created_at']),\n name: props.name,\n org: props.org,\n }),\n collection: (props) => `orgs/${props.org}/actions/secrets/${props.name}`,\n createForm: () => ({}),\n locate: (props) =>\n forgejo<Record<string, unknown>[]>('GET', `orgs/${props.org}/actions/secrets?limit=200`).pipe(\n Effect.map((rows) =>\n Array.isArray(rows) ? rows.find((row) => row['name'] === props.name) : undefined,\n ),\n ),\n matches: () => true,\n path: (props) => `orgs/${props.org}/actions/secrets/${props.name}`,\n upsert: (props, before) =>\n Effect.gen(function* () {\n if (before !== undefined) return;\n const data = yield* secretData(props.org, props.name);\n yield* forgejo('PUT', `orgs/${props.org}/actions/secrets/${props.name}`, { data });\n }),\n});\n\nexport const ForgejoOrgSecretProvider = () =>\n Provider.effect(ForgejoOrgSecret, Effect.succeed(ForgejoOrgSecret.Provider.of(handlers)));\n",
11
+ "/**\n * `Forgejo.OrgLabel` — one org-level issue label shared across every repository in the org.\n *\n * ★ RETIRES `house/nix/homeflare-config/hosts/macmini/forgejo/labels-reconcile.sh`, which\n * converged the whole `labels.yaml` file in one shot. That script CREATED and PATCHED labels\n * from the spec and REPORTED `EXTRA` labels present live but absent from the file — it never\n * deleted them, because removing a label strips it from every issue that carries it.\n *\n * ★ THIS RESOURCE DOES NOT REMOVE EXTRA LABELS EITHER. Alchemy manages only labels you declare;\n * labels on the org that nobody declared stay untouched — the same boundary as the script's\n * `EXTRA … NOT deleted` lines. Removing a declaration defaults to `retain`, so dropping a label\n * from the stack does not DELETE it unless the caller opts into `.pipe(RemovalPolicy.destroy())`.\n * That matches the script's never-delete posture while still allowing a deliberate destroy.\n *\n * ⚠️ THE WIRE PATH USES NUMERIC ID, NOT NAME — measured on live org labels: PATCH and DELETE are\n * `/orgs/{org}/labels/{id}`. `locate` lists by name; `wirePath` uses the id from that row.\n *\n * ⛔ TOKEN NEEDS `write:organization` FOR RECONCILE — the mcp-read token lacks it (POST 403\n * measured 2026-09-13). Plan/deploy needs a provision-scoped credential in `FORGEJO_TOKEN`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { forgejo } from './client.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { bool, color, text } from './values.ts';\n\nexport interface OrgLabelProps {\n /** Organization login — `homeflare` / `HomeFlare` must match the live org. */\n org: string;\n /** Label name — primary key for locate; immutable on update. */\n name: string;\n /** Six-digit hex, with or without `#`. */\n color: string;\n description?: string;\n exclusive?: boolean;\n}\n\nexport interface OrgLabelAttributes {\n org: string;\n name: string;\n labelId: number;\n color: string;\n description: string;\n exclusive: boolean;\n}\n\nexport interface ForgejoOrgLabel extends Resource<\n 'Forgejo.OrgLabel',\n OrgLabelProps,\n OrgLabelAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoOrgLabel = Resource<ForgejoOrgLabel>('Forgejo.OrgLabel', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = forgejoHandlers<OrgLabelProps, OrgLabelAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n color: color(live['color']),\n description: text(live['description']),\n exclusive: bool(live['exclusive']),\n labelId: id,\n name: props.name,\n org: props.org,\n };\n },\n collection: (props) => `orgs/${props.org}/labels`,\n createForm: (props) => ({\n color: color(props.color),\n description: props.description ?? '',\n exclusive: props.exclusive ?? false,\n name: props.name,\n }),\n locate: (props) =>\n forgejo<Record<string, unknown>[]>('GET', `orgs/${props.org}/labels?limit=200`).pipe(\n Effect.map((rows) =>\n Array.isArray(rows) ? rows.find((row) => row['name'] === props.name) : undefined,\n ),\n ),\n matches: (attributes, props) =>\n attributes.color === color(props.color) &&\n attributes.description === (props.description ?? '') &&\n attributes.exclusive === (props.exclusive ?? false),\n path: (props) => `orgs/${props.org}/labels/${props.name}`,\n updateForm: (props) => ({\n color: color(props.color),\n description: props.description ?? '',\n exclusive: props.exclusive ?? false,\n }),\n wirePath: (props, live) => `orgs/${props.org}/labels/${String(live['id'])}`,\n});\n\nexport const ForgejoOrgLabelProvider = () =>\n Provider.effect(ForgejoOrgLabel, Effect.succeed(ForgejoOrgLabel.Provider.of(handlers)));\n",
12
+ "/**\n * `Forgejo.OrgTeam` — one organization team under `/orgs/{org}/teams`.\n *\n * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json` (Forgejo 16.x OpenAPI): create is\n * `POST /orgs/{org}/teams`; read/update/delete use numeric id at `/teams/{id}` — not the name.\n *\n * ⚠️ PATCH AND DELETE USE `/teams/{id}` — same seam as org labels. `locate` lists by name;\n * `wirePath` carries the id from that row.\n *\n * ⛔ TOKEN NEEDS `write:organization` TO CREATE OR PATCH — read list/get needs `read:organization`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { forgejo } from './client.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { bool, recordEqual, stringArray, stringRecord, text } from './values.ts';\n\nexport type TeamPermission = 'read' | 'write' | 'admin';\n\nexport interface OrgTeamProps {\n org: string;\n /** Team name — primary locate key; sent on every PATCH body. */\n name: string;\n description?: string;\n permission?: TeamPermission;\n units?: string[];\n unitsMap?: Record<string, string>;\n includesAllRepositories?: boolean;\n canCreateOrgRepo?: boolean;\n}\n\nexport interface OrgTeamAttributes {\n org: string;\n name: string;\n teamId: number;\n description: string;\n permission: TeamPermission;\n units: string[];\n unitsMap: Record<string, string>;\n includesAllRepositories: boolean;\n canCreateOrgRepo: boolean;\n}\n\nexport interface ForgejoOrgTeam extends Resource<\n 'Forgejo.OrgTeam',\n OrgTeamProps,\n OrgTeamAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoOrgTeam = Resource<ForgejoOrgTeam>('Forgejo.OrgTeam');\n\nconst teamForm = (props: OrgTeamProps) => ({\n can_create_org_repo: props.canCreateOrgRepo ?? false,\n description: props.description ?? '',\n includes_all_repositories: props.includesAllRepositories ?? false,\n name: props.name,\n ...(props.permission === undefined ? {} : { permission: props.permission }),\n ...(props.units === undefined ? {} : { units: props.units }),\n ...(props.unitsMap === undefined ? {} : { units_map: props.unitsMap }),\n});\n\nconst handlers = forgejoHandlers<OrgTeamProps, OrgTeamAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n const permission = text(live['permission'], 'read');\n if (permission !== 'read' && permission !== 'write' && permission !== 'admin') return undefined;\n return {\n canCreateOrgRepo: bool(live['can_create_org_repo']),\n description: text(live['description']),\n includesAllRepositories: bool(live['includes_all_repositories']),\n name: props.name,\n org: props.org,\n permission,\n teamId: id,\n units: stringArray(live['units']),\n unitsMap: stringRecord(live['units_map']),\n };\n },\n collection: (props) => `orgs/${props.org}/teams`,\n createForm: teamForm,\n locate: (props) =>\n forgejo<Record<string, unknown>[]>('GET', `orgs/${props.org}/teams?limit=200`).pipe(\n Effect.map((rows) =>\n Array.isArray(rows) ? rows.find((row) => row['name'] === props.name) : undefined,\n ),\n ),\n matches: (attributes, props) =>\n attributes.description === (props.description ?? '') &&\n attributes.permission === (props.permission ?? 'read') &&\n attributes.includesAllRepositories === (props.includesAllRepositories ?? false) &&\n attributes.canCreateOrgRepo === (props.canCreateOrgRepo ?? false) &&\n (props.units === undefined ||\n stringArray(props.units).join('\\0') === attributes.units.join('\\0')) &&\n (props.unitsMap === undefined || recordEqual(attributes.unitsMap, props.unitsMap)),\n path: (props) => `orgs/${props.org}/teams/${props.name}`,\n updateForm: teamForm,\n wirePath: (_props, live) => `teams/${String(live['id'])}`,\n});\n\nexport const ForgejoOrgTeamProvider = () =>\n Provider.effect(ForgejoOrgTeam, Effect.succeed(ForgejoOrgTeam.Provider.of(handlers)));\n",
13
+ "/**\n * `Forgejo.RepoWebhook` — one repository webhook under `/repos/{owner}/{repo}/hooks`.\n *\n * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json`: create is POST collection; read/update/delete\n * use numeric hook id at `/repos/{owner}/{repo}/hooks/{id}`. `locate` lists and matches on `name`.\n *\n * ⛔ NO WEBHOOK `secret` IN PROPS OR ATTRIBUTES. Optional HMAC material is env-only at write time\n * (`FORGEJO_HOOK_SECRET_*` — see repo-webhook-form.ts). `hookConfigPublic` strips `secret` on read.\n *\n * ⛔ TOKEN NEEDS `write:repository` TO CREATE OR PATCH — list/get needs `read:repository`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { forgejo } from './client.ts';\nimport { createHookForm, hookUrl, updateHookForm } from './repo-webhook-form.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { bool, hookConfigPublic, stringArray, text } from './values.ts';\n\nexport type RepoWebhookType =\n | 'dingtalk'\n | 'discord'\n | 'feishu'\n | 'forgejo'\n | 'gitea'\n | 'gogs'\n | 'msteams'\n | 'packagist'\n | 'slack'\n | 'telegram'\n | 'wechatwork';\n\nexport interface RepoWebhookProps {\n owner: string;\n repo: string;\n /** Stable locate key — also sent as Hook.name on create. */\n name: string;\n type: RepoWebhookType;\n url: string;\n events: string[];\n contentType?: 'json' | 'form';\n active?: boolean;\n branchFilter?: string;\n authorizationHeader?: string;\n}\n\nexport interface RepoWebhookAttributes {\n owner: string;\n repo: string;\n name: string;\n hookId: number;\n type: RepoWebhookType;\n url: string;\n contentType: string;\n events: string[];\n active: boolean;\n branchFilter: string;\n authorizationHeader: string;\n}\n\nexport interface ForgejoRepoWebhook extends Resource<\n 'Forgejo.RepoWebhook',\n RepoWebhookProps,\n RepoWebhookAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoRepoWebhook = Resource<ForgejoRepoWebhook>('Forgejo.RepoWebhook');\n\nconst handlers = forgejoHandlers<RepoWebhookProps, RepoWebhookAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n const config = hookConfigPublic(live['config']);\n const type = text(live['type']);\n return {\n active: bool(live['active'], true),\n authorizationHeader: text(live['authorization_header']),\n branchFilter: text(live['branch_filter']),\n contentType: text(config['content_type'], text(live['content_type'], 'json')),\n events: stringArray(live['events']),\n hookId: id,\n name: props.name,\n owner: props.owner,\n repo: props.repo,\n type: type as RepoWebhookType,\n url: hookUrl(live),\n };\n },\n collection: (props) => `repos/${props.owner}/${props.repo}/hooks`,\n createForm: createHookForm,\n locate: (props) =>\n forgejo<Record<string, unknown>[]>(\n 'GET',\n `repos/${props.owner}/${props.repo}/hooks?limit=200`,\n ).pipe(\n Effect.map((rows) =>\n Array.isArray(rows)\n ? rows.find((row) => text(row['name']) === props.name || hookUrl(row) === props.url)\n : undefined,\n ),\n ),\n matches: (attributes, props) =>\n attributes.type === props.type &&\n attributes.url === props.url &&\n attributes.active === (props.active ?? true) &&\n attributes.contentType === (props.contentType ?? 'json') &&\n attributes.branchFilter === (props.branchFilter ?? '') &&\n attributes.authorizationHeader === (props.authorizationHeader ?? '') &&\n stringArray(props.events).join('\\0') === attributes.events.join('\\0'),\n path: (props) => `repos/${props.owner}/${props.repo}/hooks/${props.name}`,\n updateForm: updateHookForm,\n wirePath: (props, live) => `repos/${props.owner}/${props.repo}/hooks/${String(live['id'])}`,\n});\n\nexport const ForgejoRepoWebhookProvider = () =>\n Provider.effect(ForgejoRepoWebhook, Effect.succeed(ForgejoRepoWebhook.Provider.of(handlers)));\n",
14
+ "/**\n * JSON bodies for `Forgejo.RepoWebhook` — split to keep repo-webhook.ts under the line cap.\n *\n * ⛔ `config.secret` IS NEVER A PROP. When present it is read from `FORGEJO_HOOK_SECRET_*` at\n * create/update time only (see hookSecretEnvKey in values.ts). Alchemy attributes are Postgres\n * plaintext — a webhook HMAC secret must not be stored there.\n */\nimport type { RepoWebhookProps } from './repo-webhook.ts';\nimport { hookSecretEnvKey, stringRecord, text } from './values.ts';\n\nexport const hookUrl = (live: Record<string, unknown>) =>\n text(live['url']) || text(stringRecord(live['config'])['url']);\n\nexport const hookConfigForm = (props: RepoWebhookProps, includeSecret: boolean) => {\n const config: Record<string, string> = {\n content_type: props.contentType ?? 'json',\n url: props.url,\n };\n if (includeSecret) {\n const key = hookSecretEnvKey(props.owner, props.repo, props.name);\n const secret = process.env[key]?.trim();\n if (secret !== undefined && secret !== '') config['secret'] = secret;\n }\n return config;\n};\n\nexport const createHookForm = (props: RepoWebhookProps) => ({\n active: props.active ?? true,\n ...(props.authorizationHeader === undefined\n ? {}\n : { authorization_header: props.authorizationHeader }),\n ...(props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter }),\n config: hookConfigForm(props, true),\n events: props.events,\n name: props.name,\n type: props.type,\n});\n\nexport const updateHookForm = (props: RepoWebhookProps) => ({\n active: props.active ?? true,\n ...(props.authorizationHeader === undefined\n ? {}\n : { authorization_header: props.authorizationHeader }),\n ...(props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter }),\n config: hookConfigForm(props, false),\n events: props.events,\n});\n",
15
+ "/**\n * `Forgejo.Repository` — an organization-owned git repository.\n *\n * ★ MEASURED 2026-09-13 against the live daemon (`GET /api/v1/repos/HomeFlare/homeflare-config`):\n * responses are flat JSON with `private`, `has_issues`, `default_branch`, etc. Create is\n * `POST /orgs/{org}/repos`; read/update/delete use `/repos/{owner}/{repo}`.\n *\n * ⛔ `defaultRemovalPolicy: 'retain'` — history, issues and packages in a repo are irreplaceable.\n * Opt into deletion with `.pipe(RemovalPolicy.destroy())`; `delete` below is fully implemented.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\nimport { bool, text } from './values.ts';\n\nexport interface RepositoryProps {\n /** Organization login — the owner segment in `HomeFlare/homeflare-config`. */\n org: string;\n /** Repository name. Changing it is a replace, not an update. */\n name: string;\n description?: string;\n private?: boolean;\n hasIssues?: boolean;\n hasWiki?: boolean;\n hasProjects?: boolean;\n /** Only applied when the repository already has at least one branch. */\n defaultBranch?: string;\n /** Create an initial commit with a README when the repository is new. */\n autoInit?: boolean;\n}\n\nexport interface RepositoryAttributes {\n org: string;\n name: string;\n repoId: number;\n description: string;\n private: boolean;\n hasIssues: boolean;\n hasWiki: boolean;\n hasProjects: boolean;\n defaultBranch: string;\n empty: boolean;\n}\n\nexport interface ForgejoRepository extends Resource<\n 'Forgejo.Repository',\n RepositoryProps,\n RepositoryAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoRepository = Resource<ForgejoRepository>('Forgejo.Repository', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = forgejoHandlers<RepositoryProps, RepositoryAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n defaultBranch: text(live['default_branch'], 'main'),\n description: text(live['description']),\n empty: bool(live['empty']),\n hasIssues: bool(live['has_issues'], true),\n hasProjects: bool(live['has_projects'], true),\n hasWiki: bool(live['has_wiki'], true),\n name: props.name,\n org: props.org,\n private: bool(live['private'], true),\n repoId: id,\n };\n },\n collection: (props) => `orgs/${props.org}/repos`,\n createForm: (props) => ({\n auto_init: props.autoInit ?? false,\n description: props.description ?? '',\n has_issues: props.hasIssues ?? true,\n has_projects: props.hasProjects ?? true,\n has_wiki: props.hasWiki ?? true,\n name: props.name,\n private: props.private ?? true,\n }),\n /**\n * ⛔ AN UNDECLARED FIELD IS NEITHER COMPARED NOR SENT — the rule every other family in this\n * estate follows (storage.ts, backup-job.ts, and all four Pbs.* families say it in those\n * words), and this file was the exception.\n *\n * 🔴 WHAT THE EXCEPTION DID. Five fields were compared against a DEFAULT rather than skipped:\n * `description ?? ''`, and `?? true` for private, hasIssues, hasWiki and hasProjects. So a\n * declaration that simply did not mention `hasWiki` asserted \"the wiki is ON\", and one that\n * did not mention `description` asserted \"the description is EMPTY\" — and `updateForm` sent\n * both. Declaring a repo you only wanted to rename would have wiped its description and\n * switched features on, reported as a plain `update`. Only `defaultBranch` was written\n * correctly, on the line below, which is what the other five now copy.\n *\n * ⚠️ THE PRICE, STATED: dropping a line no longer resets a field. Clear one out of band in the\n * Forgejo UI or with a direct PATCH. It costs no forever-diff, because an undeclared field is\n * not compared either.\n *\n * ⚠️ `private` IS THE ONE WORTH DECLARING ANYWAY ON EVERY REPO. Leaving it undeclared now means\n * Alchemy will not notice a repository being made public. That is the correct trade for a\n * generic family — undeclared is unmanaged, not assumed — but it is a real gap, so say it\n * explicitly in each declaration rather than relying on a default that no longer exists.\n */\n matches: (attributes, props) =>\n (props.description === undefined || attributes.description === props.description) &&\n (props.private === undefined || attributes.private === props.private) &&\n (props.hasIssues === undefined || attributes.hasIssues === props.hasIssues) &&\n (props.hasWiki === undefined || attributes.hasWiki === props.hasWiki) &&\n (props.hasProjects === undefined || attributes.hasProjects === props.hasProjects) &&\n (props.defaultBranch === undefined || attributes.defaultBranch === props.defaultBranch),\n path: (props) => `repos/${props.org}/${props.name}`,\n updateForm: (props) => ({\n ...(props.defaultBranch === undefined ? {} : { default_branch: props.defaultBranch }),\n ...(props.description === undefined ? {} : { description: props.description }),\n ...(props.hasIssues === undefined ? {} : { has_issues: props.hasIssues }),\n ...(props.hasProjects === undefined ? {} : { has_projects: props.hasProjects }),\n ...(props.hasWiki === undefined ? {} : { has_wiki: props.hasWiki }),\n ...(props.private === undefined ? {} : { private: props.private }),\n }),\n});\n\nexport const ForgejoRepositoryProvider = () =>\n Provider.effect(ForgejoRepository, Effect.succeed(ForgejoRepository.Provider.of(handlers)));\n",
16
+ "/**\n * `Forgejo.TeamMember` — one user in one organization team.\n *\n * ★ WHY IT EXISTS: forgejo-provision, the service account the Forgejo engine mints as, belongs to the\n * HomeFlare Owners team — Tim, 2026-09-14. Membership is what its tokens can reach; the role's\n * scopes only cap it (house/openbao/src/forgejo-bootstrap.ts).\n *\n * ★ READ OFF THE LIVE 16.0.3 SWAGGER (127.0.0.1:3000/swagger.v1.json, 2026-09-14): GET, PUT and\n * DELETE `/teams/{id}/members/{username}` answer 200, 204 and 204, and 404 when absent. The team\n * is addressed by numeric id, so it is found by name first — the same seam as org-team.ts.\n *\n * ⚠️ EXISTENCE IS THE WHOLE OBJECT. Nothing about a membership can be updated, so `matches` is\n * always true; a different team or user is a different resource id in alchemy.run.ts.\n *\n * ⚠️ THE TEAM ITSELF IS ONLY LOCATED HERE, NEVER MANAGED. A missing team fails the plan by name\n * instead of folding into \"absent\", because a PUT can add a member but cannot create a team.\n *\n * ⛔ TOKEN NEEDS `write:organization` TO ADD OR REMOVE, and an identity allowed to manage the team.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { ForgejoError, forgejo } from './client.ts';\nimport { type ForgejoRequirements, forgejoHandlers } from './resource.ts';\n\nexport interface TeamMemberProps {\n org: string;\n /** Team name as Forgejo shows it, e.g. `Owners`. */\n team: string;\n username: string;\n}\n\nexport interface TeamMemberAttributes {\n org: string;\n team: string;\n teamId: number;\n username: string;\n}\n\nexport interface ForgejoTeamMember extends Resource<\n 'Forgejo.TeamMember',\n TeamMemberProps,\n TeamMemberAttributes,\n never,\n ForgejoRequirements\n> {}\n\nexport const ForgejoTeamMember = Resource<ForgejoTeamMember>('Forgejo.TeamMember');\n\n/** Where `locate` leaves the team id on the live row, for `attributes` and `wirePath`. */\nconst TEAM_ID = '__team_id';\n\nconst teamIdOf = (props: TeamMemberProps) =>\n Effect.gen(function* () {\n const path = `orgs/${props.org}/teams?limit=200`;\n const rows = yield* forgejo<Record<string, unknown>[]>('GET', path);\n const id = (Array.isArray(rows) ? rows : []).find((row) => row['name'] === props.team)?.['id'];\n if (typeof id !== 'number') {\n return yield* Effect.fail(\n new ForgejoError(0, 'GET', path, `no team named ${props.team} in org ${props.org}`),\n );\n }\n return id;\n });\n\nconst memberPath = (id: number, props: TeamMemberProps) =>\n `teams/${String(id)}/members/${props.username}`;\n\nconst handlers = forgejoHandlers<TeamMemberProps, TeamMemberAttributes>({\n attributes: (live, props) => {\n const id = live[TEAM_ID];\n const login = live['login'];\n if (typeof id !== 'number' || typeof login !== 'string') return undefined;\n if (login.toLowerCase() !== props.username.toLowerCase()) return undefined;\n return { org: props.org, team: props.team, teamId: id, username: props.username };\n },\n collection: (props) => `orgs/${props.org}/teams`,\n createForm: () => ({}),\n locate: (props) =>\n Effect.gen(function* () {\n const id = yield* teamIdOf(props);\n const user = yield* forgejo<Record<string, unknown>>('GET', memberPath(id, props));\n return user === undefined ? undefined : { ...user, [TEAM_ID]: id };\n }),\n matches: () => true,\n path: (props) => `orgs/${props.org}/teams/${props.team}/members/${props.username}`,\n upsert: (props, before) =>\n before !== undefined\n ? Effect.void\n : Effect.flatMap(teamIdOf(props), (id) =>\n Effect.asVoid(forgejo('PUT', memberPath(id, props))),\n ),\n wirePath: (props, live) => memberPath(Number(live[TEAM_ID]), props),\n});\n\nexport const ForgejoTeamMemberProvider = () =>\n Provider.effect(ForgejoTeamMember, Effect.succeed(ForgejoTeamMember.Provider.of(handlers)));\n"
17
+ ],
18
+ "mappings": "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",
19
+ "debugId": "09F62B41760BE1CD64756E2164756E21",
20
+ "names": []
21
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `Forgejo.OrgSecret` — org-level Actions secret metadata at `/orgs/{org}/actions/secrets/{name}`.
3
+ *
4
+ * ★ CHOICE: AUTHORED WITH WRITE-ONLY VALUE VIA ENV, NOT REFUSED. The OpenAPI in
5
+ * `house/mcp-servers/docs/api/upstream/forgejo.json` shows `Secret` returns only `name` and
6
+ * `created_at` — never the `data` field — so attributes stay metadata-only. Create/update is PUT
7
+ * with body `{ data }`, which does not fit POST/PATCH; this family uses the `upsert` seam on
8
+ * `ForgejoSpec` instead of hand-rolling provider handlers.
9
+ *
10
+ * ⛔ THE SECRET VALUE IS NEVER A PROP OR AN ATTRIBUTE. It is read from `FORGEJO_ORG_SECRET_{org}_{name}`
11
+ * (see orgSecretEnvKey) at PUT time when the name is absent from the list. Once present, reconcile
12
+ * noops — the API cannot confirm the value matches, and re-PUT on every deploy would rotate
13
+ * unintentionally. Rotate by destroy-then-create or a manual PUT outside Alchemy.
14
+ *
15
+ * ⚠️ PUT ANSWERS 201/204 WITH NO BODY — client.ts treats empty responses as `undefined`; read-back
16
+ * uses the list endpoint, not the PUT response.
17
+ *
18
+ * ⛔ TOKEN NEEDS `write:organization` TO PUT — list needs `read:organization`.
19
+ *
20
+ * ⛔ THE FILE NAME IS PLURAL ON PURPOSE AND MUST STAY THAT WAY. `.gitignore:198` carries
21
+ * `*-secret.*` — the rule that stops `client-secret.json` and friends being committed — and it
22
+ * matches a SOURCE file called `org-secret.ts` just as happily. This family was first authored
23
+ * under that name: it existed on disk, typechecked, passed oxlint, was imported by alchemy.run.ts
24
+ * and appeared in NO `git status`. It would have been committed as a broken import for everyone
25
+ * else, with the local tree green the whole time. `scripts/husky/ignored-imports.ts` is the gate
26
+ * that now catches the class; this comment is for whoever is tempted to rename it back.
27
+ */
28
+ import { Resource } from 'alchemy';
29
+ import * as Provider from 'alchemy/Provider';
30
+ import { type ForgejoRequirements } from './resource.ts';
31
+ export interface OrgSecretProps {
32
+ org: string;
33
+ /** Secret name — path key; value lives in env, not here. */
34
+ name: string;
35
+ }
36
+ export interface OrgSecretAttributes {
37
+ org: string;
38
+ name: string;
39
+ createdAt: string;
40
+ }
41
+ export interface ForgejoOrgSecret extends Resource<'Forgejo.OrgSecret', OrgSecretProps, OrgSecretAttributes, never, ForgejoRequirements> {
42
+ }
43
+ export declare const ForgejoOrgSecret: import("alchemy").ResourceClass<ForgejoOrgSecret>;
44
+ export declare const ForgejoOrgSecretProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoOrgSecret>, never, import("effect/unstable/http/HttpClient").HttpClient>;
45
+ //# sourceMappingURL=org-actions-secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-actions-secrets.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-actions-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAAkD,CAAC;AA2ChF,eAAO,MAAM,wBAAwB,sIACsD,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `Forgejo.OrgLabel` — one org-level issue label shared across every repository in the org.
3
+ *
4
+ * ★ RETIRES `house/nix/homeflare-config/hosts/macmini/forgejo/labels-reconcile.sh`, which
5
+ * converged the whole `labels.yaml` file in one shot. That script CREATED and PATCHED labels
6
+ * from the spec and REPORTED `EXTRA` labels present live but absent from the file — it never
7
+ * deleted them, because removing a label strips it from every issue that carries it.
8
+ *
9
+ * ★ THIS RESOURCE DOES NOT REMOVE EXTRA LABELS EITHER. Alchemy manages only labels you declare;
10
+ * labels on the org that nobody declared stay untouched — the same boundary as the script's
11
+ * `EXTRA … NOT deleted` lines. Removing a declaration defaults to `retain`, so dropping a label
12
+ * from the stack does not DELETE it unless the caller opts into `.pipe(RemovalPolicy.destroy())`.
13
+ * That matches the script's never-delete posture while still allowing a deliberate destroy.
14
+ *
15
+ * ⚠️ THE WIRE PATH USES NUMERIC ID, NOT NAME — measured on live org labels: PATCH and DELETE are
16
+ * `/orgs/{org}/labels/{id}`. `locate` lists by name; `wirePath` uses the id from that row.
17
+ *
18
+ * ⛔ TOKEN NEEDS `write:organization` FOR RECONCILE — the mcp-read token lacks it (POST 403
19
+ * measured 2026-09-13). Plan/deploy needs a provision-scoped credential in `FORGEJO_TOKEN`.
20
+ */
21
+ import { Resource } from 'alchemy';
22
+ import * as Provider from 'alchemy/Provider';
23
+ import { type ForgejoRequirements } from './resource.ts';
24
+ export interface OrgLabelProps {
25
+ /** Organization login — `homeflare` / `HomeFlare` must match the live org. */
26
+ org: string;
27
+ /** Label name — primary key for locate; immutable on update. */
28
+ name: string;
29
+ /** Six-digit hex, with or without `#`. */
30
+ color: string;
31
+ description?: string;
32
+ exclusive?: boolean;
33
+ }
34
+ export interface OrgLabelAttributes {
35
+ org: string;
36
+ name: string;
37
+ labelId: number;
38
+ color: string;
39
+ description: string;
40
+ exclusive: boolean;
41
+ }
42
+ export interface ForgejoOrgLabel extends Resource<'Forgejo.OrgLabel', OrgLabelProps, OrgLabelAttributes, never, ForgejoRequirements> {
43
+ }
44
+ export declare const ForgejoOrgLabel: import("alchemy").ResourceClass<ForgejoOrgLabel>;
45
+ export declare const ForgejoOrgLabelProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoOrgLabel>, never, import("effect/unstable/http/HttpClient").HttpClient>;
46
+ //# sourceMappingURL=org-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-label.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,eAAe,kDAE1B,CAAC;AAyCH,eAAO,MAAM,uBAAuB,qIACqD,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `Forgejo.OrgTeam` — one organization team under `/orgs/{org}/teams`.
3
+ *
4
+ * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json` (Forgejo 16.x OpenAPI): create is
5
+ * `POST /orgs/{org}/teams`; read/update/delete use numeric id at `/teams/{id}` — not the name.
6
+ *
7
+ * ⚠️ PATCH AND DELETE USE `/teams/{id}` — same seam as org labels. `locate` lists by name;
8
+ * `wirePath` carries the id from that row.
9
+ *
10
+ * ⛔ TOKEN NEEDS `write:organization` TO CREATE OR PATCH — read list/get needs `read:organization`.
11
+ */
12
+ import { Resource } from 'alchemy';
13
+ import * as Provider from 'alchemy/Provider';
14
+ import { type ForgejoRequirements } from './resource.ts';
15
+ export type TeamPermission = 'read' | 'write' | 'admin';
16
+ export interface OrgTeamProps {
17
+ org: string;
18
+ /** Team name — primary locate key; sent on every PATCH body. */
19
+ name: string;
20
+ description?: string;
21
+ permission?: TeamPermission;
22
+ units?: string[];
23
+ unitsMap?: Record<string, string>;
24
+ includesAllRepositories?: boolean;
25
+ canCreateOrgRepo?: boolean;
26
+ }
27
+ export interface OrgTeamAttributes {
28
+ org: string;
29
+ name: string;
30
+ teamId: number;
31
+ description: string;
32
+ permission: TeamPermission;
33
+ units: string[];
34
+ unitsMap: Record<string, string>;
35
+ includesAllRepositories: boolean;
36
+ canCreateOrgRepo: boolean;
37
+ }
38
+ export interface ForgejoOrgTeam extends Resource<'Forgejo.OrgTeam', OrgTeamProps, OrgTeamAttributes, never, ForgejoRequirements> {
39
+ }
40
+ export declare const ForgejoOrgTeam: import("alchemy").ResourceClass<ForgejoOrgTeam>;
41
+ export declare const ForgejoOrgTeamProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoOrgTeam>, never, import("effect/unstable/http/HttpClient").HttpClient>;
42
+ //# sourceMappingURL=org-team.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-team.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAA8C,CAAC;AAmD1E,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * JSON bodies for `Forgejo.RepoWebhook` — split to keep repo-webhook.ts under the line cap.
3
+ *
4
+ * ⛔ `config.secret` IS NEVER A PROP. When present it is read from `FORGEJO_HOOK_SECRET_*` at
5
+ * create/update time only (see hookSecretEnvKey in values.ts). Alchemy attributes are Postgres
6
+ * plaintext — a webhook HMAC secret must not be stored there.
7
+ */
8
+ import type { RepoWebhookProps } from './repo-webhook.ts';
9
+ export declare const hookUrl: (live: Record<string, unknown>) => string;
10
+ export declare const hookConfigForm: (props: RepoWebhookProps, includeSecret: boolean) => Record<string, string>;
11
+ export declare const createHookForm: (props: RepoWebhookProps) => {
12
+ active: boolean;
13
+ authorization_header?: string;
14
+ branch_filter?: string;
15
+ config: Record<string, string>;
16
+ events: string[];
17
+ name: string;
18
+ type: import("./repo-webhook.ts").RepoWebhookType;
19
+ };
20
+ export declare const updateHookForm: (props: RepoWebhookProps) => {
21
+ active: boolean;
22
+ authorization_header?: string;
23
+ branch_filter?: string;
24
+ config: Record<string, string>;
25
+ events: string[];
26
+ };
27
+ //# sourceMappingURL=repo-webhook-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-webhook-form.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook-form.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,eAAO,MAAM,OAAO,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACW,CAAC;AAEjE,eAAO,MAAM,cAAc,UAAW,gBAAgB,iBAAiB,OAAO,2BAW7E,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,gBAAgB;;;;;;;;CAUpD,CAAC;AAEH,eAAO,MAAM,cAAc,UAAW,gBAAgB;;;;;;CAQpD,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `Forgejo.RepoWebhook` — one repository webhook under `/repos/{owner}/{repo}/hooks`.
3
+ *
4
+ * ★ READ OFF `house/mcp-servers/docs/api/upstream/forgejo.json`: create is POST collection; read/update/delete
5
+ * use numeric hook id at `/repos/{owner}/{repo}/hooks/{id}`. `locate` lists and matches on `name`.
6
+ *
7
+ * ⛔ NO WEBHOOK `secret` IN PROPS OR ATTRIBUTES. Optional HMAC material is env-only at write time
8
+ * (`FORGEJO_HOOK_SECRET_*` — see repo-webhook-form.ts). `hookConfigPublic` strips `secret` on read.
9
+ *
10
+ * ⛔ TOKEN NEEDS `write:repository` TO CREATE OR PATCH — list/get needs `read:repository`.
11
+ */
12
+ import { Resource } from 'alchemy';
13
+ import * as Provider from 'alchemy/Provider';
14
+ import { type ForgejoRequirements } from './resource.ts';
15
+ export type RepoWebhookType = 'dingtalk' | 'discord' | 'feishu' | 'forgejo' | 'gitea' | 'gogs' | 'msteams' | 'packagist' | 'slack' | 'telegram' | 'wechatwork';
16
+ export interface RepoWebhookProps {
17
+ owner: string;
18
+ repo: string;
19
+ /** Stable locate key — also sent as Hook.name on create. */
20
+ name: string;
21
+ type: RepoWebhookType;
22
+ url: string;
23
+ events: string[];
24
+ contentType?: 'json' | 'form';
25
+ active?: boolean;
26
+ branchFilter?: string;
27
+ authorizationHeader?: string;
28
+ }
29
+ export interface RepoWebhookAttributes {
30
+ owner: string;
31
+ repo: string;
32
+ name: string;
33
+ hookId: number;
34
+ type: RepoWebhookType;
35
+ url: string;
36
+ contentType: string;
37
+ events: string[];
38
+ active: boolean;
39
+ branchFilter: string;
40
+ authorizationHeader: string;
41
+ }
42
+ export interface ForgejoRepoWebhook extends Resource<'Forgejo.RepoWebhook', RepoWebhookProps, RepoWebhookAttributes, never, ForgejoRequirements> {
43
+ }
44
+ export declare const ForgejoRepoWebhook: import("alchemy").ResourceClass<ForgejoRepoWebhook>;
45
+ export declare const ForgejoRepoWebhookProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoRepoWebhook>, never, import("effect/unstable/http/HttpClient").HttpClient>;
46
+ //# sourceMappingURL=repo-webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-webhook.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEjB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAgDtF,eAAO,MAAM,0BAA0B,wIACwD,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `Forgejo.Repository` — an organization-owned git repository.
3
+ *
4
+ * ★ MEASURED 2026-09-13 against the live daemon (`GET /api/v1/repos/HomeFlare/homeflare-config`):
5
+ * responses are flat JSON with `private`, `has_issues`, `default_branch`, etc. Create is
6
+ * `POST /orgs/{org}/repos`; read/update/delete use `/repos/{owner}/{repo}`.
7
+ *
8
+ * ⛔ `defaultRemovalPolicy: 'retain'` — history, issues and packages in a repo are irreplaceable.
9
+ * Opt into deletion with `.pipe(RemovalPolicy.destroy())`; `delete` below is fully implemented.
10
+ */
11
+ import { Resource } from 'alchemy';
12
+ import * as Provider from 'alchemy/Provider';
13
+ import { type ForgejoRequirements } from './resource.ts';
14
+ export interface RepositoryProps {
15
+ /** Organization login — the owner segment in `HomeFlare/homeflare-config`. */
16
+ org: string;
17
+ /** Repository name. Changing it is a replace, not an update. */
18
+ name: string;
19
+ description?: string;
20
+ private?: boolean;
21
+ hasIssues?: boolean;
22
+ hasWiki?: boolean;
23
+ hasProjects?: boolean;
24
+ /** Only applied when the repository already has at least one branch. */
25
+ defaultBranch?: string;
26
+ /** Create an initial commit with a README when the repository is new. */
27
+ autoInit?: boolean;
28
+ }
29
+ export interface RepositoryAttributes {
30
+ org: string;
31
+ name: string;
32
+ repoId: number;
33
+ description: string;
34
+ private: boolean;
35
+ hasIssues: boolean;
36
+ hasWiki: boolean;
37
+ hasProjects: boolean;
38
+ defaultBranch: string;
39
+ empty: boolean;
40
+ }
41
+ export interface ForgejoRepository extends Resource<'Forgejo.Repository', RepositoryProps, RepositoryAttributes, never, ForgejoRequirements> {
42
+ }
43
+ export declare const ForgejoRepository: import("alchemy").ResourceClass<ForgejoRepository>;
44
+ export declare const ForgejoRepositoryProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoRepository>, never, import("effect/unstable/http/HttpClient").HttpClient>;
45
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/forgejo/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAqEH,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as Effect from 'effect/Effect';
3
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
4
+ import { ForgejoError } from './client.ts';
5
+ /**
6
+ * ★ WHAT EVERY OPERATION HERE NEEDS FROM THE RUNTIME — and it is NOT what the first draft said.
7
+ *
8
+ * These resources declared `ChildProcessSpawner`, copied from house/proxmox, where it is there
9
+ * because a PVE credential is minted by shelling out to `bao`. Nothing in this package shells
10
+ * out: the token comes from `FORGEJO_TOKEN` in the environment. Declaring a service you never
11
+ * use is not harmless — it makes a stack provide a layer for nothing, and it hides the one
12
+ * service you DO need.
13
+ *
14
+ * What is actually required is `HttpClient`, for the reason in client.ts: Alchemy's own
15
+ * providers call through Effect's client rather than the global `fetch`.
16
+ */
17
+ export type ForgejoRequirements = HttpClient.HttpClient;
18
+ export type ForgejoSpec<Props extends object, Attributes> = {
19
+ /** `repos/HomeFlare/homeflare`, `orgs/homeflare/labels/40` — one object. */
20
+ readonly path: (props: Props) => string;
21
+ /** `orgs/HomeFlare/repos`, `orgs/homeflare/labels` — where a new one is POSTed. */
22
+ readonly collection: (props: Props) => string;
23
+ readonly attributes: (live: Record<string, unknown>, props: Props) => Attributes | undefined;
24
+ /** JSON body on create. ⚠️ Gitea takes JSON, not form encoding. */
25
+ readonly createForm: (props: Props) => Record<string, unknown>;
26
+ readonly updateForm?: (props: Props) => Record<string, unknown>;
27
+ readonly matches: (attributes: Attributes, props: Props) => boolean;
28
+ /**
29
+ * When the stable key is not enough for the wire path — org labels are addressed by numeric id.
30
+ * `locate` lists or searches; `wirePath` builds update/delete paths from the live row.
31
+ */
32
+ readonly locate?: (props: Props) => Effect.Effect<Record<string, unknown> | undefined, ForgejoError, ForgejoRequirements>;
33
+ readonly wirePath?: (props: Props, live: Record<string, unknown>) => string;
34
+ /**
35
+ * When create is PUT upsert rather than POST — org actions secrets have no POST and no GET-by-name.
36
+ * `before === undefined` means absent; the callback decides whether to write. Existence-only families
37
+ * noop when `before` is set because the API never returns the value for comparison.
38
+ */
39
+ readonly upsert?: (props: Props, before: Record<string, unknown> | undefined) => Effect.Effect<void, ForgejoError, ForgejoRequirements>;
40
+ };
41
+ export declare const forgejoOperations: <Props extends object, Attributes>(spec: ForgejoSpec<Props, Attributes>) => {
42
+ read: (props: Props) => Effect.Effect<Attributes | undefined, never, HttpClient.HttpClient>;
43
+ diff: (news: Input<Props>, output: Attributes | undefined) => Effect.Effect<{
44
+ readonly action: 'update';
45
+ } | {
46
+ readonly action: 'noop';
47
+ } | {
48
+ readonly action: 'replace';
49
+ } | undefined, never, HttpClient.HttpClient>;
50
+ reconcile: (news: Props) => Effect.Effect<Attributes & ({} | null), ForgejoError, HttpClient.HttpClient>;
51
+ destroy: (olds: Props) => Effect.Effect<void, ForgejoError, HttpClient.HttpClient>;
52
+ };
53
+ /**
54
+ * The five provider handlers for a spec'd Forgejo object, wired once.
55
+ *
56
+ * ⛔ IT STOPS AT THE HANDLERS AND DOES NOT RETURN THE LAYER — same reasoning as pveHandlers in
57
+ * house/proxmox: wrapping `Provider.effect` here needs casts against Alchemy's `Props<R>`.
58
+ *
59
+ * ⚠️ `list` answers empty — Forgejo index endpoints return the whole org; adoption stays explicit.
60
+ */
61
+ export declare const forgejoHandlers: <Props extends object, Attributes extends object>(spec: ForgejoSpec<Props, Attributes>) => {
62
+ list: () => Effect.Effect<never[], never, never>;
63
+ read: ({ olds }: {
64
+ olds: Props;
65
+ }) => Effect.Effect<Attributes | undefined, never, HttpClient.HttpClient>;
66
+ diff: ({ news, output }: {
67
+ news: Input<Props>;
68
+ output: Attributes | undefined;
69
+ }) => Effect.Effect<{
70
+ readonly action: 'update';
71
+ } | {
72
+ readonly action: 'noop';
73
+ } | {
74
+ readonly action: 'replace';
75
+ } | undefined, never, HttpClient.HttpClient>;
76
+ reconcile: ({ news }: {
77
+ news: Props;
78
+ }) => Effect.Effect<Attributes, ForgejoError, HttpClient.HttpClient>;
79
+ delete: ({ olds }: {
80
+ olds: Props;
81
+ }) => Effect.Effect<void, ForgejoError, HttpClient.HttpClient>;
82
+ };
83
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/forgejo/resource.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAW,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,UAAU,CAAC;AAExD,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,UAAU,IAAI;IAC1D,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IACxC,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAC;IAC7F,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACpE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC5E;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KACxC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;CAC7D,CAAC;AAWF,eAAO,MAAM,iBAAiB,GAAI,KAAK,SAAS,MAAM,EAAE,UAAU,QAC1D,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC;kBAYf,KAAK;iBAQX,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;sBAIV,KAAK;oBA8BP,KAAK;CAOxB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,QACvE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC;IAIlC,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IAChC,IAAI,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBA7DlC,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;IA4D5B,SAAS,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IACrC,MAAM,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;CAErC,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `Forgejo.TeamMember` — one user in one organization team.
3
+ *
4
+ * ★ WHY IT EXISTS: forgejo-provision, the service account the Forgejo engine mints as, belongs to the
5
+ * HomeFlare Owners team — Tim, 2026-09-14. Membership is what its tokens can reach; the role's
6
+ * scopes only cap it (house/openbao/src/forgejo-bootstrap.ts).
7
+ *
8
+ * ★ READ OFF THE LIVE 16.0.3 SWAGGER (127.0.0.1:3000/swagger.v1.json, 2026-09-14): GET, PUT and
9
+ * DELETE `/teams/{id}/members/{username}` answer 200, 204 and 204, and 404 when absent. The team
10
+ * is addressed by numeric id, so it is found by name first — the same seam as org-team.ts.
11
+ *
12
+ * ⚠️ EXISTENCE IS THE WHOLE OBJECT. Nothing about a membership can be updated, so `matches` is
13
+ * always true; a different team or user is a different resource id in alchemy.run.ts.
14
+ *
15
+ * ⚠️ THE TEAM ITSELF IS ONLY LOCATED HERE, NEVER MANAGED. A missing team fails the plan by name
16
+ * instead of folding into "absent", because a PUT can add a member but cannot create a team.
17
+ *
18
+ * ⛔ TOKEN NEEDS `write:organization` TO ADD OR REMOVE, and an identity allowed to manage the team.
19
+ */
20
+ import { Resource } from 'alchemy';
21
+ import * as Provider from 'alchemy/Provider';
22
+ import { type ForgejoRequirements } from './resource.ts';
23
+ export interface TeamMemberProps {
24
+ org: string;
25
+ /** Team name as Forgejo shows it, e.g. `Owners`. */
26
+ team: string;
27
+ username: string;
28
+ }
29
+ export interface TeamMemberAttributes {
30
+ org: string;
31
+ team: string;
32
+ teamId: number;
33
+ username: string;
34
+ }
35
+ export interface ForgejoTeamMember extends Resource<'Forgejo.TeamMember', TeamMemberProps, TeamMemberAttributes, never, ForgejoRequirements> {
36
+ }
37
+ export declare const ForgejoTeamMember: import("alchemy").ResourceClass<ForgejoTeamMember>;
38
+ export declare const ForgejoTeamMemberProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoTeamMember>, never, import("effect/unstable/http/HttpClient").HttpClient>;
39
+ //# sourceMappingURL=team-member.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-member.d.ts","sourceRoot":"","sources":["../../src/forgejo/team-member.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAAoD,CAAC;AAgDnF,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}