@homeflare/alchemy 0.20.0 → 0.21.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 (172) hide show
  1. package/README.md +25 -0
  2. package/dist/caddy/access-forward-auth.d.ts +56 -0
  3. package/dist/caddy/access-forward-auth.d.ts.map +1 -0
  4. package/dist/caddy/index.d.ts +6 -0
  5. package/dist/caddy/index.d.ts.map +1 -1
  6. package/dist/caddy/index.js +169 -75
  7. package/dist/caddy/index.js.map +10 -7
  8. package/dist/cloudflare/mcp-server-api.d.ts +135 -0
  9. package/dist/cloudflare/mcp-server-api.d.ts.map +1 -0
  10. package/dist/cloudflare/mcp-server-form.d.ts +116 -0
  11. package/dist/cloudflare/mcp-server-form.d.ts.map +1 -0
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/launchd/file-converge.d.ts +70 -0
  15. package/dist/launchd/file-converge.d.ts.map +1 -0
  16. package/dist/launchd/file-identity.d.ts +32 -0
  17. package/dist/launchd/file-identity.d.ts.map +1 -0
  18. package/dist/launchd/host-file-form.d.ts +2 -0
  19. package/dist/launchd/host-file-form.d.ts.map +1 -1
  20. package/dist/launchd/host-file-lifecycle.d.ts +11 -24
  21. package/dist/launchd/host-file-lifecycle.d.ts.map +1 -1
  22. package/dist/launchd/host-file.d.ts.map +1 -1
  23. package/dist/launchd/index.js +173 -120
  24. package/dist/launchd/index.js.map +12 -10
  25. package/dist/launchd/local-runner.d.ts.map +1 -1
  26. package/dist/launchd/runner.d.ts +17 -2
  27. package/dist/launchd/runner.d.ts.map +1 -1
  28. package/dist/launchd/sudo-runner.d.ts +11 -0
  29. package/dist/launchd/sudo-runner.d.ts.map +1 -1
  30. package/dist/linux/index.js.map +3 -3
  31. package/dist/linux/sudo-listing.d.ts +47 -0
  32. package/dist/linux/sudo-listing.d.ts.map +1 -0
  33. package/dist/proxmox/credential-literals.d.ts +1 -1
  34. package/dist/proxmox/credential-literals.d.ts.map +1 -1
  35. package/dist/proxmox/generated/pbs/nodes-node.d.ts +1 -1
  36. package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -1
  37. package/dist/proxmox/generated/pbs.d.ts +7 -0
  38. package/dist/proxmox/generated/pbs.d.ts.map +1 -1
  39. package/dist/proxmox/generated/pve.d.ts +7 -0
  40. package/dist/proxmox/generated/pve.d.ts.map +1 -1
  41. package/dist/proxmox/index.d.ts +1 -1
  42. package/dist/proxmox/index.d.ts.map +1 -1
  43. package/dist/proxmox/index.js +2 -2
  44. package/dist/proxmox/index.js.map +9 -9
  45. package/dist/proxmox/pbs-notification-target-form.d.ts +1 -1
  46. package/dist/proxmox/pbs-notification-target-form.d.ts.map +1 -1
  47. package/dist/proxmox/pbs-notification-target.d.ts +1 -1
  48. package/dist/proxmox/pbs-notification-target.d.ts.map +1 -1
  49. package/dist/release/archive.d.ts +11 -0
  50. package/dist/release/archive.d.ts.map +1 -0
  51. package/dist/release/binary-claim.d.ts +53 -0
  52. package/dist/release/binary-claim.d.ts.map +1 -0
  53. package/dist/release/binary-diff.d.ts +38 -0
  54. package/dist/release/binary-diff.d.ts.map +1 -0
  55. package/dist/release/binary-form.d.ts +118 -0
  56. package/dist/release/binary-form.d.ts.map +1 -0
  57. package/dist/release/binary-lifecycle.d.ts +55 -0
  58. package/dist/release/binary-lifecycle.d.ts.map +1 -0
  59. package/dist/release/binary-read.d.ts +11 -0
  60. package/dist/release/binary-read.d.ts.map +1 -0
  61. package/dist/release/binary.d.ts +68 -0
  62. package/dist/release/binary.d.ts.map +1 -0
  63. package/dist/release/catalog.d.ts +76 -0
  64. package/dist/release/catalog.d.ts.map +1 -0
  65. package/dist/release/checksums.d.ts +22 -0
  66. package/dist/release/checksums.d.ts.map +1 -0
  67. package/dist/release/declared-pins.d.ts +4 -0
  68. package/dist/release/declared-pins.d.ts.map +1 -0
  69. package/dist/release/download.d.ts +44 -0
  70. package/dist/release/download.d.ts.map +1 -0
  71. package/dist/release/fake-deploy.d.ts +48 -0
  72. package/dist/release/fake-deploy.d.ts.map +1 -0
  73. package/dist/release/fake-engine.d.ts +8 -0
  74. package/dist/release/fake-engine.d.ts.map +1 -0
  75. package/dist/release/fake-install.d.ts +39 -0
  76. package/dist/release/fake-install.d.ts.map +1 -0
  77. package/dist/release/fake-provider.d.ts +58 -0
  78. package/dist/release/fake-provider.d.ts.map +1 -0
  79. package/dist/release/fake-release.d.ts +95 -0
  80. package/dist/release/fake-release.d.ts.map +1 -0
  81. package/dist/release/index.d.ts +24 -0
  82. package/dist/release/index.d.ts.map +1 -0
  83. package/dist/release/index.js +1365 -0
  84. package/dist/release/index.js.map +35 -0
  85. package/dist/release/providers.d.ts +19 -0
  86. package/dist/release/providers.d.ts.map +1 -0
  87. package/dist/release/refused.d.ts +39 -0
  88. package/dist/release/refused.d.ts.map +1 -0
  89. package/dist/release/tar.d.ts +14 -0
  90. package/dist/release/tar.d.ts.map +1 -0
  91. package/dist/release/victoria.d.ts +36 -0
  92. package/dist/release/victoria.d.ts.map +1 -0
  93. package/dist/{proxmox → secrets}/write-only.d.ts.map +1 -1
  94. package/docs/release-binary-catalogs.md +113 -0
  95. package/docs/release-binary-measured.md +163 -0
  96. package/docs/release-binary-refusals.md +57 -0
  97. package/docs/release-binary-upstream.md +163 -0
  98. package/docs/release-binary.md +154 -0
  99. package/package.json +5 -1
  100. package/src/caddy/access-forward-auth.test.ts +63 -0
  101. package/src/caddy/access-forward-auth.ts +120 -0
  102. package/src/caddy/index.ts +10 -0
  103. package/src/caddy/providers.test.ts +4 -0
  104. package/src/cloudflare/mcp-server-api.ts +294 -0
  105. package/src/cloudflare/mcp-server-form.ts +219 -0
  106. package/src/index.ts +1 -0
  107. package/src/launchd/file-converge.ts +233 -0
  108. package/src/launchd/file-identity.ts +69 -0
  109. package/src/launchd/host-file-alias.test.ts +65 -0
  110. package/src/launchd/host-file-form.ts +2 -1
  111. package/src/launchd/host-file-lifecycle.ts +25 -144
  112. package/src/launchd/host-file.ts +8 -2
  113. package/src/launchd/local-runner.test.ts +12 -1
  114. package/src/launchd/local-runner.ts +2 -0
  115. package/src/launchd/runner.ts +17 -2
  116. package/src/launchd/sudo-runner.ts +11 -0
  117. package/src/linux/sudo-listing.ts +122 -0
  118. package/src/proxmox/credential-literals.ts +1 -1
  119. package/src/proxmox/generated/pbs/nodes-node.ts +1 -1
  120. package/src/proxmox/generated/pbs.ts +7 -0
  121. package/src/proxmox/generated/pve.ts +7 -0
  122. package/src/proxmox/index.ts +1 -1
  123. package/src/proxmox/pbs-notification-target-form.ts +1 -1
  124. package/src/proxmox/pbs-notification-target-lifecycle.ts +1 -1
  125. package/src/proxmox/pbs-notification-target-wire.ts +1 -1
  126. package/src/proxmox/pbs-notification-target.ts +1 -1
  127. package/src/release/adopt-parity.test.ts +106 -0
  128. package/src/release/apply-pins.test.ts +128 -0
  129. package/src/release/archive.ts +94 -0
  130. package/src/release/binary-alias.test.ts +109 -0
  131. package/src/release/binary-claim.ts +80 -0
  132. package/src/release/binary-claims.test.ts +47 -0
  133. package/src/release/binary-diff.ts +73 -0
  134. package/src/release/binary-form.ts +229 -0
  135. package/src/release/binary-lifecycle.test.ts +90 -0
  136. package/src/release/binary-lifecycle.ts +202 -0
  137. package/src/release/binary-read.ts +55 -0
  138. package/src/release/binary-refusals.test.ts +192 -0
  139. package/src/release/binary-sudo.test.ts +93 -0
  140. package/src/release/binary.ts +181 -0
  141. package/src/release/catalog.test.ts +96 -0
  142. package/src/release/catalog.ts +175 -0
  143. package/src/release/checksums.test.ts +35 -0
  144. package/src/release/checksums.ts +43 -0
  145. package/src/release/declared-pins.ts +39 -0
  146. package/src/release/download.test.ts +138 -0
  147. package/src/release/download.ts +110 -0
  148. package/src/release/error-channel.test.ts +69 -0
  149. package/src/release/fake-deploy.ts +87 -0
  150. package/src/release/fake-engine.ts +50 -0
  151. package/src/release/fake-install.ts +59 -0
  152. package/src/release/fake-provider.ts +68 -0
  153. package/src/release/fake-release.ts +185 -0
  154. package/src/release/fixtures/victoria/darwin-arm64-assets.txt +22 -0
  155. package/src/release/fixtures/victoria/victoria-logs-darwin-arm64-v1.52.0_checksums.txt +2 -0
  156. package/src/release/fixtures/victoria/victoria-metrics-darwin-arm64-v1.151.0_checksums.txt +2 -0
  157. package/src/release/fixtures/victoria/victoria-traces-darwin-arm64-v0.10.0_checksums.txt +2 -0
  158. package/src/release/fixtures/victoria/vmutils-darwin-arm64-v1.151.0_checksums.txt +8 -0
  159. package/src/release/index.ts +41 -0
  160. package/src/release/plan.test.ts +189 -0
  161. package/src/release/provider-refusals.test.ts +48 -0
  162. package/src/release/providers.test.ts +174 -0
  163. package/src/release/providers.ts +24 -0
  164. package/src/release/recovery-read.test.ts +44 -0
  165. package/src/release/refused.ts +40 -0
  166. package/src/release/tar.test.ts +142 -0
  167. package/src/release/tar.ts +195 -0
  168. package/src/release/victoria.test.ts +108 -0
  169. package/src/release/victoria.ts +154 -0
  170. package/src/{proxmox → secrets}/write-only.ts +6 -0
  171. /package/dist/{proxmox → secrets}/write-only.d.ts +0 -0
  172. /package/src/{proxmox → secrets}/write-only.test.ts +0 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `Cloudflare.McpServer` — its props, its attributes, and the pure `diff` that decides between
3
+ * update and replace.
4
+ *
5
+ * ⛔ THE ATTRIBUTES NEVER HOLD A CREDENTIAL. `auth_credentials` and `client_secret` are write-only
6
+ * at the API (the read endpoints return an `auth_config_summary` that never includes the secret
7
+ * value), and Alchemy's state store is not encrypted — `StateEncoding.ts` TAGS a `Redacted`
8
+ * value and writes the inner value beside the tag, so `Redacted` hides a secret from a log line
9
+ * and from nothing else. So both are declared as `{ fromEnv: 'NAME' }` (secrets/write-only.ts),
10
+ * the NAME lands in state, and the value exists only in the deploying process and on the wire.
11
+ * ★ AND A PLAN STILL NOTICES A CHANGE, because what was last written is remembered as a salted
12
+ * scrypt SEAL, the same mechanism the PBS notification targets use.
13
+ */
14
+ import type { Diff } from 'alchemy/Diff';
15
+ import type { Input } from 'alchemy/Input';
16
+ import type { FromEnv } from '../secrets/write-only.ts';
17
+ import { type McpCapabilityOverride, type McpServerAuthType, type McpServerAuthenticationStatus, McpServerError, type McpServerStatus } from './mcp-server-api.ts';
18
+ export interface McpServerProps {
19
+ /**
20
+ * The entry's id in the account — client-supplied, required, and the identity everything else
21
+ * names. ⛔ Changing it REPLACES the entry, because the id is what the governing Access
22
+ * application's `{ type: 'via_mcp_server_portal', mcpServerId }` destination points at.
23
+ * ★ Required rather than generated: a generated id would have to be read back out of state
24
+ * before that application could be declared, which is the opposite of a declaration.
25
+ */
26
+ readonly serverId: string;
27
+ /** Display name in the portal. Defaults to the id. */
28
+ readonly name?: string;
29
+ /**
30
+ * The FULL upstream MCP endpoint URL, including its path — `https://example.com/mcp`, not
31
+ * `example.com`. ⚠️ THE FIELD IS NAMED `hostname` AND IS NOT ONE; Cloudflare's own API doc
32
+ * calls it "URL of the upstream MCP endpoint". A bare host is refused by `validateMcpServer`
33
+ * rather than sent, because the portal would then fetch `https:///` and report a sync error
34
+ * that reads like the upstream server being down.
35
+ * ⛔ CREATE-ONLY: the update request has no `hostname`, so a change is a replace.
36
+ * ⚠️ The upstream hostname must be PROXIED in Cloudflare DNS for a portal to reach it
37
+ * (developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/mcp-portals/).
38
+ */
39
+ readonly hostname: string;
40
+ /**
41
+ * How the portal authenticates to the upstream server.
42
+ * ⛔ CREATE-ONLY, like `hostname`, and required for that reason — a default would silently fix
43
+ * a choice that can only be changed by replacing the entry.
44
+ * - `oauth` — the upstream server is its own OAuth resource (including one fronted by
45
+ * Cloudflare Access managed OAuth). The portal registers itself; no static secret here.
46
+ * - `bearer` — a static credential, declared as `authCredentials`.
47
+ * - `unauthenticated` — no credential at all.
48
+ */
49
+ readonly authType: McpServerAuthType;
50
+ /**
51
+ * The static credential for `authType: 'bearer'`, by the NAME of the environment variable
52
+ * holding it. Either a raw token, or a JSON object `{"headers":{…}}` for custom headers — which
53
+ * is how an Access service token pair (`cf-access-client-id` / `cf-access-client-secret`) is
54
+ * sent, per Cloudflare's own example.
55
+ */
56
+ readonly authCredentials?: FromEnv;
57
+ /** The pre-registered OAuth client secret for a manual-mode `oauth` entry, by variable name. */
58
+ readonly clientSecret?: FromEnv;
59
+ readonly description?: string;
60
+ /**
61
+ * Use Cloudflare's shared OAuth callback instead of the portal hostname's. Server default off.
62
+ */
63
+ readonly isSharedOauthCallbackEnabled?: boolean;
64
+ /** Route the portal's outbound traffic to this server through the Secure Web Gateway. */
65
+ readonly secureWebGateway?: boolean;
66
+ /** Server-wide tool overrides — rename, re-describe or disable an upstream tool. */
67
+ readonly updatedTools?: readonly McpCapabilityOverride[];
68
+ /** Server-wide prompt overrides. */
69
+ readonly updatedPrompts?: readonly McpCapabilityOverride[];
70
+ /**
71
+ * Re-run the catalog sync on every reconcile, not only after the create.
72
+ * ⚠️ A sync is a WRITE that talks to the upstream server; leaving this off keeps a no-op deploy
73
+ * from waking it. Turn it on for a server whose tool list changes with its own releases.
74
+ */
75
+ readonly resync?: boolean;
76
+ }
77
+ export interface McpServerAttributes {
78
+ readonly serverId: string;
79
+ /** The account the provider environment resolved when the entry was written. */
80
+ readonly accountId: string;
81
+ readonly name: string;
82
+ readonly hostname: string;
83
+ readonly authType: McpServerAuthType;
84
+ readonly description: string | undefined;
85
+ readonly isSharedOauthCallbackEnabled: boolean;
86
+ readonly secureWebGateway: boolean;
87
+ /** Catalog sync state as of the last read or write. It moves on its own. */
88
+ readonly status: McpServerStatus | undefined;
89
+ /** `required` means an administrator must still finish the upstream OAuth flow by hand. */
90
+ readonly authenticationStatus: McpServerAuthenticationStatus | undefined;
91
+ readonly lastSynced: string | undefined;
92
+ readonly createdAt: string | undefined;
93
+ /** `scrypt:<salt>:<digest>` of the credential last written, or `''`. ⛔ Never the value. */
94
+ readonly authCredentialsSeal: string;
95
+ /** `scrypt:<salt>:<digest>` of the client secret last written, or `''`. ⛔ Never the value. */
96
+ readonly clientSecretSeal: string;
97
+ }
98
+ /** ⛔ Fail closed on a declaration the API would accept but nobody meant. */
99
+ export declare const validateMcpServer: (props: McpServerProps) => McpServerError | undefined;
100
+ /**
101
+ * update or replace, from the declaration and what was last written.
102
+ *
103
+ * - Account changed → `replace`, create-first: ids are unique per ACCOUNT, so nothing collides.
104
+ * - `serverId` changed → `replace`, create-first: the new entry's id is free by definition.
105
+ * - `hostname` or `authType` changed → `replace`, and ⛔ `deleteFirst` when the id stays the
106
+ * same, because the new generation must create the SAME id the old one still holds. Create-first
107
+ * there would answer "already exists" — or, worse, a create path that "converged" on the live
108
+ * entry would record a hostname it does not have.
109
+ * - Otherwise → no opinion; the engine compares props.
110
+ *
111
+ * ⚠️ A deleteFirst replace means the entry is briefly absent from the portal, and the Access
112
+ * application that names `mcpServerId` keeps pointing at an id that exists again a moment later.
113
+ * That is the only order the API allows for a create-only field on a client-supplied id.
114
+ */
115
+ export declare const diffMcpServer: (news: Input<McpServerProps>, output: McpServerAttributes | undefined, accountId: string) => Diff | undefined;
116
+ //# sourceMappingURL=mcp-server-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server-form.d.ts","sourceRoot":"","sources":["../../src/cloudflare/mcp-server-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,6BAA6B,EAClC,cAAc,EACd,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;OAEG;IACH,QAAQ,CAAC,4BAA4B,CAAC,EAAE,OAAO,CAAC;IAChD,yFAAyF;IACzF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,oFAAoF;IACpF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACzD,oCAAoC;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,4BAA4B,EAAE,OAAO,CAAC;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,2FAA2F;IAC3F,QAAQ,CAAC,oBAAoB,EAAE,6BAA6B,GAAG,SAAS,CAAC;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,2FAA2F;IAC3F,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,8FAA8F;IAC9F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAkDD,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,UAAW,cAAc,KAAG,cAAc,GAAG,SAsB1E,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,SAClB,KAAK,CAAC,cAAc,CAAC,UACnB,mBAAmB,GAAG,SAAS,aAC5B,MAAM,KAChB,IAAI,GAAG,SAoBT,CAAC"}
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  * import { TalosCluster } from '@homeflare/alchemy/talos';
12
12
  * import { ProxmoxAcl } from '@homeflare/alchemy/proxmox';
13
13
  * import { LaunchdJob } from '@homeflare/alchemy/launchd';
14
+ * import { ReleaseBinary } from '@homeflare/alchemy/release';
14
15
  * import { CaddyConfig } from '@homeflare/alchemy/caddy';
15
16
  *
16
17
  * ★ WHY THESE EXIST AT ALL. When Alchemy has no resource for something, the alternative
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The one path a declared file's bytes take onto a host, shared by every resource that owns ONE
3
+ * whole file: `Host.File` (text it was handed) and `Release.Binary` (bytes it downloaded and
4
+ * verified). Read, diff, converge and delete, as plain async functions over a HostRunner.
5
+ *
6
+ * ★ EXTRACTED FROM host-file-lifecycle.ts, NOT COPIED (2026-09-22). The ownership rules below — a
7
+ * file this resource does not own is never overwritten, a move onto an occupied path stays
8
+ * refused, a failed create is rolled back — were red-teamed on Host.File (docs/ownership.md). A
9
+ * second resource that writes a whole file gets them by calling this, so a fix lands in both.
10
+ * ★ THE BYTES ARE ASKED FOR ONLY WHEN A WRITE IS DUE (`bytesFor`). Host.File has them already; a
11
+ * binary has only a pinned digest until it downloads, and a host whose file already matches
12
+ * must not cost a 120 MB download to prove it.
13
+ * ⛔ SYMLINKS AND DIRECTORIES ARE REFUSED, NOT REPLACED. rename(2) over a symlink replaces the LINK,
14
+ * so a path that is a symlink (nix-darwin's /etc entries point into /nix/store) would be quietly
15
+ * taken from the tool that owns it — which puts it back, or refuses its next activation. Remove
16
+ * the path from its owner first, then declare it here.
17
+ */
18
+ import type { Diff } from 'alchemy/Diff';
19
+ import type { HostFileAttributes } from './host-file-form.ts';
20
+ import { type HostRunner, type WriteOptions } from './runner.ts';
21
+ /** What a whole file must be: where, which bytes (by digest), which mode and owner. */
22
+ export type FileTarget = {
23
+ readonly path: string;
24
+ readonly sha256: string;
25
+ readonly mode: number;
26
+ readonly uid?: number;
27
+ readonly gid?: number;
28
+ };
29
+ /** A family's refusal: `<Type> <path>: <message>`, so a deploy log names the resource that said no. */
30
+ export type Refuse = (path: string, message: string) => Error;
31
+ /** Resolve a declared owner and group (names or ids) to ids through the runner. */
32
+ export declare const resolveIds: (runner: HostRunner, declared: {
33
+ readonly owner?: string | number;
34
+ readonly group?: string | number;
35
+ }, path: string, refuse: Refuse) => Promise<{
36
+ uid?: number;
37
+ gid?: number;
38
+ }>;
39
+ /** The file as it is now, or `undefined` when nothing is at the path. Non-files read with no digest. */
40
+ export declare const readFileAttributes: (runner: HostRunner, path: string) => Promise<HostFileAttributes | undefined>;
41
+ export declare const writeOptionsOf: (want: FileTarget) => WriteOptions;
42
+ export declare const fileMatches: (live: HostFileAttributes, want: FileTarget) => boolean;
43
+ /**
44
+ * ★ A PLAN THAT WILL WRITE ASKS THE RUNNER FIRST (`checkWrite`, when it has one): sudoRunner's
45
+ * refusals — a setuid or group-writable root file, a directory under a prefix another user may
46
+ * change — then fail the plan, before any resource is applied, instead of halfway through it.
47
+ */
48
+ export declare const diffTarget: (runner: HostRunner, want: FileTarget, output: HostFileAttributes) => Promise<Diff>;
49
+ export type Converge = {
50
+ readonly want: FileTarget;
51
+ /** The owner as declared (a name or an id), for the chown refusal's wording. */
52
+ readonly owner?: string | number | undefined;
53
+ /** The bytes to write, asked for only when a write is due; `before` is what is there now. */
54
+ readonly bytesFor: (before: HostFileAttributes | undefined) => Promise<Uint8Array>;
55
+ readonly refuse: Refuse;
56
+ };
57
+ /**
58
+ * Make the file at `want.path` match `want`, and return it as read back.
59
+ *
60
+ * `adopt` is whether adoption is on AND this apply is a create or an unfinished generation of our
61
+ * own (ownership/adopt.ts adoptsAtApply). It lets such a generation take over a file already at the
62
+ * path — the takeover the plan's probe would have allowed, had it run. ⛔ A move onto an occupied
63
+ * path stays refused, both ways the engine drives one: an `update` across the move (`moved`,
64
+ * below), and a fresh replace's new generation, which the caller never marks adoptable (its
65
+ * `output` is undefined too, so this function cannot tell it from a create by itself).
66
+ */
67
+ export declare const convergeFile: (runner: HostRunner, spec: Converge, output?: HostFileAttributes, adopt?: boolean) => Promise<HostFileAttributes>;
68
+ /** Remove the file. Idempotent. ⛔ Refuses a path that has since become a symlink or directory. */
69
+ export declare const removeWholeFile: (runner: HostRunner, output: HostFileAttributes, refuse: Refuse) => Promise<void>;
70
+ //# sourceMappingURL=file-converge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-converge.d.ts","sourceRoot":"","sources":["../../src/launchd/file-converge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAgB,MAAM,aAAa,CAAC;AAE/E,uFAAuF;AACvF,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,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;AAEF,uGAAuG;AACvG,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;AAK9D,mFAAmF;AACnF,eAAO,MAAM,UAAU,WACb,UAAU,YACR;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,QAC1E,MAAM,UACJ,MAAM,KACb,OAAO,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAYxC,CAAC;AAEF,wGAAwG;AACxG,eAAO,MAAM,kBAAkB,WACrB,UAAU,QACZ,MAAM,KACX,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAYxC,CAAC;AAEF,eAAO,MAAM,cAAc,SAAU,UAAU,KAAG,YAIhD,CAAC;AAEH,eAAO,MAAM,WAAW,SAAU,kBAAkB,QAAQ,UAAU,KAAG,OAItB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,UAAU,WACb,UAAU,QACZ,UAAU,UACR,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAkBd,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7C,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,WACf,UAAU,QACZ,QAAQ,WACL,kBAAkB,sBAE1B,OAAO,CAAC,kBAAkB,CA+E5B,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,eAAe,WAClB,UAAU,UACV,kBAAkB,UAClB,MAAM,KACb,OAAO,CAAC,IAAI,CAKd,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Whether two paths name ONE file — the question a move must answer before it deletes its old
3
+ * path, and a plan before it answers `replace`.
4
+ *
5
+ * 🔴 MEASURED 2026-09-22 (release/binary-alias.test.ts, localRunner over a real temp directory,
6
+ * through Alchemy's own Plan and Apply): a Release.Binary whose directory was respelled to
7
+ * another path for the SAME file — through a symlinked parent (`/tmp` → `/private/tmp`, `/etc`
8
+ * → `/private/etc`), or by case alone on case-insensitive APFS, macOS's default — planned a
9
+ * `replace`. The new generation took the file already there as its own (same bytes, same mode:
10
+ * file-converge.ts's resume rule), then Phase 2 deleted the old generation's path, which was that
11
+ * same file. The deploy SUCCEEDED and the binary was gone. The `update` route lost it in one step:
12
+ * convergeFile's move removed the old path after "writing" the new one. Host.File shares both.
13
+ * ★ IDENTITY, NOT SPELLING. Case-folding the strings would be wrong on a case-sensitive volume and
14
+ * blind to a symlinked parent; device and inode from lstat are what the kernel itself compares.
15
+ * ⚠️ A HARD LINK also shares an inode. At the old path of a move it is therefore kept, not removed:
16
+ * one stray link left behind, rather than the risk of deleting the file just verified.
17
+ */
18
+ import type { FileStat, HostRunner } from './runner.ts';
19
+ /**
20
+ * `true` when both entries exist and are one file; `false` when either is absent or they differ;
21
+ * `undefined` when the runner reported no identity, so nobody can tell (fakes, custom runners).
22
+ */
23
+ export declare const sameEntry: (a: FileStat | undefined, b: FileStat | undefined) => boolean | undefined;
24
+ /** Whether `a` and `b` are one file on this host right now. Equal strings always are. */
25
+ export declare const oneFile: (runner: HostRunner, a: string, b: string) => Promise<boolean | undefined>;
26
+ /**
27
+ * The last step of a move: `remove` the old path unless it is the file just verified at `path`.
28
+ * ⚠️ A runner that reports no identity cannot be asked beforehand, so `path` is checked after: a
29
+ * file lost to a respelling then fails the deploy instead of reporting success.
30
+ */
31
+ export declare const leaveOldPath: (runner: HostRunner, oldPath: string, path: string, remove: () => Promise<void>, refuse: (path: string, message: string) => Error) => Promise<void>;
32
+ //# sourceMappingURL=file-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity.d.ts","sourceRoot":"","sources":["../../src/launchd/file-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,SAAS,MACjB,QAAQ,GAAG,SAAS,KACpB,QAAQ,GAAG,SAAS,KACtB,OAAO,GAAG,SAMZ,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,OAAO,WACV,UAAU,KACf,MAAM,KACN,MAAM,KACR,OAAO,CAAC,OAAO,GAAG,SAAS,CAI7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,WACf,UAAU,WACT,MAAM,QACT,MAAM,UACJ,MAAM,OAAO,CAAC,IAAI,CAAC,UACnB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,KAC/C,OAAO,CAAC,IAAI,CAYd,CAAC"}
@@ -20,6 +20,8 @@ export interface HostFileAttributes {
20
20
  size: number;
21
21
  }
22
22
  export declare const DEFAULT_MODE = 420;
23
+ /** A user or group as declared: a non-negative id, or a name the host could have. */
24
+ export declare const identityProblems: (name: string, value: string | number | undefined) => string[];
23
25
  /**
24
26
  * An absolute, normalised path, or the reasons it is not. ★ Shared with sudo-allowlist.ts, so a
25
27
  * path the sudo runner may touch as root obeys exactly the rule a declared file does.
@@ -1 +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;;;GAGG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,MAAM,EAgBjD,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,aAAa,KAAG,MAAM,EAQzD,CAAC"}
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;AAElC,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,SAAU,MAAM,SAAS,MAAM,GAAG,MAAM,GAAG,SAAS,KAAG,MAAM,EAUzF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,MAAM,EAgBjD,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,aAAa,KAAG,MAAM,EAQzD,CAAC"}
@@ -2,41 +2,28 @@
2
2
  * HostFile's read / diff / reconcile / delete as plain async functions over a HostRunner, so the
3
3
  * lifecycle runs against fake-runner.ts in tests.
4
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.
5
+ * ★ THE CONVERGENCE ITSELF IS file-converge.ts, shared with every resource that owns one whole file
6
+ * (Release.Binary is the second). What stays here is what makes a HOST FILE: text content, its
7
+ * validation (never a secret — host-file-form.ts), and the refusal wording `Host.File <path>: …`.
8
+ * ⛔ SYMLINKS AND DIRECTORIES ARE REFUSED, NOT REPLACED — see file-converge.ts for why.
9
9
  */
10
10
  import type { Diff } from 'alchemy/Diff';
11
+ import { type FileTarget } from './file-converge.ts';
11
12
  import { type HostFileAttributes, type HostFileProps } from './host-file-form.ts';
12
- import { type HostRunner } from './runner.ts';
13
- type Desired = {
13
+ import type { HostRunner } from './runner.ts';
14
+ export { readFileAttributes } from './file-converge.ts';
15
+ type Desired = FileTarget & {
14
16
  readonly bytes: Uint8Array;
15
- readonly sha256: string;
16
- readonly mode: number;
17
- readonly uid?: number;
18
- readonly gid?: number;
19
17
  };
20
18
  /** Validate, then resolve owner/group names to ids through the runner. */
21
19
  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
- /**
25
- * ★ A PLAN THAT WILL WRITE ASKS THE RUNNER FIRST (`checkWrite`, when it has one): sudoRunner's
26
- * refusals — a setuid or group-writable root file, a directory under a prefix another user may
27
- * change — then fail the plan, before any resource is applied, instead of halfway through it.
28
- */
20
+ /** Declared digest, mode and owner against state and the live file — see diffTarget. */
29
21
  export declare const diffFile: (runner: HostRunner, news: HostFileProps, output: HostFileAttributes) => Promise<Diff>;
30
22
  /**
31
- * `adopt` is whether adoption is on AND this apply is a create or an unfinished generation of our
32
- * own (ownership/adopt.ts adoptsAtApply). It lets such a generation take over a file already at the
33
- * path — the takeover the plan's probe would have allowed, had it run. ⛔ A move onto an occupied
34
- * path stays refused, both ways the engine drives one: an `update` across the move (`moved`,
35
- * below), and a fresh replace's new generation, which the caller never marks adoptable (its
36
- * `output` is undefined too, so this function cannot tell it from a create by itself).
23
+ * Write the declared text, unless the file already matches. `adopt` is adoptsAtApply's answer;
24
+ * what it may and may not take over is convergeFile's ⛔.
37
25
  */
38
26
  export declare const reconcileFile: (runner: HostRunner, props: HostFileProps, output?: HostFileAttributes, adopt?: boolean) => Promise<HostFileAttributes>;
39
27
  /** Remove the file. Idempotent. ⛔ Refuses a path that has since become a symlink or directory. */
40
28
  export declare const deleteFile: (runner: HostRunner, output: HostFileAttributes) => Promise<void>;
41
- export {};
42
29
  //# sourceMappingURL=host-file-lifecycle.d.ts.map
@@ -1 +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,EAAmC,MAAM,aAAa,CAAC;AAE/E,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;AAcF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,WACX,UAAU,QACZ,aAAa,UACX,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAad,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,WAChB,UAAU,SACX,aAAa,WACX,kBAAkB,sBAE1B,OAAO,CAAC,kBAAkB,CA0D5B,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,UAAU,WAAkB,UAAU,UAAU,kBAAkB,KAAG,OAAO,CAAC,IAAI,CAK7F,CAAC"}
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,EACL,KAAK,UAAU,EAKhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,OAAO,GAAG,UAAU,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC;AAI3D,0EAA0E;AAC1E,eAAO,MAAM,WAAW,WAAkB,UAAU,SAAS,aAAa,KAAG,OAAO,CAAC,OAAO,CAW3F,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,QAAQ,WACX,UAAU,QACZ,aAAa,UACX,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAAgE,CAAC;AAEhF;;;GAGG;AACH,eAAO,MAAM,aAAa,WAChB,UAAU,SACX,aAAa,WACX,kBAAkB,sBAE1B,OAAO,CAAC,kBAAkB,CAI5B,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,UAAU,WAAY,UAAU,UAAU,kBAAkB,KAAG,OAAO,CAAC,IAAI,CAC/C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"host-file.d.ts","sourceRoot":"","sources":["../../src/launchd/host-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAK7C,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,2FAsC1B,CAAC"}
1
+ {"version":3,"file":"host-file.d.ts","sourceRoot":"","sources":["../../src/launchd/host-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAM7C,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,2FA2C1B,CAAC"}