@penvhq/cli 0.10.0 → 0.11.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/dist/index.js CHANGED
@@ -1,10 +1,20 @@
1
+ import {
2
+ detectPackageManager,
3
+ engineVersion,
4
+ installWithPackageManager,
5
+ noCommand,
6
+ planInstall,
7
+ renderInstallPlan,
8
+ startChild
9
+ } from "./chunk-JJY4RLDJ.js";
10
+
1
11
  // src/index.ts
2
12
  import { setKeychain } from "@penvhq/core";
3
13
  import { runMain as cittyRunMain, defineCommand as defineCommand22 } from "citty";
4
14
 
5
15
  // src/commands/artifact.ts
6
16
  import { mkdirSync, writeFileSync } from "fs";
7
- import { dirname as dirname3, isAbsolute as isAbsolute2, relative, resolve as resolve3 } from "path";
17
+ import { dirname as dirname3, isAbsolute, relative, resolve as resolve3 } from "path";
8
18
  import {
9
19
  ARTIFACT_FORMAT,
10
20
  candidatesFor as candidatesFor2,
@@ -12,7 +22,7 @@ import {
12
22
  formatValueFile as formatValueFile2,
13
23
  isSecret as isSecret2,
14
24
  keySourceIdentifier,
15
- PenvError as PenvError9,
25
+ PenvError as PenvError7,
16
26
  parameterId as parameterId3,
17
27
  serializeArtifact,
18
28
  variableName as variableName3
@@ -20,313 +30,9 @@ import {
20
30
  import { assertDeliverableNames, declaredRefs as declaredRefs2 } from "@penvhq/runtime";
21
31
  import { defineCommand as defineCommand4 } from "citty";
22
32
 
23
- // src/install.ts
24
- import { existsSync as existsSync2, readFileSync } from "fs";
25
- import { join as join2 } from "path";
26
- import { PenvError as PenvError2 } from "@penvhq/core";
27
-
28
- // src/child.ts
29
- import { spawn } from "child_process";
30
- import { existsSync, statSync } from "fs";
31
- import { delimiter, isAbsolute, join, win32 } from "path";
32
- import { PenvError } from "@penvhq/core";
33
- var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
34
- var startChild = (invocation) => {
35
- const [executable, ...args] = invocation.command;
36
- if (executable === void 0) {
37
- throw noCommand();
38
- }
39
- const target = resolveTarget(executable, args, invocation.env);
40
- const child = spawn(target.file, target.args, {
41
- cwd: invocation.cwd,
42
- env: invocation.env,
43
- stdio: "inherit",
44
- ...target.verbatim ? { windowsVerbatimArguments: true } : {}
45
- });
46
- const forward = /* @__PURE__ */ new Map();
47
- for (const signal of FORWARDED) {
48
- const handler = () => {
49
- child.kill(signal);
50
- };
51
- forward.set(signal, handler);
52
- process.on(signal, handler);
53
- }
54
- const release = () => {
55
- for (const [signal, handler] of forward) {
56
- process.off(signal, handler);
57
- }
58
- };
59
- const ended = new Promise((resolve9, reject) => {
60
- child.on("error", (cause) => {
61
- release();
62
- reject(cannotStart(executable, cause, invocation.purpose));
63
- });
64
- child.on("exit", (code, signal) => {
65
- release();
66
- resolve9({ exitCode: code ?? 1, signal });
67
- });
68
- });
69
- return {
70
- ended,
71
- kill(signal) {
72
- child.kill(signal);
73
- }
74
- };
75
- };
76
- function noCommand() {
77
- return new PenvError(
78
- "RUN_NO_COMMAND",
79
- "`penv run` was given no command to start",
80
- "Put the command after `--`, e.g. `penv run -- pnpm dev`."
81
- );
82
- }
83
- function cannotStart(executable, cause, purpose) {
84
- const detail = cause instanceof Error ? cause.message : String(cause);
85
- if (purpose !== void 0) {
86
- return new PenvError(
87
- "PENV_COMMAND_NOT_STARTED",
88
- `penv could not start \`${executable}\` to ${purpose}: ${detail}`,
89
- `Check that \`${executable}\` runs on its own \u2014 penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`
90
- );
91
- }
92
- return new PenvError(
93
- "RUN_COMMAND_NOT_STARTED",
94
- `\`${executable}\` could not be started: ${detail}`,
95
- `Check the command after \`--\` runs on its own \u2014 \`${executable}\` has to be on PATH, exactly as it is spelled here.`
96
- );
97
- }
98
- function resolveTarget(executable, args, env) {
99
- if (process.platform !== "win32") {
100
- return { file: executable, args, verbatim: false };
101
- }
102
- const resolved = findExecutable(executable, env);
103
- if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved)) {
104
- return { file: resolved ?? executable, args, verbatim: false };
105
- }
106
- return {
107
- file: env.ComSpec ?? "cmd.exe",
108
- args: ["/d", "/s", "/c", `"${cmdCommandLine(resolved, args)}"`],
109
- verbatim: true
110
- };
111
- }
112
- var SHIM = /(?:^|\\)node_modules\\\.bin\\[^\\]+\.cmd$/i;
113
- function cmdCommandLine(resolved, args) {
114
- const command = win32.normalize(resolved);
115
- const shim = SHIM.test(command);
116
- return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(
117
- " "
118
- );
119
- }
120
- function extensions(env, platform) {
121
- if (platform !== "win32") {
122
- return [""];
123
- }
124
- const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
125
- return [...declared.split(";").filter((extension) => extension.length > 0), ""];
126
- }
127
- function findExecutable(executable, env, platform = process.platform) {
128
- const candidates = extensions(env, platform);
129
- const isFile = (path2) => existsSync(path2) && statSync(path2).isFile();
130
- if (executable.includes("/") || executable.includes("\\") || isAbsolute(executable)) {
131
- return candidates.map((extension) => executable + extension).find(isFile);
132
- }
133
- const path = env.PATH ?? env.Path ?? "";
134
- for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {
135
- const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);
136
- if (hit !== void 0) {
137
- return hit;
138
- }
139
- }
140
- return void 0;
141
- }
142
- var CMD_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/g;
143
- function escapeCommand(command) {
144
- return command.replace(CMD_METACHARACTERS, "^$1");
145
- }
146
- function escapeArgument(argument, doubleEscape) {
147
- const quoted = `"${argument.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
148
- const escaped = quoted.replace(CMD_METACHARACTERS, "^$1");
149
- return doubleEscape ? escaped.replace(CMD_METACHARACTERS, "^$1") : escaped;
150
- }
151
-
152
- // src/install.ts
153
- var RUNTIME_PACKAGE = "@penvhq/penv";
154
- var SCHEMA_PACKAGE = "zod";
155
- var LOCKFILES = [
156
- ["pnpm", "pnpm-lock.yaml"],
157
- ["yarn", "yarn.lock"],
158
- ["bun", "bun.lock"],
159
- ["bun", "bun.lockb"],
160
- ["npm", "package-lock.json"]
161
- ];
162
- var ADD = {
163
- pnpm: ["pnpm", "add", "--save-exact"],
164
- npm: ["npm", "install", "--save-exact"],
165
- yarn: ["yarn", "add", "--exact"],
166
- bun: ["bun", "add", "--exact"]
167
- };
168
- function engineVersion() {
169
- const version = ownManifest()?.version;
170
- if (typeof version === "string" && version.length > 0) {
171
- return version;
172
- }
173
- throw new PenvError2(
174
- "ENGINE_VERSION_UNREADABLE",
175
- "penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs",
176
- `Reinstall penv, then run \`penv init\` again.`
177
- );
178
- }
179
- function schemaPackageVersion() {
180
- const peers = ownManifest()?.peerDependencies;
181
- const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
182
- const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
183
- if (floor.length > 0) {
184
- return floor;
185
- }
186
- throw new PenvError2(
187
- "ENGINE_PEER_UNREADABLE",
188
- `penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
189
- `Reinstall penv, then run \`penv init\` again.`
190
- );
191
- }
192
- function ownManifest() {
193
- try {
194
- const parsed = JSON.parse(
195
- readFileSync(new URL("../package.json", import.meta.url), "utf8")
196
- );
197
- return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
198
- } catch {
199
- return void 0;
200
- }
201
- }
202
- function detectPackageManager(root) {
203
- for (const [manager, lockfile] of LOCKFILES) {
204
- if (existsSync2(join2(root, lockfile))) {
205
- return manager;
206
- }
207
- }
208
- return declaredManager(root) ?? "npm";
209
- }
210
- function declaredManager(root) {
211
- const declared = manifestOf(root)?.packageManager;
212
- if (typeof declared !== "string") {
213
- return void 0;
214
- }
215
- const name = declared.split("@")[0];
216
- return name === "pnpm" || name === "npm" || name === "yarn" || name === "bun" ? name : void 0;
217
- }
218
- function manifestOf(root) {
219
- const file = join2(root, "package.json");
220
- if (!existsSync2(file)) {
221
- return void 0;
222
- }
223
- try {
224
- const manifest = JSON.parse(readFileSync(file, "utf8"));
225
- return manifest !== null && typeof manifest === "object" && !Array.isArray(manifest) ? manifest : void 0;
226
- } catch {
227
- return void 0;
228
- }
229
- }
230
- function declaredVersion(root, name) {
231
- const manifest = manifestOf(root);
232
- for (const field of ["dependencies", "devDependencies"]) {
233
- const block = manifest?.[field];
234
- if (block !== null && typeof block === "object" && !Array.isArray(block)) {
235
- const version = block[name];
236
- if (typeof version === "string") {
237
- return version;
238
- }
239
- }
240
- }
241
- return void 0;
242
- }
243
- function planInstall(root, version = engineVersion()) {
244
- const manager = detectPackageManager(root);
245
- const lockfile = LOCKFILES.find(
246
- ([name, file]) => name === manager && existsSync2(join2(root, file))
247
- )?.[1];
248
- const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
249
- const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
250
- const packages = [
251
- {
252
- name: RUNTIME_PACKAGE,
253
- version,
254
- ...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
255
- satisfied: runtimeDeclared === version
256
- },
257
- {
258
- name: SCHEMA_PACKAGE,
259
- version: schemaPackageVersion(),
260
- ...zodDeclared === void 0 ? {} : { declared: zodDeclared },
261
- // Any declared zod counts: which zod a project uses is the project's
262
- // decision, and penv is here to make sure there is one, not to move it.
263
- satisfied: zodDeclared !== void 0
264
- }
265
- ];
266
- const pending = packages.filter((entry) => !entry.satisfied);
267
- const specs = (pending.length === 0 ? packages : pending).map(
268
- (entry) => `${entry.name}@${entry.version}`
269
- );
270
- return {
271
- root,
272
- manager,
273
- packages,
274
- command: [...ADD[manager], ...specs],
275
- ...lockfile === void 0 ? {} : { lockfile },
276
- satisfied: pending.length === 0
277
- };
278
- }
279
- function describe(entry) {
280
- return `${entry.name} ${entry.version}`;
281
- }
282
- function renderInstallPlan(plan2) {
283
- if (plan2.satisfied) {
284
- return [
285
- `package.json already has ${plan2.packages.map(describe).join(" and ")} \u2014 nothing to install.`
286
- ];
287
- }
288
- const pending = plan2.packages.filter((entry) => !entry.satisfied);
289
- const added = pending.filter((entry) => entry.declared === void 0);
290
- const replaced = pending.filter((entry) => entry.declared !== void 0);
291
- return [
292
- "package.json",
293
- ...added.length === 0 ? [] : [
294
- ' + "dependencies": {',
295
- ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
296
- " + }"
297
- ],
298
- ...replaced.flatMap((entry) => [
299
- ` - "${entry.name}": "${entry.declared}"`,
300
- ` + "${entry.name}": "${entry.version}"`
301
- ]),
302
- ...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
303
- "",
304
- `Run with: ${plan2.command.join(" ")}`
305
- ];
306
- }
307
- var installWithPackageManager = async (plan2) => {
308
- const child = startChild({
309
- command: plan2.command,
310
- env: process.env,
311
- cwd: plan2.root,
312
- purpose: `install ${plan2.packages.map(describe).join(" and ")}`
313
- });
314
- const ended = await child.ended;
315
- if (ended.exitCode !== 0 || ended.signal !== null) {
316
- throw installFailed(plan2);
317
- }
318
- };
319
- function installFailed(plan2) {
320
- return new PenvError2(
321
- "INIT_INSTALL_FAILED",
322
- `${plan2.command.join(" ")} did not finish, so penv migrated nothing`,
323
- `Run \`${plan2.command.join(" ")}\` yourself, then start this command again. Your dotenv files are exactly where they were.`
324
- );
325
- }
326
-
327
33
  // src/project.ts
328
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
329
- import { dirname, join as join4 } from "path";
34
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
35
+ import { dirname, join as join2 } from "path";
330
36
  import {
331
37
  assertMigrated,
332
38
  candidatesFor,
@@ -335,7 +41,7 @@ import {
335
41
  isReservedToken,
336
42
  loadConfig,
337
43
  openValue,
338
- PenvError as PenvError5,
44
+ PenvError as PenvError3,
339
45
  parameterId,
340
46
  ReservedTokenError,
341
47
  recordsDir as recordsDir2,
@@ -348,7 +54,7 @@ import {
348
54
  import { FilesystemProvider } from "@penvhq/provider-filesystem";
349
55
 
350
56
  // src/env-flags.ts
351
- import { PenvError as PenvError3 } from "@penvhq/core";
57
+ import { PenvError } from "@penvhq/core";
352
58
  var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
353
59
  var COMMAND_FLAGS = [
354
60
  ...BASE_RESERVED,
@@ -381,14 +87,14 @@ function environmentFromShorthand(config, candidates, explicit) {
381
87
  const hits = candidates.filter((candidate) => config.environments.includes(candidate));
382
88
  const strangers = candidates.filter((candidate) => !config.environments.includes(candidate));
383
89
  if (strangers.length > 0) {
384
- throw new PenvError3(
90
+ throw new PenvError(
385
91
  "UNKNOWN_FLAG",
386
92
  `${quoteList(strangers)} ${strangers.length === 1 ? "is not a flag" : "are not flags"} this command takes, and ${strangers.length === 1 ? "names" : "name"} no declared environment`,
387
93
  `Declared environments work as bare flags: ${quoteList(config.environments)}. Anything else needs \`--env <name>\`.`
388
94
  );
389
95
  }
390
96
  if (hits.length > 1) {
391
- throw new PenvError3(
97
+ throw new PenvError(
392
98
  "ENVIRONMENT_FLAG_AMBIGUOUS",
393
99
  `${quoteList(hits)} name two environments at once, and a command acts on exactly one`,
394
100
  "Pass a single environment flag, or the canonical `--env <name>`."
@@ -396,7 +102,7 @@ function environmentFromShorthand(config, candidates, explicit) {
396
102
  }
397
103
  const hit = hits[0];
398
104
  if (hit !== void 0 && explicit !== void 0 && explicit !== hit) {
399
- throw new PenvError3(
105
+ throw new PenvError(
400
106
  "ENVIRONMENT_FLAG_AMBIGUOUS",
401
107
  `\`--env ${explicit}\` and \`--${hit}\` name two environments at once`,
402
108
  "Drop one of them \u2014 `--env` is the canonical spelling."
@@ -406,9 +112,9 @@ function environmentFromShorthand(config, candidates, explicit) {
406
112
  }
407
113
 
408
114
  // src/registry.ts
409
- import { readFileSync as readFileSync2 } from "fs";
115
+ import { readFileSync } from "fs";
410
116
  import { createRequire } from "module";
411
- import { join as join3, resolve } from "path";
117
+ import { join, resolve } from "path";
412
118
  import { pathToFileURL } from "url";
413
119
  import {
414
120
  holdsProjection,
@@ -416,7 +122,7 @@ import {
416
122
  localExtensionsFile,
417
123
  MANIFEST_PATH,
418
124
  PENV_DIR,
419
- PenvError as PenvError4,
125
+ PenvError as PenvError2,
420
126
  packageDir,
421
127
  packageEntry,
422
128
  parseLocalExtensions,
@@ -475,7 +181,7 @@ async function loadPluginProvider(type, context) {
475
181
  }
476
182
  const factory = mod[PLUGIN_FACTORY_EXPORT];
477
183
  if (typeof factory !== "function") {
478
- throw new PenvError4(
184
+ throw new PenvError2(
479
185
  "PROVIDER_PLUGIN_INVALID",
480
186
  `\`${type}\` does not export \`${PLUGIN_FACTORY_EXPORT}\``,
481
187
  `A penv provider package must export \`${PLUGIN_FACTORY_EXPORT}(context) => Provider\`.`
@@ -522,7 +228,7 @@ function resolveExtension(type, projectRoot, environment, local = localExtension
522
228
  function pinnedVersion(type, projectRoot) {
523
229
  let manifest;
524
230
  try {
525
- manifest = parseManifest(readFileSync2(join3(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
231
+ manifest = parseManifest(readFileSync(join(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
526
232
  } catch {
527
233
  return void 0;
528
234
  }
@@ -535,7 +241,7 @@ function storedExtension(type, version) {
535
241
  function localExtensions(projectRoot) {
536
242
  let text;
537
243
  try {
538
- text = readFileSync2(localExtensionsFile(projectRoot), "utf8");
244
+ text = readFileSync(localExtensionsFile(projectRoot), "utf8");
539
245
  } catch {
540
246
  return [];
541
247
  }
@@ -545,7 +251,7 @@ function isCi(value) {
545
251
  return value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
546
252
  }
547
253
  function localExtensionInCi(type, environment) {
548
- return new PenvError4(
254
+ return new PenvError2(
549
255
  "LOCAL_EXTENSION_IN_CI",
550
256
  `The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
551
257
  `${LOCAL_EXTENSIONS_PATH} records it as a package this project develops, so nothing pins the bytes CI would run. Publish it and run \`penv add ${type}\` to pin a release.`
@@ -574,7 +280,7 @@ function assertSatisfiesContract(provider, specifier) {
574
280
  const contract = projection ? "the @penvhq/core ProjectionProvider contract its declared capabilities select" : "the @penvhq/core Provider contract that the filesystem provider defines";
575
281
  for (const method of methods) {
576
282
  if (typeof provider[method] !== "function") {
577
- throw new PenvError4(
283
+ throw new PenvError2(
578
284
  "PROVIDER_PLUGIN_INVALID",
579
285
  `The provider from \`${specifier}\` is missing \`${method}()\``,
580
286
  `It must satisfy ${contract}.`
@@ -587,21 +293,21 @@ function where(environment) {
587
293
  }
588
294
  function unknownProvider(type, environment) {
589
295
  const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
590
- return new PenvError4(
296
+ return new PenvError2(
591
297
  "UNKNOWN_PROVIDER",
592
298
  `The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
593
299
  `Run \`penv add ${type}\` to pin a release, or \`penv add --local ${type}\` if this repository is the one that builds it. penv looks in ${LOCAL_EXTENSIONS_PATH}, this project's node_modules, and then $PENV_HOME at the version ${MANIFEST_PATH} pins. The CLI ships ${preinstalled} pre-installed.`
594
300
  );
595
301
  }
596
302
  function extensionNotInstalled(type, version, environment) {
597
- return new PenvError4(
303
+ return new PenvError2(
598
304
  "EXTENSION_NOT_INSTALLED",
599
305
  `${MANIFEST_PATH} pins \`${type}\` ${version}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
600
306
  "Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
601
307
  );
602
308
  }
603
309
  function localExtensionUnresolved(type, projectRoot, environment) {
604
- return new PenvError4(
310
+ return new PenvError2(
605
311
  "LOCAL_EXTENSION_UNRESOLVED",
606
312
  `${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
607
313
  `Add it as a dependency of the root (a workspace link is one) and run \`pnpm install\`, or drop the name from ${LOCAL_EXTENSIONS_PATH} if this project no longer builds it.`
@@ -609,7 +315,7 @@ function localExtensionUnresolved(type, projectRoot, environment) {
609
315
  }
610
316
  function providerLoadFailed(type, path, cause) {
611
317
  const detail = cause instanceof Error ? cause.message : String(cause);
612
- return new PenvError4(
318
+ return new PenvError2(
613
319
  "PROVIDER_PLUGIN_LOAD",
614
320
  `The provider package \`${type}\` failed to load from ${path}: ${detail}`,
615
321
  "penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
@@ -632,7 +338,7 @@ function openProject(cwd) {
632
338
  }
633
339
  function localTree(project) {
634
340
  if (!(project.provider instanceof FilesystemProvider)) {
635
- throw new PenvError5(
341
+ throw new PenvError3(
636
342
  "PROVIDER_NOT_LOCAL",
637
343
  `This command reads the local .penv tree synchronously, which the \`${project.provider.type}\` provider is not`,
638
344
  "Run this against a filesystem-backed project, or use a command that speaks the async provider contract."
@@ -641,11 +347,11 @@ function localTree(project) {
641
347
  return project.provider;
642
348
  }
643
349
  function selfContainedSchemaModule(root, schemaFile) {
644
- const file = join4(root, ...schemaFile.split("/"));
645
- if (!existsSync3(file)) {
350
+ const file = join2(root, ...schemaFile.split("/"));
351
+ if (!existsSync(file)) {
646
352
  return void 0;
647
353
  }
648
- const source = readFileSync3(file, "utf8");
354
+ const source = readFileSync2(file, "utf8");
649
355
  return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
650
356
  }
651
357
  function schemaShapeFileOf(project) {
@@ -674,7 +380,7 @@ function refFromKey(key, config) {
674
380
  const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
675
381
  const name = segments[segments.length - 1];
676
382
  if (name === void 0) {
677
- throw new PenvError5(
383
+ throw new PenvError3(
678
384
  "PARAMETER_KEY",
679
385
  `\`${key}\` names no parameter`,
680
386
  "A key is `<namespace>/<name>` or `<namespace>.<name>`, e.g. `redis/password`."
@@ -693,13 +399,13 @@ function assertWritableKey(key) {
693
399
  const ref = refFromAccessPath(segments);
694
400
  if (ref !== void 0) {
695
401
  const suggestion = [...ref.namespace, ref.name].join("/");
696
- throw new PenvError5(
402
+ throw new PenvError3(
697
403
  "PARAMETER_KEY_CASING",
698
404
  `\`${key}\` is not a canonical parameter name`,
699
405
  `Parameter files are lower-case and hyphenated. Did you mean \`${suggestion}\`? That is the file that backs the \`${key}\` key in your schema.`
700
406
  );
701
407
  }
702
- throw new PenvError5(
408
+ throw new PenvError3(
703
409
  "PARAMETER_KEY_UNREACHABLE",
704
410
  `No value file can be named that reaches \`${key}\``,
705
411
  "Parameter files are lower-case and hyphenated, and this key maps to no such file \u2014 a run of capitals like `apiURL` cannot be reached (use `api-url`, which the schema reads as `apiUrl`). Run `penv validate` or `penv fill` to see the names penv expects."
@@ -747,7 +453,7 @@ function refsFrom(files) {
747
453
  }
748
454
 
749
455
  // src/ui.ts
750
- import { PenvError as PenvError6 } from "@penvhq/core";
456
+ import { PenvError as PenvError4 } from "@penvhq/core";
751
457
 
752
458
  // src/style.ts
753
459
  function supportsColor(stream) {
@@ -880,7 +586,7 @@ function writeError(lines) {
880
586
  }
881
587
  }
882
588
  function reportError(error) {
883
- if (error instanceof PenvError6) {
589
+ if (error instanceof PenvError4) {
884
590
  process.stderr.write(`${err.red(CROSS)} ${error.summary}
885
591
  `);
886
592
  if (error.remedy !== void 0) {
@@ -911,9 +617,9 @@ async function guard(run) {
911
617
  }
912
618
 
913
619
  // src/commands/run.ts
914
- import { existsSync as existsSync4, readFileSync as readFileSync4, watch } from "fs";
620
+ import { existsSync as existsSync2, readFileSync as readFileSync3, watch } from "fs";
915
621
  import { createRequire as createRequire2 } from "module";
916
- import { basename, dirname as dirname2, join as join5, resolve as resolve2 } from "path";
622
+ import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
917
623
  import {
918
624
  ARTIFACT_BUILD_COMMAND,
919
625
  assertArtifactFor,
@@ -923,7 +629,7 @@ import {
923
629
  MissingMaterializationError,
924
630
  openSealed,
925
631
  own,
926
- PenvError as PenvError8,
632
+ PenvError as PenvError6,
927
633
  parseArtifact,
928
634
  RECORDS_PATH,
929
635
  UndecryptableValueError,
@@ -1172,7 +878,7 @@ import {
1172
878
  checkNameCollisions,
1173
879
  jitiFor,
1174
880
  NameCollisionError,
1175
- PenvError as PenvError7,
881
+ PenvError as PenvError5,
1176
882
  ReservedTokenError as ReservedTokenError2,
1177
883
  resolveAll,
1178
884
  SCHEMA_HARVEST_ENV,
@@ -1521,7 +1227,7 @@ async function checkEnvironment(project, environment) {
1521
1227
  try {
1522
1228
  files = await project.provider.list();
1523
1229
  } catch (error) {
1524
- if (!(error instanceof PenvError7)) {
1230
+ if (!(error instanceof PenvError5)) {
1525
1231
  throw error;
1526
1232
  }
1527
1233
  return {
@@ -1673,7 +1379,7 @@ function assertNotNested(host, inner) {
1673
1379
  if (outer === void 0) {
1674
1380
  return;
1675
1381
  }
1676
- throw new PenvError8(
1382
+ throw new PenvError6(
1677
1383
  "RUN_NESTED",
1678
1384
  `\`${inner}\` is starting inside \`${outer}\`, and two penv environments cannot own one process`,
1679
1385
  `Drop one of the two wrappers \u2014 the inner one is in a package.json script \u2014 then run \`${outer}\` again.`
@@ -1683,7 +1389,7 @@ function assertSource(source, inner) {
1683
1389
  if (source === "project" || source === "snapshot") {
1684
1390
  return source;
1685
1391
  }
1686
- throw new PenvError8(
1392
+ throw new PenvError6(
1687
1393
  "RUN_SOURCE_UNKNOWN",
1688
1394
  `\`--source ${source}\` names no source penv reads`,
1689
1395
  `A run reads ${SOURCES.map((name) => `\`${name}\``).join(" or ")}, and \`project\` is the default \u2014 so \`${inner}\` reads the local tree.`
@@ -1704,7 +1410,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
1704
1410
  }
1705
1411
  const parameter = [...ref.namespace, ref.name].join("/");
1706
1412
  const prefix = prefixes.find((candidate) => variable.startsWith(candidate));
1707
- throw new PenvError8(
1413
+ throw new PenvError6(
1708
1414
  "RUN_PUBLIC_SECRET",
1709
1415
  `The secret ${parameter} maps to ${variable}, which the \`${prefix}\` prefix publishes to the browser`,
1710
1416
  `Rename the parameter, or drop \`secret\` from its meta if it is not one \u2014 then \`${retry}\`.`
@@ -1713,7 +1419,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
1713
1419
  }
1714
1420
  function invalidConfiguration(result2, inner) {
1715
1421
  const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
1716
- return new PenvError8(
1422
+ return new PenvError6(
1717
1423
  "RUN_INVALID_CONFIGURATION",
1718
1424
  `Configuration for environment ${result2.environment} is not valid, so nothing was started:
1719
1425
  ${lines}`,
@@ -1735,7 +1441,7 @@ function assertNoActiveDotenv(project) {
1735
1441
  if (first === void 0) {
1736
1442
  return;
1737
1443
  }
1738
- throw new PenvError8(
1444
+ throw new PenvError6(
1739
1445
  "RUN_DOTENV_ACTIVE",
1740
1446
  `${first.name} is active configuration again, and your framework would read it beside penv's records`,
1741
1447
  `Adopt it with \`penv init\`, or delete ${first.name} \u2014 its values belong in ${RECORDS_PATH}/.`
@@ -1753,10 +1459,10 @@ function packageManifest(specifier, root) {
1753
1459
  return void 0;
1754
1460
  }
1755
1461
  for (; ; ) {
1756
- const file = join5(directory, "package.json");
1757
- if (existsSync4(file)) {
1462
+ const file = join3(directory, "package.json");
1463
+ if (existsSync2(file)) {
1758
1464
  try {
1759
- const parsed = JSON.parse(readFileSync4(file, "utf8"));
1465
+ const parsed = JSON.parse(readFileSync3(file, "utf8"));
1760
1466
  if (isPlainObject(parsed) && parsed.name === specifier) {
1761
1467
  return parsed;
1762
1468
  }
@@ -1783,7 +1489,7 @@ function declaredCredentials(config, root) {
1783
1489
  continue;
1784
1490
  }
1785
1491
  if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
1786
- throw new PenvError8(
1492
+ throw new PenvError6(
1787
1493
  "PROVIDER_CREDENTIALS_INVALID",
1788
1494
  `\`${provider.type}\` declares \`penv.credentials\`, and it is not a list of variable names`,
1789
1495
  `Its package.json should read \`"penv": { "credentials": ["ACME_TOKEN"] }\` \u2014 penv strips exactly what an extension declares, so it will not guess at a declaration it cannot read.`
@@ -1819,7 +1525,7 @@ async function prepare(project, environment, host, inner) {
1819
1525
  function snapshotPath(host) {
1820
1526
  const path = host[SNAPSHOT_VARIABLE]?.trim();
1821
1527
  if (path === void 0 || path.length === 0) {
1822
- throw new PenvError8(
1528
+ throw new PenvError6(
1823
1529
  "RUN_SNAPSHOT_UNSET",
1824
1530
  `\`--source snapshot\` reads the sealed artifact ${SNAPSHOT_VARIABLE} names, and ${SNAPSHOT_VARIABLE} is not set`,
1825
1531
  `Build one with \`${ARTIFACT_BUILD_COMMAND}\` and point ${SNAPSHOT_VARIABLE} at it.`
@@ -1829,9 +1535,9 @@ function snapshotPath(host) {
1829
1535
  }
1830
1536
  function readSnapshot(path) {
1831
1537
  try {
1832
- return readFileSync4(path, "utf8");
1538
+ return readFileSync3(path, "utf8");
1833
1539
  } catch {
1834
- throw new PenvError8(
1540
+ throw new PenvError6(
1835
1541
  "RUN_SNAPSHOT_MISSING",
1836
1542
  `${SNAPSHOT_VARIABLE} names ${path}, and penv cannot read a sealed artifact there`,
1837
1543
  `Point ${SNAPSHOT_VARIABLE} at the artifact your release mounted \u2014 \`${ARTIFACT_BUILD_COMMAND}\` writes one.`
@@ -1908,7 +1614,7 @@ function watchProject(project, onChange) {
1908
1614
  timer = setTimeout(onChange, DEBOUNCE_MS);
1909
1615
  };
1910
1616
  const add = (target, recursive, only) => {
1911
- if (!existsSync4(target)) {
1617
+ if (!existsSync2(target)) {
1912
1618
  return;
1913
1619
  }
1914
1620
  try {
@@ -1947,7 +1653,7 @@ async function runRun(options) {
1947
1653
  }
1948
1654
  if (source === "snapshot") {
1949
1655
  if (options.watch === true) {
1950
- throw new PenvError8(
1656
+ throw new PenvError6(
1951
1657
  "RUN_SNAPSHOT_WATCH",
1952
1658
  "`--watch` re-syncs the project tree, and a run from a sealed artifact has no tree to watch",
1953
1659
  "Drop `--watch` \u2014 an artifact is built once and read unchanged. Watch the project instead: `penv run --watch -- <command>`."
@@ -2091,7 +1797,7 @@ function buildCommand(environment, out2) {
2091
1797
  function targetOf(options) {
2092
1798
  const environment = options.environment?.trim();
2093
1799
  if (environment === void 0 || environment.length === 0) {
2094
- throw new PenvError9(
1800
+ throw new PenvError7(
2095
1801
  "ARTIFACT_ENV_REQUIRED",
2096
1802
  "`penv artifact build` names the environment it builds for, and `--env` was not given",
2097
1803
  `Name it: \`${buildCommand(void 0, options.out)}\`. An artifact carries one environment, and penv will not pick which.`
@@ -2102,13 +1808,13 @@ function targetOf(options) {
2102
1808
  function outputOf(options, environment) {
2103
1809
  const out2 = options.out?.trim();
2104
1810
  if (out2 === void 0 || out2.length === 0) {
2105
- throw new PenvError9(
1811
+ throw new PenvError7(
2106
1812
  "ARTIFACT_OUT_REQUIRED",
2107
1813
  "`penv artifact build` writes where it is told, and `--out` was not given",
2108
1814
  `Name the path: \`${buildCommand(environment, void 0)}\`. The artifact belongs outside the repository, so there is no default worth having.`
2109
1815
  );
2110
1816
  }
2111
- return isAbsolute2(out2) ? out2 : resolve3(options.cwd, out2);
1817
+ return isAbsolute(out2) ? out2 : resolve3(options.cwd, out2);
2112
1818
  }
2113
1819
  async function winnerOf(project, ref, environment) {
2114
1820
  for (const file of candidatesFor2(ref, environment, true)) {
@@ -2120,7 +1826,7 @@ async function winnerOf(project, ref, environment) {
2120
1826
  return void 0;
2121
1827
  }
2122
1828
  function plaintextSecret(parameter, location, environment) {
2123
- return new PenvError9(
1829
+ return new PenvError7(
2124
1830
  "ARTIFACT_PLAINTEXT_SECRET",
2125
1831
  `${parameter} is a secret for environment ${environment}, and its value comes from ${location}, which is not sealed`,
2126
1832
  `Seal it with \`penv encrypt ${parameter} --env ${environment}\` \u2014 an artifact carries ciphertext or nothing.`
@@ -2134,7 +1840,7 @@ async function runArtifactBuild(options) {
2134
1840
  const { schema, issues } = await loadSchema(project, environment);
2135
1841
  if (schema === void 0) {
2136
1842
  const lines = issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
2137
- throw new PenvError9(
1843
+ throw new PenvError7(
2138
1844
  "ARTIFACT_NO_SCHEMA",
2139
1845
  `The schema did not load, so penv cannot tell what this artifact should deliver:
2140
1846
  ${lines}`,
@@ -2186,7 +1892,7 @@ ${lines}`,
2186
1892
  values: present,
2187
1893
  sealed,
2188
1894
  absent: refs.length - present,
2189
- insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute2(inside)
1895
+ insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute(inside)
2190
1896
  };
2191
1897
  }
2192
1898
  function displayPath(cwd, file) {
@@ -2247,25 +1953,25 @@ import { defineCommand as defineCommand5 } from "citty";
2247
1953
 
2248
1954
  // src/cutover.ts
2249
1955
  import {
2250
- existsSync as existsSync5,
1956
+ existsSync as existsSync3,
2251
1957
  mkdirSync as mkdirSync2,
2252
1958
  readdirSync as readdirSync2,
2253
- readFileSync as readFileSync5,
1959
+ readFileSync as readFileSync4,
2254
1960
  renameSync,
2255
1961
  rmSync,
2256
1962
  writeFileSync as writeFileSync2
2257
1963
  } from "fs";
2258
- import { dirname as dirname4, join as join6, resolve as resolve4 } from "path";
1964
+ import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
2259
1965
  import {
2260
1966
  CUTOVER_PATH,
2261
1967
  findConfigFile,
2262
- PenvError as PenvError10,
1968
+ PenvError as PenvError8,
2263
1969
  ROLLBACK_DOTENV_PATH,
2264
1970
  ROLLBACK_PATH
2265
1971
  } from "@penvhq/core";
2266
1972
  var CUTOVER_FORMAT = 1;
2267
1973
  function fileFor(root, relativePosix) {
2268
- return join6(root, ...relativePosix.split("/"));
1974
+ return join4(root, ...relativePosix.split("/"));
2269
1975
  }
2270
1976
  function bundleDir(root) {
2271
1977
  return fileFor(root, ROLLBACK_DOTENV_PATH);
@@ -2289,12 +1995,12 @@ function bundleUnresolved(root) {
2289
1995
  }
2290
1996
  function readCutover(root) {
2291
1997
  const file = cutoverFile(root);
2292
- if (!existsSync5(file)) {
1998
+ if (!existsSync3(file)) {
2293
1999
  return void 0;
2294
2000
  }
2295
2001
  let parsed;
2296
2002
  try {
2297
- parsed = JSON.parse(readFileSync5(file, "utf8"));
2003
+ parsed = JSON.parse(readFileSync4(file, "utf8"));
2298
2004
  } catch {
2299
2005
  throw unreadable("it is not JSON");
2300
2006
  }
@@ -2320,7 +2026,7 @@ function readCutover(root) {
2320
2026
  };
2321
2027
  }
2322
2028
  function unreadable(what) {
2323
- return new PenvError10(
2029
+ return new PenvError8(
2324
2030
  "CUTOVER_UNREADABLE",
2325
2031
  `${CUTOVER_PATH} records the last dotenv cutover, and ${what}`,
2326
2032
  `Run \`penv cleanup\` to drop that record and the rollback bundle it names.`
@@ -2352,7 +2058,7 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
2352
2058
  const bundle = bundleDir(root);
2353
2059
  mkdirSync2(bundle, { recursive: true });
2354
2060
  for (const name of files) {
2355
- renameSync(join6(root, name), join6(bundle, name));
2061
+ renameSync(join4(root, name), join4(bundle, name));
2356
2062
  }
2357
2063
  return cutover;
2358
2064
  }
@@ -2361,7 +2067,7 @@ function runUndo(options) {
2361
2067
  const cutover = readCutover(root);
2362
2068
  const bundled = bundledFiles(root);
2363
2069
  if (cutover === void 0 && bundled.length === 0) {
2364
- throw new PenvError10(
2070
+ throw new PenvError8(
2365
2071
  "INIT_UNDO_NOTHING",
2366
2072
  "There is no dotenv cutover to undo in this project",
2367
2073
  "Run `penv init` to adopt your dotenv files; undo puts them back afterwards."
@@ -2371,11 +2077,11 @@ function runUndo(options) {
2371
2077
  const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
2372
2078
  const bundle = bundleDir(root);
2373
2079
  const held = new Set(bundled);
2374
- const occupied2 = names.filter((name) => held.has(name) && existsSync5(join6(root, name)));
2080
+ const occupied2 = names.filter((name) => held.has(name) && existsSync3(join4(root, name)));
2375
2081
  if (occupied2.length > 0) {
2376
2082
  const listed = occupied2.join(", ");
2377
2083
  const many = occupied2.length > 1;
2378
- throw new PenvError10(
2084
+ throw new PenvError8(
2379
2085
  "INIT_UNDO_OCCUPIED",
2380
2086
  `${listed} ${many ? "exist" : "exists"} again, and restoring what penv moved aside would write over ${many ? "them" : "it"}`,
2381
2087
  `Move ${listed} out of the way, or run \`penv cleanup\` to keep ${many ? "them" : "it"} and drop the bundle. Nothing was restored.`
@@ -2386,9 +2092,9 @@ function runUndo(options) {
2386
2092
  const missing = [];
2387
2093
  for (const name of names) {
2388
2094
  if (held.has(name)) {
2389
- renameSync(join6(bundle, name), join6(root, name));
2095
+ renameSync(join4(bundle, name), join4(root, name));
2390
2096
  restored.push(name);
2391
- } else if (existsSync5(join6(root, name))) {
2097
+ } else if (existsSync3(join4(root, name))) {
2392
2098
  alreadyBack.push(name);
2393
2099
  } else {
2394
2100
  missing.push(name);
@@ -2400,7 +2106,7 @@ function runUndo(options) {
2400
2106
  function runCleanup(options) {
2401
2107
  const root = cutoverRoot(options.cwd);
2402
2108
  const held = bundledFiles(root);
2403
- const cleaned = held.length > 0 || existsSync5(cutoverFile(root)) || existsSync5(fileFor(root, ROLLBACK_PATH));
2109
+ const cleaned = held.length > 0 || existsSync3(cutoverFile(root)) || existsSync3(fileFor(root, ROLLBACK_PATH));
2404
2110
  removeBundle(root);
2405
2111
  return { root, removed: held, cleaned };
2406
2112
  }
@@ -2441,8 +2147,8 @@ var cleanupCommand = defineCommand5({
2441
2147
  });
2442
2148
 
2443
2149
  // src/commands/doctor.ts
2444
- import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
2445
- import { join as join7, relative as relative2 } from "path";
2150
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "fs";
2151
+ import { join as join5, relative as relative2 } from "path";
2446
2152
  import {
2447
2153
  ARTIFACT_BUILD_COMMAND as ARTIFACT_BUILD_COMMAND2,
2448
2154
  accessPath as accessPath3,
@@ -2468,7 +2174,7 @@ import { defineCommand as defineCommand7 } from "citty";
2468
2174
  import {
2469
2175
  checkNameCollisions as checkNameCollisions2,
2470
2176
  holdsProjection as holdsProjection3,
2471
- PenvError as PenvError11,
2177
+ PenvError as PenvError9,
2472
2178
  recordPath,
2473
2179
  requireValue,
2474
2180
  variableName as variableName4
@@ -2536,7 +2242,7 @@ async function destinationFor(project, environment, options) {
2536
2242
  const declared = project.config.providers[environment];
2537
2243
  const location = declared?.location;
2538
2244
  if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
2539
- throw new PenvError11(
2245
+ throw new PenvError9(
2540
2246
  "NO_DESTINATION",
2541
2247
  `Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
2542
2248
  `Declare a provider for it in penv.config.ts \u2014 e.g. \`${environment}: { type: "@penvhq/provider-github", location: "owner/repo" }\` \u2014 or push somewhere once with \`penv push --env ${environment} --destination <package> --location <place>\`.`
@@ -2563,7 +2269,7 @@ function plan(resolutions, config, environment, allowDecrypt) {
2563
2269
  let encrypted = false;
2564
2270
  if (winner.file.encrypted) {
2565
2271
  if (!allowDecrypt) {
2566
- throw new PenvError11(
2272
+ throw new PenvError9(
2567
2273
  "ENCRYPTED_VALUE_REFUSED",
2568
2274
  `Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath(winner.location)}, and a push sends plaintext for the destination to re-seal`,
2569
2275
  "Re-run with `--allow-decrypt` to decrypt it locally and push it, or push an environment whose values are plaintext. penv's encryption stops at the projection; the destination seals it under its own key."
@@ -2616,7 +2322,7 @@ async function ensureTargetApproved(provider, environment, options) {
2616
2322
  return false;
2617
2323
  }
2618
2324
  if (provider.ensureTarget === void 0) {
2619
- throw new PenvError11(
2325
+ throw new PenvError9(
2620
2326
  "MISSING_TARGET",
2621
2327
  `The destination has no environment \`${environment}\` to receive this push, and this provider cannot create one`,
2622
2328
  `Create the environment on the destination side, then run \`penv push --env ${environment}\` again.`
@@ -2626,7 +2332,7 @@ async function ensureTargetApproved(provider, environment, options) {
2626
2332
  `The destination has no environment \`${environment}\`. Create it?`
2627
2333
  );
2628
2334
  if (!approved) {
2629
- throw new PenvError11(
2335
+ throw new PenvError9(
2630
2336
  "MISSING_TARGET",
2631
2337
  `The destination has no environment \`${environment}\` to receive this push`,
2632
2338
  `Re-run with \`--yes\` to create it, answer \`y\` at the prompt, or create it on the destination side yourself.`
@@ -3396,7 +3102,7 @@ function scannableFiles(directory, out2) {
3396
3102
  return;
3397
3103
  }
3398
3104
  for (const entry of entries) {
3399
- const path = join7(directory, entry.name);
3105
+ const path = join5(directory, entry.name);
3400
3106
  if (entry.isDirectory()) {
3401
3107
  if (!UNSCANNED_DIRS.has(entry.name)) {
3402
3108
  scannableFiles(path, out2);
@@ -3438,10 +3144,10 @@ function artifactFindings(project) {
3438
3144
  for (const file of files) {
3439
3145
  let text;
3440
3146
  try {
3441
- if (statSync2(file).size > ARTIFACT_SCAN_LIMIT) {
3147
+ if (statSync(file).size > ARTIFACT_SCAN_LIMIT) {
3442
3148
  continue;
3443
3149
  }
3444
- text = readFileSync6(file, "utf8");
3150
+ text = readFileSync5(file, "utf8");
3445
3151
  } catch {
3446
3152
  continue;
3447
3153
  }
@@ -3790,7 +3496,7 @@ import {
3790
3496
  formatValueFile as formatValueFile5,
3791
3497
  isSecret as isSecret5,
3792
3498
  openValue as openValue3,
3793
- PenvError as PenvError13,
3499
+ PenvError as PenvError11,
3794
3500
  parameterId as parameterId5,
3795
3501
  recordPath as recordPath3,
3796
3502
  sealValue as sealValue2
@@ -3801,7 +3507,7 @@ import { defineCommand as defineCommand9 } from "citty";
3801
3507
  import {
3802
3508
  formatValueFile as formatValueFile4,
3803
3509
  isSecret as isSecret4,
3804
- PenvError as PenvError12,
3510
+ PenvError as PenvError10,
3805
3511
  parameterId as parameterId4,
3806
3512
  recordPath as recordPath2,
3807
3513
  sealValue
@@ -3825,7 +3531,7 @@ function targetScope(project, options, key) {
3825
3531
  return scopeFrom(options);
3826
3532
  }
3827
3533
  if (environment.trim().length === 0) {
3828
- throw new PenvError12(
3534
+ throw new PenvError10(
3829
3535
  "ENVIRONMENT_FLAG_EMPTY",
3830
3536
  `\`--env\` for parameter ${key} names no environment`,
3831
3537
  `Pass a declared environment \u2014 ${project.config.environments.map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to write the scope that has no environment.`
@@ -3838,7 +3544,7 @@ function policyEnvironment(project, options) {
3838
3544
  }
3839
3545
  function sealFor(project, file, value, parameter, environment) {
3840
3546
  if (environment === void 0) {
3841
- throw new PenvError12(
3547
+ throw new PenvError10(
3842
3548
  "SECRET_SCOPE_AMBIGUOUS",
3843
3549
  `Parameter ${parameter} is a secret, and ${recordPath2(formatValueFile4(file))} names no environment`,
3844
3550
  "Keys are declared per environment in the `keys` block of penv.config.ts, so penv cannot tell which key should seal a file that every environment reads. Write it at an environment scope \u2014 add `--env <environment>` \u2014 or drop `secret` from the parameter's meta."
@@ -3940,7 +3646,7 @@ function twins(project, key, options) {
3940
3646
  }
3941
3647
  function environmentFor(project, options, verb) {
3942
3648
  if (options.environment === void 0) {
3943
- throw new PenvError13(
3649
+ throw new PenvError11(
3944
3650
  "SECRET_SCOPE_AMBIGUOUS",
3945
3651
  `\`penv ${verb}\` names no environment, and keys are declared per environment`,
3946
3652
  "Pass `--env <environment>`. penv cannot tell which environment's key applies to a file that names none, and will not pick one for you."
@@ -3959,7 +3665,7 @@ async function runEncrypt(options) {
3959
3665
  const value = await readOne(project, plain);
3960
3666
  if (value === void 0) {
3961
3667
  const already = await readOne(project, sealed);
3962
- throw new PenvError13(
3668
+ throw new PenvError11(
3963
3669
  "PARAMETER_ABSENT",
3964
3670
  already === void 0 ? `Parameter ${parameter} has no value file at ${recordPath3(formatValueFile5(plain))}` : `Parameter ${parameter} is already encrypted at ${recordPath3(formatValueFile5(sealed))}`,
3965
3671
  already === void 0 ? `Write it first with \`penv set ${options.key} --env ${environment}\`, which seals it automatically when the parameter's meta declares it a secret.` : "Nothing to do."
@@ -3980,7 +3686,7 @@ async function runDecrypt(options) {
3980
3686
  const [plain, sealed] = twins(project, options.key, options);
3981
3687
  const parameter = parameterId5(plain);
3982
3688
  if (isSecret5(await project.provider.readMeta(plain), environment)) {
3983
- throw new PenvError13(
3689
+ throw new PenvError11(
3984
3690
  "SECRET_DECRYPT_REFUSED",
3985
3691
  `Parameter ${parameter} is declared a secret for environment ${environment}, so penv will not write it in plaintext`,
3986
3692
  "A secret with a plaintext value file is a `penv doctor` failure. Drop `secret` from the parameter's meta if it is not one, or run `penv generate --allow-decrypt` if you need the plaintext value in a `.env` artifact."
@@ -3988,7 +3694,7 @@ async function runDecrypt(options) {
3988
3694
  }
3989
3695
  const stored = await readOne(project, sealed);
3990
3696
  if (stored === void 0) {
3991
- throw new PenvError13(
3697
+ throw new PenvError11(
3992
3698
  "PARAMETER_ABSENT",
3993
3699
  `Parameter ${parameter} has no encrypted value file at ${recordPath3(formatValueFile5(sealed))}`,
3994
3700
  `Nothing to decrypt. \`penv get ${options.key} --env ${environment} --explain\` shows every file penv looked at.`
@@ -4011,7 +3717,7 @@ async function runDecrypt(options) {
4011
3717
  removed: formatValueFile5(sealed)
4012
3718
  };
4013
3719
  }
4014
- var UndecryptableAt = class extends PenvError13 {
3720
+ var UndecryptableAt = class extends PenvError11 {
4015
3721
  constructor(parameter, environment, location, detail) {
4016
3722
  super(
4017
3723
  "VALUE_UNDECRYPTABLE",
@@ -4066,7 +3772,7 @@ var decryptCommand = defineCommand9({
4066
3772
  });
4067
3773
 
4068
3774
  // src/commands/fill.ts
4069
- import { PenvError as PenvError14, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
3775
+ import { PenvError as PenvError12, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
4070
3776
  import { defineCommand as defineCommand10 } from "citty";
4071
3777
  var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
4072
3778
  async function runFill(options) {
@@ -4081,7 +3787,7 @@ async function runFill(options) {
4081
3787
  const detail = blockers.map(
4082
3788
  (issue) => ` - ${issue.message}${issue.remedy === void 0 ? "" : ` (${issue.remedy})`}`
4083
3789
  ).join("\n");
4084
- throw new PenvError14(
3790
+ throw new PenvError12(
4085
3791
  "FILL_BLOCKED",
4086
3792
  `penv fill cannot run: environment ${environment} has ${blockers.length} unresolved configuration ${blockers.length === 1 ? "issue" : "issues"}:
4087
3793
  ${detail}`,
@@ -4217,11 +3923,11 @@ var fillCommand = defineCommand10({
4217
3923
 
4218
3924
  // src/commands/generate.ts
4219
3925
  import { writeFileSync as writeFileSync3 } from "fs";
4220
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5 } from "path";
3926
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve5 } from "path";
4221
3927
  import {
4222
3928
  checkNameCollisions as checkNameCollisions3,
4223
3929
  effectiveMeta as effectiveMeta2,
4224
- PenvError as PenvError15,
3930
+ PenvError as PenvError13,
4225
3931
  recordPath as recordPath5,
4226
3932
  requireValue as requireValue2,
4227
3933
  serializeDotenv,
@@ -4246,7 +3952,7 @@ function entriesFor(project, environment, allowDecrypt) {
4246
3952
  const winner = resolution.winner;
4247
3953
  if (winner?.file.encrypted === true) {
4248
3954
  if (!allowDecrypt) {
4249
- throw new PenvError15(
3955
+ throw new PenvError13(
4250
3956
  "ENCRYPTED_VALUE_REFUSED",
4251
3957
  `Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath5(winner.location)}, and \`penv generate\` writes plaintext`,
4252
3958
  `Re-run with \`--allow-decrypt\` to write the decrypted value into the artifact, or generate for an environment whose values are plaintext. The artifact is gitignored; a committed plaintext secret is a \`penv doctor\` failure.`
@@ -4277,7 +3983,7 @@ function runGenerate(options) {
4277
3983
  const project = openProject(options.cwd);
4278
3984
  const environment = targetEnvironment(project, options.environment, options.envFlags);
4279
3985
  const { entries, decrypted } = entriesFor(project, environment, options.allowDecrypt === true);
4280
- const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) : isAbsolute3(options.out) ? options.out : resolve5(options.cwd, options.out);
3986
+ const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) : isAbsolute2(options.out) ? options.out : resolve5(options.cwd, options.out);
4281
3987
  writeFileSync3(file, serializeDotenv(entries), "utf8");
4282
3988
  return { file, environment, entries: entries.length, decrypted };
4283
3989
  }
@@ -4330,7 +4036,7 @@ var generateCommand = defineCommand11({
4330
4036
  });
4331
4037
 
4332
4038
  // src/commands/get.ts
4333
- import { PenvError as PenvError16, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
4039
+ import { PenvError as PenvError14, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
4334
4040
  import { defineCommand as defineCommand12 } from "citty";
4335
4041
  async function runGet(options) {
4336
4042
  const project = openProject(options.cwd);
@@ -4340,7 +4046,7 @@ async function runGet(options) {
4340
4046
  const resolution = await resolveParameter(ref, environment, project.provider, keys);
4341
4047
  const value = requireValue3(resolution, environment);
4342
4048
  if (value === void 0) {
4343
- throw new PenvError16(
4049
+ throw new PenvError14(
4344
4050
  "PARAMETER_ABSENT",
4345
4051
  `Parameter ${resolution.parameter} resolves to no value for environment ${environment}`,
4346
4052
  `Set it with \`penv set ${options.key} --env ${environment}\`, or run \`penv get ${options.key} --env ${environment} --explain\` to see every file penv looked at.`
@@ -4416,15 +4122,15 @@ var getCommand = defineCommand12({
4416
4122
  });
4417
4123
 
4418
4124
  // src/commands/import.ts
4419
- import { copyFileSync, existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
4420
- import { basename as basename2, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve7 } from "path";
4125
+ import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
4126
+ import { basename as basename2, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve7 } from "path";
4421
4127
  import {
4422
4128
  assertNever as assertNever2,
4423
4129
  FilenameGrammarError,
4424
4130
  findConfigFile as findConfigFile2,
4425
4131
  loadConfigFrom as loadConfigFrom2,
4426
4132
  lookupEnvironment,
4427
- PenvError as PenvError19,
4133
+ PenvError as PenvError17,
4428
4134
  parseDotenv as parseDotenv2,
4429
4135
  schemaFileOf as schemaFileOf4,
4430
4136
  UnknownEnvironmentError
@@ -4435,7 +4141,7 @@ import { defineCommand as defineCommand14 } from "citty";
4435
4141
  import {
4436
4142
  checkNameCollisions as checkNameCollisions4,
4437
4143
  isReservedToken as isReservedToken3,
4438
- PenvError as PenvError17,
4144
+ PenvError as PenvError15,
4439
4145
  ReservedTokenError as ReservedTokenError3,
4440
4146
  refFromVariable as refFromVariable2,
4441
4147
  roundTripsCleanly,
@@ -4445,7 +4151,7 @@ function assertImportable(ref, variable) {
4445
4151
  if (!ref.name.includes(".")) {
4446
4152
  return;
4447
4153
  }
4448
- throw new PenvError17(
4154
+ throw new PenvError15(
4449
4155
  "IMPORT_UNPARSEABLE_NAME",
4450
4156
  `The variable ${variable} becomes the parameter \`${ref.name}\`, whose \`.\` would be read as a scope`,
4451
4157
  `Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
@@ -4464,7 +4170,7 @@ function assertRoundTrips(ref, variable, config) {
4464
4170
  return;
4465
4171
  }
4466
4172
  const generated = variableName7(ref, config);
4467
- throw new PenvError17(
4173
+ throw new PenvError15(
4468
4174
  "IMPORT_LOSSY_NAME",
4469
4175
  `The variable ${variable} becomes the parameter \`${ref.name}\`, which regenerates as ${generated}`,
4470
4176
  `\`penv generate\` would write ${generated}, so anything reading \`process.env["${variable}"]\` would read \`undefined\`. Declare the name you want in the \`override\` block of penv.config.ts \u2014 \`override: { "${ref.name}": "${variable}" }\` \u2014 then import it again. Nothing was imported.`
@@ -4505,8 +4211,8 @@ function writeEntries(tree, entries, refs, scope) {
4505
4211
  }
4506
4212
 
4507
4213
  // src/detect.ts
4508
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
4509
- import { join as join8 } from "path";
4214
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
4215
+ import { join as join6 } from "path";
4510
4216
  import { DEFAULT_SCHEMA_FILE } from "@penvhq/core";
4511
4217
  var SIGNATURES = [
4512
4218
  { name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
@@ -4523,7 +4229,7 @@ var SIGNATURES = [
4523
4229
  function exportsSchema(file) {
4524
4230
  let source;
4525
4231
  try {
4526
- source = readFileSync7(file, "utf8");
4232
+ source = readFileSync6(file, "utf8");
4527
4233
  } catch {
4528
4234
  return false;
4529
4235
  }
@@ -4534,8 +4240,8 @@ function exportsSchema(file) {
4534
4240
  );
4535
4241
  }
4536
4242
  function occupied(cwd, relative5) {
4537
- const file = join8(cwd, ...relative5.split("/"));
4538
- return existsSync6(file) && !exportsSchema(file);
4243
+ const file = join6(cwd, ...relative5.split("/"));
4244
+ return existsSync4(file) && !exportsSchema(file);
4539
4245
  }
4540
4246
  function schemaFileFor(cwd) {
4541
4247
  const dir = srcPrefix(cwd);
@@ -4550,10 +4256,10 @@ function schemaFileFor(cwd) {
4550
4256
  return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
4551
4257
  }
4552
4258
  function srcPrefix(cwd) {
4553
- return existsSync6(join8(cwd, "src")) ? "src/" : "";
4259
+ return existsSync4(join6(cwd, "src")) ? "src/" : "";
4554
4260
  }
4555
4261
  function dependenciesOf(cwd) {
4556
- const manifest = manifestOf2(cwd);
4262
+ const manifest = manifestOf(cwd);
4557
4263
  if (manifest === void 0) {
4558
4264
  return void 0;
4559
4265
  }
@@ -4568,14 +4274,14 @@ function dependenciesOf(cwd) {
4568
4274
  }
4569
4275
  return names;
4570
4276
  }
4571
- function manifestOf2(cwd) {
4572
- const file = join8(cwd, "package.json");
4573
- if (!existsSync6(file)) {
4277
+ function manifestOf(cwd) {
4278
+ const file = join6(cwd, "package.json");
4279
+ if (!existsSync4(file)) {
4574
4280
  return void 0;
4575
4281
  }
4576
4282
  let manifest;
4577
4283
  try {
4578
- manifest = JSON.parse(readFileSync7(file, "utf8"));
4284
+ manifest = JSON.parse(readFileSync6(file, "utf8"));
4579
4285
  } catch {
4580
4286
  return void 0;
4581
4287
  }
@@ -4591,7 +4297,7 @@ function detectFramework(cwd) {
4591
4297
  return detectedFrom(cwd, signature.name, signature.publicPrefixes);
4592
4298
  }
4593
4299
  }
4594
- if (existsSync6(join8(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
4300
+ if (existsSync4(join6(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
4595
4301
  return detectedFrom(cwd, "Bun", []);
4596
4302
  }
4597
4303
  return void 0;
@@ -4611,7 +4317,7 @@ function detectAlias(cwd) {
4611
4317
  return hasImportsBlock(cwd) ? IMPORTS_ALIAS : DEFAULT_ALIAS;
4612
4318
  }
4613
4319
  function hasImportsBlock(cwd) {
4614
- const manifest = manifestOf2(cwd);
4320
+ const manifest = manifestOf(cwd);
4615
4321
  const imports = manifest?.imports;
4616
4322
  return imports !== null && typeof imports === "object" && !Array.isArray(imports);
4617
4323
  }
@@ -4670,8 +4376,8 @@ function draftFieldsAcross(sources, environments) {
4670
4376
  }
4671
4377
 
4672
4378
  // src/commands/init.ts
4673
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
4674
- import { dirname as dirname6, join as join10, resolve as resolve6 } from "path";
4379
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
4380
+ import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
4675
4381
  import { createInterface as createInterface2 } from "readline/promises";
4676
4382
  import {
4677
4383
  CUTOVER_PATH as CUTOVER_PATH3,
@@ -4679,7 +4385,7 @@ import {
4679
4385
  isLegalEnvironmentName as isLegalEnvironmentName2,
4680
4386
  loadConfigFrom,
4681
4387
  PENV_DIR as PENV_DIR2,
4682
- PenvError as PenvError18,
4388
+ PenvError as PenvError16,
4683
4389
  parameterId as parameterId6,
4684
4390
  parseDotenv,
4685
4391
  RECORDS_PATH as RECORDS_PATH3,
@@ -4696,32 +4402,32 @@ import { defineCommand as defineCommand13 } from "citty";
4696
4402
 
4697
4403
  // src/scaffold-undo.ts
4698
4404
  import {
4699
- existsSync as existsSync7,
4405
+ existsSync as existsSync5,
4700
4406
  mkdirSync as mkdirSync3,
4701
4407
  readdirSync as readdirSync4,
4702
- readFileSync as readFileSync8,
4408
+ readFileSync as readFileSync7,
4703
4409
  rmdirSync,
4704
- statSync as statSync3,
4410
+ statSync as statSync2,
4705
4411
  unlinkSync,
4706
4412
  writeFileSync as writeFileSync4
4707
4413
  } from "fs";
4708
- import { dirname as dirname5, join as join9 } from "path";
4414
+ import { dirname as dirname5, join as join7 } from "path";
4709
4415
  function record(path, files, dirs) {
4710
4416
  let stats;
4711
4417
  try {
4712
- stats = statSync3(path);
4418
+ stats = statSync2(path);
4713
4419
  } catch {
4714
4420
  return;
4715
4421
  }
4716
4422
  if (stats.isDirectory()) {
4717
4423
  dirs.add(path);
4718
4424
  for (const entry of readdirSync4(path)) {
4719
- record(join9(path, entry), files, dirs);
4425
+ record(join7(path, entry), files, dirs);
4720
4426
  }
4721
4427
  return;
4722
4428
  }
4723
4429
  if (stats.isFile()) {
4724
- files.set(path, readFileSync8(path));
4430
+ files.set(path, readFileSync7(path));
4725
4431
  }
4726
4432
  }
4727
4433
  function captureScaffold(root, paths) {
@@ -4729,7 +4435,7 @@ function captureScaffold(root, paths) {
4729
4435
  const dirs = /* @__PURE__ */ new Set();
4730
4436
  for (const path of paths) {
4731
4437
  for (let dir = dirname5(path); dir.startsWith(root) && dir !== root; dir = dirname5(dir)) {
4732
- if (existsSync7(dir)) {
4438
+ if (existsSync5(dir)) {
4733
4439
  dirs.add(dir);
4734
4440
  }
4735
4441
  }
@@ -4752,7 +4458,7 @@ function restoreScaffold(undo) {
4752
4458
  function removeAdded(path, undo) {
4753
4459
  let stats;
4754
4460
  try {
4755
- stats = statSync3(path);
4461
+ stats = statSync2(path);
4756
4462
  } catch {
4757
4463
  return;
4758
4464
  }
@@ -4766,7 +4472,7 @@ function removeAdded(path, undo) {
4766
4472
  return;
4767
4473
  }
4768
4474
  for (const entry of readdirSync4(path)) {
4769
- removeAdded(join9(path, entry), undo);
4475
+ removeAdded(join7(path, entry), undo);
4770
4476
  }
4771
4477
  if (!undo.dirs.has(path) && readdirSync4(path).length === 0) {
4772
4478
  rmdirSync(path);
@@ -4960,7 +4666,7 @@ function suggestEnvironments(root) {
4960
4666
  return [...found].sort();
4961
4667
  }
4962
4668
  function emptyFlag(flag) {
4963
- return new PenvError18(
4669
+ return new PenvError16(
4964
4670
  "INIT_FLAG_EMPTY",
4965
4671
  `\`--${flag}\` was given without a value`,
4966
4672
  flag === "schema" ? `Name the module that exports the schema, e.g. \`--schema src/env.ts\`, or drop the flag to use ${DEFAULT_SCHEMA_FILE2}.` : "Name the environment, e.g. `--env production`, or drop the flag to leave the whitelist empty and declare it in penv.config.ts."
@@ -4984,8 +4690,8 @@ function configOf(decisions) {
4984
4690
  return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
4985
4691
  }
4986
4692
  function declaredIn(root) {
4987
- const file = join10(root, CONFIG_FILE);
4988
- if (!existsSync8(file)) {
4693
+ const file = join8(root, CONFIG_FILE);
4694
+ if (!existsSync6(file)) {
4989
4695
  return void 0;
4990
4696
  }
4991
4697
  return loadConfigFrom(file);
@@ -5023,7 +4729,7 @@ function planInit(root, flags = {}) {
5023
4729
  throw emptyFlag("alias");
5024
4730
  }
5025
4731
  if (!ALIAS_NAME.test(alias)) {
5026
- throw new PenvError18(
4732
+ throw new PenvError16(
5027
4733
  "INIT_ALIAS_INVALID",
5028
4734
  `\`${alias}\` is not an alias penv can write`,
5029
4735
  `An alias is \`@name\` \u2014 a tsconfig \`paths\` entry a bundler resolves \u2014 or \`#name\`, a package.json \`imports\` entry Node resolves itself. Those are the two things a module specifier can be that is not a package.`
@@ -5357,7 +5063,7 @@ ${objectIndent}${source.slice(close)}`;
5357
5063
  ${entryIndent}${member},${source.slice(open + 1)}`;
5358
5064
  }
5359
5065
  function shapeError(what, target, alias) {
5360
- return new PenvError18(
5066
+ return new PenvError16(
5361
5067
  "TSCONFIG_SHAPE",
5362
5068
  `penv cannot add the \`${alias}\` path alias to tsconfig.json: ${what}`,
5363
5069
  `Add it by hand: \`{ "compilerOptions": { "paths": { "${alias}": ["${target}"] } } }\`.`
@@ -5428,15 +5134,15 @@ function renderTsconfig(target, alias) {
5428
5134
  }
5429
5135
  function ensurePenvDir(root) {
5430
5136
  const dir = resolve6(root, PENV_DIR2);
5431
- if (existsSync8(dir)) {
5137
+ if (existsSync6(dir)) {
5432
5138
  return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR2}/` };
5433
5139
  }
5434
5140
  mkdirSync4(dir, { recursive: true });
5435
5141
  return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR2}/` };
5436
5142
  }
5437
5143
  function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
5438
- const file = join10(root, SCHEMA_SHAPE_FILE3);
5439
- if (existsSync8(file)) {
5144
+ const file = join8(root, SCHEMA_SHAPE_FILE3);
5145
+ if (existsSync6(file)) {
5440
5146
  return {
5441
5147
  target: "schema",
5442
5148
  action: "kept",
@@ -5462,8 +5168,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
5462
5168
  };
5463
5169
  }
5464
5170
  function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5465
- const file = join10(root, ...decisions.schemaFile.split("/"));
5466
- if (existsSync8(file)) {
5171
+ const file = join8(root, ...decisions.schemaFile.split("/"));
5172
+ if (existsSync6(file)) {
5467
5173
  return {
5468
5174
  target: "env",
5469
5175
  action: "kept",
@@ -5481,8 +5187,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5481
5187
  };
5482
5188
  }
5483
5189
  function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
5484
- const file = join10(root, CONFIG_FILE);
5485
- if (existsSync8(file)) {
5190
+ const file = join8(root, CONFIG_FILE);
5191
+ if (existsSync6(file)) {
5486
5192
  return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
5487
5193
  }
5488
5194
  writeFileSync5(file, renderConfigModule(decisions), "utf8");
@@ -5491,9 +5197,9 @@ function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
5491
5197
  function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5492
5198
  const alias = decisions.alias;
5493
5199
  const imports = alias.startsWith(IMPORTS_PREFIX);
5494
- const file = join10(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5200
+ const file = join8(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5495
5201
  const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
5496
- if (!existsSync8(file)) {
5202
+ if (!existsSync6(file)) {
5497
5203
  if (imports) {
5498
5204
  return {
5499
5205
  target: "tsconfig",
@@ -5509,7 +5215,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5509
5215
  text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
5510
5216
  };
5511
5217
  }
5512
- const source = readFileSync9(file, "utf8");
5218
+ const source = readFileSync8(file, "utf8");
5513
5219
  const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
5514
5220
  if (edit.conflict !== void 0) {
5515
5221
  return {
@@ -5534,9 +5240,9 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5534
5240
  };
5535
5241
  }
5536
5242
  function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
5537
- const file = join10(root, ...STATE_GITIGNORE_PATH.split("/"));
5243
+ const file = join8(root, ...STATE_GITIGNORE_PATH.split("/"));
5538
5244
  const wanted = renderStateGitignore(configOf(decisions));
5539
- const existing = existsSync8(file) ? readFileSync9(file, "utf8") : void 0;
5245
+ const existing = existsSync6(file) ? readFileSync8(file, "utf8") : void 0;
5540
5246
  if (existing === wanted) {
5541
5247
  return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
5542
5248
  }
@@ -5557,12 +5263,12 @@ function outdatedRuntimeWarning(root) {
5557
5263
  return `Injection needs @penvhq/penv ${INJECT_MIN_VERSION}+ \u2014 this project has ${version}, whose \`load\` ignores \`{ inject: true }\`. Upgrade, or process.env stays empty.`;
5558
5264
  }
5559
5265
  function installedPenvVersion(root) {
5560
- const file = join10(root, "node_modules", "@penvhq", "penv", "package.json");
5561
- if (!existsSync8(file)) {
5266
+ const file = join8(root, "node_modules", "@penvhq", "penv", "package.json");
5267
+ if (!existsSync6(file)) {
5562
5268
  return void 0;
5563
5269
  }
5564
5270
  try {
5565
- const version = JSON.parse(readFileSync9(file, "utf8")).version;
5271
+ const version = JSON.parse(readFileSync8(file, "utf8")).version;
5566
5272
  return typeof version === "string" ? version : void 0;
5567
5273
  } catch {
5568
5274
  return void 0;
@@ -5602,11 +5308,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
5602
5308
  };
5603
5309
  }
5604
5310
  const alsoNote = writeAlso(root, seam.also);
5605
- const file = join10(root, ...seam.file.split("/"));
5311
+ const file = join8(root, ...seam.file.split("/"));
5606
5312
  const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
5607
5313
  const notes = baseNotes.length === 0 ? "" : `
5608
5314
  ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
5609
- if (existsSync8(file)) {
5315
+ if (existsSync6(file)) {
5610
5316
  return {
5611
5317
  target: "seam",
5612
5318
  action: "info",
@@ -5627,8 +5333,8 @@ function writeAlso(root, also) {
5627
5333
  if (also === void 0) {
5628
5334
  return void 0;
5629
5335
  }
5630
- const file = join10(root, ...also.file.split("/"));
5631
- if (existsSync8(file)) {
5336
+ const file = join8(root, ...also.file.split("/"));
5337
+ if (existsSync6(file)) {
5632
5338
  return also.ifPresent;
5633
5339
  }
5634
5340
  mkdirSync4(dirname6(file), { recursive: true });
@@ -5658,7 +5364,7 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
5658
5364
  return seam === void 0 ? steps : [...steps, seam];
5659
5365
  }
5660
5366
  function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
5661
- const fileAt = (relative5) => join10(root, ...relative5.split("/"));
5367
+ const fileAt = (relative5) => join8(root, ...relative5.split("/"));
5662
5368
  const seam = decisions.inject ? seamFor(framework, {
5663
5369
  alias: decisions.alias,
5664
5370
  srcDir: srcPrefix(root),
@@ -5699,7 +5405,7 @@ function planCutover(input) {
5699
5405
  assertBundleResolved(root);
5700
5406
  const selected = [...input.selected];
5701
5407
  if (selected.length === 0) {
5702
- throw new PenvError18(
5408
+ throw new PenvError16(
5703
5409
  "INIT_NOTHING_SELECTED",
5704
5410
  "No dotenv file was selected, so there is nothing to migrate",
5705
5411
  "Run `penv init` again and choose the files penv should adopt."
@@ -5711,7 +5417,7 @@ function planCutover(input) {
5711
5417
  const environments = declared === void 0 ? chosen : declared.environments;
5712
5418
  for (const environment of chosen) {
5713
5419
  if (!environments.includes(environment)) {
5714
- throw new PenvError18(
5420
+ throw new PenvError16(
5715
5421
  "INIT_ENVIRONMENT_UNDECLARED",
5716
5422
  `${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
5717
5423
  `Add \`${environment}\` to \`environments\` in ${CONFIG_FILE}, with a provider for it, then run \`penv init\` again. Nothing was changed.`
@@ -5736,7 +5442,7 @@ function planCutover(input) {
5736
5442
  const diagnostics = [];
5737
5443
  let variables = 0;
5738
5444
  for (const file of selected) {
5739
- const parsed = parseDotenv(readFileSync9(join10(root, file.name), "utf8"));
5445
+ const parsed = parseDotenv(readFileSync8(join8(root, file.name), "utf8"));
5740
5446
  const fileRefs = refsForEntries(parsed.entries, file.name, config);
5741
5447
  adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
5742
5448
  refs.push(...fileRefs);
@@ -5798,7 +5504,7 @@ function requireEnvironment(input) {
5798
5504
  if (environment.length > 0) {
5799
5505
  return environment;
5800
5506
  }
5801
- throw new PenvError18(
5507
+ throw new PenvError16(
5802
5508
  "INIT_ENVIRONMENT_UNNAMED",
5803
5509
  "The selected files name no environment, and penv does not invent one",
5804
5510
  `Run \`penv init --env ${DEVELOPMENT}\` to say which environment these values are for.`
@@ -5810,7 +5516,7 @@ function assertCascadeComplete(root, selected, environments) {
5810
5516
  for (const environment of environments) {
5811
5517
  for (const name of cascadeFor(environment)) {
5812
5518
  if (present.has(name) && !taken.has(name)) {
5813
- throw new PenvError18(
5519
+ throw new PenvError16(
5814
5520
  "INIT_CUTOVER_INCOMPLETE",
5815
5521
  `${name} is part of ${environment}'s cascade and was not selected, so your framework would keep reading it beside penv`,
5816
5522
  "Run `penv init` again and take every file penv listed for that environment. Nothing was changed."
@@ -5823,7 +5529,7 @@ function assertBundleResolved(root) {
5823
5529
  if (!bundleUnresolved(root)) {
5824
5530
  return;
5825
5531
  }
5826
- throw new PenvError18(
5532
+ throw new PenvError16(
5827
5533
  "INIT_BUNDLE_UNRESOLVED",
5828
5534
  `The dotenv files from the last cutover are still in ${ROLLBACK_DOTENV_PATH3}/, and penv will not migrate a second time over them`,
5829
5535
  "Run `penv init undo` to put them back, or `penv cleanup` to drop them once you are happy with the migration."
@@ -5835,7 +5541,7 @@ function selectionForYes(plan2) {
5835
5541
  (file) => file.environment !== void 0 && file.environment !== DEVELOPMENT
5836
5542
  );
5837
5543
  if (shared !== void 0 && other !== void 0) {
5838
- throw new PenvError18(
5544
+ throw new PenvError16(
5839
5545
  "INIT_YES_SHARED_FALLBACK",
5840
5546
  `${other.name} falls back to the shared ${shared.name} this cutover would move, so \`--yes\` will not decide what happens to ${other.environment ?? ""}`,
5841
5547
  "Run `penv init` without `--yes` and choose every file the cutover takes. Nothing was changed."
@@ -5881,7 +5587,7 @@ function issueLines(result2) {
5881
5587
  }
5882
5588
  var SCAFFOLD_ROLLED_BACK = "penv put the project back as it found it \u2014 your dotenv files and everything else are exactly where they were.";
5883
5589
  function invalidAfterImport(result2) {
5884
- return new PenvError18(
5590
+ return new PenvError16(
5885
5591
  "INIT_CUTOVER_INVALID",
5886
5592
  `The imported values do not satisfy the draft schema for ${result2.environment}:
5887
5593
  ${issueLines(result2)}`,
@@ -5889,7 +5595,7 @@ ${issueLines(result2)}`,
5889
5595
  );
5890
5596
  }
5891
5597
  function draftNotLoaded(result2) {
5892
- return new PenvError18(
5598
+ return new PenvError16(
5893
5599
  "INIT_DRAFT_NOT_LOADED",
5894
5600
  `penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
5895
5601
  ${issueLines(result2)}`,
@@ -5980,12 +5686,12 @@ function dailyCommand(root) {
5980
5686
  return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
5981
5687
  }
5982
5688
  function devScript(root) {
5983
- const file = join10(root, PACKAGE_FILE);
5984
- if (!existsSync8(file)) {
5689
+ const file = join8(root, PACKAGE_FILE);
5690
+ if (!existsSync6(file)) {
5985
5691
  return "dev";
5986
5692
  }
5987
5693
  try {
5988
- const scripts = JSON.parse(readFileSync9(file, "utf8")).scripts;
5694
+ const scripts = JSON.parse(readFileSync8(file, "utf8")).scripts;
5989
5695
  if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
5990
5696
  const named = Object.keys(scripts);
5991
5697
  return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
@@ -6029,7 +5735,7 @@ async function promptForSelection(plan2, io) {
6029
5735
  for (const name of names) {
6030
5736
  const file = plan2.found.find((candidate) => candidate.name === name);
6031
5737
  if (file === void 0) {
6032
- throw new PenvError18(
5738
+ throw new PenvError16(
6033
5739
  "INIT_SELECTION_UNKNOWN",
6034
5740
  `\`${name}\` is not one of the dotenv files penv found`,
6035
5741
  "Run `penv init` again and name the files exactly as they are listed, e.g. `.env.production`."
@@ -6192,7 +5898,7 @@ var initCommand = defineCommand13({
6192
5898
  });
6193
5899
  function runUndoAction(root, action) {
6194
5900
  if (action !== "undo") {
6195
- throw new PenvError18(
5901
+ throw new PenvError16(
6196
5902
  "INIT_UNKNOWN_ACTION",
6197
5903
  `\`penv init ${action}\` is not something init does`,
6198
5904
  "Run `penv init undo` to put back the dotenv files of the last cutover."
@@ -6298,7 +6004,7 @@ function explicitEnvironment(options, source, config) {
6298
6004
  if (value.length > 0) {
6299
6005
  return value;
6300
6006
  }
6301
- throw new PenvError19(
6007
+ throw new PenvError17(
6302
6008
  "IMPORT_ENV_FLAG_EMPTY",
6303
6009
  `\`--env\` for the import of ${source} names no environment`,
6304
6010
  `Pass a declared environment \u2014 ${config.environments.map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to import ${source} as the scope that has no environment. Nothing was imported.`
@@ -6308,7 +6014,7 @@ function assertEnvironmentAgrees(derived, explicit, source) {
6308
6014
  if (derived === void 0 || explicit === void 0 || derived === explicit) {
6309
6015
  return;
6310
6016
  }
6311
- throw new PenvError19(
6017
+ throw new PenvError17(
6312
6018
  "IMPORT_ENV_CONFLICT",
6313
6019
  `The file ${source} is scoped to environment ${derived}, but \`--env ${explicit}\` names ${explicit}`,
6314
6020
  `Drop \`--env\` to import ${source} as ${derived}, pass \`--env ${derived}\` to say the same thing twice, or point \`penv import\` at the file that holds ${explicit}'s values. Nothing was imported.`
@@ -6353,15 +6059,15 @@ function configInEffect(cwd, environment) {
6353
6059
  }
6354
6060
  function importDotenv(options) {
6355
6061
  const cwd = resolve7(options.cwd);
6356
- const file = isAbsolute4(options.file) ? options.file : resolve7(cwd, options.file);
6357
- if (!existsSync9(file)) {
6358
- throw new PenvError19(
6062
+ const file = isAbsolute3(options.file) ? options.file : resolve7(cwd, options.file);
6063
+ if (!existsSync7(file)) {
6064
+ throw new PenvError17(
6359
6065
  "IMPORT_FILE_MISSING",
6360
6066
  `There is no file at ${file} to import`,
6361
6067
  "Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
6362
6068
  );
6363
6069
  }
6364
- const parsed = parseDotenv2(readFileSync10(file, "utf8"));
6070
+ const parsed = parseDotenv2(readFileSync9(file, "utf8"));
6365
6071
  const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
6366
6072
  const source = displayPath3(cwd, file);
6367
6073
  const named = scopeFromFilename(file, config);
@@ -6479,19 +6185,19 @@ var importCommand = defineCommand14({
6479
6185
 
6480
6186
  // src/commands/key.ts
6481
6187
  import { randomBytes } from "crypto";
6482
- import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError21 } from "@penvhq/core";
6188
+ import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError19 } from "@penvhq/core";
6483
6189
  import { defineCommand as defineCommand15 } from "citty";
6484
6190
 
6485
6191
  // src/keychain.ts
6486
6192
  import { createRequire as createRequire3 } from "module";
6487
- import { PenvError as PenvError20 } from "@penvhq/core";
6193
+ import { PenvError as PenvError18 } from "@penvhq/core";
6488
6194
  var KEYRING_MODULE = "@napi-rs/keyring";
6489
6195
  var nodeRequire = (id) => createRequire3(import.meta.url)(id);
6490
6196
  function firstLine2(cause) {
6491
6197
  return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
6492
6198
  }
6493
6199
  function keyringMissing(cause) {
6494
- return new PenvError20(
6200
+ return new PenvError18(
6495
6201
  "KEYCHAIN_BINDING_MISSING",
6496
6202
  `penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
6497
6203
  `This penv engine was installed as a plain tarball, which carries no native modules. Install penv with \`npm install -g @penvhq/launcher\`, or declare \`source: "env"\` for this environment and export its key. Original error: ${firstLine2(cause)}`
@@ -6533,7 +6239,7 @@ function runKeyCreate(options) {
6533
6239
  const environment = targetEnvironment(project, options.environment);
6534
6240
  const declared = project.config.keys?.[environment];
6535
6241
  if (declared === void 0) {
6536
- throw new PenvError21(
6242
+ throw new PenvError19(
6537
6243
  "KEY_SOURCE_UNDECLARED",
6538
6244
  `Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
6539
6245
  `Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
@@ -6547,17 +6253,17 @@ function runKeyCreate(options) {
6547
6253
  try {
6548
6254
  existing = keychain.getPassword(KEYCHAIN_SERVICE, declared.id);
6549
6255
  } catch (cause) {
6550
- if (cause instanceof PenvError21) {
6256
+ if (cause instanceof PenvError19) {
6551
6257
  throw cause;
6552
6258
  }
6553
- throw new PenvError21(
6259
+ throw new PenvError19(
6554
6260
  "KEYCHAIN_UNAVAILABLE",
6555
6261
  `penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
6556
6262
  `Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
6557
6263
  );
6558
6264
  }
6559
6265
  if (existing !== null) {
6560
- throw new PenvError21(
6266
+ throw new PenvError19(
6561
6267
  "KEY_EXISTS",
6562
6268
  `Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
6563
6269
  "Replacing it would orphan every value already sealed under it \u2014 they could never be decrypted again. Re-run with `--force` only if you are certain nothing is sealed under the current key."
@@ -6712,21 +6418,21 @@ var listCommand = defineCommand16({
6712
6418
 
6713
6419
  // src/commands/migrate.ts
6714
6420
  import {
6715
- existsSync as existsSync10,
6421
+ existsSync as existsSync8,
6716
6422
  mkdirSync as mkdirSync5,
6717
6423
  readdirSync as readdirSync6,
6718
- readFileSync as readFileSync11,
6424
+ readFileSync as readFileSync10,
6719
6425
  renameSync as renameSync2,
6720
6426
  rmSync as rmSync2,
6721
6427
  writeFileSync as writeFileSync6
6722
6428
  } from "fs";
6723
- import { dirname as dirname7, join as join11 } from "path";
6429
+ import { dirname as dirname7, join as join9 } from "path";
6724
6430
  import { createInterface as createInterface3 } from "readline/promises";
6725
6431
  import {
6726
6432
  loadConfig as loadConfig2,
6727
6433
  oldLayoutEntries,
6728
6434
  PENV_DIR as PENV_DIR3,
6729
- PenvError as PenvError22,
6435
+ PenvError as PenvError20,
6730
6436
  RECORDS_PATH as RECORDS_PATH5,
6731
6437
  recordsDir as recordsDir3,
6732
6438
  renderStateGitignore as renderStateGitignore2,
@@ -6741,17 +6447,17 @@ function planMigrate(cwd) {
6741
6447
  const tree = recordsDir3(root);
6742
6448
  const collisions = collidingEntries(entries, tree);
6743
6449
  if (collisions.length > 0) {
6744
- throw new PenvError22(
6450
+ throw new PenvError20(
6745
6451
  "HALF_MIGRATED",
6746
- `${describe2(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
6452
+ `${describe(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
6747
6453
  `Move what is left under \`${PENV_DIR3}/\` into \`${RECORDS_PATH5}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
6748
6454
  );
6749
6455
  }
6750
6456
  const creates = [];
6751
- if (entries.length > 0 && !existsSync10(tree)) {
6457
+ if (entries.length > 0 && !existsSync8(tree)) {
6752
6458
  creates.push(`${RECORDS_PATH5}/`);
6753
6459
  }
6754
- if (readIfPresent(join11(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
6460
+ if (readIfPresent(join9(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
6755
6461
  creates.push(STATE_GITIGNORE_PATH2);
6756
6462
  }
6757
6463
  return {
@@ -6761,11 +6467,11 @@ function planMigrate(cwd) {
6761
6467
  to: `${RECORDS_PATH5}/${entry}`
6762
6468
  })),
6763
6469
  creates,
6764
- removes: existsSync10(join11(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6470
+ removes: existsSync8(join9(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6765
6471
  };
6766
6472
  }
6767
6473
  function readIfPresent(file) {
6768
- return existsSync10(file) ? readFileSync11(file, "utf8") : void 0;
6474
+ return existsSync8(file) ? readFileSync10(file, "utf8") : void 0;
6769
6475
  }
6770
6476
  function collidingEntries(entries, tree) {
6771
6477
  let held;
@@ -6777,7 +6483,7 @@ function collidingEntries(entries, tree) {
6777
6483
  const taken = new Set(held.map((name) => name.toLowerCase()));
6778
6484
  return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
6779
6485
  }
6780
- function describe2(names) {
6486
+ function describe(names) {
6781
6487
  return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
6782
6488
  }
6783
6489
  function isNoop(plan2) {
@@ -6791,16 +6497,16 @@ function applyMigrate(plan2) {
6791
6497
  if (plan2.moves.length > 0) {
6792
6498
  mkdirSync5(recordsDir3(plan2.root), { recursive: true });
6793
6499
  for (const move of plan2.moves) {
6794
- renameSync2(join11(plan2.root, ...move.from.split("/")), join11(plan2.root, ...move.to.split("/")));
6500
+ renameSync2(join9(plan2.root, ...move.from.split("/")), join9(plan2.root, ...move.to.split("/")));
6795
6501
  }
6796
6502
  }
6797
6503
  if (plan2.creates.includes(STATE_GITIGNORE_PATH2)) {
6798
- const ignore = join11(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
6504
+ const ignore = join9(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
6799
6505
  mkdirSync5(dirname7(ignore), { recursive: true });
6800
6506
  writeFileSync6(ignore, renderStateGitignore2(config), "utf8");
6801
6507
  }
6802
6508
  for (const removed of plan2.removes) {
6803
- rmSync2(join11(plan2.root, ...removed.split("/")), { force: true });
6509
+ rmSync2(join9(plan2.root, ...removed.split("/")), { force: true });
6804
6510
  }
6805
6511
  return { ...plan2, status: "migrated" };
6806
6512
  }
@@ -6882,7 +6588,7 @@ import {
6882
6588
  formatMetaFile,
6883
6589
  formatValueFile as formatValueFile6,
6884
6590
  openValue as openValue4,
6885
- PenvError as PenvError23,
6591
+ PenvError as PenvError21,
6886
6592
  parameterId as parameterId7,
6887
6593
  recordPath as recordPath7,
6888
6594
  sealValue as sealValue3
@@ -6902,7 +6608,7 @@ async function planFile(project, source, target, parameter) {
6902
6608
  }
6903
6609
  const environment = environmentOf2(source);
6904
6610
  if (environment === void 0) {
6905
- throw new PenvError23(
6611
+ throw new PenvError21(
6906
6612
  "SECRET_SCOPE_AMBIGUOUS",
6907
6613
  `${recordPath7(formatValueFile6(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
6908
6614
  "Keys are declared per environment in the `keys` block of penv.config.ts. Decrypt it with `penv decrypt`, move the parameter, then encrypt it again at its new address."
@@ -6911,7 +6617,7 @@ async function planFile(project, source, target, parameter) {
6911
6617
  const keys = keySourceFor(project, environment);
6912
6618
  const opened = openValue4(source, stored, keys);
6913
6619
  if (opened.kind === "failed") {
6914
- throw new PenvError23(
6620
+ throw new PenvError21(
6915
6621
  "VALUE_UNDECRYPTABLE",
6916
6622
  `${recordPath7(formatValueFile6(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
6917
6623
  "A sealed value is bound to the file it lives in, so moving it means opening it and sealing it again. Make the key available and run this again. Nothing has been moved."
@@ -6934,7 +6640,7 @@ async function runMove(options) {
6934
6640
  assertWritableKey(options.to);
6935
6641
  const to = refFromKey(options.to, project.config);
6936
6642
  if (parameterId7(from) === parameterId7(to)) {
6937
- throw new PenvError23(
6643
+ throw new PenvError21(
6938
6644
  "PARAMETER_UNCHANGED",
6939
6645
  `\`${options.from}\` and \`${options.to}\` are the same parameter`,
6940
6646
  "Name a different destination, e.g. `penv mv redis-password redis/password`."
@@ -6944,7 +6650,7 @@ async function runMove(options) {
6944
6650
  const sources = filesOf(all, from);
6945
6651
  const meta = await project.provider.readMeta(from);
6946
6652
  if (sources.length === 0 && meta === void 0) {
6947
- throw new PenvError23(
6653
+ throw new PenvError21(
6948
6654
  "PARAMETER_ABSENT",
6949
6655
  `Parameter ${parameterId7(from)} has no value files and no meta, so there is nothing to move`,
6950
6656
  `\`penv list\` shows every parameter penv holds.`
@@ -6952,7 +6658,7 @@ async function runMove(options) {
6952
6658
  }
6953
6659
  const occupied2 = filesOf(all, to);
6954
6660
  if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
6955
- throw new PenvError23(
6661
+ throw new PenvError21(
6956
6662
  "PARAMETER_EXISTS",
6957
6663
  `Parameter ${parameterId7(to)} already exists, and penv will not merge two parameters into one`,
6958
6664
  `Remove or rename ${parameterId7(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
@@ -7111,7 +6817,7 @@ import {
7111
6817
  beginRotation,
7112
6818
  completeRotation,
7113
6819
  holdsRecords as holdsRecords2,
7114
- PenvError as PenvError24,
6820
+ PenvError as PenvError22,
7115
6821
  retainsPrevious,
7116
6822
  rotationOf as rotationOf2
7117
6823
  } from "@penvhq/core";
@@ -7140,7 +6846,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
7140
6846
  }
7141
6847
  function requireNewValue(value, phase, key) {
7142
6848
  if (value === void 0) {
7143
- throw new PenvError24(
6849
+ throw new PenvError22(
7144
6850
  "ROTATION_NO_VALUE",
7145
6851
  `A ${phase} rotation of ${key} writes a new value, and none was given`,
7146
6852
  "Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
@@ -7150,7 +6856,7 @@ function requireNewValue(value, phase, key) {
7150
6856
  }
7151
6857
  function requireRetaining(provider, environment) {
7152
6858
  if (!retainsPrevious(provider)) {
7153
- throw new PenvError24(
6859
+ throw new PenvError22(
7154
6860
  "ROTATION_NOT_RETAINING",
7155
6861
  `A dual-valid rotation needs the previous value to stay readable during the grace window, and the \`${provider.type}\` provider for environment ${environment} does not retain it`,
7156
6862
  "Point this environment at a provider that keeps prior versions (its `readPrevious` is what penv reads during the window), or, if a momentary overlap is not required, declare the parameter `atomic-cutover` in its meta and flip it in one step."
@@ -7164,7 +6870,7 @@ async function runRotate(options) {
7164
6870
  const ref = refFromKey(options.key, project.config);
7165
6871
  const source = await sourceProviderFor(project, environment);
7166
6872
  if (!holdsRecords2(source)) {
7167
- throw new PenvError24(
6873
+ throw new PenvError22(
7168
6874
  "ROTATION_NOT_RECORDS",
7169
6875
  `Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
7170
6876
  "Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
@@ -7175,7 +6881,7 @@ async function runRotate(options) {
7175
6881
  const before = await provider.readMeta(ref);
7176
6882
  const { mechanism } = rotationOf2(before, environment);
7177
6883
  if (mechanism === void 0) {
7178
- throw new PenvError24(
6884
+ throw new PenvError22(
7179
6885
  "ROTATION_NO_MECHANISM",
7180
6886
  `Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
7181
6887
  'Set `rotationMechanism` in the parameter\'s meta to `"dual-valid"` (a grace-window overlap) or `"atomic-cutover"` (a single flip), then run `penv rotate` again.'
@@ -7185,7 +6891,7 @@ async function runRotate(options) {
7185
6891
  const complete = options.complete === true;
7186
6892
  if (mechanism === "atomic-cutover") {
7187
6893
  if (begin || complete) {
7188
- throw new PenvError24(
6894
+ throw new PenvError22(
7189
6895
  "ROTATION_MECHANISM_MISMATCH",
7190
6896
  `Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
7191
6897
  "Run `penv rotate <key> <value>` with no phase flag to flip it. `--begin`/`--complete` bracket a dual-valid grace window, which atomic-cutover has none of."
@@ -7199,7 +6905,7 @@ async function runRotate(options) {
7199
6905
  }
7200
6906
  const retaining = requireRetaining(provider, environment);
7201
6907
  if (begin === complete) {
7202
- throw new PenvError24(
6908
+ throw new PenvError22(
7203
6909
  "ROTATION_PHASE_REQUIRED",
7204
6910
  `A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
7205
6911
  "`--begin` writes the new value and opens the grace window; `--complete` closes it once every reader has moved to the new value. Run them in that order, one at a time."
@@ -7302,7 +7008,7 @@ var rotateCommand = defineCommand20({
7302
7008
  });
7303
7009
 
7304
7010
  // src/commands/watch.ts
7305
- import { existsSync as existsSync11, watch as watch2 } from "fs";
7011
+ import { existsSync as existsSync9, watch as watch2 } from "fs";
7306
7012
  import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
7307
7013
  import { SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE4, schemaFileOf as schemaFileOf5, schemaInsideTree } from "@penvhq/core";
7308
7014
  import { defineCommand as defineCommand21 } from "citty";
@@ -7370,14 +7076,14 @@ function runWatch(options) {
7370
7076
  if (closed || recovery === void 0) {
7371
7077
  return;
7372
7078
  }
7373
- if (!existsSync11(parent)) {
7079
+ if (!existsSync9(parent)) {
7374
7080
  stop2(recovery);
7375
7081
  return;
7376
7082
  }
7377
7083
  if (filename !== null && basename3(filename) !== name) {
7378
7084
  return;
7379
7085
  }
7380
- if (!existsSync11(target)) {
7086
+ if (!existsSync9(target)) {
7381
7087
  return;
7382
7088
  }
7383
7089
  stop2(recovery);
@@ -7401,7 +7107,7 @@ function runWatch(options) {
7401
7107
  if (closed) {
7402
7108
  return;
7403
7109
  }
7404
- if (!existsSync11(target)) {
7110
+ if (!existsSync9(target)) {
7405
7111
  if (watcher !== void 0) {
7406
7112
  stop2(watcher);
7407
7113
  }