@penvhq/cli 0.10.0 → 0.12.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,21 @@
1
+ import {
2
+ detectPackageManager,
3
+ engineVersion,
4
+ installWithPackageManager,
5
+ installedPackages,
6
+ noCommand,
7
+ planInstall,
8
+ renderInstallPlan,
9
+ startChild
10
+ } from "./chunk-NHTDIZZ2.js";
11
+
1
12
  // src/index.ts
2
13
  import { setKeychain } from "@penvhq/core";
3
14
  import { runMain as cittyRunMain, defineCommand as defineCommand22 } from "citty";
4
15
 
5
16
  // src/commands/artifact.ts
6
17
  import { mkdirSync, writeFileSync } from "fs";
7
- import { dirname as dirname3, isAbsolute as isAbsolute2, relative, resolve as resolve3 } from "path";
18
+ import { dirname as dirname3, isAbsolute, relative, resolve as resolve3 } from "path";
8
19
  import {
9
20
  ARTIFACT_FORMAT,
10
21
  candidatesFor as candidatesFor2,
@@ -12,7 +23,7 @@ import {
12
23
  formatValueFile as formatValueFile2,
13
24
  isSecret as isSecret2,
14
25
  keySourceIdentifier,
15
- PenvError as PenvError9,
26
+ PenvError as PenvError6,
16
27
  parameterId as parameterId3,
17
28
  serializeArtifact,
18
29
  variableName as variableName3
@@ -20,313 +31,9 @@ import {
20
31
  import { assertDeliverableNames, declaredRefs as declaredRefs2 } from "@penvhq/runtime";
21
32
  import { defineCommand as defineCommand4 } from "citty";
22
33
 
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
34
  // src/project.ts
328
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
329
- import { dirname, join as join4 } from "path";
35
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
36
+ import { dirname, join as join2 } from "path";
330
37
  import {
331
38
  assertMigrated,
332
39
  candidatesFor,
@@ -335,7 +42,7 @@ import {
335
42
  isReservedToken,
336
43
  loadConfig,
337
44
  openValue,
338
- PenvError as PenvError5,
45
+ PenvError as PenvError3,
339
46
  parameterId,
340
47
  ReservedTokenError,
341
48
  recordsDir as recordsDir2,
@@ -348,7 +55,7 @@ import {
348
55
  import { FilesystemProvider } from "@penvhq/provider-filesystem";
349
56
 
350
57
  // src/env-flags.ts
351
- import { PenvError as PenvError3 } from "@penvhq/core";
58
+ import { PenvError } from "@penvhq/core";
352
59
  var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
353
60
  var COMMAND_FLAGS = [
354
61
  ...BASE_RESERVED,
@@ -381,14 +88,14 @@ function environmentFromShorthand(config, candidates, explicit) {
381
88
  const hits = candidates.filter((candidate) => config.environments.includes(candidate));
382
89
  const strangers = candidates.filter((candidate) => !config.environments.includes(candidate));
383
90
  if (strangers.length > 0) {
384
- throw new PenvError3(
91
+ throw new PenvError(
385
92
  "UNKNOWN_FLAG",
386
93
  `${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
94
  `Declared environments work as bare flags: ${quoteList(config.environments)}. Anything else needs \`--env <name>\`.`
388
95
  );
389
96
  }
390
97
  if (hits.length > 1) {
391
- throw new PenvError3(
98
+ throw new PenvError(
392
99
  "ENVIRONMENT_FLAG_AMBIGUOUS",
393
100
  `${quoteList(hits)} name two environments at once, and a command acts on exactly one`,
394
101
  "Pass a single environment flag, or the canonical `--env <name>`."
@@ -396,7 +103,7 @@ function environmentFromShorthand(config, candidates, explicit) {
396
103
  }
397
104
  const hit = hits[0];
398
105
  if (hit !== void 0 && explicit !== void 0 && explicit !== hit) {
399
- throw new PenvError3(
106
+ throw new PenvError(
400
107
  "ENVIRONMENT_FLAG_AMBIGUOUS",
401
108
  `\`--env ${explicit}\` and \`--${hit}\` name two environments at once`,
402
109
  "Drop one of them \u2014 `--env` is the canonical spelling."
@@ -406,9 +113,9 @@ function environmentFromShorthand(config, candidates, explicit) {
406
113
  }
407
114
 
408
115
  // src/registry.ts
409
- import { readFileSync as readFileSync2 } from "fs";
116
+ import { readFileSync } from "fs";
410
117
  import { createRequire } from "module";
411
- import { join as join3, resolve } from "path";
118
+ import { join, resolve } from "path";
412
119
  import { pathToFileURL } from "url";
413
120
  import {
414
121
  holdsProjection,
@@ -416,7 +123,7 @@ import {
416
123
  localExtensionsFile,
417
124
  MANIFEST_PATH,
418
125
  PENV_DIR,
419
- PenvError as PenvError4,
126
+ PenvError as PenvError2,
420
127
  packageDir,
421
128
  packageEntry,
422
129
  parseLocalExtensions,
@@ -475,7 +182,7 @@ async function loadPluginProvider(type, context) {
475
182
  }
476
183
  const factory = mod[PLUGIN_FACTORY_EXPORT];
477
184
  if (typeof factory !== "function") {
478
- throw new PenvError4(
185
+ throw new PenvError2(
479
186
  "PROVIDER_PLUGIN_INVALID",
480
187
  `\`${type}\` does not export \`${PLUGIN_FACTORY_EXPORT}\``,
481
188
  `A penv provider package must export \`${PLUGIN_FACTORY_EXPORT}(context) => Provider\`.`
@@ -522,7 +229,7 @@ function resolveExtension(type, projectRoot, environment, local = localExtension
522
229
  function pinnedVersion(type, projectRoot) {
523
230
  let manifest;
524
231
  try {
525
- manifest = parseManifest(readFileSync2(join3(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
232
+ manifest = parseManifest(readFileSync(join(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
526
233
  } catch {
527
234
  return void 0;
528
235
  }
@@ -535,7 +242,7 @@ function storedExtension(type, version) {
535
242
  function localExtensions(projectRoot) {
536
243
  let text;
537
244
  try {
538
- text = readFileSync2(localExtensionsFile(projectRoot), "utf8");
245
+ text = readFileSync(localExtensionsFile(projectRoot), "utf8");
539
246
  } catch {
540
247
  return [];
541
248
  }
@@ -545,7 +252,7 @@ function isCi(value) {
545
252
  return value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
546
253
  }
547
254
  function localExtensionInCi(type, environment) {
548
- return new PenvError4(
255
+ return new PenvError2(
549
256
  "LOCAL_EXTENSION_IN_CI",
550
257
  `The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
551
258
  `${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 +281,7 @@ function assertSatisfiesContract(provider, specifier) {
574
281
  const contract = projection ? "the @penvhq/core ProjectionProvider contract its declared capabilities select" : "the @penvhq/core Provider contract that the filesystem provider defines";
575
282
  for (const method of methods) {
576
283
  if (typeof provider[method] !== "function") {
577
- throw new PenvError4(
284
+ throw new PenvError2(
578
285
  "PROVIDER_PLUGIN_INVALID",
579
286
  `The provider from \`${specifier}\` is missing \`${method}()\``,
580
287
  `It must satisfy ${contract}.`
@@ -587,21 +294,21 @@ function where(environment) {
587
294
  }
588
295
  function unknownProvider(type, environment) {
589
296
  const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
590
- return new PenvError4(
297
+ return new PenvError2(
591
298
  "UNKNOWN_PROVIDER",
592
299
  `The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
593
300
  `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
301
  );
595
302
  }
596
303
  function extensionNotInstalled(type, version, environment) {
597
- return new PenvError4(
304
+ return new PenvError2(
598
305
  "EXTENSION_NOT_INSTALLED",
599
306
  `${MANIFEST_PATH} pins \`${type}\` ${version}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
600
307
  "Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
601
308
  );
602
309
  }
603
310
  function localExtensionUnresolved(type, projectRoot, environment) {
604
- return new PenvError4(
311
+ return new PenvError2(
605
312
  "LOCAL_EXTENSION_UNRESOLVED",
606
313
  `${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
607
314
  `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 +316,7 @@ function localExtensionUnresolved(type, projectRoot, environment) {
609
316
  }
610
317
  function providerLoadFailed(type, path, cause) {
611
318
  const detail = cause instanceof Error ? cause.message : String(cause);
612
- return new PenvError4(
319
+ return new PenvError2(
613
320
  "PROVIDER_PLUGIN_LOAD",
614
321
  `The provider package \`${type}\` failed to load from ${path}: ${detail}`,
615
322
  "penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
@@ -632,7 +339,7 @@ function openProject(cwd) {
632
339
  }
633
340
  function localTree(project) {
634
341
  if (!(project.provider instanceof FilesystemProvider)) {
635
- throw new PenvError5(
342
+ throw new PenvError3(
636
343
  "PROVIDER_NOT_LOCAL",
637
344
  `This command reads the local .penv tree synchronously, which the \`${project.provider.type}\` provider is not`,
638
345
  "Run this against a filesystem-backed project, or use a command that speaks the async provider contract."
@@ -641,11 +348,11 @@ function localTree(project) {
641
348
  return project.provider;
642
349
  }
643
350
  function selfContainedSchemaModule(root, schemaFile) {
644
- const file = join4(root, ...schemaFile.split("/"));
645
- if (!existsSync3(file)) {
351
+ const file = join2(root, ...schemaFile.split("/"));
352
+ if (!existsSync(file)) {
646
353
  return void 0;
647
354
  }
648
- const source = readFileSync3(file, "utf8");
355
+ const source = readFileSync2(file, "utf8");
649
356
  return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
650
357
  }
651
358
  function schemaShapeFileOf(project) {
@@ -674,7 +381,7 @@ function refFromKey(key, config) {
674
381
  const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
675
382
  const name = segments[segments.length - 1];
676
383
  if (name === void 0) {
677
- throw new PenvError5(
384
+ throw new PenvError3(
678
385
  "PARAMETER_KEY",
679
386
  `\`${key}\` names no parameter`,
680
387
  "A key is `<namespace>/<name>` or `<namespace>.<name>`, e.g. `redis/password`."
@@ -693,13 +400,13 @@ function assertWritableKey(key) {
693
400
  const ref = refFromAccessPath(segments);
694
401
  if (ref !== void 0) {
695
402
  const suggestion = [...ref.namespace, ref.name].join("/");
696
- throw new PenvError5(
403
+ throw new PenvError3(
697
404
  "PARAMETER_KEY_CASING",
698
405
  `\`${key}\` is not a canonical parameter name`,
699
406
  `Parameter files are lower-case and hyphenated. Did you mean \`${suggestion}\`? That is the file that backs the \`${key}\` key in your schema.`
700
407
  );
701
408
  }
702
- throw new PenvError5(
409
+ throw new PenvError3(
703
410
  "PARAMETER_KEY_UNREACHABLE",
704
411
  `No value file can be named that reaches \`${key}\``,
705
412
  "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 +454,7 @@ function refsFrom(files) {
747
454
  }
748
455
 
749
456
  // src/ui.ts
750
- import { PenvError as PenvError6 } from "@penvhq/core";
457
+ import { isPenvErrorLike } from "@penvhq/core";
751
458
 
752
459
  // src/style.ts
753
460
  function supportsColor(stream) {
@@ -880,7 +587,7 @@ function writeError(lines) {
880
587
  }
881
588
  }
882
589
  function reportError(error) {
883
- if (error instanceof PenvError6) {
590
+ if (isPenvErrorLike(error)) {
884
591
  process.stderr.write(`${err.red(CROSS)} ${error.summary}
885
592
  `);
886
593
  if (error.remedy !== void 0) {
@@ -911,9 +618,9 @@ async function guard(run) {
911
618
  }
912
619
 
913
620
  // src/commands/run.ts
914
- import { existsSync as existsSync4, readFileSync as readFileSync4, watch } from "fs";
621
+ import { existsSync as existsSync2, readFileSync as readFileSync3, watch } from "fs";
915
622
  import { createRequire as createRequire2 } from "module";
916
- import { basename, dirname as dirname2, join as join5, resolve as resolve2 } from "path";
623
+ import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
917
624
  import {
918
625
  ARTIFACT_BUILD_COMMAND,
919
626
  assertArtifactFor,
@@ -923,7 +630,7 @@ import {
923
630
  MissingMaterializationError,
924
631
  openSealed,
925
632
  own,
926
- PenvError as PenvError8,
633
+ PenvError as PenvError5,
927
634
  parseArtifact,
928
635
  RECORDS_PATH,
929
636
  UndecryptableValueError,
@@ -1172,7 +879,7 @@ import {
1172
879
  checkNameCollisions,
1173
880
  jitiFor,
1174
881
  NameCollisionError,
1175
- PenvError as PenvError7,
882
+ PenvError as PenvError4,
1176
883
  ReservedTokenError as ReservedTokenError2,
1177
884
  resolveAll,
1178
885
  SCHEMA_HARVEST_ENV,
@@ -1521,7 +1228,7 @@ async function checkEnvironment(project, environment) {
1521
1228
  try {
1522
1229
  files = await project.provider.list();
1523
1230
  } catch (error) {
1524
- if (!(error instanceof PenvError7)) {
1231
+ if (!(error instanceof PenvError4)) {
1525
1232
  throw error;
1526
1233
  }
1527
1234
  return {
@@ -1673,7 +1380,7 @@ function assertNotNested(host, inner) {
1673
1380
  if (outer === void 0) {
1674
1381
  return;
1675
1382
  }
1676
- throw new PenvError8(
1383
+ throw new PenvError5(
1677
1384
  "RUN_NESTED",
1678
1385
  `\`${inner}\` is starting inside \`${outer}\`, and two penv environments cannot own one process`,
1679
1386
  `Drop one of the two wrappers \u2014 the inner one is in a package.json script \u2014 then run \`${outer}\` again.`
@@ -1683,7 +1390,7 @@ function assertSource(source, inner) {
1683
1390
  if (source === "project" || source === "snapshot") {
1684
1391
  return source;
1685
1392
  }
1686
- throw new PenvError8(
1393
+ throw new PenvError5(
1687
1394
  "RUN_SOURCE_UNKNOWN",
1688
1395
  `\`--source ${source}\` names no source penv reads`,
1689
1396
  `A run reads ${SOURCES.map((name) => `\`${name}\``).join(" or ")}, and \`project\` is the default \u2014 so \`${inner}\` reads the local tree.`
@@ -1704,7 +1411,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
1704
1411
  }
1705
1412
  const parameter = [...ref.namespace, ref.name].join("/");
1706
1413
  const prefix = prefixes.find((candidate) => variable.startsWith(candidate));
1707
- throw new PenvError8(
1414
+ throw new PenvError5(
1708
1415
  "RUN_PUBLIC_SECRET",
1709
1416
  `The secret ${parameter} maps to ${variable}, which the \`${prefix}\` prefix publishes to the browser`,
1710
1417
  `Rename the parameter, or drop \`secret\` from its meta if it is not one \u2014 then \`${retry}\`.`
@@ -1713,7 +1420,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
1713
1420
  }
1714
1421
  function invalidConfiguration(result2, inner) {
1715
1422
  const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
1716
- return new PenvError8(
1423
+ return new PenvError5(
1717
1424
  "RUN_INVALID_CONFIGURATION",
1718
1425
  `Configuration for environment ${result2.environment} is not valid, so nothing was started:
1719
1426
  ${lines}`,
@@ -1735,7 +1442,7 @@ function assertNoActiveDotenv(project) {
1735
1442
  if (first === void 0) {
1736
1443
  return;
1737
1444
  }
1738
- throw new PenvError8(
1445
+ throw new PenvError5(
1739
1446
  "RUN_DOTENV_ACTIVE",
1740
1447
  `${first.name} is active configuration again, and your framework would read it beside penv's records`,
1741
1448
  `Adopt it with \`penv init\`, or delete ${first.name} \u2014 its values belong in ${RECORDS_PATH}/.`
@@ -1753,10 +1460,10 @@ function packageManifest(specifier, root) {
1753
1460
  return void 0;
1754
1461
  }
1755
1462
  for (; ; ) {
1756
- const file = join5(directory, "package.json");
1757
- if (existsSync4(file)) {
1463
+ const file = join3(directory, "package.json");
1464
+ if (existsSync2(file)) {
1758
1465
  try {
1759
- const parsed = JSON.parse(readFileSync4(file, "utf8"));
1466
+ const parsed = JSON.parse(readFileSync3(file, "utf8"));
1760
1467
  if (isPlainObject(parsed) && parsed.name === specifier) {
1761
1468
  return parsed;
1762
1469
  }
@@ -1783,7 +1490,7 @@ function declaredCredentials(config, root) {
1783
1490
  continue;
1784
1491
  }
1785
1492
  if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
1786
- throw new PenvError8(
1493
+ throw new PenvError5(
1787
1494
  "PROVIDER_CREDENTIALS_INVALID",
1788
1495
  `\`${provider.type}\` declares \`penv.credentials\`, and it is not a list of variable names`,
1789
1496
  `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 +1526,7 @@ async function prepare(project, environment, host, inner) {
1819
1526
  function snapshotPath(host) {
1820
1527
  const path = host[SNAPSHOT_VARIABLE]?.trim();
1821
1528
  if (path === void 0 || path.length === 0) {
1822
- throw new PenvError8(
1529
+ throw new PenvError5(
1823
1530
  "RUN_SNAPSHOT_UNSET",
1824
1531
  `\`--source snapshot\` reads the sealed artifact ${SNAPSHOT_VARIABLE} names, and ${SNAPSHOT_VARIABLE} is not set`,
1825
1532
  `Build one with \`${ARTIFACT_BUILD_COMMAND}\` and point ${SNAPSHOT_VARIABLE} at it.`
@@ -1829,9 +1536,9 @@ function snapshotPath(host) {
1829
1536
  }
1830
1537
  function readSnapshot(path) {
1831
1538
  try {
1832
- return readFileSync4(path, "utf8");
1539
+ return readFileSync3(path, "utf8");
1833
1540
  } catch {
1834
- throw new PenvError8(
1541
+ throw new PenvError5(
1835
1542
  "RUN_SNAPSHOT_MISSING",
1836
1543
  `${SNAPSHOT_VARIABLE} names ${path}, and penv cannot read a sealed artifact there`,
1837
1544
  `Point ${SNAPSHOT_VARIABLE} at the artifact your release mounted \u2014 \`${ARTIFACT_BUILD_COMMAND}\` writes one.`
@@ -1908,7 +1615,7 @@ function watchProject(project, onChange) {
1908
1615
  timer = setTimeout(onChange, DEBOUNCE_MS);
1909
1616
  };
1910
1617
  const add = (target, recursive, only) => {
1911
- if (!existsSync4(target)) {
1618
+ if (!existsSync2(target)) {
1912
1619
  return;
1913
1620
  }
1914
1621
  try {
@@ -1947,7 +1654,7 @@ async function runRun(options) {
1947
1654
  }
1948
1655
  if (source === "snapshot") {
1949
1656
  if (options.watch === true) {
1950
- throw new PenvError8(
1657
+ throw new PenvError5(
1951
1658
  "RUN_SNAPSHOT_WATCH",
1952
1659
  "`--watch` re-syncs the project tree, and a run from a sealed artifact has no tree to watch",
1953
1660
  "Drop `--watch` \u2014 an artifact is built once and read unchanged. Watch the project instead: `penv run --watch -- <command>`."
@@ -2091,7 +1798,7 @@ function buildCommand(environment, out2) {
2091
1798
  function targetOf(options) {
2092
1799
  const environment = options.environment?.trim();
2093
1800
  if (environment === void 0 || environment.length === 0) {
2094
- throw new PenvError9(
1801
+ throw new PenvError6(
2095
1802
  "ARTIFACT_ENV_REQUIRED",
2096
1803
  "`penv artifact build` names the environment it builds for, and `--env` was not given",
2097
1804
  `Name it: \`${buildCommand(void 0, options.out)}\`. An artifact carries one environment, and penv will not pick which.`
@@ -2102,13 +1809,13 @@ function targetOf(options) {
2102
1809
  function outputOf(options, environment) {
2103
1810
  const out2 = options.out?.trim();
2104
1811
  if (out2 === void 0 || out2.length === 0) {
2105
- throw new PenvError9(
1812
+ throw new PenvError6(
2106
1813
  "ARTIFACT_OUT_REQUIRED",
2107
1814
  "`penv artifact build` writes where it is told, and `--out` was not given",
2108
1815
  `Name the path: \`${buildCommand(environment, void 0)}\`. The artifact belongs outside the repository, so there is no default worth having.`
2109
1816
  );
2110
1817
  }
2111
- return isAbsolute2(out2) ? out2 : resolve3(options.cwd, out2);
1818
+ return isAbsolute(out2) ? out2 : resolve3(options.cwd, out2);
2112
1819
  }
2113
1820
  async function winnerOf(project, ref, environment) {
2114
1821
  for (const file of candidatesFor2(ref, environment, true)) {
@@ -2120,7 +1827,7 @@ async function winnerOf(project, ref, environment) {
2120
1827
  return void 0;
2121
1828
  }
2122
1829
  function plaintextSecret(parameter, location, environment) {
2123
- return new PenvError9(
1830
+ return new PenvError6(
2124
1831
  "ARTIFACT_PLAINTEXT_SECRET",
2125
1832
  `${parameter} is a secret for environment ${environment}, and its value comes from ${location}, which is not sealed`,
2126
1833
  `Seal it with \`penv encrypt ${parameter} --env ${environment}\` \u2014 an artifact carries ciphertext or nothing.`
@@ -2134,7 +1841,7 @@ async function runArtifactBuild(options) {
2134
1841
  const { schema, issues } = await loadSchema(project, environment);
2135
1842
  if (schema === void 0) {
2136
1843
  const lines = issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
2137
- throw new PenvError9(
1844
+ throw new PenvError6(
2138
1845
  "ARTIFACT_NO_SCHEMA",
2139
1846
  `The schema did not load, so penv cannot tell what this artifact should deliver:
2140
1847
  ${lines}`,
@@ -2186,7 +1893,7 @@ ${lines}`,
2186
1893
  values: present,
2187
1894
  sealed,
2188
1895
  absent: refs.length - present,
2189
- insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute2(inside)
1896
+ insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute(inside)
2190
1897
  };
2191
1898
  }
2192
1899
  function displayPath(cwd, file) {
@@ -2247,25 +1954,25 @@ import { defineCommand as defineCommand5 } from "citty";
2247
1954
 
2248
1955
  // src/cutover.ts
2249
1956
  import {
2250
- existsSync as existsSync5,
1957
+ existsSync as existsSync3,
2251
1958
  mkdirSync as mkdirSync2,
2252
1959
  readdirSync as readdirSync2,
2253
- readFileSync as readFileSync5,
1960
+ readFileSync as readFileSync4,
2254
1961
  renameSync,
2255
1962
  rmSync,
2256
1963
  writeFileSync as writeFileSync2
2257
1964
  } from "fs";
2258
- import { dirname as dirname4, join as join6, resolve as resolve4 } from "path";
1965
+ import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
2259
1966
  import {
2260
1967
  CUTOVER_PATH,
2261
1968
  findConfigFile,
2262
- PenvError as PenvError10,
1969
+ PenvError as PenvError7,
2263
1970
  ROLLBACK_DOTENV_PATH,
2264
1971
  ROLLBACK_PATH
2265
1972
  } from "@penvhq/core";
2266
1973
  var CUTOVER_FORMAT = 1;
2267
1974
  function fileFor(root, relativePosix) {
2268
- return join6(root, ...relativePosix.split("/"));
1975
+ return join4(root, ...relativePosix.split("/"));
2269
1976
  }
2270
1977
  function bundleDir(root) {
2271
1978
  return fileFor(root, ROLLBACK_DOTENV_PATH);
@@ -2289,12 +1996,12 @@ function bundleUnresolved(root) {
2289
1996
  }
2290
1997
  function readCutover(root) {
2291
1998
  const file = cutoverFile(root);
2292
- if (!existsSync5(file)) {
1999
+ if (!existsSync3(file)) {
2293
2000
  return void 0;
2294
2001
  }
2295
2002
  let parsed;
2296
2003
  try {
2297
- parsed = JSON.parse(readFileSync5(file, "utf8"));
2004
+ parsed = JSON.parse(readFileSync4(file, "utf8"));
2298
2005
  } catch {
2299
2006
  throw unreadable("it is not JSON");
2300
2007
  }
@@ -2320,7 +2027,7 @@ function readCutover(root) {
2320
2027
  };
2321
2028
  }
2322
2029
  function unreadable(what) {
2323
- return new PenvError10(
2030
+ return new PenvError7(
2324
2031
  "CUTOVER_UNREADABLE",
2325
2032
  `${CUTOVER_PATH} records the last dotenv cutover, and ${what}`,
2326
2033
  `Run \`penv cleanup\` to drop that record and the rollback bundle it names.`
@@ -2352,7 +2059,7 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
2352
2059
  const bundle = bundleDir(root);
2353
2060
  mkdirSync2(bundle, { recursive: true });
2354
2061
  for (const name of files) {
2355
- renameSync(join6(root, name), join6(bundle, name));
2062
+ renameSync(join4(root, name), join4(bundle, name));
2356
2063
  }
2357
2064
  return cutover;
2358
2065
  }
@@ -2361,7 +2068,7 @@ function runUndo(options) {
2361
2068
  const cutover = readCutover(root);
2362
2069
  const bundled = bundledFiles(root);
2363
2070
  if (cutover === void 0 && bundled.length === 0) {
2364
- throw new PenvError10(
2071
+ throw new PenvError7(
2365
2072
  "INIT_UNDO_NOTHING",
2366
2073
  "There is no dotenv cutover to undo in this project",
2367
2074
  "Run `penv init` to adopt your dotenv files; undo puts them back afterwards."
@@ -2371,11 +2078,11 @@ function runUndo(options) {
2371
2078
  const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
2372
2079
  const bundle = bundleDir(root);
2373
2080
  const held = new Set(bundled);
2374
- const occupied2 = names.filter((name) => held.has(name) && existsSync5(join6(root, name)));
2081
+ const occupied2 = names.filter((name) => held.has(name) && existsSync3(join4(root, name)));
2375
2082
  if (occupied2.length > 0) {
2376
2083
  const listed = occupied2.join(", ");
2377
2084
  const many = occupied2.length > 1;
2378
- throw new PenvError10(
2085
+ throw new PenvError7(
2379
2086
  "INIT_UNDO_OCCUPIED",
2380
2087
  `${listed} ${many ? "exist" : "exists"} again, and restoring what penv moved aside would write over ${many ? "them" : "it"}`,
2381
2088
  `Move ${listed} out of the way, or run \`penv cleanup\` to keep ${many ? "them" : "it"} and drop the bundle. Nothing was restored.`
@@ -2386,9 +2093,9 @@ function runUndo(options) {
2386
2093
  const missing = [];
2387
2094
  for (const name of names) {
2388
2095
  if (held.has(name)) {
2389
- renameSync(join6(bundle, name), join6(root, name));
2096
+ renameSync(join4(bundle, name), join4(root, name));
2390
2097
  restored.push(name);
2391
- } else if (existsSync5(join6(root, name))) {
2098
+ } else if (existsSync3(join4(root, name))) {
2392
2099
  alreadyBack.push(name);
2393
2100
  } else {
2394
2101
  missing.push(name);
@@ -2400,7 +2107,7 @@ function runUndo(options) {
2400
2107
  function runCleanup(options) {
2401
2108
  const root = cutoverRoot(options.cwd);
2402
2109
  const held = bundledFiles(root);
2403
- const cleaned = held.length > 0 || existsSync5(cutoverFile(root)) || existsSync5(fileFor(root, ROLLBACK_PATH));
2110
+ const cleaned = held.length > 0 || existsSync3(cutoverFile(root)) || existsSync3(fileFor(root, ROLLBACK_PATH));
2404
2111
  removeBundle(root);
2405
2112
  return { root, removed: held, cleaned };
2406
2113
  }
@@ -2441,8 +2148,8 @@ var cleanupCommand = defineCommand5({
2441
2148
  });
2442
2149
 
2443
2150
  // 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";
2151
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "fs";
2152
+ import { join as join5, relative as relative2 } from "path";
2446
2153
  import {
2447
2154
  ARTIFACT_BUILD_COMMAND as ARTIFACT_BUILD_COMMAND2,
2448
2155
  accessPath as accessPath3,
@@ -2468,7 +2175,7 @@ import { defineCommand as defineCommand7 } from "citty";
2468
2175
  import {
2469
2176
  checkNameCollisions as checkNameCollisions2,
2470
2177
  holdsProjection as holdsProjection3,
2471
- PenvError as PenvError11,
2178
+ PenvError as PenvError8,
2472
2179
  recordPath,
2473
2180
  requireValue,
2474
2181
  variableName as variableName4
@@ -2536,7 +2243,7 @@ async function destinationFor(project, environment, options) {
2536
2243
  const declared = project.config.providers[environment];
2537
2244
  const location = declared?.location;
2538
2245
  if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
2539
- throw new PenvError11(
2246
+ throw new PenvError8(
2540
2247
  "NO_DESTINATION",
2541
2248
  `Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
2542
2249
  `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 +2270,7 @@ function plan(resolutions, config, environment, allowDecrypt) {
2563
2270
  let encrypted = false;
2564
2271
  if (winner.file.encrypted) {
2565
2272
  if (!allowDecrypt) {
2566
- throw new PenvError11(
2273
+ throw new PenvError8(
2567
2274
  "ENCRYPTED_VALUE_REFUSED",
2568
2275
  `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
2276
  "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 +2323,7 @@ async function ensureTargetApproved(provider, environment, options) {
2616
2323
  return false;
2617
2324
  }
2618
2325
  if (provider.ensureTarget === void 0) {
2619
- throw new PenvError11(
2326
+ throw new PenvError8(
2620
2327
  "MISSING_TARGET",
2621
2328
  `The destination has no environment \`${environment}\` to receive this push, and this provider cannot create one`,
2622
2329
  `Create the environment on the destination side, then run \`penv push --env ${environment}\` again.`
@@ -2626,7 +2333,7 @@ async function ensureTargetApproved(provider, environment, options) {
2626
2333
  `The destination has no environment \`${environment}\`. Create it?`
2627
2334
  );
2628
2335
  if (!approved) {
2629
- throw new PenvError11(
2336
+ throw new PenvError8(
2630
2337
  "MISSING_TARGET",
2631
2338
  `The destination has no environment \`${environment}\` to receive this push`,
2632
2339
  `Re-run with \`--yes\` to create it, answer \`y\` at the prompt, or create it on the destination side yourself.`
@@ -3396,7 +3103,7 @@ function scannableFiles(directory, out2) {
3396
3103
  return;
3397
3104
  }
3398
3105
  for (const entry of entries) {
3399
- const path = join7(directory, entry.name);
3106
+ const path = join5(directory, entry.name);
3400
3107
  if (entry.isDirectory()) {
3401
3108
  if (!UNSCANNED_DIRS.has(entry.name)) {
3402
3109
  scannableFiles(path, out2);
@@ -3438,10 +3145,10 @@ function artifactFindings(project) {
3438
3145
  for (const file of files) {
3439
3146
  let text;
3440
3147
  try {
3441
- if (statSync2(file).size > ARTIFACT_SCAN_LIMIT) {
3148
+ if (statSync(file).size > ARTIFACT_SCAN_LIMIT) {
3442
3149
  continue;
3443
3150
  }
3444
- text = readFileSync6(file, "utf8");
3151
+ text = readFileSync5(file, "utf8");
3445
3152
  } catch {
3446
3153
  continue;
3447
3154
  }
@@ -3790,7 +3497,7 @@ import {
3790
3497
  formatValueFile as formatValueFile5,
3791
3498
  isSecret as isSecret5,
3792
3499
  openValue as openValue3,
3793
- PenvError as PenvError13,
3500
+ PenvError as PenvError10,
3794
3501
  parameterId as parameterId5,
3795
3502
  recordPath as recordPath3,
3796
3503
  sealValue as sealValue2
@@ -3801,7 +3508,7 @@ import { defineCommand as defineCommand9 } from "citty";
3801
3508
  import {
3802
3509
  formatValueFile as formatValueFile4,
3803
3510
  isSecret as isSecret4,
3804
- PenvError as PenvError12,
3511
+ PenvError as PenvError9,
3805
3512
  parameterId as parameterId4,
3806
3513
  recordPath as recordPath2,
3807
3514
  sealValue
@@ -3825,7 +3532,7 @@ function targetScope(project, options, key) {
3825
3532
  return scopeFrom(options);
3826
3533
  }
3827
3534
  if (environment.trim().length === 0) {
3828
- throw new PenvError12(
3535
+ throw new PenvError9(
3829
3536
  "ENVIRONMENT_FLAG_EMPTY",
3830
3537
  `\`--env\` for parameter ${key} names no environment`,
3831
3538
  `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 +3545,7 @@ function policyEnvironment(project, options) {
3838
3545
  }
3839
3546
  function sealFor(project, file, value, parameter, environment) {
3840
3547
  if (environment === void 0) {
3841
- throw new PenvError12(
3548
+ throw new PenvError9(
3842
3549
  "SECRET_SCOPE_AMBIGUOUS",
3843
3550
  `Parameter ${parameter} is a secret, and ${recordPath2(formatValueFile4(file))} names no environment`,
3844
3551
  "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 +3647,7 @@ function twins(project, key, options) {
3940
3647
  }
3941
3648
  function environmentFor(project, options, verb) {
3942
3649
  if (options.environment === void 0) {
3943
- throw new PenvError13(
3650
+ throw new PenvError10(
3944
3651
  "SECRET_SCOPE_AMBIGUOUS",
3945
3652
  `\`penv ${verb}\` names no environment, and keys are declared per environment`,
3946
3653
  "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 +3666,7 @@ async function runEncrypt(options) {
3959
3666
  const value = await readOne(project, plain);
3960
3667
  if (value === void 0) {
3961
3668
  const already = await readOne(project, sealed);
3962
- throw new PenvError13(
3669
+ throw new PenvError10(
3963
3670
  "PARAMETER_ABSENT",
3964
3671
  already === void 0 ? `Parameter ${parameter} has no value file at ${recordPath3(formatValueFile5(plain))}` : `Parameter ${parameter} is already encrypted at ${recordPath3(formatValueFile5(sealed))}`,
3965
3672
  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 +3687,7 @@ async function runDecrypt(options) {
3980
3687
  const [plain, sealed] = twins(project, options.key, options);
3981
3688
  const parameter = parameterId5(plain);
3982
3689
  if (isSecret5(await project.provider.readMeta(plain), environment)) {
3983
- throw new PenvError13(
3690
+ throw new PenvError10(
3984
3691
  "SECRET_DECRYPT_REFUSED",
3985
3692
  `Parameter ${parameter} is declared a secret for environment ${environment}, so penv will not write it in plaintext`,
3986
3693
  "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 +3695,7 @@ async function runDecrypt(options) {
3988
3695
  }
3989
3696
  const stored = await readOne(project, sealed);
3990
3697
  if (stored === void 0) {
3991
- throw new PenvError13(
3698
+ throw new PenvError10(
3992
3699
  "PARAMETER_ABSENT",
3993
3700
  `Parameter ${parameter} has no encrypted value file at ${recordPath3(formatValueFile5(sealed))}`,
3994
3701
  `Nothing to decrypt. \`penv get ${options.key} --env ${environment} --explain\` shows every file penv looked at.`
@@ -4011,7 +3718,7 @@ async function runDecrypt(options) {
4011
3718
  removed: formatValueFile5(sealed)
4012
3719
  };
4013
3720
  }
4014
- var UndecryptableAt = class extends PenvError13 {
3721
+ var UndecryptableAt = class extends PenvError10 {
4015
3722
  constructor(parameter, environment, location, detail) {
4016
3723
  super(
4017
3724
  "VALUE_UNDECRYPTABLE",
@@ -4066,7 +3773,7 @@ var decryptCommand = defineCommand9({
4066
3773
  });
4067
3774
 
4068
3775
  // src/commands/fill.ts
4069
- import { PenvError as PenvError14, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
3776
+ import { PenvError as PenvError11, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
4070
3777
  import { defineCommand as defineCommand10 } from "citty";
4071
3778
  var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
4072
3779
  async function runFill(options) {
@@ -4081,7 +3788,7 @@ async function runFill(options) {
4081
3788
  const detail = blockers.map(
4082
3789
  (issue) => ` - ${issue.message}${issue.remedy === void 0 ? "" : ` (${issue.remedy})`}`
4083
3790
  ).join("\n");
4084
- throw new PenvError14(
3791
+ throw new PenvError11(
4085
3792
  "FILL_BLOCKED",
4086
3793
  `penv fill cannot run: environment ${environment} has ${blockers.length} unresolved configuration ${blockers.length === 1 ? "issue" : "issues"}:
4087
3794
  ${detail}`,
@@ -4217,11 +3924,11 @@ var fillCommand = defineCommand10({
4217
3924
 
4218
3925
  // src/commands/generate.ts
4219
3926
  import { writeFileSync as writeFileSync3 } from "fs";
4220
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5 } from "path";
3927
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve5 } from "path";
4221
3928
  import {
4222
3929
  checkNameCollisions as checkNameCollisions3,
4223
3930
  effectiveMeta as effectiveMeta2,
4224
- PenvError as PenvError15,
3931
+ PenvError as PenvError12,
4225
3932
  recordPath as recordPath5,
4226
3933
  requireValue as requireValue2,
4227
3934
  serializeDotenv,
@@ -4246,7 +3953,7 @@ function entriesFor(project, environment, allowDecrypt) {
4246
3953
  const winner = resolution.winner;
4247
3954
  if (winner?.file.encrypted === true) {
4248
3955
  if (!allowDecrypt) {
4249
- throw new PenvError15(
3956
+ throw new PenvError12(
4250
3957
  "ENCRYPTED_VALUE_REFUSED",
4251
3958
  `Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath5(winner.location)}, and \`penv generate\` writes plaintext`,
4252
3959
  `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 +3984,7 @@ function runGenerate(options) {
4277
3984
  const project = openProject(options.cwd);
4278
3985
  const environment = targetEnvironment(project, options.environment, options.envFlags);
4279
3986
  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);
3987
+ const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) : isAbsolute2(options.out) ? options.out : resolve5(options.cwd, options.out);
4281
3988
  writeFileSync3(file, serializeDotenv(entries), "utf8");
4282
3989
  return { file, environment, entries: entries.length, decrypted };
4283
3990
  }
@@ -4330,7 +4037,7 @@ var generateCommand = defineCommand11({
4330
4037
  });
4331
4038
 
4332
4039
  // src/commands/get.ts
4333
- import { PenvError as PenvError16, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
4040
+ import { PenvError as PenvError13, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
4334
4041
  import { defineCommand as defineCommand12 } from "citty";
4335
4042
  async function runGet(options) {
4336
4043
  const project = openProject(options.cwd);
@@ -4340,7 +4047,7 @@ async function runGet(options) {
4340
4047
  const resolution = await resolveParameter(ref, environment, project.provider, keys);
4341
4048
  const value = requireValue3(resolution, environment);
4342
4049
  if (value === void 0) {
4343
- throw new PenvError16(
4050
+ throw new PenvError13(
4344
4051
  "PARAMETER_ABSENT",
4345
4052
  `Parameter ${resolution.parameter} resolves to no value for environment ${environment}`,
4346
4053
  `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 +4123,15 @@ var getCommand = defineCommand12({
4416
4123
  });
4417
4124
 
4418
4125
  // 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";
4126
+ import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
4127
+ import { basename as basename2, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve7 } from "path";
4421
4128
  import {
4422
4129
  assertNever as assertNever2,
4423
4130
  FilenameGrammarError,
4424
4131
  findConfigFile as findConfigFile2,
4425
4132
  loadConfigFrom as loadConfigFrom2,
4426
4133
  lookupEnvironment,
4427
- PenvError as PenvError19,
4134
+ PenvError as PenvError16,
4428
4135
  parseDotenv as parseDotenv2,
4429
4136
  schemaFileOf as schemaFileOf4,
4430
4137
  UnknownEnvironmentError
@@ -4435,7 +4142,7 @@ import { defineCommand as defineCommand14 } from "citty";
4435
4142
  import {
4436
4143
  checkNameCollisions as checkNameCollisions4,
4437
4144
  isReservedToken as isReservedToken3,
4438
- PenvError as PenvError17,
4145
+ PenvError as PenvError14,
4439
4146
  ReservedTokenError as ReservedTokenError3,
4440
4147
  refFromVariable as refFromVariable2,
4441
4148
  roundTripsCleanly,
@@ -4445,7 +4152,7 @@ function assertImportable(ref, variable) {
4445
4152
  if (!ref.name.includes(".")) {
4446
4153
  return;
4447
4154
  }
4448
- throw new PenvError17(
4155
+ throw new PenvError14(
4449
4156
  "IMPORT_UNPARSEABLE_NAME",
4450
4157
  `The variable ${variable} becomes the parameter \`${ref.name}\`, whose \`.\` would be read as a scope`,
4451
4158
  `Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
@@ -4464,7 +4171,7 @@ function assertRoundTrips(ref, variable, config) {
4464
4171
  return;
4465
4172
  }
4466
4173
  const generated = variableName7(ref, config);
4467
- throw new PenvError17(
4174
+ throw new PenvError14(
4468
4175
  "IMPORT_LOSSY_NAME",
4469
4176
  `The variable ${variable} becomes the parameter \`${ref.name}\`, which regenerates as ${generated}`,
4470
4177
  `\`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 +4212,8 @@ function writeEntries(tree, entries, refs, scope) {
4505
4212
  }
4506
4213
 
4507
4214
  // src/detect.ts
4508
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
4509
- import { join as join8 } from "path";
4215
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
4216
+ import { join as join6 } from "path";
4510
4217
  import { DEFAULT_SCHEMA_FILE } from "@penvhq/core";
4511
4218
  var SIGNATURES = [
4512
4219
  { name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
@@ -4523,7 +4230,7 @@ var SIGNATURES = [
4523
4230
  function exportsSchema(file) {
4524
4231
  let source;
4525
4232
  try {
4526
- source = readFileSync7(file, "utf8");
4233
+ source = readFileSync6(file, "utf8");
4527
4234
  } catch {
4528
4235
  return false;
4529
4236
  }
@@ -4534,8 +4241,8 @@ function exportsSchema(file) {
4534
4241
  );
4535
4242
  }
4536
4243
  function occupied(cwd, relative5) {
4537
- const file = join8(cwd, ...relative5.split("/"));
4538
- return existsSync6(file) && !exportsSchema(file);
4244
+ const file = join6(cwd, ...relative5.split("/"));
4245
+ return existsSync4(file) && !exportsSchema(file);
4539
4246
  }
4540
4247
  function schemaFileFor(cwd) {
4541
4248
  const dir = srcPrefix(cwd);
@@ -4550,10 +4257,10 @@ function schemaFileFor(cwd) {
4550
4257
  return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
4551
4258
  }
4552
4259
  function srcPrefix(cwd) {
4553
- return existsSync6(join8(cwd, "src")) ? "src/" : "";
4260
+ return existsSync4(join6(cwd, "src")) ? "src/" : "";
4554
4261
  }
4555
4262
  function dependenciesOf(cwd) {
4556
- const manifest = manifestOf2(cwd);
4263
+ const manifest = manifestOf(cwd);
4557
4264
  if (manifest === void 0) {
4558
4265
  return void 0;
4559
4266
  }
@@ -4568,14 +4275,14 @@ function dependenciesOf(cwd) {
4568
4275
  }
4569
4276
  return names;
4570
4277
  }
4571
- function manifestOf2(cwd) {
4572
- const file = join8(cwd, "package.json");
4573
- if (!existsSync6(file)) {
4278
+ function manifestOf(cwd) {
4279
+ const file = join6(cwd, "package.json");
4280
+ if (!existsSync4(file)) {
4574
4281
  return void 0;
4575
4282
  }
4576
4283
  let manifest;
4577
4284
  try {
4578
- manifest = JSON.parse(readFileSync7(file, "utf8"));
4285
+ manifest = JSON.parse(readFileSync6(file, "utf8"));
4579
4286
  } catch {
4580
4287
  return void 0;
4581
4288
  }
@@ -4591,7 +4298,7 @@ function detectFramework(cwd) {
4591
4298
  return detectedFrom(cwd, signature.name, signature.publicPrefixes);
4592
4299
  }
4593
4300
  }
4594
- if (existsSync6(join8(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
4301
+ if (existsSync4(join6(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
4595
4302
  return detectedFrom(cwd, "Bun", []);
4596
4303
  }
4597
4304
  return void 0;
@@ -4611,7 +4318,7 @@ function detectAlias(cwd) {
4611
4318
  return hasImportsBlock(cwd) ? IMPORTS_ALIAS : DEFAULT_ALIAS;
4612
4319
  }
4613
4320
  function hasImportsBlock(cwd) {
4614
- const manifest = manifestOf2(cwd);
4321
+ const manifest = manifestOf(cwd);
4615
4322
  const imports = manifest?.imports;
4616
4323
  return imports !== null && typeof imports === "object" && !Array.isArray(imports);
4617
4324
  }
@@ -4670,8 +4377,8 @@ function draftFieldsAcross(sources, environments) {
4670
4377
  }
4671
4378
 
4672
4379
  // 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";
4380
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
4381
+ import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
4675
4382
  import { createInterface as createInterface2 } from "readline/promises";
4676
4383
  import {
4677
4384
  CUTOVER_PATH as CUTOVER_PATH3,
@@ -4679,7 +4386,7 @@ import {
4679
4386
  isLegalEnvironmentName as isLegalEnvironmentName2,
4680
4387
  loadConfigFrom,
4681
4388
  PENV_DIR as PENV_DIR2,
4682
- PenvError as PenvError18,
4389
+ PenvError as PenvError15,
4683
4390
  parameterId as parameterId6,
4684
4391
  parseDotenv,
4685
4392
  RECORDS_PATH as RECORDS_PATH3,
@@ -4696,32 +4403,32 @@ import { defineCommand as defineCommand13 } from "citty";
4696
4403
 
4697
4404
  // src/scaffold-undo.ts
4698
4405
  import {
4699
- existsSync as existsSync7,
4406
+ existsSync as existsSync5,
4700
4407
  mkdirSync as mkdirSync3,
4701
4408
  readdirSync as readdirSync4,
4702
- readFileSync as readFileSync8,
4409
+ readFileSync as readFileSync7,
4703
4410
  rmdirSync,
4704
- statSync as statSync3,
4411
+ statSync as statSync2,
4705
4412
  unlinkSync,
4706
4413
  writeFileSync as writeFileSync4
4707
4414
  } from "fs";
4708
- import { dirname as dirname5, join as join9 } from "path";
4415
+ import { dirname as dirname5, join as join7 } from "path";
4709
4416
  function record(path, files, dirs) {
4710
4417
  let stats;
4711
4418
  try {
4712
- stats = statSync3(path);
4419
+ stats = statSync2(path);
4713
4420
  } catch {
4714
4421
  return;
4715
4422
  }
4716
4423
  if (stats.isDirectory()) {
4717
4424
  dirs.add(path);
4718
4425
  for (const entry of readdirSync4(path)) {
4719
- record(join9(path, entry), files, dirs);
4426
+ record(join7(path, entry), files, dirs);
4720
4427
  }
4721
4428
  return;
4722
4429
  }
4723
4430
  if (stats.isFile()) {
4724
- files.set(path, readFileSync8(path));
4431
+ files.set(path, readFileSync7(path));
4725
4432
  }
4726
4433
  }
4727
4434
  function captureScaffold(root, paths) {
@@ -4729,7 +4436,7 @@ function captureScaffold(root, paths) {
4729
4436
  const dirs = /* @__PURE__ */ new Set();
4730
4437
  for (const path of paths) {
4731
4438
  for (let dir = dirname5(path); dir.startsWith(root) && dir !== root; dir = dirname5(dir)) {
4732
- if (existsSync7(dir)) {
4439
+ if (existsSync5(dir)) {
4733
4440
  dirs.add(dir);
4734
4441
  }
4735
4442
  }
@@ -4752,7 +4459,7 @@ function restoreScaffold(undo) {
4752
4459
  function removeAdded(path, undo) {
4753
4460
  let stats;
4754
4461
  try {
4755
- stats = statSync3(path);
4462
+ stats = statSync2(path);
4756
4463
  } catch {
4757
4464
  return;
4758
4465
  }
@@ -4766,7 +4473,7 @@ function removeAdded(path, undo) {
4766
4473
  return;
4767
4474
  }
4768
4475
  for (const entry of readdirSync4(path)) {
4769
- removeAdded(join9(path, entry), undo);
4476
+ removeAdded(join7(path, entry), undo);
4770
4477
  }
4771
4478
  if (!undo.dirs.has(path) && readdirSync4(path).length === 0) {
4772
4479
  rmdirSync(path);
@@ -4960,7 +4667,7 @@ function suggestEnvironments(root) {
4960
4667
  return [...found].sort();
4961
4668
  }
4962
4669
  function emptyFlag(flag) {
4963
- return new PenvError18(
4670
+ return new PenvError15(
4964
4671
  "INIT_FLAG_EMPTY",
4965
4672
  `\`--${flag}\` was given without a value`,
4966
4673
  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 +4691,8 @@ function configOf(decisions) {
4984
4691
  return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
4985
4692
  }
4986
4693
  function declaredIn(root) {
4987
- const file = join10(root, CONFIG_FILE);
4988
- if (!existsSync8(file)) {
4694
+ const file = join8(root, CONFIG_FILE);
4695
+ if (!existsSync6(file)) {
4989
4696
  return void 0;
4990
4697
  }
4991
4698
  return loadConfigFrom(file);
@@ -5023,7 +4730,7 @@ function planInit(root, flags = {}) {
5023
4730
  throw emptyFlag("alias");
5024
4731
  }
5025
4732
  if (!ALIAS_NAME.test(alias)) {
5026
- throw new PenvError18(
4733
+ throw new PenvError15(
5027
4734
  "INIT_ALIAS_INVALID",
5028
4735
  `\`${alias}\` is not an alias penv can write`,
5029
4736
  `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 +5064,7 @@ ${objectIndent}${source.slice(close)}`;
5357
5064
  ${entryIndent}${member},${source.slice(open + 1)}`;
5358
5065
  }
5359
5066
  function shapeError(what, target, alias) {
5360
- return new PenvError18(
5067
+ return new PenvError15(
5361
5068
  "TSCONFIG_SHAPE",
5362
5069
  `penv cannot add the \`${alias}\` path alias to tsconfig.json: ${what}`,
5363
5070
  `Add it by hand: \`{ "compilerOptions": { "paths": { "${alias}": ["${target}"] } } }\`.`
@@ -5428,15 +5135,15 @@ function renderTsconfig(target, alias) {
5428
5135
  }
5429
5136
  function ensurePenvDir(root) {
5430
5137
  const dir = resolve6(root, PENV_DIR2);
5431
- if (existsSync8(dir)) {
5138
+ if (existsSync6(dir)) {
5432
5139
  return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR2}/` };
5433
5140
  }
5434
5141
  mkdirSync4(dir, { recursive: true });
5435
5142
  return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR2}/` };
5436
5143
  }
5437
5144
  function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
5438
- const file = join10(root, SCHEMA_SHAPE_FILE3);
5439
- if (existsSync8(file)) {
5145
+ const file = join8(root, SCHEMA_SHAPE_FILE3);
5146
+ if (existsSync6(file)) {
5440
5147
  return {
5441
5148
  target: "schema",
5442
5149
  action: "kept",
@@ -5462,8 +5169,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
5462
5169
  };
5463
5170
  }
5464
5171
  function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5465
- const file = join10(root, ...decisions.schemaFile.split("/"));
5466
- if (existsSync8(file)) {
5172
+ const file = join8(root, ...decisions.schemaFile.split("/"));
5173
+ if (existsSync6(file)) {
5467
5174
  return {
5468
5175
  target: "env",
5469
5176
  action: "kept",
@@ -5481,8 +5188,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5481
5188
  };
5482
5189
  }
5483
5190
  function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
5484
- const file = join10(root, CONFIG_FILE);
5485
- if (existsSync8(file)) {
5191
+ const file = join8(root, CONFIG_FILE);
5192
+ if (existsSync6(file)) {
5486
5193
  return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
5487
5194
  }
5488
5195
  writeFileSync5(file, renderConfigModule(decisions), "utf8");
@@ -5491,9 +5198,9 @@ function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
5491
5198
  function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5492
5199
  const alias = decisions.alias;
5493
5200
  const imports = alias.startsWith(IMPORTS_PREFIX);
5494
- const file = join10(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5201
+ const file = join8(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5495
5202
  const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
5496
- if (!existsSync8(file)) {
5203
+ if (!existsSync6(file)) {
5497
5204
  if (imports) {
5498
5205
  return {
5499
5206
  target: "tsconfig",
@@ -5509,7 +5216,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5509
5216
  text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
5510
5217
  };
5511
5218
  }
5512
- const source = readFileSync9(file, "utf8");
5219
+ const source = readFileSync8(file, "utf8");
5513
5220
  const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
5514
5221
  if (edit.conflict !== void 0) {
5515
5222
  return {
@@ -5534,9 +5241,9 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5534
5241
  };
5535
5242
  }
5536
5243
  function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
5537
- const file = join10(root, ...STATE_GITIGNORE_PATH.split("/"));
5244
+ const file = join8(root, ...STATE_GITIGNORE_PATH.split("/"));
5538
5245
  const wanted = renderStateGitignore(configOf(decisions));
5539
- const existing = existsSync8(file) ? readFileSync9(file, "utf8") : void 0;
5246
+ const existing = existsSync6(file) ? readFileSync8(file, "utf8") : void 0;
5540
5247
  if (existing === wanted) {
5541
5248
  return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
5542
5249
  }
@@ -5557,12 +5264,12 @@ function outdatedRuntimeWarning(root) {
5557
5264
  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
5265
  }
5559
5266
  function installedPenvVersion(root) {
5560
- const file = join10(root, "node_modules", "@penvhq", "penv", "package.json");
5561
- if (!existsSync8(file)) {
5267
+ const file = join8(root, "node_modules", "@penvhq", "penv", "package.json");
5268
+ if (!existsSync6(file)) {
5562
5269
  return void 0;
5563
5270
  }
5564
5271
  try {
5565
- const version = JSON.parse(readFileSync9(file, "utf8")).version;
5272
+ const version = JSON.parse(readFileSync8(file, "utf8")).version;
5566
5273
  return typeof version === "string" ? version : void 0;
5567
5274
  } catch {
5568
5275
  return void 0;
@@ -5602,11 +5309,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
5602
5309
  };
5603
5310
  }
5604
5311
  const alsoNote = writeAlso(root, seam.also);
5605
- const file = join10(root, ...seam.file.split("/"));
5312
+ const file = join8(root, ...seam.file.split("/"));
5606
5313
  const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
5607
5314
  const notes = baseNotes.length === 0 ? "" : `
5608
5315
  ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
5609
- if (existsSync8(file)) {
5316
+ if (existsSync6(file)) {
5610
5317
  return {
5611
5318
  target: "seam",
5612
5319
  action: "info",
@@ -5627,8 +5334,8 @@ function writeAlso(root, also) {
5627
5334
  if (also === void 0) {
5628
5335
  return void 0;
5629
5336
  }
5630
- const file = join10(root, ...also.file.split("/"));
5631
- if (existsSync8(file)) {
5337
+ const file = join8(root, ...also.file.split("/"));
5338
+ if (existsSync6(file)) {
5632
5339
  return also.ifPresent;
5633
5340
  }
5634
5341
  mkdirSync4(dirname6(file), { recursive: true });
@@ -5658,7 +5365,7 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
5658
5365
  return seam === void 0 ? steps : [...steps, seam];
5659
5366
  }
5660
5367
  function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
5661
- const fileAt = (relative5) => join10(root, ...relative5.split("/"));
5368
+ const fileAt = (relative5) => join8(root, ...relative5.split("/"));
5662
5369
  const seam = decisions.inject ? seamFor(framework, {
5663
5370
  alias: decisions.alias,
5664
5371
  srcDir: srcPrefix(root),
@@ -5699,7 +5406,7 @@ function planCutover(input) {
5699
5406
  assertBundleResolved(root);
5700
5407
  const selected = [...input.selected];
5701
5408
  if (selected.length === 0) {
5702
- throw new PenvError18(
5409
+ throw new PenvError15(
5703
5410
  "INIT_NOTHING_SELECTED",
5704
5411
  "No dotenv file was selected, so there is nothing to migrate",
5705
5412
  "Run `penv init` again and choose the files penv should adopt."
@@ -5711,7 +5418,7 @@ function planCutover(input) {
5711
5418
  const environments = declared === void 0 ? chosen : declared.environments;
5712
5419
  for (const environment of chosen) {
5713
5420
  if (!environments.includes(environment)) {
5714
- throw new PenvError18(
5421
+ throw new PenvError15(
5715
5422
  "INIT_ENVIRONMENT_UNDECLARED",
5716
5423
  `${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
5717
5424
  `Add \`${environment}\` to \`environments\` in ${CONFIG_FILE}, with a provider for it, then run \`penv init\` again. Nothing was changed.`
@@ -5736,7 +5443,7 @@ function planCutover(input) {
5736
5443
  const diagnostics = [];
5737
5444
  let variables = 0;
5738
5445
  for (const file of selected) {
5739
- const parsed = parseDotenv(readFileSync9(join10(root, file.name), "utf8"));
5446
+ const parsed = parseDotenv(readFileSync8(join8(root, file.name), "utf8"));
5740
5447
  const fileRefs = refsForEntries(parsed.entries, file.name, config);
5741
5448
  adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
5742
5449
  refs.push(...fileRefs);
@@ -5798,7 +5505,7 @@ function requireEnvironment(input) {
5798
5505
  if (environment.length > 0) {
5799
5506
  return environment;
5800
5507
  }
5801
- throw new PenvError18(
5508
+ throw new PenvError15(
5802
5509
  "INIT_ENVIRONMENT_UNNAMED",
5803
5510
  "The selected files name no environment, and penv does not invent one",
5804
5511
  `Run \`penv init --env ${DEVELOPMENT}\` to say which environment these values are for.`
@@ -5810,7 +5517,7 @@ function assertCascadeComplete(root, selected, environments) {
5810
5517
  for (const environment of environments) {
5811
5518
  for (const name of cascadeFor(environment)) {
5812
5519
  if (present.has(name) && !taken.has(name)) {
5813
- throw new PenvError18(
5520
+ throw new PenvError15(
5814
5521
  "INIT_CUTOVER_INCOMPLETE",
5815
5522
  `${name} is part of ${environment}'s cascade and was not selected, so your framework would keep reading it beside penv`,
5816
5523
  "Run `penv init` again and take every file penv listed for that environment. Nothing was changed."
@@ -5823,7 +5530,7 @@ function assertBundleResolved(root) {
5823
5530
  if (!bundleUnresolved(root)) {
5824
5531
  return;
5825
5532
  }
5826
- throw new PenvError18(
5533
+ throw new PenvError15(
5827
5534
  "INIT_BUNDLE_UNRESOLVED",
5828
5535
  `The dotenv files from the last cutover are still in ${ROLLBACK_DOTENV_PATH3}/, and penv will not migrate a second time over them`,
5829
5536
  "Run `penv init undo` to put them back, or `penv cleanup` to drop them once you are happy with the migration."
@@ -5835,7 +5542,7 @@ function selectionForYes(plan2) {
5835
5542
  (file) => file.environment !== void 0 && file.environment !== DEVELOPMENT
5836
5543
  );
5837
5544
  if (shared !== void 0 && other !== void 0) {
5838
- throw new PenvError18(
5545
+ throw new PenvError15(
5839
5546
  "INIT_YES_SHARED_FALLBACK",
5840
5547
  `${other.name} falls back to the shared ${shared.name} this cutover would move, so \`--yes\` will not decide what happens to ${other.environment ?? ""}`,
5841
5548
  "Run `penv init` without `--yes` and choose every file the cutover takes. Nothing was changed."
@@ -5881,7 +5588,7 @@ function issueLines(result2) {
5881
5588
  }
5882
5589
  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
5590
  function invalidAfterImport(result2) {
5884
- return new PenvError18(
5591
+ return new PenvError15(
5885
5592
  "INIT_CUTOVER_INVALID",
5886
5593
  `The imported values do not satisfy the draft schema for ${result2.environment}:
5887
5594
  ${issueLines(result2)}`,
@@ -5889,7 +5596,7 @@ ${issueLines(result2)}`,
5889
5596
  );
5890
5597
  }
5891
5598
  function draftNotLoaded(result2) {
5892
- return new PenvError18(
5599
+ return new PenvError15(
5893
5600
  "INIT_DRAFT_NOT_LOADED",
5894
5601
  `penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
5895
5602
  ${issueLines(result2)}`,
@@ -5956,7 +5663,11 @@ function renderCutover(result2) {
5956
5663
  const glyph = step.action === "conflicted" ? WARN : step.action === "info" ? "\u2192" : CHECK;
5957
5664
  return step.note === void 0 ? { glyph, text: step.text } : { glyph, text: step.text, note: step.note };
5958
5665
  }),
5959
- ...plan2.install.packages.filter((entry) => !entry.satisfied).map((entry) => ({ glyph: CHECK, text: `Installed ${entry.name}`, note: entry.version })),
5666
+ ...installedPackages(plan2.install).map((entry) => ({
5667
+ glyph: CHECK,
5668
+ text: `Installed ${entry.name}`,
5669
+ note: entry.version
5670
+ })),
5960
5671
  {
5961
5672
  glyph: CHECK,
5962
5673
  text: `Imported ${plan2.fields.length} parameters`,
@@ -5980,12 +5691,12 @@ function dailyCommand(root) {
5980
5691
  return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
5981
5692
  }
5982
5693
  function devScript(root) {
5983
- const file = join10(root, PACKAGE_FILE);
5984
- if (!existsSync8(file)) {
5694
+ const file = join8(root, PACKAGE_FILE);
5695
+ if (!existsSync6(file)) {
5985
5696
  return "dev";
5986
5697
  }
5987
5698
  try {
5988
- const scripts = JSON.parse(readFileSync9(file, "utf8")).scripts;
5699
+ const scripts = JSON.parse(readFileSync8(file, "utf8")).scripts;
5989
5700
  if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
5990
5701
  const named = Object.keys(scripts);
5991
5702
  return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
@@ -6029,7 +5740,7 @@ async function promptForSelection(plan2, io) {
6029
5740
  for (const name of names) {
6030
5741
  const file = plan2.found.find((candidate) => candidate.name === name);
6031
5742
  if (file === void 0) {
6032
- throw new PenvError18(
5743
+ throw new PenvError15(
6033
5744
  "INIT_SELECTION_UNKNOWN",
6034
5745
  `\`${name}\` is not one of the dotenv files penv found`,
6035
5746
  "Run `penv init` again and name the files exactly as they are listed, e.g. `.env.production`."
@@ -6192,7 +5903,7 @@ var initCommand = defineCommand13({
6192
5903
  });
6193
5904
  function runUndoAction(root, action) {
6194
5905
  if (action !== "undo") {
6195
- throw new PenvError18(
5906
+ throw new PenvError15(
6196
5907
  "INIT_UNKNOWN_ACTION",
6197
5908
  `\`penv init ${action}\` is not something init does`,
6198
5909
  "Run `penv init undo` to put back the dotenv files of the last cutover."
@@ -6298,7 +6009,7 @@ function explicitEnvironment(options, source, config) {
6298
6009
  if (value.length > 0) {
6299
6010
  return value;
6300
6011
  }
6301
- throw new PenvError19(
6012
+ throw new PenvError16(
6302
6013
  "IMPORT_ENV_FLAG_EMPTY",
6303
6014
  `\`--env\` for the import of ${source} names no environment`,
6304
6015
  `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 +6019,7 @@ function assertEnvironmentAgrees(derived, explicit, source) {
6308
6019
  if (derived === void 0 || explicit === void 0 || derived === explicit) {
6309
6020
  return;
6310
6021
  }
6311
- throw new PenvError19(
6022
+ throw new PenvError16(
6312
6023
  "IMPORT_ENV_CONFLICT",
6313
6024
  `The file ${source} is scoped to environment ${derived}, but \`--env ${explicit}\` names ${explicit}`,
6314
6025
  `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 +6064,15 @@ function configInEffect(cwd, environment) {
6353
6064
  }
6354
6065
  function importDotenv(options) {
6355
6066
  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(
6067
+ const file = isAbsolute3(options.file) ? options.file : resolve7(cwd, options.file);
6068
+ if (!existsSync7(file)) {
6069
+ throw new PenvError16(
6359
6070
  "IMPORT_FILE_MISSING",
6360
6071
  `There is no file at ${file} to import`,
6361
6072
  "Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
6362
6073
  );
6363
6074
  }
6364
- const parsed = parseDotenv2(readFileSync10(file, "utf8"));
6075
+ const parsed = parseDotenv2(readFileSync9(file, "utf8"));
6365
6076
  const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
6366
6077
  const source = displayPath3(cwd, file);
6367
6078
  const named = scopeFromFilename(file, config);
@@ -6479,19 +6190,19 @@ var importCommand = defineCommand14({
6479
6190
 
6480
6191
  // src/commands/key.ts
6481
6192
  import { randomBytes } from "crypto";
6482
- import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError21 } from "@penvhq/core";
6193
+ import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError18 } from "@penvhq/core";
6483
6194
  import { defineCommand as defineCommand15 } from "citty";
6484
6195
 
6485
6196
  // src/keychain.ts
6486
6197
  import { createRequire as createRequire3 } from "module";
6487
- import { PenvError as PenvError20 } from "@penvhq/core";
6198
+ import { PenvError as PenvError17 } from "@penvhq/core";
6488
6199
  var KEYRING_MODULE = "@napi-rs/keyring";
6489
6200
  var nodeRequire = (id) => createRequire3(import.meta.url)(id);
6490
6201
  function firstLine2(cause) {
6491
6202
  return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
6492
6203
  }
6493
6204
  function keyringMissing(cause) {
6494
- return new PenvError20(
6205
+ return new PenvError17(
6495
6206
  "KEYCHAIN_BINDING_MISSING",
6496
6207
  `penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
6497
6208
  `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 +6244,7 @@ function runKeyCreate(options) {
6533
6244
  const environment = targetEnvironment(project, options.environment);
6534
6245
  const declared = project.config.keys?.[environment];
6535
6246
  if (declared === void 0) {
6536
- throw new PenvError21(
6247
+ throw new PenvError18(
6537
6248
  "KEY_SOURCE_UNDECLARED",
6538
6249
  `Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
6539
6250
  `Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
@@ -6547,17 +6258,17 @@ function runKeyCreate(options) {
6547
6258
  try {
6548
6259
  existing = keychain.getPassword(KEYCHAIN_SERVICE, declared.id);
6549
6260
  } catch (cause) {
6550
- if (cause instanceof PenvError21) {
6261
+ if (cause instanceof PenvError18) {
6551
6262
  throw cause;
6552
6263
  }
6553
- throw new PenvError21(
6264
+ throw new PenvError18(
6554
6265
  "KEYCHAIN_UNAVAILABLE",
6555
6266
  `penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
6556
6267
  `Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
6557
6268
  );
6558
6269
  }
6559
6270
  if (existing !== null) {
6560
- throw new PenvError21(
6271
+ throw new PenvError18(
6561
6272
  "KEY_EXISTS",
6562
6273
  `Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
6563
6274
  "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 +6423,21 @@ var listCommand = defineCommand16({
6712
6423
 
6713
6424
  // src/commands/migrate.ts
6714
6425
  import {
6715
- existsSync as existsSync10,
6426
+ existsSync as existsSync8,
6716
6427
  mkdirSync as mkdirSync5,
6717
6428
  readdirSync as readdirSync6,
6718
- readFileSync as readFileSync11,
6429
+ readFileSync as readFileSync10,
6719
6430
  renameSync as renameSync2,
6720
6431
  rmSync as rmSync2,
6721
6432
  writeFileSync as writeFileSync6
6722
6433
  } from "fs";
6723
- import { dirname as dirname7, join as join11 } from "path";
6434
+ import { dirname as dirname7, join as join9 } from "path";
6724
6435
  import { createInterface as createInterface3 } from "readline/promises";
6725
6436
  import {
6726
6437
  loadConfig as loadConfig2,
6727
6438
  oldLayoutEntries,
6728
6439
  PENV_DIR as PENV_DIR3,
6729
- PenvError as PenvError22,
6440
+ PenvError as PenvError19,
6730
6441
  RECORDS_PATH as RECORDS_PATH5,
6731
6442
  recordsDir as recordsDir3,
6732
6443
  renderStateGitignore as renderStateGitignore2,
@@ -6741,17 +6452,17 @@ function planMigrate(cwd) {
6741
6452
  const tree = recordsDir3(root);
6742
6453
  const collisions = collidingEntries(entries, tree);
6743
6454
  if (collisions.length > 0) {
6744
- throw new PenvError22(
6455
+ throw new PenvError19(
6745
6456
  "HALF_MIGRATED",
6746
- `${describe2(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
6457
+ `${describe(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
6747
6458
  `Move what is left under \`${PENV_DIR3}/\` into \`${RECORDS_PATH5}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
6748
6459
  );
6749
6460
  }
6750
6461
  const creates = [];
6751
- if (entries.length > 0 && !existsSync10(tree)) {
6462
+ if (entries.length > 0 && !existsSync8(tree)) {
6752
6463
  creates.push(`${RECORDS_PATH5}/`);
6753
6464
  }
6754
- if (readIfPresent(join11(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
6465
+ if (readIfPresent(join9(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
6755
6466
  creates.push(STATE_GITIGNORE_PATH2);
6756
6467
  }
6757
6468
  return {
@@ -6761,11 +6472,11 @@ function planMigrate(cwd) {
6761
6472
  to: `${RECORDS_PATH5}/${entry}`
6762
6473
  })),
6763
6474
  creates,
6764
- removes: existsSync10(join11(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6475
+ removes: existsSync8(join9(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6765
6476
  };
6766
6477
  }
6767
6478
  function readIfPresent(file) {
6768
- return existsSync10(file) ? readFileSync11(file, "utf8") : void 0;
6479
+ return existsSync8(file) ? readFileSync10(file, "utf8") : void 0;
6769
6480
  }
6770
6481
  function collidingEntries(entries, tree) {
6771
6482
  let held;
@@ -6777,7 +6488,7 @@ function collidingEntries(entries, tree) {
6777
6488
  const taken = new Set(held.map((name) => name.toLowerCase()));
6778
6489
  return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
6779
6490
  }
6780
- function describe2(names) {
6491
+ function describe(names) {
6781
6492
  return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
6782
6493
  }
6783
6494
  function isNoop(plan2) {
@@ -6791,16 +6502,16 @@ function applyMigrate(plan2) {
6791
6502
  if (plan2.moves.length > 0) {
6792
6503
  mkdirSync5(recordsDir3(plan2.root), { recursive: true });
6793
6504
  for (const move of plan2.moves) {
6794
- renameSync2(join11(plan2.root, ...move.from.split("/")), join11(plan2.root, ...move.to.split("/")));
6505
+ renameSync2(join9(plan2.root, ...move.from.split("/")), join9(plan2.root, ...move.to.split("/")));
6795
6506
  }
6796
6507
  }
6797
6508
  if (plan2.creates.includes(STATE_GITIGNORE_PATH2)) {
6798
- const ignore = join11(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
6509
+ const ignore = join9(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
6799
6510
  mkdirSync5(dirname7(ignore), { recursive: true });
6800
6511
  writeFileSync6(ignore, renderStateGitignore2(config), "utf8");
6801
6512
  }
6802
6513
  for (const removed of plan2.removes) {
6803
- rmSync2(join11(plan2.root, ...removed.split("/")), { force: true });
6514
+ rmSync2(join9(plan2.root, ...removed.split("/")), { force: true });
6804
6515
  }
6805
6516
  return { ...plan2, status: "migrated" };
6806
6517
  }
@@ -6882,7 +6593,7 @@ import {
6882
6593
  formatMetaFile,
6883
6594
  formatValueFile as formatValueFile6,
6884
6595
  openValue as openValue4,
6885
- PenvError as PenvError23,
6596
+ PenvError as PenvError20,
6886
6597
  parameterId as parameterId7,
6887
6598
  recordPath as recordPath7,
6888
6599
  sealValue as sealValue3
@@ -6902,7 +6613,7 @@ async function planFile(project, source, target, parameter) {
6902
6613
  }
6903
6614
  const environment = environmentOf2(source);
6904
6615
  if (environment === void 0) {
6905
- throw new PenvError23(
6616
+ throw new PenvError20(
6906
6617
  "SECRET_SCOPE_AMBIGUOUS",
6907
6618
  `${recordPath7(formatValueFile6(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
6908
6619
  "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 +6622,7 @@ async function planFile(project, source, target, parameter) {
6911
6622
  const keys = keySourceFor(project, environment);
6912
6623
  const opened = openValue4(source, stored, keys);
6913
6624
  if (opened.kind === "failed") {
6914
- throw new PenvError23(
6625
+ throw new PenvError20(
6915
6626
  "VALUE_UNDECRYPTABLE",
6916
6627
  `${recordPath7(formatValueFile6(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
6917
6628
  "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 +6645,7 @@ async function runMove(options) {
6934
6645
  assertWritableKey(options.to);
6935
6646
  const to = refFromKey(options.to, project.config);
6936
6647
  if (parameterId7(from) === parameterId7(to)) {
6937
- throw new PenvError23(
6648
+ throw new PenvError20(
6938
6649
  "PARAMETER_UNCHANGED",
6939
6650
  `\`${options.from}\` and \`${options.to}\` are the same parameter`,
6940
6651
  "Name a different destination, e.g. `penv mv redis-password redis/password`."
@@ -6944,7 +6655,7 @@ async function runMove(options) {
6944
6655
  const sources = filesOf(all, from);
6945
6656
  const meta = await project.provider.readMeta(from);
6946
6657
  if (sources.length === 0 && meta === void 0) {
6947
- throw new PenvError23(
6658
+ throw new PenvError20(
6948
6659
  "PARAMETER_ABSENT",
6949
6660
  `Parameter ${parameterId7(from)} has no value files and no meta, so there is nothing to move`,
6950
6661
  `\`penv list\` shows every parameter penv holds.`
@@ -6952,7 +6663,7 @@ async function runMove(options) {
6952
6663
  }
6953
6664
  const occupied2 = filesOf(all, to);
6954
6665
  if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
6955
- throw new PenvError23(
6666
+ throw new PenvError20(
6956
6667
  "PARAMETER_EXISTS",
6957
6668
  `Parameter ${parameterId7(to)} already exists, and penv will not merge two parameters into one`,
6958
6669
  `Remove or rename ${parameterId7(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
@@ -7111,7 +6822,7 @@ import {
7111
6822
  beginRotation,
7112
6823
  completeRotation,
7113
6824
  holdsRecords as holdsRecords2,
7114
- PenvError as PenvError24,
6825
+ PenvError as PenvError21,
7115
6826
  retainsPrevious,
7116
6827
  rotationOf as rotationOf2
7117
6828
  } from "@penvhq/core";
@@ -7140,7 +6851,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
7140
6851
  }
7141
6852
  function requireNewValue(value, phase, key) {
7142
6853
  if (value === void 0) {
7143
- throw new PenvError24(
6854
+ throw new PenvError21(
7144
6855
  "ROTATION_NO_VALUE",
7145
6856
  `A ${phase} rotation of ${key} writes a new value, and none was given`,
7146
6857
  "Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
@@ -7150,7 +6861,7 @@ function requireNewValue(value, phase, key) {
7150
6861
  }
7151
6862
  function requireRetaining(provider, environment) {
7152
6863
  if (!retainsPrevious(provider)) {
7153
- throw new PenvError24(
6864
+ throw new PenvError21(
7154
6865
  "ROTATION_NOT_RETAINING",
7155
6866
  `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
6867
  "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 +6875,7 @@ async function runRotate(options) {
7164
6875
  const ref = refFromKey(options.key, project.config);
7165
6876
  const source = await sourceProviderFor(project, environment);
7166
6877
  if (!holdsRecords2(source)) {
7167
- throw new PenvError24(
6878
+ throw new PenvError21(
7168
6879
  "ROTATION_NOT_RECORDS",
7169
6880
  `Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
7170
6881
  "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 +6886,7 @@ async function runRotate(options) {
7175
6886
  const before = await provider.readMeta(ref);
7176
6887
  const { mechanism } = rotationOf2(before, environment);
7177
6888
  if (mechanism === void 0) {
7178
- throw new PenvError24(
6889
+ throw new PenvError21(
7179
6890
  "ROTATION_NO_MECHANISM",
7180
6891
  `Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
7181
6892
  '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 +6896,7 @@ async function runRotate(options) {
7185
6896
  const complete = options.complete === true;
7186
6897
  if (mechanism === "atomic-cutover") {
7187
6898
  if (begin || complete) {
7188
- throw new PenvError24(
6899
+ throw new PenvError21(
7189
6900
  "ROTATION_MECHANISM_MISMATCH",
7190
6901
  `Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
7191
6902
  "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 +6910,7 @@ async function runRotate(options) {
7199
6910
  }
7200
6911
  const retaining = requireRetaining(provider, environment);
7201
6912
  if (begin === complete) {
7202
- throw new PenvError24(
6913
+ throw new PenvError21(
7203
6914
  "ROTATION_PHASE_REQUIRED",
7204
6915
  `A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
7205
6916
  "`--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 +7013,7 @@ var rotateCommand = defineCommand20({
7302
7013
  });
7303
7014
 
7304
7015
  // src/commands/watch.ts
7305
- import { existsSync as existsSync11, watch as watch2 } from "fs";
7016
+ import { existsSync as existsSync9, watch as watch2 } from "fs";
7306
7017
  import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
7307
7018
  import { SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE4, schemaFileOf as schemaFileOf5, schemaInsideTree } from "@penvhq/core";
7308
7019
  import { defineCommand as defineCommand21 } from "citty";
@@ -7370,14 +7081,14 @@ function runWatch(options) {
7370
7081
  if (closed || recovery === void 0) {
7371
7082
  return;
7372
7083
  }
7373
- if (!existsSync11(parent)) {
7084
+ if (!existsSync9(parent)) {
7374
7085
  stop2(recovery);
7375
7086
  return;
7376
7087
  }
7377
7088
  if (filename !== null && basename3(filename) !== name) {
7378
7089
  return;
7379
7090
  }
7380
- if (!existsSync11(target)) {
7091
+ if (!existsSync9(target)) {
7381
7092
  return;
7382
7093
  }
7383
7094
  stop2(recovery);
@@ -7401,7 +7112,7 @@ function runWatch(options) {
7401
7112
  if (closed) {
7402
7113
  return;
7403
7114
  }
7404
- if (!existsSync11(target)) {
7115
+ if (!existsSync9(target)) {
7405
7116
  if (watcher !== void 0) {
7406
7117
  stop2(watcher);
7407
7118
  }