@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
package/dist/index.cjs CHANGED
@@ -30,13 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- createCnos: () => createCnos2,
34
- defaultPlugins: () => defaultPlugins,
35
- planDump: () => planDump,
36
- resolveBrowserData: () => resolveBrowserData,
37
- toEnv: () => toEnv,
38
- toPublicEnv: () => toPublicEnv,
39
- writeDump: () => writeDump
33
+ cnos: () => runtime_default,
34
+ default: () => runtime_default
40
35
  });
41
36
  module.exports = __toCommonJS(index_exports);
42
37
 
@@ -59,6 +54,11 @@ var CnosSecurityError = class extends CnosError {
59
54
  super(message);
60
55
  }
61
56
  };
57
+ var CnosAuthenticationError = class extends CnosError {
58
+ constructor(message) {
59
+ super(message);
60
+ }
61
+ };
62
62
  var CnosKeyNotFoundError = class extends CnosError {
63
63
  constructor(key) {
64
64
  super(`Missing required CNOS config key: ${key}`);
@@ -111,6 +111,68 @@ function createProvenanceInspector() {
111
111
  };
112
112
  }
113
113
 
114
+ // ../core/src/keychain/linux.ts
115
+ var import_node_child_process = require("child_process");
116
+ var import_node_util = require("util");
117
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
118
+ async function readLinuxKeychain(entry) {
119
+ try {
120
+ const { stdout } = await execFileAsync("secret-tool", ["lookup", "service", "cnos", "account", entry]);
121
+ const value = stdout.trim();
122
+ return value.length > 0 ? value : void 0;
123
+ } catch {
124
+ return void 0;
125
+ }
126
+ }
127
+
128
+ // ../core/src/keychain/macos.ts
129
+ var import_node_child_process2 = require("child_process");
130
+ var import_node_util2 = require("util");
131
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
132
+ async function readMacosKeychain(entry) {
133
+ try {
134
+ const { stdout } = await execFileAsync2("security", ["find-generic-password", "-a", "cnos", "-s", entry, "-w"]);
135
+ const value = stdout.trim();
136
+ return value.length > 0 ? value : void 0;
137
+ } catch {
138
+ return void 0;
139
+ }
140
+ }
141
+
142
+ // ../core/src/keychain/windows.ts
143
+ var import_node_child_process3 = require("child_process");
144
+ var import_node_util3 = require("util");
145
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
146
+ function wrap(script) {
147
+ return ["-NoProfile", "-Command", script];
148
+ }
149
+ async function readWindowsKeychain(entry) {
150
+ try {
151
+ const { stdout } = await execFileAsync3(
152
+ "powershell",
153
+ 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`)
154
+ );
155
+ const value = stdout.trim();
156
+ return value.length > 0 ? value : void 0;
157
+ } catch {
158
+ return void 0;
159
+ }
160
+ }
161
+
162
+ // ../core/src/keychain/index.ts
163
+ async function readKeychain(entry) {
164
+ if (process.platform === "win32") {
165
+ return readWindowsKeychain(entry);
166
+ }
167
+ if (process.platform === "darwin") {
168
+ return readMacosKeychain(entry);
169
+ }
170
+ if (process.platform === "linux") {
171
+ return readLinuxKeychain(entry);
172
+ }
173
+ return void 0;
174
+ }
175
+
114
176
  // ../core/src/manifest/loadManifest.ts
115
177
  var import_promises2 = require("fs/promises");
116
178
  var import_node_path2 = __toESM(require("path"), 1);
@@ -233,6 +295,11 @@ var DEFAULT_NAMESPACES = {
233
295
  shareable: false,
234
296
  readonly: true
235
297
  },
298
+ process: {
299
+ kind: "system",
300
+ shareable: false,
301
+ readonly: true
302
+ },
236
303
  public: {
237
304
  kind: "projection",
238
305
  source: "promote",
@@ -287,22 +354,82 @@ function normalizeNamespaces(namespaces) {
287
354
  function normalizeVaults(vaults) {
288
355
  return Object.fromEntries(
289
356
  Object.entries(vaults ?? {}).map(([name, definition]) => {
357
+ const legacyPassphrase = definition.passphrase;
358
+ if (legacyPassphrase !== void 0) {
359
+ throw new CnosManifestError(
360
+ `Vault "${name}" uses legacy passphrase configuration. Use vaults.${name}.auth instead.`
361
+ );
362
+ }
290
363
  const provider = definition.provider?.trim();
291
364
  if (!provider) {
292
365
  throw new CnosManifestError(`Vault "${name}" requires a provider`);
293
366
  }
367
+ const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
368
+ const normalizedMapping = Object.fromEntries(
369
+ Object.entries(definition.mapping ?? {}).filter(
370
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
371
+ ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
372
+ );
294
373
  return [
295
374
  name,
296
375
  {
297
376
  provider,
298
- ...definition.passphrase?.trim() ? {
299
- passphrase: definition.passphrase.trim()
377
+ auth: normalizedAuth,
378
+ ...Object.keys(normalizedMapping).length > 0 ? {
379
+ mapping: normalizedMapping
300
380
  } : {}
301
381
  }
302
382
  ];
303
383
  })
304
384
  );
305
385
  }
386
+ function normalizeAuthSources(value) {
387
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
388
+ return void 0;
389
+ }
390
+ const sources = Array.isArray(value.from) ? value.from : void 0;
391
+ const normalized = (sources ?? []).map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
392
+ return normalized.length > 0 ? normalized : void 0;
393
+ }
394
+ function normalizeVaultAuth(vaultName, provider, auth) {
395
+ if (provider === "local") {
396
+ const passphraseSources = normalizeAuthSources(auth?.passphrase);
397
+ const defaultToken = vaultName.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
398
+ const defaultSources = [
399
+ ...defaultToken ? [`env:CNOS_SECRET_PASSPHRASE_${defaultToken}`] : [],
400
+ "env:CNOS_SECRET_PASSPHRASE",
401
+ `keychain:cnos/${vaultName}`,
402
+ "prompt"
403
+ ];
404
+ return {
405
+ method: auth?.method ?? "passphrase",
406
+ passphrase: {
407
+ from: passphraseSources ?? defaultSources
408
+ },
409
+ ...auth?.config ? { config: auth.config } : {}
410
+ };
411
+ }
412
+ if (provider === "github-secrets") {
413
+ return {
414
+ method: auth?.method ?? "environment",
415
+ ...auth?.config ? { config: auth.config } : {}
416
+ };
417
+ }
418
+ return {
419
+ ...auth?.method ? { method: auth.method } : {},
420
+ ...normalizeAuthSources(auth?.passphrase) ? {
421
+ passphrase: {
422
+ from: normalizeAuthSources(auth?.passphrase) ?? []
423
+ }
424
+ } : {},
425
+ ...normalizeAuthSources(auth?.token) ? {
426
+ token: {
427
+ from: normalizeAuthSources(auth?.token) ?? []
428
+ }
429
+ } : {},
430
+ ...auth?.config ? { config: auth.config } : {}
431
+ };
432
+ }
306
433
  function normalizeManifest(manifest) {
307
434
  const version = manifest.version ?? 1;
308
435
  if (version !== 1) {
@@ -832,12 +959,15 @@ async function exists2(targetPath) {
832
959
  return false;
833
960
  }
834
961
  }
835
- async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId) {
962
+ async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
836
963
  const workspaceRoot = import_node_path5.default.join(manifestRoot, "workspaces", workspaceId);
837
964
  if (await exists2(workspaceRoot)) {
838
965
  return workspaceRoot;
839
966
  }
840
- const legacyMarkers = ["values", "secrets", "env", "profiles"].map(
967
+ const customDataNamespaceRoots = Object.entries(manifest.namespaces).filter(
968
+ ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
969
+ ).map(([namespace]) => namespace);
970
+ const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
841
971
  (segment) => import_node_path5.default.join(manifestRoot, segment)
842
972
  );
843
973
  if ((await Promise.all(legacyMarkers.map((marker) => exists2(marker)))).some(Boolean)) {
@@ -924,7 +1054,7 @@ async function resolveWorkspaceContext(manifest, options) {
924
1054
  workspaceRoots.push({
925
1055
  scope: "local",
926
1056
  workspaceId: chainWorkspaceId,
927
- path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId)
1057
+ path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId, manifest)
928
1058
  });
929
1059
  }
930
1060
  return {
@@ -1089,6 +1219,7 @@ async function runPipeline(options) {
1089
1219
  const collectedEntries = await Promise.all(
1090
1220
  options.plugins.map(
1091
1221
  (plugin) => plugin.load({
1222
+ manifest: options.manifest,
1092
1223
  manifestConfig: {
1093
1224
  ...options.manifest.sources[plugin.id] ?? {},
1094
1225
  envMapping: options.manifest.envMapping
@@ -1106,6 +1237,712 @@ async function runPipeline(options) {
1106
1237
  return collectedEntries.flat();
1107
1238
  }
1108
1239
 
1240
+ // ../core/src/secrets/auditLog.ts
1241
+ var import_promises8 = require("fs/promises");
1242
+ var import_node_path8 = __toESM(require("path"), 1);
1243
+
1244
+ // ../core/src/utils/secretStore.ts
1245
+ var import_node_crypto = require("crypto");
1246
+ var import_promises7 = require("fs/promises");
1247
+ var import_node_path7 = __toESM(require("path"), 1);
1248
+
1249
+ // ../core/src/secrets/sessionStore.ts
1250
+ var import_promises6 = require("fs/promises");
1251
+ var import_node_path6 = __toESM(require("path"), 1);
1252
+ function buildSessionRoot(processEnv = process.env) {
1253
+ return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
1254
+ }
1255
+ function buildSessionPath(vault, processEnv) {
1256
+ return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1257
+ }
1258
+ async function readVaultSessionKey(vault, processEnv) {
1259
+ try {
1260
+ const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
1261
+ const document = JSON.parse(source);
1262
+ if (document.version !== 1 || typeof document.derivedKey !== "string") {
1263
+ return void 0;
1264
+ }
1265
+ const key = Buffer.from(document.derivedKey, "hex");
1266
+ return key.length > 0 ? key : void 0;
1267
+ } catch {
1268
+ return void 0;
1269
+ }
1270
+ }
1271
+
1272
+ // ../core/src/utils/secretStore.ts
1273
+ var KEY_LENGTH = 32;
1274
+ var SALT_LENGTH = 32;
1275
+ var IV_LENGTH = 12;
1276
+ var AUTH_TAG_LENGTH = 16;
1277
+ var PBKDF2_ITERATIONS = 6e5;
1278
+ var KEYSTORE_VERSION = 1;
1279
+ var METADATA_VERSION = 1;
1280
+ var META_FILENAME = "meta.yml";
1281
+ var KEYSTORE_FILENAME = "keystore.enc";
1282
+ function isObject(value) {
1283
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1284
+ }
1285
+ function isSecretReference(value) {
1286
+ 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));
1287
+ }
1288
+ function resolveSecretStoreRoot(processEnv = process.env) {
1289
+ return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1290
+ }
1291
+ function normalizeVaultToken(vault = "default") {
1292
+ return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1293
+ }
1294
+ function getVaultPassphraseEnvVar(vault = "default") {
1295
+ const vaultToken = normalizeVaultToken(vault);
1296
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
1297
+ }
1298
+ function getVaultSessionKeyEnvVar(vault = "default") {
1299
+ const vaultToken = normalizeVaultToken(vault);
1300
+ return `__CNOS_VAULT_KEY_${vaultToken || "DEFAULT"}__`;
1301
+ }
1302
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1303
+ return processEnv[getVaultPassphraseEnvVar(vault)] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1304
+ }
1305
+ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1306
+ const encoded = processEnv[getVaultSessionKeyEnvVar(vault)];
1307
+ if (!encoded) {
1308
+ return readVaultSessionKey(vault, processEnv);
1309
+ }
1310
+ try {
1311
+ const key = Buffer.from(encoded, "hex");
1312
+ return key.length === KEY_LENGTH ? key : void 0;
1313
+ } catch {
1314
+ return void 0;
1315
+ }
1316
+ }
1317
+ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1318
+ return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1319
+ }
1320
+ function buildMetaPath(storeRoot, vault = "default") {
1321
+ return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
1322
+ }
1323
+ function buildKeystorePath(storeRoot, vault = "default") {
1324
+ return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1325
+ }
1326
+ function buildLegacyVaultFile(storeRoot, vault = "default") {
1327
+ return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
1328
+ }
1329
+ function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1330
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
1331
+ }
1332
+ function assertVaultMetadata(value, filePath) {
1333
+ if (!isObject(value)) {
1334
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1335
+ }
1336
+ 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") {
1337
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1338
+ }
1339
+ return value;
1340
+ }
1341
+ async function exists3(targetPath) {
1342
+ try {
1343
+ await (0, import_promises7.stat)(targetPath);
1344
+ return true;
1345
+ } catch {
1346
+ return false;
1347
+ }
1348
+ }
1349
+ async function detectLegacyVaultFormat(storeRoot, vault = "default") {
1350
+ const legacyFile = buildLegacyVaultFile(storeRoot, vault);
1351
+ const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
1352
+ if (await exists3(legacyFile)) {
1353
+ return legacyFile;
1354
+ }
1355
+ if (await exists3(legacyStore)) {
1356
+ return legacyStore;
1357
+ }
1358
+ return void 0;
1359
+ }
1360
+ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1361
+ const legacyPath = await detectLegacyVaultFormat(storeRoot, vault);
1362
+ if (!legacyPath) {
1363
+ return;
1364
+ }
1365
+ throw new CnosSecurityError(
1366
+ `Legacy CNOS local vault format detected for vault "${vault}" at ${legacyPath}. CNOS 1.4 requires the new keystore format. Remove and recreate the vault.`
1367
+ );
1368
+ }
1369
+ function encryptPayload(payload, key) {
1370
+ const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1371
+ const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
1372
+ const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1373
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1374
+ const tag = cipher.getAuthTag();
1375
+ return Buffer.concat([
1376
+ Buffer.from(Uint32Array.of(KEYSTORE_VERSION).buffer),
1377
+ iv,
1378
+ tag,
1379
+ ciphertext
1380
+ ]);
1381
+ }
1382
+ function decryptPayload(buffer, key) {
1383
+ if (buffer.length < 4 + IV_LENGTH + AUTH_TAG_LENGTH) {
1384
+ throw new CnosSecurityError("Invalid CNOS local vault keystore");
1385
+ }
1386
+ const version = buffer.readUInt32LE(0);
1387
+ if (version !== KEYSTORE_VERSION) {
1388
+ throw new CnosSecurityError(`Unsupported CNOS local vault keystore version: ${version}`);
1389
+ }
1390
+ const ivOffset = 4;
1391
+ const tagOffset = ivOffset + IV_LENGTH;
1392
+ const cipherOffset = tagOffset + AUTH_TAG_LENGTH;
1393
+ const iv = buffer.subarray(ivOffset, tagOffset);
1394
+ const tag = buffer.subarray(tagOffset, cipherOffset);
1395
+ const ciphertext = buffer.subarray(cipherOffset);
1396
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1397
+ decipher.setAuthTag(tag);
1398
+ try {
1399
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
1400
+ const payload = JSON.parse(plaintext);
1401
+ if (!payload || !isObject(payload.secrets) || !isObject(payload.metadata)) {
1402
+ throw new Error("invalid");
1403
+ }
1404
+ return {
1405
+ secrets: Object.fromEntries(
1406
+ Object.entries(payload.secrets).filter((entry) => typeof entry[1] === "string")
1407
+ ),
1408
+ metadata: Object.fromEntries(
1409
+ Object.entries(payload.metadata).filter(
1410
+ (entry) => isObject(entry[1]) && typeof entry[1].createdAt === "string" && typeof entry[1].updatedAt === "string"
1411
+ )
1412
+ )
1413
+ };
1414
+ } catch {
1415
+ throw new CnosAuthenticationError("Failed to decrypt CNOS local vault. Check vault authentication.");
1416
+ }
1417
+ }
1418
+ function buildInitialPayload() {
1419
+ return {
1420
+ secrets: {},
1421
+ metadata: {}
1422
+ };
1423
+ }
1424
+ async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1425
+ const metaPath = buildMetaPath(storeRoot, vault);
1426
+ const keystorePath = buildKeystorePath(storeRoot, vault);
1427
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
1428
+ await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1429
+ await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
1430
+ }
1431
+ async function readVaultMetadata(storeRoot, vault = "default") {
1432
+ await assertNoLegacyVaultFormat(storeRoot, vault);
1433
+ const metaPath = buildMetaPath(storeRoot, vault);
1434
+ try {
1435
+ const source = await (0, import_promises7.readFile)(metaPath, "utf8");
1436
+ return assertVaultMetadata(parseYaml(source), metaPath);
1437
+ } catch (error) {
1438
+ if (error.code === "ENOENT") {
1439
+ return void 0;
1440
+ }
1441
+ throw error;
1442
+ }
1443
+ }
1444
+ async function createSecretVault(storeRoot, vault, passphrase) {
1445
+ const normalizedVault = vault.trim() || "default";
1446
+ await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1447
+ const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
1448
+ const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1449
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1450
+ const meta = {
1451
+ version: METADATA_VERSION,
1452
+ algorithm: "aes-256-gcm",
1453
+ kdf: "pbkdf2-sha512",
1454
+ iterations: PBKDF2_ITERATIONS,
1455
+ salt: salt.toString("base64"),
1456
+ createdAt,
1457
+ secretCount: 0
1458
+ };
1459
+ await writeVaultFiles(storeRoot, normalizedVault, meta, buildInitialPayload(), key);
1460
+ return buildMetaPath(storeRoot, normalizedVault);
1461
+ }
1462
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
1463
+ const normalizedVault = vault.trim() || "default";
1464
+ const meta = await readVaultMetadata(storeRoot, normalizedVault);
1465
+ if (meta) {
1466
+ return buildMetaPath(storeRoot, normalizedVault);
1467
+ }
1468
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
1469
+ }
1470
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
1471
+ if (definition?.provider !== "local") {
1472
+ return void 0;
1473
+ }
1474
+ const configuredSources = definition.auth?.passphrase?.from ?? [];
1475
+ for (const source of configuredSources) {
1476
+ if (source.startsWith("env:")) {
1477
+ const value = processEnv[source.slice(4)];
1478
+ if (value) {
1479
+ return value;
1480
+ }
1481
+ }
1482
+ }
1483
+ return resolveSecretPassphrase(vault, processEnv);
1484
+ }
1485
+ async function resolveVaultAccessKey(storeRoot, definition, vault = "default", processEnv = process.env) {
1486
+ if (definition?.provider !== "local") {
1487
+ return definition?.provider === "github-secrets" ? {
1488
+ method: definition.auth?.method ?? "environment",
1489
+ ...definition?.auth?.config ? { config: definition.auth.config } : {}
1490
+ } : void 0;
1491
+ }
1492
+ const sessionKey = await resolveVaultSessionKey(vault, processEnv);
1493
+ if (sessionKey) {
1494
+ return {
1495
+ derivedKey: sessionKey,
1496
+ method: "keychain",
1497
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1498
+ };
1499
+ }
1500
+ const passphrase = resolveConfiguredVaultPassphrase(definition, vault, processEnv);
1501
+ if (passphrase) {
1502
+ return {
1503
+ passphrase,
1504
+ method: "passphrase",
1505
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1506
+ };
1507
+ }
1508
+ const metadata = await readVaultMetadata(storeRoot, vault);
1509
+ if (!metadata) {
1510
+ return void 0;
1511
+ }
1512
+ throw new CnosAuthenticationError(
1513
+ `Cannot authenticate to vault "${vault}". Set ${getVaultPassphraseEnvVar(vault)} or run cnos vault auth ${vault}.`
1514
+ );
1515
+ }
1516
+ async function loadVaultPayload(storeRoot, vault, auth) {
1517
+ const meta = await readVaultMetadata(storeRoot, vault);
1518
+ if (!meta) {
1519
+ throw new CnosManifestError(`Missing CNOS vault metadata for "${vault}"`);
1520
+ }
1521
+ const salt = Buffer.from(meta.salt, "base64");
1522
+ const key = auth.derivedKey ?? (auth.passphrase ? deriveVaultKey(auth.passphrase, salt, meta.iterations) : void 0);
1523
+ if (!key) {
1524
+ throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1525
+ }
1526
+ const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
1527
+ return {
1528
+ meta,
1529
+ payload: decryptPayload(buffer, key),
1530
+ key
1531
+ };
1532
+ }
1533
+ async function writeLocalSecret(storeRoot, ref, value, authOrPassphrase, vault = "default") {
1534
+ const auth = typeof authOrPassphrase === "string" ? {
1535
+ passphrase: authOrPassphrase,
1536
+ method: "passphrase"
1537
+ } : authOrPassphrase;
1538
+ if (auth.passphrase) {
1539
+ await ensureSecretVault(storeRoot, vault, auth.passphrase);
1540
+ } else {
1541
+ const meta2 = await readVaultMetadata(storeRoot, vault);
1542
+ if (!meta2) {
1543
+ throw new CnosAuthenticationError(`Vault "${vault}" requires passphrase-based authentication for initial creation.`);
1544
+ }
1545
+ }
1546
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1547
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1548
+ const existing = payload.metadata[ref];
1549
+ payload.secrets[ref] = value;
1550
+ payload.metadata[ref] = {
1551
+ createdAt: existing?.createdAt ?? now,
1552
+ updatedAt: now
1553
+ };
1554
+ const nextMeta = {
1555
+ ...meta,
1556
+ secretCount: Object.keys(payload.secrets).length
1557
+ };
1558
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1559
+ return buildKeystorePath(storeRoot, vault);
1560
+ }
1561
+ async function deleteLocalSecret(storeRoot, ref, auth, vault = "default") {
1562
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1563
+ if (!(ref in payload.secrets)) {
1564
+ return false;
1565
+ }
1566
+ delete payload.secrets[ref];
1567
+ delete payload.metadata[ref];
1568
+ const nextMeta = {
1569
+ ...meta,
1570
+ secretCount: Object.keys(payload.secrets).length
1571
+ };
1572
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1573
+ return true;
1574
+ }
1575
+ async function readLocalSecret(storeRoot, ref, auth, vault = "default") {
1576
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1577
+ const value = payload.secrets[ref];
1578
+ if (value === void 0) {
1579
+ throw new CnosManifestError(`Missing local secret ref "${ref}" in vault "${vault}"`);
1580
+ }
1581
+ return value;
1582
+ }
1583
+ async function listLocalSecrets(storeRoot, auth, vault = "default") {
1584
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1585
+ return Object.keys(payload.secrets).sort((left, right) => left.localeCompare(right));
1586
+ }
1587
+ function resolveVaultDefinition(vaults, vault = "default") {
1588
+ const definition = vaults?.[vault];
1589
+ const provider = definition?.provider ?? "local";
1590
+ return {
1591
+ name: vault,
1592
+ provider,
1593
+ ...definition?.auth ? { auth: definition.auth } : {},
1594
+ ...definition?.mapping ? { mapping: definition.mapping } : {},
1595
+ requiresAuthentication: provider === "local"
1596
+ };
1597
+ }
1598
+
1599
+ // ../core/src/secrets/auditLog.ts
1600
+ async function appendAuditEvent(event, processEnv = process.env) {
1601
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1602
+ await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
1603
+ await (0, import_promises8.appendFile)(
1604
+ auditFile,
1605
+ `${JSON.stringify({
1606
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1607
+ ...event
1608
+ })}
1609
+ `,
1610
+ "utf8"
1611
+ );
1612
+ }
1613
+
1614
+ // ../core/src/secrets/secretCache.ts
1615
+ var SecretCache = class {
1616
+ cache = /* @__PURE__ */ new Map();
1617
+ authenticated = /* @__PURE__ */ new Set();
1618
+ load(vaultId, secrets) {
1619
+ this.authenticated.add(vaultId);
1620
+ for (const [ref, value] of secrets) {
1621
+ this.cache.set(`${vaultId}:${ref}`, value);
1622
+ }
1623
+ }
1624
+ isVaultAuthenticated(vaultId) {
1625
+ return this.authenticated.has(vaultId);
1626
+ }
1627
+ get(vaultId, ref) {
1628
+ return this.cache.get(`${vaultId}:${ref}`);
1629
+ }
1630
+ clear(vaultId) {
1631
+ if (!vaultId) {
1632
+ this.cache.clear();
1633
+ this.authenticated.clear();
1634
+ return;
1635
+ }
1636
+ this.authenticated.delete(vaultId);
1637
+ for (const key of Array.from(this.cache.keys())) {
1638
+ if (key.startsWith(`${vaultId}:`)) {
1639
+ this.cache.delete(key);
1640
+ }
1641
+ }
1642
+ }
1643
+ };
1644
+
1645
+ // ../core/src/secrets/providers/github.ts
1646
+ var GithubSecretsVaultProvider = class {
1647
+ constructor(vaultId, definition, processEnv = process.env) {
1648
+ this.vaultId = vaultId;
1649
+ this.definition = definition;
1650
+ this.processEnv = processEnv;
1651
+ }
1652
+ vaultId;
1653
+ definition;
1654
+ processEnv;
1655
+ authenticated = false;
1656
+ async authenticate(_authConfig) {
1657
+ void _authConfig;
1658
+ this.authenticated = true;
1659
+ }
1660
+ isAuthenticated() {
1661
+ return this.authenticated;
1662
+ }
1663
+ resolveEnvVar(ref) {
1664
+ if (this.processEnv[ref] !== void 0) {
1665
+ return ref;
1666
+ }
1667
+ return Object.entries(this.definition.mapping ?? {}).find(([, logicalRef]) => logicalRef === ref)?.[0];
1668
+ }
1669
+ async batchGet(refs) {
1670
+ this.authenticated = true;
1671
+ const resolved = /* @__PURE__ */ new Map();
1672
+ for (const ref of Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right))) {
1673
+ const envVar = this.resolveEnvVar(ref);
1674
+ const value = envVar ? this.processEnv[envVar] : void 0;
1675
+ if (value !== void 0) {
1676
+ resolved.set(ref, value);
1677
+ }
1678
+ }
1679
+ return resolved;
1680
+ }
1681
+ async get(ref) {
1682
+ const envVar = this.resolveEnvVar(ref);
1683
+ this.authenticated = true;
1684
+ return envVar ? this.processEnv[envVar] : void 0;
1685
+ }
1686
+ async set(ref, value) {
1687
+ void ref;
1688
+ void value;
1689
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be written by CNOS.`);
1690
+ }
1691
+ async delete(ref) {
1692
+ void ref;
1693
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be mutated by CNOS.`);
1694
+ }
1695
+ async list() {
1696
+ return Object.values(this.definition.mapping ?? {}).sort((left, right) => left.localeCompare(right));
1697
+ }
1698
+ };
1699
+
1700
+ // ../core/src/secrets/providers/local.ts
1701
+ var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
1702
+ constructor(vaultId, definition, processEnv = process.env, storeRoot = resolveSecretStoreRoot(processEnv)) {
1703
+ this.vaultId = vaultId;
1704
+ this.processEnv = processEnv;
1705
+ this.storeRoot = storeRoot;
1706
+ this.definition = definition;
1707
+ }
1708
+ vaultId;
1709
+ processEnv;
1710
+ storeRoot;
1711
+ authConfig;
1712
+ definition;
1713
+ static fromVaults(vaults, vaultId, processEnv) {
1714
+ const definition = resolveVaultDefinition(vaults, vaultId);
1715
+ return new _LocalSecretVaultProvider(vaultId, definition, processEnv);
1716
+ }
1717
+ async authenticate(authConfig) {
1718
+ this.authConfig = authConfig;
1719
+ await this.list();
1720
+ }
1721
+ isAuthenticated() {
1722
+ return Boolean(this.authConfig);
1723
+ }
1724
+ async requireAuth() {
1725
+ if (this.authConfig) {
1726
+ return this.authConfig;
1727
+ }
1728
+ const resolved = await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1729
+ if (!resolved) {
1730
+ throw new CnosAuthenticationError(
1731
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1732
+ );
1733
+ }
1734
+ this.authConfig = resolved;
1735
+ return resolved;
1736
+ }
1737
+ async batchGet(refs) {
1738
+ const auth = await this.requireAuth();
1739
+ const entries = await Promise.all(
1740
+ Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right)).map(async (ref) => [ref, await readLocalSecret(this.storeRoot, ref, auth, this.vaultId)])
1741
+ );
1742
+ return new Map(entries);
1743
+ }
1744
+ async get(ref) {
1745
+ const auth = await this.requireAuth();
1746
+ try {
1747
+ return await readLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1748
+ } catch {
1749
+ return void 0;
1750
+ }
1751
+ }
1752
+ async set(ref, value) {
1753
+ const auth = await this.requireAuth();
1754
+ await writeLocalSecret(this.storeRoot, ref, value, auth, this.vaultId);
1755
+ await appendAuditEvent(
1756
+ {
1757
+ action: "write",
1758
+ vault: this.vaultId,
1759
+ ref,
1760
+ caller: "cli"
1761
+ },
1762
+ this.processEnv
1763
+ );
1764
+ }
1765
+ async delete(ref) {
1766
+ const auth = await this.requireAuth();
1767
+ await deleteLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1768
+ await appendAuditEvent(
1769
+ {
1770
+ action: "delete",
1771
+ vault: this.vaultId,
1772
+ ref,
1773
+ caller: "cli"
1774
+ },
1775
+ this.processEnv
1776
+ );
1777
+ }
1778
+ async list() {
1779
+ const auth = this.authConfig ?? await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1780
+ if (!auth) {
1781
+ throw new CnosAuthenticationError(
1782
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1783
+ );
1784
+ }
1785
+ this.authConfig = auth;
1786
+ return listLocalSecrets(this.storeRoot, auth, this.vaultId);
1787
+ }
1788
+ };
1789
+
1790
+ // ../core/src/secrets/providers/registry.ts
1791
+ function createSecretVaultProvider(vaultId, definition, processEnv) {
1792
+ if (definition.provider === "local") {
1793
+ return new LocalSecretVaultProvider(vaultId, definition, processEnv);
1794
+ }
1795
+ if (definition.provider === "github-secrets") {
1796
+ return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
1797
+ }
1798
+ throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
1799
+ }
1800
+
1801
+ // ../core/src/secrets/prompt.ts
1802
+ var import_node_readline = __toESM(require("readline"), 1);
1803
+ var import_node_stream = require("stream");
1804
+ async function promptHidden(message) {
1805
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1806
+ return void 0;
1807
+ }
1808
+ const mutableStdout = new WritableMask();
1809
+ const rl = import_node_readline.default.createInterface({
1810
+ input: process.stdin,
1811
+ output: mutableStdout,
1812
+ terminal: true
1813
+ });
1814
+ try {
1815
+ mutableStdout.muted = true;
1816
+ const value = await new Promise((resolve) => {
1817
+ rl.question(message, resolve);
1818
+ });
1819
+ process.stdout.write("\n");
1820
+ return value;
1821
+ } finally {
1822
+ rl.close();
1823
+ }
1824
+ }
1825
+ var WritableMask = class extends import_node_stream.Writable {
1826
+ muted = false;
1827
+ _write(chunk, _encoding, callback) {
1828
+ if (!this.muted) {
1829
+ process.stdout.write(chunk);
1830
+ }
1831
+ callback();
1832
+ }
1833
+ };
1834
+
1835
+ // ../core/src/secrets/resolveAuth.ts
1836
+ function toAuthError(vaultId, sources) {
1837
+ return new CnosAuthenticationError(
1838
+ `Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
1839
+ );
1840
+ }
1841
+ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
1842
+ const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
1843
+ if (sessionKey) {
1844
+ return {
1845
+ derivedKey: sessionKey,
1846
+ method: "keychain",
1847
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1848
+ };
1849
+ }
1850
+ if (definition.provider === "github-secrets") {
1851
+ return {
1852
+ method: definition.auth?.method ?? "environment",
1853
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1854
+ };
1855
+ }
1856
+ const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
1857
+ for (const source of sources) {
1858
+ if (source.startsWith("env:")) {
1859
+ const value = processEnv[source.slice(4)];
1860
+ if (value) {
1861
+ return {
1862
+ passphrase: value,
1863
+ method: "passphrase",
1864
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1865
+ };
1866
+ }
1867
+ }
1868
+ if (source.startsWith("keychain:")) {
1869
+ const value = await readKeychain(source.slice("keychain:".length));
1870
+ if (value) {
1871
+ return {
1872
+ derivedKey: Buffer.from(value, "hex"),
1873
+ method: "keychain",
1874
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1875
+ };
1876
+ }
1877
+ }
1878
+ if (source === "prompt") {
1879
+ const value = await promptHidden(`Enter passphrase for vault "${vaultId}": `);
1880
+ if (value) {
1881
+ return {
1882
+ passphrase: value,
1883
+ method: "passphrase",
1884
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1885
+ };
1886
+ }
1887
+ }
1888
+ }
1889
+ const fallback = resolveSecretPassphrase(vaultId, processEnv);
1890
+ if (fallback) {
1891
+ return {
1892
+ passphrase: fallback,
1893
+ method: "passphrase",
1894
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1895
+ };
1896
+ }
1897
+ throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
1898
+ }
1899
+
1900
+ // ../core/src/secrets/batchResolve.ts
1901
+ function collectSecretDescriptors(graph) {
1902
+ return Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => ({
1903
+ logicalKey: entry.key,
1904
+ ref: entry.value
1905
+ }));
1906
+ }
1907
+ async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
1908
+ const cache = new SecretCache();
1909
+ const descriptors = collectSecretDescriptors(graph);
1910
+ const grouped = descriptors.reduce((accumulator, descriptor) => {
1911
+ const vaultId = descriptor.ref.vault ?? "default";
1912
+ const bucket = accumulator.get(vaultId) ?? [];
1913
+ bucket.push(descriptor);
1914
+ accumulator.set(vaultId, bucket);
1915
+ return accumulator;
1916
+ }, /* @__PURE__ */ new Map());
1917
+ for (const [vaultId, refs] of grouped) {
1918
+ const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
1919
+ const provider = createSecretVaultProvider(vaultId, definition, processEnv);
1920
+ const auth = await resolveVaultAuth(vaultId, definition, processEnv);
1921
+ await provider.authenticate(auth);
1922
+ const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
1923
+ cache.load(vaultId, resolved);
1924
+ await appendAuditEvent(
1925
+ {
1926
+ action: "batch_read",
1927
+ vault: vaultId,
1928
+ refs: Array.from(resolved.keys()).sort((left, right) => left.localeCompare(right)),
1929
+ caller: "runtime",
1930
+ workspace: graph.workspace.workspaceId,
1931
+ profile: graph.profile
1932
+ },
1933
+ processEnv
1934
+ );
1935
+ }
1936
+ return cache;
1937
+ }
1938
+ function resolveSecretEntryValue(key, value, cache) {
1939
+ if (!key.startsWith("secret.") || !isSecretReference(value)) {
1940
+ return value;
1941
+ }
1942
+ const vaultId = value.vault ?? "default";
1943
+ return cache.get(vaultId, value.ref) ?? value;
1944
+ }
1945
+
1109
1946
  // ../core/src/runtime/projection.ts
1110
1947
  function setNestedValue(target, pathSegments, value) {
1111
1948
  const [head, ...tail] = pathSegments;
@@ -1156,55 +1993,6 @@ function requireValue(graph, key) {
1156
1993
  return value;
1157
1994
  }
1158
1995
 
1159
- // ../core/src/utils/secretStore.ts
1160
- var import_node_crypto = require("crypto");
1161
- var import_promises6 = require("fs/promises");
1162
- var import_node_path6 = __toESM(require("path"), 1);
1163
- function isObject(value) {
1164
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1165
- }
1166
- function isSecretReference(value) {
1167
- 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));
1168
- }
1169
- function resolveSecretStoreRoot(processEnv = process.env) {
1170
- return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1171
- }
1172
- function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1173
- return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1174
- }
1175
- function deriveKey(passphrase, salt) {
1176
- return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1177
- }
1178
- function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1179
- const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1180
- return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1181
- }
1182
- function decryptDocument(document, passphrase) {
1183
- const salt = Buffer.from(document.salt, "base64");
1184
- const iv = Buffer.from(document.iv, "base64");
1185
- const tag = Buffer.from(document.tag, "base64");
1186
- const ciphertext = Buffer.from(document.ciphertext, "base64");
1187
- const key = deriveKey(passphrase, salt);
1188
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1189
- decipher.setAuthTag(tag);
1190
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1191
- return plaintext.toString("utf8");
1192
- }
1193
- async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1194
- if (!passphrase) {
1195
- throw new CnosManifestError(
1196
- `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1197
- );
1198
- }
1199
- const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1200
- const source = await (0, import_promises6.readFile)(filePath, "utf8");
1201
- const document = JSON.parse(source);
1202
- 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") {
1203
- throw new CnosManifestError("Invalid local secret document", filePath);
1204
- }
1205
- return decryptDocument(document, passphrase);
1206
- }
1207
-
1208
1996
  // ../core/src/runtime/toEnv.ts
1209
1997
  function normalizeEnvValue(value) {
1210
1998
  if (value === void 0 || value === null) {
@@ -1286,28 +2074,42 @@ function toPublicEnv(graph, manifest, options = {}) {
1286
2074
  }
1287
2075
 
1288
2076
  // ../core/src/orchestrator/runtime.ts
1289
- function createRuntime(manifest, graph, plugins = []) {
2077
+ function createRuntime(manifest, graph, plugins = [], secretCache) {
2078
+ function readLogicalKey(key) {
2079
+ const entry = graph.entries.get(key);
2080
+ if (!entry) {
2081
+ return void 0;
2082
+ }
2083
+ if (!secretCache) {
2084
+ return entry.value;
2085
+ }
2086
+ return resolveSecretEntryValue(key, entry.value, secretCache);
2087
+ }
1290
2088
  return {
1291
2089
  manifest,
1292
2090
  plugins,
1293
2091
  graph,
1294
2092
  read(key) {
1295
- return readValue(graph, key);
2093
+ return readLogicalKey(key);
1296
2094
  },
1297
2095
  require(key) {
1298
- return requireValue(graph, key);
2096
+ const value = readLogicalKey(key);
2097
+ if (value === void 0) {
2098
+ return requireValue(graph, key);
2099
+ }
2100
+ return value;
1299
2101
  },
1300
2102
  readOr(key, fallback) {
1301
2103
  return readOrValue(graph, key, fallback);
1302
2104
  },
1303
- value(path10) {
1304
- return readValue(graph, toLogicalKey("value", path10));
2105
+ value(path12) {
2106
+ return readLogicalKey(toLogicalKey("value", path12));
1305
2107
  },
1306
- secret(path10) {
1307
- return readValue(graph, toLogicalKey("secret", path10));
2108
+ secret(path12) {
2109
+ return readLogicalKey(toLogicalKey("secret", path12));
1308
2110
  },
1309
- meta(path10) {
1310
- return readValue(graph, toLogicalKey("meta", path10));
2111
+ meta(path12) {
2112
+ return readLogicalKey(toLogicalKey("meta", path12));
1311
2113
  },
1312
2114
  inspect(key) {
1313
2115
  return inspectValue(graph, key);
@@ -1465,61 +2267,21 @@ async function createCnos(options = {}) {
1465
2267
  });
1466
2268
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
1467
2269
  const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
2270
+ const secretCache = options.secretResolution === "lazy" ? void 0 : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
1468
2271
  return createRuntime(
1469
2272
  loadedManifest.manifest,
1470
2273
  appendMetaEntries({
1471
2274
  ...promotedGraph,
1472
2275
  profileSource: activeProfile.source
1473
2276
  }, options.cnosVersion),
1474
- plugins
2277
+ plugins,
2278
+ secretCache
1475
2279
  );
1476
2280
  }
1477
2281
 
1478
2282
  // ../core/src/runtime/dump.ts
1479
- var import_promises7 = require("fs/promises");
1480
- var import_node_path7 = __toESM(require("path"), 1);
1481
- function buildDumpFiles(graph, options = {}) {
1482
- const basePath = options.flatten ? "" : import_node_path7.default.posix.join("workspaces", graph.workspace.workspaceId);
1483
- const values = toNamespaceObject(graph, "value");
1484
- const secrets = toNamespaceObject(graph, "secret");
1485
- const files = [];
1486
- if (Object.keys(values).length > 0) {
1487
- files.push({
1488
- path: import_node_path7.default.posix.join(basePath, "values", graph.profile, "app.yml"),
1489
- namespace: "value",
1490
- content: stringifyYaml(values)
1491
- });
1492
- }
1493
- if (Object.keys(secrets).length > 0) {
1494
- files.push({
1495
- path: import_node_path7.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1496
- namespace: "secret",
1497
- content: stringifyYaml(secrets)
1498
- });
1499
- }
1500
- return files.sort((left, right) => left.path.localeCompare(right.path));
1501
- }
1502
- function planDump(graph, options = {}) {
1503
- return {
1504
- workspaceId: graph.workspace.workspaceId,
1505
- profile: graph.profile,
1506
- flatten: options.flatten ?? false,
1507
- files: buildDumpFiles(graph, options)
1508
- };
1509
- }
1510
- async function writeDump(graph, options) {
1511
- const root = import_node_path7.default.resolve(options.to);
1512
- const plan = planDump(graph, options);
1513
- for (const file of plan.files) {
1514
- const destination = import_node_path7.default.join(root, file.path);
1515
- await (0, import_promises7.mkdir)(import_node_path7.default.dirname(destination), { recursive: true });
1516
- await (0, import_promises7.writeFile)(destination, file.content, "utf8");
1517
- }
1518
- return {
1519
- ...plan,
1520
- root
1521
- };
1522
- }
2283
+ var import_promises9 = require("fs/promises");
2284
+ var import_node_path9 = __toESM(require("path"), 1);
1523
2285
 
1524
2286
  // ../core/src/utils/envNaming.ts
1525
2287
  function normalizeMappingConfig(config = {}) {
@@ -1528,8 +2290,8 @@ function normalizeMappingConfig(config = {}) {
1528
2290
  explicit: config.explicit ?? {}
1529
2291
  };
1530
2292
  }
1531
- function fromScreamingSnake(path10) {
1532
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2293
+ function fromScreamingSnake(path12) {
2294
+ return path12.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1533
2295
  }
1534
2296
  function envVarToLogicalKey(envVar, config = {}) {
1535
2297
  const normalized = normalizeMappingConfig(config);
@@ -1556,7 +2318,7 @@ function envVarToLogicalKey(envVar, config = {}) {
1556
2318
  // package.json
1557
2319
  var package_default = {
1558
2320
  name: "@kitsy/cnos",
1559
- version: "1.2.0",
2321
+ version: "1.4.0",
1560
2322
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1561
2323
  type: "module",
1562
2324
  main: "./dist/index.cjs",
@@ -1568,6 +2330,16 @@ var package_default = {
1568
2330
  import: "./dist/index.js",
1569
2331
  require: "./dist/index.cjs"
1570
2332
  },
2333
+ "./configure": {
2334
+ types: "./dist/configure/index.d.ts",
2335
+ import: "./dist/configure/index.js",
2336
+ require: "./dist/configure/index.cjs"
2337
+ },
2338
+ "./create": {
2339
+ types: "./dist/configure/index.d.ts",
2340
+ import: "./dist/configure/index.js",
2341
+ require: "./dist/configure/index.cjs"
2342
+ },
1571
2343
  "./internal": {
1572
2344
  types: "./dist/internal.d.ts",
1573
2345
  import: "./dist/internal.js",
@@ -1745,8 +2517,8 @@ function createCliArgsPlugin() {
1745
2517
  }
1746
2518
 
1747
2519
  // ../../plugins/dotenv/src/index.ts
1748
- var import_promises8 = require("fs/promises");
1749
- var import_node_path8 = __toESM(require("path"), 1);
2520
+ var import_promises10 = require("fs/promises");
2521
+ var import_node_path10 = __toESM(require("path"), 1);
1750
2522
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
1751
2523
  function parseDoubleQuoted(value) {
1752
2524
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -1803,7 +2575,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
1803
2575
  }
1804
2576
  async function readIfPresent(filePath) {
1805
2577
  try {
1806
- return await (0, import_promises8.readFile)(filePath, "utf8");
2578
+ return await (0, import_promises10.readFile)(filePath, "utf8");
1807
2579
  } catch {
1808
2580
  return void 0;
1809
2581
  }
@@ -1822,7 +2594,7 @@ function createDotenvPlugin() {
1822
2594
  workspace: workspaceRoot.workspaceId
1823
2595
  });
1824
2596
  for (const fileName of fileNames) {
1825
- const absolutePath = import_node_path8.default.join(envRoot, fileName);
2597
+ const absolutePath = import_node_path10.default.join(envRoot, fileName);
1826
2598
  const document = await readIfPresent(absolutePath);
1827
2599
  if (!document) {
1828
2600
  continue;
@@ -1831,7 +2603,7 @@ function createDotenvPlugin() {
1831
2603
  ...dotenvEntriesFromObject(
1832
2604
  parseDotenv(document),
1833
2605
  config.envMapping,
1834
- toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(context.manifestRoot), absolutePath)),
2606
+ toPortablePath(import_node_path10.default.relative(import_node_path10.default.dirname(context.manifestRoot), absolutePath)),
1835
2607
  workspaceRoot.workspaceId
1836
2608
  )
1837
2609
  );
@@ -1869,32 +2641,32 @@ function createPublicEnvExportPlugin() {
1869
2641
  }
1870
2642
 
1871
2643
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
1872
- var import_promises10 = require("fs/promises");
2644
+ var import_promises12 = require("fs/promises");
1873
2645
 
1874
2646
  // ../../plugins/filesystem/src/helpers.ts
1875
- var import_promises9 = require("fs/promises");
1876
- var import_node_path9 = __toESM(require("path"), 1);
2647
+ var import_promises11 = require("fs/promises");
2648
+ var import_node_path11 = __toESM(require("path"), 1);
1877
2649
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
1878
2650
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
1879
2651
  async function existsDirectory(targetPath) {
1880
2652
  try {
1881
- const stat = await (0, import_promises9.readdir)(targetPath);
1882
- void stat;
2653
+ const stat2 = await (0, import_promises11.readdir)(targetPath);
2654
+ void stat2;
1883
2655
  return true;
1884
2656
  } catch {
1885
2657
  return false;
1886
2658
  }
1887
2659
  }
1888
2660
  async function collectYamlFiles(root) {
1889
- const entries = await (0, import_promises9.readdir)(root, { withFileTypes: true });
2661
+ const entries = await (0, import_promises11.readdir)(root, { withFileTypes: true });
1890
2662
  const results = [];
1891
2663
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
1892
- const absolutePath = import_node_path9.default.join(root, entry.name);
2664
+ const absolutePath = import_node_path11.default.join(root, entry.name);
1893
2665
  if (entry.isDirectory()) {
1894
2666
  results.push(...await collectYamlFiles(absolutePath));
1895
2667
  continue;
1896
2668
  }
1897
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path9.default.extname(entry.name).toLowerCase())) {
2669
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path11.default.extname(entry.name).toLowerCase())) {
1898
2670
  results.push(absolutePath);
1899
2671
  }
1900
2672
  }
@@ -1902,16 +2674,16 @@ async function collectYamlFiles(root) {
1902
2674
  }
1903
2675
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
1904
2676
  const files = [];
1905
- const repoRoot = import_node_path9.default.dirname(manifestRoot);
2677
+ const repoRoot = import_node_path11.default.dirname(manifestRoot);
1906
2678
  for (const workspaceRoot of workspaceRoots) {
1907
- const resolvedRoot = import_node_path9.default.resolve(workspaceRoot.path, sourceRoot);
2679
+ const resolvedRoot = import_node_path11.default.resolve(workspaceRoot.path, sourceRoot);
1908
2680
  for (const layer of activeLayers) {
1909
- const layerRoot = import_node_path9.default.join(resolvedRoot, layer);
2681
+ const layerRoot = import_node_path11.default.join(resolvedRoot, layer);
1910
2682
  if (!await existsDirectory(layerRoot)) {
1911
2683
  continue;
1912
2684
  }
1913
2685
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
1914
- const relativePath = import_node_path9.default.relative(repoRoot, absolutePath);
2686
+ const relativePath = import_node_path11.default.relative(repoRoot, absolutePath);
1915
2687
  files.push({
1916
2688
  absolutePath,
1917
2689
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -1961,31 +2733,6 @@ function yamlObjectToEntries(document, filePath, namespace, sourceId, workspaceI
1961
2733
  }
1962
2734
  }));
1963
2735
  }
1964
- async function resolveSecretValue(value, processEnv) {
1965
- if (!isSecretReference(value)) {
1966
- return value;
1967
- }
1968
- if (value.provider === "local") {
1969
- const passphrase = resolveSecretPassphrase(value.vault, processEnv);
1970
- if (!passphrase) {
1971
- return value;
1972
- }
1973
- return readLocalSecret(
1974
- resolveSecretStoreRoot(processEnv),
1975
- value.ref,
1976
- passphrase,
1977
- value.vault
1978
- );
1979
- }
1980
- if (value.provider === "env" || value.provider === "github-secrets") {
1981
- const resolved = processEnv?.[value.ref];
1982
- if (resolved === void 0) {
1983
- return value;
1984
- }
1985
- return resolved;
1986
- }
1987
- return value;
1988
- }
1989
2736
  function toSecretReferenceMetadata(value) {
1990
2737
  if (!isSecretReference(value)) {
1991
2738
  return void 0;
@@ -2013,14 +2760,12 @@ function createFilesystemSecretsPlugin() {
2013
2760
  );
2014
2761
  const entries = [];
2015
2762
  for (const file of files) {
2016
- const document = await (0, import_promises10.readFile)(file.absolutePath, "utf8");
2763
+ const document = await (0, import_promises12.readFile)(file.absolutePath, "utf8");
2017
2764
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
2018
2765
  for (const entry of fileEntries) {
2019
2766
  const metadata = toSecretReferenceMetadata(entry.value);
2020
- const resolvedValue = await resolveSecretValue(entry.value, context.processEnv);
2021
2767
  entries.push({
2022
2768
  ...entry,
2023
- value: resolvedValue,
2024
2769
  ...metadata ? { metadata } : {}
2025
2770
  });
2026
2771
  }
@@ -2031,7 +2776,7 @@ function createFilesystemSecretsPlugin() {
2031
2776
  }
2032
2777
 
2033
2778
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
2034
- var import_promises11 = require("fs/promises");
2779
+ var import_promises13 = require("fs/promises");
2035
2780
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
2036
2781
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
2037
2782
  }
@@ -2047,11 +2792,30 @@ function createFilesystemValuesPlugin() {
2047
2792
  sourceRoot,
2048
2793
  context.profileActivation.values
2049
2794
  );
2795
+ const customNamespaces = Object.entries(context.manifest.namespaces).filter(
2796
+ ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
2797
+ ).map(([namespace]) => namespace);
2050
2798
  const entries = [];
2051
2799
  for (const file of files) {
2052
- const document = await (0, import_promises11.readFile)(file.absolutePath, "utf8");
2800
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
2053
2801
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
2054
2802
  }
2803
+ for (const namespace of customNamespaces) {
2804
+ const layers = [
2805
+ namespace,
2806
+ ...context.profileChain.filter((profile) => profile !== "base").map((profile) => `profiles/${profile}/${namespace}`)
2807
+ ];
2808
+ const namespaceFiles = await collectFilesystemLayerFiles(
2809
+ context.manifestRoot,
2810
+ context.workspace.workspaceRoots,
2811
+ sourceRoot,
2812
+ layers
2813
+ );
2814
+ for (const file of namespaceFiles) {
2815
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
2816
+ entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
2817
+ }
2818
+ }
2055
2819
  return entries;
2056
2820
  }
2057
2821
  };
@@ -2059,6 +2823,11 @@ function createFilesystemValuesPlugin() {
2059
2823
 
2060
2824
  // ../../plugins/process-env/src/index.ts
2061
2825
  var PROCESS_ENV_PLUGIN_ID = "@kitsy/cnos/plugins/process-env";
2826
+ var PROCESS_GRAPH_OMIT = /* @__PURE__ */ new Set([
2827
+ "__CNOS_GRAPH__",
2828
+ "__CNOS_SECRET_PAYLOAD__",
2829
+ "__CNOS_SESSION_KEY__"
2830
+ ]);
2062
2831
  function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default") {
2063
2832
  return Object.entries(env).flatMap(([envVar, value]) => {
2064
2833
  if (typeof value !== "string") {
@@ -2083,17 +2852,77 @@ function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default")
2083
2852
  ];
2084
2853
  });
2085
2854
  }
2855
+ function processNamespaceEntriesFromContext(env, workspaceId = "default") {
2856
+ const envEntries = Object.entries(env).filter((entry) => typeof entry[1] === "string").filter(([envVar]) => !PROCESS_GRAPH_OMIT.has(envVar)).map(([envVar, value]) => ({
2857
+ key: `process.env.${envVar}`,
2858
+ value,
2859
+ namespace: "process",
2860
+ sourceId: "process-runtime",
2861
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2862
+ workspaceId,
2863
+ origin: {
2864
+ envVar
2865
+ }
2866
+ }));
2867
+ const runtimeEntries = [
2868
+ {
2869
+ key: "process.cwd",
2870
+ value: process.cwd(),
2871
+ namespace: "process",
2872
+ sourceId: "process-runtime",
2873
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2874
+ workspaceId
2875
+ },
2876
+ {
2877
+ key: "process.platform",
2878
+ value: process.platform,
2879
+ namespace: "process",
2880
+ sourceId: "process-runtime",
2881
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2882
+ workspaceId
2883
+ },
2884
+ {
2885
+ key: "process.arch",
2886
+ value: process.arch,
2887
+ namespace: "process",
2888
+ sourceId: "process-runtime",
2889
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2890
+ workspaceId
2891
+ },
2892
+ {
2893
+ key: "process.node.version",
2894
+ value: process.version,
2895
+ namespace: "process",
2896
+ sourceId: "process-runtime",
2897
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2898
+ workspaceId
2899
+ },
2900
+ {
2901
+ key: "process.args.raw",
2902
+ value: process.argv.slice(2),
2903
+ namespace: "process",
2904
+ sourceId: "process-runtime",
2905
+ pluginId: PROCESS_ENV_PLUGIN_ID,
2906
+ workspaceId
2907
+ }
2908
+ ];
2909
+ return [...runtimeEntries, ...envEntries];
2910
+ }
2086
2911
  function createProcessEnvPlugin() {
2087
2912
  return {
2088
2913
  id: "process-env",
2089
2914
  kind: "loader",
2090
2915
  async load(context) {
2091
2916
  const config = context.manifestConfig;
2092
- return processEnvEntriesFromObject(
2093
- context.processEnv ?? process.env,
2094
- config.envMapping,
2095
- context.workspace.workspaceId
2096
- );
2917
+ const env = context.processEnv ?? process.env;
2918
+ return [
2919
+ ...processEnvEntriesFromObject(
2920
+ env,
2921
+ config.envMapping,
2922
+ context.workspace.workspaceId
2923
+ ),
2924
+ ...processNamespaceEntriesFromContext(env, context.workspace.workspaceId)
2925
+ ];
2097
2926
  }
2098
2927
  };
2099
2928
  }
@@ -2116,16 +2945,35 @@ function defaultPlugins() {
2116
2945
  // src/runtime/state.ts
2117
2946
  var singletonRuntime;
2118
2947
  var singletonReady;
2948
+ var bootstrappedSecretHydrationRequired = false;
2949
+ function getSingletonRuntime() {
2950
+ return singletonRuntime;
2951
+ }
2119
2952
  function setSingletonRuntime(runtime) {
2120
2953
  singletonRuntime = runtime;
2121
2954
  singletonReady = Promise.resolve(runtime);
2955
+ bootstrappedSecretHydrationRequired = false;
2122
2956
  return runtime;
2123
2957
  }
2958
+ function getSingletonReady() {
2959
+ return singletonReady;
2960
+ }
2961
+ function setSingletonReady(promise) {
2962
+ singletonReady = promise;
2963
+ return promise;
2964
+ }
2965
+ function getBootstrappedSecretHydrationRequired() {
2966
+ return bootstrappedSecretHydrationRequired;
2967
+ }
2968
+ function setBootstrappedSecretHydrationRequired(value) {
2969
+ bootstrappedSecretHydrationRequired = value;
2970
+ }
2124
2971
 
2125
2972
  // src/createCnos.ts
2126
2973
  async function createCnos2(options = {}) {
2127
2974
  const runtime = await createCnos({
2128
2975
  ...options,
2976
+ processEnv: options.processEnv ?? process.env,
2129
2977
  cnosVersion: package_default.version,
2130
2978
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
2131
2979
  });
@@ -2133,25 +2981,234 @@ async function createCnos2(options = {}) {
2133
2981
  return runtime;
2134
2982
  }
2135
2983
 
2136
- // src/build/index.ts
2137
- async function resolveBrowserData(options = {}) {
2138
- const runtime = await createCnos2(options);
2139
- const browserData = {};
2140
- for (const [key, entry] of runtime.graph.entries) {
2141
- if (!key.startsWith("public.")) {
2142
- continue;
2984
+ // src/runtime/bootstrap.ts
2985
+ var import_node_crypto2 = require("crypto");
2986
+ var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
2987
+ var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
2988
+ var CNOS_SESSION_KEY_ENV_VAR = "__CNOS_SESSION_KEY__";
2989
+ function deserializeRuntimeGraph(source) {
2990
+ const payload = JSON.parse(source);
2991
+ if (!payload || !Array.isArray(payload.entries) || typeof payload.profile !== "string" || typeof payload.resolvedAt !== "string" || !payload.profileSource || !payload.workspace || typeof payload.workspace.workspaceId !== "string" || !Array.isArray(payload.workspace.workspaceChain) || !Array.isArray(payload.workspace.workspaceRoots)) {
2992
+ throw new Error("Invalid CNOS runtime bootstrap payload");
2993
+ }
2994
+ return {
2995
+ entries: new Map(
2996
+ payload.entries.map((entry) => [
2997
+ entry.key,
2998
+ {
2999
+ key: entry.key,
3000
+ value: entry.value,
3001
+ namespace: entry.namespace,
3002
+ winner: entry.winner,
3003
+ overridden: entry.overridden ?? []
3004
+ }
3005
+ ])
3006
+ ),
3007
+ profile: payload.profile,
3008
+ resolvedAt: payload.resolvedAt,
3009
+ profileSource: payload.profileSource,
3010
+ workspace: payload.workspace
3011
+ };
3012
+ }
3013
+ function decryptSecretPayload(serialized, sessionKey) {
3014
+ const payload = JSON.parse(serialized);
3015
+ if (!payload || typeof payload.iv !== "string" || typeof payload.tag !== "string" || typeof payload.ciphertext !== "string") {
3016
+ throw new Error("Invalid CNOS secret payload");
3017
+ }
3018
+ const key = Buffer.from(sessionKey, "hex");
3019
+ const iv = Buffer.from(payload.iv, "base64");
3020
+ const tag = Buffer.from(payload.tag, "base64");
3021
+ const ciphertext = Buffer.from(payload.ciphertext, "base64");
3022
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
3023
+ decipher.setAuthTag(tag);
3024
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
3025
+ return JSON.parse(plaintext);
3026
+ }
3027
+ function readRuntimeGraphFromEnv(processEnv = process.env) {
3028
+ const serialized = processEnv[CNOS_GRAPH_ENV_VAR];
3029
+ if (!serialized) {
3030
+ return void 0;
3031
+ }
3032
+ const graph = deserializeRuntimeGraph(serialized);
3033
+ const secretPayload = processEnv[CNOS_SECRET_PAYLOAD_ENV_VAR];
3034
+ const sessionKey = processEnv[CNOS_SESSION_KEY_ENV_VAR];
3035
+ if (secretPayload && sessionKey) {
3036
+ const decrypted = decryptSecretPayload(secretPayload, sessionKey);
3037
+ for (const [key, value] of Object.entries(decrypted)) {
3038
+ const entry = graph.entries.get(key);
3039
+ if (entry) {
3040
+ entry.value = value;
3041
+ }
2143
3042
  }
2144
- browserData[key] = entry.value;
2145
3043
  }
2146
- return browserData;
3044
+ return graph;
2147
3045
  }
3046
+ function graphRequiresSecretHydration(graph) {
3047
+ return Array.from(graph.entries.values()).some((entry) => entry.namespace === "secret" && isSecretReference(entry.value));
3048
+ }
3049
+
3050
+ // src/runtime/index.ts
3051
+ var NOT_READY_MESSAGE = "CNOS not initialized. Call await cnos.ready() or use cnos run.";
3052
+ function getRuntimeOrThrow() {
3053
+ const runtime = getSingletonRuntime();
3054
+ if (!runtime) {
3055
+ throw new Error(NOT_READY_MESSAGE);
3056
+ }
3057
+ return runtime;
3058
+ }
3059
+ function attachBootstrappedGraph(graph) {
3060
+ if (getSingletonRuntime()) {
3061
+ return;
3062
+ }
3063
+ const bootstrappedManifest = {
3064
+ version: 1,
3065
+ project: {
3066
+ name: "bootstrapped"
3067
+ },
3068
+ workspaces: {
3069
+ global: {
3070
+ enabled: Boolean(graph.workspace.globalRoot),
3071
+ ...graph.workspace.globalRoot ? {
3072
+ root: graph.workspace.globalRoot
3073
+ } : {},
3074
+ allowWrite: false
3075
+ },
3076
+ items: {},
3077
+ ...graph.workspace.workspaceSource === "implicit" ? {} : {
3078
+ default: graph.workspace.workspaceId
3079
+ }
3080
+ },
3081
+ profiles: {
3082
+ default: graph.profile,
3083
+ resolveFrom: ["default"]
3084
+ },
3085
+ plugins: {
3086
+ loaders: [],
3087
+ resolver: "profile-aware",
3088
+ validators: [],
3089
+ exporters: [],
3090
+ inspectors: []
3091
+ },
3092
+ sources: {},
3093
+ resolution: {
3094
+ precedence: [],
3095
+ arrayPolicy: "replace"
3096
+ },
3097
+ envMapping: {
3098
+ explicit: {}
3099
+ },
3100
+ public: {
3101
+ promote: [],
3102
+ frameworks: {}
3103
+ },
3104
+ namespaces: {},
3105
+ vaults: {},
3106
+ writePolicy: {
3107
+ define: {
3108
+ defaultProfile: graph.profile,
3109
+ targets: {
3110
+ value: "./values/app.yml",
3111
+ secret: "./secrets/app.yml"
3112
+ }
3113
+ }
3114
+ },
3115
+ schema: {}
3116
+ };
3117
+ const runtime = {
3118
+ manifest: bootstrappedManifest,
3119
+ plugins: [],
3120
+ graph,
3121
+ read(key) {
3122
+ return readValue(graph, key);
3123
+ },
3124
+ require(key) {
3125
+ return requireValue(graph, key);
3126
+ },
3127
+ readOr(key, fallback) {
3128
+ return readOrValue(graph, key, fallback);
3129
+ },
3130
+ value(path12) {
3131
+ return readValue(graph, toLogicalKey("value", path12));
3132
+ },
3133
+ secret(path12) {
3134
+ return readValue(graph, toLogicalKey("secret", path12));
3135
+ },
3136
+ meta(path12) {
3137
+ return readValue(graph, toLogicalKey("meta", path12));
3138
+ },
3139
+ inspect(key) {
3140
+ return inspectValue(graph, key);
3141
+ },
3142
+ toObject() {
3143
+ return toNamespaceObject(graph);
3144
+ },
3145
+ toNamespace(namespace) {
3146
+ return toNamespaceObject(graph, namespace);
3147
+ },
3148
+ toEnv(options) {
3149
+ return toEnv(graph, bootstrappedManifest, options);
3150
+ },
3151
+ toPublicEnv(options) {
3152
+ return toPublicEnv(graph, bootstrappedManifest, options);
3153
+ }
3154
+ };
3155
+ setSingletonRuntime(runtime);
3156
+ setBootstrappedSecretHydrationRequired(graphRequiresSecretHydration(graph));
3157
+ }
3158
+ function bootstrapFromProcessEnv() {
3159
+ if (typeof process === "undefined") {
3160
+ return;
3161
+ }
3162
+ try {
3163
+ const graph = readRuntimeGraphFromEnv(process.env);
3164
+ if (graph) {
3165
+ attachBootstrappedGraph(graph);
3166
+ }
3167
+ } catch {
3168
+ }
3169
+ }
3170
+ bootstrapFromProcessEnv();
3171
+ var cnos = Object.assign(
3172
+ ((key) => readValue(getRuntimeOrThrow().graph, key)),
3173
+ {
3174
+ read(key) {
3175
+ return readValue(getRuntimeOrThrow().graph, key);
3176
+ },
3177
+ require(key) {
3178
+ return requireValue(getRuntimeOrThrow().graph, key);
3179
+ },
3180
+ readOr(key, fallback) {
3181
+ return readOrValue(getRuntimeOrThrow().graph, key, fallback);
3182
+ },
3183
+ value(path12) {
3184
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("value", path12));
3185
+ },
3186
+ secret(path12) {
3187
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("secret", path12));
3188
+ },
3189
+ meta(path12) {
3190
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("meta", path12));
3191
+ },
3192
+ async ready() {
3193
+ if (getSingletonRuntime() && !getBootstrappedSecretHydrationRequired()) {
3194
+ return;
3195
+ }
3196
+ const existing = getSingletonReady();
3197
+ if (existing && !getBootstrappedSecretHydrationRequired()) {
3198
+ await existing;
3199
+ return;
3200
+ }
3201
+ const readyPromise = createCnos2().then((runtime) => {
3202
+ setSingletonRuntime(runtime);
3203
+ return runtime;
3204
+ });
3205
+ setSingletonReady(readyPromise);
3206
+ await readyPromise;
3207
+ }
3208
+ }
3209
+ );
3210
+ var runtime_default = cnos;
2148
3211
  // Annotate the CommonJS export names for ESM import in node:
2149
3212
  0 && (module.exports = {
2150
- createCnos,
2151
- defaultPlugins,
2152
- planDump,
2153
- resolveBrowserData,
2154
- toEnv,
2155
- toPublicEnv,
2156
- writeDump
3213
+ cnos
2157
3214
  });