@penvhq/cli 0.9.0 → 0.9.1

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.cjs CHANGED
@@ -65,7 +65,7 @@ __export(src_exports, {
65
65
  runWatch: () => runWatch
66
66
  });
67
67
  module.exports = __toCommonJS(src_exports);
68
- var import_core34 = require("@penvhq/core");
68
+ var import_core35 = require("@penvhq/core");
69
69
  var import_citty22 = require("citty");
70
70
 
71
71
  // src/commands/artifact.ts
@@ -471,9 +471,9 @@ function resolvePlugin(specifier, fromDir) {
471
471
  }
472
472
  }
473
473
  function importPlugin(resolvedPath) {
474
- const cached2 = pluginModuleCache.get(resolvedPath);
475
- if (cached2 !== void 0) {
476
- return cached2;
474
+ const cached = pluginModuleCache.get(resolvedPath);
475
+ if (cached !== void 0) {
476
+ return cached;
477
477
  }
478
478
  const loading = import((0, import_node_url.pathToFileURL)(resolvedPath).href);
479
479
  pluginModuleCache.set(resolvedPath, loading);
@@ -6054,30 +6054,51 @@ var importCommand = (0, import_citty14.defineCommand)({
6054
6054
 
6055
6055
  // src/commands/key.ts
6056
6056
  var import_node_crypto = require("crypto");
6057
- var import_core27 = require("@penvhq/core");
6057
+ var import_core28 = require("@penvhq/core");
6058
6058
  var import_citty15 = require("citty");
6059
6059
 
6060
6060
  // src/keychain.ts
6061
6061
  var import_node_module3 = require("module");
6062
+ var import_core27 = require("@penvhq/core");
6062
6063
  var import_meta2 = {};
6063
- var cached;
6064
- function entryConstructor() {
6065
- if (cached === void 0) {
6066
- const require2 = (0, import_node_module3.createRequire)(import_meta2.url);
6067
- cached = require2("@napi-rs/keyring").Entry;
6068
- }
6069
- return cached;
6070
- }
6071
- var defaultKeychain = {
6072
- getPassword(service, account) {
6073
- const Entry = entryConstructor();
6074
- return new Entry(service, account).getPassword();
6075
- },
6076
- setPassword(service, account, password) {
6077
- const Entry = entryConstructor();
6078
- new Entry(service, account).setPassword(password);
6079
- }
6080
- };
6064
+ var KEYRING_MODULE = "@napi-rs/keyring";
6065
+ var nodeRequire = (id) => (0, import_node_module3.createRequire)(import_meta2.url)(id);
6066
+ function firstLine2(cause) {
6067
+ return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
6068
+ }
6069
+ function keyringMissing(cause) {
6070
+ return new import_core27.PenvError(
6071
+ "KEYCHAIN_BINDING_MISSING",
6072
+ `penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
6073
+ `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)}`
6074
+ );
6075
+ }
6076
+ function createKeychain(load = nodeRequire) {
6077
+ let cached;
6078
+ const entryConstructor = () => {
6079
+ if (cached === void 0) {
6080
+ let module2;
6081
+ try {
6082
+ module2 = load(KEYRING_MODULE);
6083
+ } catch (cause) {
6084
+ throw keyringMissing(cause);
6085
+ }
6086
+ cached = module2.Entry;
6087
+ }
6088
+ return cached;
6089
+ };
6090
+ return {
6091
+ getPassword(service, account) {
6092
+ const Entry = entryConstructor();
6093
+ return new Entry(service, account).getPassword();
6094
+ },
6095
+ setPassword(service, account, password) {
6096
+ const Entry = entryConstructor();
6097
+ new Entry(service, account).setPassword(password);
6098
+ }
6099
+ };
6100
+ }
6101
+ var defaultKeychain = createKeychain();
6081
6102
 
6082
6103
  // src/commands/key.ts
6083
6104
  function envVarFor(id) {
@@ -6088,35 +6109,38 @@ function runKeyCreate(options) {
6088
6109
  const environment = targetEnvironment(project, options.environment);
6089
6110
  const declared = project.config.keys?.[environment];
6090
6111
  if (declared === void 0) {
6091
- throw new import_core27.PenvError(
6112
+ throw new import_core28.PenvError(
6092
6113
  "KEY_SOURCE_UNDECLARED",
6093
6114
  `Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
6094
6115
  `Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
6095
6116
  );
6096
6117
  }
6097
- const key = (0, import_node_crypto.randomBytes)(import_core27.KEY_BYTES).toString("base64");
6118
+ const key = (0, import_node_crypto.randomBytes)(import_core28.KEY_BYTES).toString("base64");
6098
6119
  if (declared.source === "keychain") {
6099
6120
  const keychain = options.keychain ?? defaultKeychain;
6100
6121
  if (options.force !== true) {
6101
6122
  let existing;
6102
6123
  try {
6103
- existing = keychain.getPassword(import_core27.KEYCHAIN_SERVICE, declared.id);
6124
+ existing = keychain.getPassword(import_core28.KEYCHAIN_SERVICE, declared.id);
6104
6125
  } catch (cause) {
6105
- throw new import_core27.PenvError(
6126
+ if (cause instanceof import_core28.PenvError) {
6127
+ throw cause;
6128
+ }
6129
+ throw new import_core28.PenvError(
6106
6130
  "KEYCHAIN_UNAVAILABLE",
6107
6131
  `penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
6108
6132
  `Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
6109
6133
  );
6110
6134
  }
6111
6135
  if (existing !== null) {
6112
- throw new import_core27.PenvError(
6136
+ throw new import_core28.PenvError(
6113
6137
  "KEY_EXISTS",
6114
6138
  `Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
6115
6139
  "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."
6116
6140
  );
6117
6141
  }
6118
6142
  }
6119
- keychain.setPassword(import_core27.KEYCHAIN_SERVICE, declared.id, key);
6143
+ keychain.setPassword(import_core28.KEYCHAIN_SERVICE, declared.id, key);
6120
6144
  return { source: "keychain", environment, id: declared.id };
6121
6145
  }
6122
6146
  return {
@@ -6184,7 +6208,7 @@ var keyCommand = (0, import_citty15.defineCommand)({
6184
6208
  });
6185
6209
 
6186
6210
  // src/commands/list.ts
6187
- var import_core28 = require("@penvhq/core");
6211
+ var import_core29 = require("@penvhq/core");
6188
6212
  var import_citty16 = require("citty");
6189
6213
  function scopeLabel2(scope) {
6190
6214
  switch (scope.kind) {
@@ -6197,7 +6221,7 @@ function scopeLabel2(scope) {
6197
6221
  case "unscoped":
6198
6222
  return "default";
6199
6223
  default:
6200
- return (0, import_core28.assertNever)(scope, "scope");
6224
+ return (0, import_core29.assertNever)(scope, "scope");
6201
6225
  }
6202
6226
  }
6203
6227
  async function runList(options) {
@@ -6205,12 +6229,12 @@ async function runList(options) {
6205
6229
  const environment = targetEnvironment(project, options.environment);
6206
6230
  const keys = keySourceFor(project, environment);
6207
6231
  const parameters = [];
6208
- for (const resolution of await (0, import_core28.resolveAll)(environment, project.provider, keys)) {
6232
+ for (const resolution of await (0, import_core29.resolveAll)(environment, project.provider, keys)) {
6209
6233
  const winner = resolution.winner;
6210
6234
  const scope = winner?.file.scope;
6211
6235
  parameters.push({
6212
6236
  parameter: resolution.parameter,
6213
- variable: (0, import_core28.variableName)(resolution.ref, project.config),
6237
+ variable: (0, import_core29.variableName)(resolution.ref, project.config),
6214
6238
  scope: scope === void 0 ? "absent" : scopeLabel2(scope),
6215
6239
  location: winner?.location,
6216
6240
  encrypted: winner?.file.encrypted === true,
@@ -6228,7 +6252,7 @@ function paintScope(entry) {
6228
6252
  function renderList(result2) {
6229
6253
  if (result2.parameters.length === 0) {
6230
6254
  return [
6231
- `No parameters in ${import_core28.RECORDS_PATH}/ for environment ${result2.environment}.`,
6255
+ `No parameters in ${import_core29.RECORDS_PATH}/ for environment ${result2.environment}.`,
6232
6256
  tip(`penv set <key> --env ${result2.environment}`)
6233
6257
  ];
6234
6258
  }
@@ -6266,34 +6290,34 @@ var listCommand = (0, import_citty16.defineCommand)({
6266
6290
  var import_node_fs13 = require("fs");
6267
6291
  var import_node_path14 = require("path");
6268
6292
  var import_promises2 = require("readline/promises");
6269
- var import_core29 = require("@penvhq/core");
6293
+ var import_core30 = require("@penvhq/core");
6270
6294
  var import_citty17 = require("citty");
6271
- var OLD_GITIGNORE = `${import_core29.PENV_DIR}/.gitignore`;
6295
+ var OLD_GITIGNORE = `${import_core30.PENV_DIR}/.gitignore`;
6272
6296
  function planMigrate(cwd) {
6273
- const { config, file } = (0, import_core29.loadConfig)(cwd);
6297
+ const { config, file } = (0, import_core30.loadConfig)(cwd);
6274
6298
  const root = (0, import_node_path14.dirname)(file);
6275
- const entries = (0, import_core29.oldLayoutEntries)(root, config);
6276
- const tree = (0, import_core29.recordsDir)(root);
6299
+ const entries = (0, import_core30.oldLayoutEntries)(root, config);
6300
+ const tree = (0, import_core30.recordsDir)(root);
6277
6301
  const collisions = collidingEntries(entries, tree);
6278
6302
  if (collisions.length > 0) {
6279
- throw new import_core29.PenvError(
6303
+ throw new import_core30.PenvError(
6280
6304
  "HALF_MIGRATED",
6281
- `${describe(collisions)} in both \`${import_core29.PENV_DIR}/\` and \`${import_core29.RECORDS_PATH}/\`, and penv cannot tell which copy is current`,
6282
- `Move what is left under \`${import_core29.PENV_DIR}/\` into \`${import_core29.RECORDS_PATH}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
6305
+ `${describe(collisions)} in both \`${import_core30.PENV_DIR}/\` and \`${import_core30.RECORDS_PATH}/\`, and penv cannot tell which copy is current`,
6306
+ `Move what is left under \`${import_core30.PENV_DIR}/\` into \`${import_core30.RECORDS_PATH}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
6283
6307
  );
6284
6308
  }
6285
6309
  const creates = [];
6286
6310
  if (entries.length > 0 && !(0, import_node_fs13.existsSync)(tree)) {
6287
- creates.push(`${import_core29.RECORDS_PATH}/`);
6311
+ creates.push(`${import_core30.RECORDS_PATH}/`);
6288
6312
  }
6289
- if (readIfPresent((0, import_node_path14.join)(root, ...import_core29.STATE_GITIGNORE_PATH.split("/"))) !== (0, import_core29.renderStateGitignore)(config)) {
6290
- creates.push(import_core29.STATE_GITIGNORE_PATH);
6313
+ if (readIfPresent((0, import_node_path14.join)(root, ...import_core30.STATE_GITIGNORE_PATH.split("/"))) !== (0, import_core30.renderStateGitignore)(config)) {
6314
+ creates.push(import_core30.STATE_GITIGNORE_PATH);
6291
6315
  }
6292
6316
  return {
6293
6317
  root,
6294
6318
  moves: entries.map((entry) => ({
6295
- from: `${import_core29.PENV_DIR}/${entry}`,
6296
- to: `${import_core29.RECORDS_PATH}/${entry}`
6319
+ from: `${import_core30.PENV_DIR}/${entry}`,
6320
+ to: `${import_core30.RECORDS_PATH}/${entry}`
6297
6321
  })),
6298
6322
  creates,
6299
6323
  removes: (0, import_node_fs13.existsSync)((0, import_node_path14.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
@@ -6322,17 +6346,17 @@ function applyMigrate(plan2) {
6322
6346
  if (isNoop(plan2)) {
6323
6347
  return { ...plan2, status: "current" };
6324
6348
  }
6325
- const { config } = (0, import_core29.loadConfig)(plan2.root);
6349
+ const { config } = (0, import_core30.loadConfig)(plan2.root);
6326
6350
  if (plan2.moves.length > 0) {
6327
- (0, import_node_fs13.mkdirSync)((0, import_core29.recordsDir)(plan2.root), { recursive: true });
6351
+ (0, import_node_fs13.mkdirSync)((0, import_core30.recordsDir)(plan2.root), { recursive: true });
6328
6352
  for (const move of plan2.moves) {
6329
6353
  (0, import_node_fs13.renameSync)((0, import_node_path14.join)(plan2.root, ...move.from.split("/")), (0, import_node_path14.join)(plan2.root, ...move.to.split("/")));
6330
6354
  }
6331
6355
  }
6332
- if (plan2.creates.includes(import_core29.STATE_GITIGNORE_PATH)) {
6333
- const ignore = (0, import_node_path14.join)(plan2.root, ...import_core29.STATE_GITIGNORE_PATH.split("/"));
6356
+ if (plan2.creates.includes(import_core30.STATE_GITIGNORE_PATH)) {
6357
+ const ignore = (0, import_node_path14.join)(plan2.root, ...import_core30.STATE_GITIGNORE_PATH.split("/"));
6334
6358
  (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(ignore), { recursive: true });
6335
- (0, import_node_fs13.writeFileSync)(ignore, (0, import_core29.renderStateGitignore)(config), "utf8");
6359
+ (0, import_node_fs13.writeFileSync)(ignore, (0, import_core30.renderStateGitignore)(config), "utf8");
6336
6360
  }
6337
6361
  for (const removed of plan2.removes) {
6338
6362
  (0, import_node_fs13.rmSync)((0, import_node_path14.join)(plan2.root, ...removed.split("/")), { force: true });
@@ -6348,7 +6372,7 @@ function runMigrate(options) {
6348
6372
  }
6349
6373
  function renderMigrate(result2) {
6350
6374
  if (result2.status === "current") {
6351
- return [`${out.green(CHECK)} Already on ${import_core29.RECORDS_PATH}/ \u2014 nothing to migrate.`];
6375
+ return [`${out.green(CHECK)} Already on ${import_core30.RECORDS_PATH}/ \u2014 nothing to migrate.`];
6352
6376
  }
6353
6377
  const done = result2.status === "migrated";
6354
6378
  const glyph = done ? CHECK : "\u2192";
@@ -6362,7 +6386,7 @@ function renderMigrate(result2) {
6362
6386
  ...result2.removes.map((removed) => ({
6363
6387
  glyph,
6364
6388
  text: `${done ? "Removed" : "Remove"} ${removed}`,
6365
- note: `replaced by ${import_core29.STATE_GITIGNORE_PATH}`
6389
+ note: `replaced by ${import_core30.STATE_GITIGNORE_PATH}`
6366
6390
  }))
6367
6391
  ];
6368
6392
  return [
@@ -6389,7 +6413,7 @@ async function approveOnTty() {
6389
6413
  var migrateCommand = (0, import_citty17.defineCommand)({
6390
6414
  meta: {
6391
6415
  name: "migrate",
6392
- description: `Move a project written under an earlier layout to ${import_core29.RECORDS_PATH}/`
6416
+ description: `Move a project written under an earlier layout to ${import_core30.RECORDS_PATH}/`
6393
6417
  },
6394
6418
  args: {
6395
6419
  yes: { type: "boolean", description: "Apply the previewed move without asking" }
@@ -6412,7 +6436,7 @@ var migrateCommand = (0, import_citty17.defineCommand)({
6412
6436
  });
6413
6437
 
6414
6438
  // src/commands/mv.ts
6415
- var import_core30 = require("@penvhq/core");
6439
+ var import_core31 = require("@penvhq/core");
6416
6440
  var import_citty18 = require("citty");
6417
6441
  function environmentOf2(file) {
6418
6442
  const scope = file.scope;
@@ -6428,39 +6452,39 @@ async function planFile(project, source, target, parameter) {
6428
6452
  }
6429
6453
  const environment = environmentOf2(source);
6430
6454
  if (environment === void 0) {
6431
- throw new import_core30.PenvError(
6455
+ throw new import_core31.PenvError(
6432
6456
  "SECRET_SCOPE_AMBIGUOUS",
6433
- `${(0, import_core30.recordPath)((0, import_core30.formatValueFile)(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
6457
+ `${(0, import_core31.recordPath)((0, import_core31.formatValueFile)(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
6434
6458
  "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."
6435
6459
  );
6436
6460
  }
6437
6461
  const keys = keySourceFor(project, environment);
6438
- const opened = (0, import_core30.openValue)(source, stored, keys);
6462
+ const opened = (0, import_core31.openValue)(source, stored, keys);
6439
6463
  if (opened.kind === "failed") {
6440
- throw new import_core30.PenvError(
6464
+ throw new import_core31.PenvError(
6441
6465
  "VALUE_UNDECRYPTABLE",
6442
- `${(0, import_core30.recordPath)((0, import_core30.formatValueFile)(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
6466
+ `${(0, import_core31.recordPath)((0, import_core31.formatValueFile)(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
6443
6467
  "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."
6444
6468
  );
6445
6469
  }
6446
6470
  return {
6447
6471
  source,
6448
6472
  target,
6449
- contents: (0, import_core30.sealValue)(target, opened.value, keys, parameter, environment),
6473
+ contents: (0, import_core31.sealValue)(target, opened.value, keys, parameter, environment),
6450
6474
  resealed: true
6451
6475
  };
6452
6476
  }
6453
6477
  function filesOf(all, ref) {
6454
- const id = (0, import_core30.parameterId)(ref);
6455
- return all.filter((file) => (0, import_core30.parameterId)(file) === id);
6478
+ const id = (0, import_core31.parameterId)(ref);
6479
+ return all.filter((file) => (0, import_core31.parameterId)(file) === id);
6456
6480
  }
6457
6481
  async function runMove(options) {
6458
6482
  const project = openProject(options.cwd);
6459
6483
  const from = refFromKey(options.from, project.config);
6460
6484
  assertWritableKey(options.to);
6461
6485
  const to = refFromKey(options.to, project.config);
6462
- if ((0, import_core30.parameterId)(from) === (0, import_core30.parameterId)(to)) {
6463
- throw new import_core30.PenvError(
6486
+ if ((0, import_core31.parameterId)(from) === (0, import_core31.parameterId)(to)) {
6487
+ throw new import_core31.PenvError(
6464
6488
  "PARAMETER_UNCHANGED",
6465
6489
  `\`${options.from}\` and \`${options.to}\` are the same parameter`,
6466
6490
  "Name a different destination, e.g. `penv mv redis-password redis/password`."
@@ -6470,24 +6494,24 @@ async function runMove(options) {
6470
6494
  const sources = filesOf(all, from);
6471
6495
  const meta = await project.provider.readMeta(from);
6472
6496
  if (sources.length === 0 && meta === void 0) {
6473
- throw new import_core30.PenvError(
6497
+ throw new import_core31.PenvError(
6474
6498
  "PARAMETER_ABSENT",
6475
- `Parameter ${(0, import_core30.parameterId)(from)} has no value files and no meta, so there is nothing to move`,
6499
+ `Parameter ${(0, import_core31.parameterId)(from)} has no value files and no meta, so there is nothing to move`,
6476
6500
  `\`penv list\` shows every parameter penv holds.`
6477
6501
  );
6478
6502
  }
6479
6503
  const occupied2 = filesOf(all, to);
6480
6504
  if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
6481
- throw new import_core30.PenvError(
6505
+ throw new import_core31.PenvError(
6482
6506
  "PARAMETER_EXISTS",
6483
- `Parameter ${(0, import_core30.parameterId)(to)} already exists, and penv will not merge two parameters into one`,
6484
- `Remove or rename ${(0, import_core30.parameterId)(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
6507
+ `Parameter ${(0, import_core31.parameterId)(to)} already exists, and penv will not merge two parameters into one`,
6508
+ `Remove or rename ${(0, import_core31.parameterId)(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
6485
6509
  );
6486
6510
  }
6487
6511
  const planned = [];
6488
6512
  for (const source of sources) {
6489
6513
  const target = { ...source, namespace: to.namespace, name: to.name };
6490
- const one = await planFile(project, source, target, (0, import_core30.parameterId)(to));
6514
+ const one = await planFile(project, source, target, (0, import_core31.parameterId)(to));
6491
6515
  if (one !== void 0) {
6492
6516
  planned.push(one);
6493
6517
  }
@@ -6505,18 +6529,18 @@ async function runMove(options) {
6505
6529
  await project.provider.removeMeta(from);
6506
6530
  }
6507
6531
  return {
6508
- from: (0, import_core30.parameterId)(from),
6509
- to: (0, import_core30.parameterId)(to),
6532
+ from: (0, import_core31.parameterId)(from),
6533
+ to: (0, import_core31.parameterId)(to),
6510
6534
  files: planned.map((file) => ({
6511
- from: (0, import_core30.formatValueFile)(file.source),
6512
- to: (0, import_core30.formatValueFile)(file.target),
6535
+ from: (0, import_core31.formatValueFile)(file.source),
6536
+ to: (0, import_core31.formatValueFile)(file.target),
6513
6537
  resealed: file.resealed
6514
6538
  })),
6515
6539
  // The meta's path, not the parameter's dotted id: `redis.password` is what
6516
6540
  // the schema calls it and `redis/password.json` is the file, and a report
6517
6541
  // that printed the first while moving the second names no file on disk.
6518
- meta: meta === void 0 ? void 0 : (0, import_core30.formatMetaFile)({ ...to, format: "json" }),
6519
- schema: { was: (0, import_core30.accessPath)(from).join("."), now: (0, import_core30.accessPath)(to).join(".") },
6542
+ meta: meta === void 0 ? void 0 : (0, import_core31.formatMetaFile)({ ...to, format: "json" }),
6543
+ schema: { was: (0, import_core31.accessPath)(from).join("."), now: (0, import_core31.accessPath)(to).join(".") },
6520
6544
  schemaFile: schemaShapeFileOf(project)
6521
6545
  };
6522
6546
  }
@@ -6524,11 +6548,11 @@ function renderMove(result2) {
6524
6548
  const rows = result2.files.map((file) => ({
6525
6549
  glyph: CHECK,
6526
6550
  label: "Moved",
6527
- subject: (0, import_core30.recordPath)(file.to),
6551
+ subject: (0, import_core31.recordPath)(file.to),
6528
6552
  ...file.resealed ? { detail: "re-sealed for its new address" } : {}
6529
6553
  }));
6530
6554
  if (result2.meta !== void 0) {
6531
- rows.push({ glyph: CHECK, label: "Moved", subject: (0, import_core30.recordPath)(result2.meta) });
6555
+ rows.push({ glyph: CHECK, label: "Moved", subject: (0, import_core31.recordPath)(result2.meta) });
6532
6556
  }
6533
6557
  const lines = formatRows(rows);
6534
6558
  lines.push(
@@ -6561,7 +6585,7 @@ var mvCommand = (0, import_citty18.defineCommand)({
6561
6585
  });
6562
6586
 
6563
6587
  // src/commands/remove.ts
6564
- var import_core31 = require("@penvhq/core");
6588
+ var import_core32 = require("@penvhq/core");
6565
6589
  var import_citty19 = require("citty");
6566
6590
  async function runRemove(options) {
6567
6591
  const project = openProject(options.cwd);
@@ -6579,12 +6603,12 @@ async function runRemove(options) {
6579
6603
  continue;
6580
6604
  }
6581
6605
  await project.provider.remove(file);
6582
- removed.push((0, import_core31.formatValueFile)(file));
6606
+ removed.push((0, import_core32.formatValueFile)(file));
6583
6607
  }
6584
6608
  return {
6585
6609
  parameter: options.key,
6586
6610
  removed,
6587
- considered: files.map((file) => (0, import_core31.formatValueFile)(file))
6611
+ considered: files.map((file) => (0, import_core32.formatValueFile)(file))
6588
6612
  };
6589
6613
  }
6590
6614
  function renderRemove(result2) {
@@ -6594,7 +6618,7 @@ function renderRemove(result2) {
6594
6618
  {
6595
6619
  glyph: WARN,
6596
6620
  label: "Nothing to remove",
6597
- subject: (0, import_core31.recordPath)(first),
6621
+ subject: (0, import_core32.recordPath)(first),
6598
6622
  detail: "no value file at that scope"
6599
6623
  }
6600
6624
  ]);
@@ -6602,7 +6626,7 @@ function renderRemove(result2) {
6602
6626
  const rows = result2.removed.map((location) => ({
6603
6627
  glyph: CHECK,
6604
6628
  label: "Removed",
6605
- subject: (0, import_core31.recordPath)(location)
6629
+ subject: (0, import_core32.recordPath)(location)
6606
6630
  }));
6607
6631
  return formatRows(rows);
6608
6632
  }
@@ -6633,7 +6657,7 @@ var removeCommand = (0, import_citty19.defineCommand)({
6633
6657
  });
6634
6658
 
6635
6659
  // src/commands/rotate.ts
6636
- var import_core32 = require("@penvhq/core");
6660
+ var import_core33 = require("@penvhq/core");
6637
6661
  var import_citty20 = require("citty");
6638
6662
  function rotatingFile(ref, environment) {
6639
6663
  return {
@@ -6659,7 +6683,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
6659
6683
  }
6660
6684
  function requireNewValue(value, phase, key) {
6661
6685
  if (value === void 0) {
6662
- throw new import_core32.PenvError(
6686
+ throw new import_core33.PenvError(
6663
6687
  "ROTATION_NO_VALUE",
6664
6688
  `A ${phase} rotation of ${key} writes a new value, and none was given`,
6665
6689
  "Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
@@ -6668,8 +6692,8 @@ function requireNewValue(value, phase, key) {
6668
6692
  return value;
6669
6693
  }
6670
6694
  function requireRetaining(provider, environment) {
6671
- if (!(0, import_core32.retainsPrevious)(provider)) {
6672
- throw new import_core32.PenvError(
6695
+ if (!(0, import_core33.retainsPrevious)(provider)) {
6696
+ throw new import_core33.PenvError(
6673
6697
  "ROTATION_NOT_RETAINING",
6674
6698
  `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`,
6675
6699
  "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."
@@ -6682,8 +6706,8 @@ async function runRotate(options) {
6682
6706
  const environment = targetEnvironment(project, options.environment);
6683
6707
  const ref = refFromKey(options.key, project.config);
6684
6708
  const source = await sourceProviderFor(project, environment);
6685
- if (!(0, import_core32.holdsRecords)(source)) {
6686
- throw new import_core32.PenvError(
6709
+ if (!(0, import_core33.holdsRecords)(source)) {
6710
+ throw new import_core33.PenvError(
6687
6711
  "ROTATION_NOT_RECORDS",
6688
6712
  `Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
6689
6713
  "Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
@@ -6692,9 +6716,9 @@ async function runRotate(options) {
6692
6716
  const provider = source;
6693
6717
  const nowIso = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
6694
6718
  const before = await provider.readMeta(ref);
6695
- const { mechanism } = (0, import_core32.rotationOf)(before, environment);
6719
+ const { mechanism } = (0, import_core33.rotationOf)(before, environment);
6696
6720
  if (mechanism === void 0) {
6697
- throw new import_core32.PenvError(
6721
+ throw new import_core33.PenvError(
6698
6722
  "ROTATION_NO_MECHANISM",
6699
6723
  `Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
6700
6724
  '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.'
@@ -6704,7 +6728,7 @@ async function runRotate(options) {
6704
6728
  const complete = options.complete === true;
6705
6729
  if (mechanism === "atomic-cutover") {
6706
6730
  if (begin || complete) {
6707
- throw new import_core32.PenvError(
6731
+ throw new import_core33.PenvError(
6708
6732
  "ROTATION_MECHANISM_MISMATCH",
6709
6733
  `Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
6710
6734
  "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."
@@ -6712,13 +6736,13 @@ async function runRotate(options) {
6712
6736
  }
6713
6737
  const value = requireNewValue(options.value, "cutover", options.key);
6714
6738
  await writeRotatedValue(project, provider, ref, environment, value);
6715
- const after2 = (0, import_core32.completeRotation)(before, environment, nowIso);
6739
+ const after2 = (0, import_core33.completeRotation)(before, environment, nowIso);
6716
6740
  await provider.writeMeta(ref, after2);
6717
6741
  return result(ref, environment, mechanism, "cutover", provider.type, true, after2);
6718
6742
  }
6719
6743
  const retaining = requireRetaining(provider, environment);
6720
6744
  if (begin === complete) {
6721
- throw new import_core32.PenvError(
6745
+ throw new import_core33.PenvError(
6722
6746
  "ROTATION_PHASE_REQUIRED",
6723
6747
  `A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
6724
6748
  "`--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."
@@ -6727,16 +6751,16 @@ async function runRotate(options) {
6727
6751
  if (begin) {
6728
6752
  const value = requireNewValue(options.value, "begin", options.key);
6729
6753
  await writeRotatedValue(project, retaining, ref, environment, value);
6730
- const after2 = (0, import_core32.beginRotation)(before, environment, nowIso);
6754
+ const after2 = (0, import_core33.beginRotation)(before, environment, nowIso);
6731
6755
  await retaining.writeMeta(ref, after2);
6732
6756
  return result(ref, environment, mechanism, "begin", retaining.type, true, after2);
6733
6757
  }
6734
- const after = (0, import_core32.completeRotation)(before, environment, nowIso);
6758
+ const after = (0, import_core33.completeRotation)(before, environment, nowIso);
6735
6759
  await retaining.writeMeta(ref, after);
6736
6760
  return result(ref, environment, mechanism, "complete", retaining.type, false, after);
6737
6761
  }
6738
6762
  function result(ref, environment, mechanism, phase, source, wroteValue, after) {
6739
- const { state, rotatingSince, lastRotated } = (0, import_core32.rotationOf)(after, environment);
6763
+ const { state, rotatingSince, lastRotated } = (0, import_core33.rotationOf)(after, environment);
6740
6764
  return {
6741
6765
  parameter: [...ref.namespace, ref.name].join("/"),
6742
6766
  environment,
@@ -6823,7 +6847,7 @@ var rotateCommand = (0, import_citty20.defineCommand)({
6823
6847
  // src/commands/watch.ts
6824
6848
  var import_node_fs14 = require("fs");
6825
6849
  var import_node_path15 = require("path");
6826
- var import_core33 = require("@penvhq/core");
6850
+ var import_core34 = require("@penvhq/core");
6827
6851
  var import_citty21 = require("citty");
6828
6852
  var DEBOUNCE_MS2 = 100;
6829
6853
  function runWatch(options) {
@@ -6956,9 +6980,9 @@ function runWatch(options) {
6956
6980
  }
6957
6981
  addWatcher(project.recordsDir, true);
6958
6982
  addWatcher((0, import_node_path15.dirname)(project.configFile), false, configFile);
6959
- addWatcher(project.root, false, import_core33.SCHEMA_SHAPE_FILE);
6960
- if ((0, import_core33.schemaInsideTree)(project.config) === void 0) {
6961
- const schemaFile = (0, import_node_path15.resolve)(project.root, (0, import_core33.schemaFileOf)(project.config));
6983
+ addWatcher(project.root, false, import_core34.SCHEMA_SHAPE_FILE);
6984
+ if ((0, import_core34.schemaInsideTree)(project.config) === void 0) {
6985
+ const schemaFile = (0, import_node_path15.resolve)(project.root, (0, import_core34.schemaFileOf)(project.config));
6962
6986
  addWatcher((0, import_node_path15.dirname)(schemaFile), false, (0, import_node_path15.basename)(schemaFile));
6963
6987
  }
6964
6988
  void validate();
@@ -7042,10 +7066,13 @@ var watchCommand = (0, import_citty21.defineCommand)({
7042
7066
 
7043
7067
  // src/index.ts
7044
7068
  var main = (0, import_citty22.defineCommand)({
7045
- meta: {
7069
+ // Lazy, so reading the engine's own version off disk is something `--version`
7070
+ // does rather than something importing this module does.
7071
+ meta: () => ({
7046
7072
  name: "penv",
7047
- description: "Configuration that shares a data model with your production secret manager"
7048
- },
7073
+ description: "Configuration that shares a data model with your production secret manager",
7074
+ version: engineVersion()
7075
+ }),
7049
7076
  subCommands: {
7050
7077
  init: initCommand,
7051
7078
  import: importCommand,
@@ -7072,7 +7099,7 @@ var main = (0, import_citty22.defineCommand)({
7072
7099
  }
7073
7100
  });
7074
7101
  function runMain() {
7075
- (0, import_core34.setKeychain)(defaultKeychain);
7102
+ (0, import_core35.setKeychain)(defaultKeychain);
7076
7103
  return (0, import_citty22.runMain)(main);
7077
7104
  }
7078
7105
  // Annotate the CommonJS export names for ESM import in node: