@homeflare/alchemy 0.5.0 → 0.7.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 (288) hide show
  1. package/README.md +68 -55
  2. package/dist/caddy/admin-calls.d.ts +48 -0
  3. package/dist/caddy/admin-calls.d.ts.map +1 -0
  4. package/dist/caddy/admin-guard.d.ts +40 -0
  5. package/dist/caddy/admin-guard.d.ts.map +1 -0
  6. package/dist/caddy/admin.d.ts +73 -0
  7. package/dist/caddy/admin.d.ts.map +1 -0
  8. package/dist/caddy/caddyfile-tripwire.d.ts +5 -0
  9. package/dist/caddy/caddyfile-tripwire.d.ts.map +1 -0
  10. package/dist/caddy/config-form.d.ts +27 -0
  11. package/dist/caddy/config-form.d.ts.map +1 -0
  12. package/dist/caddy/config-lifecycle.d.ts +45 -0
  13. package/dist/caddy/config-lifecycle.d.ts.map +1 -0
  14. package/dist/caddy/config.d.ts +30 -0
  15. package/dist/caddy/config.d.ts.map +1 -0
  16. package/dist/caddy/digest.d.ts +7 -0
  17. package/dist/caddy/digest.d.ts.map +1 -0
  18. package/dist/caddy/fake-caddy.d.ts +43 -0
  19. package/dist/caddy/fake-caddy.d.ts.map +1 -0
  20. package/dist/caddy/index.d.ts +21 -0
  21. package/dist/caddy/index.d.ts.map +1 -0
  22. package/dist/caddy/index.js +912 -0
  23. package/dist/caddy/index.js.map +28 -0
  24. package/dist/caddy/local-admin.d.ts +45 -0
  25. package/dist/caddy/local-admin.d.ts.map +1 -0
  26. package/dist/caddy/providers.d.ts +14 -0
  27. package/dist/caddy/providers.d.ts.map +1 -0
  28. package/dist/caddy/with-file.d.ts +47 -0
  29. package/dist/caddy/with-file.d.ts.map +1 -0
  30. package/dist/cloudflare/fake-mesh.d.ts +58 -0
  31. package/dist/cloudflare/fake-mesh.d.ts.map +1 -0
  32. package/dist/cloudflare/index.d.ts +9 -3
  33. package/dist/cloudflare/index.d.ts.map +1 -1
  34. package/dist/cloudflare/index.js +218 -7
  35. package/dist/cloudflare/index.js.map +9 -4
  36. package/dist/cloudflare/mesh-node-api.d.ts +75 -0
  37. package/dist/cloudflare/mesh-node-api.d.ts.map +1 -0
  38. package/dist/cloudflare/mesh-node-form.d.ts +82 -0
  39. package/dist/cloudflare/mesh-node-form.d.ts.map +1 -0
  40. package/dist/cloudflare/mesh-node-harness.d.ts +46 -0
  41. package/dist/cloudflare/mesh-node-harness.d.ts.map +1 -0
  42. package/dist/cloudflare/mesh-node-lifecycle.d.ts +22 -0
  43. package/dist/cloudflare/mesh-node-lifecycle.d.ts.map +1 -0
  44. package/dist/cloudflare/mesh-node-token.d.ts +23 -0
  45. package/dist/cloudflare/mesh-node-token.d.ts.map +1 -0
  46. package/dist/cloudflare/mesh-node.d.ts +48 -0
  47. package/dist/cloudflare/mesh-node.d.ts.map +1 -0
  48. package/dist/cloudflare/providers.d.ts +1 -12
  49. package/dist/cloudflare/providers.d.ts.map +1 -1
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/launchd/fake-runner.d.ts +36 -0
  53. package/dist/launchd/fake-runner.d.ts.map +1 -0
  54. package/dist/launchd/host-effect.d.ts +19 -0
  55. package/dist/launchd/host-effect.d.ts.map +1 -0
  56. package/dist/launchd/host-file-form.d.ts +24 -0
  57. package/dist/launchd/host-file-form.d.ts.map +1 -0
  58. package/dist/launchd/host-file-lifecycle.d.ts +29 -0
  59. package/dist/launchd/host-file-lifecycle.d.ts.map +1 -0
  60. package/dist/launchd/host-file.d.ts +25 -0
  61. package/dist/launchd/host-file.d.ts.map +1 -0
  62. package/dist/launchd/host-lookup.d.ts +27 -0
  63. package/dist/launchd/host-lookup.d.ts.map +1 -0
  64. package/dist/launchd/index.d.ts +21 -0
  65. package/dist/launchd/index.d.ts.map +1 -0
  66. package/dist/launchd/index.js +969 -0
  67. package/dist/launchd/index.js.map +25 -0
  68. package/dist/launchd/job-form.d.ts +90 -0
  69. package/dist/launchd/job-form.d.ts.map +1 -0
  70. package/dist/launchd/job-lifecycle.d.ts +34 -0
  71. package/dist/launchd/job-lifecycle.d.ts.map +1 -0
  72. package/dist/launchd/job-preflight.d.ts +50 -0
  73. package/dist/launchd/job-preflight.d.ts.map +1 -0
  74. package/dist/launchd/job-validate.d.ts +25 -0
  75. package/dist/launchd/job-validate.d.ts.map +1 -0
  76. package/dist/launchd/job.d.ts +34 -0
  77. package/dist/launchd/job.d.ts.map +1 -0
  78. package/dist/launchd/launchctl.d.ts +62 -0
  79. package/dist/launchd/launchctl.d.ts.map +1 -0
  80. package/dist/launchd/local-runner.d.ts +7 -0
  81. package/dist/launchd/local-runner.d.ts.map +1 -0
  82. package/dist/launchd/plist.d.ts +41 -0
  83. package/dist/launchd/plist.d.ts.map +1 -0
  84. package/dist/launchd/providers.d.ts +15 -0
  85. package/dist/launchd/providers.d.ts.map +1 -0
  86. package/dist/launchd/runner.d.ts +91 -0
  87. package/dist/launchd/runner.d.ts.map +1 -0
  88. package/dist/launchd/secret-tripwire.d.ts +24 -0
  89. package/dist/launchd/secret-tripwire.d.ts.map +1 -0
  90. package/dist/openbao/auth-method-form.d.ts +7 -0
  91. package/dist/openbao/auth-method-form.d.ts.map +1 -1
  92. package/dist/openbao/auth-method-reconcile.d.ts +15 -0
  93. package/dist/openbao/auth-method-reconcile.d.ts.map +1 -0
  94. package/dist/openbao/auth-method.d.ts +6 -0
  95. package/dist/openbao/auth-method.d.ts.map +1 -1
  96. package/dist/openbao/auth-role-form.d.ts +9 -0
  97. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  98. package/dist/openbao/auth-role.d.ts.map +1 -1
  99. package/dist/openbao/auth-token-form.d.ts +48 -0
  100. package/dist/openbao/auth-token-form.d.ts.map +1 -0
  101. package/dist/openbao/bao-identity.d.ts +81 -0
  102. package/dist/openbao/bao-identity.d.ts.map +1 -0
  103. package/dist/openbao/bao-namespace.d.ts +20 -0
  104. package/dist/openbao/bao-namespace.d.ts.map +1 -0
  105. package/dist/openbao/cloudflare-role.d.ts.map +1 -1
  106. package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -1
  107. package/dist/openbao/fake-engines.d.ts +30 -0
  108. package/dist/openbao/fake-engines.d.ts.map +1 -0
  109. package/dist/openbao/fake-stack.d.ts +29 -0
  110. package/dist/openbao/fake-stack.d.ts.map +1 -0
  111. package/dist/openbao/host-approles.d.ts +62 -0
  112. package/dist/openbao/host-approles.d.ts.map +1 -0
  113. package/dist/openbao/index.d.ts +19 -0
  114. package/dist/openbao/index.d.ts.map +1 -1
  115. package/dist/openbao/index.js +1518 -310
  116. package/dist/openbao/index.js.map +40 -17
  117. package/dist/openbao/jwt-config-form.d.ts +51 -0
  118. package/dist/openbao/jwt-config-form.d.ts.map +1 -0
  119. package/dist/openbao/jwt-config.d.ts +25 -0
  120. package/dist/openbao/jwt-config.d.ts.map +1 -0
  121. package/dist/openbao/jwt-role-form.d.ts +91 -0
  122. package/dist/openbao/jwt-role-form.d.ts.map +1 -0
  123. package/dist/openbao/jwt-role.d.ts +31 -0
  124. package/dist/openbao/jwt-role.d.ts.map +1 -0
  125. package/dist/openbao/kubernetes-role-form.d.ts +68 -0
  126. package/dist/openbao/kubernetes-role-form.d.ts.map +1 -0
  127. package/dist/openbao/kubernetes-role.d.ts +25 -0
  128. package/dist/openbao/kubernetes-role.d.ts.map +1 -0
  129. package/dist/openbao/mfa-enforcement-form.d.ts +46 -0
  130. package/dist/openbao/mfa-enforcement-form.d.ts.map +1 -0
  131. package/dist/openbao/mfa-enforcement.d.ts +41 -0
  132. package/dist/openbao/mfa-enforcement.d.ts.map +1 -0
  133. package/dist/openbao/mfa-totp-form.d.ts +52 -0
  134. package/dist/openbao/mfa-totp-form.d.ts.map +1 -0
  135. package/dist/openbao/mfa-totp.d.ts +33 -0
  136. package/dist/openbao/mfa-totp.d.ts.map +1 -0
  137. package/dist/openbao/mfa-wire.d.ts +63 -0
  138. package/dist/openbao/mfa-wire.d.ts.map +1 -0
  139. package/dist/openbao/mount-form.d.ts +7 -0
  140. package/dist/openbao/mount-form.d.ts.map +1 -1
  141. package/dist/openbao/mount-move.d.ts +49 -0
  142. package/dist/openbao/mount-move.d.ts.map +1 -0
  143. package/dist/openbao/mount-path.d.ts +8 -4
  144. package/dist/openbao/mount-path.d.ts.map +1 -1
  145. package/dist/openbao/mount-reconcile.d.ts +16 -0
  146. package/dist/openbao/mount-reconcile.d.ts.map +1 -0
  147. package/dist/openbao/mount.d.ts +9 -0
  148. package/dist/openbao/mount.d.ts.map +1 -1
  149. package/dist/openbao/pki-role-canonical.d.ts +46 -0
  150. package/dist/openbao/pki-role-canonical.d.ts.map +1 -0
  151. package/dist/openbao/pki-role-form.d.ts +38 -30
  152. package/dist/openbao/pki-role-form.d.ts.map +1 -1
  153. package/dist/openbao/pki-role.d.ts.map +1 -1
  154. package/dist/openbao/policy.d.ts +2 -0
  155. package/dist/openbao/policy.d.ts.map +1 -1
  156. package/dist/openbao/proxmox-role-form.d.ts +18 -8
  157. package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
  158. package/dist/openbao/proxmox-role-wire.d.ts +18 -0
  159. package/dist/openbao/proxmox-role-wire.d.ts.map +1 -0
  160. package/dist/openbao/proxmox-role.d.ts.map +1 -1
  161. package/dist/openbao/remount-wire.d.ts +44 -0
  162. package/dist/openbao/remount-wire.d.ts.map +1 -0
  163. package/dist/openbao/rename.d.ts +106 -0
  164. package/dist/openbao/rename.d.ts.map +1 -0
  165. package/dist/openbao/role-reconcile.d.ts +40 -0
  166. package/dist/openbao/role-reconcile.d.ts.map +1 -0
  167. package/docs/caddy.md +180 -0
  168. package/docs/launchd.md +167 -0
  169. package/docs/mesh-node.md +188 -0
  170. package/package.json +12 -2
  171. package/src/caddy/admin-calls.ts +128 -0
  172. package/src/caddy/admin-guard.ts +170 -0
  173. package/src/caddy/admin.ts +74 -0
  174. package/src/caddy/caddyfile-tripwire.ts +133 -0
  175. package/src/caddy/config-form.ts +64 -0
  176. package/src/caddy/config-lifecycle.test.ts +218 -0
  177. package/src/caddy/config-lifecycle.ts +148 -0
  178. package/src/caddy/config.ts +116 -0
  179. package/src/caddy/digest.ts +44 -0
  180. package/src/caddy/fake-caddy.ts +160 -0
  181. package/src/caddy/guards.test.ts +156 -0
  182. package/src/caddy/index.ts +25 -0
  183. package/src/caddy/local-admin.test.ts +160 -0
  184. package/src/caddy/local-admin.ts +189 -0
  185. package/src/caddy/providers.test.ts +244 -0
  186. package/src/caddy/providers.ts +17 -0
  187. package/src/caddy/with-file.ts +62 -0
  188. package/src/cloudflare/fake-mesh.ts +181 -0
  189. package/src/cloudflare/index.ts +9 -3
  190. package/src/cloudflare/mesh-node-api.ts +127 -0
  191. package/src/cloudflare/mesh-node-edges.test.ts +103 -0
  192. package/src/cloudflare/mesh-node-form.test.ts +80 -0
  193. package/src/cloudflare/mesh-node-form.ts +115 -0
  194. package/src/cloudflare/mesh-node-harness.ts +124 -0
  195. package/src/cloudflare/mesh-node-lifecycle.ts +187 -0
  196. package/src/cloudflare/mesh-node-policy.test.ts +151 -0
  197. package/src/cloudflare/mesh-node-token.test.ts +117 -0
  198. package/src/cloudflare/mesh-node-token.ts +125 -0
  199. package/src/cloudflare/mesh-node.test.ts +172 -0
  200. package/src/cloudflare/mesh-node.ts +120 -0
  201. package/src/cloudflare/providers.ts +20 -2
  202. package/src/index.ts +2 -0
  203. package/src/launchd/fake-runner.ts +176 -0
  204. package/src/launchd/host-effect.ts +28 -0
  205. package/src/launchd/host-file-form.ts +71 -0
  206. package/src/launchd/host-file-lifecycle.test.ts +198 -0
  207. package/src/launchd/host-file-lifecycle.ts +164 -0
  208. package/src/launchd/host-file.ts +64 -0
  209. package/src/launchd/host-lookup.test.ts +92 -0
  210. package/src/launchd/host-lookup.ts +94 -0
  211. package/src/launchd/index.ts +26 -0
  212. package/src/launchd/job-form.test.ts +171 -0
  213. package/src/launchd/job-form.ts +180 -0
  214. package/src/launchd/job-identity.test.ts +147 -0
  215. package/src/launchd/job-lifecycle.test.ts +220 -0
  216. package/src/launchd/job-lifecycle.ts +194 -0
  217. package/src/launchd/job-preflight.ts +152 -0
  218. package/src/launchd/job-validate.ts +134 -0
  219. package/src/launchd/job.ts +96 -0
  220. package/src/launchd/launchctl.test.ts +181 -0
  221. package/src/launchd/launchctl.ts +188 -0
  222. package/src/launchd/local-runner.test.ts +125 -0
  223. package/src/launchd/local-runner.ts +143 -0
  224. package/src/launchd/plist.test.ts +186 -0
  225. package/src/launchd/plist.ts +156 -0
  226. package/src/launchd/providers.test.ts +222 -0
  227. package/src/launchd/providers.ts +21 -0
  228. package/src/launchd/runner.ts +95 -0
  229. package/src/launchd/secret-tripwire.test.ts +110 -0
  230. package/src/launchd/secret-tripwire.ts +81 -0
  231. package/src/openbao/README.md +181 -0
  232. package/src/openbao/REPLACE.md +163 -0
  233. package/src/openbao/auth-method-form.ts +7 -0
  234. package/src/openbao/auth-method-reconcile.ts +74 -0
  235. package/src/openbao/auth-method.ts +20 -41
  236. package/src/openbao/auth-role-form.test.ts +16 -1
  237. package/src/openbao/auth-role-form.ts +11 -0
  238. package/src/openbao/auth-role.ts +12 -0
  239. package/src/openbao/auth-token-form.ts +161 -0
  240. package/src/openbao/bao-identity.test.ts +131 -0
  241. package/src/openbao/bao-identity.ts +188 -0
  242. package/src/openbao/bao-namespace.ts +32 -0
  243. package/src/openbao/cloudflare-role-wire.test.ts +8 -34
  244. package/src/openbao/cloudflare-role.ts +30 -8
  245. package/src/openbao/cloudflare-roles-expand.ts +2 -5
  246. package/src/openbao/fake-engines.ts +119 -0
  247. package/src/openbao/fake-stack.ts +112 -0
  248. package/src/openbao/host-approles.test.ts +121 -0
  249. package/src/openbao/host-approles.ts +126 -0
  250. package/src/openbao/index.ts +19 -0
  251. package/src/openbao/jwt-config-form.ts +161 -0
  252. package/src/openbao/jwt-config.ts +85 -0
  253. package/src/openbao/jwt-role-form.test.ts +130 -0
  254. package/src/openbao/jwt-role-form.ts +231 -0
  255. package/src/openbao/jwt-role.ts +102 -0
  256. package/src/openbao/kubernetes-role-form.test.ts +82 -0
  257. package/src/openbao/kubernetes-role-form.ts +149 -0
  258. package/src/openbao/kubernetes-role.ts +94 -0
  259. package/src/openbao/mfa-enforcement-form.ts +147 -0
  260. package/src/openbao/mfa-enforcement.ts +143 -0
  261. package/src/openbao/mfa-totp-form.ts +155 -0
  262. package/src/openbao/mfa-totp.ts +118 -0
  263. package/src/openbao/mfa-wire.ts +117 -0
  264. package/src/openbao/mfa.test.ts +200 -0
  265. package/src/openbao/mount-form.ts +9 -1
  266. package/src/openbao/mount-move.test.ts +164 -0
  267. package/src/openbao/mount-move.ts +113 -0
  268. package/src/openbao/mount-path.test.ts +11 -1
  269. package/src/openbao/mount-path.ts +17 -11
  270. package/src/openbao/mount-reconcile.ts +75 -0
  271. package/src/openbao/mount.ts +26 -43
  272. package/src/openbao/pki-role-canonical.ts +163 -0
  273. package/src/openbao/pki-role-form.test.ts +112 -0
  274. package/src/openbao/pki-role-form.ts +76 -110
  275. package/src/openbao/pki-role.ts +12 -27
  276. package/src/openbao/policy-rename.test.ts +112 -0
  277. package/src/openbao/policy-wire.test.ts +7 -26
  278. package/src/openbao/policy.ts +27 -4
  279. package/src/openbao/proxmox-role-form.ts +27 -8
  280. package/src/openbao/proxmox-role-wire.ts +24 -0
  281. package/src/openbao/proxmox-role.ts +62 -37
  282. package/src/openbao/remount-wire.test.ts +87 -0
  283. package/src/openbao/remount-wire.ts +112 -0
  284. package/src/openbao/rename-occupied.test.ts +166 -0
  285. package/src/openbao/rename.ts +162 -0
  286. package/src/openbao/role-reconcile.test.ts +104 -0
  287. package/src/openbao/role-reconcile.ts +84 -0
  288. package/src/openbao/role-rename.test.ts +205 -0
@@ -0,0 +1,46 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import * as Exit from 'effect/Exit';
3
+ import { type FakeMesh } from './fake-mesh.ts';
4
+ import { MeshNode } from './mesh-node.ts';
5
+ import type { MeshNodeAttributes, MeshNodeProps } from './mesh-node-form.ts';
6
+ export declare const ids: {
7
+ fqn: string;
8
+ id: string;
9
+ instanceId: string;
10
+ };
11
+ export declare const extra: {
12
+ bindings: never;
13
+ session: never;
14
+ };
15
+ export type P = Effect.Success<typeof MeshNode.Provider>;
16
+ export declare const handler: <F>(name: string, fn: F | undefined) => F;
17
+ /** Build the provider over the fake (as `accountId`'s environment) and hand it to `use`. */
18
+ export declare const run: <A, E>(fake: FakeMesh, use: (p: P) => Effect.Effect<A, E>, accountId?: string) => Promise<A>;
19
+ export declare const reconcile: (p: P, news: MeshNodeProps, output?: MeshNodeAttributes) => Effect.Effect<MeshNodeAttributes, any, never>;
20
+ export declare const read: (p: P, olds: MeshNodeProps, output?: MeshNodeAttributes) => Effect.Effect<MeshNodeAttributes | undefined, any, never>;
21
+ export declare const diff: (p: P, news: MeshNodeProps, olds: MeshNodeProps, output: MeshNodeAttributes) => Effect.Effect<void | import("alchemy").Diff, any, never>;
22
+ /** A stored attribute set for a node that may or may not exist in the fake. */
23
+ export declare const stored: (accountId: string, over?: Partial<MeshNodeAttributes>) => MeshNodeAttributes;
24
+ export declare const tokenReads: (fake: FakeMesh) => number;
25
+ /** The writes the fake saw, in order: `['POST', 'DELETE', …]`. */
26
+ export declare const writes: (fake: FakeMesh) => string[];
27
+ /**
28
+ * ★ THE REAL ENGINE, NOT A MODEL OF IT: Alchemy's own `Plan.make` + `apply` over an in-memory
29
+ * state store, which is what its Test harness's scratch stacks run (Test/Core.ts
30
+ * `scratchStack`), minus the profile, config and telemetry layers a fake Cloudflare does not
31
+ * need. Removal policy lives in the engine, so only this can show what it does to a replace.
32
+ * ⚠️ `FetchHttpClient.Fetch` IS READ PER REQUEST, from the fiber running the engine, so it is
33
+ * provided around the whole deploy. Provided only under the provider layer, the requests went
34
+ * to the real `fetch` (measured 2026-09-21: ENOTFOUND api.example.com, the RFC 2606 host, so
35
+ * no API answered — but a real DNS lookup went out).
36
+ */
37
+ export declare const engine: (fake: FakeMesh) => {
38
+ deploy: <A, E, R>(declare: Effect.Effect<A, E, R>) => Promise<Exit.Exit<unknown, unknown>>;
39
+ /** The stored row's status (`created`, `replacing`, …), or `undefined` when there is none. */
40
+ status: (fqn: string) => "created" | "creating" | "deleting" | "replaced" | "replacing" | "updated" | "updating" | undefined;
41
+ /** ★ What `alchemy state rm` does ("Delete state records without deleting cloud resources"). */
42
+ forget: (fqn: string) => void;
43
+ };
44
+ /** The failure sentence of a deploy, or `''` when it succeeded. */
45
+ export declare const failureOf: (exit: Exit.Exit<unknown, unknown>) => string;
46
+ //# sourceMappingURL=mesh-node-harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-node-harness.d.ts","sourceRoot":"","sources":["../../src/cloudflare/mesh-node-harness.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAGpC,OAAO,EAAE,KAAK,QAAQ,EAAqB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAoB,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,eAAO,MAAM,GAAG;IAAK,GAAG;IAAgB,EAAE;IAAU,UAAU;CAAS,CAAC;AACxE,eAAO,MAAM,KAAK;IAAK,QAAQ,EAAQ,KAAK;IAAE,OAAO,EAAe,KAAK;CAAE,CAAC;AAE5E,MAAM,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEzD,eAAO,MAAM,OAAO,GAAI,CAAC,QAAQ,MAAM,MAAM,CAAC,GAAG,SAAS,KAAG,CAG5D,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,GAAG,GAAI,CAAC,EAAE,CAAC,QAAQ,QAAQ,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,cAAc,MAAM,eAS7F,CAAC;AAEJ,eAAO,MAAM,SAAS,MAAO,CAAC,QAAQ,aAAa,WAAW,kBAAkB,kDACd,CAAC;AAEnE,eAAO,MAAM,IAAI,MAAO,CAAC,QAAQ,aAAa,WAAW,kBAAkB,8DACxB,CAAC;AAEpD,eAAO,MAAM,IAAI,MAAO,CAAC,QAAQ,aAAa,QAAQ,aAAa,UAAU,kBAAkB,6DAW3F,CAAC;AAEL,+EAA+E;AAC/E,eAAO,MAAM,MAAM,cACN,MAAM,SACX,OAAO,CAAC,kBAAkB,CAAC,KAChC,kBAOD,CAAC;AAEH,eAAO,MAAM,UAAU,SAAU,QAAQ,WACkB,CAAC;AAE5D,kEAAkE;AAClE,eAAO,MAAM,MAAM,SAAU,QAAQ,aAC6B,CAAC;AAInE;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,SAAU,QAAQ;aAInB,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IActD,8FAA8F;kBAChF,MAAM;IACpB,gGAAgG;kBAClF,MAAM;CAIvB,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,SAAS,SAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,WACG,CAAC"}
@@ -0,0 +1,22 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { MeshNodeError } from './mesh-node-api.ts';
3
+ import { type MeshNodeAttributes, type MeshNodeProps } from './mesh-node-form.ts';
4
+ /**
5
+ * Owned: refresh by the stored id. Cold: find the live node by exact name and hand it back
6
+ * `Unowned`, so Alchemy refuses to take it over until the stack says `adopt(true)` — the same
7
+ * gate `Cloudflare.Tunnel.WarpConnector` uses, because a `warp_connector` carries no ownership
8
+ * marker to prove it was ours.
9
+ *
10
+ * ⚠️ ADOPTION RECORDS THE DECLARED `ha`, BECAUSE NOTHING ELSE CAN. No documented read returns a
11
+ * node's HA flag. Adopting an existing node with the wrong `ha` records the wrong value, and no
12
+ * later plan can notice. Check the node's HA badge in the dashboard before adopting.
13
+ */
14
+ export declare const readMeshNode: (accountId: string, olds: MeshNodeProps | undefined, output: MeshNodeAttributes | undefined) => Effect.Effect<MeshNodeAttributes | undefined, MeshNodeError | import("@distilled.cloud/cloudflare/zero-trust").ListTunnelWarpConnectorsError, import("@distilled.cloud/cloudflare/Protocol").CloudflareOpContext>;
15
+ export declare const reconcileMeshNode: (accountId: string, news: MeshNodeProps, output: MeshNodeAttributes | undefined) => Effect.Effect<MeshNodeAttributes, import("@distilled.cloud/cloudflare/zero-trust").Forbidden | MeshNodeError | import("@distilled.cloud/cloudflare/zero-trust").TunnelNotFound | import("@distilled.cloud/cloudflare/Protocol").CloudflareOpError, import("@distilled.cloud/cloudflare/Protocol").CloudflareOpContext>;
16
+ /**
17
+ * ⚠️ Cloudflare may refuse to delete a node while replicas are connected (Alchemy's WarpConnector
18
+ * notes "Deleting requires all connector sessions to be down"; unmeasured here). Stop the
19
+ * client on every replica first. A node already gone is a successful delete.
20
+ */
21
+ export declare const deleteMeshNode: (output: MeshNodeAttributes) => Effect.Effect<void, import("@distilled.cloud/cloudflare/zero-trust").Forbidden | import("@distilled.cloud/cloudflare/Protocol").CloudflareOpError, import("@distilled.cloud/cloudflare/Protocol").CloudflareOpContext>;
22
+ //# sourceMappingURL=mesh-node-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-node-lifecycle.d.ts","sourceRoot":"","sources":["../../src/cloudflare/mesh-node-lifecycle.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,aAAa,EAOd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAUpG;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,cACZ,MAAM,QACX,aAAa,GAAG,SAAS,UACvB,kBAAkB,GAAG,SAAS,sNAapC,CAAC;AAoGL,eAAO,MAAM,iBAAiB,cACjB,MAAM,QACX,aAAa,UACX,kBAAkB,GAAG,SAAS,2TAwBpC,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,cAAc,WAAY,kBAAkB,2NAChB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';
2
+ import * as Effect from 'effect/Effect';
3
+ import * as Redacted from 'effect/Redacted';
4
+ import { MeshNodeError } from './mesh-node-api.ts';
5
+ /** Which node: by id (the `MeshNode` attribute) or by its exact name. */
6
+ export type MeshNodeRef = {
7
+ readonly accountId: string;
8
+ } & ({
9
+ readonly id: string;
10
+ readonly name?: undefined;
11
+ } | {
12
+ readonly name: string;
13
+ readonly id?: undefined;
14
+ });
15
+ /**
16
+ * ⛔ An EMPTY token fails closed: `warp-cli connector new ""` would not enrol, and a script that
17
+ * wrote an empty file would report success over a host that never joined.
18
+ * ⛔ The failures made here name the node and account and quote no response body, because the
19
+ * success body IS the token. Other SDK errors pass through; they come only from non-2xx
20
+ * responses, which carry an error envelope, not a token.
21
+ */
22
+ export declare const fetchMeshNodeToken: (ref: MeshNodeRef) => Effect.Effect<Redacted.Redacted<string>, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").CloudflareError | import("@distilled.cloud/cloudflare/Errors").CloudflareHttpError | import("@distilled.cloud/cloudflare/Errors").CloudflareParseError | import("@distilled.cloud/cloudflare/Errors").CloudflareRateLimited | import("@distilled.cloud/cloudflare/Errors").ConfigError | zeroTrust.Forbidden | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | import("@distilled.cloud/cloudflare/Errors").InvalidRoute | MeshNodeError | import("@distilled.cloud/cloudflare/Credentials").OAuthRefreshError | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/cloudflare/Errors").UnknownCloudflareError, zeroTrust.CloudflareOpContext>;
23
+ //# sourceMappingURL=mesh-node-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-node-token.d.ts","sourceRoot":"","sources":["../../src/cloudflare/mesh-node-token.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,SAAS,MAAM,wCAAwC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAkB,MAAM,oBAAoB,CAAC;AAEnE,yEAAyE;AACzE,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,CACvD;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAA;CAAE,CACrD,CAAC;AAoDF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAAS,WAAW,wjCA4B/C,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `Cloudflare.MeshNode` — a Cloudflare Mesh node (a `warp_connector` object), declared without
3
+ * its token ever touching Alchemy state. Usage and the enrolment step: docs/mesh-node.md.
4
+ *
5
+ * 🔴 WHY THIS EXISTS ALONGSIDE `Cloudflare.Tunnel.WarpConnector`. A Mesh node that fronts a
6
+ * private service needs a node record in the graph, so the Gateway rules and DNS records that
7
+ * name it deploy together. Alchemy beta.79's WarpConnector would put the node token — the
8
+ * credential that lets any Linux host join the account's Mesh as that node — into plaintext
9
+ * state on every read, and it cannot create an HA node. See mesh-node-form.ts.
10
+ *
11
+ * ★ REMOVAL POLICY: `retain` BY DEFAULT — see the ★ on `MeshNode` below, and what it does to an
12
+ * `ha` replace in mesh-node-lifecycle.ts.
13
+ */
14
+ import { Credentials } from '@distilled.cloud/cloudflare/Credentials';
15
+ import * as Cloudflare from 'alchemy/Cloudflare';
16
+ import * as Provider from 'alchemy/Provider';
17
+ import { Resource, type ResourceClass } from 'alchemy/Resource';
18
+ import * as HttpClient from 'effect/unstable/http/HttpClient';
19
+ import { type MeshNodeAttributes, type MeshNodeProps } from './mesh-node-form.ts';
20
+ import type { Providers } from './providers.ts';
21
+ export interface MeshNode extends Resource<'Cloudflare.MeshNode', MeshNodeProps, MeshNodeAttributes, never, Providers> {
22
+ }
23
+ /**
24
+ * ★ `defaultRemovalPolicy: 'retain'`, LIKE EVERY KIT RESOURCE WHOSE DELETION BREAKS ITS CONSUMERS
25
+ * (the `Bao.*` families, `R2BucketLock`, the Proxmox storage families; the reasoning is written
26
+ * once in proxmox/resource.ts). Deleting a node cuts every enrolled replica off the Mesh, and
27
+ * nothing brings it back: a new node has a new id, a new token and a new Mesh IP per replica, so
28
+ * each host must be re-enrolled by hand and every Gateway rule or allow-list that pins the old
29
+ * Mesh IP breaks. So dropping the declaration, or `alchemy destroy`, leaves the node live.
30
+ * ⚠️ IT ALSO SKIPS THE OLD NODE'S DELETE IN A REPLACE (Apply.ts, `deleteOldGenerations` and the
31
+ * GC's `retainOldGeneration`, beta.79). A same-name `ha` change therefore REFUSES, deleting
32
+ * nothing, until that deploy opts in with `.pipe(RemovalPolicy.destroy())`; a create-first
33
+ * replace leaves the old node live and unmanaged. Both measured in mesh-node-policy.test.ts.
34
+ * ⛔ `delete` STAYS FULLY IMPLEMENTED. Retain is Terraform's `prevent_destroy`, not a stub: it runs
35
+ * the moment a declaration opts in with `.pipe(RemovalPolicy.destroy())`.
36
+ */
37
+ export declare const MeshNode: ResourceClass<MeshNode>;
38
+ /** What every call below needs: the account, credentials and HTTP — Alchemy's own trio. */
39
+ export type MeshNodeServices = Cloudflare.CloudflareEnvironment | Credentials | HttpClient.HttpClient;
40
+ /**
41
+ * ★ `Provider.effect`, CAPTURING THE THREE SERVICES AT BUILD. `Provider.succeed` (what Alchemy's
42
+ * own providers use) leaves each handler to find CloudflareEnvironment and Credentials in the
43
+ * engine's context at call time — which works for `Cloudflare.providers()` only because it
44
+ * `provideMerge`s them into the stack. Capturing them here keeps this provider correct whether
45
+ * or not the stack also merges `Cloudflare.providers()`.
46
+ */
47
+ export declare const MeshNodeProvider: () => import("effect/Layer").Layer<Provider.Provider<MeshNode>, never, Cloudflare.CloudflareEnvironment | Credentials | HttpClient.HttpClient>;
48
+ //# sourceMappingURL=mesh-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-node.d.ts","sourceRoot":"","sources":["../../src/cloudflare/mesh-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,UAAU,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAGnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,QAAS,SAAQ,QAAQ,CACxC,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAE3C,CAAC;AAEH,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GACxB,UAAU,CAAC,qBAAqB,GAChC,WAAW,GACX,UAAU,CAAC,UAAU,CAAC;AAQ1B;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,gJA4C1B,CAAC"}
@@ -1,19 +1,8 @@
1
- /**
2
- * The HomeFlare Cloudflare provider collection, in the shape `LiteLLM.providers()` has.
3
- *
4
- * ★ ONE LAYER, BUILT ONCE, so the single `Config.Redacted('CLOUDFLARE_API_TOKEN')` read is shared
5
- * by every resource in the stack rather than repeated per resource.
6
- *
7
- * ⚠️ THIS IS NOT `alchemy/Cloudflare`'s COLLECTION AND DOES NOT REPLACE IT. A stack that declares
8
- * both a `Cloudflare.R2.Bucket` and this lock merges the two:
9
- * `Layer.mergeAll(Cloudflare.providers(), HomeflareCloudflare.providers())` — the shape
10
- * a consuming stack names for one talking to more than one account.
11
- */
12
1
  import * as Provider from 'alchemy/Provider';
13
2
  import * as Layer from 'effect/Layer';
14
3
  declare const Providers_base: Provider.ProviderCollection<Providers, "HomeflareCloudflare">;
15
4
  export declare class Providers extends Providers_base {
16
5
  }
17
- export declare const providers: () => Layer.Layer<import("./client.ts").CloudflareApi | Providers, never, never>;
6
+ export declare const providers: () => Layer.Layer<import("./client.ts").CloudflareApi | Providers, never, import("alchemy").AuthProviders | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/unstable/http/HttpClient").HttpClient | import("effect/Path").Path>;
18
7
  export {};
19
8
  //# sourceMappingURL=providers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/cloudflare/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;AAItC,qBAAa,SAAU,SAAQ,cAA+D;CAAG;AAEjG,eAAO,MAAM,SAAS,kFAInB,CAAC"}
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/cloudflare/providers.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;AAKtC,qBAAa,SAAU,SAAQ,cAA+D;CAAG;AAEjG,eAAO,MAAM,SAAS,sVAWnB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@
9
9
  * import { BaoMount } from '@homeflare/alchemy/openbao';
10
10
  * import { TalosCluster } from '@homeflare/alchemy/talos';
11
11
  * import { ProxmoxAcl } from '@homeflare/alchemy/proxmox';
12
+ * import { LaunchdJob } from '@homeflare/alchemy/launchd';
13
+ * import { CaddyConfig } from '@homeflare/alchemy/caddy';
12
14
  *
13
15
  * ★ WHY THESE EXIST AT ALL. When Alchemy has no resource for something, the alternative
14
16
  * is a runbook step a human runs once — and a plan can never show a missing runbook
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ExecResult, FileStat, HostRunner, HostUser } from './runner.ts';
2
+ type Entry = {
3
+ bytes: Uint8Array;
4
+ mode: number;
5
+ uid: number;
6
+ gid: number;
7
+ kind: FileStat['kind'];
8
+ };
9
+ export type FakeOptions = {
10
+ euid?: number;
11
+ privileged?: boolean;
12
+ /** Directories that exist, and their owner uid. */
13
+ dirs?: Record<string, number>;
14
+ users?: Record<string, HostUser>;
15
+ groups?: Record<string, number>;
16
+ };
17
+ export declare const fakeRunner: (options?: FakeOptions) => {
18
+ calls: string[][];
19
+ disabled: Set<string>;
20
+ dirs: Map<string, number>;
21
+ files: Map<string, Entry>;
22
+ loaded: Map<string, number | undefined>;
23
+ runner: HostRunner;
24
+ state: {
25
+ lingerPrints: number;
26
+ nextPid: number;
27
+ bootstrapFailure: ExecResult | undefined;
28
+ /** Returned by bootout in place of success. The job still goes, unless `bootoutKeepsJob`. */
29
+ bootoutFailure: ExecResult | undefined;
30
+ bootoutKeepsJob: boolean;
31
+ /** gui uids with no login session: every launchctl call into `gui/<uid>` exits 112. */
32
+ loggedOut: Set<number>;
33
+ };
34
+ };
35
+ export {};
36
+ //# sourceMappingURL=fake-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-runner.d.ts","sourceRoot":"","sources":["../../src/launchd/fake-runner.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAgB,MAAM,aAAa,CAAC;AAE5F,KAAK,KAAK,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAEnG,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AA0BF,eAAO,MAAM,UAAU,aAAa,WAAW;;;;;;;;;;0BAaZ,UAAU,GAAG,SAAS;QACrD,6FAA6F;wBAChE,UAAU,GAAG,SAAS;;QAEnD,uFAAuF;;;CA4G1F,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The one place a HostRunner's promises become Effects — plus the one probe of an unresolved
3
+ * `news` both providers' diffs share.
4
+ *
5
+ * ★ The lifecycle files are plain async functions over a HostRunner (see runner.ts for why the
6
+ * runner is promise-based), so each provider handler is a single `lift(() => step(runner, …))`.
7
+ * ⚠️ A thrown Error stays that Error — its message is the refusal a deploy prints. Anything else
8
+ * thrown is wrapped, so the failure channel is always an Error with a readable message.
9
+ */
10
+ import * as Effect from 'effect/Effect';
11
+ export declare const lift: <A>(step: () => Promise<A>) => Effect.Effect<A, Error>;
12
+ /**
13
+ * A string prop of a diff's `news`, when that one prop is already resolved; `undefined` while it
14
+ * is still an Output (a function-typed proxy), an Effect or a Config.
15
+ * ★ For the identity props a diff must see even when the rest of `news` is unresolved — a plain
16
+ * string is resolved by definition, so `typeof` is the whole test.
17
+ */
18
+ export declare const resolvedString: (news: unknown, key: string) => string | undefined;
19
+ //# sourceMappingURL=host-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-effect.d.ts","sourceRoot":"","sources":["../../src/launchd/host-effect.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,eAAO,MAAM,IAAI,GAAI,CAAC,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAInE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,cAAc,SAAU,OAAO,OAAO,MAAM,KAAG,MAAM,GAAG,SAIpE,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface HostFileProps {
2
+ /** Absolute path. The parent directory must already exist. */
3
+ path: string;
4
+ /** UTF-8 text. ⛔ Never a secret — see the file header. */
5
+ content: string;
6
+ /** Permission bits. @default 0o644 */
7
+ mode?: number;
8
+ /** User name or numeric uid. Omitted: whoever the runner writes as (a rewrite keeps no owner). */
9
+ owner?: string | number;
10
+ /** Group name or numeric gid. Omitted: the directory's default group. */
11
+ group?: string | number;
12
+ }
13
+ export interface HostFileAttributes {
14
+ path: string;
15
+ /** SHA-256 of the bytes on disk. */
16
+ sha256: string;
17
+ mode: number;
18
+ uid: number;
19
+ gid: number;
20
+ size: number;
21
+ }
22
+ export declare const DEFAULT_MODE = 420;
23
+ export declare const fileProblems: (props: HostFileProps) => string[];
24
+ //# sourceMappingURL=host-file-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-file-form.d.ts","sourceRoot":"","sources":["../../src/launchd/host-file-form.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,YAAY,MAAQ,CAAC;AAclC,eAAO,MAAM,YAAY,UAAW,aAAa,KAAG,MAAM,EAsBzD,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * HostFile's read / diff / reconcile / delete as plain async functions over a HostRunner, so the
3
+ * lifecycle runs against fake-runner.ts in tests.
4
+ *
5
+ * ⛔ SYMLINKS AND DIRECTORIES ARE REFUSED, NOT REPLACED. rename(2) over a symlink replaces the LINK,
6
+ * so a path that is a symlink (nix-darwin's /etc entries point into /nix/store) would be quietly
7
+ * taken from the tool that owns it — which puts it back, or refuses its next activation. Remove
8
+ * the path from its owner first, then declare it here.
9
+ */
10
+ import type { Diff } from 'alchemy/Diff';
11
+ import { type HostFileAttributes, type HostFileProps } from './host-file-form.ts';
12
+ import { type HostRunner } from './runner.ts';
13
+ type Desired = {
14
+ readonly bytes: Uint8Array;
15
+ readonly sha256: string;
16
+ readonly mode: number;
17
+ readonly uid?: number;
18
+ readonly gid?: number;
19
+ };
20
+ /** Validate, then resolve owner/group names to ids through the runner. */
21
+ export declare const desiredFile: (runner: HostRunner, props: HostFileProps) => Promise<Desired>;
22
+ /** The file as it is now, or `undefined` when nothing is at the path. Non-files read with no digest. */
23
+ export declare const readFileAttributes: (runner: HostRunner, path: string) => Promise<HostFileAttributes | undefined>;
24
+ export declare const diffFile: (runner: HostRunner, news: HostFileProps, output: HostFileAttributes) => Promise<Diff>;
25
+ export declare const reconcileFile: (runner: HostRunner, props: HostFileProps, output?: HostFileAttributes) => Promise<HostFileAttributes>;
26
+ /** Remove the file. Idempotent. ⛔ Refuses a path that has since become a symlink or directory. */
27
+ export declare const deleteFile: (runner: HostRunner, output: HostFileAttributes) => Promise<void>;
28
+ export {};
29
+ //# sourceMappingURL=host-file-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-file-lifecycle.d.ts","sourceRoot":"","sources":["../../src/launchd/host-file-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D,KAAK,OAAO,GAAG;IACb,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAOF,0EAA0E;AAC1E,eAAO,MAAM,WAAW,WAAkB,UAAU,SAAS,aAAa,KAAG,OAAO,CAAC,OAAO,CAqB3F,CAAC;AAEF,wGAAwG;AACxG,eAAO,MAAM,kBAAkB,WACrB,UAAU,QACZ,MAAM,KACX,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAYxC,CAAC;AAQF,eAAO,MAAM,QAAQ,WACX,UAAU,QACZ,aAAa,UACX,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,eAAO,MAAM,aAAa,WAChB,UAAU,SACX,aAAa,WACX,kBAAkB,KAC1B,OAAO,CAAC,kBAAkB,CA0D5B,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,UAAU,WAAkB,UAAU,UAAU,kBAAkB,KAAG,OAAO,CAAC,IAAI,CAK7F,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `Host.File` — one text file on the host, declared: content, mode, owner, group.
3
+ *
4
+ * - create / update — atomic write (temp file in the same directory, mode and owner set, rename),
5
+ * skipped when the file already matches; then read back and compared.
6
+ * - read — lstat plus the SHA-256 of the bytes.
7
+ * - diff — declared SHA-256 against the stored and on-disk digest, plus mode and owner.
8
+ * - replace — only when `path` changes; create-before-delete.
9
+ * - delete — removes the file.
10
+ *
11
+ * ⛔ NEVER A SECRET — see host-file-form.ts. Secret files stay rendered by openbao-agent.
12
+ * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt`: a file already at the path reads as `Unowned`.
13
+ * ★ IT LIVES IN THE launchd SUBPATH because the two travel together: a Mac host stack declares a
14
+ * daemon's config file and the daemon that reads it, through the same HostRunner.
15
+ */
16
+ import { Resource } from 'alchemy';
17
+ import * as Provider from 'alchemy/Provider';
18
+ import type { HostFileAttributes, HostFileProps } from './host-file-form.ts';
19
+ import { HostRunnerService } from './runner.ts';
20
+ export type { HostFileAttributes, HostFileProps } from './host-file-form.ts';
21
+ export interface HostFile extends Resource<'Host.File', HostFileProps, HostFileAttributes> {
22
+ }
23
+ export declare const HostFile: import("alchemy").ResourceClass<HostFile>;
24
+ export declare const HostFileProvider: () => import("effect/Layer").Layer<Provider.Provider<HostFile>, never, HostRunnerService>;
25
+ //# sourceMappingURL=host-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-file.d.ts","sourceRoot":"","sources":["../../src/launchd/host-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,WAAW,QAAS,SAAQ,QAAQ,CAAC,WAAW,EAAE,aAAa,EAAE,kBAAkB,CAAC;CAAG;AAE7F,eAAO,MAAM,QAAQ,2CAAkC,CAAC;AAExD,eAAO,MAAM,gBAAgB,2FAgC1B,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * User and group lookups, as argv plus a parser — split out so the parsing is tested against the
3
+ * real output shapes without running anything.
4
+ *
5
+ * ★ macOS FIRST, getent ELSEWHERE. macOS has no `getent`; its directory service answers through
6
+ * `dscacheutil`. Shape MEASURED 2026-09-21 on macOS 27.2 (read-only query):
7
+ *
8
+ * name: someone
9
+ * password: ********
10
+ * uid: 501
11
+ * gid: 20
12
+ * dir: /Users/someone
13
+ * shell: /bin/zsh
14
+ *
15
+ * An unknown uid printed NOTHING and exited 0 — so "no record" is an empty parse, never an
16
+ * exit code. `getent` exits 2 for an unknown key; that also parses as no record.
17
+ * ⛔ NEVER PARSE OR KEEP THE `password:` LINE. It is a placeholder on macOS, but a parser that
18
+ * copies every field would carry a hash from any host where it is not.
19
+ */
20
+ import type { HostUser } from './runner.ts';
21
+ /** argv for looking up a user, by name or numeric id. */
22
+ export declare const userQuery: (platform: string, nameOrId: string) => readonly string[];
23
+ /** argv for looking up a group, by name or numeric id. */
24
+ export declare const groupQuery: (platform: string, nameOrId: string) => readonly string[];
25
+ export declare const parseUser: (platform: string, stdout: string) => HostUser | undefined;
26
+ export declare const parseGroup: (platform: string, stdout: string) => number | undefined;
27
+ //# sourceMappingURL=host-lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-lookup.d.ts","sourceRoot":"","sources":["../../src/launchd/host-lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,yDAAyD;AACzD,eAAO,MAAM,SAAS,aAAc,MAAM,YAAY,MAAM,KAAG,SAAS,MAAM,EAU1C,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,UAAU,aAAc,MAAM,YAAY,MAAM,KAAG,SAAS,MAAM,EAU5C,CAAC;AAsBpC,eAAO,MAAM,SAAS,aAAc,MAAM,UAAU,MAAM,KAAG,QAAQ,GAAG,SAkBvE,CAAC;AAEF,eAAO,MAAM,UAAU,aAAc,MAAM,UAAU,MAAM,KAAG,MAAM,GAAG,SAItE,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * launchd providers for Alchemy — declare a Mac host's jobs and their config files.
3
+ *
4
+ * ⛔ THIS BARREL IS THE PUBLIC API, AND IT IS DELIBERATELY SMALLER THAN THE DIRECTORY. The
5
+ * lifecycle files, the launchctl parsers and the fake runner are internals; an `export *` would
6
+ * publish them as API and make the next refactor a breaking change.
7
+ * ⛔ macOS HOSTS ONLY for LaunchdJob. HostFile and the runner work on any POSIX host, but the
8
+ * subpath exists for the Mac: see docs/launchd.md for the cutover from nix-darwin.
9
+ */
10
+ export type { HostFileAttributes, HostFileProps } from './host-file.ts';
11
+ export { HostFile, HostFileProvider } from './host-file.ts';
12
+ export type { CalendarInterval, KeepAlive, LaunchdDomain, LaunchdJobAttributes, LaunchdJobProps, } from './job.ts';
13
+ export { LaunchdJob, LaunchdJobProvider } from './job.ts';
14
+ export type { LocalRunnerOptions } from './local-runner.ts';
15
+ export { localRunner } from './local-runner.ts';
16
+ export type { PlistDict, PlistValue } from './plist.ts';
17
+ export { PlistError, renderPlist } from './plist.ts';
18
+ export { launchdProviders } from './providers.ts';
19
+ export type { ExecResult, FileStat, HostRunner, HostUser, WriteOptions } from './runner.ts';
20
+ export { HostRunnerService, canActAsRoot, hostRunnerLayer } from './runner.ts';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/launchd/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}