@clef-sh/cli 0.1.8 → 0.1.9-beta.57

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
@@ -967,7 +967,7 @@ var require_command = __commonJS({
967
967
  var EventEmitter = require("node:events").EventEmitter;
968
968
  var childProcess = require("node:child_process");
969
969
  var path44 = require("node:path");
970
- var fs27 = require("node:fs");
970
+ var fs26 = require("node:fs");
971
971
  var process2 = require("node:process");
972
972
  var { Argument: Argument2, humanReadableArgName } = require_argument();
973
973
  var { CommanderError: CommanderError2 } = require_error();
@@ -1900,10 +1900,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1900
1900
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1901
1901
  function findFile(baseDir, baseName) {
1902
1902
  const localBin = path44.resolve(baseDir, baseName);
1903
- if (fs27.existsSync(localBin)) return localBin;
1903
+ if (fs26.existsSync(localBin)) return localBin;
1904
1904
  if (sourceExt.includes(path44.extname(baseName))) return void 0;
1905
1905
  const foundExt = sourceExt.find(
1906
- (ext) => fs27.existsSync(`${localBin}${ext}`)
1906
+ (ext) => fs26.existsSync(`${localBin}${ext}`)
1907
1907
  );
1908
1908
  if (foundExt) return `${localBin}${foundExt}`;
1909
1909
  return void 0;
@@ -1915,7 +1915,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1915
1915
  if (this._scriptPath) {
1916
1916
  let resolvedScriptPath;
1917
1917
  try {
1918
- resolvedScriptPath = fs27.realpathSync(this._scriptPath);
1918
+ resolvedScriptPath = fs26.realpathSync(this._scriptPath);
1919
1919
  } catch (err) {
1920
1920
  resolvedScriptPath = this._scriptPath;
1921
1921
  }
@@ -9866,14 +9866,14 @@ var require_parser = __commonJS({
9866
9866
  case "scalar":
9867
9867
  case "single-quoted-scalar":
9868
9868
  case "double-quoted-scalar": {
9869
- const fs27 = this.flowScalar(this.type);
9869
+ const fs26 = this.flowScalar(this.type);
9870
9870
  if (atNextItem || it.value) {
9871
- map.items.push({ start, key: fs27, sep: [] });
9871
+ map.items.push({ start, key: fs26, sep: [] });
9872
9872
  this.onKeyLine = true;
9873
9873
  } else if (it.sep) {
9874
- this.stack.push(fs27);
9874
+ this.stack.push(fs26);
9875
9875
  } else {
9876
- Object.assign(it, { key: fs27, sep: [] });
9876
+ Object.assign(it, { key: fs26, sep: [] });
9877
9877
  this.onKeyLine = true;
9878
9878
  }
9879
9879
  return;
@@ -10001,13 +10001,13 @@ var require_parser = __commonJS({
10001
10001
  case "scalar":
10002
10002
  case "single-quoted-scalar":
10003
10003
  case "double-quoted-scalar": {
10004
- const fs27 = this.flowScalar(this.type);
10004
+ const fs26 = this.flowScalar(this.type);
10005
10005
  if (!it || it.value)
10006
- fc.items.push({ start: [], key: fs27, sep: [] });
10006
+ fc.items.push({ start: [], key: fs26, sep: [] });
10007
10007
  else if (it.sep)
10008
- this.stack.push(fs27);
10008
+ this.stack.push(fs26);
10009
10009
  else
10010
- Object.assign(it, { key: fs27, sep: [] });
10010
+ Object.assign(it, { key: fs26, sep: [] });
10011
10011
  return;
10012
10012
  }
10013
10013
  case "flow-map-end":
@@ -10215,7 +10215,7 @@ var require_public_api = __commonJS({
10215
10215
  }
10216
10216
  return doc;
10217
10217
  }
10218
- function parse16(src, reviver, options) {
10218
+ function parse15(src, reviver, options) {
10219
10219
  let _reviver = void 0;
10220
10220
  if (typeof reviver === "function") {
10221
10221
  _reviver = reviver;
@@ -10256,7 +10256,7 @@ var require_public_api = __commonJS({
10256
10256
  return value.toString(options);
10257
10257
  return new Document.Document(value, _replacer, options).toString(options);
10258
10258
  }
10259
- exports2.parse = parse16;
10259
+ exports2.parse = parse15;
10260
10260
  exports2.parseAllDocuments = parseAllDocuments;
10261
10261
  exports2.parseDocument = parseDocument;
10262
10262
  exports2.stringify = stringify7;
@@ -10737,6 +10737,12 @@ var init_parser = __esm({
10737
10737
  "namespaces"
10738
10738
  );
10739
10739
  }
10740
+ if (!ENV_NAME_PATTERN.test(nsObj.name)) {
10741
+ throw new ManifestValidationError(
10742
+ `Namespace name '${nsObj.name}' is invalid. Names must start with a lowercase letter and contain only lowercase letters, digits, hyphens, and underscores.`,
10743
+ "namespaces"
10744
+ );
10745
+ }
10740
10746
  if (!nsObj.description || typeof nsObj.description !== "string") {
10741
10747
  throw new ManifestValidationError(
10742
10748
  `Namespace '${nsObj.name}' is missing a 'description' string.`,
@@ -10837,9 +10843,9 @@ var init_parser = __esm({
10837
10843
  );
10838
10844
  }
10839
10845
  const siName = siObj.name;
10840
- if (!siObj.description || typeof siObj.description !== "string") {
10846
+ if (siObj.description != null && typeof siObj.description !== "string") {
10841
10847
  throw new ManifestValidationError(
10842
- `Service identity '${siName}' is missing a 'description' string.`,
10848
+ `Service identity '${siName}' has a non-string 'description'.`,
10843
10849
  "service_identities"
10844
10850
  );
10845
10851
  }
@@ -10954,7 +10960,7 @@ var init_parser = __esm({
10954
10960
  }
10955
10961
  return {
10956
10962
  name: siName,
10957
- description: siObj.description,
10963
+ description: siObj.description ?? "",
10958
10964
  namespaces: siObj.namespaces,
10959
10965
  environments: parsedEnvs
10960
10966
  };
@@ -11155,7 +11161,11 @@ var init_scanner = __esm({
11155
11161
  init_patterns();
11156
11162
  init_ignore();
11157
11163
  ALWAYS_SKIP_EXTENSIONS = [".enc.yaml", ".enc.json"];
11158
- ALWAYS_SKIP_NAMES = [".clef-meta.yaml"];
11164
+ ALWAYS_SKIP_NAMES = [
11165
+ ".clef-meta.yaml",
11166
+ ".sops.yaml"
11167
+ // contains age public keys and KMS ARNs — configuration, not secrets
11168
+ ];
11159
11169
  ALWAYS_SKIP_DIRS = ["node_modules", ".git"];
11160
11170
  MAX_FILE_SIZE = 1024 * 1024;
11161
11171
  ScanRunner = class {
@@ -11451,15 +11461,36 @@ var init_metadata = __esm({
11451
11461
  }
11452
11462
  });
11453
11463
 
11464
+ // ../core/src/sops/keys.ts
11465
+ function readSopsKeyNames(filePath) {
11466
+ try {
11467
+ const raw = fs5.readFileSync(filePath, "utf-8");
11468
+ const parsed = YAML3.parse(raw);
11469
+ if (parsed === null || parsed === void 0 || typeof parsed !== "object") return null;
11470
+ return Object.keys(parsed).filter((k) => k !== "sops");
11471
+ } catch {
11472
+ return null;
11473
+ }
11474
+ }
11475
+ var fs5, YAML3;
11476
+ var init_keys = __esm({
11477
+ "../core/src/sops/keys.ts"() {
11478
+ "use strict";
11479
+ fs5 = __toESM(require("fs"));
11480
+ YAML3 = __toESM(require_dist());
11481
+ }
11482
+ });
11483
+
11454
11484
  // ../core/src/matrix/manager.ts
11455
- var fs5, path4, YAML3, MatrixManager;
11485
+ var fs6, path4, YAML4, MatrixManager;
11456
11486
  var init_manager = __esm({
11457
11487
  "../core/src/matrix/manager.ts"() {
11458
11488
  "use strict";
11459
- fs5 = __toESM(require("fs"));
11489
+ fs6 = __toESM(require("fs"));
11460
11490
  path4 = __toESM(require("path"));
11461
- YAML3 = __toESM(require_dist());
11491
+ YAML4 = __toESM(require_dist());
11462
11492
  init_metadata();
11493
+ init_keys();
11463
11494
  MatrixManager = class {
11464
11495
  /**
11465
11496
  * Build the full grid of {@link MatrixCell} objects from the manifest.
@@ -11478,7 +11509,7 @@ var init_manager = __esm({
11478
11509
  namespace: ns.name,
11479
11510
  environment: env.name,
11480
11511
  filePath,
11481
- exists: fs5.existsSync(filePath)
11512
+ exists: fs6.existsSync(filePath)
11482
11513
  });
11483
11514
  }
11484
11515
  }
@@ -11502,8 +11533,8 @@ var init_manager = __esm({
11502
11533
  */
11503
11534
  async scaffoldCell(cell, sopsClient, manifest) {
11504
11535
  const dir = path4.dirname(cell.filePath);
11505
- if (!fs5.existsSync(dir)) {
11506
- fs5.mkdirSync(dir, { recursive: true });
11536
+ if (!fs6.existsSync(dir)) {
11537
+ fs6.mkdirSync(dir, { recursive: true });
11507
11538
  }
11508
11539
  await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);
11509
11540
  }
@@ -11572,22 +11603,15 @@ var init_manager = __esm({
11572
11603
  * SOPS stores key names in plaintext — only values are encrypted.
11573
11604
  */
11574
11605
  readKeyNames(filePath) {
11575
- try {
11576
- const raw = fs5.readFileSync(filePath, "utf-8");
11577
- const parsed = YAML3.parse(raw);
11578
- if (!parsed || typeof parsed !== "object") return [];
11579
- return Object.keys(parsed).filter((k) => k !== "sops");
11580
- } catch {
11581
- return [];
11582
- }
11606
+ return readSopsKeyNames(filePath) ?? [];
11583
11607
  }
11584
11608
  /**
11585
11609
  * Read the lastModified timestamp from SOPS metadata without decryption.
11586
11610
  */
11587
11611
  readLastModified(filePath) {
11588
11612
  try {
11589
- const raw = fs5.readFileSync(filePath, "utf-8");
11590
- const parsed = YAML3.parse(raw);
11613
+ const raw = fs6.readFileSync(filePath, "utf-8");
11614
+ const parsed = YAML4.parse(raw);
11591
11615
  const sops = parsed?.sops;
11592
11616
  if (sops?.lastmodified) return new Date(String(sops.lastmodified));
11593
11617
  return null;
@@ -11610,12 +11634,12 @@ var init_manager = __esm({
11610
11634
  });
11611
11635
 
11612
11636
  // ../core/src/schema/validator.ts
11613
- var fs6, YAML4, SchemaValidator;
11637
+ var fs7, YAML5, SchemaValidator;
11614
11638
  var init_validator2 = __esm({
11615
11639
  "../core/src/schema/validator.ts"() {
11616
11640
  "use strict";
11617
- fs6 = __toESM(require("fs"));
11618
- YAML4 = __toESM(require_dist());
11641
+ fs7 = __toESM(require("fs"));
11642
+ YAML5 = __toESM(require_dist());
11619
11643
  init_types();
11620
11644
  SchemaValidator = class {
11621
11645
  /**
@@ -11628,13 +11652,13 @@ var init_validator2 = __esm({
11628
11652
  loadSchema(filePath) {
11629
11653
  let raw;
11630
11654
  try {
11631
- raw = fs6.readFileSync(filePath, "utf-8");
11655
+ raw = fs7.readFileSync(filePath, "utf-8");
11632
11656
  } catch {
11633
11657
  throw new SchemaLoadError(`Could not read schema file at '${filePath}'.`, filePath);
11634
11658
  }
11635
11659
  let parsed;
11636
11660
  try {
11637
- parsed = YAML4.parse(raw);
11661
+ parsed = YAML5.parse(raw);
11638
11662
  } catch {
11639
11663
  throw new SchemaLoadError(`Schema file '${filePath}' contains invalid YAML.`, filePath);
11640
11664
  }
@@ -11949,11 +11973,11 @@ ${details}`
11949
11973
  });
11950
11974
 
11951
11975
  // ../core/src/git/integration.ts
11952
- var fs7, path7, PRE_COMMIT_HOOK, GitIntegration;
11976
+ var fs8, path7, PRE_COMMIT_HOOK, GitIntegration;
11953
11977
  var init_integration = __esm({
11954
11978
  "../core/src/git/integration.ts"() {
11955
11979
  "use strict";
11956
- fs7 = __toESM(require("fs"));
11980
+ fs8 = __toESM(require("fs"));
11957
11981
  path7 = __toESM(require("path"));
11958
11982
  init_types();
11959
11983
  PRE_COMMIT_HOOK = `#!/bin/sh
@@ -12163,14 +12187,14 @@ exit $EXIT_CODE
12163
12187
  });
12164
12188
  const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;
12165
12189
  const attrFilePath = path7.join(repoRoot, ".gitattributes");
12166
- const attrContent = fs7.existsSync(attrFilePath) ? fs7.readFileSync(attrFilePath, "utf-8") : "";
12190
+ const attrContent = fs8.existsSync(attrFilePath) ? fs8.readFileSync(attrFilePath, "utf-8") : "";
12167
12191
  const gitattributes = attrContent.includes("merge=sops");
12168
12192
  return { gitConfig, gitattributes };
12169
12193
  }
12170
12194
  async ensureGitattributes(repoRoot) {
12171
12195
  const attrPath = path7.join(repoRoot, ".gitattributes");
12172
12196
  const mergeRule = "*.enc.yaml merge=sops\n*.enc.json merge=sops";
12173
- const existing = fs7.existsSync(attrPath) ? fs7.readFileSync(attrPath, "utf-8") : "";
12197
+ const existing = fs8.existsSync(attrPath) ? fs8.readFileSync(attrPath, "utf-8") : "";
12174
12198
  if (existing.includes("merge=sops")) {
12175
12199
  return;
12176
12200
  }
@@ -12233,16 +12257,16 @@ function tryBundled() {
12233
12257
  const packageMain = require.resolve(`${packageName}/package.json`);
12234
12258
  const packageDir = path8.dirname(packageMain);
12235
12259
  const binPath = path8.join(packageDir, "bin", binName);
12236
- return fs8.existsSync(binPath) ? binPath : null;
12260
+ return fs9.existsSync(binPath) ? binPath : null;
12237
12261
  } catch {
12238
12262
  return null;
12239
12263
  }
12240
12264
  }
12241
- var fs8, path8;
12265
+ var fs9, path8;
12242
12266
  var init_bundled = __esm({
12243
12267
  "../core/src/sops/bundled.ts"() {
12244
12268
  "use strict";
12245
- fs8 = __toESM(require("fs"));
12269
+ fs9 = __toESM(require("fs"));
12246
12270
  path8 = __toESM(require("path"));
12247
12271
  }
12248
12272
  });
@@ -12264,7 +12288,7 @@ function resolveSopsPath() {
12264
12288
  const envPath = process.env.CLEF_SOPS_PATH?.trim();
12265
12289
  if (envPath) {
12266
12290
  validateSopsPath(envPath);
12267
- if (!fs9.existsSync(envPath)) {
12291
+ if (!fs10.existsSync(envPath)) {
12268
12292
  throw new Error(`CLEF_SOPS_PATH points to '${envPath}' but the file does not exist.`);
12269
12293
  }
12270
12294
  cached = { path: envPath, source: "env" };
@@ -12281,11 +12305,11 @@ function resolveSopsPath() {
12281
12305
  function resetSopsResolution() {
12282
12306
  cached = void 0;
12283
12307
  }
12284
- var fs9, path9, cached;
12308
+ var fs10, path9, cached;
12285
12309
  var init_resolver = __esm({
12286
12310
  "../core/src/sops/resolver.ts"() {
12287
12311
  "use strict";
12288
- fs9 = __toESM(require("fs"));
12312
+ fs10 = __toESM(require("fs"));
12289
12313
  path9 = __toESM(require("path"));
12290
12314
  init_bundled();
12291
12315
  }
@@ -19314,14 +19338,14 @@ function openWindowsInputPipe(content) {
19314
19338
  });
19315
19339
  });
19316
19340
  }
19317
- var fs10, net, import_crypto, YAML5, SopsClient;
19341
+ var fs11, net, import_crypto, YAML6, SopsClient;
19318
19342
  var init_client = __esm({
19319
19343
  "../core/src/sops/client.ts"() {
19320
19344
  "use strict";
19321
- fs10 = __toESM(require("fs"));
19345
+ fs11 = __toESM(require("fs"));
19322
19346
  net = __toESM(require("net"));
19323
19347
  import_crypto = require("crypto");
19324
- YAML5 = __toESM(require_dist());
19348
+ YAML6 = __toESM(require_dist());
19325
19349
  init_types();
19326
19350
  init_checker();
19327
19351
  init_keygen();
@@ -19386,7 +19410,7 @@ var init_client = __esm({
19386
19410
  }
19387
19411
  let parsed;
19388
19412
  try {
19389
- parsed = YAML5.parse(result.stdout) ?? {};
19413
+ parsed = YAML6.parse(result.stdout) ?? {};
19390
19414
  } catch {
19391
19415
  throw new SopsDecryptionError(
19392
19416
  `Decrypted content of '${filePath}' is not valid YAML.`,
@@ -19413,7 +19437,7 @@ var init_client = __esm({
19413
19437
  async encrypt(filePath, values, manifest, environment) {
19414
19438
  await assertSops(this.runner, this.sopsCommand);
19415
19439
  const fmt = formatFromPath(filePath);
19416
- const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML5.stringify(values);
19440
+ const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML6.stringify(values);
19417
19441
  const args = this.buildEncryptArgs(filePath, manifest, environment);
19418
19442
  const env = this.buildSopsEnv();
19419
19443
  let inputArg;
@@ -19457,7 +19481,7 @@ var init_client = __esm({
19457
19481
  );
19458
19482
  }
19459
19483
  try {
19460
- fs10.writeFileSync(filePath, result.stdout);
19484
+ fs11.writeFileSync(filePath, result.stdout);
19461
19485
  } catch {
19462
19486
  throw new SopsEncryptionError(`Failed to write encrypted data to '${filePath}'.`, filePath);
19463
19487
  }
@@ -19470,21 +19494,7 @@ var init_client = __esm({
19470
19494
  * @throws {@link SopsEncryptionError} On failure.
19471
19495
  */
19472
19496
  async reEncrypt(filePath, newKey) {
19473
- await assertSops(this.runner, this.sopsCommand);
19474
- const env = this.buildSopsEnv();
19475
- const result = await this.runner.run(
19476
- this.sopsCommand,
19477
- ["rotate", "-i", "--add-age", newKey, filePath],
19478
- {
19479
- ...env ? { env } : {}
19480
- }
19481
- );
19482
- if (result.exitCode !== 0) {
19483
- throw new SopsEncryptionError(
19484
- `Failed to re-encrypt '${filePath}': ${result.stderr.trim()}`,
19485
- filePath
19486
- );
19487
- }
19497
+ await this.addRecipient(filePath, newKey);
19488
19498
  }
19489
19499
  /**
19490
19500
  * Add an age recipient to an existing SOPS file.
@@ -19587,7 +19597,7 @@ var init_client = __esm({
19587
19597
  if (!this.ageKey && !this.ageKeyFile) return "key-not-found";
19588
19598
  let keyContent;
19589
19599
  try {
19590
- keyContent = this.ageKey ?? fs10.readFileSync(this.ageKeyFile, "utf-8");
19600
+ keyContent = this.ageKey ?? fs11.readFileSync(this.ageKeyFile, "utf-8");
19591
19601
  } catch {
19592
19602
  return "key-not-found";
19593
19603
  }
@@ -19604,7 +19614,7 @@ var init_client = __esm({
19604
19614
  parseMetadataFromFile(filePath) {
19605
19615
  let content;
19606
19616
  try {
19607
- content = fs10.readFileSync(filePath, "utf-8");
19617
+ content = fs11.readFileSync(filePath, "utf-8");
19608
19618
  } catch {
19609
19619
  throw new SopsDecryptionError(
19610
19620
  `Could not read file '${filePath}' to extract SOPS metadata.`,
@@ -19613,7 +19623,7 @@ var init_client = __esm({
19613
19623
  }
19614
19624
  let parsed;
19615
19625
  try {
19616
- parsed = YAML5.parse(content);
19626
+ parsed = YAML6.parse(content);
19617
19627
  } catch {
19618
19628
  throw new SopsDecryptionError(
19619
19629
  `File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,
@@ -20080,7 +20090,7 @@ function detectFormat(filePath, content) {
20080
20090
  } catch {
20081
20091
  }
20082
20092
  try {
20083
- const parsed = YAML6.parse(content);
20093
+ const parsed = YAML7.parse(content);
20084
20094
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
20085
20095
  return "yaml";
20086
20096
  }
@@ -20161,7 +20171,7 @@ function parseJson(content) {
20161
20171
  function parseYaml(content) {
20162
20172
  let parsed;
20163
20173
  try {
20164
- parsed = YAML6.parse(content);
20174
+ parsed = YAML7.parse(content);
20165
20175
  } catch (err) {
20166
20176
  throw new Error(`Invalid YAML: ${err.message}`);
20167
20177
  }
@@ -20195,7 +20205,7 @@ function parseYaml(content) {
20195
20205
  }
20196
20206
  return { pairs, format: "yaml", skipped, warnings };
20197
20207
  }
20198
- function parse7(content, format, filePath) {
20208
+ function parse8(content, format, filePath) {
20199
20209
  const resolved = format === "auto" ? detectFormat(filePath ?? "", content) : format;
20200
20210
  switch (resolved) {
20201
20211
  case "dotenv":
@@ -20206,12 +20216,12 @@ function parse7(content, format, filePath) {
20206
20216
  return parseYaml(content);
20207
20217
  }
20208
20218
  }
20209
- var path11, YAML6;
20219
+ var path11, YAML7;
20210
20220
  var init_parsers = __esm({
20211
20221
  "../core/src/import/parsers.ts"() {
20212
20222
  "use strict";
20213
20223
  path11 = __toESM(require("path"));
20214
- YAML6 = __toESM(require_dist());
20224
+ YAML7 = __toESM(require_dist());
20215
20225
  }
20216
20226
  });
20217
20227
 
@@ -20242,7 +20252,7 @@ var init_import = __esm({
20242
20252
  repoRoot,
20243
20253
  manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env)
20244
20254
  );
20245
- const parsed = parse7(content, options.format ?? "auto", sourcePath ?? "");
20255
+ const parsed = parse8(content, options.format ?? "auto", sourcePath ?? "");
20246
20256
  let candidates = Object.entries(parsed.pairs);
20247
20257
  if (options.prefix) {
20248
20258
  const prefix = options.prefix;
@@ -20320,12 +20330,12 @@ function toRecipient(entry) {
20320
20330
  }
20321
20331
  function readManifestYaml(repoRoot) {
20322
20332
  const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
20323
- const raw = fs11.readFileSync(manifestPath, "utf-8");
20324
- return YAML7.parse(raw);
20333
+ const raw = fs12.readFileSync(manifestPath, "utf-8");
20334
+ return YAML8.parse(raw);
20325
20335
  }
20326
20336
  function writeManifestYaml(repoRoot, doc) {
20327
20337
  const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
20328
- fs11.writeFileSync(manifestPath, YAML7.stringify(doc), "utf-8");
20338
+ fs12.writeFileSync(manifestPath, YAML8.stringify(doc), "utf-8");
20329
20339
  }
20330
20340
  function getRecipientsArray(doc) {
20331
20341
  const sops = doc.sops;
@@ -20373,13 +20383,13 @@ function ensureEnvironmentRecipientsArray(doc, envName) {
20373
20383
  }
20374
20384
  return env.recipients;
20375
20385
  }
20376
- var fs11, path13, YAML7, RecipientManager;
20386
+ var fs12, path13, YAML8, RecipientManager;
20377
20387
  var init_recipients = __esm({
20378
20388
  "../core/src/recipients/index.ts"() {
20379
20389
  "use strict";
20380
- fs11 = __toESM(require("fs"));
20390
+ fs12 = __toESM(require("fs"));
20381
20391
  path13 = __toESM(require("path"));
20382
- YAML7 = __toESM(require_dist());
20392
+ YAML8 = __toESM(require_dist());
20383
20393
  init_validator();
20384
20394
  init_parser();
20385
20395
  RecipientManager = class {
@@ -20435,7 +20445,7 @@ var init_recipients = __esm({
20435
20445
  throw new Error(`Recipient '${keyPreview(normalizedKey)}' is already present.`);
20436
20446
  }
20437
20447
  const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
20438
- const manifestBackup = fs11.readFileSync(manifestPath, "utf-8");
20448
+ const manifestBackup = fs12.readFileSync(manifestPath, "utf-8");
20439
20449
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
20440
20450
  if (label) {
20441
20451
  recipients.push({ key: normalizedKey, label });
@@ -20450,16 +20460,16 @@ var init_recipients = __esm({
20450
20460
  const fileBackups = /* @__PURE__ */ new Map();
20451
20461
  for (const cell of cells) {
20452
20462
  try {
20453
- fileBackups.set(cell.filePath, fs11.readFileSync(cell.filePath, "utf-8"));
20463
+ fileBackups.set(cell.filePath, fs12.readFileSync(cell.filePath, "utf-8"));
20454
20464
  await this.encryption.addRecipient(cell.filePath, normalizedKey);
20455
20465
  reEncryptedFiles.push(cell.filePath);
20456
20466
  } catch {
20457
20467
  failedFiles.push(cell.filePath);
20458
- fs11.writeFileSync(manifestPath, manifestBackup, "utf-8");
20468
+ fs12.writeFileSync(manifestPath, manifestBackup, "utf-8");
20459
20469
  for (const reEncryptedFile of reEncryptedFiles) {
20460
20470
  const backup = fileBackups.get(reEncryptedFile);
20461
20471
  if (backup) {
20462
- fs11.writeFileSync(reEncryptedFile, backup, "utf-8");
20472
+ fs12.writeFileSync(reEncryptedFile, backup, "utf-8");
20463
20473
  }
20464
20474
  }
20465
20475
  const restoredDoc = readManifestYaml(repoRoot);
@@ -20513,7 +20523,7 @@ var init_recipients = __esm({
20513
20523
  }
20514
20524
  const removedEntry = parsed[matchIndex];
20515
20525
  const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
20516
- const manifestBackup = fs11.readFileSync(manifestPath, "utf-8");
20526
+ const manifestBackup = fs12.readFileSync(manifestPath, "utf-8");
20517
20527
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
20518
20528
  recipients.splice(matchIndex, 1);
20519
20529
  writeManifestYaml(repoRoot, doc);
@@ -20524,16 +20534,16 @@ var init_recipients = __esm({
20524
20534
  const fileBackups = /* @__PURE__ */ new Map();
20525
20535
  for (const cell of cells) {
20526
20536
  try {
20527
- fileBackups.set(cell.filePath, fs11.readFileSync(cell.filePath, "utf-8"));
20537
+ fileBackups.set(cell.filePath, fs12.readFileSync(cell.filePath, "utf-8"));
20528
20538
  await this.encryption.removeRecipient(cell.filePath, trimmedKey);
20529
20539
  reEncryptedFiles.push(cell.filePath);
20530
20540
  } catch {
20531
20541
  failedFiles.push(cell.filePath);
20532
- fs11.writeFileSync(manifestPath, manifestBackup, "utf-8");
20542
+ fs12.writeFileSync(manifestPath, manifestBackup, "utf-8");
20533
20543
  for (const reEncryptedFile of reEncryptedFiles) {
20534
20544
  const backup = fileBackups.get(reEncryptedFile);
20535
20545
  if (backup) {
20536
- fs11.writeFileSync(reEncryptedFile, backup, "utf-8");
20546
+ fs12.writeFileSync(reEncryptedFile, backup, "utf-8");
20537
20547
  }
20538
20548
  }
20539
20549
  const restoredDoc = readManifestYaml(repoRoot);
@@ -20575,9 +20585,9 @@ function requestsFilePath(repoRoot) {
20575
20585
  function loadRequests(repoRoot) {
20576
20586
  const filePath = requestsFilePath(repoRoot);
20577
20587
  try {
20578
- if (!fs12.existsSync(filePath)) return [];
20579
- const content = fs12.readFileSync(filePath, "utf-8");
20580
- const parsed = YAML8.parse(content);
20588
+ if (!fs13.existsSync(filePath)) return [];
20589
+ const content = fs13.readFileSync(filePath, "utf-8");
20590
+ const parsed = YAML9.parse(content);
20581
20591
  if (!parsed || !Array.isArray(parsed.requests)) return [];
20582
20592
  return parsed.requests.map((r) => ({
20583
20593
  key: r.key,
@@ -20593,7 +20603,7 @@ function saveRequests(repoRoot, requests) {
20593
20603
  const filePath = requestsFilePath(repoRoot);
20594
20604
  if (requests.length === 0) {
20595
20605
  try {
20596
- fs12.unlinkSync(filePath);
20606
+ fs13.unlinkSync(filePath);
20597
20607
  } catch {
20598
20608
  }
20599
20609
  return;
@@ -20609,7 +20619,7 @@ function saveRequests(repoRoot, requests) {
20609
20619
  return raw;
20610
20620
  })
20611
20621
  };
20612
- fs12.writeFileSync(filePath, HEADER_COMMENT2 + YAML8.stringify(data), "utf-8");
20622
+ fs13.writeFileSync(filePath, HEADER_COMMENT2 + YAML9.stringify(data), "utf-8");
20613
20623
  }
20614
20624
  function upsertRequest(repoRoot, key, label, environment) {
20615
20625
  const requests = loadRequests(repoRoot);
@@ -20643,28 +20653,27 @@ function findInList(requests, identifier) {
20643
20653
  const byKey = requests.find((r) => r.key === identifier);
20644
20654
  return byKey ?? null;
20645
20655
  }
20646
- var fs12, path14, YAML8, REQUESTS_FILENAME, HEADER_COMMENT2;
20656
+ var fs13, path14, YAML9, REQUESTS_FILENAME, HEADER_COMMENT2;
20647
20657
  var init_requests = __esm({
20648
20658
  "../core/src/recipients/requests.ts"() {
20649
20659
  "use strict";
20650
- fs12 = __toESM(require("fs"));
20660
+ fs13 = __toESM(require("fs"));
20651
20661
  path14 = __toESM(require("path"));
20652
- YAML8 = __toESM(require_dist());
20662
+ YAML9 = __toESM(require_dist());
20653
20663
  REQUESTS_FILENAME = ".clef-requests.yaml";
20654
20664
  HEADER_COMMENT2 = "# Pending recipient access requests. Approve with: clef recipients approve <label>\n";
20655
20665
  }
20656
20666
  });
20657
20667
 
20658
20668
  // ../core/src/drift/detector.ts
20659
- var fs13, path15, YAML9, DriftDetector;
20669
+ var path15, DriftDetector;
20660
20670
  var init_detector = __esm({
20661
20671
  "../core/src/drift/detector.ts"() {
20662
20672
  "use strict";
20663
- fs13 = __toESM(require("fs"));
20664
20673
  path15 = __toESM(require("path"));
20665
- YAML9 = __toESM(require_dist());
20666
20674
  init_parser();
20667
20675
  init_manager();
20676
+ init_keys();
20668
20677
  DriftDetector = class {
20669
20678
  parser = new ManifestParser();
20670
20679
  matrix = new MatrixManager();
@@ -20692,45 +20701,30 @@ var init_detector = __esm({
20692
20701
  }
20693
20702
  const issues = [];
20694
20703
  let namespacesClean = 0;
20704
+ const remoteEnvSet = new Set(remoteEnvNames);
20705
+ const sharedEnvSet = new Set(localEnvNames.filter((e) => remoteEnvSet.has(e)));
20695
20706
  for (const ns of sharedNamespaces) {
20696
- const keyEnvs = /* @__PURE__ */ new Map();
20697
- const allEnvs = /* @__PURE__ */ new Set();
20698
- const localNsCells = localCells.filter((c) => c.namespace === ns);
20699
- for (const cell of localNsCells) {
20700
- const keys = this.readKeysFromFile(cell.filePath);
20701
- if (keys === null) continue;
20702
- allEnvs.add(cell.environment);
20703
- for (const key of keys) {
20704
- if (!keyEnvs.has(key)) keyEnvs.set(key, /* @__PURE__ */ new Set());
20705
- keyEnvs.get(key).add(cell.environment);
20706
- }
20707
- }
20708
- const remoteNsCells = remoteCells.filter((c) => c.namespace === ns);
20709
- for (const cell of remoteNsCells) {
20710
- const keys = this.readKeysFromFile(cell.filePath);
20711
- if (keys === null) continue;
20712
- allEnvs.add(cell.environment);
20713
- for (const key of keys) {
20714
- if (!keyEnvs.has(key)) keyEnvs.set(key, /* @__PURE__ */ new Set());
20715
- keyEnvs.get(key).add(cell.environment);
20716
- }
20717
- }
20718
- const envList = [...allEnvs];
20707
+ const localKeyEnvs = this.collectKeyEnvs(localCells, ns, sharedEnvSet);
20708
+ const remoteKeyEnvs = this.collectKeyEnvs(remoteCells, ns, sharedEnvSet);
20719
20709
  let nsClean = true;
20720
- for (const [key, envSet] of keyEnvs) {
20721
- const missingFrom = envList.filter((e) => !envSet.has(e));
20722
- if (missingFrom.length > 0) {
20723
- nsClean = false;
20724
- const presentIn = [...envSet].sort();
20725
- issues.push({
20726
- namespace: ns,
20727
- key,
20728
- presentIn,
20729
- missingFrom: missingFrom.sort(),
20730
- message: `Key '${key}' in namespace '${ns}' exists in [${presentIn.join(", ")}] but is missing from [${missingFrom.sort().join(", ")}]`
20731
- });
20710
+ const reportDrift = (sourceMap, targetMap, direction) => {
20711
+ for (const [key, sourceEnvs] of sourceMap) {
20712
+ const targetEnvs = targetMap.get(key);
20713
+ const missingFrom = [...sourceEnvs].filter((e) => !targetEnvs?.has(e)).sort();
20714
+ if (missingFrom.length > 0) {
20715
+ nsClean = false;
20716
+ issues.push({
20717
+ namespace: ns,
20718
+ key,
20719
+ presentIn: [...sourceEnvs].sort(),
20720
+ missingFrom,
20721
+ message: `Key '${key}' in namespace '${ns}' exists in ${direction} [${[...sourceEnvs].sort().join(", ")}] but is missing from [${missingFrom.join(", ")}]`
20722
+ });
20723
+ }
20732
20724
  }
20733
- }
20725
+ };
20726
+ reportDrift(remoteKeyEnvs, localKeyEnvs, "remote");
20727
+ reportDrift(localKeyEnvs, remoteKeyEnvs, "local");
20734
20728
  if (nsClean) namespacesClean++;
20735
20729
  }
20736
20730
  return {
@@ -20741,22 +20735,18 @@ var init_detector = __esm({
20741
20735
  remoteEnvironments: remoteEnvNames
20742
20736
  };
20743
20737
  }
20744
- /**
20745
- * Read top-level key names from an encrypted SOPS YAML file,
20746
- * filtering out the `sops` metadata key.
20747
- *
20748
- * @returns Array of key names, or `null` if the file cannot be read.
20749
- */
20750
- readKeysFromFile(filePath) {
20751
- try {
20752
- if (!fs13.existsSync(filePath)) return null;
20753
- const raw = fs13.readFileSync(filePath, "utf-8");
20754
- const parsed = YAML9.parse(raw);
20755
- if (parsed === null || parsed === void 0 || typeof parsed !== "object") return null;
20756
- return Object.keys(parsed).filter((k) => k !== "sops");
20757
- } catch {
20758
- return null;
20738
+ collectKeyEnvs(cells, ns, sharedEnvSet) {
20739
+ const keyEnvs = /* @__PURE__ */ new Map();
20740
+ for (const cell of cells) {
20741
+ if (cell.namespace !== ns || !sharedEnvSet.has(cell.environment)) continue;
20742
+ const keys = readSopsKeyNames(cell.filePath);
20743
+ if (keys === null) continue;
20744
+ for (const key of keys) {
20745
+ if (!keyEnvs.has(key)) keyEnvs.set(key, /* @__PURE__ */ new Set());
20746
+ keyEnvs.get(key).add(cell.environment);
20747
+ }
20759
20748
  }
20749
+ return keyEnvs;
20760
20750
  }
20761
20751
  };
20762
20752
  }
@@ -20902,19 +20892,18 @@ var init_sanitizer = __esm({
20902
20892
  });
20903
20893
 
20904
20894
  // ../core/src/report/generator.ts
20905
- var fs14, path16, YAML10, ReportGenerator;
20895
+ var path16, ReportGenerator;
20906
20896
  var init_generator = __esm({
20907
20897
  "../core/src/report/generator.ts"() {
20908
20898
  "use strict";
20909
- fs14 = __toESM(require("fs"));
20910
20899
  path16 = __toESM(require("path"));
20911
- YAML10 = __toESM(require_dist());
20912
20900
  init_types();
20913
20901
  init_parser();
20914
20902
  init_runner();
20915
20903
  init_metadata();
20916
20904
  init_checker();
20917
20905
  init_sanitizer();
20906
+ init_keys();
20918
20907
  ReportGenerator = class {
20919
20908
  constructor(runner2, sopsClient, matrixManager, schemaValidator) {
20920
20909
  this.runner = runner2;
@@ -21084,15 +21073,7 @@ var init_generator = __esm({
21084
21073
  };
21085
21074
  }
21086
21075
  readKeyCount(filePath) {
21087
- try {
21088
- if (!fs14.existsSync(filePath)) return 0;
21089
- const raw = fs14.readFileSync(filePath, "utf-8");
21090
- const parsed = YAML10.parse(raw);
21091
- if (parsed === null || parsed === void 0 || typeof parsed !== "object") return 0;
21092
- return Object.keys(parsed).filter((k) => k !== "sops").length;
21093
- } catch {
21094
- return 0;
21095
- }
21076
+ return readSopsKeyNames(filePath)?.length ?? 0;
21096
21077
  }
21097
21078
  async buildPolicy(manifest, repoRoot) {
21098
21079
  try {
@@ -21459,14 +21440,14 @@ var init_driver = __esm({
21459
21440
  });
21460
21441
 
21461
21442
  // ../core/src/service-identity/manager.ts
21462
- var fs15, os, path17, YAML11, PartialRotationError, ServiceIdentityManager;
21443
+ var fs14, os, path17, YAML10, PartialRotationError, ServiceIdentityManager;
21463
21444
  var init_manager2 = __esm({
21464
21445
  "../core/src/service-identity/manager.ts"() {
21465
21446
  "use strict";
21466
- fs15 = __toESM(require("fs"));
21447
+ fs14 = __toESM(require("fs"));
21467
21448
  os = __toESM(require("os"));
21468
21449
  path17 = __toESM(require("path"));
21469
- YAML11 = __toESM(require_dist());
21450
+ YAML10 = __toESM(require_dist());
21470
21451
  init_types();
21471
21452
  init_keygen();
21472
21453
  init_parser();
@@ -21521,8 +21502,8 @@ var init_manager2 = __esm({
21521
21502
  };
21522
21503
  await this.registerRecipients(definition, manifest, repoRoot);
21523
21504
  const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
21524
- const raw = fs15.readFileSync(manifestPath, "utf-8");
21525
- const doc = YAML11.parse(raw);
21505
+ const raw = fs14.readFileSync(manifestPath, "utf-8");
21506
+ const doc = YAML10.parse(raw);
21526
21507
  if (!Array.isArray(doc.service_identities)) {
21527
21508
  doc.service_identities = [];
21528
21509
  }
@@ -21534,11 +21515,11 @@ var init_manager2 = __esm({
21534
21515
  });
21535
21516
  const tmpCreate = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
21536
21517
  try {
21537
- fs15.writeFileSync(tmpCreate, YAML11.stringify(doc), "utf-8");
21538
- fs15.renameSync(tmpCreate, manifestPath);
21518
+ fs14.writeFileSync(tmpCreate, YAML10.stringify(doc), "utf-8");
21519
+ fs14.renameSync(tmpCreate, manifestPath);
21539
21520
  } finally {
21540
21521
  try {
21541
- fs15.unlinkSync(tmpCreate);
21522
+ fs14.unlinkSync(tmpCreate);
21542
21523
  } catch {
21543
21524
  }
21544
21525
  }
@@ -21577,8 +21558,8 @@ var init_manager2 = __esm({
21577
21558
  }
21578
21559
  }
21579
21560
  const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
21580
- const raw = fs15.readFileSync(manifestPath, "utf-8");
21581
- const doc = YAML11.parse(raw);
21561
+ const raw = fs14.readFileSync(manifestPath, "utf-8");
21562
+ const doc = YAML10.parse(raw);
21582
21563
  const identities = doc.service_identities;
21583
21564
  if (Array.isArray(identities)) {
21584
21565
  doc.service_identities = identities.filter(
@@ -21587,11 +21568,11 @@ var init_manager2 = __esm({
21587
21568
  }
21588
21569
  const tmp = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
21589
21570
  try {
21590
- fs15.writeFileSync(tmp, YAML11.stringify(doc), "utf-8");
21591
- fs15.renameSync(tmp, manifestPath);
21571
+ fs14.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
21572
+ fs14.renameSync(tmp, manifestPath);
21592
21573
  } finally {
21593
21574
  try {
21594
- fs15.unlinkSync(tmp);
21575
+ fs14.unlinkSync(tmp);
21595
21576
  } catch {
21596
21577
  }
21597
21578
  }
@@ -21607,8 +21588,8 @@ var init_manager2 = __esm({
21607
21588
  throw new Error(`Service identity '${name}' not found.`);
21608
21589
  }
21609
21590
  const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
21610
- const raw = fs15.readFileSync(manifestPath, "utf-8");
21611
- const doc = YAML11.parse(raw);
21591
+ const raw = fs14.readFileSync(manifestPath, "utf-8");
21592
+ const doc = YAML10.parse(raw);
21612
21593
  const identities = doc.service_identities;
21613
21594
  const siDoc = identities.find((si) => si.name === name);
21614
21595
  const envs = siDoc.environments;
@@ -21635,11 +21616,11 @@ var init_manager2 = __esm({
21635
21616
  }
21636
21617
  const tmp = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
21637
21618
  try {
21638
- fs15.writeFileSync(tmp, YAML11.stringify(doc), "utf-8");
21639
- fs15.renameSync(tmp, manifestPath);
21619
+ fs14.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
21620
+ fs14.renameSync(tmp, manifestPath);
21640
21621
  } finally {
21641
21622
  try {
21642
- fs15.unlinkSync(tmp);
21623
+ fs14.unlinkSync(tmp);
21643
21624
  } catch {
21644
21625
  }
21645
21626
  }
@@ -21676,8 +21657,8 @@ var init_manager2 = __esm({
21676
21657
  throw new Error(`Service identity '${name}' not found.`);
21677
21658
  }
21678
21659
  const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
21679
- const raw = fs15.readFileSync(manifestPath, "utf-8");
21680
- const doc = YAML11.parse(raw);
21660
+ const raw = fs14.readFileSync(manifestPath, "utf-8");
21661
+ const doc = YAML10.parse(raw);
21681
21662
  const identities = doc.service_identities;
21682
21663
  const siDoc = identities.find((si) => si.name === name);
21683
21664
  const envs = siDoc.environments;
@@ -21732,11 +21713,11 @@ var init_manager2 = __esm({
21732
21713
  }
21733
21714
  const tmpRotate = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
21734
21715
  try {
21735
- fs15.writeFileSync(tmpRotate, YAML11.stringify(doc), "utf-8");
21736
- fs15.renameSync(tmpRotate, manifestPath);
21716
+ fs14.writeFileSync(tmpRotate, YAML10.stringify(doc), "utf-8");
21717
+ fs14.renameSync(tmpRotate, manifestPath);
21737
21718
  } finally {
21738
21719
  try {
21739
- fs15.unlinkSync(tmpRotate);
21720
+ fs14.unlinkSync(tmpRotate);
21740
21721
  } catch {
21741
21722
  }
21742
21723
  }
@@ -21866,7 +21847,7 @@ var init_resolve = __esm({
21866
21847
  // ../core/src/artifact/signer.ts
21867
21848
  function buildSigningPayload(artifact) {
21868
21849
  const fields = [
21869
- "clef-sig-v1",
21850
+ "clef-sig-v2",
21870
21851
  String(artifact.version),
21871
21852
  artifact.identity,
21872
21853
  artifact.environment,
@@ -21878,7 +21859,9 @@ function buildSigningPayload(artifact) {
21878
21859
  artifact.envelope?.provider ?? "",
21879
21860
  artifact.envelope?.keyId ?? "",
21880
21861
  artifact.envelope?.wrappedKey ?? "",
21881
- artifact.envelope?.algorithm ?? ""
21862
+ artifact.envelope?.algorithm ?? "",
21863
+ artifact.envelope?.iv ?? "",
21864
+ artifact.envelope?.authTag ?? ""
21882
21865
  ];
21883
21866
  return Buffer.from(fields.join("\n"), "utf-8");
21884
21867
  }
@@ -21948,11 +21931,11 @@ var init_signer = __esm({
21948
21931
  });
21949
21932
 
21950
21933
  // ../core/src/artifact/packer.ts
21951
- var fs16, path18, crypto4, ArtifactPacker;
21934
+ var fs15, path18, crypto4, ArtifactPacker;
21952
21935
  var init_packer = __esm({
21953
21936
  "../core/src/artifact/packer.ts"() {
21954
21937
  "use strict";
21955
- fs16 = __toESM(require("fs"));
21938
+ fs15 = __toESM(require("fs"));
21956
21939
  path18 = __toESM(require("path"));
21957
21940
  crypto4 = __toESM(require("crypto"));
21958
21941
  init_types();
@@ -21989,37 +21972,41 @@ var init_packer = __esm({
21989
21972
  if (!this.kms) {
21990
21973
  throw new Error("KMS provider required for envelope encryption but none was provided.");
21991
21974
  }
21992
- const { generateIdentity, identityToRecipient, Encrypter } = await Promise.resolve().then(() => __toESM(require_age_encryption()));
21993
- const ephemeralPrivateKey = await generateIdentity();
21994
- const ephemeralPublicKey = await identityToRecipient(ephemeralPrivateKey);
21975
+ const dek = crypto4.randomBytes(32);
21976
+ const iv = crypto4.randomBytes(12);
21995
21977
  try {
21996
- const e = new Encrypter();
21997
- e.addRecipient(ephemeralPublicKey);
21998
- const encrypted = await e.encrypt(plaintext);
21999
- ciphertext = Buffer.from(encrypted).toString("base64");
22000
- } catch {
22001
- throw new Error("Failed to age-encrypt artifact with ephemeral key.");
21978
+ const cipher = crypto4.createCipheriv("aes-256-gcm", dek, iv);
21979
+ const ciphertextBuf = Buffer.concat([
21980
+ cipher.update(Buffer.from(plaintext, "utf-8")),
21981
+ cipher.final()
21982
+ ]);
21983
+ const authTag = cipher.getAuthTag();
21984
+ ciphertext = ciphertextBuf.toString("base64");
21985
+ const kmsConfig = resolved.envConfig.kms;
21986
+ const wrapped = await this.kms.wrap(kmsConfig.keyId, dek);
21987
+ const revision = `${Date.now()}-${crypto4.randomBytes(4).toString("hex")}`;
21988
+ const ciphertextHash = crypto4.createHash("sha256").update(ciphertext).digest("hex");
21989
+ artifact = {
21990
+ version: 1,
21991
+ identity: config.identity,
21992
+ environment: config.environment,
21993
+ packedAt: (/* @__PURE__ */ new Date()).toISOString(),
21994
+ revision,
21995
+ ciphertextHash,
21996
+ ciphertext,
21997
+ keys: Object.keys(resolved.values),
21998
+ envelope: {
21999
+ provider: kmsConfig.provider,
22000
+ keyId: kmsConfig.keyId,
22001
+ wrappedKey: wrapped.wrappedKey.toString("base64"),
22002
+ algorithm: wrapped.algorithm,
22003
+ iv: iv.toString("base64"),
22004
+ authTag: authTag.toString("base64")
22005
+ }
22006
+ };
22007
+ } finally {
22008
+ dek.fill(0);
22002
22009
  }
22003
- const kmsConfig = resolved.envConfig.kms;
22004
- const wrapped = await this.kms.wrap(kmsConfig.keyId, Buffer.from(ephemeralPrivateKey));
22005
- const revision = `${Date.now()}-${crypto4.randomBytes(4).toString("hex")}`;
22006
- const ciphertextHash = crypto4.createHash("sha256").update(ciphertext).digest("hex");
22007
- artifact = {
22008
- version: 1,
22009
- identity: config.identity,
22010
- environment: config.environment,
22011
- packedAt: (/* @__PURE__ */ new Date()).toISOString(),
22012
- revision,
22013
- ciphertextHash,
22014
- ciphertext,
22015
- keys: Object.keys(resolved.values),
22016
- envelope: {
22017
- provider: kmsConfig.provider,
22018
- keyId: kmsConfig.keyId,
22019
- wrappedKey: wrapped.wrappedKey.toString("base64"),
22020
- algorithm: wrapped.algorithm
22021
- }
22022
- };
22023
22010
  } else {
22024
22011
  try {
22025
22012
  const { Encrypter } = await Promise.resolve().then(() => __toESM(require_age_encryption()));
@@ -22027,8 +22014,10 @@ var init_packer = __esm({
22027
22014
  e.addRecipient(resolved.recipient);
22028
22015
  const encrypted = await e.encrypt(plaintext);
22029
22016
  ciphertext = Buffer.from(encrypted).toString("base64");
22030
- } catch {
22031
- throw new Error("Failed to age-encrypt artifact. Check recipient key.");
22017
+ } catch (err) {
22018
+ throw new Error(
22019
+ `Failed to age-encrypt artifact: ${err instanceof Error ? err.message : String(err)}`
22020
+ );
22032
22021
  }
22033
22022
  const revision = `${Date.now()}-${crypto4.randomBytes(4).toString("hex")}`;
22034
22023
  const ciphertextHash = crypto4.createHash("sha256").update(ciphertext).digest("hex");
@@ -22044,8 +22033,8 @@ var init_packer = __esm({
22044
22033
  };
22045
22034
  }
22046
22035
  const outputDir = path18.dirname(config.outputPath);
22047
- if (!fs16.existsSync(outputDir)) {
22048
- fs16.mkdirSync(outputDir, { recursive: true });
22036
+ if (!fs15.existsSync(outputDir)) {
22037
+ fs15.mkdirSync(outputDir, { recursive: true });
22049
22038
  }
22050
22039
  if (config.ttl && config.ttl > 0) {
22051
22040
  artifact.expiresAt = new Date(Date.now() + config.ttl * 1e3).toISOString();
@@ -22064,8 +22053,8 @@ var init_packer = __esm({
22064
22053
  }
22065
22054
  const json = JSON.stringify(artifact, null, 2);
22066
22055
  const tmpOutput = `${config.outputPath}.tmp.${process.pid}`;
22067
- fs16.writeFileSync(tmpOutput, json, "utf-8");
22068
- fs16.renameSync(tmpOutput, config.outputPath);
22056
+ fs15.writeFileSync(tmpOutput, json, "utf-8");
22057
+ fs15.renameSync(tmpOutput, config.outputPath);
22069
22058
  return {
22070
22059
  outputPath: config.outputPath,
22071
22060
  namespaceCount: resolved.identity.namespaces.length,
@@ -22078,6 +22067,23 @@ var init_packer = __esm({
22078
22067
  }
22079
22068
  });
22080
22069
 
22070
+ // ../core/src/kms/types.ts
22071
+ var VALID_KMS_PROVIDERS;
22072
+ var init_types2 = __esm({
22073
+ "../core/src/kms/types.ts"() {
22074
+ "use strict";
22075
+ VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
22076
+ }
22077
+ });
22078
+
22079
+ // ../core/src/kms/index.ts
22080
+ var init_kms = __esm({
22081
+ "../core/src/kms/index.ts"() {
22082
+ "use strict";
22083
+ init_types2();
22084
+ }
22085
+ });
22086
+
22081
22087
  // ../core/src/index.ts
22082
22088
  var src_exports = {};
22083
22089
  __export(src_exports, {
@@ -22117,6 +22123,7 @@ __export(src_exports, {
22117
22123
  SopsMergeDriver: () => SopsMergeDriver,
22118
22124
  SopsMissingError: () => SopsMissingError,
22119
22125
  SopsVersionError: () => SopsVersionError,
22126
+ VALID_KMS_PROVIDERS: () => VALID_KMS_PROVIDERS,
22120
22127
  assertSops: () => assertSops,
22121
22128
  buildSigningPayload: () => buildSigningPayload,
22122
22129
  checkAll: () => checkAll,
@@ -22143,7 +22150,7 @@ __export(src_exports, {
22143
22150
  markResolved: () => markResolved,
22144
22151
  matchPatterns: () => matchPatterns,
22145
22152
  metadataPath: () => metadataPath,
22146
- parse: () => parse7,
22153
+ parse: () => parse8,
22147
22154
  parseDotenv: () => parseDotenv,
22148
22155
  parseIgnoreContent: () => parseIgnoreContent,
22149
22156
  parseJson: () => parseJson,
@@ -22197,6 +22204,7 @@ var init_src = __esm({
22197
22204
  init_resolve();
22198
22205
  init_packer();
22199
22206
  init_signer();
22207
+ init_kms();
22200
22208
  }
22201
22209
  });
22202
22210
 
@@ -22285,7 +22293,7 @@ var require_ms = __commonJS({
22285
22293
  options = options || {};
22286
22294
  var type = typeof val;
22287
22295
  if (type === "string" && val.length > 0) {
22288
- return parse16(val);
22296
+ return parse15(val);
22289
22297
  } else if (type === "number" && isFinite(val)) {
22290
22298
  return options.long ? fmtLong(val) : fmtShort(val);
22291
22299
  }
@@ -22293,7 +22301,7 @@ var require_ms = __commonJS({
22293
22301
  "val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
22294
22302
  );
22295
22303
  };
22296
- function parse16(str2) {
22304
+ function parse15(str2) {
22297
22305
  str2 = String(str2);
22298
22306
  if (str2.length > 100) {
22299
22307
  return;
@@ -22752,7 +22760,7 @@ var require_has_flag = __commonJS({
22752
22760
  var require_supports_color = __commonJS({
22753
22761
  "../../node_modules/supports-color/index.js"(exports2, module2) {
22754
22762
  "use strict";
22755
- var os3 = require("os");
22763
+ var os4 = require("os");
22756
22764
  var tty = require("tty");
22757
22765
  var hasFlag = require_has_flag();
22758
22766
  var { env } = process;
@@ -22800,7 +22808,7 @@ var require_supports_color = __commonJS({
22800
22808
  return min;
22801
22809
  }
22802
22810
  if (process.platform === "win32") {
22803
- const osRelease = os3.release().split(".");
22811
+ const osRelease = os4.release().split(".");
22804
22812
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
22805
22813
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
22806
22814
  }
@@ -23732,7 +23740,7 @@ var require_bytes = __commonJS({
23732
23740
  "use strict";
23733
23741
  module2.exports = bytes;
23734
23742
  module2.exports.format = format;
23735
- module2.exports.parse = parse16;
23743
+ module2.exports.parse = parse15;
23736
23744
  var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
23737
23745
  var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
23738
23746
  var map = {
@@ -23746,7 +23754,7 @@ var require_bytes = __commonJS({
23746
23754
  var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;
23747
23755
  function bytes(value, options) {
23748
23756
  if (typeof value === "string") {
23749
- return parse16(value);
23757
+ return parse15(value);
23750
23758
  }
23751
23759
  if (typeof value === "number") {
23752
23760
  return format(value, options);
@@ -23790,7 +23798,7 @@ var require_bytes = __commonJS({
23790
23798
  }
23791
23799
  return str2 + unitSeparator + unit;
23792
23800
  }
23793
- function parse16(val) {
23801
+ function parse15(val) {
23794
23802
  if (typeof val === "number" && !isNaN(val)) {
23795
23803
  return val;
23796
23804
  }
@@ -27995,7 +28003,7 @@ var require_content_type = __commonJS({
27995
28003
  var QUOTE_REGEXP = /([\\"])/g;
27996
28004
  var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
27997
28005
  exports2.format = format;
27998
- exports2.parse = parse16;
28006
+ exports2.parse = parse15;
27999
28007
  function format(obj) {
28000
28008
  if (!obj || typeof obj !== "object") {
28001
28009
  throw new TypeError("argument obj is required");
@@ -28019,7 +28027,7 @@ var require_content_type = __commonJS({
28019
28027
  }
28020
28028
  return string;
28021
28029
  }
28022
- function parse16(string) {
28030
+ function parse15(string) {
28023
28031
  if (!string) {
28024
28032
  throw new TypeError("argument string is required");
28025
28033
  }
@@ -37601,7 +37609,7 @@ var require_media_typer = __commonJS({
37601
37609
  var TYPE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/;
37602
37610
  var TYPE_REGEXP = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/;
37603
37611
  exports2.format = format;
37604
- exports2.parse = parse16;
37612
+ exports2.parse = parse15;
37605
37613
  exports2.test = test;
37606
37614
  function format(obj) {
37607
37615
  if (!obj || typeof obj !== "object") {
@@ -37634,7 +37642,7 @@ var require_media_typer = __commonJS({
37634
37642
  }
37635
37643
  return TYPE_REGEXP.test(string.toLowerCase());
37636
37644
  }
37637
- function parse16(string) {
37645
+ function parse15(string) {
37638
37646
  if (!string) {
37639
37647
  throw new TypeError("argument string is required");
37640
37648
  }
@@ -37820,7 +37828,7 @@ var require_read = __commonJS({
37820
37828
  var hasBody = require_type_is().hasBody;
37821
37829
  var { getCharset } = require_utils();
37822
37830
  module2.exports = read;
37823
- function read(req, res, next, parse16, debug, options) {
37831
+ function read(req, res, next, parse15, debug, options) {
37824
37832
  if (onFinished.isFinished(req)) {
37825
37833
  debug("body already parsed");
37826
37834
  next();
@@ -37908,7 +37916,7 @@ var require_read = __commonJS({
37908
37916
  try {
37909
37917
  debug("parse body");
37910
37918
  str2 = typeof body !== "string" && encoding !== null ? iconv.decode(body, encoding) : body;
37911
- req.body = parse16(str2, encoding);
37919
+ req.body = parse15(str2, encoding);
37912
37920
  } catch (err) {
37913
37921
  next(createError(400, err, {
37914
37922
  body: str2,
@@ -37981,7 +37989,7 @@ var require_json = __commonJS({
37981
37989
  const normalizedOptions = normalizeOptions(options, "application/json");
37982
37990
  var reviver = options?.reviver;
37983
37991
  var strict = options?.strict !== false;
37984
- function parse16(body) {
37992
+ function parse15(body) {
37985
37993
  if (body.length === 0) {
37986
37994
  return {};
37987
37995
  }
@@ -38008,7 +38016,7 @@ var require_json = __commonJS({
38008
38016
  isValidCharset: (charset) => charset.slice(0, 4) === "utf-"
38009
38017
  };
38010
38018
  return function jsonParser(req, res, next) {
38011
- read(req, res, next, parse16, debug, readOptions);
38019
+ read(req, res, next, parse15, debug, readOptions);
38012
38020
  };
38013
38021
  }
38014
38022
  function createStrictSyntaxError(str2, char) {
@@ -40586,11 +40594,11 @@ var require_lib2 = __commonJS({
40586
40594
  "../../node_modules/qs/lib/index.js"(exports2, module2) {
40587
40595
  "use strict";
40588
40596
  var stringify7 = require_stringify2();
40589
- var parse16 = require_parse();
40597
+ var parse15 = require_parse();
40590
40598
  var formats = require_formats();
40591
40599
  module2.exports = {
40592
40600
  formats,
40593
- parse: parse16,
40601
+ parse: parse15,
40594
40602
  stringify: stringify7
40595
40603
  };
40596
40604
  }
@@ -40612,7 +40620,7 @@ var require_urlencoded = __commonJS({
40612
40620
  throw new TypeError("option defaultCharset must be either utf-8 or iso-8859-1");
40613
40621
  }
40614
40622
  var queryparse = createQueryParser(options);
40615
- function parse16(body, encoding) {
40623
+ function parse15(body, encoding) {
40616
40624
  return body.length ? queryparse(body, encoding) : {};
40617
40625
  }
40618
40626
  const readOptions = {
@@ -40621,7 +40629,7 @@ var require_urlencoded = __commonJS({
40621
40629
  isValidCharset: (charset) => charset === "utf-8" || charset === "iso-8859-1"
40622
40630
  };
40623
40631
  return function urlencodedParser(req, res, next) {
40624
- read(req, res, next, parse16, debug, readOptions);
40632
+ read(req, res, next, parse15, debug, readOptions);
40625
40633
  };
40626
40634
  }
40627
40635
  function createQueryParser(options) {
@@ -40805,7 +40813,7 @@ var require_parseurl = __commonJS({
40805
40813
  "../../node_modules/parseurl/index.js"(exports2, module2) {
40806
40814
  "use strict";
40807
40815
  var url = require("url");
40808
- var parse16 = url.parse;
40816
+ var parse15 = url.parse;
40809
40817
  var Url = url.Url;
40810
40818
  module2.exports = parseurl;
40811
40819
  module2.exports.original = originalurl;
@@ -40837,7 +40845,7 @@ var require_parseurl = __commonJS({
40837
40845
  }
40838
40846
  function fastparse(str2) {
40839
40847
  if (typeof str2 !== "string" || str2.charCodeAt(0) !== 47) {
40840
- return parse16(str2);
40848
+ return parse15(str2);
40841
40849
  }
40842
40850
  var pathname = str2;
40843
40851
  var query = null;
@@ -40865,7 +40873,7 @@ var require_parseurl = __commonJS({
40865
40873
  /* # */
40866
40874
  case 160:
40867
40875
  case 65279:
40868
- return parse16(str2);
40876
+ return parse15(str2);
40869
40877
  }
40870
40878
  }
40871
40879
  var url2 = Url !== void 0 ? new Url() : {};
@@ -41017,7 +41025,7 @@ var require_view = __commonJS({
41017
41025
  "use strict";
41018
41026
  var debug = require_src()("express:view");
41019
41027
  var path44 = require("node:path");
41020
- var fs27 = require("node:fs");
41028
+ var fs26 = require("node:fs");
41021
41029
  var dirname7 = path44.dirname;
41022
41030
  var basename5 = path44.basename;
41023
41031
  var extname2 = path44.extname;
@@ -41097,7 +41105,7 @@ var require_view = __commonJS({
41097
41105
  function tryStat(path45) {
41098
41106
  debug('stat "%s"', path45);
41099
41107
  try {
41100
- return fs27.statSync(path45);
41108
+ return fs26.statSync(path45);
41101
41109
  } catch (e) {
41102
41110
  return void 0;
41103
41111
  }
@@ -50661,7 +50669,7 @@ var require_forwarded = __commonJS({
50661
50669
  if (!req) {
50662
50670
  throw new TypeError("argument req is required");
50663
50671
  }
50664
- var proxyAddrs = parse16(req.headers["x-forwarded-for"] || "");
50672
+ var proxyAddrs = parse15(req.headers["x-forwarded-for"] || "");
50665
50673
  var socketAddr = getSocketAddr(req);
50666
50674
  var addrs = [socketAddr].concat(proxyAddrs);
50667
50675
  return addrs;
@@ -50669,7 +50677,7 @@ var require_forwarded = __commonJS({
50669
50677
  function getSocketAddr(req) {
50670
50678
  return req.socket ? req.socket.remoteAddress : req.connection.remoteAddress;
50671
50679
  }
50672
- function parse16(header) {
50680
+ function parse15(header) {
50673
50681
  var end = header.length;
50674
50682
  var list = [];
50675
50683
  var start = header.length;
@@ -51698,7 +51706,7 @@ var require_dist2 = __commonJS({
51698
51706
  "use strict";
51699
51707
  Object.defineProperty(exports2, "__esModule", { value: true });
51700
51708
  exports2.PathError = exports2.TokenData = void 0;
51701
- exports2.parse = parse16;
51709
+ exports2.parse = parse15;
51702
51710
  exports2.compile = compile;
51703
51711
  exports2.match = match;
51704
51712
  exports2.pathToRegexp = pathToRegexp;
@@ -51744,7 +51752,7 @@ var require_dist2 = __commonJS({
51744
51752
  }
51745
51753
  };
51746
51754
  exports2.PathError = PathError;
51747
- function parse16(str2, options = {}) {
51755
+ function parse15(str2, options = {}) {
51748
51756
  const { encodePath = NOOP_VALUE } = options;
51749
51757
  const chars = [...str2];
51750
51758
  const tokens = [];
@@ -51834,7 +51842,7 @@ var require_dist2 = __commonJS({
51834
51842
  }
51835
51843
  function compile(path44, options = {}) {
51836
51844
  const { encode = encodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
51837
- const data = typeof path44 === "object" ? path44 : parse16(path44, options);
51845
+ const data = typeof path44 === "object" ? path44 : parse15(path44, options);
51838
51846
  const fn = tokensToFunction(data.tokens, delimiter, encode);
51839
51847
  return function path45(params = {}) {
51840
51848
  const [path46, ...missing] = fn(params);
@@ -51929,7 +51937,7 @@ var require_dist2 = __commonJS({
51929
51937
  const flags = sensitive ? "" : "i";
51930
51938
  const sources = [];
51931
51939
  for (const input of pathsToArray(path44, [])) {
51932
- const data = typeof input === "object" ? input : parse16(input, options);
51940
+ const data = typeof input === "object" ? input : parse15(input, options);
51933
51941
  for (const tokens of flatten(data.tokens, 0, [])) {
51934
51942
  sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath));
51935
51943
  }
@@ -63238,7 +63246,7 @@ var require_request = __commonJS({
63238
63246
  var http = require("node:http");
63239
63247
  var fresh = require_fresh();
63240
63248
  var parseRange = require_range_parser();
63241
- var parse16 = require_parseurl();
63249
+ var parse15 = require_parseurl();
63242
63250
  var proxyaddr = require_proxy_addr();
63243
63251
  var req = Object.create(http.IncomingMessage.prototype);
63244
63252
  module2.exports = req;
@@ -63283,7 +63291,7 @@ var require_request = __commonJS({
63283
63291
  if (!queryparse) {
63284
63292
  return /* @__PURE__ */ Object.create(null);
63285
63293
  }
63286
- var querystring = parse16(this).query;
63294
+ var querystring = parse15(this).query;
63287
63295
  return queryparse(querystring);
63288
63296
  });
63289
63297
  req.is = function is(types) {
@@ -63327,7 +63335,7 @@ var require_request = __commonJS({
63327
63335
  return subdomains2.slice(offset);
63328
63336
  });
63329
63337
  defineGetter(req, "path", function path44() {
63330
- return parse16(this).pathname;
63338
+ return parse15(this).pathname;
63331
63339
  });
63332
63340
  defineGetter(req, "host", function host() {
63333
63341
  var trust = this.app.get("trust proxy fn");
@@ -63381,7 +63389,7 @@ var require_content_disposition = __commonJS({
63381
63389
  "../../node_modules/content-disposition/index.js"(exports2, module2) {
63382
63390
  "use strict";
63383
63391
  module2.exports = contentDisposition;
63384
- module2.exports.parse = parse16;
63392
+ module2.exports.parse = parse15;
63385
63393
  var basename5 = require("path").basename;
63386
63394
  var ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g;
63387
63395
  var HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/;
@@ -63472,7 +63480,7 @@ var require_content_disposition = __commonJS({
63472
63480
  function getlatin1(val) {
63473
63481
  return String(val).replace(NON_LATIN1_REGEXP, "?");
63474
63482
  }
63475
- function parse16(string) {
63483
+ function parse15(string) {
63476
63484
  if (!string || typeof string !== "string") {
63477
63485
  throw new TypeError("argument string is required");
63478
63486
  }
@@ -63561,7 +63569,7 @@ var require_cookie_signature = __commonJS({
63561
63569
  var require_cookie = __commonJS({
63562
63570
  "../../node_modules/cookie/index.js"(exports2) {
63563
63571
  "use strict";
63564
- exports2.parse = parse16;
63572
+ exports2.parse = parse15;
63565
63573
  exports2.serialize = serialize;
63566
63574
  var __toString = Object.prototype.toString;
63567
63575
  var __hasOwnProperty = Object.prototype.hasOwnProperty;
@@ -63569,7 +63577,7 @@ var require_cookie = __commonJS({
63569
63577
  var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
63570
63578
  var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
63571
63579
  var pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
63572
- function parse16(str2, opt) {
63580
+ function parse15(str2, opt) {
63573
63581
  if (typeof str2 !== "string") {
63574
63582
  throw new TypeError("argument str must be a string");
63575
63583
  }
@@ -73238,7 +73246,7 @@ var require_send = __commonJS({
73238
73246
  var escapeHtml = require_escape_html();
73239
73247
  var etag = require_etag();
73240
73248
  var fresh = require_fresh();
73241
- var fs27 = require("fs");
73249
+ var fs26 = require("fs");
73242
73250
  var mime = require_mime_types4();
73243
73251
  var ms = require_ms();
73244
73252
  var onFinished = require_on_finished();
@@ -73520,7 +73528,7 @@ var require_send = __commonJS({
73520
73528
  var i = 0;
73521
73529
  var self = this;
73522
73530
  debug('stat "%s"', path45);
73523
- fs27.stat(path45, function onstat(err, stat) {
73531
+ fs26.stat(path45, function onstat(err, stat) {
73524
73532
  var pathEndsWithSep = path45[path45.length - 1] === sep;
73525
73533
  if (err && err.code === "ENOENT" && !extname2(path45) && !pathEndsWithSep) {
73526
73534
  return next(err);
@@ -73537,7 +73545,7 @@ var require_send = __commonJS({
73537
73545
  }
73538
73546
  var p = path45 + "." + self._extensions[i++];
73539
73547
  debug('stat "%s"', p);
73540
- fs27.stat(p, function(err2, stat) {
73548
+ fs26.stat(p, function(err2, stat) {
73541
73549
  if (err2) return next(err2);
73542
73550
  if (stat.isDirectory()) return next();
73543
73551
  self.emit("file", p, stat);
@@ -73555,7 +73563,7 @@ var require_send = __commonJS({
73555
73563
  }
73556
73564
  var p = join39(path45, self._index[i]);
73557
73565
  debug('stat "%s"', p);
73558
- fs27.stat(p, function(err2, stat) {
73566
+ fs26.stat(p, function(err2, stat) {
73559
73567
  if (err2) return next(err2);
73560
73568
  if (stat.isDirectory()) return next();
73561
73569
  self.emit("file", p, stat);
@@ -73567,7 +73575,7 @@ var require_send = __commonJS({
73567
73575
  SendStream.prototype.stream = function stream(path45, options) {
73568
73576
  var self = this;
73569
73577
  var res = this.res;
73570
- var stream2 = fs27.createReadStream(path45, options);
73578
+ var stream2 = fs26.createReadStream(path45, options);
73571
73579
  this.emit("stream", stream2);
73572
73580
  stream2.pipe(res);
73573
73581
  function cleanup() {
@@ -73725,7 +73733,7 @@ var require_vary = __commonJS({
73725
73733
  if (!field) {
73726
73734
  throw new TypeError("field argument is required");
73727
73735
  }
73728
- var fields = !Array.isArray(field) ? parse16(String(field)) : field;
73736
+ var fields = !Array.isArray(field) ? parse15(String(field)) : field;
73729
73737
  for (var j = 0; j < fields.length; j++) {
73730
73738
  if (!FIELD_NAME_REGEXP.test(fields[j])) {
73731
73739
  throw new TypeError("field argument contains an invalid header name");
@@ -73735,7 +73743,7 @@ var require_vary = __commonJS({
73735
73743
  return header;
73736
73744
  }
73737
73745
  var val = header;
73738
- var vals = parse16(header.toLowerCase());
73746
+ var vals = parse15(header.toLowerCase());
73739
73747
  if (fields.indexOf("*") !== -1 || vals.indexOf("*") !== -1) {
73740
73748
  return "*";
73741
73749
  }
@@ -73748,7 +73756,7 @@ var require_vary = __commonJS({
73748
73756
  }
73749
73757
  return val;
73750
73758
  }
73751
- function parse16(header) {
73759
+ function parse15(header) {
73752
73760
  var end = 0;
73753
73761
  var list = [];
73754
73762
  var start = 0;
@@ -75253,7 +75261,7 @@ var require_api = __commonJS({
75253
75261
  Object.defineProperty(exports2, "__esModule", { value: true });
75254
75262
  exports2.createApiRouter = createApiRouter;
75255
75263
  var path44 = __importStar(require("path"));
75256
- var os3 = __importStar(require("os"));
75264
+ var os4 = __importStar(require("os"));
75257
75265
  var child_process_1 = require("child_process");
75258
75266
  var express_1 = require_express2();
75259
75267
  var _useStdinFifo = process.platform === "linux" && !process.env.JEST_WORKER_ID;
@@ -75273,7 +75281,7 @@ var require_api = __commonJS({
75273
75281
  env: { SOPS_CONFIG: path44.join(deps2.repoRoot, ".sops.yaml"), ...opts?.env }
75274
75282
  });
75275
75283
  }
75276
- const fifoDir = (0, child_process_1.execFileSync)("mktemp", ["-d", path44.join(os3.tmpdir(), "clef-fifo-XXXXXX")]).toString().trim();
75284
+ const fifoDir = (0, child_process_1.execFileSync)("mktemp", ["-d", path44.join(os4.tmpdir(), "clef-fifo-XXXXXX")]).toString().trim();
75277
75285
  const fifoPath = path44.join(fifoDir, "input");
75278
75286
  (0, child_process_1.execFileSync)("mkfifo", [fifoPath]);
75279
75287
  const writer = (0, child_process_1.spawn)("dd", [`of=${fifoPath}`, "status=none"], {
@@ -75307,6 +75315,7 @@ var require_api = __commonJS({
75307
75315
  const git = new core_1.GitIntegration(deps2.runner);
75308
75316
  const scanRunner = new core_1.ScanRunner(deps2.runner);
75309
75317
  const recipientManager = new core_1.RecipientManager(sops, matrix);
75318
+ const serviceIdManager = new core_1.ServiceIdentityManager(sops, matrix);
75310
75319
  const bulkOps = new core_1.BulkOps();
75311
75320
  let lastScanResult = null;
75312
75321
  let lastScanAt = null;
@@ -75314,6 +75323,10 @@ var require_api = __commonJS({
75314
75323
  const manifestPath = `${deps2.repoRoot}/clef.yaml`;
75315
75324
  return parser.parse(manifestPath);
75316
75325
  }
75326
+ function zeroStringRecord(record) {
75327
+ for (const k of Object.keys(record))
75328
+ record[k] = "";
75329
+ }
75317
75330
  function setNoCacheHeaders(res) {
75318
75331
  res.set({
75319
75332
  "Cache-Control": "no-store, no-cache, must-revalidate",
@@ -75555,6 +75568,14 @@ var require_api = __commonJS({
75555
75568
  return;
75556
75569
  }
75557
75570
  const result = await diffEngine.diffFiles(ns, envA, envB, manifest, sops, deps2.repoRoot);
75571
+ if (req.query.showValues !== "true") {
75572
+ for (const row of result.rows) {
75573
+ if (row.valueA !== null)
75574
+ row.valueA = "\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF";
75575
+ if (row.valueB !== null)
75576
+ row.valueB = "\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF";
75577
+ }
75578
+ }
75558
75579
  res.json(result);
75559
75580
  } catch {
75560
75581
  res.status(500).json({ error: "Failed to compute diff", code: "DIFF_ERROR" });
@@ -75883,6 +75904,100 @@ var require_api = __commonJS({
75883
75904
  res.status(500).json({ error: message, code: "SERVICE_IDENTITY_ERROR" });
75884
75905
  }
75885
75906
  });
75907
+ router.post("/service-identities", async (req, res) => {
75908
+ try {
75909
+ const manifest = loadManifest();
75910
+ const { name, description, namespaces, kmsEnvConfigs } = req.body;
75911
+ if (!name || typeof name !== "string") {
75912
+ res.status(400).json({ error: "name is required.", code: "BAD_REQUEST" });
75913
+ return;
75914
+ }
75915
+ if (!Array.isArray(namespaces) || namespaces.length === 0) {
75916
+ res.status(400).json({ error: "namespaces must be a non-empty array.", code: "BAD_REQUEST" });
75917
+ return;
75918
+ }
75919
+ let typedKmsConfigs;
75920
+ if (kmsEnvConfigs && Object.keys(kmsEnvConfigs).length > 0) {
75921
+ typedKmsConfigs = {};
75922
+ for (const [envName, cfg] of Object.entries(kmsEnvConfigs)) {
75923
+ if (!core_1.VALID_KMS_PROVIDERS.includes(cfg.provider)) {
75924
+ res.status(400).json({
75925
+ error: `Invalid KMS provider '${cfg.provider}' for environment '${envName}'. Must be aws, gcp, or azure.`,
75926
+ code: "BAD_REQUEST"
75927
+ });
75928
+ return;
75929
+ }
75930
+ typedKmsConfigs[envName] = {
75931
+ provider: cfg.provider,
75932
+ keyId: cfg.keyId
75933
+ };
75934
+ }
75935
+ }
75936
+ const result = await serviceIdManager.create(name, namespaces, description ?? "", manifest, deps2.repoRoot, typedKmsConfigs);
75937
+ setNoCacheHeaders(res);
75938
+ res.json({ identity: result.identity, privateKeys: result.privateKeys });
75939
+ zeroStringRecord(result.privateKeys);
75940
+ } catch (err) {
75941
+ const message = err instanceof Error ? err.message : "Failed to create service identity";
75942
+ res.status(500).json({ error: message, code: "SERVICE_IDENTITY_ERROR" });
75943
+ }
75944
+ });
75945
+ router.delete("/service-identities/:name", async (req, res) => {
75946
+ try {
75947
+ const name = req.params.name;
75948
+ const manifest = loadManifest();
75949
+ if (!manifest.service_identities?.find((si) => si.name === name)) {
75950
+ res.status(404).json({ error: `Service identity '${name}' not found.`, code: "NOT_FOUND" });
75951
+ return;
75952
+ }
75953
+ await serviceIdManager.delete(name, manifest, deps2.repoRoot);
75954
+ res.json({ ok: true });
75955
+ } catch (err) {
75956
+ const message = err instanceof Error ? err.message : "Failed to delete service identity";
75957
+ res.status(500).json({ error: message, code: "SERVICE_IDENTITY_ERROR" });
75958
+ }
75959
+ });
75960
+ router.patch("/service-identities/:name", async (req, res) => {
75961
+ try {
75962
+ const name = req.params.name;
75963
+ const { kmsEnvConfigs } = req.body;
75964
+ if (!kmsEnvConfigs || Object.keys(kmsEnvConfigs).length === 0) {
75965
+ res.status(400).json({ error: "kmsEnvConfigs must be a non-empty object.", code: "BAD_REQUEST" });
75966
+ return;
75967
+ }
75968
+ const manifest = loadManifest();
75969
+ const typedKmsConfigs = {};
75970
+ for (const [envName, cfg] of Object.entries(kmsEnvConfigs)) {
75971
+ if (cfg.provider !== "aws" && cfg.provider !== "gcp" && cfg.provider !== "azure") {
75972
+ res.status(400).json({
75973
+ error: `Invalid KMS provider '${cfg.provider}' for environment '${envName}'. Must be aws, gcp, or azure.`,
75974
+ code: "BAD_REQUEST"
75975
+ });
75976
+ return;
75977
+ }
75978
+ typedKmsConfigs[envName] = { provider: cfg.provider, keyId: cfg.keyId };
75979
+ }
75980
+ await serviceIdManager.updateEnvironments(name, typedKmsConfigs, manifest, deps2.repoRoot);
75981
+ res.json({ ok: true });
75982
+ } catch (err) {
75983
+ const message = err instanceof Error ? err.message : "Failed to update service identity";
75984
+ res.status(500).json({ error: message, code: "SERVICE_IDENTITY_ERROR" });
75985
+ }
75986
+ });
75987
+ router.post("/service-identities/:name/rotate", async (req, res) => {
75988
+ try {
75989
+ const name = req.params.name;
75990
+ const { environment } = req.body;
75991
+ const manifest = loadManifest();
75992
+ const privateKeys = await serviceIdManager.rotateKey(name, manifest, deps2.repoRoot, environment);
75993
+ setNoCacheHeaders(res);
75994
+ res.json({ privateKeys });
75995
+ zeroStringRecord(privateKeys);
75996
+ } catch (err) {
75997
+ const message = err instanceof Error ? err.message : "Failed to rotate service identity key";
75998
+ res.status(500).json({ error: message, code: "SERVICE_IDENTITY_ERROR" });
75999
+ }
76000
+ });
75886
76001
  function dispose() {
75887
76002
  lastScanResult = null;
75888
76003
  lastScanAt = null;
@@ -76038,7 +76153,7 @@ var require_server = __commonJS({
76038
76153
  const resolvedClientDir = clientDir ?? path44.resolve(__dirname, "../client");
76039
76154
  app.use(staticLimiter, express_1.default.static(resolvedClientDir));
76040
76155
  app.get("/{*splat}", staticLimiter, (_req, res) => {
76041
- res.sendFile(path44.join(resolvedClientDir, "index.html"));
76156
+ res.sendFile("index.html", { root: resolvedClientDir });
76042
76157
  });
76043
76158
  }
76044
76159
  const url = `http://127.0.0.1:${port}`;
@@ -76092,6 +76207,11 @@ var require_secrets_cache = __commonJS({
76092
76207
  snapshot = null;
76093
76208
  /** Replace the cached secrets in a single reference assignment. */
76094
76209
  swap(values, keys, revision) {
76210
+ if (this.snapshot) {
76211
+ for (const k of Object.keys(this.snapshot.values)) {
76212
+ this.snapshot.values[k] = "";
76213
+ }
76214
+ }
76095
76215
  this.snapshot = { values: { ...values }, keys: [...keys], revision, swappedAt: Date.now() };
76096
76216
  }
76097
76217
  /** Whether the cache has exceeded the given TTL (seconds). */
@@ -76100,8 +76220,13 @@ var require_secrets_cache = __commonJS({
76100
76220
  return false;
76101
76221
  return (Date.now() - this.snapshot.swappedAt) / 1e3 > ttlSeconds;
76102
76222
  }
76103
- /** Clear the cached snapshot. */
76223
+ /** Clear the cached snapshot, zeroing values first (best-effort). */
76104
76224
  wipe() {
76225
+ if (this.snapshot) {
76226
+ for (const k of Object.keys(this.snapshot.values)) {
76227
+ this.snapshot.values[k] = "";
76228
+ }
76229
+ }
76105
76230
  this.snapshot = null;
76106
76231
  }
76107
76232
  /** Epoch ms when the cache was last swapped, or null if never loaded. */
@@ -76180,7 +76305,7 @@ var require_disk_cache = __commonJS({
76180
76305
  })();
76181
76306
  Object.defineProperty(exports2, "__esModule", { value: true });
76182
76307
  exports2.DiskCache = void 0;
76183
- var fs27 = __importStar(require("fs"));
76308
+ var fs26 = __importStar(require("fs"));
76184
76309
  var path44 = __importStar(require("path"));
76185
76310
  var DiskCache = class {
76186
76311
  artifactPath;
@@ -76193,39 +76318,35 @@ var require_disk_cache = __commonJS({
76193
76318
  /** Write an artifact and optional metadata to disk (atomic via tmp+rename). */
76194
76319
  write(raw, sha) {
76195
76320
  const dir = path44.dirname(this.artifactPath);
76196
- fs27.mkdirSync(dir, { recursive: true });
76321
+ fs26.mkdirSync(dir, { recursive: true });
76197
76322
  const tmpArtifact = `${this.artifactPath}.tmp.${process.pid}`;
76198
- fs27.writeFileSync(tmpArtifact, raw, "utf-8");
76199
- fs27.renameSync(tmpArtifact, this.artifactPath);
76323
+ fs26.writeFileSync(tmpArtifact, raw, "utf-8");
76324
+ fs26.renameSync(tmpArtifact, this.artifactPath);
76200
76325
  const meta = { sha, fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
76201
76326
  const tmpMeta = `${this.metaPath}.tmp.${process.pid}`;
76202
- fs27.writeFileSync(tmpMeta, JSON.stringify(meta), "utf-8");
76203
- fs27.renameSync(tmpMeta, this.metaPath);
76327
+ fs26.writeFileSync(tmpMeta, JSON.stringify(meta), "utf-8");
76328
+ fs26.renameSync(tmpMeta, this.metaPath);
76204
76329
  }
76205
76330
  /** Read the cached artifact. Returns null if no cache file exists. */
76206
76331
  read() {
76207
76332
  try {
76208
- return fs27.readFileSync(this.artifactPath, "utf-8");
76333
+ return fs26.readFileSync(this.artifactPath, "utf-8");
76209
76334
  } catch {
76210
76335
  return null;
76211
76336
  }
76212
76337
  }
76213
76338
  /** Get the SHA from the cached metadata, if available. */
76214
76339
  getCachedSha() {
76215
- try {
76216
- const raw = fs27.readFileSync(this.metaPath, "utf-8");
76217
- const meta = JSON.parse(raw);
76218
- return meta.sha;
76219
- } catch {
76220
- return void 0;
76221
- }
76340
+ return this.readMeta()?.sha;
76222
76341
  }
76223
76342
  /** Get the fetchedAt timestamp from metadata, if available. */
76224
76343
  getFetchedAt() {
76344
+ return this.readMeta()?.fetchedAt;
76345
+ }
76346
+ readMeta() {
76225
76347
  try {
76226
- const raw = fs27.readFileSync(this.metaPath, "utf-8");
76227
- const meta = JSON.parse(raw);
76228
- return meta.fetchedAt;
76348
+ const raw = fs26.readFileSync(this.metaPath, "utf-8");
76349
+ return JSON.parse(raw);
76229
76350
  } catch {
76230
76351
  return void 0;
76231
76352
  }
@@ -76233,11 +76354,11 @@ var require_disk_cache = __commonJS({
76233
76354
  /** Remove cached artifact and metadata files. */
76234
76355
  purge() {
76235
76356
  try {
76236
- fs27.unlinkSync(this.artifactPath);
76357
+ fs26.unlinkSync(this.artifactPath);
76237
76358
  } catch {
76238
76359
  }
76239
76360
  try {
76240
- fs27.unlinkSync(this.metaPath);
76361
+ fs26.unlinkSync(this.metaPath);
76241
76362
  } catch {
76242
76363
  }
76243
76364
  }
@@ -76289,7 +76410,7 @@ var require_decrypt = __commonJS({
76289
76410
  })();
76290
76411
  Object.defineProperty(exports2, "__esModule", { value: true });
76291
76412
  exports2.AgeDecryptor = void 0;
76292
- var fs27 = __importStar(require("fs"));
76413
+ var fs26 = __importStar(require("fs"));
76293
76414
  var AgeDecryptor = class {
76294
76415
  /**
76295
76416
  * Decrypt an age-encrypted PEM-armored ciphertext string.
@@ -76315,7 +76436,7 @@ var require_decrypt = __commonJS({
76315
76436
  if (ageKey)
76316
76437
  return ageKey.trim();
76317
76438
  if (ageKeyFile) {
76318
- const content = fs27.readFileSync(ageKeyFile, "utf-8").trim();
76439
+ const content = fs26.readFileSync(ageKeyFile, "utf-8").trim();
76319
76440
  const lines = content.split("\n").filter((l) => l.startsWith("AGE-SECRET-KEY-"));
76320
76441
  if (lines.length === 0) {
76321
76442
  throw new Error(`No age secret key found in file: ${ageKeyFile}`);
@@ -76647,6 +76768,147 @@ var require_kms = __commonJS({
76647
76768
  }
76648
76769
  });
76649
76770
 
76771
+ // ../runtime/dist/artifact-decryptor.js
76772
+ var require_artifact_decryptor = __commonJS({
76773
+ "../runtime/dist/artifact-decryptor.js"(exports2) {
76774
+ "use strict";
76775
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
76776
+ if (k2 === void 0) k2 = k;
76777
+ var desc = Object.getOwnPropertyDescriptor(m, k);
76778
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
76779
+ desc = { enumerable: true, get: function() {
76780
+ return m[k];
76781
+ } };
76782
+ }
76783
+ Object.defineProperty(o, k2, desc);
76784
+ }) : (function(o, m, k, k2) {
76785
+ if (k2 === void 0) k2 = k;
76786
+ o[k2] = m[k];
76787
+ }));
76788
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
76789
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
76790
+ }) : function(o, v) {
76791
+ o["default"] = v;
76792
+ });
76793
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
76794
+ var ownKeys = function(o) {
76795
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
76796
+ var ar = [];
76797
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
76798
+ return ar;
76799
+ };
76800
+ return ownKeys(o);
76801
+ };
76802
+ return function(mod) {
76803
+ if (mod && mod.__esModule) return mod;
76804
+ var result = {};
76805
+ if (mod != null) {
76806
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
76807
+ }
76808
+ __setModuleDefault(result, mod);
76809
+ return result;
76810
+ };
76811
+ })();
76812
+ Object.defineProperty(exports2, "__esModule", { value: true });
76813
+ exports2.ArtifactDecryptor = void 0;
76814
+ var crypto6 = __importStar(require("crypto"));
76815
+ var decrypt_1 = require_decrypt();
76816
+ var kms_1 = require_kms();
76817
+ var ArtifactDecryptor = class {
76818
+ ageDecryptor = new decrypt_1.AgeDecryptor();
76819
+ privateKey;
76820
+ telemetryOverride;
76821
+ initialTelemetry;
76822
+ constructor(options) {
76823
+ this.privateKey = options.privateKey;
76824
+ this.initialTelemetry = options.telemetry;
76825
+ }
76826
+ /** Set or replace the telemetry emitter. */
76827
+ setTelemetry(emitter) {
76828
+ this.telemetryOverride = emitter;
76829
+ }
76830
+ get telemetry() {
76831
+ return this.telemetryOverride ?? this.initialTelemetry;
76832
+ }
76833
+ /**
76834
+ * Decrypt an artifact envelope into plaintext key-value pairs.
76835
+ *
76836
+ * @throws On KMS unwrap failure, AES-GCM auth failure, age decrypt failure,
76837
+ * missing private key (config error), or malformed plaintext JSON.
76838
+ */
76839
+ async decrypt(artifact) {
76840
+ let plaintext;
76841
+ if (artifact.envelope) {
76842
+ plaintext = await this.decryptKmsEnvelope(artifact);
76843
+ } else {
76844
+ plaintext = await this.decryptAge(artifact);
76845
+ }
76846
+ let values;
76847
+ try {
76848
+ values = JSON.parse(plaintext);
76849
+ } catch (err) {
76850
+ this.telemetry?.artifactInvalid({
76851
+ reason: "payload_parse",
76852
+ error: err instanceof Error ? err.message : String(err)
76853
+ });
76854
+ throw err;
76855
+ } finally {
76856
+ plaintext = "";
76857
+ }
76858
+ return { values, keys: artifact.keys, revision: artifact.revision };
76859
+ }
76860
+ /** KMS envelope: unwrap DEK via KMS, then AES-256-GCM decrypt. */
76861
+ async decryptKmsEnvelope(artifact) {
76862
+ const envelope = artifact.envelope;
76863
+ let dek;
76864
+ try {
76865
+ const kms = (0, kms_1.createKmsProvider)(envelope.provider);
76866
+ const wrappedKey = Buffer.from(envelope.wrappedKey, "base64");
76867
+ dek = await kms.unwrap(envelope.keyId, wrappedKey, envelope.algorithm);
76868
+ } catch (err) {
76869
+ this.telemetry?.artifactInvalid({
76870
+ reason: "kms_unwrap",
76871
+ error: err instanceof Error ? err.message : String(err)
76872
+ });
76873
+ throw err;
76874
+ }
76875
+ try {
76876
+ const iv = Buffer.from(envelope.iv, "base64");
76877
+ const authTag = Buffer.from(envelope.authTag, "base64");
76878
+ const ciphertextBuf = Buffer.from(artifact.ciphertext, "base64");
76879
+ const decipher = crypto6.createDecipheriv("aes-256-gcm", dek, iv);
76880
+ decipher.setAuthTag(authTag);
76881
+ return Buffer.concat([decipher.update(ciphertextBuf), decipher.final()]).toString("utf-8");
76882
+ } catch (err) {
76883
+ this.telemetry?.artifactInvalid({
76884
+ reason: "decrypt",
76885
+ error: err instanceof Error ? err.message : String(err)
76886
+ });
76887
+ throw err;
76888
+ } finally {
76889
+ dek.fill(0);
76890
+ }
76891
+ }
76892
+ /** Age-only: decrypt with the static private key. */
76893
+ async decryptAge(artifact) {
76894
+ if (!this.privateKey) {
76895
+ throw new Error("Artifact requires an age private key. Set CLEF_AGENT_AGE_KEY or use KMS envelope encryption.");
76896
+ }
76897
+ try {
76898
+ return await this.ageDecryptor.decrypt(artifact.ciphertext, this.privateKey);
76899
+ } catch (err) {
76900
+ this.telemetry?.artifactInvalid({
76901
+ reason: err instanceof SyntaxError ? "payload_parse" : "decrypt",
76902
+ error: err instanceof Error ? err.message : String(err)
76903
+ });
76904
+ throw err;
76905
+ }
76906
+ }
76907
+ };
76908
+ exports2.ArtifactDecryptor = ArtifactDecryptor;
76909
+ }
76910
+ });
76911
+
76650
76912
  // ../runtime/dist/signature.js
76651
76913
  var require_signature = __commonJS({
76652
76914
  "../runtime/dist/signature.js"(exports2) {
@@ -76694,7 +76956,7 @@ var require_signature = __commonJS({
76694
76956
  var crypto6 = __importStar(require("crypto"));
76695
76957
  function buildSigningPayload2(artifact) {
76696
76958
  const fields = [
76697
- "clef-sig-v1",
76959
+ "clef-sig-v2",
76698
76960
  String(artifact.version),
76699
76961
  artifact.identity,
76700
76962
  artifact.environment,
@@ -76706,7 +76968,9 @@ var require_signature = __commonJS({
76706
76968
  artifact.envelope?.provider ?? "",
76707
76969
  artifact.envelope?.keyId ?? "",
76708
76970
  artifact.envelope?.wrappedKey ?? "",
76709
- artifact.envelope?.algorithm ?? ""
76971
+ artifact.envelope?.algorithm ?? "",
76972
+ artifact.envelope?.iv ?? "",
76973
+ artifact.envelope?.authTag ?? ""
76710
76974
  ];
76711
76975
  return Buffer.from(fields.join("\n"), "utf-8");
76712
76976
  }
@@ -76773,8 +77037,7 @@ var require_poller = __commonJS({
76773
77037
  Object.defineProperty(exports2, "__esModule", { value: true });
76774
77038
  exports2.ArtifactPoller = void 0;
76775
77039
  var crypto6 = __importStar(require("crypto"));
76776
- var decrypt_1 = require_decrypt();
76777
- var kms_1 = require_kms();
77040
+ var artifact_decryptor_1 = require_artifact_decryptor();
76778
77041
  var signature_1 = require_signature();
76779
77042
  var MIN_POLL_MS = 5e3;
76780
77043
  var ArtifactPoller = class {
@@ -76782,21 +77045,68 @@ var require_poller = __commonJS({
76782
77045
  lastContentHash = null;
76783
77046
  lastRevision = null;
76784
77047
  lastExpiresAt = null;
76785
- decryptor = new decrypt_1.AgeDecryptor();
77048
+ decryptor;
76786
77049
  options;
77050
+ jitMode;
76787
77051
  telemetryOverride;
76788
77052
  constructor(options) {
76789
77053
  this.options = options;
77054
+ this.jitMode = !!options.encryptedStore;
77055
+ this.decryptor = new artifact_decryptor_1.ArtifactDecryptor({
77056
+ privateKey: options.privateKey,
77057
+ telemetry: options.telemetry
77058
+ });
77059
+ }
77060
+ /** Get the decryptor instance (for JIT mode server wiring). */
77061
+ getDecryptor() {
77062
+ return this.decryptor;
76790
77063
  }
76791
77064
  /** Set or replace the telemetry emitter (e.g. after resolving token from secrets). */
76792
77065
  setTelemetry(emitter) {
76793
77066
  this.telemetryOverride = emitter;
77067
+ this.decryptor.setTelemetry(emitter);
76794
77068
  }
76795
77069
  get telemetry() {
76796
77070
  return this.telemetryOverride ?? this.options.telemetry;
76797
77071
  }
76798
- /** Fetch, validate, decrypt, and cache the artifact. */
77072
+ /**
77073
+ * Fetch, validate, decrypt, and cache the artifact.
77074
+ * Used in cached mode (cacheTtl > 0).
77075
+ */
76799
77076
  async fetchAndDecrypt() {
77077
+ const result = await this.fetchRaw();
77078
+ if (!result)
77079
+ return;
77080
+ await this.validateDecryptAndCache(result.artifact, result.contentHash);
77081
+ }
77082
+ /**
77083
+ * Fetch and validate the artifact without decrypting.
77084
+ * Stores the validated envelope in the encryptedStore for on-demand decryption.
77085
+ * Used in JIT mode (cacheTtl = 0).
77086
+ */
77087
+ async fetchAndValidate() {
77088
+ const result = await this.fetchRaw();
77089
+ if (!result)
77090
+ return;
77091
+ const artifact = this.validateArtifact(result.artifact);
77092
+ this.options.encryptedStore.swap(artifact);
77093
+ this.lastRevision = artifact.revision;
77094
+ this.lastContentHash = result.contentHash ?? null;
77095
+ this.lastExpiresAt = artifact.expiresAt ?? null;
77096
+ this.options.onRefresh?.(artifact.revision);
77097
+ this.telemetry?.artifactRefreshed({
77098
+ revision: artifact.revision,
77099
+ keyCount: artifact.keys.length,
77100
+ kmsEnvelope: !!artifact.envelope
77101
+ });
77102
+ }
77103
+ /**
77104
+ * Fetch the raw artifact from the source (with disk cache fallback),
77105
+ * parse JSON, and check for revocation.
77106
+ *
77107
+ * Returns null when the content hash is unchanged (short-circuit).
77108
+ */
77109
+ async fetchRaw() {
76800
77110
  let raw;
76801
77111
  let contentHash;
76802
77112
  try {
@@ -76804,7 +77114,7 @@ var require_poller = __commonJS({
76804
77114
  raw = result.raw;
76805
77115
  contentHash = result.contentHash;
76806
77116
  if (contentHash && contentHash === this.lastContentHash)
76807
- return;
77117
+ return null;
76808
77118
  this.options.diskCache?.write(raw, contentHash);
76809
77119
  } catch (err) {
76810
77120
  this.telemetry?.fetchFailed({
@@ -76815,7 +77125,7 @@ var require_poller = __commonJS({
76815
77125
  if (this.options.diskCache) {
76816
77126
  const cached2 = this.options.diskCache.read();
76817
77127
  if (cached2) {
76818
- if (ttl !== void 0) {
77128
+ if (ttl !== void 0 && ttl > 0) {
76819
77129
  const fetchedAt = this.options.diskCache.getFetchedAt();
76820
77130
  if (fetchedAt && (Date.now() - new Date(fetchedAt).getTime()) / 1e3 > ttl) {
76821
77131
  this.options.cache.wipe();
@@ -76830,9 +77140,9 @@ var require_poller = __commonJS({
76830
77140
  raw = cached2;
76831
77141
  contentHash = this.options.diskCache.getCachedSha();
76832
77142
  if (contentHash && contentHash === this.lastContentHash)
76833
- return;
77143
+ return null;
76834
77144
  } else {
76835
- if (ttl !== void 0 && this.options.cache.isExpired(ttl)) {
77145
+ if (ttl !== void 0 && ttl > 0 && this.options.cache.isExpired(ttl)) {
76836
77146
  this.options.cache.wipe();
76837
77147
  this.telemetry?.cacheExpired({
76838
77148
  cacheTtlSeconds: ttl,
@@ -76843,7 +77153,7 @@ var require_poller = __commonJS({
76843
77153
  throw err;
76844
77154
  }
76845
77155
  } else {
76846
- if (ttl !== void 0 && this.options.cache.isExpired(ttl)) {
77156
+ if (ttl !== void 0 && ttl > 0 && this.options.cache.isExpired(ttl)) {
76847
77157
  this.options.cache.wipe();
76848
77158
  this.telemetry?.cacheExpired({
76849
77159
  cacheTtlSeconds: ttl,
@@ -76857,6 +77167,7 @@ var require_poller = __commonJS({
76857
77167
  const parsed = JSON.parse(raw);
76858
77168
  if (parsed.revokedAt) {
76859
77169
  this.options.cache.wipe();
77170
+ this.options.encryptedStore?.wipe();
76860
77171
  this.options.diskCache?.purge();
76861
77172
  this.lastRevision = null;
76862
77173
  this.lastContentHash = null;
@@ -76865,17 +77176,18 @@ var require_poller = __commonJS({
76865
77176
  });
76866
77177
  throw new Error(`Artifact revoked: ${parsed.identity}/${parsed.environment} at ${parsed.revokedAt}`);
76867
77178
  }
76868
- await this.validateDecryptAndCache(raw, contentHash);
77179
+ return { artifact: parsed, contentHash };
76869
77180
  }
76870
77181
  /**
76871
- * Validate the artifact, decrypt it, and swap the cache.
76872
- * Emits `artifact.invalid` on any validation or decryption failure,
76873
- * and `artifact.expired` / `artifact.refreshed` on their respective paths.
77182
+ * Validate the artifact envelope: version, required fields, expiry,
77183
+ * revision dedup, integrity hash, and signature.
77184
+ * Emits `artifact.invalid` / `artifact.expired` telemetry on failure.
77185
+ * Returns the validated artifact, or throws.
76874
77186
  */
76875
- async validateDecryptAndCache(raw, contentHash) {
77187
+ validateArtifact(parsed) {
76876
77188
  let artifact;
76877
77189
  try {
76878
- artifact = this.parseAndValidate(raw);
77190
+ artifact = this.validateEnvelope(parsed);
76879
77191
  } catch (err) {
76880
77192
  this.telemetry?.artifactInvalid({
76881
77193
  reason: classifyValidationError(err),
@@ -76885,12 +77197,13 @@ var require_poller = __commonJS({
76885
77197
  }
76886
77198
  if (artifact.expiresAt && Date.now() > new Date(artifact.expiresAt).getTime()) {
76887
77199
  this.options.cache.wipe();
77200
+ this.options.encryptedStore?.wipe();
76888
77201
  this.options.diskCache?.purge();
76889
77202
  this.telemetry?.artifactExpired({ expiresAt: artifact.expiresAt });
76890
77203
  throw new Error(`Artifact expired at ${artifact.expiresAt}`);
76891
77204
  }
76892
77205
  if (artifact.revision === this.lastRevision)
76893
- return;
77206
+ return artifact;
76894
77207
  const hash = crypto6.createHash("sha256").update(artifact.ciphertext).digest("hex");
76895
77208
  if (hash !== artifact.ciphertextHash) {
76896
77209
  const err = new Error(`Artifact integrity check failed: expected hash ${artifact.ciphertextHash}, got ${hash}`);
@@ -76930,53 +77243,34 @@ var require_poller = __commonJS({
76930
77243
  throw err;
76931
77244
  }
76932
77245
  }
76933
- let agePrivateKey;
76934
- if (artifact.envelope) {
76935
- try {
76936
- const kms = (0, kms_1.createKmsProvider)(artifact.envelope.provider);
76937
- const wrappedKey = Buffer.from(artifact.envelope.wrappedKey, "base64");
76938
- const unwrapped = await kms.unwrap(artifact.envelope.keyId, wrappedKey, artifact.envelope.algorithm);
76939
- agePrivateKey = unwrapped.toString("utf-8");
76940
- unwrapped.fill(0);
76941
- } catch (err) {
76942
- this.telemetry?.artifactInvalid({
76943
- reason: "kms_unwrap",
76944
- error: err instanceof Error ? err.message : String(err)
76945
- });
76946
- throw err;
76947
- }
76948
- } else {
76949
- if (!this.options.privateKey) {
76950
- throw new Error("Artifact requires an age private key. Set CLEF_AGENT_AGE_KEY or use KMS envelope encryption.");
76951
- }
76952
- agePrivateKey = this.options.privateKey;
76953
- }
76954
- try {
76955
- const plaintext = await this.decryptor.decrypt(artifact.ciphertext, agePrivateKey);
76956
- const values = JSON.parse(plaintext);
76957
- this.options.cache.swap(values, artifact.keys, artifact.revision);
76958
- this.lastRevision = artifact.revision;
76959
- this.lastContentHash = contentHash ?? null;
76960
- this.lastExpiresAt = artifact.expiresAt ?? null;
76961
- this.options.onRefresh?.(artifact.revision);
76962
- this.telemetry?.artifactRefreshed({
76963
- revision: artifact.revision,
76964
- keyCount: artifact.keys.length,
76965
- kmsEnvelope: !!artifact.envelope
76966
- });
76967
- } catch (err) {
76968
- if (err instanceof Error && !err.message.includes("integrity check failed")) {
76969
- this.telemetry?.artifactInvalid({
76970
- reason: err instanceof SyntaxError ? "payload_parse" : "decrypt",
76971
- error: err.message
76972
- });
76973
- }
76974
- throw err;
76975
- }
77246
+ return artifact;
77247
+ }
77248
+ /**
77249
+ * Validate then decrypt and cache. Used by fetchAndDecrypt (cached mode).
77250
+ */
77251
+ async validateDecryptAndCache(parsed, contentHash) {
77252
+ const artifact = this.validateArtifact(parsed);
77253
+ if (artifact.revision === this.lastRevision)
77254
+ return;
77255
+ const { values } = await this.decryptor.decrypt(artifact);
77256
+ this.options.cache.swap(values, artifact.keys, artifact.revision);
77257
+ this.lastRevision = artifact.revision;
77258
+ this.lastContentHash = contentHash ?? null;
77259
+ this.lastExpiresAt = artifact.expiresAt ?? null;
77260
+ this.options.onRefresh?.(artifact.revision);
77261
+ this.telemetry?.artifactRefreshed({
77262
+ revision: artifact.revision,
77263
+ keyCount: artifact.keys.length,
77264
+ kmsEnvelope: !!artifact.envelope
77265
+ });
76976
77266
  }
76977
77267
  /** Start the polling loop. Performs an initial fetch immediately. */
76978
77268
  async start() {
76979
- await this.fetchAndDecrypt();
77269
+ if (this.jitMode) {
77270
+ await this.fetchAndValidate();
77271
+ } else {
77272
+ await this.fetchAndDecrypt();
77273
+ }
76980
77274
  this.scheduleNext();
76981
77275
  }
76982
77276
  /** Start only the polling schedule (no initial fetch). */
@@ -77002,7 +77296,11 @@ var require_poller = __commonJS({
77002
77296
  this.timer = setTimeout(async () => {
77003
77297
  this.timer = null;
77004
77298
  try {
77005
- await this.fetchAndDecrypt();
77299
+ if (this.jitMode) {
77300
+ await this.fetchAndValidate();
77301
+ } else {
77302
+ await this.fetchAndDecrypt();
77303
+ }
77006
77304
  } catch (err) {
77007
77305
  this.options.onError?.(err instanceof Error ? err : new Error(String(err)));
77008
77306
  }
@@ -77018,14 +77316,15 @@ var require_poller = __commonJS({
77018
77316
  }
77019
77317
  return MIN_POLL_MS;
77020
77318
  }
77319
+ if (this.jitMode)
77320
+ return MIN_POLL_MS;
77021
77321
  const ttl = this.options.cacheTtl;
77022
77322
  if (ttl !== void 0) {
77023
77323
  return Math.max(ttl / 10 * 1e3, MIN_POLL_MS);
77024
77324
  }
77025
77325
  return 3e4;
77026
77326
  }
77027
- parseAndValidate(raw) {
77028
- const artifact = JSON.parse(raw);
77327
+ validateEnvelope(artifact) {
77029
77328
  if (artifact.version !== 1) {
77030
77329
  throw new Error(`Unsupported artifact version: ${artifact.version}`);
77031
77330
  }
@@ -77033,7 +77332,7 @@ var require_poller = __commonJS({
77033
77332
  throw new Error("Invalid artifact: missing required fields.");
77034
77333
  }
77035
77334
  if (artifact.envelope) {
77036
- if (!artifact.envelope.provider || !artifact.envelope.keyId || !artifact.envelope.wrappedKey || !artifact.envelope.algorithm) {
77335
+ if (!artifact.envelope.provider || !artifact.envelope.keyId || !artifact.envelope.wrappedKey || !artifact.envelope.algorithm || !artifact.envelope.iv || !artifact.envelope.authTag) {
77037
77336
  throw new Error("Invalid artifact: incomplete envelope fields.");
77038
77337
  }
77039
77338
  }
@@ -77058,6 +77357,50 @@ var require_poller = __commonJS({
77058
77357
  }
77059
77358
  });
77060
77359
 
77360
+ // ../runtime/dist/encrypted-artifact-store.js
77361
+ var require_encrypted_artifact_store = __commonJS({
77362
+ "../runtime/dist/encrypted-artifact-store.js"(exports2) {
77363
+ "use strict";
77364
+ Object.defineProperty(exports2, "__esModule", { value: true });
77365
+ exports2.EncryptedArtifactStore = void 0;
77366
+ var EncryptedArtifactStore = class {
77367
+ artifact = null;
77368
+ _storedAt = null;
77369
+ /** Atomically replace the stored artifact. */
77370
+ swap(artifact) {
77371
+ this.artifact = artifact;
77372
+ this._storedAt = Date.now();
77373
+ }
77374
+ /** Get the current encrypted artifact. Returns null if not yet loaded. */
77375
+ get() {
77376
+ return this.artifact;
77377
+ }
77378
+ /** Whether an artifact has been stored. */
77379
+ isReady() {
77380
+ return this.artifact !== null;
77381
+ }
77382
+ /** Epoch ms of last store, or null. */
77383
+ getStoredAt() {
77384
+ return this._storedAt;
77385
+ }
77386
+ /** Get key names from the stored artifact metadata (no decryption needed). */
77387
+ getKeys() {
77388
+ return this.artifact ? [...this.artifact.keys] : [];
77389
+ }
77390
+ /** Get the revision from the stored artifact. */
77391
+ getRevision() {
77392
+ return this.artifact?.revision ?? null;
77393
+ }
77394
+ /** Clear the stored artifact (on revocation/expiry). */
77395
+ wipe() {
77396
+ this.artifact = null;
77397
+ this._storedAt = null;
77398
+ }
77399
+ };
77400
+ exports2.EncryptedArtifactStore = EncryptedArtifactStore;
77401
+ }
77402
+ });
77403
+
77061
77404
  // ../runtime/dist/telemetry.js
77062
77405
  var require_telemetry = __commonJS({
77063
77406
  "../runtime/dist/telemetry.js"(exports2) {
@@ -77412,14 +77755,23 @@ var require_http = __commonJS({
77412
77755
  async fetch() {
77413
77756
  const res = await fetch(this.url);
77414
77757
  if (!res.ok) {
77415
- throw new Error(`Failed to fetch artifact from ${this.url}: ${res.status}`);
77758
+ throw new Error(`Failed to fetch artifact from ${this.describe()}: ${res.status}`);
77416
77759
  }
77417
77760
  const raw = await res.text();
77418
77761
  const etag = res.headers.get("etag") ?? void 0;
77419
77762
  return { raw, contentHash: etag };
77420
77763
  }
77421
77764
  describe() {
77422
- return `HTTP ${this.url}`;
77765
+ try {
77766
+ const parsed = new URL(this.url);
77767
+ if (parsed.username || parsed.password) {
77768
+ parsed.username = "***";
77769
+ parsed.password = "";
77770
+ }
77771
+ return `HTTP ${parsed.href}`;
77772
+ } catch {
77773
+ return "HTTP <invalid-url>";
77774
+ }
77423
77775
  }
77424
77776
  };
77425
77777
  exports2.HttpArtifactSource = HttpArtifactSource;
@@ -77469,14 +77821,14 @@ var require_file = __commonJS({
77469
77821
  })();
77470
77822
  Object.defineProperty(exports2, "__esModule", { value: true });
77471
77823
  exports2.FileArtifactSource = void 0;
77472
- var fs27 = __importStar(require("fs"));
77824
+ var fs26 = __importStar(require("fs"));
77473
77825
  var FileArtifactSource = class {
77474
77826
  path;
77475
77827
  constructor(filePath) {
77476
77828
  this.path = filePath;
77477
77829
  }
77478
77830
  async fetch() {
77479
- const raw = fs27.readFileSync(this.path, "utf-8");
77831
+ const raw = fs26.readFileSync(this.path, "utf-8");
77480
77832
  return { raw };
77481
77833
  }
77482
77834
  describe() {
@@ -77521,7 +77873,7 @@ var require_dist4 = __commonJS({
77521
77873
  "../runtime/dist/index.js"(exports2) {
77522
77874
  "use strict";
77523
77875
  Object.defineProperty(exports2, "__esModule", { value: true });
77524
- exports2.ClefRuntime = exports2.verifySignature = exports2.buildSigningPayload = exports2.VcsArtifactSource = exports2.FileArtifactSource = exports2.HttpArtifactSource = exports2.createKmsProvider = exports2.AwsKmsProvider = exports2.createVcsProvider = exports2.BitbucketProvider = exports2.GitLabProvider = exports2.GitHubProvider = exports2.TelemetryEmitter = exports2.ArtifactPoller = exports2.AgeDecryptor = exports2.DiskCache = exports2.SecretsCache = void 0;
77876
+ exports2.ClefRuntime = exports2.verifySignature = exports2.buildSigningPayload = exports2.VcsArtifactSource = exports2.FileArtifactSource = exports2.HttpArtifactSource = exports2.createKmsProvider = exports2.AwsKmsProvider = exports2.createVcsProvider = exports2.BitbucketProvider = exports2.GitLabProvider = exports2.GitHubProvider = exports2.TelemetryEmitter = exports2.EncryptedArtifactStore = exports2.ArtifactDecryptor = exports2.ArtifactPoller = exports2.AgeDecryptor = exports2.DiskCache = exports2.SecretsCache = void 0;
77525
77877
  exports2.init = init;
77526
77878
  var secrets_cache_1 = require_secrets_cache();
77527
77879
  Object.defineProperty(exports2, "SecretsCache", { enumerable: true, get: function() {
@@ -77539,6 +77891,14 @@ var require_dist4 = __commonJS({
77539
77891
  Object.defineProperty(exports2, "ArtifactPoller", { enumerable: true, get: function() {
77540
77892
  return poller_1.ArtifactPoller;
77541
77893
  } });
77894
+ var artifact_decryptor_1 = require_artifact_decryptor();
77895
+ Object.defineProperty(exports2, "ArtifactDecryptor", { enumerable: true, get: function() {
77896
+ return artifact_decryptor_1.ArtifactDecryptor;
77897
+ } });
77898
+ var encrypted_artifact_store_1 = require_encrypted_artifact_store();
77899
+ Object.defineProperty(exports2, "EncryptedArtifactStore", { enumerable: true, get: function() {
77900
+ return encrypted_artifact_store_1.EncryptedArtifactStore;
77901
+ } });
77542
77902
  var telemetry_1 = require_telemetry();
77543
77903
  Object.defineProperty(exports2, "TelemetryEmitter", { enumerable: true, get: function() {
77544
77904
  return telemetry_1.TelemetryEmitter;
@@ -77751,10 +78111,13 @@ var NodeSubprocessRunner = class {
77751
78111
  };
77752
78112
 
77753
78113
  // src/commands/init.ts
77754
- var fs17 = __toESM(require("fs"));
78114
+ var fs16 = __toESM(require("fs"));
77755
78115
  var path19 = __toESM(require("path"));
77756
78116
  var readline2 = __toESM(require("readline"));
77757
- var YAML12 = __toESM(require_dist());
78117
+ var YAML11 = __toESM(require_dist());
78118
+ init_src();
78119
+
78120
+ // src/handle-error.ts
77758
78121
  init_src();
77759
78122
 
77760
78123
  // src/output/formatter.ts
@@ -77966,6 +78329,16 @@ function pad(str2, width) {
77966
78329
  return diff > 0 ? str2 + " ".repeat(diff) : str2;
77967
78330
  }
77968
78331
 
78332
+ // src/handle-error.ts
78333
+ function handleCommandError(err) {
78334
+ if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
78335
+ formatter.formatDependencyError(err);
78336
+ } else {
78337
+ formatter.error(err.message);
78338
+ }
78339
+ process.exit(1);
78340
+ }
78341
+
77969
78342
  // src/keychain.ts
77970
78343
  var SERVICE = "clef";
77971
78344
  var CRED_HELPER_CS = `
@@ -78416,8 +78789,8 @@ function registerInitCommand(program3, deps2) {
78416
78789
  }
78417
78790
  const manifestPath = path19.join(repoRoot, "clef.yaml");
78418
78791
  const clefConfigPath = path19.join(repoRoot, CLEF_DIR, CLEF_CONFIG_FILENAME);
78419
- const manifestExists = fs17.existsSync(manifestPath);
78420
- const localConfigExists = fs17.existsSync(clefConfigPath);
78792
+ const manifestExists = fs16.existsSync(manifestPath);
78793
+ const localConfigExists = fs16.existsSync(clefConfigPath);
78421
78794
  if (manifestExists && localConfigExists) {
78422
78795
  formatter.print("Already initialised. Run 'clef update' to scaffold new environments.");
78423
78796
  process.exit(0);
@@ -78439,8 +78812,7 @@ function registerInitCommand(program3, deps2) {
78439
78812
  }
78440
78813
  await handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, options);
78441
78814
  } catch (err) {
78442
- formatter.error(err.message);
78443
- process.exit(1);
78815
+ handleCommandError(err);
78444
78816
  }
78445
78817
  });
78446
78818
  }
@@ -78472,22 +78844,22 @@ async function handleSecondDevOnboarding(repoRoot, clefConfigPath, deps2, option
78472
78844
  const publicKey = identity.publicKey;
78473
78845
  const keyContent = formatAgeKeyFile(privateKey, publicKey);
78474
78846
  const keyDir = path19.dirname(keyPath);
78475
- if (!fs17.existsSync(keyDir)) {
78476
- fs17.mkdirSync(keyDir, { recursive: true });
78847
+ if (!fs16.existsSync(keyDir)) {
78848
+ fs16.mkdirSync(keyDir, { recursive: true });
78477
78849
  }
78478
- fs17.writeFileSync(keyPath, keyContent, { encoding: "utf-8", mode: 384 });
78850
+ fs16.writeFileSync(keyPath, keyContent, { encoding: "utf-8", mode: 384 });
78479
78851
  formatter.success(`Generated age key at ${keyPath}`);
78480
78852
  config = { age_key_file: keyPath, age_key_storage: "file", age_keychain_label: label };
78481
78853
  }
78482
78854
  const clefDir = path19.dirname(clefConfigPath);
78483
- if (!fs17.existsSync(clefDir)) {
78484
- fs17.mkdirSync(clefDir, { recursive: true });
78855
+ if (!fs16.existsSync(clefDir)) {
78856
+ fs16.mkdirSync(clefDir, { recursive: true });
78485
78857
  }
78486
- fs17.writeFileSync(clefConfigPath, YAML12.stringify(config), "utf-8");
78858
+ fs16.writeFileSync(clefConfigPath, YAML11.stringify(config), "utf-8");
78487
78859
  formatter.success("Created .clef/config.yaml");
78488
78860
  const gitignorePath = path19.join(clefDir, ".gitignore");
78489
- if (!fs17.existsSync(gitignorePath)) {
78490
- fs17.writeFileSync(gitignorePath, "*\n", "utf-8");
78861
+ if (!fs16.existsSync(gitignorePath)) {
78862
+ fs16.writeFileSync(gitignorePath, "*\n", "utf-8");
78491
78863
  formatter.success("Created .clef/.gitignore");
78492
78864
  }
78493
78865
  formatter.success(`Key label: ${label}`);
@@ -78579,38 +78951,38 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
78579
78951
  }
78580
78952
  const keyContent = formatAgeKeyFile(privateKey, publicKey);
78581
78953
  const keyDir = path19.dirname(keyPath);
78582
- if (!fs17.existsSync(keyDir)) {
78583
- fs17.mkdirSync(keyDir, { recursive: true });
78954
+ if (!fs16.existsSync(keyDir)) {
78955
+ fs16.mkdirSync(keyDir, { recursive: true });
78584
78956
  }
78585
- fs17.writeFileSync(keyPath, keyContent, { encoding: "utf-8", mode: 384 });
78957
+ fs16.writeFileSync(keyPath, keyContent, { encoding: "utf-8", mode: 384 });
78586
78958
  formatter.success(`Generated age key at ${keyPath}`);
78587
78959
  ageKeyFile = keyPath;
78588
78960
  }
78589
78961
  const clefDir = path19.dirname(clefConfigPath);
78590
- if (!fs17.existsSync(clefDir)) {
78591
- fs17.mkdirSync(clefDir, { recursive: true });
78962
+ if (!fs16.existsSync(clefDir)) {
78963
+ fs16.mkdirSync(clefDir, { recursive: true });
78592
78964
  }
78593
78965
  const config = ageKeyFile ? { age_key_file: ageKeyFile, age_key_storage: "file", age_keychain_label: label } : { age_key_storage: "keychain", age_keychain_label: label };
78594
- fs17.writeFileSync(clefConfigPath, YAML12.stringify(config), "utf-8");
78966
+ fs16.writeFileSync(clefConfigPath, YAML11.stringify(config), "utf-8");
78595
78967
  formatter.success("Created .clef/config.yaml");
78596
78968
  const gitignorePath = path19.join(clefDir, ".gitignore");
78597
- if (!fs17.existsSync(gitignorePath)) {
78598
- fs17.writeFileSync(gitignorePath, "*\n", "utf-8");
78969
+ if (!fs16.existsSync(gitignorePath)) {
78970
+ fs16.writeFileSync(gitignorePath, "*\n", "utf-8");
78599
78971
  formatter.success("Created .clef/.gitignore");
78600
78972
  }
78601
78973
  formatter.success(`Key label: ${label}`);
78602
78974
  }
78603
- const manifestDoc = YAML12.parse(YAML12.stringify(manifest));
78975
+ const manifestDoc = YAML11.parse(YAML11.stringify(manifest));
78604
78976
  if (backend === "age" && publicKey) {
78605
78977
  const sopsDoc = manifestDoc.sops;
78606
78978
  sopsDoc.age = { recipients: [publicKey] };
78607
78979
  }
78608
- fs17.writeFileSync(manifestPath, YAML12.stringify(manifestDoc), "utf-8");
78980
+ fs16.writeFileSync(manifestPath, YAML11.stringify(manifestDoc), "utf-8");
78609
78981
  formatter.success("Created clef.yaml");
78610
78982
  {
78611
78983
  const sopsYamlPath = path19.join(repoRoot, ".sops.yaml");
78612
78984
  const sopsConfig = buildSopsYaml(manifest, repoRoot, publicKey);
78613
- fs17.writeFileSync(sopsYamlPath, YAML12.stringify(sopsConfig), "utf-8");
78985
+ fs16.writeFileSync(sopsYamlPath, YAML11.stringify(sopsConfig), "utf-8");
78614
78986
  formatter.success("Created .sops.yaml");
78615
78987
  }
78616
78988
  const sopsClient = new SopsClient(deps2.runner, ageKeyFile, ageKey);
@@ -78667,8 +79039,8 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
78667
79039
  }
78668
79040
  }
78669
79041
  const clefignorePath = path19.join(repoRoot, ".clefignore");
78670
- if (!fs17.existsSync(clefignorePath)) {
78671
- fs17.writeFileSync(clefignorePath, DEFAULT_CLEFIGNORE, "utf-8");
79042
+ if (!fs16.existsSync(clefignorePath)) {
79043
+ fs16.writeFileSync(clefignorePath, DEFAULT_CLEFIGNORE, "utf-8");
78672
79044
  formatter.success("Created .clefignore");
78673
79045
  } else {
78674
79046
  formatter.print(" .clefignore already exists \u2014 skipping");
@@ -78700,7 +79072,7 @@ function scaffoldSopsConfig(repoRoot) {
78700
79072
  agePublicKey = resolveAgePublicKeyFromEnvOrFile(repoRoot);
78701
79073
  }
78702
79074
  const sopsConfig = buildSopsYaml(manifest, repoRoot, agePublicKey);
78703
- fs17.writeFileSync(sopsYamlPath, YAML12.stringify(sopsConfig), "utf-8");
79075
+ fs16.writeFileSync(sopsYamlPath, YAML11.stringify(sopsConfig), "utf-8");
78704
79076
  }
78705
79077
  function buildSopsYaml(manifest, _repoRoot, agePublicKey) {
78706
79078
  const creationRules = [];
@@ -78764,9 +79136,9 @@ function resolveAgePublicKeyFromEnvOrFile(repoRoot) {
78764
79136
  if (match) return match[1];
78765
79137
  }
78766
79138
  const clefConfigPath = path19.join(repoRoot, CLEF_DIR, CLEF_CONFIG_FILENAME);
78767
- if (fs17.existsSync(clefConfigPath)) {
79139
+ if (fs16.existsSync(clefConfigPath)) {
78768
79140
  try {
78769
- const config = YAML12.parse(fs17.readFileSync(clefConfigPath, "utf-8"));
79141
+ const config = YAML11.parse(fs16.readFileSync(clefConfigPath, "utf-8"));
78770
79142
  if (config?.age_key_file) {
78771
79143
  const pubKey = extractAgePublicKey(config.age_key_file);
78772
79144
  if (pubKey) return pubKey;
@@ -78778,8 +79150,8 @@ function resolveAgePublicKeyFromEnvOrFile(repoRoot) {
78778
79150
  }
78779
79151
  function extractAgePublicKey(keyFilePath) {
78780
79152
  try {
78781
- if (!fs17.existsSync(keyFilePath)) return void 0;
78782
- const content = fs17.readFileSync(keyFilePath, "utf-8");
79153
+ if (!fs16.existsSync(keyFilePath)) return void 0;
79154
+ const content = fs16.readFileSync(keyFilePath, "utf-8");
78783
79155
  const match = content.match(/# public key: (age1[a-z0-9]+)/);
78784
79156
  return match ? match[1] : void 0;
78785
79157
  } catch {
@@ -78846,9 +79218,9 @@ var path21 = __toESM(require("path"));
78846
79218
  init_src();
78847
79219
 
78848
79220
  // src/age-credential.ts
78849
- var fs18 = __toESM(require("fs"));
79221
+ var fs17 = __toESM(require("fs"));
78850
79222
  var path20 = __toESM(require("path"));
78851
- var YAML13 = __toESM(require_dist());
79223
+ var YAML12 = __toESM(require_dist());
78852
79224
  init_src();
78853
79225
  var CLEF_DIR2 = ".clef";
78854
79226
  var CLEF_CONFIG_FILENAME2 = "config.yaml";
@@ -78913,7 +79285,7 @@ async function resolveAgePrivateKey(repoRoot, runner2) {
78913
79285
  const filePath = process.env.CLEF_AGE_KEY_FILE;
78914
79286
  if (!filePath) return null;
78915
79287
  try {
78916
- const content = fs18.readFileSync(filePath, "utf-8");
79288
+ const content = fs17.readFileSync(filePath, "utf-8");
78917
79289
  const match = content.match(AGE_SECRET_KEY_RE);
78918
79290
  return match ? match[1] : null;
78919
79291
  } catch (err) {
@@ -78925,7 +79297,7 @@ async function resolveAgePrivateKey(repoRoot, runner2) {
78925
79297
  }
78926
79298
  case "config-file": {
78927
79299
  try {
78928
- const content = fs18.readFileSync(credential.path, "utf-8");
79300
+ const content = fs17.readFileSync(credential.path, "utf-8");
78929
79301
  const match = content.match(AGE_SECRET_KEY_RE);
78930
79302
  return match ? match[1] : null;
78931
79303
  } catch (err) {
@@ -78940,8 +79312,8 @@ async function resolveAgePrivateKey(repoRoot, runner2) {
78940
79312
  function readLocalConfig(repoRoot) {
78941
79313
  const clefConfigPath = path20.join(repoRoot, CLEF_DIR2, CLEF_CONFIG_FILENAME2);
78942
79314
  try {
78943
- if (!fs18.existsSync(clefConfigPath)) return null;
78944
- return YAML13.parse(fs18.readFileSync(clefConfigPath, "utf-8"));
79315
+ if (!fs17.existsSync(clefConfigPath)) return null;
79316
+ return YAML12.parse(fs17.readFileSync(clefConfigPath, "utf-8"));
78945
79317
  } catch (err) {
78946
79318
  formatter.warn(
78947
79319
  `Failed to parse ${clefConfigPath}: ${err instanceof Error ? err.message : String(err)}
@@ -78983,6 +79355,15 @@ function maskedPlaceholder() {
78983
79355
  return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
78984
79356
  }
78985
79357
 
79358
+ // src/parse-target.ts
79359
+ function parseTarget(target) {
79360
+ const parts = target.split("/");
79361
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
79362
+ throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79363
+ }
79364
+ return [parts[0], parts[1]];
79365
+ }
79366
+
78986
79367
  // src/commands/get.ts
78987
79368
  function registerGetCommand(program3, deps2) {
78988
79369
  program3.command("get <target> <key>").description(
@@ -79018,23 +79399,10 @@ function registerGetCommand(program3, deps2) {
79018
79399
  }
79019
79400
  }
79020
79401
  } catch (err) {
79021
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79022
- formatter.formatDependencyError(err);
79023
- process.exit(1);
79024
- return;
79025
- }
79026
- formatter.error(err.message);
79027
- process.exit(1);
79402
+ handleCommandError(err);
79028
79403
  }
79029
79404
  });
79030
79405
  }
79031
- function parseTarget(target) {
79032
- const parts = target.split("/");
79033
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
79034
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79035
- }
79036
- return [parts[0], parts[1]];
79037
- }
79038
79406
 
79039
79407
  // src/commands/set.ts
79040
79408
  var path22 = __toESM(require("path"));
@@ -79149,7 +79517,7 @@ function registerSetCommand(program3, deps2) {
79149
79517
  Consider using the interactive prompt instead: clef set ${target} ${key}`
79150
79518
  );
79151
79519
  }
79152
- const [namespace, environment] = parseTarget2(target);
79520
+ const [namespace, environment] = parseTarget(target);
79153
79521
  const matrixManager = new MatrixManager();
79154
79522
  if (matrixManager.isProtectedEnvironment(manifest, environment)) {
79155
79523
  const confirmed = await formatter.confirm(
@@ -79181,11 +79549,22 @@ function registerSetCommand(program3, deps2) {
79181
79549
  try {
79182
79550
  await markPendingWithRetry(filePath, [key], "clef set --random");
79183
79551
  } catch {
79184
- formatter.error(
79185
- `${key} was encrypted but pending state could not be recorded.
79186
- The encrypted file contains a random placeholder value with no tracking metadata.
79552
+ try {
79553
+ delete decrypted.values[key];
79554
+ await sopsClient.encrypt(filePath, decrypted.values, manifest, environment);
79555
+ } catch {
79556
+ formatter.error(
79557
+ `${key} was encrypted but pending state could not be recorded, and rollback failed.
79558
+ The encrypted file may contain an untracked random placeholder.
79187
79559
  This key MUST be set to a real value before deploying.
79188
79560
  Run: clef set ${namespace}/${environment} ${key}`
79561
+ );
79562
+ process.exit(1);
79563
+ return;
79564
+ }
79565
+ formatter.error(
79566
+ `${key}: pending state could not be recorded. The value was rolled back.
79567
+ Retry: clef set --random ${namespace}/${environment} ${key}`
79189
79568
  );
79190
79569
  process.exit(1);
79191
79570
  return;
@@ -79210,24 +79589,11 @@ function registerSetCommand(program3, deps2) {
79210
79589
  );
79211
79590
  }
79212
79591
  } catch (err) {
79213
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79214
- formatter.formatDependencyError(err);
79215
- process.exit(1);
79216
- return;
79217
- }
79218
- formatter.error(err.message);
79219
- process.exit(1);
79592
+ handleCommandError(err);
79220
79593
  }
79221
79594
  }
79222
79595
  );
79223
79596
  }
79224
- function parseTarget2(target) {
79225
- const parts = target.split("/");
79226
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
79227
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79228
- }
79229
- return [parts[0], parts[1]];
79230
- }
79231
79597
 
79232
79598
  // src/commands/compare.ts
79233
79599
  var path23 = __toESM(require("path"));
@@ -79238,7 +79604,7 @@ function registerCompareCommand(program3, deps2) {
79238
79604
  "Compare a stored secret with a supplied value.\n\n target: namespace/environment (e.g. payments/staging)\n key: the key name to compare\n value: optional \u2014 if omitted, prompts with hidden input\n\nNeither value is ever printed to stdout.\n\nExit codes:\n 0 values match\n 1 values do not match or operation failed"
79239
79605
  ).action(async (target, key, value) => {
79240
79606
  try {
79241
- const [namespace, environment] = parseTarget3(target);
79607
+ const [namespace, environment] = parseTarget(target);
79242
79608
  const repoRoot = program3.opts().dir || process.cwd();
79243
79609
  const parser = new ManifestParser();
79244
79610
  const manifest = parser.parse(path23.join(repoRoot, "clef.yaml"));
@@ -79268,7 +79634,15 @@ function registerCompareCommand(program3, deps2) {
79268
79634
  return;
79269
79635
  }
79270
79636
  const stored = decrypted.values[key];
79271
- const match = stored.length === compareValue.length && crypto5.timingSafeEqual(Buffer.from(stored), Buffer.from(compareValue));
79637
+ const storedBuf = Buffer.from(stored);
79638
+ const compareBuf = Buffer.from(compareValue);
79639
+ const maxLen = Math.max(storedBuf.length, compareBuf.length, 1);
79640
+ const paddedStored = Buffer.alloc(maxLen);
79641
+ const paddedCompare = Buffer.alloc(maxLen);
79642
+ storedBuf.copy(paddedStored);
79643
+ compareBuf.copy(paddedCompare);
79644
+ const timingEqual = crypto5.timingSafeEqual(paddedStored, paddedCompare);
79645
+ const match = storedBuf.length === compareBuf.length && timingEqual;
79272
79646
  if (match) {
79273
79647
  formatter.success(`${key} ${sym("arrow")} values match`);
79274
79648
  } else {
@@ -79276,23 +79650,10 @@ function registerCompareCommand(program3, deps2) {
79276
79650
  process.exit(1);
79277
79651
  }
79278
79652
  } catch (err) {
79279
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79280
- formatter.formatDependencyError(err);
79281
- process.exit(1);
79282
- return;
79283
- }
79284
- formatter.error(err.message);
79285
- process.exit(1);
79653
+ handleCommandError(err);
79286
79654
  }
79287
79655
  });
79288
79656
  }
79289
- function parseTarget3(target) {
79290
- const parts = target.split("/");
79291
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
79292
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79293
- }
79294
- return [parts[0], parts[1]];
79295
- }
79296
79657
 
79297
79658
  // src/commands/delete.ts
79298
79659
  var path24 = __toESM(require("path"));
@@ -79324,7 +79685,7 @@ Type the key name to confirm:`
79324
79685
  await bulkOps.deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot);
79325
79686
  formatter.success(`Deleted '${key}' from ${namespace} in all environments`);
79326
79687
  } else {
79327
- const [namespace, environment] = parseTarget4(target);
79688
+ const [namespace, environment] = parseTarget(target);
79328
79689
  if (matrixManager.isProtectedEnvironment(manifest, environment)) {
79329
79690
  const protConfirmed = await formatter.confirm(
79330
79691
  `This is a protected environment (${environment}). Are you sure you want to delete '${key}'?`
@@ -79366,23 +79727,10 @@ Type the key name to confirm:`
79366
79727
  );
79367
79728
  }
79368
79729
  } catch (err) {
79369
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79370
- formatter.formatDependencyError(err);
79371
- process.exit(1);
79372
- return;
79373
- }
79374
- formatter.error(err.message);
79375
- process.exit(1);
79730
+ handleCommandError(err);
79376
79731
  }
79377
79732
  });
79378
79733
  }
79379
- function parseTarget4(target) {
79380
- const parts = target.split("/");
79381
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
79382
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79383
- }
79384
- return [parts[0], parts[1]];
79385
- }
79386
79734
 
79387
79735
  // src/commands/diff.ts
79388
79736
  var path25 = __toESM(require("path"));
@@ -79437,13 +79785,7 @@ function registerDiffCommand(program3, deps2) {
79437
79785
  const hasDiffs = result.rows.some((r) => r.status !== "identical");
79438
79786
  process.exit(hasDiffs ? 1 : 0);
79439
79787
  } catch (err) {
79440
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79441
- formatter.formatDependencyError(err);
79442
- process.exit(1);
79443
- return;
79444
- }
79445
- formatter.error(err.message);
79446
- process.exit(1);
79788
+ handleCommandError(err);
79447
79789
  }
79448
79790
  }
79449
79791
  );
@@ -79730,7 +80072,7 @@ async function fetchCheckpoint(config) {
79730
80072
  }
79731
80073
 
79732
80074
  // package.json
79733
- var version = "0.1.8";
80075
+ var version = "0.1.9-beta.57";
79734
80076
  var package_default = {
79735
80077
  name: "@clef-sh/cli",
79736
80078
  version,
@@ -79835,13 +80177,7 @@ function registerLintCommand(program3, deps2) {
79835
80177
  const hasErrors = result.issues.some((i) => i.severity === "error");
79836
80178
  process.exit(hasErrors ? 1 : 0);
79837
80179
  } catch (err) {
79838
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79839
- formatter.formatDependencyError(err);
79840
- process.exit(1);
79841
- return;
79842
- }
79843
- formatter.error(err.message);
79844
- process.exit(1);
80180
+ handleCommandError(err);
79845
80181
  }
79846
80182
  });
79847
80183
  }
@@ -79911,7 +80247,7 @@ function registerRotateCommand(program3, deps2) {
79911
80247
  "Rotate encryption key for a namespace/environment file.\n\n target: namespace/environment (e.g. payments/production)\n --new-key: the new age public key to add (required)\n\nExit codes:\n 0 key rotated successfully\n 1 operation failed"
79912
80248
  ).requiredOption("--new-key <key>", "New age public key to rotate to").action(async (target, options) => {
79913
80249
  try {
79914
- const [namespace, environment] = parseTarget5(target);
80250
+ const [namespace, environment] = parseTarget(target);
79915
80251
  const repoRoot = program3.opts().dir || process.cwd();
79916
80252
  const parser = new ManifestParser();
79917
80253
  const manifest = parser.parse(path27.join(repoRoot, "clef.yaml"));
@@ -79938,26 +80274,13 @@ function registerRotateCommand(program3, deps2) {
79938
80274
  `git add ${relativeFile} && git commit -m "rotate: ${namespace}/${environment}"`
79939
80275
  );
79940
80276
  } catch (err) {
79941
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
79942
- formatter.formatDependencyError(err);
79943
- process.exit(1);
79944
- return;
79945
- }
79946
- formatter.error(err.message);
79947
- process.exit(1);
80277
+ handleCommandError(err);
79948
80278
  }
79949
80279
  });
79950
80280
  }
79951
- function parseTarget5(target) {
79952
- const parts = target.split("/");
79953
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
79954
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
79955
- }
79956
- return [parts[0], parts[1]];
79957
- }
79958
80281
 
79959
80282
  // src/commands/hooks.ts
79960
- var fs19 = __toESM(require("fs"));
80283
+ var fs18 = __toESM(require("fs"));
79961
80284
  var path28 = __toESM(require("path"));
79962
80285
  init_src();
79963
80286
  function registerHooksCommand(program3, deps2) {
@@ -79966,8 +80289,8 @@ function registerHooksCommand(program3, deps2) {
79966
80289
  try {
79967
80290
  const repoRoot = program3.opts().dir || process.cwd();
79968
80291
  const hookPath = path28.join(repoRoot, ".git", "hooks", "pre-commit");
79969
- if (fs19.existsSync(hookPath)) {
79970
- const content = fs19.readFileSync(hookPath, "utf-8");
80292
+ if (fs18.existsSync(hookPath)) {
80293
+ const content = fs18.readFileSync(hookPath, "utf-8");
79971
80294
  if (content.includes("clef") || content.includes("SOPS")) {
79972
80295
  const confirmed = await formatter.confirm(
79973
80296
  "A Clef pre-commit hook already exists. Overwrite?"
@@ -80097,6 +80420,7 @@ async function openBrowser(url, runner2) {
80097
80420
  }
80098
80421
 
80099
80422
  // src/commands/exec.ts
80423
+ var os2 = __toESM(require("os"));
80100
80424
  var path30 = __toESM(require("path"));
80101
80425
  var import_child_process3 = require("child_process");
80102
80426
  init_src();
@@ -80133,7 +80457,7 @@ function registerExecCommand(program3, deps2) {
80133
80457
  process.exit(1);
80134
80458
  return;
80135
80459
  }
80136
- const [namespace, environment] = parseTarget6(target);
80460
+ const [namespace, environment] = parseTarget(target);
80137
80461
  const repoRoot = program3.opts().dir || process.cwd();
80138
80462
  const parser = new ManifestParser();
80139
80463
  const manifest = parser.parse(path30.join(repoRoot, "clef.yaml"));
@@ -80150,7 +80474,7 @@ function registerExecCommand(program3, deps2) {
80150
80474
  const mergedValues = { ...primaryDecrypted.values };
80151
80475
  for (const alsoTarget of options.also) {
80152
80476
  try {
80153
- const [alsoNs, alsoEnv] = parseTarget6(alsoTarget);
80477
+ const [alsoNs, alsoEnv] = parseTarget(alsoTarget);
80154
80478
  const alsoFilePath = path30.join(
80155
80479
  repoRoot,
80156
80480
  manifest.file_pattern.replace("{namespace}", alsoNs).replace("{environment}", alsoEnv)
@@ -80187,14 +80511,7 @@ function registerExecCommand(program3, deps2) {
80187
80511
  const exitCode = await spawnChild(childCommand, childCommandArgs, childEnv);
80188
80512
  process.exit(exitCode);
80189
80513
  } catch (err) {
80190
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
80191
- formatter.formatDependencyError(err);
80192
- process.exit(1);
80193
- return;
80194
- }
80195
- const message = err instanceof Error ? err.message : "Execution failed";
80196
- formatter.error(message);
80197
- process.exit(1);
80514
+ handleCommandError(err);
80198
80515
  }
80199
80516
  }
80200
80517
  );
@@ -80220,12 +80537,8 @@ function spawnChild(command, args, env) {
80220
80537
  process.off("SIGTERM", sigtermHandler);
80221
80538
  process.off("SIGINT", sigintHandler);
80222
80539
  if (signal) {
80223
- const signalCodes = {
80224
- SIGHUP: 129,
80225
- SIGINT: 130,
80226
- SIGTERM: 143
80227
- };
80228
- resolve6(signalCodes[signal] ?? 128);
80540
+ const sigNum = os2.constants?.signals?.[signal];
80541
+ resolve6(sigNum ? 128 + sigNum : 128);
80229
80542
  } else {
80230
80543
  resolve6(code ?? 1);
80231
80544
  }
@@ -80243,13 +80556,6 @@ function spawnChild(command, args, env) {
80243
80556
  process.on("SIGINT", sigintHandler);
80244
80557
  });
80245
80558
  }
80246
- function parseTarget6(target) {
80247
- const parts = target.split("/");
80248
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
80249
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
80250
- }
80251
- return [parts[0], parts[1]];
80252
- }
80253
80559
 
80254
80560
  // src/commands/export.ts
80255
80561
  var path31 = __toESM(require("path"));
@@ -80278,7 +80584,7 @@ Usage: clef export payments/production --format env`
80278
80584
  process.exit(1);
80279
80585
  return;
80280
80586
  }
80281
- const [namespace, environment] = parseTarget7(target);
80587
+ const [namespace, environment] = parseTarget(target);
80282
80588
  const repoRoot = program3.opts().dir || process.cwd();
80283
80589
  const parser = new ManifestParser();
80284
80590
  const manifest = parser.parse(path31.join(repoRoot, "clef.yaml"));
@@ -80313,29 +80619,15 @@ Usage: clef export payments/production --format env`
80313
80619
  }
80314
80620
  }
80315
80621
  } catch (err) {
80316
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
80317
- formatter.formatDependencyError(err);
80318
- process.exit(1);
80319
- return;
80320
- }
80321
- const message = err instanceof Error ? err.message : "Export failed";
80322
- formatter.error(message);
80323
- process.exit(1);
80622
+ handleCommandError(err);
80324
80623
  }
80325
80624
  });
80326
80625
  }
80327
- function parseTarget7(target) {
80328
- const parts = target.split("/");
80329
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
80330
- throw new Error(`Invalid target "${target}". Expected format: namespace/environment`);
80331
- }
80332
- return [parts[0], parts[1]];
80333
- }
80334
80626
 
80335
80627
  // src/commands/doctor.ts
80336
- var fs20 = __toESM(require("fs"));
80628
+ var fs19 = __toESM(require("fs"));
80337
80629
  var path32 = __toESM(require("path"));
80338
- var YAML14 = __toESM(require_dist());
80630
+ var YAML13 = __toESM(require_dist());
80339
80631
  init_src();
80340
80632
  function registerDoctorCommand(program3, deps2) {
80341
80633
  program3.command("doctor").description(
@@ -80385,7 +80677,7 @@ function registerDoctorCommand(program3, deps2) {
80385
80677
  });
80386
80678
  }
80387
80679
  const manifestPath = path32.join(repoRoot, "clef.yaml");
80388
- const manifestFound = fs20.existsSync(manifestPath);
80680
+ const manifestFound = fs19.existsSync(manifestPath);
80389
80681
  checks.push({
80390
80682
  name: "manifest",
80391
80683
  ok: manifestFound,
@@ -80395,7 +80687,7 @@ function registerDoctorCommand(program3, deps2) {
80395
80687
  const ageKeyResult = await checkAgeKey(repoRoot, deps2.runner);
80396
80688
  checks.push(ageKeyResult);
80397
80689
  const sopsYamlPath = path32.join(repoRoot, ".sops.yaml");
80398
- const sopsYamlFound = fs20.existsSync(sopsYamlPath);
80690
+ const sopsYamlFound = fs19.existsSync(sopsYamlPath);
80399
80691
  checks.push({
80400
80692
  name: ".sops.yaml",
80401
80693
  ok: sopsYamlFound,
@@ -80403,11 +80695,11 @@ function registerDoctorCommand(program3, deps2) {
80403
80695
  hint: sopsYamlFound ? void 0 : "run: clef init"
80404
80696
  });
80405
80697
  const clefignorePath = path32.join(repoRoot, ".clefignore");
80406
- const clefignoreFound = fs20.existsSync(clefignorePath);
80698
+ const clefignoreFound = fs19.existsSync(clefignorePath);
80407
80699
  let clefignoreRuleCount = 0;
80408
80700
  if (clefignoreFound) {
80409
80701
  try {
80410
- const content = fs20.readFileSync(clefignorePath, "utf-8");
80702
+ const content = fs19.readFileSync(clefignorePath, "utf-8");
80411
80703
  clefignoreRuleCount = content.split("\n").filter(
80412
80704
  (l) => l.trim() && !l.trim().startsWith("#") && !l.trim().startsWith("ignore-pattern:")
80413
80705
  ).length;
@@ -80439,7 +80731,7 @@ function registerDoctorCommand(program3, deps2) {
80439
80731
  formatter.info("Attempting to fix: generating .sops.yaml from manifest...");
80440
80732
  try {
80441
80733
  scaffoldSopsConfig(repoRoot);
80442
- const nowFound = fs20.existsSync(sopsYamlPath);
80734
+ const nowFound = fs19.existsSync(sopsYamlPath);
80443
80735
  if (nowFound) {
80444
80736
  const sopsYamlCheck = checks.find((c) => c.name === ".sops.yaml");
80445
80737
  if (sopsYamlCheck) {
@@ -80563,7 +80855,7 @@ async function checkAgeKey(repoRoot, runner2) {
80563
80855
  source: "env"
80564
80856
  };
80565
80857
  case "config-file":
80566
- if (!fs20.existsSync(credential.path)) {
80858
+ if (!fs19.existsSync(credential.path)) {
80567
80859
  return {
80568
80860
  name: "age key",
80569
80861
  ok: false,
@@ -80582,9 +80874,9 @@ async function checkAgeKey(repoRoot, runner2) {
80582
80874
  }
80583
80875
  function countAgeRecipients(sopsYamlPath) {
80584
80876
  try {
80585
- if (!fs20.existsSync(sopsYamlPath)) return 0;
80586
- const content = fs20.readFileSync(sopsYamlPath, "utf-8");
80587
- const config = YAML14.parse(content);
80877
+ if (!fs19.existsSync(sopsYamlPath)) return 0;
80878
+ const content = fs19.readFileSync(sopsYamlPath, "utf-8");
80879
+ const config = YAML13.parse(content);
80588
80880
  if (!config?.creation_rules || !Array.isArray(config.creation_rules)) {
80589
80881
  return 0;
80590
80882
  }
@@ -80610,7 +80902,7 @@ function getSopsInstallHint() {
80610
80902
  }
80611
80903
 
80612
80904
  // src/commands/update.ts
80613
- var fs21 = __toESM(require("fs"));
80905
+ var fs20 = __toESM(require("fs"));
80614
80906
  var path33 = __toESM(require("path"));
80615
80907
  init_src();
80616
80908
  function registerUpdateCommand(program3, deps2) {
@@ -80620,7 +80912,7 @@ function registerUpdateCommand(program3, deps2) {
80620
80912
  try {
80621
80913
  const repoRoot = program3.opts().dir || process.cwd();
80622
80914
  const manifestPath = path33.join(repoRoot, "clef.yaml");
80623
- if (!fs21.existsSync(manifestPath)) {
80915
+ if (!fs20.existsSync(manifestPath)) {
80624
80916
  formatter.error("clef.yaml not found. Run 'clef init' to initialise this repository.");
80625
80917
  process.exit(1);
80626
80918
  return;
@@ -80654,19 +80946,12 @@ function registerUpdateCommand(program3, deps2) {
80654
80946
  formatter.success(`Scaffolded ${scaffoldedCount} encrypted file(s)`);
80655
80947
  }
80656
80948
  if (failedCount === 0) {
80657
- process.exit(0);
80658
80949
  return;
80659
80950
  }
80660
80951
  formatter.error(`${failedCount} cell(s) could not be scaffolded.`);
80661
80952
  process.exit(1);
80662
80953
  } catch (err) {
80663
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
80664
- formatter.formatDependencyError(err);
80665
- process.exit(1);
80666
- return;
80667
- }
80668
- formatter.error(err.message);
80669
- process.exit(1);
80954
+ handleCommandError(err);
80670
80955
  }
80671
80956
  });
80672
80957
  }
@@ -80791,7 +81076,7 @@ function formatScanOutput(result) {
80791
81076
  }
80792
81077
 
80793
81078
  // src/commands/import.ts
80794
- var fs22 = __toESM(require("fs"));
81079
+ var fs21 = __toESM(require("fs"));
80795
81080
  var path35 = __toESM(require("path"));
80796
81081
  init_src();
80797
81082
  async function readStdin() {
@@ -80853,13 +81138,13 @@ function registerImportCommand(program3, deps2) {
80853
81138
  if (opts.stdin) {
80854
81139
  content = await readStdin();
80855
81140
  } else if (source) {
80856
- if (!fs22.existsSync(source)) {
81141
+ if (!fs21.existsSync(source)) {
80857
81142
  formatter.error(`Source file not found: ${source}`);
80858
81143
  process.exit(2);
80859
81144
  return;
80860
81145
  }
80861
81146
  try {
80862
- content = fs22.readFileSync(source, "utf-8");
81147
+ content = fs21.readFileSync(source, "utf-8");
80863
81148
  sourcePath = source;
80864
81149
  } catch (err) {
80865
81150
  formatter.error(`Could not read source file: ${err.message}`);
@@ -80947,13 +81232,7 @@ ${result.imported.length} imported, ${result.skipped.length} skipped, ${result.f
80947
81232
  }
80948
81233
  }
80949
81234
  } catch (err) {
80950
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
80951
- formatter.formatDependencyError(err);
80952
- process.exit(1);
80953
- return;
80954
- }
80955
- formatter.error(err.message);
80956
- process.exit(1);
81235
+ handleCommandError(err);
80957
81236
  }
80958
81237
  }
80959
81238
  );
@@ -81012,13 +81291,7 @@ function registerRecipientsCommand(program3, deps2) {
81012
81291
  formatter.recipientItem(r.label || r.preview, r.label ? r.preview : "");
81013
81292
  }
81014
81293
  } catch (err) {
81015
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81016
- formatter.formatDependencyError(err);
81017
- process.exit(1);
81018
- return;
81019
- }
81020
- formatter.error(err.message);
81021
- process.exit(1);
81294
+ handleCommandError(err);
81022
81295
  }
81023
81296
  });
81024
81297
  recipientsCmd.command("add <key>").description(
@@ -81048,13 +81321,7 @@ function registerRecipientsCommand(program3, deps2) {
81048
81321
  );
81049
81322
  }
81050
81323
  } catch (err) {
81051
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81052
- formatter.formatDependencyError(err);
81053
- process.exit(1);
81054
- return;
81055
- }
81056
- formatter.error(err.message);
81057
- process.exit(1);
81324
+ handleCommandError(err);
81058
81325
  }
81059
81326
  });
81060
81327
  recipientsCmd.command("remove <key>").description("Remove an age recipient and re-encrypt all files in the matrix.").option("-e, --environment <env>", "Scope removal to a specific environment").action(async (key, opts) => {
@@ -81162,13 +81429,7 @@ ${sym("failure")} Re-encryption failed on ${path36.basename(failedFile)}`
81162
81429
  `git add clef.yaml && git add -A && git commit -m "remove recipient: ${label}${envSuffix}"`
81163
81430
  );
81164
81431
  } catch (err) {
81165
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81166
- formatter.formatDependencyError(err);
81167
- process.exit(1);
81168
- return;
81169
- }
81170
- formatter.error(err.message);
81171
- process.exit(1);
81432
+ handleCommandError(err);
81172
81433
  }
81173
81434
  });
81174
81435
  recipientsCmd.command("request").description(
@@ -81286,13 +81547,7 @@ ${sym("failure")} Re-encryption failed on ${path36.basename(failedFile)}`
81286
81547
  );
81287
81548
  }
81288
81549
  } catch (err) {
81289
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81290
- formatter.formatDependencyError(err);
81291
- process.exit(1);
81292
- return;
81293
- }
81294
- formatter.error(err.message);
81295
- process.exit(1);
81550
+ handleCommandError(err);
81296
81551
  }
81297
81552
  });
81298
81553
  }
@@ -81362,14 +81617,14 @@ ${sym("failure")} Re-encryption failed on ${path36.basename(failedFile)}`);
81362
81617
  }
81363
81618
 
81364
81619
  // src/commands/merge-driver.ts
81365
- var fs23 = __toESM(require("fs"));
81620
+ var fs22 = __toESM(require("fs"));
81366
81621
  var path37 = __toESM(require("path"));
81367
81622
  init_src();
81368
81623
  async function findRepoRoot(filePath, runner2) {
81369
81624
  try {
81370
81625
  let dir = path37.dirname(path37.resolve(filePath));
81371
81626
  for (let i = 0; i < 50; i++) {
81372
- if (fs23.existsSync(path37.join(dir, "clef.yaml"))) return dir;
81627
+ if (fs22.existsSync(path37.join(dir, "clef.yaml"))) return dir;
81373
81628
  const parent = path37.dirname(dir);
81374
81629
  if (parent === dir) break;
81375
81630
  dir = parent;
@@ -81402,7 +81657,7 @@ function registerMergeDriverCommand(program3, deps2) {
81402
81657
  const manifestPath = path37.join(repoRoot, "clef.yaml");
81403
81658
  let manifest;
81404
81659
  let environment;
81405
- if (fs23.existsSync(manifestPath)) {
81660
+ if (fs22.existsSync(manifestPath)) {
81406
81661
  const parser = new ManifestParser();
81407
81662
  manifest = parser.parse(manifestPath);
81408
81663
  for (const ns of manifest.namespaces) {
@@ -81496,40 +81751,7 @@ function registerServiceCommand(program3, deps2) {
81496
81751
  const parser = new ManifestParser();
81497
81752
  const manifest = parser.parse(path38.join(repoRoot, "clef.yaml"));
81498
81753
  const namespaces = opts.namespaces.split(",").map((s) => s.trim());
81499
- let kmsEnvConfigs;
81500
- if (opts.kmsEnv.length > 0) {
81501
- kmsEnvConfigs = {};
81502
- for (const mapping of opts.kmsEnv) {
81503
- const eqIdx = mapping.indexOf("=");
81504
- if (eqIdx === -1) {
81505
- throw new Error(
81506
- `Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`
81507
- );
81508
- }
81509
- const envName = mapping.slice(0, eqIdx);
81510
- const rest = mapping.slice(eqIdx + 1);
81511
- const colonIdx = rest.indexOf(":");
81512
- if (colonIdx === -1) {
81513
- throw new Error(
81514
- `Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`
81515
- );
81516
- }
81517
- const provider = rest.slice(0, colonIdx);
81518
- const keyId = rest.slice(colonIdx + 1);
81519
- if (!["aws", "gcp", "azure"].includes(provider)) {
81520
- throw new Error(
81521
- `Invalid KMS provider '${provider}'. Must be one of: aws, gcp, azure.`
81522
- );
81523
- }
81524
- if (kmsEnvConfigs[envName]) {
81525
- throw new Error(`Duplicate --kms-env for environment '${envName}'.`);
81526
- }
81527
- kmsEnvConfigs[envName] = {
81528
- provider,
81529
- keyId
81530
- };
81531
- }
81532
- }
81754
+ const kmsEnvConfigs = opts.kmsEnv.length > 0 ? parseKmsEnvMappings(opts.kmsEnv) : void 0;
81533
81755
  const hasAgeEnvs = !kmsEnvConfigs || manifest.environments.some((e) => !kmsEnvConfigs[e.name]);
81534
81756
  const protectedEnvs = manifest.environments.filter((e) => e.protected).map((e) => e.name);
81535
81757
  if (protectedEnvs.length > 0 && hasAgeEnvs) {
@@ -81594,13 +81816,7 @@ function registerServiceCommand(program3, deps2) {
81594
81816
  `git add clef.yaml && git commit -m "feat: add service identity '${name}'"`
81595
81817
  );
81596
81818
  } catch (err) {
81597
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81598
- formatter.formatDependencyError(err);
81599
- process.exit(1);
81600
- return;
81601
- }
81602
- formatter.error(err.message);
81603
- process.exit(1);
81819
+ handleCommandError(err);
81604
81820
  }
81605
81821
  }
81606
81822
  );
@@ -81625,13 +81841,7 @@ function registerServiceCommand(program3, deps2) {
81625
81841
  });
81626
81842
  formatter.table(rows, ["Name", "Namespaces", "Environments"]);
81627
81843
  } catch (err) {
81628
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81629
- formatter.formatDependencyError(err);
81630
- process.exit(1);
81631
- return;
81632
- }
81633
- formatter.error(err.message);
81634
- process.exit(1);
81844
+ handleCommandError(err);
81635
81845
  }
81636
81846
  });
81637
81847
  serviceCmd.command("show <name>").description("Show details of a service identity.").action(async (name) => {
@@ -81664,13 +81874,7 @@ Service Identity: ${identity.name}`);
81664
81874
  }
81665
81875
  formatter.print("");
81666
81876
  } catch (err) {
81667
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81668
- formatter.formatDependencyError(err);
81669
- process.exit(1);
81670
- return;
81671
- }
81672
- formatter.error(err.message);
81673
- process.exit(1);
81877
+ handleCommandError(err);
81674
81878
  }
81675
81879
  });
81676
81880
  serviceCmd.command("validate").description("Validate service identity configurations and report drift issues.").action(async () => {
@@ -81705,13 +81909,7 @@ Service Identity: ${identity.name}`);
81705
81909
  formatter.warn(`${warnCount} warning(s)`);
81706
81910
  }
81707
81911
  } catch (err) {
81708
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81709
- formatter.formatDependencyError(err);
81710
- process.exit(1);
81711
- return;
81712
- }
81713
- formatter.error(err.message);
81714
- process.exit(1);
81912
+ handleCommandError(err);
81715
81913
  }
81716
81914
  });
81717
81915
  serviceCmd.command("update <name>").description("Update an existing service identity's environment backends.").option(
@@ -81732,31 +81930,7 @@ Service Identity: ${identity.name}`);
81732
81930
  const repoRoot = program3.opts().dir || process.cwd();
81733
81931
  const parser = new ManifestParser();
81734
81932
  const manifest = parser.parse(path38.join(repoRoot, "clef.yaml"));
81735
- const kmsEnvConfigs = {};
81736
- for (const mapping of opts.kmsEnv) {
81737
- const eqIdx = mapping.indexOf("=");
81738
- if (eqIdx === -1) {
81739
- throw new Error(`Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`);
81740
- }
81741
- const envName = mapping.slice(0, eqIdx);
81742
- const rest = mapping.slice(eqIdx + 1);
81743
- const colonIdx = rest.indexOf(":");
81744
- if (colonIdx === -1) {
81745
- throw new Error(`Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`);
81746
- }
81747
- const provider = rest.slice(0, colonIdx);
81748
- const keyId = rest.slice(colonIdx + 1);
81749
- if (!["aws", "gcp", "azure"].includes(provider)) {
81750
- throw new Error(`Invalid KMS provider '${provider}'. Must be one of: aws, gcp, azure.`);
81751
- }
81752
- if (kmsEnvConfigs[envName]) {
81753
- throw new Error(`Duplicate --kms-env for environment '${envName}'.`);
81754
- }
81755
- kmsEnvConfigs[envName] = {
81756
- provider,
81757
- keyId
81758
- };
81759
- }
81933
+ const kmsEnvConfigs = parseKmsEnvMappings(opts.kmsEnv);
81760
81934
  const matrixManager = new MatrixManager();
81761
81935
  const sopsClient = await createSopsClient(repoRoot, deps2.runner);
81762
81936
  const manager = new ServiceIdentityManager(sopsClient, matrixManager);
@@ -81770,13 +81944,7 @@ Service Identity: ${identity.name}`);
81770
81944
  `git add clef.yaml && git commit -m "chore: update service identity '${name}'"`
81771
81945
  );
81772
81946
  } catch (err) {
81773
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81774
- formatter.formatDependencyError(err);
81775
- process.exit(1);
81776
- return;
81777
- }
81778
- formatter.error(err.message);
81779
- process.exit(1);
81947
+ handleCommandError(err);
81780
81948
  }
81781
81949
  });
81782
81950
  serviceCmd.command("delete <name>").description("Delete a service identity and remove its recipients from scoped files.").action(async (name) => {
@@ -81808,13 +81976,7 @@ Service Identity: ${identity.name}`);
81808
81976
  `git add clef.yaml && git commit -m "chore: delete service identity '${name}'"`
81809
81977
  );
81810
81978
  } catch (err) {
81811
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81812
- formatter.formatDependencyError(err);
81813
- process.exit(1);
81814
- return;
81815
- }
81816
- formatter.error(err.message);
81817
- process.exit(1);
81979
+ handleCommandError(err);
81818
81980
  }
81819
81981
  });
81820
81982
  serviceCmd.command("rotate <name>").description("Rotate the age key for a service identity.").option("-e, --environment <env>", "Rotate only a specific environment").action(async (name, opts) => {
@@ -81868,11 +82030,6 @@ Service Identity: ${identity.name}`);
81868
82030
  `git add clef.yaml && git commit -m "chore: rotate service identity '${name}'"`
81869
82031
  );
81870
82032
  } catch (err) {
81871
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81872
- formatter.formatDependencyError(err);
81873
- process.exit(1);
81874
- return;
81875
- }
81876
82033
  if (err instanceof PartialRotationError) {
81877
82034
  formatter.error(err.message);
81878
82035
  const partialEntries = Object.entries(err.rotatedKeys);
@@ -81905,11 +82062,37 @@ Service Identity: ${identity.name}`);
81905
82062
  process.exit(1);
81906
82063
  return;
81907
82064
  }
81908
- formatter.error(err.message);
81909
- process.exit(1);
82065
+ handleCommandError(err);
81910
82066
  }
81911
82067
  });
81912
82068
  }
82069
+ function parseKmsEnvMappings(mappings) {
82070
+ const configs = {};
82071
+ for (const mapping of mappings) {
82072
+ const eqIdx = mapping.indexOf("=");
82073
+ if (eqIdx === -1) {
82074
+ throw new Error(`Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`);
82075
+ }
82076
+ const envName = mapping.slice(0, eqIdx);
82077
+ const rest = mapping.slice(eqIdx + 1);
82078
+ const colonIdx = rest.indexOf(":");
82079
+ if (colonIdx === -1) {
82080
+ throw new Error(`Invalid --kms-env format: '${mapping}'. Expected: env=provider:keyId`);
82081
+ }
82082
+ const provider = rest.slice(0, colonIdx);
82083
+ const keyId = rest.slice(colonIdx + 1);
82084
+ if (!VALID_KMS_PROVIDERS.includes(provider)) {
82085
+ throw new Error(
82086
+ `Invalid KMS provider '${provider}'. Must be one of: ${VALID_KMS_PROVIDERS.join(", ")}.`
82087
+ );
82088
+ }
82089
+ if (configs[envName]) {
82090
+ throw new Error(`Duplicate --kms-env for environment '${envName}'.`);
82091
+ }
82092
+ configs[envName] = { provider, keyId };
82093
+ }
82094
+ return configs;
82095
+ }
81913
82096
 
81914
82097
  // src/commands/pack.ts
81915
82098
  var path39 = __toESM(require("path"));
@@ -81983,21 +82166,14 @@ function registerPackCommand(program3, deps2) {
81983
82166
  "\nUpload the artifact to an HTTP-accessible store (S3, GCS, etc.) or commit to\n .clef/packed/ for VCS-based delivery. See: clef.sh/guide/service-identities"
81984
82167
  );
81985
82168
  } catch (err) {
81986
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
81987
- formatter.formatDependencyError(err);
81988
- process.exit(1);
81989
- return;
81990
- }
81991
- const message = err instanceof Error ? err.message : "Pack failed";
81992
- formatter.error(message);
81993
- process.exit(1);
82169
+ handleCommandError(err);
81994
82170
  }
81995
82171
  }
81996
82172
  );
81997
82173
  }
81998
82174
 
81999
82175
  // src/commands/revoke.ts
82000
- var fs24 = __toESM(require("fs"));
82176
+ var fs23 = __toESM(require("fs"));
82001
82177
  var path40 = __toESM(require("path"));
82002
82178
  init_src();
82003
82179
  function registerRevokeCommand(program3, _deps) {
@@ -82031,8 +82207,8 @@ function registerRevokeCommand(program3, _deps) {
82031
82207
  environment,
82032
82208
  revokedAt: (/* @__PURE__ */ new Date()).toISOString()
82033
82209
  };
82034
- fs24.mkdirSync(artifactDir, { recursive: true });
82035
- fs24.writeFileSync(artifactPath, JSON.stringify(revoked, null, 2) + "\n", "utf-8");
82210
+ fs23.mkdirSync(artifactDir, { recursive: true });
82211
+ fs23.writeFileSync(artifactPath, JSON.stringify(revoked, null, 2) + "\n", "utf-8");
82036
82212
  const relPath = path40.relative(repoRoot, artifactPath);
82037
82213
  formatter.success(`Artifact revoked: ${relPath}`);
82038
82214
  formatter.print("");
@@ -82134,12 +82310,12 @@ var import_picocolors6 = __toESM(require_picocolors());
82134
82310
  init_src();
82135
82311
 
82136
82312
  // src/report/historical.ts
82137
- var os2 = __toESM(require("os"));
82313
+ var os3 = __toESM(require("os"));
82138
82314
  var path42 = __toESM(require("path"));
82139
- var fs25 = __toESM(require("fs"));
82315
+ var fs24 = __toESM(require("fs"));
82140
82316
  init_src();
82141
82317
  async function generateReportAtCommit(repoRoot, commitSha, clefVersion, runner2) {
82142
- const tmpDir = path42.join(os2.tmpdir(), `clef-report-${commitSha.slice(0, 8)}-${Date.now()}`);
82318
+ const tmpDir = path42.join(os3.tmpdir(), `clef-report-${commitSha.slice(0, 8)}-${Date.now()}`);
82143
82319
  try {
82144
82320
  const addResult = await runner2.run("git", ["worktree", "add", tmpDir, commitSha, "--detach"], {
82145
82321
  cwd: repoRoot
@@ -82157,7 +82333,7 @@ async function generateReportAtCommit(repoRoot, commitSha, clefVersion, runner2)
82157
82333
  await runner2.run("git", ["worktree", "remove", tmpDir, "--force"], { cwd: repoRoot });
82158
82334
  } catch {
82159
82335
  try {
82160
- fs25.rmSync(tmpDir, { recursive: true, force: true });
82336
+ fs24.rmSync(tmpDir, { recursive: true, force: true });
82161
82337
  await runner2.run("git", ["worktree", "prune"], { cwd: repoRoot });
82162
82338
  } catch {
82163
82339
  }
@@ -82247,13 +82423,7 @@ function registerReportCommand(program3, deps2) {
82247
82423
  }
82248
82424
  outputReport(headReport, options);
82249
82425
  } catch (err) {
82250
- if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
82251
- formatter.formatDependencyError(err);
82252
- process.exit(1);
82253
- return;
82254
- }
82255
- formatter.error(err.message);
82256
- process.exit(1);
82426
+ handleCommandError(err);
82257
82427
  }
82258
82428
  }
82259
82429
  );
@@ -82384,7 +82554,7 @@ function formatReportOutput(report) {
82384
82554
  }
82385
82555
 
82386
82556
  // src/commands/install.ts
82387
- var fs26 = __toESM(require("fs"));
82557
+ var fs25 = __toESM(require("fs"));
82388
82558
  var path43 = __toESM(require("path"));
82389
82559
  var import_yaml = __toESM(require_dist());
82390
82560
 
@@ -82434,7 +82604,7 @@ function registerInstallCommand(program3, _deps) {
82434
82604
  return;
82435
82605
  }
82436
82606
  const brokerDir = path43.join(repoRoot, "brokers", entry.name);
82437
- if (fs26.existsSync(brokerDir) && !options.force) {
82607
+ if (fs25.existsSync(brokerDir) && !options.force) {
82438
82608
  const overwrite = await formatter.confirm(
82439
82609
  `brokers/${entry.name}/ already exists. Overwrite?`
82440
82610
  );
@@ -82465,11 +82635,11 @@ function registerInstallCommand(program3, _deps) {
82465
82635
  process.exit(1);
82466
82636
  return;
82467
82637
  }
82468
- if (!fs26.existsSync(brokerDir)) {
82469
- fs26.mkdirSync(brokerDir, { recursive: true });
82638
+ if (!fs25.existsSync(brokerDir)) {
82639
+ fs25.mkdirSync(brokerDir, { recursive: true });
82470
82640
  }
82471
82641
  for (const file of files) {
82472
- fs26.writeFileSync(path43.join(brokerDir, file.name), file.content, "utf-8");
82642
+ fs25.writeFileSync(path43.join(brokerDir, file.name), file.content, "utf-8");
82473
82643
  }
82474
82644
  const manifestFile = files.find((f) => f.name === "broker.yaml");
82475
82645
  const manifest = manifestFile ? (0, import_yaml.parse)(manifestFile.content) : {};