@kitsy/cnos 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +3 -3
  2. package/dist/build/index.cjs +1003 -121
  3. package/dist/build/index.d.cts +1 -1
  4. package/dist/build/index.d.ts +1 -1
  5. package/dist/build/index.js +22 -10
  6. package/dist/{chunk-APCTXRUN.js → chunk-APIU4GTB.js} +1012 -195
  7. package/dist/chunk-EQSKV3DP.js +105 -0
  8. package/dist/{chunk-MLQGYCO7.js → chunk-FWJC4Y2D.js} +1 -1
  9. package/dist/{chunk-RD5WMHPM.js → chunk-HMM76UYZ.js} +1 -1
  10. package/dist/{chunk-EIN55XXA.js → chunk-J4K4JUJL.js} +1 -1
  11. package/dist/{chunk-SO5XREEU.js → chunk-JSBVYK2T.js} +32 -11
  12. package/dist/chunk-LJD4SM32.js +189 -0
  13. package/dist/{chunk-SXTMTACL.js → chunk-T6Y57KTT.js} +20 -31
  14. package/dist/chunk-WCHX2QFY.js +115 -0
  15. package/dist/{chunk-ZA74BO47.js → chunk-ZTPSFXWP.js} +1 -1
  16. package/dist/configure/index.cjs +3021 -0
  17. package/dist/configure/index.d.cts +12 -0
  18. package/dist/configure/index.d.ts +12 -0
  19. package/dist/configure/index.js +24 -0
  20. package/dist/{envNaming-CcsqAel3.d.ts → envNaming-Dvm_LP2D.d.ts} +1 -1
  21. package/dist/{envNaming-BTJpH93W.d.cts → envNaming-S4B-dHUx.d.cts} +1 -1
  22. package/dist/index.cjs +1243 -186
  23. package/dist/index.d.cts +2 -13
  24. package/dist/index.d.ts +2 -13
  25. package/dist/index.js +13 -25
  26. package/dist/internal.cjs +1525 -81
  27. package/dist/internal.d.cts +171 -14
  28. package/dist/internal.d.ts +171 -14
  29. package/dist/internal.js +652 -5
  30. package/dist/plugin/basic-schema.cjs +29 -2
  31. package/dist/plugin/basic-schema.d.cts +1 -1
  32. package/dist/plugin/basic-schema.d.ts +1 -1
  33. package/dist/plugin/basic-schema.js +2 -2
  34. package/dist/plugin/cli-args.cjs +29 -2
  35. package/dist/plugin/cli-args.d.cts +1 -1
  36. package/dist/plugin/cli-args.d.ts +1 -1
  37. package/dist/plugin/cli-args.js +2 -2
  38. package/dist/plugin/dotenv.cjs +36 -9
  39. package/dist/plugin/dotenv.d.cts +2 -2
  40. package/dist/plugin/dotenv.d.ts +2 -2
  41. package/dist/plugin/dotenv.js +2 -2
  42. package/dist/plugin/env-export.cjs +31 -2
  43. package/dist/plugin/env-export.d.cts +2 -2
  44. package/dist/plugin/env-export.d.ts +2 -2
  45. package/dist/plugin/env-export.js +2 -2
  46. package/dist/plugin/filesystem.cjs +65 -91
  47. package/dist/plugin/filesystem.d.cts +1 -1
  48. package/dist/plugin/filesystem.d.ts +1 -1
  49. package/dist/plugin/filesystem.js +2 -2
  50. package/dist/plugin/process-env.cjs +105 -11
  51. package/dist/plugin/process-env.d.cts +4 -3
  52. package/dist/plugin/process-env.d.ts +4 -3
  53. package/dist/plugin/process-env.js +6 -4
  54. package/dist/{plugin-DkOIT5uI.d.cts → plugin-B4xwySxw.d.cts} +15 -2
  55. package/dist/{plugin-DkOIT5uI.d.ts → plugin-B4xwySxw.d.ts} +15 -2
  56. package/dist/runtime/index.cjs +1057 -136
  57. package/dist/runtime/index.d.cts +1 -1
  58. package/dist/runtime/index.d.ts +1 -1
  59. package/dist/runtime/index.js +11 -186
  60. package/dist/{toPublicEnv-C9clvXLo.d.ts → toPublicEnv-CvhGAfsB.d.ts} +1 -1
  61. package/dist/{toPublicEnv-DvFeV3qG.d.cts → toPublicEnv-ggmphZFs.d.cts} +1 -1
  62. package/package.json +11 -1
  63. package/dist/chunk-JUHPBAEH.js +0 -20
  64. package/dist/chunk-PQ4KSV76.js +0 -50
  65. package/dist/chunk-WHUGFPE4.js +0 -49
@@ -53,6 +53,11 @@ var CnosSecurityError = class extends CnosError {
53
53
  super(message);
54
54
  }
55
55
  };
56
+ var CnosAuthenticationError = class extends CnosError {
57
+ constructor(message) {
58
+ super(message);
59
+ }
60
+ };
56
61
  var CnosKeyNotFoundError = class extends CnosError {
57
62
  constructor(key) {
58
63
  super(`Missing required CNOS config key: ${key}`);
@@ -105,6 +110,68 @@ function createProvenanceInspector() {
105
110
  };
106
111
  }
107
112
 
113
+ // ../core/src/keychain/linux.ts
114
+ var import_node_child_process = require("child_process");
115
+ var import_node_util = require("util");
116
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
117
+ async function readLinuxKeychain(entry) {
118
+ try {
119
+ const { stdout } = await execFileAsync("secret-tool", ["lookup", "service", "cnos", "account", entry]);
120
+ const value = stdout.trim();
121
+ return value.length > 0 ? value : void 0;
122
+ } catch {
123
+ return void 0;
124
+ }
125
+ }
126
+
127
+ // ../core/src/keychain/macos.ts
128
+ var import_node_child_process2 = require("child_process");
129
+ var import_node_util2 = require("util");
130
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
131
+ async function readMacosKeychain(entry) {
132
+ try {
133
+ const { stdout } = await execFileAsync2("security", ["find-generic-password", "-a", "cnos", "-s", entry, "-w"]);
134
+ const value = stdout.trim();
135
+ return value.length > 0 ? value : void 0;
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
140
+
141
+ // ../core/src/keychain/windows.ts
142
+ var import_node_child_process3 = require("child_process");
143
+ var import_node_util3 = require("util");
144
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
145
+ function wrap(script) {
146
+ return ["-NoProfile", "-Command", script];
147
+ }
148
+ async function readWindowsKeychain(entry) {
149
+ try {
150
+ const { stdout } = await execFileAsync3(
151
+ "powershell",
152
+ wrap(`Add-Type -AssemblyName System.Runtime.WindowsRuntime; [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime] > $null; $vault = New-Object Windows.Security.Credentials.PasswordVault; $credential = $vault.Retrieve('cnos','${entry}'); $credential.RetrievePassword(); Write-Output $credential.Password`)
153
+ );
154
+ const value = stdout.trim();
155
+ return value.length > 0 ? value : void 0;
156
+ } catch {
157
+ return void 0;
158
+ }
159
+ }
160
+
161
+ // ../core/src/keychain/index.ts
162
+ async function readKeychain(entry) {
163
+ if (process.platform === "win32") {
164
+ return readWindowsKeychain(entry);
165
+ }
166
+ if (process.platform === "darwin") {
167
+ return readMacosKeychain(entry);
168
+ }
169
+ if (process.platform === "linux") {
170
+ return readLinuxKeychain(entry);
171
+ }
172
+ return void 0;
173
+ }
174
+
108
175
  // ../core/src/manifest/loadManifest.ts
109
176
  var import_promises2 = require("fs/promises");
110
177
  var import_node_path2 = __toESM(require("path"), 1);
@@ -191,6 +258,9 @@ var import_yaml = require("yaml");
191
258
  function parseYaml(source) {
192
259
  return (0, import_yaml.parse)(source);
193
260
  }
261
+ function stringifyYaml(value) {
262
+ return (0, import_yaml.stringify)(value);
263
+ }
194
264
 
195
265
  // ../core/src/manifest/normalizeManifest.ts
196
266
  var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
@@ -224,6 +294,11 @@ var DEFAULT_NAMESPACES = {
224
294
  shareable: false,
225
295
  readonly: true
226
296
  },
297
+ process: {
298
+ kind: "system",
299
+ shareable: false,
300
+ readonly: true
301
+ },
227
302
  public: {
228
303
  kind: "projection",
229
304
  source: "promote",
@@ -278,22 +353,82 @@ function normalizeNamespaces(namespaces) {
278
353
  function normalizeVaults(vaults) {
279
354
  return Object.fromEntries(
280
355
  Object.entries(vaults ?? {}).map(([name, definition]) => {
356
+ const legacyPassphrase = definition.passphrase;
357
+ if (legacyPassphrase !== void 0) {
358
+ throw new CnosManifestError(
359
+ `Vault "${name}" uses legacy passphrase configuration. Use vaults.${name}.auth instead.`
360
+ );
361
+ }
281
362
  const provider = definition.provider?.trim();
282
363
  if (!provider) {
283
364
  throw new CnosManifestError(`Vault "${name}" requires a provider`);
284
365
  }
366
+ const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
367
+ const normalizedMapping = Object.fromEntries(
368
+ Object.entries(definition.mapping ?? {}).filter(
369
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
370
+ ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
371
+ );
285
372
  return [
286
373
  name,
287
374
  {
288
375
  provider,
289
- ...definition.passphrase?.trim() ? {
290
- passphrase: definition.passphrase.trim()
376
+ auth: normalizedAuth,
377
+ ...Object.keys(normalizedMapping).length > 0 ? {
378
+ mapping: normalizedMapping
291
379
  } : {}
292
380
  }
293
381
  ];
294
382
  })
295
383
  );
296
384
  }
385
+ function normalizeAuthSources(value) {
386
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
387
+ return void 0;
388
+ }
389
+ const sources = Array.isArray(value.from) ? value.from : void 0;
390
+ const normalized = (sources ?? []).map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
391
+ return normalized.length > 0 ? normalized : void 0;
392
+ }
393
+ function normalizeVaultAuth(vaultName, provider, auth) {
394
+ if (provider === "local") {
395
+ const passphraseSources = normalizeAuthSources(auth?.passphrase);
396
+ const defaultToken = vaultName.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
397
+ const defaultSources = [
398
+ ...defaultToken ? [`env:CNOS_SECRET_PASSPHRASE_${defaultToken}`] : [],
399
+ "env:CNOS_SECRET_PASSPHRASE",
400
+ `keychain:cnos/${vaultName}`,
401
+ "prompt"
402
+ ];
403
+ return {
404
+ method: auth?.method ?? "passphrase",
405
+ passphrase: {
406
+ from: passphraseSources ?? defaultSources
407
+ },
408
+ ...auth?.config ? { config: auth.config } : {}
409
+ };
410
+ }
411
+ if (provider === "github-secrets") {
412
+ return {
413
+ method: auth?.method ?? "environment",
414
+ ...auth?.config ? { config: auth.config } : {}
415
+ };
416
+ }
417
+ return {
418
+ ...auth?.method ? { method: auth.method } : {},
419
+ ...normalizeAuthSources(auth?.passphrase) ? {
420
+ passphrase: {
421
+ from: normalizeAuthSources(auth?.passphrase) ?? []
422
+ }
423
+ } : {},
424
+ ...normalizeAuthSources(auth?.token) ? {
425
+ token: {
426
+ from: normalizeAuthSources(auth?.token) ?? []
427
+ }
428
+ } : {},
429
+ ...auth?.config ? { config: auth.config } : {}
430
+ };
431
+ }
297
432
  function normalizeManifest(manifest) {
298
433
  const version = manifest.version ?? 1;
299
434
  if (version !== 1) {
@@ -823,12 +958,15 @@ async function exists2(targetPath) {
823
958
  return false;
824
959
  }
825
960
  }
826
- async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId) {
961
+ async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
827
962
  const workspaceRoot = import_node_path5.default.join(manifestRoot, "workspaces", workspaceId);
828
963
  if (await exists2(workspaceRoot)) {
829
964
  return workspaceRoot;
830
965
  }
831
- const legacyMarkers = ["values", "secrets", "env", "profiles"].map(
966
+ const customDataNamespaceRoots = Object.entries(manifest.namespaces).filter(
967
+ ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
968
+ ).map(([namespace]) => namespace);
969
+ const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
832
970
  (segment) => import_node_path5.default.join(manifestRoot, segment)
833
971
  );
834
972
  if ((await Promise.all(legacyMarkers.map((marker) => exists2(marker)))).some(Boolean)) {
@@ -915,7 +1053,7 @@ async function resolveWorkspaceContext(manifest, options) {
915
1053
  workspaceRoots.push({
916
1054
  scope: "local",
917
1055
  workspaceId: chainWorkspaceId,
918
- path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId)
1056
+ path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId, manifest)
919
1057
  });
920
1058
  }
921
1059
  return {
@@ -1080,6 +1218,7 @@ async function runPipeline(options) {
1080
1218
  const collectedEntries = await Promise.all(
1081
1219
  options.plugins.map(
1082
1220
  (plugin) => plugin.load({
1221
+ manifest: options.manifest,
1083
1222
  manifestConfig: {
1084
1223
  ...options.manifest.sources[plugin.id] ?? {},
1085
1224
  envMapping: options.manifest.envMapping
@@ -1097,6 +1236,712 @@ async function runPipeline(options) {
1097
1236
  return collectedEntries.flat();
1098
1237
  }
1099
1238
 
1239
+ // ../core/src/secrets/auditLog.ts
1240
+ var import_promises8 = require("fs/promises");
1241
+ var import_node_path8 = __toESM(require("path"), 1);
1242
+
1243
+ // ../core/src/utils/secretStore.ts
1244
+ var import_node_crypto = require("crypto");
1245
+ var import_promises7 = require("fs/promises");
1246
+ var import_node_path7 = __toESM(require("path"), 1);
1247
+
1248
+ // ../core/src/secrets/sessionStore.ts
1249
+ var import_promises6 = require("fs/promises");
1250
+ var import_node_path6 = __toESM(require("path"), 1);
1251
+ function buildSessionRoot(processEnv = process.env) {
1252
+ return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
1253
+ }
1254
+ function buildSessionPath(vault, processEnv) {
1255
+ return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1256
+ }
1257
+ async function readVaultSessionKey(vault, processEnv) {
1258
+ try {
1259
+ const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
1260
+ const document = JSON.parse(source);
1261
+ if (document.version !== 1 || typeof document.derivedKey !== "string") {
1262
+ return void 0;
1263
+ }
1264
+ const key = Buffer.from(document.derivedKey, "hex");
1265
+ return key.length > 0 ? key : void 0;
1266
+ } catch {
1267
+ return void 0;
1268
+ }
1269
+ }
1270
+
1271
+ // ../core/src/utils/secretStore.ts
1272
+ var KEY_LENGTH = 32;
1273
+ var SALT_LENGTH = 32;
1274
+ var IV_LENGTH = 12;
1275
+ var AUTH_TAG_LENGTH = 16;
1276
+ var PBKDF2_ITERATIONS = 6e5;
1277
+ var KEYSTORE_VERSION = 1;
1278
+ var METADATA_VERSION = 1;
1279
+ var META_FILENAME = "meta.yml";
1280
+ var KEYSTORE_FILENAME = "keystore.enc";
1281
+ function isObject(value) {
1282
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1283
+ }
1284
+ function isSecretReference(value) {
1285
+ return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
1286
+ }
1287
+ function resolveSecretStoreRoot(processEnv = process.env) {
1288
+ return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1289
+ }
1290
+ function normalizeVaultToken(vault = "default") {
1291
+ return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1292
+ }
1293
+ function getVaultPassphraseEnvVar(vault = "default") {
1294
+ const vaultToken = normalizeVaultToken(vault);
1295
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
1296
+ }
1297
+ function getVaultSessionKeyEnvVar(vault = "default") {
1298
+ const vaultToken = normalizeVaultToken(vault);
1299
+ return `__CNOS_VAULT_KEY_${vaultToken || "DEFAULT"}__`;
1300
+ }
1301
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1302
+ return processEnv[getVaultPassphraseEnvVar(vault)] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1303
+ }
1304
+ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1305
+ const encoded = processEnv[getVaultSessionKeyEnvVar(vault)];
1306
+ if (!encoded) {
1307
+ return readVaultSessionKey(vault, processEnv);
1308
+ }
1309
+ try {
1310
+ const key = Buffer.from(encoded, "hex");
1311
+ return key.length === KEY_LENGTH ? key : void 0;
1312
+ } catch {
1313
+ return void 0;
1314
+ }
1315
+ }
1316
+ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1317
+ return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1318
+ }
1319
+ function buildMetaPath(storeRoot, vault = "default") {
1320
+ return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
1321
+ }
1322
+ function buildKeystorePath(storeRoot, vault = "default") {
1323
+ return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1324
+ }
1325
+ function buildLegacyVaultFile(storeRoot, vault = "default") {
1326
+ return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
1327
+ }
1328
+ function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1329
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
1330
+ }
1331
+ function assertVaultMetadata(value, filePath) {
1332
+ if (!isObject(value)) {
1333
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1334
+ }
1335
+ if (value.version !== METADATA_VERSION || value.algorithm !== "aes-256-gcm" || value.kdf !== "pbkdf2-sha512" || typeof value.iterations !== "number" || typeof value.salt !== "string" || typeof value.createdAt !== "string" || typeof value.secretCount !== "number") {
1336
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1337
+ }
1338
+ return value;
1339
+ }
1340
+ async function exists3(targetPath) {
1341
+ try {
1342
+ await (0, import_promises7.stat)(targetPath);
1343
+ return true;
1344
+ } catch {
1345
+ return false;
1346
+ }
1347
+ }
1348
+ async function detectLegacyVaultFormat(storeRoot, vault = "default") {
1349
+ const legacyFile = buildLegacyVaultFile(storeRoot, vault);
1350
+ const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
1351
+ if (await exists3(legacyFile)) {
1352
+ return legacyFile;
1353
+ }
1354
+ if (await exists3(legacyStore)) {
1355
+ return legacyStore;
1356
+ }
1357
+ return void 0;
1358
+ }
1359
+ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1360
+ const legacyPath = await detectLegacyVaultFormat(storeRoot, vault);
1361
+ if (!legacyPath) {
1362
+ return;
1363
+ }
1364
+ throw new CnosSecurityError(
1365
+ `Legacy CNOS local vault format detected for vault "${vault}" at ${legacyPath}. CNOS 1.4 requires the new keystore format. Remove and recreate the vault.`
1366
+ );
1367
+ }
1368
+ function encryptPayload(payload, key) {
1369
+ const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1370
+ const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
1371
+ const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1372
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1373
+ const tag = cipher.getAuthTag();
1374
+ return Buffer.concat([
1375
+ Buffer.from(Uint32Array.of(KEYSTORE_VERSION).buffer),
1376
+ iv,
1377
+ tag,
1378
+ ciphertext
1379
+ ]);
1380
+ }
1381
+ function decryptPayload(buffer, key) {
1382
+ if (buffer.length < 4 + IV_LENGTH + AUTH_TAG_LENGTH) {
1383
+ throw new CnosSecurityError("Invalid CNOS local vault keystore");
1384
+ }
1385
+ const version = buffer.readUInt32LE(0);
1386
+ if (version !== KEYSTORE_VERSION) {
1387
+ throw new CnosSecurityError(`Unsupported CNOS local vault keystore version: ${version}`);
1388
+ }
1389
+ const ivOffset = 4;
1390
+ const tagOffset = ivOffset + IV_LENGTH;
1391
+ const cipherOffset = tagOffset + AUTH_TAG_LENGTH;
1392
+ const iv = buffer.subarray(ivOffset, tagOffset);
1393
+ const tag = buffer.subarray(tagOffset, cipherOffset);
1394
+ const ciphertext = buffer.subarray(cipherOffset);
1395
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1396
+ decipher.setAuthTag(tag);
1397
+ try {
1398
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
1399
+ const payload = JSON.parse(plaintext);
1400
+ if (!payload || !isObject(payload.secrets) || !isObject(payload.metadata)) {
1401
+ throw new Error("invalid");
1402
+ }
1403
+ return {
1404
+ secrets: Object.fromEntries(
1405
+ Object.entries(payload.secrets).filter((entry) => typeof entry[1] === "string")
1406
+ ),
1407
+ metadata: Object.fromEntries(
1408
+ Object.entries(payload.metadata).filter(
1409
+ (entry) => isObject(entry[1]) && typeof entry[1].createdAt === "string" && typeof entry[1].updatedAt === "string"
1410
+ )
1411
+ )
1412
+ };
1413
+ } catch {
1414
+ throw new CnosAuthenticationError("Failed to decrypt CNOS local vault. Check vault authentication.");
1415
+ }
1416
+ }
1417
+ function buildInitialPayload() {
1418
+ return {
1419
+ secrets: {},
1420
+ metadata: {}
1421
+ };
1422
+ }
1423
+ async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1424
+ const metaPath = buildMetaPath(storeRoot, vault);
1425
+ const keystorePath = buildKeystorePath(storeRoot, vault);
1426
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
1427
+ await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1428
+ await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
1429
+ }
1430
+ async function readVaultMetadata(storeRoot, vault = "default") {
1431
+ await assertNoLegacyVaultFormat(storeRoot, vault);
1432
+ const metaPath = buildMetaPath(storeRoot, vault);
1433
+ try {
1434
+ const source = await (0, import_promises7.readFile)(metaPath, "utf8");
1435
+ return assertVaultMetadata(parseYaml(source), metaPath);
1436
+ } catch (error) {
1437
+ if (error.code === "ENOENT") {
1438
+ return void 0;
1439
+ }
1440
+ throw error;
1441
+ }
1442
+ }
1443
+ async function createSecretVault(storeRoot, vault, passphrase) {
1444
+ const normalizedVault = vault.trim() || "default";
1445
+ await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1446
+ const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
1447
+ const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1448
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1449
+ const meta = {
1450
+ version: METADATA_VERSION,
1451
+ algorithm: "aes-256-gcm",
1452
+ kdf: "pbkdf2-sha512",
1453
+ iterations: PBKDF2_ITERATIONS,
1454
+ salt: salt.toString("base64"),
1455
+ createdAt,
1456
+ secretCount: 0
1457
+ };
1458
+ await writeVaultFiles(storeRoot, normalizedVault, meta, buildInitialPayload(), key);
1459
+ return buildMetaPath(storeRoot, normalizedVault);
1460
+ }
1461
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
1462
+ const normalizedVault = vault.trim() || "default";
1463
+ const meta = await readVaultMetadata(storeRoot, normalizedVault);
1464
+ if (meta) {
1465
+ return buildMetaPath(storeRoot, normalizedVault);
1466
+ }
1467
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
1468
+ }
1469
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
1470
+ if (definition?.provider !== "local") {
1471
+ return void 0;
1472
+ }
1473
+ const configuredSources = definition.auth?.passphrase?.from ?? [];
1474
+ for (const source of configuredSources) {
1475
+ if (source.startsWith("env:")) {
1476
+ const value = processEnv[source.slice(4)];
1477
+ if (value) {
1478
+ return value;
1479
+ }
1480
+ }
1481
+ }
1482
+ return resolveSecretPassphrase(vault, processEnv);
1483
+ }
1484
+ async function resolveVaultAccessKey(storeRoot, definition, vault = "default", processEnv = process.env) {
1485
+ if (definition?.provider !== "local") {
1486
+ return definition?.provider === "github-secrets" ? {
1487
+ method: definition.auth?.method ?? "environment",
1488
+ ...definition?.auth?.config ? { config: definition.auth.config } : {}
1489
+ } : void 0;
1490
+ }
1491
+ const sessionKey = await resolveVaultSessionKey(vault, processEnv);
1492
+ if (sessionKey) {
1493
+ return {
1494
+ derivedKey: sessionKey,
1495
+ method: "keychain",
1496
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1497
+ };
1498
+ }
1499
+ const passphrase = resolveConfiguredVaultPassphrase(definition, vault, processEnv);
1500
+ if (passphrase) {
1501
+ return {
1502
+ passphrase,
1503
+ method: "passphrase",
1504
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1505
+ };
1506
+ }
1507
+ const metadata = await readVaultMetadata(storeRoot, vault);
1508
+ if (!metadata) {
1509
+ return void 0;
1510
+ }
1511
+ throw new CnosAuthenticationError(
1512
+ `Cannot authenticate to vault "${vault}". Set ${getVaultPassphraseEnvVar(vault)} or run cnos vault auth ${vault}.`
1513
+ );
1514
+ }
1515
+ async function loadVaultPayload(storeRoot, vault, auth) {
1516
+ const meta = await readVaultMetadata(storeRoot, vault);
1517
+ if (!meta) {
1518
+ throw new CnosManifestError(`Missing CNOS vault metadata for "${vault}"`);
1519
+ }
1520
+ const salt = Buffer.from(meta.salt, "base64");
1521
+ const key = auth.derivedKey ?? (auth.passphrase ? deriveVaultKey(auth.passphrase, salt, meta.iterations) : void 0);
1522
+ if (!key) {
1523
+ throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1524
+ }
1525
+ const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
1526
+ return {
1527
+ meta,
1528
+ payload: decryptPayload(buffer, key),
1529
+ key
1530
+ };
1531
+ }
1532
+ async function writeLocalSecret(storeRoot, ref, value, authOrPassphrase, vault = "default") {
1533
+ const auth = typeof authOrPassphrase === "string" ? {
1534
+ passphrase: authOrPassphrase,
1535
+ method: "passphrase"
1536
+ } : authOrPassphrase;
1537
+ if (auth.passphrase) {
1538
+ await ensureSecretVault(storeRoot, vault, auth.passphrase);
1539
+ } else {
1540
+ const meta2 = await readVaultMetadata(storeRoot, vault);
1541
+ if (!meta2) {
1542
+ throw new CnosAuthenticationError(`Vault "${vault}" requires passphrase-based authentication for initial creation.`);
1543
+ }
1544
+ }
1545
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1546
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1547
+ const existing = payload.metadata[ref];
1548
+ payload.secrets[ref] = value;
1549
+ payload.metadata[ref] = {
1550
+ createdAt: existing?.createdAt ?? now,
1551
+ updatedAt: now
1552
+ };
1553
+ const nextMeta = {
1554
+ ...meta,
1555
+ secretCount: Object.keys(payload.secrets).length
1556
+ };
1557
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1558
+ return buildKeystorePath(storeRoot, vault);
1559
+ }
1560
+ async function deleteLocalSecret(storeRoot, ref, auth, vault = "default") {
1561
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1562
+ if (!(ref in payload.secrets)) {
1563
+ return false;
1564
+ }
1565
+ delete payload.secrets[ref];
1566
+ delete payload.metadata[ref];
1567
+ const nextMeta = {
1568
+ ...meta,
1569
+ secretCount: Object.keys(payload.secrets).length
1570
+ };
1571
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1572
+ return true;
1573
+ }
1574
+ async function readLocalSecret(storeRoot, ref, auth, vault = "default") {
1575
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1576
+ const value = payload.secrets[ref];
1577
+ if (value === void 0) {
1578
+ throw new CnosManifestError(`Missing local secret ref "${ref}" in vault "${vault}"`);
1579
+ }
1580
+ return value;
1581
+ }
1582
+ async function listLocalSecrets(storeRoot, auth, vault = "default") {
1583
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1584
+ return Object.keys(payload.secrets).sort((left, right) => left.localeCompare(right));
1585
+ }
1586
+ function resolveVaultDefinition(vaults, vault = "default") {
1587
+ const definition = vaults?.[vault];
1588
+ const provider = definition?.provider ?? "local";
1589
+ return {
1590
+ name: vault,
1591
+ provider,
1592
+ ...definition?.auth ? { auth: definition.auth } : {},
1593
+ ...definition?.mapping ? { mapping: definition.mapping } : {},
1594
+ requiresAuthentication: provider === "local"
1595
+ };
1596
+ }
1597
+
1598
+ // ../core/src/secrets/auditLog.ts
1599
+ async function appendAuditEvent(event, processEnv = process.env) {
1600
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1601
+ await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
1602
+ await (0, import_promises8.appendFile)(
1603
+ auditFile,
1604
+ `${JSON.stringify({
1605
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1606
+ ...event
1607
+ })}
1608
+ `,
1609
+ "utf8"
1610
+ );
1611
+ }
1612
+
1613
+ // ../core/src/secrets/secretCache.ts
1614
+ var SecretCache = class {
1615
+ cache = /* @__PURE__ */ new Map();
1616
+ authenticated = /* @__PURE__ */ new Set();
1617
+ load(vaultId, secrets) {
1618
+ this.authenticated.add(vaultId);
1619
+ for (const [ref, value] of secrets) {
1620
+ this.cache.set(`${vaultId}:${ref}`, value);
1621
+ }
1622
+ }
1623
+ isVaultAuthenticated(vaultId) {
1624
+ return this.authenticated.has(vaultId);
1625
+ }
1626
+ get(vaultId, ref) {
1627
+ return this.cache.get(`${vaultId}:${ref}`);
1628
+ }
1629
+ clear(vaultId) {
1630
+ if (!vaultId) {
1631
+ this.cache.clear();
1632
+ this.authenticated.clear();
1633
+ return;
1634
+ }
1635
+ this.authenticated.delete(vaultId);
1636
+ for (const key of Array.from(this.cache.keys())) {
1637
+ if (key.startsWith(`${vaultId}:`)) {
1638
+ this.cache.delete(key);
1639
+ }
1640
+ }
1641
+ }
1642
+ };
1643
+
1644
+ // ../core/src/secrets/providers/github.ts
1645
+ var GithubSecretsVaultProvider = class {
1646
+ constructor(vaultId, definition, processEnv = process.env) {
1647
+ this.vaultId = vaultId;
1648
+ this.definition = definition;
1649
+ this.processEnv = processEnv;
1650
+ }
1651
+ vaultId;
1652
+ definition;
1653
+ processEnv;
1654
+ authenticated = false;
1655
+ async authenticate(_authConfig) {
1656
+ void _authConfig;
1657
+ this.authenticated = true;
1658
+ }
1659
+ isAuthenticated() {
1660
+ return this.authenticated;
1661
+ }
1662
+ resolveEnvVar(ref) {
1663
+ if (this.processEnv[ref] !== void 0) {
1664
+ return ref;
1665
+ }
1666
+ return Object.entries(this.definition.mapping ?? {}).find(([, logicalRef]) => logicalRef === ref)?.[0];
1667
+ }
1668
+ async batchGet(refs) {
1669
+ this.authenticated = true;
1670
+ const resolved = /* @__PURE__ */ new Map();
1671
+ for (const ref of Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right))) {
1672
+ const envVar = this.resolveEnvVar(ref);
1673
+ const value = envVar ? this.processEnv[envVar] : void 0;
1674
+ if (value !== void 0) {
1675
+ resolved.set(ref, value);
1676
+ }
1677
+ }
1678
+ return resolved;
1679
+ }
1680
+ async get(ref) {
1681
+ const envVar = this.resolveEnvVar(ref);
1682
+ this.authenticated = true;
1683
+ return envVar ? this.processEnv[envVar] : void 0;
1684
+ }
1685
+ async set(ref, value) {
1686
+ void ref;
1687
+ void value;
1688
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be written by CNOS.`);
1689
+ }
1690
+ async delete(ref) {
1691
+ void ref;
1692
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be mutated by CNOS.`);
1693
+ }
1694
+ async list() {
1695
+ return Object.values(this.definition.mapping ?? {}).sort((left, right) => left.localeCompare(right));
1696
+ }
1697
+ };
1698
+
1699
+ // ../core/src/secrets/providers/local.ts
1700
+ var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
1701
+ constructor(vaultId, definition, processEnv = process.env, storeRoot = resolveSecretStoreRoot(processEnv)) {
1702
+ this.vaultId = vaultId;
1703
+ this.processEnv = processEnv;
1704
+ this.storeRoot = storeRoot;
1705
+ this.definition = definition;
1706
+ }
1707
+ vaultId;
1708
+ processEnv;
1709
+ storeRoot;
1710
+ authConfig;
1711
+ definition;
1712
+ static fromVaults(vaults, vaultId, processEnv) {
1713
+ const definition = resolveVaultDefinition(vaults, vaultId);
1714
+ return new _LocalSecretVaultProvider(vaultId, definition, processEnv);
1715
+ }
1716
+ async authenticate(authConfig) {
1717
+ this.authConfig = authConfig;
1718
+ await this.list();
1719
+ }
1720
+ isAuthenticated() {
1721
+ return Boolean(this.authConfig);
1722
+ }
1723
+ async requireAuth() {
1724
+ if (this.authConfig) {
1725
+ return this.authConfig;
1726
+ }
1727
+ const resolved = await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1728
+ if (!resolved) {
1729
+ throw new CnosAuthenticationError(
1730
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1731
+ );
1732
+ }
1733
+ this.authConfig = resolved;
1734
+ return resolved;
1735
+ }
1736
+ async batchGet(refs) {
1737
+ const auth = await this.requireAuth();
1738
+ const entries = await Promise.all(
1739
+ Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right)).map(async (ref) => [ref, await readLocalSecret(this.storeRoot, ref, auth, this.vaultId)])
1740
+ );
1741
+ return new Map(entries);
1742
+ }
1743
+ async get(ref) {
1744
+ const auth = await this.requireAuth();
1745
+ try {
1746
+ return await readLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1747
+ } catch {
1748
+ return void 0;
1749
+ }
1750
+ }
1751
+ async set(ref, value) {
1752
+ const auth = await this.requireAuth();
1753
+ await writeLocalSecret(this.storeRoot, ref, value, auth, this.vaultId);
1754
+ await appendAuditEvent(
1755
+ {
1756
+ action: "write",
1757
+ vault: this.vaultId,
1758
+ ref,
1759
+ caller: "cli"
1760
+ },
1761
+ this.processEnv
1762
+ );
1763
+ }
1764
+ async delete(ref) {
1765
+ const auth = await this.requireAuth();
1766
+ await deleteLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1767
+ await appendAuditEvent(
1768
+ {
1769
+ action: "delete",
1770
+ vault: this.vaultId,
1771
+ ref,
1772
+ caller: "cli"
1773
+ },
1774
+ this.processEnv
1775
+ );
1776
+ }
1777
+ async list() {
1778
+ const auth = this.authConfig ?? await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1779
+ if (!auth) {
1780
+ throw new CnosAuthenticationError(
1781
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1782
+ );
1783
+ }
1784
+ this.authConfig = auth;
1785
+ return listLocalSecrets(this.storeRoot, auth, this.vaultId);
1786
+ }
1787
+ };
1788
+
1789
+ // ../core/src/secrets/providers/registry.ts
1790
+ function createSecretVaultProvider(vaultId, definition, processEnv) {
1791
+ if (definition.provider === "local") {
1792
+ return new LocalSecretVaultProvider(vaultId, definition, processEnv);
1793
+ }
1794
+ if (definition.provider === "github-secrets") {
1795
+ return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
1796
+ }
1797
+ throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
1798
+ }
1799
+
1800
+ // ../core/src/secrets/prompt.ts
1801
+ var import_node_readline = __toESM(require("readline"), 1);
1802
+ var import_node_stream = require("stream");
1803
+ async function promptHidden(message) {
1804
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1805
+ return void 0;
1806
+ }
1807
+ const mutableStdout = new WritableMask();
1808
+ const rl = import_node_readline.default.createInterface({
1809
+ input: process.stdin,
1810
+ output: mutableStdout,
1811
+ terminal: true
1812
+ });
1813
+ try {
1814
+ mutableStdout.muted = true;
1815
+ const value = await new Promise((resolve) => {
1816
+ rl.question(message, resolve);
1817
+ });
1818
+ process.stdout.write("\n");
1819
+ return value;
1820
+ } finally {
1821
+ rl.close();
1822
+ }
1823
+ }
1824
+ var WritableMask = class extends import_node_stream.Writable {
1825
+ muted = false;
1826
+ _write(chunk, _encoding, callback) {
1827
+ if (!this.muted) {
1828
+ process.stdout.write(chunk);
1829
+ }
1830
+ callback();
1831
+ }
1832
+ };
1833
+
1834
+ // ../core/src/secrets/resolveAuth.ts
1835
+ function toAuthError(vaultId, sources) {
1836
+ return new CnosAuthenticationError(
1837
+ `Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
1838
+ );
1839
+ }
1840
+ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
1841
+ const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
1842
+ if (sessionKey) {
1843
+ return {
1844
+ derivedKey: sessionKey,
1845
+ method: "keychain",
1846
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1847
+ };
1848
+ }
1849
+ if (definition.provider === "github-secrets") {
1850
+ return {
1851
+ method: definition.auth?.method ?? "environment",
1852
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1853
+ };
1854
+ }
1855
+ const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
1856
+ for (const source of sources) {
1857
+ if (source.startsWith("env:")) {
1858
+ const value = processEnv[source.slice(4)];
1859
+ if (value) {
1860
+ return {
1861
+ passphrase: value,
1862
+ method: "passphrase",
1863
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1864
+ };
1865
+ }
1866
+ }
1867
+ if (source.startsWith("keychain:")) {
1868
+ const value = await readKeychain(source.slice("keychain:".length));
1869
+ if (value) {
1870
+ return {
1871
+ derivedKey: Buffer.from(value, "hex"),
1872
+ method: "keychain",
1873
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1874
+ };
1875
+ }
1876
+ }
1877
+ if (source === "prompt") {
1878
+ const value = await promptHidden(`Enter passphrase for vault "${vaultId}": `);
1879
+ if (value) {
1880
+ return {
1881
+ passphrase: value,
1882
+ method: "passphrase",
1883
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1884
+ };
1885
+ }
1886
+ }
1887
+ }
1888
+ const fallback = resolveSecretPassphrase(vaultId, processEnv);
1889
+ if (fallback) {
1890
+ return {
1891
+ passphrase: fallback,
1892
+ method: "passphrase",
1893
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1894
+ };
1895
+ }
1896
+ throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
1897
+ }
1898
+
1899
+ // ../core/src/secrets/batchResolve.ts
1900
+ function collectSecretDescriptors(graph) {
1901
+ return Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => ({
1902
+ logicalKey: entry.key,
1903
+ ref: entry.value
1904
+ }));
1905
+ }
1906
+ async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
1907
+ const cache = new SecretCache();
1908
+ const descriptors = collectSecretDescriptors(graph);
1909
+ const grouped = descriptors.reduce((accumulator, descriptor) => {
1910
+ const vaultId = descriptor.ref.vault ?? "default";
1911
+ const bucket = accumulator.get(vaultId) ?? [];
1912
+ bucket.push(descriptor);
1913
+ accumulator.set(vaultId, bucket);
1914
+ return accumulator;
1915
+ }, /* @__PURE__ */ new Map());
1916
+ for (const [vaultId, refs] of grouped) {
1917
+ const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
1918
+ const provider = createSecretVaultProvider(vaultId, definition, processEnv);
1919
+ const auth = await resolveVaultAuth(vaultId, definition, processEnv);
1920
+ await provider.authenticate(auth);
1921
+ const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
1922
+ cache.load(vaultId, resolved);
1923
+ await appendAuditEvent(
1924
+ {
1925
+ action: "batch_read",
1926
+ vault: vaultId,
1927
+ refs: Array.from(resolved.keys()).sort((left, right) => left.localeCompare(right)),
1928
+ caller: "runtime",
1929
+ workspace: graph.workspace.workspaceId,
1930
+ profile: graph.profile
1931
+ },
1932
+ processEnv
1933
+ );
1934
+ }
1935
+ return cache;
1936
+ }
1937
+ function resolveSecretEntryValue(key, value, cache) {
1938
+ if (!key.startsWith("secret.") || !isSecretReference(value)) {
1939
+ return value;
1940
+ }
1941
+ const vaultId = value.vault ?? "default";
1942
+ return cache.get(vaultId, value.ref) ?? value;
1943
+ }
1944
+
1100
1945
  // ../core/src/runtime/projection.ts
1101
1946
  function setNestedValue(target, pathSegments, value) {
1102
1947
  const [head, ...tail] = pathSegments;
@@ -1147,55 +1992,6 @@ function requireValue(graph, key) {
1147
1992
  return value;
1148
1993
  }
1149
1994
 
1150
- // ../core/src/utils/secretStore.ts
1151
- var import_node_crypto = require("crypto");
1152
- var import_promises6 = require("fs/promises");
1153
- var import_node_path6 = __toESM(require("path"), 1);
1154
- function isObject(value) {
1155
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1156
- }
1157
- function isSecretReference(value) {
1158
- return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
1159
- }
1160
- function resolveSecretStoreRoot(processEnv = process.env) {
1161
- return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1162
- }
1163
- function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1164
- return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1165
- }
1166
- function deriveKey(passphrase, salt) {
1167
- return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1168
- }
1169
- function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1170
- const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1171
- return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1172
- }
1173
- function decryptDocument(document, passphrase) {
1174
- const salt = Buffer.from(document.salt, "base64");
1175
- const iv = Buffer.from(document.iv, "base64");
1176
- const tag = Buffer.from(document.tag, "base64");
1177
- const ciphertext = Buffer.from(document.ciphertext, "base64");
1178
- const key = deriveKey(passphrase, salt);
1179
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1180
- decipher.setAuthTag(tag);
1181
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1182
- return plaintext.toString("utf8");
1183
- }
1184
- async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1185
- if (!passphrase) {
1186
- throw new CnosManifestError(
1187
- `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1188
- );
1189
- }
1190
- const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1191
- const source = await (0, import_promises6.readFile)(filePath, "utf8");
1192
- const document = JSON.parse(source);
1193
- if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
1194
- throw new CnosManifestError("Invalid local secret document", filePath);
1195
- }
1196
- return decryptDocument(document, passphrase);
1197
- }
1198
-
1199
1995
  // ../core/src/runtime/toEnv.ts
1200
1996
  function normalizeEnvValue(value) {
1201
1997
  if (value === void 0 || value === null) {
@@ -1277,28 +2073,42 @@ function toPublicEnv(graph, manifest, options = {}) {
1277
2073
  }
1278
2074
 
1279
2075
  // ../core/src/orchestrator/runtime.ts
1280
- function createRuntime(manifest, graph, plugins = []) {
2076
+ function createRuntime(manifest, graph, plugins = [], secretCache) {
2077
+ function readLogicalKey(key) {
2078
+ const entry = graph.entries.get(key);
2079
+ if (!entry) {
2080
+ return void 0;
2081
+ }
2082
+ if (!secretCache) {
2083
+ return entry.value;
2084
+ }
2085
+ return resolveSecretEntryValue(key, entry.value, secretCache);
2086
+ }
1281
2087
  return {
1282
2088
  manifest,
1283
2089
  plugins,
1284
2090
  graph,
1285
2091
  read(key) {
1286
- return readValue(graph, key);
2092
+ return readLogicalKey(key);
1287
2093
  },
1288
2094
  require(key) {
1289
- return requireValue(graph, key);
2095
+ const value = readLogicalKey(key);
2096
+ if (value === void 0) {
2097
+ return requireValue(graph, key);
2098
+ }
2099
+ return value;
1290
2100
  },
1291
2101
  readOr(key, fallback) {
1292
2102
  return readOrValue(graph, key, fallback);
1293
2103
  },
1294
- value(path10) {
1295
- return readValue(graph, toLogicalKey("value", path10));
2104
+ value(path12) {
2105
+ return readLogicalKey(toLogicalKey("value", path12));
1296
2106
  },
1297
- secret(path10) {
1298
- return readValue(graph, toLogicalKey("secret", path10));
2107
+ secret(path12) {
2108
+ return readLogicalKey(toLogicalKey("secret", path12));
1299
2109
  },
1300
- meta(path10) {
1301
- return readValue(graph, toLogicalKey("meta", path10));
2110
+ meta(path12) {
2111
+ return readLogicalKey(toLogicalKey("meta", path12));
1302
2112
  },
1303
2113
  inspect(key) {
1304
2114
  return inspectValue(graph, key);
@@ -1456,19 +2266,21 @@ async function createCnos(options = {}) {
1456
2266
  });
1457
2267
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
1458
2268
  const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
2269
+ const secretCache = options.secretResolution === "lazy" ? void 0 : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
1459
2270
  return createRuntime(
1460
2271
  loadedManifest.manifest,
1461
2272
  appendMetaEntries({
1462
2273
  ...promotedGraph,
1463
2274
  profileSource: activeProfile.source
1464
2275
  }, options.cnosVersion),
1465
- plugins
2276
+ plugins,
2277
+ secretCache
1466
2278
  );
1467
2279
  }
1468
2280
 
1469
2281
  // ../core/src/runtime/dump.ts
1470
- var import_promises7 = require("fs/promises");
1471
- var import_node_path7 = __toESM(require("path"), 1);
2282
+ var import_promises9 = require("fs/promises");
2283
+ var import_node_path9 = __toESM(require("path"), 1);
1472
2284
 
1473
2285
  // ../core/src/utils/envNaming.ts
1474
2286
  function normalizeMappingConfig(config = {}) {
@@ -1477,8 +2289,8 @@ function normalizeMappingConfig(config = {}) {
1477
2289
  explicit: config.explicit ?? {}
1478
2290
  };
1479
2291
  }
1480
- function fromScreamingSnake(path10) {
1481
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2292
+ function fromScreamingSnake(path12) {
2293
+ return path12.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1482
2294
  }
1483
2295
  function envVarToLogicalKey(envVar, config = {}) {
1484
2296
  const normalized = normalizeMappingConfig(config);
@@ -1505,7 +2317,7 @@ function envVarToLogicalKey(envVar, config = {}) {
1505
2317
  // package.json
1506
2318
  var package_default = {
1507
2319
  name: "@kitsy/cnos",
1508
- version: "1.2.0",
2320
+ version: "1.4.0",
1509
2321
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1510
2322
  type: "module",
1511
2323
  main: "./dist/index.cjs",
@@ -1517,6 +2329,16 @@ var package_default = {
1517
2329
  import: "./dist/index.js",
1518
2330
  require: "./dist/index.cjs"
1519
2331
  },
2332
+ "./configure": {
2333
+ types: "./dist/configure/index.d.ts",
2334
+ import: "./dist/configure/index.js",
2335
+ require: "./dist/configure/index.cjs"
2336
+ },
2337
+ "./create": {
2338
+ types: "./dist/configure/index.d.ts",
2339
+ import: "./dist/configure/index.js",
2340
+ require: "./dist/configure/index.cjs"
2341
+ },
1520
2342
  "./internal": {
1521
2343
  types: "./dist/internal.d.ts",
1522
2344
  import: "./dist/internal.js",
@@ -1694,8 +2516,8 @@ function createCliArgsPlugin() {
1694
2516
  }
1695
2517
 
1696
2518
  // ../../plugins/dotenv/src/index.ts
1697
- var import_promises8 = require("fs/promises");
1698
- var import_node_path8 = __toESM(require("path"), 1);
2519
+ var import_promises10 = require("fs/promises");
2520
+ var import_node_path10 = __toESM(require("path"), 1);
1699
2521
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
1700
2522
  function parseDoubleQuoted(value) {
1701
2523
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -1752,7 +2574,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
1752
2574
  }
1753
2575
  async function readIfPresent(filePath) {
1754
2576
  try {
1755
- return await (0, import_promises8.readFile)(filePath, "utf8");
2577
+ return await (0, import_promises10.readFile)(filePath, "utf8");
1756
2578
  } catch {
1757
2579
  return void 0;
1758
2580
  }
@@ -1771,7 +2593,7 @@ function createDotenvPlugin() {
1771
2593
  workspace: workspaceRoot.workspaceId
1772
2594
  });
1773
2595
  for (const fileName of fileNames) {
1774
- const absolutePath = import_node_path8.default.join(envRoot, fileName);
2596
+ const absolutePath = import_node_path10.default.join(envRoot, fileName);
1775
2597
  const document = await readIfPresent(absolutePath);
1776
2598
  if (!document) {
1777
2599
  continue;
@@ -1780,7 +2602,7 @@ function createDotenvPlugin() {
1780
2602
  ...dotenvEntriesFromObject(
1781
2603
  parseDotenv(document),
1782
2604
  config.envMapping,
1783
- toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(context.manifestRoot), absolutePath)),
2605
+ toPortablePath(import_node_path10.default.relative(import_node_path10.default.dirname(context.manifestRoot), absolutePath)),
1784
2606
  workspaceRoot.workspaceId
1785
2607
  )
1786
2608
  );
@@ -1818,32 +2640,32 @@ function createPublicEnvExportPlugin() {
1818
2640
  }
1819
2641
 
1820
2642
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
1821
- var import_promises10 = require("fs/promises");
2643
+ var import_promises12 = require("fs/promises");
1822
2644
 
1823
2645
  // ../../plugins/filesystem/src/helpers.ts
1824
- var import_promises9 = require("fs/promises");
1825
- var import_node_path9 = __toESM(require("path"), 1);
2646
+ var import_promises11 = require("fs/promises");
2647
+ var import_node_path11 = __toESM(require("path"), 1);
1826
2648
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
1827
2649
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
1828
2650
  async function existsDirectory(targetPath) {
1829
2651
  try {
1830
- const stat = await (0, import_promises9.readdir)(targetPath);
1831
- void stat;
2652
+ const stat2 = await (0, import_promises11.readdir)(targetPath);
2653
+ void stat2;
1832
2654
  return true;
1833
2655
  } catch {
1834
2656
  return false;
1835
2657
  }
1836
2658
  }
1837
2659
  async function collectYamlFiles(root) {
1838
- const entries = await (0, import_promises9.readdir)(root, { withFileTypes: true });
2660
+ const entries = await (0, import_promises11.readdir)(root, { withFileTypes: true });
1839
2661
  const results = [];
1840
2662
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
1841
- const absolutePath = import_node_path9.default.join(root, entry.name);
2663
+ const absolutePath = import_node_path11.default.join(root, entry.name);
1842
2664
  if (entry.isDirectory()) {
1843
2665
  results.push(...await collectYamlFiles(absolutePath));
1844
2666
  continue;
1845
2667
  }
1846
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path9.default.extname(entry.name).toLowerCase())) {
2668
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path11.default.extname(entry.name).toLowerCase())) {
1847
2669
  results.push(absolutePath);
1848
2670
  }
1849
2671
  }
@@ -1851,16 +2673,16 @@ async function collectYamlFiles(root) {
1851
2673
  }
1852
2674
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
1853
2675
  const files = [];
1854
- const repoRoot = import_node_path9.default.dirname(manifestRoot);
2676
+ const repoRoot = import_node_path11.default.dirname(manifestRoot);
1855
2677
  for (const workspaceRoot of workspaceRoots) {
1856
- const resolvedRoot = import_node_path9.default.resolve(workspaceRoot.path, sourceRoot);
2678
+ const resolvedRoot = import_node_path11.default.resolve(workspaceRoot.path, sourceRoot);
1857
2679
  for (const layer of activeLayers) {
1858
- const layerRoot = import_node_path9.default.join(resolvedRoot, layer);
2680
+ const layerRoot = import_node_path11.default.join(resolvedRoot, layer);
1859
2681
  if (!await existsDirectory(layerRoot)) {
1860
2682
  continue;
1861
2683
  }
1862
2684
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
1863
- const relativePath = import_node_path9.default.relative(repoRoot, absolutePath);
2685
+ const relativePath = import_node_path11.default.relative(repoRoot, absolutePath);
1864
2686
  files.push({
1865
2687
  absolutePath,
1866
2688
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -1910,31 +2732,6 @@ function yamlObjectToEntries(document, filePath, namespace, sourceId, workspaceI
1910
2732
  }
1911
2733
  }));
1912
2734
  }
1913
- async function resolveSecretValue(value, processEnv) {
1914
- if (!isSecretReference(value)) {
1915
- return value;
1916
- }
1917
- if (value.provider === "local") {
1918
- const passphrase = resolveSecretPassphrase(value.vault, processEnv);
1919
- if (!passphrase) {
1920
- return value;
1921
- }
1922
- return readLocalSecret(
1923
- resolveSecretStoreRoot(processEnv),
1924
- value.ref,
1925
- passphrase,
1926
- value.vault
1927
- );
1928
- }
1929
- if (value.provider === "env" || value.provider === "github-secrets") {
1930
- const resolved = processEnv?.[value.ref];
1931
- if (resolved === void 0) {
1932
- return value;
1933
- }
1934
- return resolved;
1935
- }
1936
- return value;
1937
- }
1938
2735
  function toSecretReferenceMetadata(value) {
1939
2736
  if (!isSecretReference(value)) {
1940
2737
  return void 0;
@@ -1962,14 +2759,12 @@ function createFilesystemSecretsPlugin() {
1962
2759
  );
1963
2760
  const entries = [];
1964
2761
  for (const file of files) {
1965
- const document = await (0, import_promises10.readFile)(file.absolutePath, "utf8");
2762
+ const document = await (0, import_promises12.readFile)(file.absolutePath, "utf8");
1966
2763
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
1967
2764
  for (const entry of fileEntries) {
1968
2765
  const metadata = toSecretReferenceMetadata(entry.value);
1969
- const resolvedValue = await resolveSecretValue(entry.value, context.processEnv);
1970
2766
  entries.push({
1971
2767
  ...entry,
1972
- value: resolvedValue,
1973
2768
  ...metadata ? { metadata } : {}
1974
2769
  });
1975
2770
  }
@@ -1980,7 +2775,7 @@ function createFilesystemSecretsPlugin() {
1980
2775
  }
1981
2776
 
1982
2777
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
1983
- var import_promises11 = require("fs/promises");
2778
+ var import_promises13 = require("fs/promises");
1984
2779
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
1985
2780
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
1986
2781
  }
@@ -1996,11 +2791,30 @@ function createFilesystemValuesPlugin() {
1996
2791
  sourceRoot,
1997
2792
  context.profileActivation.values
1998
2793
  );
2794
+ const customNamespaces = Object.entries(context.manifest.namespaces).filter(
2795
+ ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
2796
+ ).map(([namespace]) => namespace);
1999
2797
  const entries = [];
2000
2798
  for (const file of files) {
2001
- const document = await (0, import_promises11.readFile)(file.absolutePath, "utf8");
2799
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
2002
2800
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
2003
2801
  }
2802
+ for (const namespace of customNamespaces) {
2803
+ const layers = [
2804
+ namespace,
2805
+ ...context.profileChain.filter((profile) => profile !== "base").map((profile) => `profiles/${profile}/${namespace}`)
2806
+ ];
2807
+ const namespaceFiles = await collectFilesystemLayerFiles(
2808
+ context.manifestRoot,
2809
+ context.workspace.workspaceRoots,
2810
+ sourceRoot,
2811
+ layers
2812
+ );
2813
+ for (const file of namespaceFiles) {
2814
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
2815
+ entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
2816
+ }
2817
+ }
2004
2818
  return entries;
2005
2819
  }
2006
2820
  };
@@ -2008,6 +2822,11 @@ function createFilesystemValuesPlugin() {
2008
2822
 
2009
2823
  // ../../plugins/process-env/src/index.ts
2010
2824
  var PROCESS_ENV_PLUGIN_ID = "@kitsy/cnos/plugins/process-env";
2825
+ var PROCESS_GRAPH_OMIT = /* @__PURE__ */ new Set([
2826
+ "__CNOS_GRAPH__",
2827
+ "__CNOS_SECRET_PAYLOAD__",
2828
+ "__CNOS_SESSION_KEY__"
2829
+ ]);
2011
2830
  function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default") {
2012
2831
  return Object.entries(env).flatMap(([envVar, value]) => {
2013
2832
  if (typeof value !== "string") {
@@ -2032,17 +2851,77 @@ function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default")
2032
2851
  ];
2033
2852
  });
2034
2853
  }
2854
+ function processNamespaceEntriesFromContext(env, workspaceId = "default") {
2855
+ const envEntries = Object.entries(env).filter((entry) => typeof entry[1] === "string").filter(([envVar]) => !PROCESS_GRAPH_OMIT.has(envVar)).map(([envVar, value]) => ({
2856
+ key: `process.env.${envVar}`,
2857
+ value,
2858
+ namespace: "process",
2859
+ sourceId: "process-runtime",
2860
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2861
+ workspaceId,
2862
+ origin: {
2863
+ envVar
2864
+ }
2865
+ }));
2866
+ const runtimeEntries = [
2867
+ {
2868
+ key: "process.cwd",
2869
+ value: process.cwd(),
2870
+ namespace: "process",
2871
+ sourceId: "process-runtime",
2872
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2873
+ workspaceId
2874
+ },
2875
+ {
2876
+ key: "process.platform",
2877
+ value: process.platform,
2878
+ namespace: "process",
2879
+ sourceId: "process-runtime",
2880
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2881
+ workspaceId
2882
+ },
2883
+ {
2884
+ key: "process.arch",
2885
+ value: process.arch,
2886
+ namespace: "process",
2887
+ sourceId: "process-runtime",
2888
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2889
+ workspaceId
2890
+ },
2891
+ {
2892
+ key: "process.node.version",
2893
+ value: process.version,
2894
+ namespace: "process",
2895
+ sourceId: "process-runtime",
2896
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2897
+ workspaceId
2898
+ },
2899
+ {
2900
+ key: "process.args.raw",
2901
+ value: process.argv.slice(2),
2902
+ namespace: "process",
2903
+ sourceId: "process-runtime",
2904
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2905
+ workspaceId
2906
+ }
2907
+ ];
2908
+ return [...runtimeEntries, ...envEntries];
2909
+ }
2035
2910
  function createProcessEnvPlugin() {
2036
2911
  return {
2037
2912
  id: "process-env",
2038
2913
  kind: "loader",
2039
2914
  async load(context) {
2040
2915
  const config = context.manifestConfig;
2041
- return processEnvEntriesFromObject(
2042
- context.processEnv ?? process.env,
2043
- config.envMapping,
2044
- context.workspace.workspaceId
2045
- );
2916
+ const env = context.processEnv ?? process.env;
2917
+ return [
2918
+ ...processEnvEntriesFromObject(
2919
+ env,
2920
+ config.envMapping,
2921
+ context.workspace.workspaceId
2922
+ ),
2923
+ ...processNamespaceEntriesFromContext(env, context.workspace.workspaceId)
2924
+ ];
2046
2925
  }
2047
2926
  };
2048
2927
  }
@@ -2065,9 +2944,11 @@ function defaultPlugins() {
2065
2944
  // src/runtime/state.ts
2066
2945
  var singletonRuntime;
2067
2946
  var singletonReady;
2947
+ var bootstrappedSecretHydrationRequired = false;
2068
2948
  function setSingletonRuntime(runtime) {
2069
2949
  singletonRuntime = runtime;
2070
2950
  singletonReady = Promise.resolve(runtime);
2951
+ bootstrappedSecretHydrationRequired = false;
2071
2952
  return runtime;
2072
2953
  }
2073
2954
 
@@ -2075,6 +2956,7 @@ function setSingletonRuntime(runtime) {
2075
2956
  async function createCnos2(options = {}) {
2076
2957
  const runtime = await createCnos({
2077
2958
  ...options,
2959
+ processEnv: options.processEnv ?? process.env,
2078
2960
  cnosVersion: package_default.version,
2079
2961
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
2080
2962
  });