@clef-sh/core 0.1.10 → 0.1.11-beta.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -608,7 +608,7 @@ var require_age_encryption = __commonJS({
608
608
  };
609
609
  }
610
610
  // @__NO_SIDE_EFFECTS__
611
- function join16(separator = "") {
611
+ function join18(separator = "") {
612
612
  astr("join", separator);
613
613
  return {
614
614
  encode: (from) => {
@@ -738,9 +738,9 @@ var require_age_encryption = __commonJS({
738
738
  decode(s) {
739
739
  return decodeBase64Builtin(s, false);
740
740
  }
741
- } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join16(""));
742
- var base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join16(""));
743
- var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join16(""));
741
+ } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join18(""));
742
+ var base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join18(""));
743
+ var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join18(""));
744
744
  var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
745
745
  function bech32Polymod(pre) {
746
746
  const b = pre >> 25;
@@ -5789,7 +5789,7 @@ var require_age_encryption = __commonJS({
5789
5789
  return new TextDecoder().decode(bytes);
5790
5790
  }
5791
5791
  function flatten(arr) {
5792
- const len = arr.reduce((sum, line) => sum + line.length, 0);
5792
+ const len = arr.reduce(((sum, line) => sum + line.length), 0);
5793
5793
  const out = new Uint8Array(len);
5794
5794
  let n = 0;
5795
5795
  for (const a of arr) {
@@ -6911,9 +6911,10 @@ var require_age_encryption = __commonJS({
6911
6911
  });
6912
6912
 
6913
6913
  // src/index.ts
6914
- var src_exports = {};
6915
- __export(src_exports, {
6914
+ var index_exports = {};
6915
+ __export(index_exports, {
6916
6916
  ArtifactPacker: () => ArtifactPacker,
6917
+ BackendMigrator: () => BackendMigrator,
6917
6918
  BulkOps: () => BulkOps,
6918
6919
  CLEF_MANIFEST_FILENAME: () => CLEF_MANIFEST_FILENAME,
6919
6920
  CLEF_REPORT_SCHEMA_VERSION: () => CLEF_REPORT_SCHEMA_VERSION,
@@ -6976,11 +6977,12 @@ __export(src_exports, {
6976
6977
  markResolved: () => markResolved,
6977
6978
  matchPatterns: () => matchPatterns,
6978
6979
  metadataPath: () => metadataPath,
6979
- parse: () => parse8,
6980
+ parse: () => parse9,
6980
6981
  parseDotenv: () => parseDotenv,
6981
6982
  parseIgnoreContent: () => parseIgnoreContent,
6982
6983
  parseJson: () => parseJson,
6983
6984
  parseYaml: () => parseYaml,
6985
+ readManifestYaml: () => readManifestYaml,
6984
6986
  redactValue: () => redactValue,
6985
6987
  removeAccessRequest: () => removeRequest,
6986
6988
  requestsFilePath: () => requestsFilePath,
@@ -6998,9 +7000,10 @@ __export(src_exports, {
6998
7000
  signKms: () => signKms,
6999
7001
  upsertRequest: () => upsertRequest,
7000
7002
  validateAgePublicKey: () => validateAgePublicKey,
7001
- verifySignature: () => verifySignature
7003
+ verifySignature: () => verifySignature,
7004
+ writeManifestYaml: () => writeManifestYaml
7002
7005
  });
7003
- module.exports = __toCommonJS(src_exports);
7006
+ module.exports = __toCommonJS(index_exports);
7004
7007
 
7005
7008
  // src/types/index.ts
7006
7009
  var CLEF_SUPPORTED_EXTENSIONS = [".enc.yaml", ".enc.json"];
@@ -7692,9 +7695,21 @@ var ManifestParser = class {
7692
7695
  }
7693
7696
  };
7694
7697
 
7698
+ // src/manifest/io.ts
7699
+ var fs2 = __toESM(require("fs"));
7700
+ var path = __toESM(require("path"));
7701
+ var YAML2 = __toESM(require("yaml"));
7702
+ function readManifestYaml(repoRoot) {
7703
+ const raw = fs2.readFileSync(path.join(repoRoot, CLEF_MANIFEST_FILENAME), "utf-8");
7704
+ return YAML2.parse(raw);
7705
+ }
7706
+ function writeManifestYaml(repoRoot, doc) {
7707
+ fs2.writeFileSync(path.join(repoRoot, CLEF_MANIFEST_FILENAME), YAML2.stringify(doc), "utf-8");
7708
+ }
7709
+
7695
7710
  // src/scanner/index.ts
7696
- var fs3 = __toESM(require("fs"));
7697
- var path2 = __toESM(require("path"));
7711
+ var fs4 = __toESM(require("fs"));
7712
+ var path3 = __toESM(require("path"));
7698
7713
 
7699
7714
  // src/scanner/patterns.ts
7700
7715
  var PATTERNS = [
@@ -7754,12 +7769,12 @@ function matchPatterns(line, lineNumber, filePath) {
7754
7769
  }
7755
7770
 
7756
7771
  // src/scanner/ignore.ts
7757
- var fs2 = __toESM(require("fs"));
7758
- var path = __toESM(require("path"));
7772
+ var fs3 = __toESM(require("fs"));
7773
+ var path2 = __toESM(require("path"));
7759
7774
  function loadIgnoreRules(repoRoot) {
7760
- const ignorePath = path.join(repoRoot, ".clefignore");
7775
+ const ignorePath = path2.join(repoRoot, ".clefignore");
7761
7776
  try {
7762
- const content = fs2.readFileSync(ignorePath, "utf-8");
7777
+ const content = fs3.readFileSync(ignorePath, "utf-8");
7763
7778
  return parseIgnoreContent(content);
7764
7779
  } catch {
7765
7780
  return { files: [], patterns: [], paths: [] };
@@ -7842,9 +7857,9 @@ var ScanRunner = class {
7842
7857
  for (const ns of manifest.namespaces) {
7843
7858
  for (const env of manifest.environments) {
7844
7859
  const relPath = manifest.file_pattern.replace("{namespace}", ns.name).replace("{environment}", env.name);
7845
- const absPath = path2.join(repoRoot, relPath);
7846
- if (fs3.existsSync(absPath)) {
7847
- const content = fs3.readFileSync(absPath, "utf-8");
7860
+ const absPath = path3.join(repoRoot, relPath);
7861
+ if (fs4.existsSync(absPath)) {
7862
+ const content = fs4.readFileSync(absPath, "utf-8");
7848
7863
  if (!content.includes("sops:") && !content.includes('"sops"')) {
7849
7864
  unencryptedMatrixFiles.push(relPath);
7850
7865
  }
@@ -7860,8 +7875,8 @@ var ScanRunner = class {
7860
7875
  filesToScan = await this.getAllTrackedFiles(repoRoot);
7861
7876
  }
7862
7877
  for (const relFile of filesToScan) {
7863
- const absFile = path2.isAbsolute(relFile) ? relFile : path2.join(repoRoot, relFile);
7864
- const relPath = path2.relative(repoRoot, absFile).replace(/\\/g, "/");
7878
+ const absFile = path3.isAbsolute(relFile) ? relFile : path3.join(repoRoot, relFile);
7879
+ const relPath = path3.relative(repoRoot, absFile).replace(/\\/g, "/");
7865
7880
  if (this.shouldAlwaysSkip(relPath)) {
7866
7881
  filesSkipped++;
7867
7882
  continue;
@@ -7870,13 +7885,13 @@ var ScanRunner = class {
7870
7885
  filesSkipped++;
7871
7886
  continue;
7872
7887
  }
7873
- if (!fs3.existsSync(absFile)) {
7888
+ if (!fs4.existsSync(absFile)) {
7874
7889
  filesSkipped++;
7875
7890
  continue;
7876
7891
  }
7877
7892
  let stat;
7878
7893
  try {
7879
- stat = fs3.statSync(absFile);
7894
+ stat = fs4.statSync(absFile);
7880
7895
  } catch {
7881
7896
  filesSkipped++;
7882
7897
  continue;
@@ -7890,7 +7905,7 @@ var ScanRunner = class {
7890
7905
  continue;
7891
7906
  }
7892
7907
  filesScanned++;
7893
- const content = fs3.readFileSync(absFile, "utf-8");
7908
+ const content = fs4.readFileSync(absFile, "utf-8");
7894
7909
  const lines = content.split("\n");
7895
7910
  for (let i = 0; i < lines.length; i++) {
7896
7911
  const line = lines[i];
@@ -7932,10 +7947,10 @@ var ScanRunner = class {
7932
7947
  }
7933
7948
  isBinary(filePath) {
7934
7949
  try {
7935
- const fd = fs3.openSync(filePath, "r");
7950
+ const fd = fs4.openSync(filePath, "r");
7936
7951
  const buf = Buffer.alloc(512);
7937
- const bytesRead = fs3.readSync(fd, buf, 0, 512, 0);
7938
- fs3.closeSync(fd);
7952
+ const bytesRead = fs4.readSync(fd, buf, 0, 512, 0);
7953
+ fs4.closeSync(fd);
7939
7954
  for (let i = 0; i < bytesRead; i++) {
7940
7955
  if (buf[i] === 0) return true;
7941
7956
  }
@@ -7975,13 +7990,13 @@ var ScanRunner = class {
7975
7990
  async getFilesInPaths(repoRoot, paths) {
7976
7991
  const files = [];
7977
7992
  for (const p of paths) {
7978
- const absPath = path2.isAbsolute(p) ? p : path2.join(repoRoot, p);
7979
- if (!fs3.existsSync(absPath)) continue;
7980
- const stat = fs3.statSync(absPath);
7993
+ const absPath = path3.isAbsolute(p) ? p : path3.join(repoRoot, p);
7994
+ if (!fs4.existsSync(absPath)) continue;
7995
+ const stat = fs4.statSync(absPath);
7981
7996
  if (stat.isDirectory()) {
7982
7997
  files.push(...this.walkDir(absPath, repoRoot));
7983
7998
  } else {
7984
- files.push(path2.relative(repoRoot, absPath).replace(/\\/g, "/"));
7999
+ files.push(path3.relative(repoRoot, absPath).replace(/\\/g, "/"));
7985
8000
  }
7986
8001
  }
7987
8002
  return files;
@@ -7997,13 +8012,13 @@ var ScanRunner = class {
7997
8012
  const files = [];
7998
8013
  let entries;
7999
8014
  try {
8000
- entries = fs3.readdirSync(dir, { withFileTypes: true });
8015
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
8001
8016
  } catch {
8002
8017
  return files;
8003
8018
  }
8004
8019
  for (const entry of entries) {
8005
- const fullPath = path2.join(dir, entry.name);
8006
- const relPath = path2.relative(repoRoot, fullPath).replace(/\\/g, "/");
8020
+ const fullPath = path3.join(dir, entry.name);
8021
+ const relPath = path3.relative(repoRoot, fullPath).replace(/\\/g, "/");
8007
8022
  if (entry.isDirectory()) {
8008
8023
  if (!ALWAYS_SKIP_DIRS.includes(entry.name)) {
8009
8024
  files.push(...this.walkDir(fullPath, repoRoot));
@@ -8017,29 +8032,29 @@ var ScanRunner = class {
8017
8032
  };
8018
8033
 
8019
8034
  // src/matrix/manager.ts
8020
- var fs6 = __toESM(require("fs"));
8021
- var path4 = __toESM(require("path"));
8022
- var YAML4 = __toESM(require("yaml"));
8035
+ var fs7 = __toESM(require("fs"));
8036
+ var path5 = __toESM(require("path"));
8037
+ var YAML5 = __toESM(require("yaml"));
8023
8038
 
8024
8039
  // src/pending/metadata.ts
8025
- var fs4 = __toESM(require("fs"));
8026
- var path3 = __toESM(require("path"));
8040
+ var fs5 = __toESM(require("fs"));
8041
+ var path4 = __toESM(require("path"));
8027
8042
  var crypto2 = __toESM(require("crypto"));
8028
- var YAML2 = __toESM(require("yaml"));
8043
+ var YAML3 = __toESM(require("yaml"));
8029
8044
  function metadataPath(encryptedFilePath) {
8030
- const dir = path3.dirname(encryptedFilePath);
8031
- const base = path3.basename(encryptedFilePath).replace(/\.enc\.(yaml|json)$/, "");
8032
- return path3.join(dir, `${base}.clef-meta.yaml`);
8045
+ const dir = path4.dirname(encryptedFilePath);
8046
+ const base = path4.basename(encryptedFilePath).replace(/\.enc\.(yaml|json)$/, "");
8047
+ return path4.join(dir, `${base}.clef-meta.yaml`);
8033
8048
  }
8034
8049
  var HEADER_COMMENT = "# Managed by Clef. Do not edit manually.\n";
8035
8050
  async function loadMetadata(filePath) {
8036
8051
  const metaPath = metadataPath(filePath);
8037
8052
  try {
8038
- if (!fs4.existsSync(metaPath)) {
8053
+ if (!fs5.existsSync(metaPath)) {
8039
8054
  return { version: 1, pending: [] };
8040
8055
  }
8041
- const content = fs4.readFileSync(metaPath, "utf-8");
8042
- const parsed = YAML2.parse(content);
8056
+ const content = fs5.readFileSync(metaPath, "utf-8");
8057
+ const parsed = YAML3.parse(content);
8043
8058
  if (!parsed || !Array.isArray(parsed.pending)) {
8044
8059
  return { version: 1, pending: [] };
8045
8060
  }
@@ -8057,9 +8072,9 @@ async function loadMetadata(filePath) {
8057
8072
  }
8058
8073
  async function saveMetadata(filePath, metadata) {
8059
8074
  const metaPath = metadataPath(filePath);
8060
- const dir = path3.dirname(metaPath);
8061
- if (!fs4.existsSync(dir)) {
8062
- fs4.mkdirSync(dir, { recursive: true });
8075
+ const dir = path4.dirname(metaPath);
8076
+ if (!fs5.existsSync(dir)) {
8077
+ fs5.mkdirSync(dir, { recursive: true });
8063
8078
  }
8064
8079
  const data = {
8065
8080
  version: metadata.version,
@@ -8069,7 +8084,7 @@ async function saveMetadata(filePath, metadata) {
8069
8084
  setBy: p.setBy
8070
8085
  }))
8071
8086
  };
8072
- fs4.writeFileSync(metaPath, HEADER_COMMENT + YAML2.stringify(data), "utf-8");
8087
+ fs5.writeFileSync(metaPath, HEADER_COMMENT + YAML3.stringify(data), "utf-8");
8073
8088
  }
8074
8089
  async function markPending(filePath, keys, setBy) {
8075
8090
  const metadata = await loadMetadata(filePath);
@@ -8110,12 +8125,12 @@ async function markPendingWithRetry(filePath, keys, setBy, retryDelayMs = 200) {
8110
8125
  }
8111
8126
 
8112
8127
  // src/sops/keys.ts
8113
- var fs5 = __toESM(require("fs"));
8114
- var YAML3 = __toESM(require("yaml"));
8128
+ var fs6 = __toESM(require("fs"));
8129
+ var YAML4 = __toESM(require("yaml"));
8115
8130
  function readSopsKeyNames(filePath) {
8116
8131
  try {
8117
- const raw = fs5.readFileSync(filePath, "utf-8");
8118
- const parsed = YAML3.parse(raw);
8132
+ const raw = fs6.readFileSync(filePath, "utf-8");
8133
+ const parsed = YAML4.parse(raw);
8119
8134
  if (parsed === null || parsed === void 0 || typeof parsed !== "object") return null;
8120
8135
  return Object.keys(parsed).filter((k) => k !== "sops");
8121
8136
  } catch {
@@ -8137,12 +8152,12 @@ var MatrixManager = class {
8137
8152
  for (const ns of manifest.namespaces) {
8138
8153
  for (const env of manifest.environments) {
8139
8154
  const relativePath = manifest.file_pattern.replace("{namespace}", ns.name).replace("{environment}", env.name);
8140
- const filePath = path4.join(repoRoot, relativePath);
8155
+ const filePath = path5.join(repoRoot, relativePath);
8141
8156
  cells.push({
8142
8157
  namespace: ns.name,
8143
8158
  environment: env.name,
8144
8159
  filePath,
8145
- exists: fs6.existsSync(filePath)
8160
+ exists: fs7.existsSync(filePath)
8146
8161
  });
8147
8162
  }
8148
8163
  }
@@ -8165,9 +8180,9 @@ var MatrixManager = class {
8165
8180
  * @param manifest - Parsed manifest used to determine the encryption backend.
8166
8181
  */
8167
8182
  async scaffoldCell(cell, sopsClient, manifest) {
8168
- const dir = path4.dirname(cell.filePath);
8169
- if (!fs6.existsSync(dir)) {
8170
- fs6.mkdirSync(dir, { recursive: true });
8183
+ const dir = path5.dirname(cell.filePath);
8184
+ if (!fs7.existsSync(dir)) {
8185
+ fs7.mkdirSync(dir, { recursive: true });
8171
8186
  }
8172
8187
  await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);
8173
8188
  }
@@ -8243,8 +8258,8 @@ var MatrixManager = class {
8243
8258
  */
8244
8259
  readLastModified(filePath) {
8245
8260
  try {
8246
- const raw = fs6.readFileSync(filePath, "utf-8");
8247
- const parsed = YAML4.parse(raw);
8261
+ const raw = fs7.readFileSync(filePath, "utf-8");
8262
+ const parsed = YAML5.parse(raw);
8248
8263
  const sops = parsed?.sops;
8249
8264
  if (sops?.lastmodified) return new Date(String(sops.lastmodified));
8250
8265
  return null;
@@ -8265,8 +8280,8 @@ var MatrixManager = class {
8265
8280
  };
8266
8281
 
8267
8282
  // src/schema/validator.ts
8268
- var fs7 = __toESM(require("fs"));
8269
- var YAML5 = __toESM(require("yaml"));
8283
+ var fs8 = __toESM(require("fs"));
8284
+ var YAML6 = __toESM(require("yaml"));
8270
8285
  var SchemaValidator = class {
8271
8286
  /**
8272
8287
  * Read and parse a YAML schema file from disk.
@@ -8278,13 +8293,13 @@ var SchemaValidator = class {
8278
8293
  loadSchema(filePath) {
8279
8294
  let raw;
8280
8295
  try {
8281
- raw = fs7.readFileSync(filePath, "utf-8");
8296
+ raw = fs8.readFileSync(filePath, "utf-8");
8282
8297
  } catch {
8283
8298
  throw new SchemaLoadError(`Could not read schema file at '${filePath}'.`, filePath);
8284
8299
  }
8285
8300
  let parsed;
8286
8301
  try {
8287
- parsed = YAML5.parse(raw);
8302
+ parsed = YAML6.parse(raw);
8288
8303
  } catch {
8289
8304
  throw new SchemaLoadError(`Schema file '${filePath}' contains invalid YAML.`, filePath);
8290
8305
  }
@@ -8412,7 +8427,7 @@ var SchemaValidator = class {
8412
8427
  };
8413
8428
 
8414
8429
  // src/diff/engine.ts
8415
- var path5 = __toESM(require("path"));
8430
+ var path6 = __toESM(require("path"));
8416
8431
  var DiffEngine = class {
8417
8432
  /**
8418
8433
  * Compare two in-memory value maps and produce a sorted diff result.
@@ -8469,11 +8484,11 @@ var DiffEngine = class {
8469
8484
  * @throws {@link SopsDecryptionError} If either file cannot be decrypted.
8470
8485
  */
8471
8486
  async diffFiles(namespace, envA, envB, manifest, sopsClient, repoRoot) {
8472
- const fileA = path5.join(
8487
+ const fileA = path6.join(
8473
8488
  repoRoot,
8474
8489
  manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envA)
8475
8490
  );
8476
- const fileB = path5.join(
8491
+ const fileB = path6.join(
8477
8492
  repoRoot,
8478
8493
  manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envB)
8479
8494
  );
@@ -8486,7 +8501,7 @@ var DiffEngine = class {
8486
8501
  };
8487
8502
 
8488
8503
  // src/bulk/ops.ts
8489
- var path6 = __toESM(require("path"));
8504
+ var path7 = __toESM(require("path"));
8490
8505
  var BulkOps = class {
8491
8506
  /**
8492
8507
  * Set a key to different values in multiple environments at once.
@@ -8505,7 +8520,7 @@ var BulkOps = class {
8505
8520
  if (!(env.name in values)) {
8506
8521
  continue;
8507
8522
  }
8508
- const filePath = path6.join(
8523
+ const filePath = path7.join(
8509
8524
  repoRoot,
8510
8525
  manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
8511
8526
  );
@@ -8539,7 +8554,7 @@ Successfully updated ${Object.keys(values).length - errors.length} environment(s
8539
8554
  async deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot) {
8540
8555
  const errors = [];
8541
8556
  for (const env of manifest.environments) {
8542
- const filePath = path6.join(
8557
+ const filePath = path7.join(
8543
8558
  repoRoot,
8544
8559
  manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
8545
8560
  );
@@ -8585,8 +8600,8 @@ ${details}`
8585
8600
  };
8586
8601
 
8587
8602
  // src/git/integration.ts
8588
- var fs8 = __toESM(require("fs"));
8589
- var path7 = __toESM(require("path"));
8603
+ var fs9 = __toESM(require("fs"));
8604
+ var path8 = __toESM(require("path"));
8590
8605
  var PRE_COMMIT_HOOK = `#!/bin/sh
8591
8606
  # Clef pre-commit hook \u2014 blocks commits of files missing SOPS encryption metadata
8592
8607
  # and scans staged files for plaintext secrets.
@@ -8793,15 +8808,15 @@ var GitIntegration = class {
8793
8808
  cwd: repoRoot
8794
8809
  });
8795
8810
  const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;
8796
- const attrFilePath = path7.join(repoRoot, ".gitattributes");
8797
- const attrContent = fs8.existsSync(attrFilePath) ? fs8.readFileSync(attrFilePath, "utf-8") : "";
8811
+ const attrFilePath = path8.join(repoRoot, ".gitattributes");
8812
+ const attrContent = fs9.existsSync(attrFilePath) ? fs9.readFileSync(attrFilePath, "utf-8") : "";
8798
8813
  const gitattributes = attrContent.includes("merge=sops");
8799
8814
  return { gitConfig, gitattributes };
8800
8815
  }
8801
8816
  async ensureGitattributes(repoRoot) {
8802
- const attrPath = path7.join(repoRoot, ".gitattributes");
8817
+ const attrPath = path8.join(repoRoot, ".gitattributes");
8803
8818
  const mergeRule = "*.enc.yaml merge=sops\n*.enc.json merge=sops";
8804
- const existing = fs8.existsSync(attrPath) ? fs8.readFileSync(attrPath, "utf-8") : "";
8819
+ const existing = fs9.existsSync(attrPath) ? fs9.readFileSync(attrPath, "utf-8") : "";
8805
8820
  if (existing.includes("merge=sops")) {
8806
8821
  return;
8807
8822
  }
@@ -8828,7 +8843,7 @@ ${mergeRule}
8828
8843
  * @throws {@link GitOperationError} On failure.
8829
8844
  */
8830
8845
  async installPreCommitHook(repoRoot) {
8831
- const hookPath = path7.join(repoRoot, ".git", "hooks", "pre-commit");
8846
+ const hookPath = path8.join(repoRoot, ".git", "hooks", "pre-commit");
8832
8847
  const result = await this.runner.run("tee", [hookPath], {
8833
8848
  stdin: PRE_COMMIT_HOOK,
8834
8849
  cwd: repoRoot
@@ -8849,18 +8864,18 @@ ${mergeRule}
8849
8864
  };
8850
8865
 
8851
8866
  // src/sops/client.ts
8852
- var fs11 = __toESM(require("fs"));
8867
+ var fs12 = __toESM(require("fs"));
8853
8868
  var net = __toESM(require("net"));
8854
8869
  var import_crypto = require("crypto");
8855
- var YAML6 = __toESM(require("yaml"));
8870
+ var YAML7 = __toESM(require("yaml"));
8856
8871
 
8857
8872
  // src/sops/resolver.ts
8858
- var fs10 = __toESM(require("fs"));
8859
- var path9 = __toESM(require("path"));
8873
+ var fs11 = __toESM(require("fs"));
8874
+ var path10 = __toESM(require("path"));
8860
8875
 
8861
8876
  // src/sops/bundled.ts
8862
- var fs9 = __toESM(require("fs"));
8863
- var path8 = __toESM(require("path"));
8877
+ var fs10 = __toESM(require("fs"));
8878
+ var path9 = __toESM(require("path"));
8864
8879
  function tryBundled() {
8865
8880
  const platform = process.platform;
8866
8881
  const arch = process.arch;
@@ -8872,9 +8887,9 @@ function tryBundled() {
8872
8887
  const binName = platform === "win32" ? "sops.exe" : "sops";
8873
8888
  try {
8874
8889
  const packageMain = require.resolve(`${packageName}/package.json`);
8875
- const packageDir = path8.dirname(packageMain);
8876
- const binPath = path8.join(packageDir, "bin", binName);
8877
- return fs9.existsSync(binPath) ? binPath : null;
8890
+ const packageDir = path9.dirname(packageMain);
8891
+ const binPath = path9.join(packageDir, "bin", binName);
8892
+ return fs10.existsSync(binPath) ? binPath : null;
8878
8893
  } catch {
8879
8894
  return null;
8880
8895
  }
@@ -8882,7 +8897,7 @@ function tryBundled() {
8882
8897
 
8883
8898
  // src/sops/resolver.ts
8884
8899
  function validateSopsPath(candidate) {
8885
- if (!path9.isAbsolute(candidate)) {
8900
+ if (!path10.isAbsolute(candidate)) {
8886
8901
  throw new Error(`CLEF_SOPS_PATH must be an absolute path, got '${candidate}'.`);
8887
8902
  }
8888
8903
  const segments = candidate.split(/[/\\]/);
@@ -8898,7 +8913,7 @@ function resolveSopsPath() {
8898
8913
  const envPath = process.env.CLEF_SOPS_PATH?.trim();
8899
8914
  if (envPath) {
8900
8915
  validateSopsPath(envPath);
8901
- if (!fs10.existsSync(envPath)) {
8916
+ if (!fs11.existsSync(envPath)) {
8902
8917
  throw new Error(`CLEF_SOPS_PATH points to '${envPath}' but the file does not exist.`);
8903
8918
  }
8904
8919
  cached = { path: envPath, source: "env" };
@@ -9106,7 +9121,7 @@ var SopsClient = class {
9106
9121
  }
9107
9122
  let parsed;
9108
9123
  try {
9109
- parsed = YAML6.parse(result.stdout) ?? {};
9124
+ parsed = YAML7.parse(result.stdout) ?? {};
9110
9125
  } catch {
9111
9126
  throw new SopsDecryptionError(
9112
9127
  `Decrypted content of '${filePath}' is not valid YAML.`,
@@ -9133,7 +9148,7 @@ var SopsClient = class {
9133
9148
  async encrypt(filePath, values, manifest, environment) {
9134
9149
  await assertSops(this.runner, this.sopsCommand);
9135
9150
  const fmt = formatFromPath(filePath);
9136
- const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML6.stringify(values);
9151
+ const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML7.stringify(values);
9137
9152
  const args = this.buildEncryptArgs(filePath, manifest, environment);
9138
9153
  const env = this.buildSopsEnv();
9139
9154
  let inputArg;
@@ -9177,7 +9192,7 @@ var SopsClient = class {
9177
9192
  );
9178
9193
  }
9179
9194
  try {
9180
- fs11.writeFileSync(filePath, result.stdout);
9195
+ fs12.writeFileSync(filePath, result.stdout);
9181
9196
  } catch {
9182
9197
  throw new SopsEncryptionError(`Failed to write encrypted data to '${filePath}'.`, filePath);
9183
9198
  }
@@ -9293,7 +9308,7 @@ var SopsClient = class {
9293
9308
  if (!this.ageKey && !this.ageKeyFile) return "key-not-found";
9294
9309
  let keyContent;
9295
9310
  try {
9296
- keyContent = this.ageKey ?? fs11.readFileSync(this.ageKeyFile, "utf-8");
9311
+ keyContent = this.ageKey ?? fs12.readFileSync(this.ageKeyFile, "utf-8");
9297
9312
  } catch {
9298
9313
  return "key-not-found";
9299
9314
  }
@@ -9310,7 +9325,7 @@ var SopsClient = class {
9310
9325
  parseMetadataFromFile(filePath) {
9311
9326
  let content;
9312
9327
  try {
9313
- content = fs11.readFileSync(filePath, "utf-8");
9328
+ content = fs12.readFileSync(filePath, "utf-8");
9314
9329
  } catch {
9315
9330
  throw new SopsDecryptionError(
9316
9331
  `Could not read file '${filePath}' to extract SOPS metadata.`,
@@ -9319,7 +9334,7 @@ var SopsClient = class {
9319
9334
  }
9320
9335
  let parsed;
9321
9336
  try {
9322
- parsed = YAML6.parse(content);
9337
+ parsed = YAML7.parse(content);
9323
9338
  } catch {
9324
9339
  throw new SopsDecryptionError(
9325
9340
  `File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,
@@ -9414,7 +9429,7 @@ var SopsClient = class {
9414
9429
  };
9415
9430
 
9416
9431
  // src/lint/runner.ts
9417
- var path10 = __toESM(require("path"));
9432
+ var path11 = __toESM(require("path"));
9418
9433
  var LintRunner = class {
9419
9434
  constructor(matrixManager, schemaValidator, sopsClient) {
9420
9435
  this.matrixManager = matrixManager;
@@ -9514,7 +9529,7 @@ var LintRunner = class {
9514
9529
  }
9515
9530
  const ns = manifest.namespaces.find((n) => n.name === cell.namespace);
9516
9531
  if (ns?.schema) {
9517
- const schemaPath = path10.join(repoRoot, ns.schema);
9532
+ const schemaPath = path11.join(repoRoot, ns.schema);
9518
9533
  try {
9519
9534
  const schema = this.schemaValidator.loadSchema(schemaPath);
9520
9535
  const result = this.schemaValidator.validate(decrypted.values, schema);
@@ -9747,14 +9762,14 @@ Use 'clef exec' to inject secrets directly into a process, or 'clef export --for
9747
9762
  };
9748
9763
 
9749
9764
  // src/import/index.ts
9750
- var path12 = __toESM(require("path"));
9765
+ var path13 = __toESM(require("path"));
9751
9766
 
9752
9767
  // src/import/parsers.ts
9753
- var path11 = __toESM(require("path"));
9754
- var YAML7 = __toESM(require("yaml"));
9768
+ var path12 = __toESM(require("path"));
9769
+ var YAML8 = __toESM(require("yaml"));
9755
9770
  function detectFormat(filePath, content) {
9756
- const base = path11.basename(filePath);
9757
- const ext = path11.extname(filePath).toLowerCase();
9771
+ const base = path12.basename(filePath);
9772
+ const ext = path12.extname(filePath).toLowerCase();
9758
9773
  if (base === ".env" || base.startsWith(".env.")) {
9759
9774
  return "dotenv";
9760
9775
  }
@@ -9775,7 +9790,7 @@ function detectFormat(filePath, content) {
9775
9790
  } catch {
9776
9791
  }
9777
9792
  try {
9778
- const parsed = YAML7.parse(content);
9793
+ const parsed = YAML8.parse(content);
9779
9794
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
9780
9795
  return "yaml";
9781
9796
  }
@@ -9856,7 +9871,7 @@ function parseJson(content) {
9856
9871
  function parseYaml(content) {
9857
9872
  let parsed;
9858
9873
  try {
9859
- parsed = YAML7.parse(content);
9874
+ parsed = YAML8.parse(content);
9860
9875
  } catch (err) {
9861
9876
  throw new Error(`Invalid YAML: ${err.message}`);
9862
9877
  }
@@ -9890,7 +9905,7 @@ function parseYaml(content) {
9890
9905
  }
9891
9906
  return { pairs, format: "yaml", skipped, warnings };
9892
9907
  }
9893
- function parse8(content, format, filePath) {
9908
+ function parse9(content, format, filePath) {
9894
9909
  const resolved = format === "auto" ? detectFormat(filePath ?? "", content) : format;
9895
9910
  switch (resolved) {
9896
9911
  case "dotenv":
@@ -9919,11 +9934,11 @@ var ImportRunner = class {
9919
9934
  */
9920
9935
  async import(target, sourcePath, content, manifest, repoRoot, options) {
9921
9936
  const [ns, env] = target.split("/");
9922
- const filePath = path12.join(
9937
+ const filePath = path13.join(
9923
9938
  repoRoot,
9924
9939
  manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env)
9925
9940
  );
9926
- const parsed = parse8(content, options.format ?? "auto", sourcePath ?? "");
9941
+ const parsed = parse9(content, options.format ?? "auto", sourcePath ?? "");
9927
9942
  let candidates = Object.entries(parsed.pairs);
9928
9943
  if (options.prefix) {
9929
9944
  const prefix = options.prefix;
@@ -9977,9 +9992,8 @@ var ImportRunner = class {
9977
9992
  };
9978
9993
 
9979
9994
  // src/recipients/index.ts
9980
- var fs12 = __toESM(require("fs"));
9981
- var path13 = __toESM(require("path"));
9982
- var YAML8 = __toESM(require("yaml"));
9995
+ var fs13 = __toESM(require("fs"));
9996
+ var path14 = __toESM(require("path"));
9983
9997
  function parseRecipientEntry(entry) {
9984
9998
  if (typeof entry === "string") {
9985
9999
  return { key: entry };
@@ -10000,15 +10014,6 @@ function toRecipient(entry) {
10000
10014
  ...entry.label ? { label: entry.label } : {}
10001
10015
  };
10002
10016
  }
10003
- function readManifestYaml(repoRoot) {
10004
- const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
10005
- const raw = fs12.readFileSync(manifestPath, "utf-8");
10006
- return YAML8.parse(raw);
10007
- }
10008
- function writeManifestYaml(repoRoot, doc) {
10009
- const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
10010
- fs12.writeFileSync(manifestPath, YAML8.stringify(doc), "utf-8");
10011
- }
10012
10017
  function getRecipientsArray(doc) {
10013
10018
  const sops = doc.sops;
10014
10019
  if (!sops) return [];
@@ -10107,8 +10112,8 @@ var RecipientManager = class {
10107
10112
  if (currentKeys.includes(normalizedKey)) {
10108
10113
  throw new Error(`Recipient '${keyPreview(normalizedKey)}' is already present.`);
10109
10114
  }
10110
- const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
10111
- const manifestBackup = fs12.readFileSync(manifestPath, "utf-8");
10115
+ const manifestPath = path14.join(repoRoot, CLEF_MANIFEST_FILENAME);
10116
+ const manifestBackup = fs13.readFileSync(manifestPath, "utf-8");
10112
10117
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
10113
10118
  if (label) {
10114
10119
  recipients.push({ key: normalizedKey, label });
@@ -10123,16 +10128,16 @@ var RecipientManager = class {
10123
10128
  const fileBackups = /* @__PURE__ */ new Map();
10124
10129
  for (const cell of cells) {
10125
10130
  try {
10126
- fileBackups.set(cell.filePath, fs12.readFileSync(cell.filePath, "utf-8"));
10131
+ fileBackups.set(cell.filePath, fs13.readFileSync(cell.filePath, "utf-8"));
10127
10132
  await this.encryption.addRecipient(cell.filePath, normalizedKey);
10128
10133
  reEncryptedFiles.push(cell.filePath);
10129
10134
  } catch {
10130
10135
  failedFiles.push(cell.filePath);
10131
- fs12.writeFileSync(manifestPath, manifestBackup, "utf-8");
10136
+ fs13.writeFileSync(manifestPath, manifestBackup, "utf-8");
10132
10137
  for (const reEncryptedFile of reEncryptedFiles) {
10133
10138
  const backup = fileBackups.get(reEncryptedFile);
10134
10139
  if (backup) {
10135
- fs12.writeFileSync(reEncryptedFile, backup, "utf-8");
10140
+ fs13.writeFileSync(reEncryptedFile, backup, "utf-8");
10136
10141
  }
10137
10142
  }
10138
10143
  const restoredDoc = readManifestYaml(repoRoot);
@@ -10185,8 +10190,8 @@ var RecipientManager = class {
10185
10190
  throw new Error(`Recipient '${keyPreview(trimmedKey)}' is not in the manifest.`);
10186
10191
  }
10187
10192
  const removedEntry = parsed[matchIndex];
10188
- const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
10189
- const manifestBackup = fs12.readFileSync(manifestPath, "utf-8");
10193
+ const manifestPath = path14.join(repoRoot, CLEF_MANIFEST_FILENAME);
10194
+ const manifestBackup = fs13.readFileSync(manifestPath, "utf-8");
10190
10195
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
10191
10196
  recipients.splice(matchIndex, 1);
10192
10197
  writeManifestYaml(repoRoot, doc);
@@ -10197,16 +10202,16 @@ var RecipientManager = class {
10197
10202
  const fileBackups = /* @__PURE__ */ new Map();
10198
10203
  for (const cell of cells) {
10199
10204
  try {
10200
- fileBackups.set(cell.filePath, fs12.readFileSync(cell.filePath, "utf-8"));
10205
+ fileBackups.set(cell.filePath, fs13.readFileSync(cell.filePath, "utf-8"));
10201
10206
  await this.encryption.removeRecipient(cell.filePath, trimmedKey);
10202
10207
  reEncryptedFiles.push(cell.filePath);
10203
10208
  } catch {
10204
10209
  failedFiles.push(cell.filePath);
10205
- fs12.writeFileSync(manifestPath, manifestBackup, "utf-8");
10210
+ fs13.writeFileSync(manifestPath, manifestBackup, "utf-8");
10206
10211
  for (const reEncryptedFile of reEncryptedFiles) {
10207
10212
  const backup = fileBackups.get(reEncryptedFile);
10208
10213
  if (backup) {
10209
- fs12.writeFileSync(reEncryptedFile, backup, "utf-8");
10214
+ fs13.writeFileSync(reEncryptedFile, backup, "utf-8");
10210
10215
  }
10211
10216
  }
10212
10217
  const restoredDoc = readManifestYaml(repoRoot);
@@ -10240,19 +10245,19 @@ var RecipientManager = class {
10240
10245
  };
10241
10246
 
10242
10247
  // src/recipients/requests.ts
10243
- var fs13 = __toESM(require("fs"));
10244
- var path14 = __toESM(require("path"));
10248
+ var fs14 = __toESM(require("fs"));
10249
+ var path15 = __toESM(require("path"));
10245
10250
  var YAML9 = __toESM(require("yaml"));
10246
10251
  var REQUESTS_FILENAME = ".clef-requests.yaml";
10247
10252
  var HEADER_COMMENT2 = "# Pending recipient access requests. Approve with: clef recipients approve <label>\n";
10248
10253
  function requestsFilePath(repoRoot) {
10249
- return path14.join(repoRoot, REQUESTS_FILENAME);
10254
+ return path15.join(repoRoot, REQUESTS_FILENAME);
10250
10255
  }
10251
10256
  function loadRequests(repoRoot) {
10252
10257
  const filePath = requestsFilePath(repoRoot);
10253
10258
  try {
10254
- if (!fs13.existsSync(filePath)) return [];
10255
- const content = fs13.readFileSync(filePath, "utf-8");
10259
+ if (!fs14.existsSync(filePath)) return [];
10260
+ const content = fs14.readFileSync(filePath, "utf-8");
10256
10261
  const parsed = YAML9.parse(content);
10257
10262
  if (!parsed || !Array.isArray(parsed.requests)) return [];
10258
10263
  return parsed.requests.map((r) => ({
@@ -10269,7 +10274,7 @@ function saveRequests(repoRoot, requests) {
10269
10274
  const filePath = requestsFilePath(repoRoot);
10270
10275
  if (requests.length === 0) {
10271
10276
  try {
10272
- fs13.unlinkSync(filePath);
10277
+ fs14.unlinkSync(filePath);
10273
10278
  } catch {
10274
10279
  }
10275
10280
  return;
@@ -10285,7 +10290,7 @@ function saveRequests(repoRoot, requests) {
10285
10290
  return raw;
10286
10291
  })
10287
10292
  };
10288
- fs13.writeFileSync(filePath, HEADER_COMMENT2 + YAML9.stringify(data), "utf-8");
10293
+ fs14.writeFileSync(filePath, HEADER_COMMENT2 + YAML9.stringify(data), "utf-8");
10289
10294
  }
10290
10295
  function upsertRequest(repoRoot, key, label, environment) {
10291
10296
  const requests = loadRequests(repoRoot);
@@ -10321,7 +10326,7 @@ function findInList(requests, identifier) {
10321
10326
  }
10322
10327
 
10323
10328
  // src/drift/detector.ts
10324
- var path15 = __toESM(require("path"));
10329
+ var path16 = __toESM(require("path"));
10325
10330
  var DriftDetector = class {
10326
10331
  parser = new ManifestParser();
10327
10332
  matrix = new MatrixManager();
@@ -10334,8 +10339,8 @@ var DriftDetector = class {
10334
10339
  * @returns Drift result with any issues found.
10335
10340
  */
10336
10341
  detect(localRoot, remoteRoot, namespaceFilter) {
10337
- const localManifest = this.parser.parse(path15.join(localRoot, CLEF_MANIFEST_FILENAME));
10338
- const remoteManifest = this.parser.parse(path15.join(remoteRoot, CLEF_MANIFEST_FILENAME));
10342
+ const localManifest = this.parser.parse(path16.join(localRoot, CLEF_MANIFEST_FILENAME));
10343
+ const remoteManifest = this.parser.parse(path16.join(remoteRoot, CLEF_MANIFEST_FILENAME));
10339
10344
  const localCells = this.matrix.resolveMatrix(localManifest, localRoot);
10340
10345
  const remoteCells = this.matrix.resolveMatrix(remoteManifest, remoteRoot);
10341
10346
  const localEnvNames = localManifest.environments.map((e) => e.name);
@@ -10399,7 +10404,7 @@ var DriftDetector = class {
10399
10404
  };
10400
10405
 
10401
10406
  // src/report/generator.ts
10402
- var path16 = __toESM(require("path"));
10407
+ var path17 = __toESM(require("path"));
10403
10408
 
10404
10409
  // src/report/sanitizer.ts
10405
10410
  var ReportSanitizer = class {
@@ -10555,7 +10560,7 @@ var ReportGenerator = class {
10555
10560
  let manifest = null;
10556
10561
  try {
10557
10562
  const parser = new ManifestParser();
10558
- manifest = parser.parse(path16.join(repoRoot, "clef.yaml"));
10563
+ manifest = parser.parse(path17.join(repoRoot, "clef.yaml"));
10559
10564
  } catch {
10560
10565
  const emptyManifest = {
10561
10566
  manifestVersion: 0,
@@ -11033,9 +11038,9 @@ var SopsMergeDriver = class {
11033
11038
  };
11034
11039
 
11035
11040
  // src/service-identity/manager.ts
11036
- var fs14 = __toESM(require("fs"));
11041
+ var fs15 = __toESM(require("fs"));
11037
11042
  var os = __toESM(require("os"));
11038
- var path17 = __toESM(require("path"));
11043
+ var path18 = __toESM(require("path"));
11039
11044
  var YAML10 = __toESM(require("yaml"));
11040
11045
  var PartialRotationError = class extends Error {
11041
11046
  constructor(message, rotatedKeys) {
@@ -11087,8 +11092,8 @@ var ServiceIdentityManager = class {
11087
11092
  environments
11088
11093
  };
11089
11094
  await this.registerRecipients(definition, manifest, repoRoot);
11090
- const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
11091
- const raw = fs14.readFileSync(manifestPath, "utf-8");
11095
+ const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
11096
+ const raw = fs15.readFileSync(manifestPath, "utf-8");
11092
11097
  const doc = YAML10.parse(raw);
11093
11098
  if (!Array.isArray(doc.service_identities)) {
11094
11099
  doc.service_identities = [];
@@ -11099,13 +11104,13 @@ var ServiceIdentityManager = class {
11099
11104
  namespaces,
11100
11105
  environments
11101
11106
  });
11102
- const tmpCreate = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11107
+ const tmpCreate = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11103
11108
  try {
11104
- fs14.writeFileSync(tmpCreate, YAML10.stringify(doc), "utf-8");
11105
- fs14.renameSync(tmpCreate, manifestPath);
11109
+ fs15.writeFileSync(tmpCreate, YAML10.stringify(doc), "utf-8");
11110
+ fs15.renameSync(tmpCreate, manifestPath);
11106
11111
  } finally {
11107
11112
  try {
11108
- fs14.unlinkSync(tmpCreate);
11113
+ fs15.unlinkSync(tmpCreate);
11109
11114
  } catch {
11110
11115
  }
11111
11116
  }
@@ -11143,8 +11148,8 @@ var ServiceIdentityManager = class {
11143
11148
  } catch {
11144
11149
  }
11145
11150
  }
11146
- const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
11147
- const raw = fs14.readFileSync(manifestPath, "utf-8");
11151
+ const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
11152
+ const raw = fs15.readFileSync(manifestPath, "utf-8");
11148
11153
  const doc = YAML10.parse(raw);
11149
11154
  const identities = doc.service_identities;
11150
11155
  if (Array.isArray(identities)) {
@@ -11152,13 +11157,13 @@ var ServiceIdentityManager = class {
11152
11157
  (si) => si.name !== name
11153
11158
  );
11154
11159
  }
11155
- const tmp = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11160
+ const tmp = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11156
11161
  try {
11157
- fs14.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
11158
- fs14.renameSync(tmp, manifestPath);
11162
+ fs15.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
11163
+ fs15.renameSync(tmp, manifestPath);
11159
11164
  } finally {
11160
11165
  try {
11161
- fs14.unlinkSync(tmp);
11166
+ fs15.unlinkSync(tmp);
11162
11167
  } catch {
11163
11168
  }
11164
11169
  }
@@ -11173,8 +11178,8 @@ var ServiceIdentityManager = class {
11173
11178
  if (!identity) {
11174
11179
  throw new Error(`Service identity '${name}' not found.`);
11175
11180
  }
11176
- const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
11177
- const raw = fs14.readFileSync(manifestPath, "utf-8");
11181
+ const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
11182
+ const raw = fs15.readFileSync(manifestPath, "utf-8");
11178
11183
  const doc = YAML10.parse(raw);
11179
11184
  const identities = doc.service_identities;
11180
11185
  const siDoc = identities.find((si) => si.name === name);
@@ -11200,13 +11205,13 @@ var ServiceIdentityManager = class {
11200
11205
  envs[envName] = { kms: kmsConfig };
11201
11206
  identity.environments[envName] = { kms: kmsConfig };
11202
11207
  }
11203
- const tmp = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11208
+ const tmp = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11204
11209
  try {
11205
- fs14.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
11206
- fs14.renameSync(tmp, manifestPath);
11210
+ fs15.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
11211
+ fs15.renameSync(tmp, manifestPath);
11207
11212
  } finally {
11208
11213
  try {
11209
- fs14.unlinkSync(tmp);
11214
+ fs15.unlinkSync(tmp);
11210
11215
  } catch {
11211
11216
  }
11212
11217
  }
@@ -11242,8 +11247,8 @@ var ServiceIdentityManager = class {
11242
11247
  if (!identity) {
11243
11248
  throw new Error(`Service identity '${name}' not found.`);
11244
11249
  }
11245
- const manifestPath = path17.join(repoRoot, CLEF_MANIFEST_FILENAME);
11246
- const raw = fs14.readFileSync(manifestPath, "utf-8");
11250
+ const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
11251
+ const raw = fs15.readFileSync(manifestPath, "utf-8");
11247
11252
  const doc = YAML10.parse(raw);
11248
11253
  const identities = doc.service_identities;
11249
11254
  const siDoc = identities.find((si) => si.name === name);
@@ -11297,13 +11302,13 @@ var ServiceIdentityManager = class {
11297
11302
  }
11298
11303
  throw err;
11299
11304
  }
11300
- const tmpRotate = path17.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11305
+ const tmpRotate = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
11301
11306
  try {
11302
- fs14.writeFileSync(tmpRotate, YAML10.stringify(doc), "utf-8");
11303
- fs14.renameSync(tmpRotate, manifestPath);
11307
+ fs15.writeFileSync(tmpRotate, YAML10.stringify(doc), "utf-8");
11308
+ fs15.renameSync(tmpRotate, manifestPath);
11304
11309
  } finally {
11305
11310
  try {
11306
- fs14.unlinkSync(tmpRotate);
11311
+ fs15.unlinkSync(tmpRotate);
11307
11312
  } catch {
11308
11313
  }
11309
11314
  }
@@ -11424,8 +11429,8 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
11424
11429
  }
11425
11430
 
11426
11431
  // src/artifact/packer.ts
11427
- var fs15 = __toESM(require("fs"));
11428
- var path18 = __toESM(require("path"));
11432
+ var fs16 = __toESM(require("fs"));
11433
+ var path19 = __toESM(require("path"));
11429
11434
  var crypto4 = __toESM(require("crypto"));
11430
11435
 
11431
11436
  // src/artifact/signer.ts
@@ -11600,9 +11605,9 @@ var ArtifactPacker = class {
11600
11605
  keys: Object.keys(resolved.values)
11601
11606
  };
11602
11607
  }
11603
- const outputDir = path18.dirname(config.outputPath);
11604
- if (!fs15.existsSync(outputDir)) {
11605
- fs15.mkdirSync(outputDir, { recursive: true });
11608
+ const outputDir = path19.dirname(config.outputPath);
11609
+ if (!fs16.existsSync(outputDir)) {
11610
+ fs16.mkdirSync(outputDir, { recursive: true });
11606
11611
  }
11607
11612
  if (config.ttl && config.ttl > 0) {
11608
11613
  artifact.expiresAt = new Date(Date.now() + config.ttl * 1e3).toISOString();
@@ -11621,8 +11626,8 @@ var ArtifactPacker = class {
11621
11626
  }
11622
11627
  const json = JSON.stringify(artifact, null, 2);
11623
11628
  const tmpOutput = `${config.outputPath}.tmp.${process.pid}`;
11624
- fs15.writeFileSync(tmpOutput, json, "utf-8");
11625
- fs15.renameSync(tmpOutput, config.outputPath);
11629
+ fs16.writeFileSync(tmpOutput, json, "utf-8");
11630
+ fs16.renameSync(tmpOutput, config.outputPath);
11626
11631
  return {
11627
11632
  outputPath: config.outputPath,
11628
11633
  namespaceCount: resolved.identity.namespaces.length,
@@ -11635,9 +11640,216 @@ var ArtifactPacker = class {
11635
11640
 
11636
11641
  // src/kms/types.ts
11637
11642
  var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
11643
+
11644
+ // src/migration/backend.ts
11645
+ var fs17 = __toESM(require("fs"));
11646
+ var path20 = __toESM(require("path"));
11647
+ var YAML11 = __toESM(require("yaml"));
11648
+ var BACKEND_KEY_FIELDS = {
11649
+ age: void 0,
11650
+ awskms: "aws_kms_arn",
11651
+ gcpkms: "gcp_kms_resource_id",
11652
+ azurekv: "azure_kv_url",
11653
+ pgp: "pgp_fingerprint"
11654
+ };
11655
+ var ALL_KEY_FIELDS = Object.values(BACKEND_KEY_FIELDS).filter(
11656
+ (v) => v !== void 0
11657
+ );
11658
+ function metadataMatchesTarget(meta, target) {
11659
+ if (meta.backend !== target.backend) return false;
11660
+ if (!target.key) return true;
11661
+ return meta.recipients.includes(target.key);
11662
+ }
11663
+ var BackendMigrator = class {
11664
+ constructor(encryption, matrixManager) {
11665
+ this.encryption = encryption;
11666
+ this.matrixManager = matrixManager;
11667
+ }
11668
+ async migrate(manifest, repoRoot, options, callbacks, onProgress) {
11669
+ const { target, environment, dryRun, skipVerify } = options;
11670
+ if (environment) {
11671
+ const env = manifest.environments.find((e) => e.name === environment);
11672
+ if (!env) {
11673
+ throw new Error(`Environment '${environment}' not found in manifest.`);
11674
+ }
11675
+ }
11676
+ const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);
11677
+ const targetCells = environment ? allCells.filter((c) => c.environment === environment) : allCells;
11678
+ if (targetCells.length === 0) {
11679
+ return {
11680
+ migratedFiles: [],
11681
+ skippedFiles: [],
11682
+ rolledBack: false,
11683
+ verifiedFiles: [],
11684
+ warnings: ["No encrypted files found to migrate."]
11685
+ };
11686
+ }
11687
+ const toMigrate = [];
11688
+ const skippedFiles = [];
11689
+ for (const cell of targetCells) {
11690
+ const meta = await this.encryption.getMetadata(cell.filePath);
11691
+ if (metadataMatchesTarget(meta, target)) {
11692
+ skippedFiles.push(cell.filePath);
11693
+ onProgress?.({
11694
+ type: "skip",
11695
+ file: cell.filePath,
11696
+ message: `${cell.namespace}/${cell.environment}: already on ${target.backend}, skipping`
11697
+ });
11698
+ } else {
11699
+ toMigrate.push(cell);
11700
+ }
11701
+ }
11702
+ if (toMigrate.length === 0) {
11703
+ return {
11704
+ migratedFiles: [],
11705
+ skippedFiles,
11706
+ rolledBack: false,
11707
+ verifiedFiles: [],
11708
+ warnings: ["All files already use the target backend and key. Nothing to migrate."]
11709
+ };
11710
+ }
11711
+ if (dryRun) {
11712
+ const warnings2 = [];
11713
+ for (const cell of toMigrate) {
11714
+ onProgress?.({
11715
+ type: "info",
11716
+ file: cell.filePath,
11717
+ message: `Would migrate ${cell.namespace}/${cell.environment} to ${target.backend}`
11718
+ });
11719
+ }
11720
+ if (environment) {
11721
+ warnings2.push(
11722
+ `Would add per-environment backend override for '${environment}' \u2192 ${target.backend}`
11723
+ );
11724
+ } else {
11725
+ warnings2.push(`Would update global default_backend \u2192 ${target.backend}`);
11726
+ }
11727
+ this.checkAgeRecipientsWarning(manifest, target, environment, warnings2);
11728
+ return {
11729
+ migratedFiles: [],
11730
+ skippedFiles,
11731
+ rolledBack: false,
11732
+ verifiedFiles: [],
11733
+ warnings: warnings2
11734
+ };
11735
+ }
11736
+ const manifestPath = path20.join(repoRoot, CLEF_MANIFEST_FILENAME);
11737
+ const manifestBackup = fs17.readFileSync(manifestPath, "utf-8");
11738
+ const sopsYamlPath = path20.join(repoRoot, ".sops.yaml");
11739
+ const sopsYamlBackup = fs17.existsSync(sopsYamlPath) ? fs17.readFileSync(sopsYamlPath, "utf-8") : void 0;
11740
+ const fileBackups = /* @__PURE__ */ new Map();
11741
+ const doc = readManifestYaml(repoRoot);
11742
+ this.updateManifestDoc(doc, target, environment);
11743
+ writeManifestYaml(repoRoot, doc);
11744
+ const updatedManifest = YAML11.parse(YAML11.stringify(doc));
11745
+ callbacks.regenerateSopsConfig();
11746
+ const migratedFiles = [];
11747
+ for (const cell of toMigrate) {
11748
+ try {
11749
+ fileBackups.set(cell.filePath, fs17.readFileSync(cell.filePath, "utf-8"));
11750
+ onProgress?.({
11751
+ type: "migrate",
11752
+ file: cell.filePath,
11753
+ message: `Migrating ${cell.namespace}/${cell.environment}...`
11754
+ });
11755
+ const decrypted = await this.encryption.decrypt(cell.filePath);
11756
+ await this.encryption.encrypt(
11757
+ cell.filePath,
11758
+ decrypted.values,
11759
+ updatedManifest,
11760
+ cell.environment
11761
+ );
11762
+ migratedFiles.push(cell.filePath);
11763
+ } catch (err) {
11764
+ this.rollback(manifestPath, manifestBackup, sopsYamlPath, sopsYamlBackup, fileBackups);
11765
+ const errorMsg = err instanceof Error ? err.message : String(err);
11766
+ onProgress?.({
11767
+ type: "warn",
11768
+ file: cell.filePath,
11769
+ message: `Migration failed: ${errorMsg}. All changes rolled back.`
11770
+ });
11771
+ return {
11772
+ migratedFiles: [],
11773
+ skippedFiles,
11774
+ rolledBack: true,
11775
+ error: `Failed on ${cell.namespace}/${cell.environment}: ${errorMsg}`,
11776
+ verifiedFiles: [],
11777
+ warnings: ["All changes have been rolled back."]
11778
+ };
11779
+ }
11780
+ }
11781
+ const verifiedFiles = [];
11782
+ const warnings = [];
11783
+ if (!skipVerify) {
11784
+ for (const cell of toMigrate) {
11785
+ try {
11786
+ onProgress?.({
11787
+ type: "verify",
11788
+ file: cell.filePath,
11789
+ message: `Verifying ${cell.namespace}/${cell.environment}...`
11790
+ });
11791
+ await this.encryption.decrypt(cell.filePath);
11792
+ verifiedFiles.push(cell.filePath);
11793
+ } catch (err) {
11794
+ const errorMsg = err instanceof Error ? err.message : String(err);
11795
+ warnings.push(
11796
+ `Verification failed for ${cell.namespace}/${cell.environment}: ${errorMsg}`
11797
+ );
11798
+ }
11799
+ }
11800
+ }
11801
+ this.checkAgeRecipientsWarning(manifest, target, environment, warnings);
11802
+ return { migratedFiles, skippedFiles, rolledBack: false, verifiedFiles, warnings };
11803
+ }
11804
+ // ── Private helpers ──────────────────────────────────────────────────
11805
+ updateManifestDoc(doc, target, environment) {
11806
+ const keyField = BACKEND_KEY_FIELDS[target.backend];
11807
+ if (environment) {
11808
+ const environments = doc.environments;
11809
+ const envDoc = environments.find(
11810
+ (e) => e.name === environment
11811
+ );
11812
+ const sopsOverride = { backend: target.backend };
11813
+ if (keyField && target.key) {
11814
+ sopsOverride[keyField] = target.key;
11815
+ }
11816
+ envDoc.sops = sopsOverride;
11817
+ } else {
11818
+ const sops = doc.sops;
11819
+ sops.default_backend = target.backend;
11820
+ for (const field of ALL_KEY_FIELDS) {
11821
+ delete sops[field];
11822
+ }
11823
+ if (keyField && target.key) {
11824
+ sops[keyField] = target.key;
11825
+ }
11826
+ }
11827
+ }
11828
+ rollback(manifestPath, manifestBackup, sopsYamlPath, sopsYamlBackup, fileBackups) {
11829
+ for (const [filePath, backup] of fileBackups) {
11830
+ fs17.writeFileSync(filePath, backup, "utf-8");
11831
+ }
11832
+ if (sopsYamlBackup !== void 0) {
11833
+ fs17.writeFileSync(sopsYamlPath, sopsYamlBackup, "utf-8");
11834
+ } else if (fs17.existsSync(sopsYamlPath)) {
11835
+ fs17.unlinkSync(sopsYamlPath);
11836
+ }
11837
+ fs17.writeFileSync(manifestPath, manifestBackup, "utf-8");
11838
+ }
11839
+ checkAgeRecipientsWarning(manifest, target, environment, warnings) {
11840
+ if (target.backend === "age") return;
11841
+ const hasRecipients = environment ? manifest.environments.find((e) => e.name === environment)?.recipients?.length : manifest.environments.some((e) => e.recipients?.length);
11842
+ if (hasRecipients) {
11843
+ warnings.push(
11844
+ "Per-environment age recipients are no longer used for encryption on the migrated environments. Consider removing them from clef.yaml if they are no longer needed."
11845
+ );
11846
+ }
11847
+ }
11848
+ };
11638
11849
  // Annotate the CommonJS export names for ESM import in node:
11639
11850
  0 && (module.exports = {
11640
11851
  ArtifactPacker,
11852
+ BackendMigrator,
11641
11853
  BulkOps,
11642
11854
  CLEF_MANIFEST_FILENAME,
11643
11855
  CLEF_REPORT_SCHEMA_VERSION,
@@ -11705,6 +11917,7 @@ var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
11705
11917
  parseIgnoreContent,
11706
11918
  parseJson,
11707
11919
  parseYaml,
11920
+ readManifestYaml,
11708
11921
  redactValue,
11709
11922
  removeAccessRequest,
11710
11923
  requestsFilePath,
@@ -11722,10 +11935,13 @@ var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
11722
11935
  signKms,
11723
11936
  upsertRequest,
11724
11937
  validateAgePublicKey,
11725
- verifySignature
11938
+ verifySignature,
11939
+ writeManifestYaml
11726
11940
  });
11727
11941
  /*! Bundled license information:
11728
11942
 
11943
+ @noble/hashes/utils.js:
11944
+ @noble/hashes/utils.js:
11729
11945
  @noble/hashes/utils.js:
11730
11946
  (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11731
11947
 
@@ -11735,42 +11951,18 @@ var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
11735
11951
  @noble/ciphers/utils.js:
11736
11952
  (*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) *)
11737
11953
 
11738
- @noble/hashes/utils.js:
11739
- (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11740
-
11741
11954
  @noble/curves/utils.js:
11742
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11743
-
11744
11955
  @noble/curves/abstract/modular.js:
11745
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11746
-
11747
11956
  @noble/curves/abstract/curve.js:
11748
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11749
-
11750
11957
  @noble/curves/abstract/montgomery.js:
11751
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11752
-
11753
11958
  @noble/curves/abstract/weierstrass.js:
11754
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11755
-
11756
11959
  @noble/curves/ed25519.js:
11757
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11758
-
11759
11960
  @noble/curves/nist.js:
11760
11961
  (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11761
11962
 
11762
- @noble/hashes/utils.js:
11763
- (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11764
-
11765
11963
  @noble/post-quantum/utils.js:
11766
- (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
11767
-
11768
11964
  @noble/post-quantum/_crystals.js:
11769
- (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
11770
-
11771
11965
  @noble/post-quantum/ml-kem.js:
11772
- (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
11773
-
11774
11966
  @noble/post-quantum/hybrid.js:
11775
11967
  (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
11776
11968
  */