@homeflare/alchemy 0.7.0 → 0.8.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.
- package/README.md +26 -37
- package/dist/caddy/config-lifecycle.d.ts +49 -3
- package/dist/caddy/config-lifecycle.d.ts.map +1 -1
- package/dist/caddy/config.d.ts +6 -2
- package/dist/caddy/config.d.ts.map +1 -1
- package/dist/caddy/fake-caddy.d.ts +11 -0
- package/dist/caddy/fake-caddy.d.ts.map +1 -1
- package/dist/caddy/index.d.ts +2 -1
- package/dist/caddy/index.d.ts.map +1 -1
- package/dist/caddy/index.js +71 -13
- package/dist/caddy/index.js.map +9 -9
- package/dist/caddy/with-file.d.ts +7 -3
- package/dist/caddy/with-file.d.ts.map +1 -1
- package/dist/launchd/fake-sudo.d.ts +50 -0
- package/dist/launchd/fake-sudo.d.ts.map +1 -0
- package/dist/launchd/host-effect.d.ts +18 -9
- package/dist/launchd/host-effect.d.ts.map +1 -1
- package/dist/launchd/host-file-form.d.ts +5 -0
- package/dist/launchd/host-file-form.d.ts.map +1 -1
- package/dist/launchd/host-file-lifecycle.d.ts.map +1 -1
- package/dist/launchd/index.d.ts +2 -0
- package/dist/launchd/index.d.ts.map +1 -1
- package/dist/launchd/index.js +342 -8
- package/dist/launchd/index.js.map +15 -11
- package/dist/launchd/job-preflight.d.ts.map +1 -1
- package/dist/launchd/job-validate.d.ts +2 -0
- package/dist/launchd/job-validate.d.ts.map +1 -1
- package/dist/launchd/local-runner.d.ts +2 -0
- package/dist/launchd/local-runner.d.ts.map +1 -1
- package/dist/launchd/providers.d.ts +2 -1
- package/dist/launchd/providers.d.ts.map +1 -1
- package/dist/launchd/runner.d.ts +7 -5
- package/dist/launchd/runner.d.ts.map +1 -1
- package/dist/launchd/sudo-allowlist.d.ts +47 -0
- package/dist/launchd/sudo-allowlist.d.ts.map +1 -0
- package/dist/launchd/sudo-guard.d.ts +56 -0
- package/dist/launchd/sudo-guard.d.ts.map +1 -0
- package/dist/launchd/sudo-runner.d.ts +60 -0
- package/dist/launchd/sudo-runner.d.ts.map +1 -0
- package/dist/launchd/sudo-stage.d.ts +7 -0
- package/dist/launchd/sudo-stage.d.ts.map +1 -0
- package/dist/openbao/auth-role-form.d.ts +0 -9
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role.d.ts +3 -2
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/cloudflare-role.d.ts.map +1 -1
- package/dist/openbao/fake-engines-roles.d.ts +32 -0
- package/dist/openbao/fake-engines-roles.d.ts.map +1 -0
- package/dist/openbao/fake-engines.d.ts +8 -2
- package/dist/openbao/fake-engines.d.ts.map +1 -1
- package/dist/openbao/fake-families.d.ts +49 -0
- package/dist/openbao/fake-families.d.ts.map +1 -0
- package/dist/openbao/host-approles.d.ts.map +1 -1
- package/dist/openbao/index.js +359 -280
- package/dist/openbao/index.js.map +21 -19
- package/dist/openbao/jwt-config.d.ts +5 -0
- package/dist/openbao/jwt-config.d.ts.map +1 -1
- package/dist/openbao/jwt-role.d.ts +2 -1
- package/dist/openbao/jwt-role.d.ts.map +1 -1
- package/dist/openbao/kubernetes-role.d.ts +2 -1
- package/dist/openbao/kubernetes-role.d.ts.map +1 -1
- package/dist/openbao/mfa-enforcement.d.ts +6 -3
- package/dist/openbao/mfa-enforcement.d.ts.map +1 -1
- package/dist/openbao/mfa-totp.d.ts.map +1 -1
- package/dist/openbao/pki-role.d.ts.map +1 -1
- package/dist/openbao/plugin.d.ts.map +1 -1
- package/dist/openbao/policy.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts.map +1 -1
- package/dist/openbao/rename-identity.d.ts +69 -0
- package/dist/openbao/rename-identity.d.ts.map +1 -0
- package/dist/openbao/rename.d.ts +13 -12
- package/dist/openbao/rename.d.ts.map +1 -1
- package/dist/openbao/ssh-role-erase.d.ts +11 -0
- package/dist/openbao/ssh-role-erase.d.ts.map +1 -0
- package/dist/openbao/ssh-role.d.ts.map +1 -1
- package/docs/caddy-admin.md +24 -0
- package/docs/caddy.md +73 -54
- package/docs/launchd-sudo.md +122 -0
- package/docs/launchd.md +7 -4
- package/docs/peers.md +39 -0
- package/package.json +1 -1
- package/src/caddy/adopt-scope.test.ts +82 -0
- package/src/caddy/config-adopt.test.ts +235 -0
- package/src/caddy/config-lifecycle.test.ts +31 -53
- package/src/caddy/config-lifecycle.ts +98 -7
- package/src/caddy/config.ts +48 -11
- package/src/caddy/fake-caddy.ts +19 -0
- package/src/caddy/index.ts +2 -1
- package/src/caddy/providers.test.ts +8 -23
- package/src/caddy/with-file.ts +7 -3
- package/src/launchd/fake-sudo.ts +136 -0
- package/src/launchd/host-effect.ts +33 -1
- package/src/launchd/host-file-form.ts +10 -2
- package/src/launchd/host-file-lifecycle.ts +2 -1
- package/src/launchd/index.ts +2 -0
- package/src/launchd/job-preflight.ts +3 -2
- package/src/launchd/job-validate.ts +1 -1
- package/src/launchd/local-runner.ts +6 -3
- package/src/launchd/providers.test.ts +3 -0
- package/src/launchd/providers.ts +2 -1
- package/src/launchd/runner.ts +7 -5
- package/src/launchd/sudo-allowlist.test.ts +191 -0
- package/src/launchd/sudo-allowlist.ts +213 -0
- package/src/launchd/sudo-guard.test.ts +100 -0
- package/src/launchd/sudo-guard.ts +151 -0
- package/src/launchd/sudo-lifecycle.test.ts +172 -0
- package/src/launchd/sudo-runner-files.test.ts +240 -0
- package/src/launchd/sudo-runner.test.ts +126 -0
- package/src/launchd/sudo-runner.ts +235 -0
- package/src/launchd/sudo-stage.ts +28 -0
- package/src/openbao/README.md +8 -2
- package/src/openbao/REPLACE.md +65 -37
- package/src/openbao/auth-role-form.test.ts +2 -16
- package/src/openbao/auth-role-form.ts +0 -11
- package/src/openbao/auth-role.ts +35 -14
- package/src/openbao/cloudflare-role.ts +6 -14
- package/src/openbao/fake-engines-roles.ts +114 -0
- package/src/openbao/fake-engines.ts +18 -11
- package/src/openbao/fake-families.ts +182 -0
- package/src/openbao/host-approles.test.ts +15 -2
- package/src/openbao/host-approles.ts +19 -4
- package/src/openbao/jwt-config.ts +26 -5
- package/src/openbao/jwt-role.ts +24 -8
- package/src/openbao/kubernetes-role.ts +20 -8
- package/src/openbao/mfa-enforcement.ts +23 -8
- package/src/openbao/mfa-totp.ts +21 -8
- package/src/openbao/pki-role.ts +19 -7
- package/src/openbao/plugin.ts +41 -15
- package/src/openbao/policy.ts +7 -7
- package/src/openbao/proxmox-role.ts +7 -16
- package/src/openbao/rename-adoption.test.ts +82 -0
- package/src/openbao/rename-families.test.ts +122 -0
- package/src/openbao/rename-identity.test.ts +129 -0
- package/src/openbao/rename-identity.ts +117 -0
- package/src/openbao/rename.ts +19 -15
- package/src/openbao/ssh-role-erase.ts +50 -0
- package/src/openbao/ssh-role.ts +16 -54
package/README.md
CHANGED
|
@@ -119,17 +119,37 @@ delete-first.
|
|
|
119
119
|
- ⛔ **No silent sudo:** the system domain needs a deploy started as root, or a `privileged` runner.
|
|
120
120
|
- ⚠️ `org.nixos.*` jobs are never adopted. Guide and nix-darwin cutover: [docs/launchd.md](./docs/launchd.md).
|
|
121
121
|
|
|
122
|
+
### Deploying as yourself — `sudoRunner()`
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
launchdProviders(sudoRunner({ prefixes: ['/Library/LaunchDaemons', '/opt/example'] }));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The deploy runs as you. Only `launchctl bootstrap | bootout | kickstart` in the system domain, and
|
|
129
|
+
`install` / `rm` of a file under a declared prefix, go through `sudo -n`, in exact argv shapes. Each
|
|
130
|
+
one is logged (argv only, never content) before it runs. A plan never calls sudo.
|
|
131
|
+
|
|
132
|
+
- ⛔ **Opt-in, never a fallback**, and it never prompts: if sudo needs a password, the call fails at
|
|
133
|
+
once and says to run `sudo -v`, or to grant exactly these commands `NOPASSWD`.
|
|
134
|
+
- ⚠️ Refused before sudo is asked: any other argv, a prefix that is not a real directory only root
|
|
135
|
+
may write, a path outside every prefix that needs root, a symlink on the way, a file you could
|
|
136
|
+
not read back, and another user's `gui/<uid>`.
|
|
137
|
+
- Full list, sudoers cautions and limits: [docs/launchd-sudo.md](./docs/launchd-sudo.md).
|
|
138
|
+
|
|
122
139
|
## Caddy — `@homeflare/alchemy/caddy`
|
|
123
140
|
|
|
124
141
|
`CaddyConfig` declares a running Caddy's config as Caddyfile text, applied through Caddy's own admin
|
|
125
142
|
API: `POST /load` (graceful reload; Caddy keeps the old config if it refuses the new one, and the
|
|
126
|
-
deploy says why), `GET /config/` for drift.
|
|
127
|
-
`
|
|
128
|
-
the transport, `http://127.0.0.1:2019` by default.
|
|
143
|
+
deploy says why), `GET /config/` for drift. `caddyWithFile()` also writes the file Caddy starts from,
|
|
144
|
+
with `HostFile`; `caddyProviders()` provides the transport, `http://127.0.0.1:2019` by default.
|
|
129
145
|
|
|
146
|
+
- ⛔ **Nothing is adopted silently:** a running Caddy whose config is not the declared one plans as
|
|
147
|
+
`Unowned`, and the deploy needs `--adopt`.
|
|
130
148
|
- ⛔ **No secrets in the Caddyfile** (state is unencrypted): `{env.NAME}` / `{file./path}` placeholders.
|
|
131
149
|
- ⛔ **The admin API stays on loopback or a unix socket**, and a Caddyfile that would move it is refused.
|
|
132
|
-
- ⛔ **Delete never unloads or stops Caddy.** Order of file and load
|
|
150
|
+
- ⛔ **Delete never unloads or stops Caddy.** Order of file and load: [docs/caddy.md](./docs/caddy.md).
|
|
151
|
+
- ★ **Managed Caddies run `--resume` with their own `XDG_CONFIG_HOME`**, so a restart runs the last
|
|
152
|
+
config Caddy accepted — and after one, SIGUSR1 has no file to reload. Why, and the rest: same doc.
|
|
133
153
|
|
|
134
154
|
## Credentials
|
|
135
155
|
|
|
@@ -144,12 +164,6 @@ like a bad credential.
|
|
|
144
164
|
|
|
145
165
|
## Peers — and one override you need
|
|
146
166
|
|
|
147
|
-
```sh
|
|
148
|
-
bun add @homeflare/alchemy alchemy@2.0.0-beta.79 effect@4.0.0-rc.115 \
|
|
149
|
-
@effect/platform-node@4.0.0-rc.115 cloudflare@4.5.0 mime@4.1.0 \
|
|
150
|
-
@distilled.cloud/cloudflare@1.0.0-rc.12
|
|
151
|
-
```
|
|
152
|
-
|
|
153
167
|
⚠️ Peers, not dependencies: Alchemy's resource registry and Effect's context both break if
|
|
154
168
|
two copies load in one process.
|
|
155
169
|
|
|
@@ -167,33 +181,8 @@ two copies load in one process.
|
|
|
167
181
|
}
|
|
168
182
|
```
|
|
169
183
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`effect >= 4.0.0-rc.115`; an unlocked `@effect/platform-node-shared` still floats to the
|
|
173
|
-
next rc and breaks at import. Pin the whole set.
|
|
174
|
-
|
|
175
|
-
| what resolves | what happens |
|
|
176
|
-
| ------------------------------------------------------------- | ------------------------------------------------------ |
|
|
177
|
-
| Alchemy 77 + `effect` → rc.115 (measured 2026-09-16) | `TypeError: Config.string is not a function` at import |
|
|
178
|
-
| `@effect/platform-node-shared` newer than the pinned `effect` | `Cannot find module 'effect/ByteSize'` |
|
|
179
|
-
| `rolldown` → 1.2.9 via vite's `~1.2.6` (measured 2026-09-16) | `GET …/rolldown-1.2.9.tgz - 404` at `bun add` |
|
|
180
|
-
| no `mime` (measured 2026-09-17 against Alchemy 78) | `Cannot find package 'mime'` from cloudflare-runtime |
|
|
181
|
-
|
|
182
|
-
Alchemy 78 adapted to rc.115 — that first row is why we used to pin 112, not a reason to
|
|
183
|
-
stay there. The override is still the only thing that holds the set together.
|
|
184
|
-
|
|
185
|
-
⚠️ `@effect/platform-node` is **required, not optional**: Alchemy's module graph reaches
|
|
186
|
-
`Cloudflare/Workers/WorkerBridge → @effect/platform-node/NodeServices` even when you only
|
|
187
|
-
import the Proxmox subpath.
|
|
188
|
-
|
|
189
|
-
⚠️ So is `mime`. Alchemy 78's `@alchemy.run/cloudflare-runtime` imported it without
|
|
190
|
-
declaring it (measured 2026-09-17). 79 declares it; the peer stays so a consumer
|
|
191
|
-
that followed the 78 README does not drop a required line.
|
|
192
|
-
|
|
193
|
-
⚠️ So is `cloudflare`. It was marked optional in 0.1.1, which claimed the `/cloudflare`
|
|
194
|
-
subpath would degrade without it — measured 2026-09-16, the subpath does not load at all:
|
|
195
|
-
`Cannot find package 'cloudflare'`. An optional peer should mean a feature is absent, not
|
|
196
|
-
that an import fails.
|
|
184
|
+
Why each peer is required and each pin exists — the measurements, and the error a missing one
|
|
185
|
+
produces: [docs/peers.md](./docs/peers.md).
|
|
197
186
|
|
|
198
187
|
## License
|
|
199
188
|
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* · live ≠ stored — DRIFT: a hand `curl` to the API, or a restart that loaded a different file
|
|
10
10
|
* (or an `autosave.json` holding someone else's change, under `--resume`).
|
|
11
11
|
* · declared = live — nothing to load, whatever the state says.
|
|
12
|
+
* With NO state there is no stored digest, and the question is whose config is running: see
|
|
13
|
+
* `claimable` below, which both the adoption probe and the apply ask.
|
|
12
14
|
*/
|
|
13
15
|
import type { Diff } from 'alchemy/Diff';
|
|
14
|
-
import type
|
|
16
|
+
import { type CaddyAdmin } from './admin.ts';
|
|
15
17
|
import { type CaddyConfigAttributes, type CaddyConfigProps } from './config-form.ts';
|
|
16
18
|
type Desired = {
|
|
17
19
|
readonly digest: string;
|
|
@@ -26,6 +28,31 @@ type Desired = {
|
|
|
26
28
|
export declare const desiredConfig: (admin: CaddyAdmin, props: CaddyConfigProps) => Promise<Desired>;
|
|
27
29
|
/** What is running now, as attributes. `sourceFile` is carried over, never discovered. */
|
|
28
30
|
export declare const readLive: (admin: CaddyAdmin, sourceFile?: string) => Promise<CaddyConfigAttributes>;
|
|
31
|
+
/**
|
|
32
|
+
* ⛔ WITH NO STATE, A RUNNING CONFIG IS THIS STACK'S ONLY IF IT IS THE DECLARED ONE (decision,
|
|
33
|
+
* 2026-09-21: CaddyConfig must not adopt silently — the house rule HostFile and LaunchdJob keep).
|
|
34
|
+
* Anything else was put there by a person, another tool or another stack, and loading over it
|
|
35
|
+
* replaces every site it serves. ★ A config that serves nothing (`null`, or no apps) is claimable
|
|
36
|
+
* too: there is nothing to take over, as an empty R2 lock rule set reads as no lock.
|
|
37
|
+
*/
|
|
38
|
+
export declare const claimable: (running: unknown, declaredDigest: string) => boolean;
|
|
39
|
+
export type Probe = {
|
|
40
|
+
readonly attributes: CaddyConfigAttributes;
|
|
41
|
+
/** The live config IS the declared one: adopting it changes nothing. */
|
|
42
|
+
readonly ours: boolean;
|
|
43
|
+
/** Why the declaration could not be compared, when it could not — the Caddy is then not ours. */
|
|
44
|
+
readonly unchecked?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The adoption probe — `read` with no state: `undefined` when Caddy serves nothing (plan a create),
|
|
48
|
+
* else the live attributes and whether they are provably ours (the provider brands the rest
|
|
49
|
+
* `Unowned`, and the plan refuses them without `--adopt`).
|
|
50
|
+
* ⛔ IT NEVER THROWS OVER THE DECLARATION. The engine also runs this read to recover an interrupted
|
|
51
|
+
* create, with THAT deploy's props (Plan.ts, `status: "creating"`) — so a Caddyfile that failed to
|
|
52
|
+
* adapt then would fail every later plan, the one carrying the fix included. What cannot be
|
|
53
|
+
* compared is "not proven ours", with the reason; the next diff or apply validates the Caddyfile.
|
|
54
|
+
*/
|
|
55
|
+
export declare const probeLive: (admin: CaddyAdmin, caddyfile: string | undefined, sourceFile?: string) => Promise<Probe | undefined>;
|
|
29
56
|
/**
|
|
30
57
|
* ★ NEVER `replace`. Nothing in the props names a different object: a new Caddyfile is a reload of
|
|
31
58
|
* the same Caddy, and `sourceFile` is a header. (Which Caddy is the transport's business — see
|
|
@@ -39,7 +66,26 @@ export type Applied = {
|
|
|
39
66
|
/** Whether a `POST /load` was sent (false when Caddy already ran this config). */
|
|
40
67
|
readonly loaded: boolean;
|
|
41
68
|
};
|
|
42
|
-
/**
|
|
43
|
-
|
|
69
|
+
/**
|
|
70
|
+
* What this apply may load over — config.ts decides from the state and the adopt setting:
|
|
71
|
+
* · `takeOver` — any running config: the state was applied to THIS Caddy (an update, drift
|
|
72
|
+
* correction, or a create the engine adopted), or adoption is on (`--adopt`, or the
|
|
73
|
+
* resource's own `adopt(true)` — host-effect.ts adoptEnabled).
|
|
74
|
+
* · otherwise only a claimable one — plus, as `stored`, the digest the state last recorded: a
|
|
75
|
+
* Caddy reached at a NEW endpoint that runs exactly that is the same config under another name.
|
|
76
|
+
*/
|
|
77
|
+
export type Authority = {
|
|
78
|
+
readonly takeOver: boolean;
|
|
79
|
+
readonly stored?: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Load the Caddyfile unless Caddy already runs it, then read it back and insist it matches.
|
|
83
|
+
* ⛔ WITHOUT `takeOver`, A CONFIG THE STACK CANNOT CLAIM IS NEVER LOADED OVER. The engine's adoption
|
|
84
|
+
* probe cannot guard every create: it is skipped while `news` holds an Output — ALWAYS on
|
|
85
|
+
* caddyWithFile's first deploy (`sourceFile` is its HostFile's path) — and a Caddy that was down at
|
|
86
|
+
* plan time read as nothing. Nor does state vouch for a Caddy the transport NOW reaches at another
|
|
87
|
+
* endpoint (diff plans that as an update). Refused here, before any `/load`, like HostFile's.
|
|
88
|
+
*/
|
|
89
|
+
export declare const reconcileConfig: (admin: CaddyAdmin, props: CaddyConfigProps, authority: Authority) => Promise<Applied>;
|
|
44
90
|
export {};
|
|
45
91
|
//# sourceMappingURL=config-lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-lifecycle.d.ts","sourceRoot":"","sources":["../../src/caddy/config-lifecycle.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config-lifecycle.d.ts","sourceRoot":"","sources":["../../src/caddy/config-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,KAAK,UAAU,EAAyB,MAAM,YAAY,CAAC;AAQpE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAEtB,MAAM,kBAAkB,CAAC;AAQ1B,KAAK,OAAO,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAcjF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,UACjB,UAAU,SACV,gBAAgB,KACtB,OAAO,CAAC,OAAO,CAUjB,CAAC;AAYF,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,UACZ,UAAU,eACJ,MAAM,KAClB,OAAO,CAAC,qBAAqB,CACiC,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,YAAa,OAAO,kBAAkB,MAAM,KAAG,OACD,CAAC;AAErE,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,UACb,UAAU,aACN,MAAM,GAAG,SAAS,eAChB,MAAM,KAClB,OAAO,CAAC,KAAK,GAAG,SAAS,CAmB3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,UACd,UAAU,QACX,gBAAgB,UACd,qBAAqB,KAC5B,OAAO,CAAC,IAAI,CAYd,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,UACnB,UAAU,SACV,gBAAgB,aACZ,SAAS,KACnB,OAAO,CAAC,OAAO,CA8CjB,CAAC"}
|
package/dist/caddy/config.d.ts
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* - create / update — `POST /adapt` (validate), `POST /load` with `text/caddyfile` (a graceful
|
|
6
6
|
* reload; Caddy keeps the old config if it refuses the new one, and the error names why), then
|
|
7
7
|
* `GET /config/` and insist it matches. Skipped when Caddy already runs this config.
|
|
8
|
-
* - read — the digest of `GET /config/`.
|
|
9
|
-
* config becomes the starting point, and the deploy's forced update loads the declared one.
|
|
8
|
+
* - read — the digest of `GET /config/`. With no state it is the adoption probe (below).
|
|
10
9
|
* - diff — declared, live and stored digests (config-lifecycle.ts). Never `replace`.
|
|
11
10
|
* - delete — ⛔ NOTHING. See below.
|
|
12
11
|
*
|
|
@@ -15,6 +14,11 @@
|
|
|
15
14
|
* servers. `retain` is the default (the engine never calls delete); with `destroy` the handler
|
|
16
15
|
* still only forgets. It never calls `/stop` (which exits the process) or `DELETE /config/`.
|
|
17
16
|
* To empty a Caddy, do it by hand at its admin API.
|
|
17
|
+
* ⛔ NOTHING IS ADOPTED SILENTLY (decision, 2026-09-21 — the house rule HostFile and LaunchdJob
|
|
18
|
+
* keep). With no state, a running Caddy whose live config IS the declared one reads as ours (the
|
|
19
|
+
* adoption changes nothing); any other config reads as `Unowned`, and the plan refuses it unless
|
|
20
|
+
* the deploy runs with `--adopt`. A Caddy serving nothing reads as absent: a create. Where the
|
|
21
|
+
* engine skips that probe, reconcile refuses the same takeover (config-lifecycle.ts).
|
|
18
22
|
* ⛔ `caddyfile` IS NEVER A SECRET — see config-form.ts; refused before anything is sent.
|
|
19
23
|
* ⚠️ WHICH CADDY is the transport's (providers.ts), not a prop: one CaddyAdmin per stack.
|
|
20
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/caddy/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/caddy/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,iBAAiB,EAAyB,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGhF,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AAsBH,eAAO,MAAM,mBAAmB,8FAsF7B,CAAC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* ★ `listenPort` is the port Caddy BELIEVES it listens on — its Host check uses it — while the
|
|
18
18
|
* fake really listens on an ephemeral one: a Caddy on its default :2019 behind a forward.
|
|
19
19
|
*/
|
|
20
|
+
import type { CaddyAdmin } from './admin.ts';
|
|
20
21
|
export type Seen = {
|
|
21
22
|
readonly method: string;
|
|
22
23
|
readonly path: string;
|
|
@@ -40,4 +41,14 @@ export declare const fakeCaddy: (options?: {
|
|
|
40
41
|
origins?: string[];
|
|
41
42
|
listenPort?: number;
|
|
42
43
|
}) => FakeCaddy;
|
|
44
|
+
/**
|
|
45
|
+
* A fake Caddy on its DEFAULT :2019 behind a forward (`hostHeader`), and the transport to it.
|
|
46
|
+
* ★ Addressed that way so a Caddyfile with no `admin` line — the common case — passes the guard.
|
|
47
|
+
* A Caddy reached on any other port needs `admin <address>` declared, or the first load would
|
|
48
|
+
* move it (admin-guard.ts).
|
|
49
|
+
*/
|
|
50
|
+
export declare const fakeDefaultCaddy: (running?: unknown) => {
|
|
51
|
+
admin: CaddyAdmin;
|
|
52
|
+
caddy: FakeCaddy;
|
|
53
|
+
};
|
|
43
54
|
//# sourceMappingURL=fake-caddy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-caddy.d.ts","sourceRoot":"","sources":["../../src/caddy/fake-caddy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACtB,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAqDF,eAAO,MAAM,SAAS,aACX;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KACrF,SAoEF,CAAC"}
|
|
1
|
+
{"version":3,"file":"fake-caddy.d.ts","sourceRoot":"","sources":["../../src/caddy/fake-caddy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACtB,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAqDF,eAAO,MAAM,SAAS,aACX;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KACrF,SAoEF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,aAAc,OAAO,KAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAQzF,CAAC"}
|
package/dist/caddy/index.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* the digest, the guards and the admin calls are internals; an `export *` would publish them.
|
|
7
7
|
* ⛔ THE ADMIN API STAYS ON LOOPBACK OR A UNIX SOCKET. It has no authentication; localCaddyAdmin()
|
|
8
8
|
* refuses any other address, and a Caddyfile that would move or expose it is refused before load.
|
|
9
|
-
*
|
|
9
|
+
* ⛔ NOTHING IS ADOPTED SILENTLY: a running config that is not the declared one needs `--adopt`.
|
|
10
|
+
* Guide, order of file and load, adoption, `--resume` and secrets: docs/caddy.md.
|
|
10
11
|
*/
|
|
11
12
|
export type { CaddyAdmin, CaddyAdminListener, CaddyAdminRequest, CaddyAdminResponse, } from './admin.ts';
|
|
12
13
|
export { CaddyAdminService, CaddyUnreachableError, caddyAdminLayer } from './admin.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caddy/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caddy/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC/D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/caddy/index.js
CHANGED
|
@@ -292,12 +292,17 @@ var loadCaddyfile = async (admin, caddyfile, sourceFile) => {
|
|
|
292
292
|
};
|
|
293
293
|
// src/caddy/config.ts
|
|
294
294
|
import { Resource } from "alchemy";
|
|
295
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
295
296
|
import { isResolved } from "alchemy/Diff";
|
|
296
297
|
import * as Provider from "alchemy/Provider";
|
|
297
298
|
import * as Effect2 from "effect/Effect";
|
|
298
299
|
|
|
299
300
|
// src/launchd/host-effect.ts
|
|
301
|
+
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
302
|
+
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
303
|
+
import { Stack } from "alchemy/Stack";
|
|
300
304
|
import * as Effect from "effect/Effect";
|
|
305
|
+
import * as Option from "effect/Option";
|
|
301
306
|
var lift = (step) => Effect.tryPromise({
|
|
302
307
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
303
308
|
try: step
|
|
@@ -308,6 +313,17 @@ var resolvedString = (news, key) => {
|
|
|
308
313
|
const value = news[key];
|
|
309
314
|
return typeof value === "string" ? value : undefined;
|
|
310
315
|
};
|
|
316
|
+
var adoptEnabled = (fqn) => Effect.gen(function* () {
|
|
317
|
+
const stack = yield* Effect.serviceOption(Stack);
|
|
318
|
+
const scoped = Option.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
319
|
+
if (scoped !== undefined)
|
|
320
|
+
return scoped;
|
|
321
|
+
const policy = yield* Effect.serviceOption(AdoptPolicy);
|
|
322
|
+
if (Option.isSome(policy))
|
|
323
|
+
return policy.value;
|
|
324
|
+
const context = yield* Effect.serviceOption(AlchemyContext);
|
|
325
|
+
return Option.isSome(context) ? context.value.adopt : false;
|
|
326
|
+
});
|
|
311
327
|
|
|
312
328
|
// src/caddy/admin-guard.ts
|
|
313
329
|
var DEFAULT_ADMIN_PORT = 2019;
|
|
@@ -544,20 +560,48 @@ var desiredConfig = async (admin, props) => {
|
|
|
544
560
|
throw refuse(admin, guard.join("; "));
|
|
545
561
|
return { digest: configDigest(adapted.config), warnings: adapted.warnings };
|
|
546
562
|
};
|
|
547
|
-
var
|
|
548
|
-
configSha256: configDigest(
|
|
563
|
+
var attributesOf = (admin, running, sourceFile) => ({
|
|
564
|
+
configSha256: configDigest(running),
|
|
549
565
|
endpoint: admin.endpoint,
|
|
550
566
|
...sourceFile === undefined ? {} : { sourceFile }
|
|
551
567
|
});
|
|
568
|
+
var readLive = async (admin, sourceFile) => attributesOf(admin, await readRunningConfig(admin), sourceFile);
|
|
569
|
+
var claimable = (running, declaredDigest) => servesNothing(running) || configDigest(running) === declaredDigest;
|
|
570
|
+
var probeLive = async (admin, caddyfile, sourceFile) => {
|
|
571
|
+
const running = await readRunningConfig(admin);
|
|
572
|
+
if (servesNothing(running))
|
|
573
|
+
return;
|
|
574
|
+
const attributes = attributesOf(admin, running, sourceFile);
|
|
575
|
+
if (caddyfile === undefined) {
|
|
576
|
+
return { attributes, ours: false, unchecked: "the stored Caddyfile is not plain text" };
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
const want = await desiredConfig(admin, { caddyfile });
|
|
580
|
+
return { attributes, ours: claimable(running, want.digest) };
|
|
581
|
+
} catch (cause) {
|
|
582
|
+
if (cause instanceof CaddyUnreachableError)
|
|
583
|
+
throw cause;
|
|
584
|
+
return {
|
|
585
|
+
attributes,
|
|
586
|
+
ours: false,
|
|
587
|
+
unchecked: cause instanceof Error ? cause.message : String(cause)
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
};
|
|
552
591
|
var diffConfig = async (admin, news, output) => {
|
|
553
592
|
const want = await desiredConfig(admin, news);
|
|
554
593
|
const live = await readLive(admin);
|
|
555
594
|
const converged = want.digest === live.configSha256 && live.configSha256 === output.configSha256 && news.sourceFile === output.sourceFile && admin.endpoint === output.endpoint;
|
|
556
595
|
return converged ? { action: "noop" } : { action: "update" };
|
|
557
596
|
};
|
|
558
|
-
var reconcileConfig = async (admin, props) => {
|
|
597
|
+
var reconcileConfig = async (admin, props, authority) => {
|
|
559
598
|
const want = await desiredConfig(admin, props);
|
|
560
|
-
const
|
|
599
|
+
const running = await readRunningConfig(admin);
|
|
600
|
+
const before = configDigest(running);
|
|
601
|
+
const known = before === authority.stored || claimable(running, want.digest);
|
|
602
|
+
if (!authority.takeOver && !known) {
|
|
603
|
+
throw refuse(admin, `Caddy runs config ${short(before)}, which this stack did not load, and the Caddyfile adapts ` + `to ${short(want.digest)} — loading it would replace every site that config serves. ` + "Deploy with --adopt (or wrap the resource in adopt(true)) to take this Caddy over, or " + "point caddyProviders() at the Caddy you meant.");
|
|
604
|
+
}
|
|
561
605
|
let warnings = want.warnings;
|
|
562
606
|
const loaded = before !== want.digest;
|
|
563
607
|
if (loaded) {
|
|
@@ -588,9 +632,17 @@ var unreachable = (error) => error instanceof CaddyUnreachableError;
|
|
|
588
632
|
var planWithoutCaddy = (admin, fallback) => (error) => Effect2.as(Effect2.logWarning(`Caddy.Config: no Caddy at ${admin.endpoint} (${error.message}); planning without it — ` + "the Caddyfile is validated when the deploy reaches Caddy"), fallback);
|
|
589
633
|
var CaddyConfigProvider = () => Provider.effect(CaddyConfig, Effect2.gen(function* () {
|
|
590
634
|
const admin = yield* CaddyAdminService;
|
|
635
|
+
const probe = (caddyfile, sourceFile) => Effect2.flatMap(lift(() => probeLive(admin, caddyfile, sourceFile)), (found) => {
|
|
636
|
+
if (found === undefined)
|
|
637
|
+
return Effect2.succeed(undefined);
|
|
638
|
+
if (found.ours)
|
|
639
|
+
return Effect2.succeed(found.attributes);
|
|
640
|
+
const unowned = Unowned(found.attributes);
|
|
641
|
+
return found.unchecked === undefined ? Effect2.succeed(unowned) : Effect2.as(Effect2.logWarning(`Caddy.Config at ${admin.endpoint}: the running config could not be compared ` + `with the declared Caddyfile (${found.unchecked}), so it is not treated as ours`), unowned);
|
|
642
|
+
});
|
|
591
643
|
return CaddyConfig.Provider.of({
|
|
592
644
|
list: () => Effect2.succeed([]),
|
|
593
|
-
read: ({ olds }) => lift(() => readLive(admin, resolvedString(olds, "sourceFile"))).pipe(Effect2.catchIf(unreachable, planWithoutCaddy(admin, undefined))),
|
|
645
|
+
read: ({ olds, output }) => (output === undefined ? probe(resolvedString(olds, "caddyfile"), resolvedString(olds, "sourceFile")) : lift(() => readLive(admin, resolvedString(olds, "sourceFile")))).pipe(Effect2.catchIf(unreachable, planWithoutCaddy(admin, undefined))),
|
|
594
646
|
diff: ({ news, output }) => {
|
|
595
647
|
if (output === undefined)
|
|
596
648
|
return Effect2.succeed(undefined);
|
|
@@ -606,8 +658,11 @@ var CaddyConfigProvider = () => Provider.effect(CaddyConfig, Effect2.gen(functio
|
|
|
606
658
|
return { action: "update" };
|
|
607
659
|
}).pipe(Effect2.catchIf(unreachable, update));
|
|
608
660
|
},
|
|
609
|
-
reconcile: ({ news }) => Effect2.gen(function* () {
|
|
610
|
-
const
|
|
661
|
+
reconcile: ({ fqn, news, output }) => Effect2.gen(function* () {
|
|
662
|
+
const sameCaddy = output !== undefined && output.endpoint === admin.endpoint;
|
|
663
|
+
const takeOver = sameCaddy || (yield* adoptEnabled(fqn));
|
|
664
|
+
const stored = output === undefined ? {} : { stored: output.configSha256 };
|
|
665
|
+
const applied = yield* lift(() => reconcileConfig(admin, news, { takeOver, ...stored }));
|
|
611
666
|
for (const warning of applied.warnings) {
|
|
612
667
|
yield* Effect2.logWarning(`Caddy.Config at ${admin.endpoint}: ${warning}`);
|
|
613
668
|
}
|
|
@@ -716,7 +771,7 @@ import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
|
716
771
|
|
|
717
772
|
// src/launchd/host-file.ts
|
|
718
773
|
import { Resource as Resource2 } from "alchemy";
|
|
719
|
-
import { Unowned } from "alchemy/AdoptPolicy";
|
|
774
|
+
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
720
775
|
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
721
776
|
import * as Provider2 from "alchemy/Provider";
|
|
722
777
|
import * as Effect3 from "effect/Effect";
|
|
@@ -731,9 +786,8 @@ var identityProblems = (name, value) => {
|
|
|
731
786
|
}
|
|
732
787
|
return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\d+$/.test(value) ? [] : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];
|
|
733
788
|
};
|
|
734
|
-
var
|
|
789
|
+
var pathProblems = (path) => {
|
|
735
790
|
const found = [];
|
|
736
|
-
const { path } = props;
|
|
737
791
|
if (!path.startsWith("/"))
|
|
738
792
|
found.push("path must be absolute");
|
|
739
793
|
if (path.endsWith("/") || path.split("/").slice(1).some((part) => part === "" || part === "." || part === "..")) {
|
|
@@ -741,6 +795,10 @@ var fileProblems = (props) => {
|
|
|
741
795
|
}
|
|
742
796
|
if (path.includes("\x00"))
|
|
743
797
|
found.push("path contains NUL");
|
|
798
|
+
return found;
|
|
799
|
+
};
|
|
800
|
+
var fileProblems = (props) => {
|
|
801
|
+
const found = pathProblems(props.path);
|
|
744
802
|
const mode = props.mode ?? DEFAULT_MODE;
|
|
745
803
|
if (!Number.isSafeInteger(mode) || mode < 0 || mode > 4095)
|
|
746
804
|
found.push("mode must be 0–0o7777");
|
|
@@ -814,7 +872,7 @@ var diffFile = async (runner, news, output) => {
|
|
|
814
872
|
var reconcileFile = async (runner, props, output) => {
|
|
815
873
|
const want = await desiredFile(runner, props);
|
|
816
874
|
if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {
|
|
817
|
-
throw refuse3(props.path, `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy as root ` + "or through a privileged HostRunner (
|
|
875
|
+
throw refuse3(props.path, `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy as root ` + "or through a privileged HostRunner (sudoRunner() with this path under a prefix). This " + "provider never calls sudo itself.");
|
|
818
876
|
}
|
|
819
877
|
const stat = await runner.stat(props.path);
|
|
820
878
|
if (stat !== undefined && stat.kind !== "file") {
|
|
@@ -864,7 +922,7 @@ var HostFileProvider = () => Provider2.effect(HostFile, Effect3.gen(function* ()
|
|
|
864
922
|
const found = await readFileAttributes(runner, olds.path);
|
|
865
923
|
if (found === undefined)
|
|
866
924
|
return;
|
|
867
|
-
return output === undefined ?
|
|
925
|
+
return output === undefined ? Unowned2(found) : found;
|
|
868
926
|
}),
|
|
869
927
|
diff: ({ news, output }) => {
|
|
870
928
|
if (output === undefined)
|
|
@@ -908,5 +966,5 @@ export {
|
|
|
908
966
|
localCaddyAdmin
|
|
909
967
|
};
|
|
910
968
|
|
|
911
|
-
//# debugId=
|
|
969
|
+
//# debugId=7B177B132A41844D64756E2164756E21
|
|
912
970
|
//# sourceMappingURL=index.js.map
|