@kitsy/cnos 1.10.0 → 1.11.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 (61) hide show
  1. package/dist/build/index.cjs +331 -63
  2. package/dist/build/index.d.cts +1 -1
  3. package/dist/build/index.d.ts +1 -1
  4. package/dist/build/index.js +13 -15
  5. package/dist/{chunk-A5U7EZCJ.js → chunk-2DMCB3PK.js} +1 -1
  6. package/dist/{chunk-RTHKUGJV.js → chunk-5JGNRADB.js} +1 -1
  7. package/dist/{chunk-3EZGPQCE.js → chunk-DPC2BV3S.js} +1 -1
  8. package/dist/{chunk-FHXLOWAB.js → chunk-KJ57PF47.js} +1 -1
  9. package/dist/{chunk-CSA4L64V.js → chunk-NFGPS7VJ.js} +10 -10
  10. package/dist/{chunk-ESBHCFC6.js → chunk-NU25VFA2.js} +1 -1
  11. package/dist/{chunk-UGLATJJD.js → chunk-RNTTPI5S.js} +1 -1
  12. package/dist/{chunk-UKNL2Y4N.js → chunk-T3E57MSQ.js} +1 -1
  13. package/dist/{chunk-MQ4WG3K6.js → chunk-WPB4HB2K.js} +320 -49
  14. package/dist/{chunk-EIK7OUFP.js → chunk-XGK6DXQL.js} +157 -37
  15. package/dist/configure/index.cjs +329 -59
  16. package/dist/configure/index.d.cts +3 -3
  17. package/dist/configure/index.d.ts +3 -3
  18. package/dist/configure/index.js +8 -8
  19. package/dist/{core-Ud1o2MBn.d.cts → core-BW8SLnRx.d.cts} +34 -2
  20. package/dist/{core-Ud1o2MBn.d.ts → core-BW8SLnRx.d.ts} +34 -2
  21. package/dist/{envNaming-DxxqiGKN.d.cts → envNaming-1rk7BR0e.d.cts} +1 -1
  22. package/dist/{envNaming-CPwXl4I6.d.ts → envNaming-CjL28IeH.d.ts} +1 -1
  23. package/dist/index.cjs +480 -91
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +10 -10
  27. package/dist/internal.cjs +89 -23
  28. package/dist/internal.d.cts +3 -3
  29. package/dist/internal.d.ts +3 -3
  30. package/dist/internal.js +2 -2
  31. package/dist/plugin/basic-schema.cjs +4 -1
  32. package/dist/plugin/basic-schema.d.cts +1 -1
  33. package/dist/plugin/basic-schema.d.ts +1 -1
  34. package/dist/plugin/basic-schema.js +2 -2
  35. package/dist/plugin/cli-args.cjs +4 -1
  36. package/dist/plugin/cli-args.d.cts +1 -1
  37. package/dist/plugin/cli-args.d.ts +1 -1
  38. package/dist/plugin/cli-args.js +2 -2
  39. package/dist/plugin/dotenv.cjs +6 -3
  40. package/dist/plugin/dotenv.d.cts +2 -2
  41. package/dist/plugin/dotenv.d.ts +2 -2
  42. package/dist/plugin/dotenv.js +2 -2
  43. package/dist/plugin/env-export.cjs +5 -2
  44. package/dist/plugin/env-export.d.cts +2 -2
  45. package/dist/plugin/env-export.d.ts +2 -2
  46. package/dist/plugin/env-export.js +2 -2
  47. package/dist/plugin/filesystem.cjs +13 -10
  48. package/dist/plugin/filesystem.d.cts +1 -1
  49. package/dist/plugin/filesystem.d.ts +1 -1
  50. package/dist/plugin/filesystem.js +2 -2
  51. package/dist/plugin/process-env.cjs +4 -1
  52. package/dist/plugin/process-env.d.cts +2 -2
  53. package/dist/plugin/process-env.d.ts +2 -2
  54. package/dist/plugin/process-env.js +2 -2
  55. package/dist/runtime/index.cjs +480 -91
  56. package/dist/runtime/index.d.cts +13 -6
  57. package/dist/runtime/index.d.ts +13 -6
  58. package/dist/runtime/index.js +10 -10
  59. package/dist/{toPublicEnv-fUZMRUOz.d.cts → toPublicEnv-CZzpvhGg.d.cts} +1 -1
  60. package/dist/{toPublicEnv-C9wPSpRo.d.ts → toPublicEnv-CmydGcxg.d.ts} +1 -1
  61. package/package.json +1 -1
@@ -1603,11 +1603,19 @@ function normalizeVaults(vaults) {
1603
1603
  throw new CnosManifestError(`Vault "${name}" requires a provider`);
1604
1604
  }
1605
1605
  const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
1606
- const normalizedMapping = Object.fromEntries(
1607
- Object.entries(definition.mapping ?? {}).filter(
1608
- (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
1609
- ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
1610
- );
1606
+ const normalizedMapping = normalizeVaultMapping(definition.mapping);
1607
+ const fallback = (definition.fallback ?? []).map((entry, index) => {
1608
+ const fallbackProvider = entry.provider?.trim();
1609
+ if (!fallbackProvider) {
1610
+ throw new CnosManifestError(`Vault "${name}" fallback ${index + 1} requires a provider`);
1611
+ }
1612
+ const fallbackMapping = normalizeVaultMapping(entry.mapping);
1613
+ return {
1614
+ provider: fallbackProvider,
1615
+ auth: normalizeVaultAuth(name, fallbackProvider, entry.auth),
1616
+ ...Object.keys(fallbackMapping).length > 0 ? { mapping: fallbackMapping } : {}
1617
+ };
1618
+ });
1611
1619
  return [
1612
1620
  name,
1613
1621
  {
@@ -1615,12 +1623,20 @@ function normalizeVaults(vaults) {
1615
1623
  auth: normalizedAuth,
1616
1624
  ...Object.keys(normalizedMapping).length > 0 ? {
1617
1625
  mapping: normalizedMapping
1618
- } : {}
1626
+ } : {},
1627
+ ...fallback.length > 0 ? { fallback } : {}
1619
1628
  }
1620
1629
  ];
1621
1630
  })
1622
1631
  );
1623
1632
  }
1633
+ function normalizeVaultMapping(mapping) {
1634
+ return Object.fromEntries(
1635
+ Object.entries(mapping ?? {}).filter(
1636
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
1637
+ ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
1638
+ );
1639
+ }
1624
1640
  function normalizeAuthSources(value) {
1625
1641
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1626
1642
  return void 0;
@@ -2568,7 +2584,7 @@ function isObject(value) {
2568
2584
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2569
2585
  }
2570
2586
  function isSecretReference(value) {
2571
- 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));
2587
+ return isObject(value) && (value.provider === void 0 || 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));
2572
2588
  }
2573
2589
  function resolveSecretStoreRoot(processEnv = process.env) {
2574
2590
  return import_node_path11.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
@@ -2915,6 +2931,23 @@ var SecretCache = class {
2915
2931
  get(vaultId, ref) {
2916
2932
  return this.cache.get(`${vaultId}:${ref}`);
2917
2933
  }
2934
+ delete(vaultId, ref) {
2935
+ this.cache.delete(`${vaultId}:${ref}`);
2936
+ }
2937
+ replace(vaultId, secrets) {
2938
+ this.clear(vaultId);
2939
+ this.load(vaultId, secrets);
2940
+ }
2941
+ entriesForVault(vaultId) {
2942
+ const entries = /* @__PURE__ */ new Map();
2943
+ for (const [key, value] of this.cache) {
2944
+ const prefix = `${vaultId}:`;
2945
+ if (key.startsWith(prefix)) {
2946
+ entries.set(key.slice(prefix.length), value);
2947
+ }
2948
+ }
2949
+ return entries;
2950
+ }
2918
2951
  clear(vaultId) {
2919
2952
  if (!vaultId) {
2920
2953
  this.cache.clear();
@@ -3080,7 +3113,7 @@ var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
3080
3113
  };
3081
3114
 
3082
3115
  // ../core/src/secrets/providers/registry.ts
3083
- function createSecretVaultProvider(vaultId, definition, processEnv) {
3116
+ function createSecretVaultProvider(vaultId, definition, processEnv, factories = []) {
3084
3117
  if (definition.provider === "local") {
3085
3118
  return new LocalSecretVaultProvider(vaultId, definition, processEnv);
3086
3119
  }
@@ -3090,9 +3123,30 @@ function createSecretVaultProvider(vaultId, definition, processEnv) {
3090
3123
  if (definition.provider === "github-secrets") {
3091
3124
  return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
3092
3125
  }
3126
+ const factory = factories.find((candidate) => candidate.provider === definition.provider);
3127
+ if (factory) {
3128
+ return factory.create(vaultId, definition, processEnv);
3129
+ }
3093
3130
  throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
3094
3131
  }
3095
3132
 
3133
+ // ../core/src/secrets/providerCompatibility.ts
3134
+ function assertSecretRefVaultProviderCompatible(manifest, ref, logicalKey = "secret ref") {
3135
+ if (!ref.vault || !ref.provider) {
3136
+ return;
3137
+ }
3138
+ const definition = manifest.vaults[ref.vault];
3139
+ if (!definition || definition.provider === ref.provider) {
3140
+ return;
3141
+ }
3142
+ throw new CnosManifestError(
3143
+ `Secret ref "${logicalKey}" declares provider "${ref.provider}" but vault "${ref.vault}" uses provider "${definition.provider}". Remove the ref provider or use a matching vault.`
3144
+ );
3145
+ }
3146
+
3147
+ // ../core/src/secrets/resolveAuth.ts
3148
+ var import_promises12 = require("fs/promises");
3149
+
3096
3150
  // ../core/src/secrets/prompt.ts
3097
3151
  var import_node_readline = __toESM(require("readline"), 1);
3098
3152
  var import_node_stream = require("stream");
@@ -3133,6 +3187,23 @@ function toAuthError(vaultId, sources) {
3133
3187
  `Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
3134
3188
  );
3135
3189
  }
3190
+ async function resolveTokenFromSource(source, processEnv) {
3191
+ if (source.startsWith("env:")) {
3192
+ return processEnv[source.slice(4)] || void 0;
3193
+ }
3194
+ if (source.startsWith("file:")) {
3195
+ try {
3196
+ const value = await (0, import_promises12.readFile)(expandHomePath(source.slice("file:".length)), "utf8");
3197
+ return value.trim() || void 0;
3198
+ } catch {
3199
+ return void 0;
3200
+ }
3201
+ }
3202
+ if (source.startsWith("keychain:")) {
3203
+ return readKeychain(source.slice("keychain:".length));
3204
+ }
3205
+ return void 0;
3206
+ }
3136
3207
  async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
3137
3208
  const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
3138
3209
  if (sessionKey) {
@@ -3148,6 +3219,32 @@ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
3148
3219
  ...definition.auth?.config ? { config: definition.auth.config } : {}
3149
3220
  };
3150
3221
  }
3222
+ if (definition.auth?.method === "iam") {
3223
+ return {
3224
+ method: "iam",
3225
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
3226
+ };
3227
+ }
3228
+ if (definition.auth?.method === "environment") {
3229
+ return {
3230
+ method: "environment",
3231
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
3232
+ };
3233
+ }
3234
+ const tokenSources = definition.auth?.token?.from ?? [];
3235
+ for (const source of tokenSources) {
3236
+ const token = await resolveTokenFromSource(source, processEnv);
3237
+ if (token) {
3238
+ return {
3239
+ token,
3240
+ method: "token",
3241
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
3242
+ };
3243
+ }
3244
+ }
3245
+ if (definition.auth?.method === "token") {
3246
+ throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...tokenSources]);
3247
+ }
3151
3248
  const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
3152
3249
  for (const source of sources) {
3153
3250
  if (source.startsWith("env:")) {
@@ -3199,22 +3296,76 @@ function collectSecretDescriptors(graph) {
3199
3296
  ref: entry.value
3200
3297
  }));
3201
3298
  }
3202
- async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
3299
+ function secretGroupKey(manifest, descriptor) {
3300
+ assertSecretRefVaultProviderCompatible(manifest, descriptor.ref, descriptor.logicalKey);
3301
+ const vaultId = descriptor.ref.vault ?? "default";
3302
+ const provider = descriptor.ref.provider ?? manifest.vaults[vaultId]?.provider ?? "local";
3303
+ return `${vaultId}\0${provider}`;
3304
+ }
3305
+ function vaultDefinitionForRef(manifest, ref) {
3306
+ assertSecretRefVaultProviderCompatible(manifest, ref);
3307
+ const vaultId = ref.vault ?? "default";
3308
+ const base = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
3309
+ if (!ref.provider || ref.provider === base.provider) {
3310
+ return base;
3311
+ }
3312
+ return {
3313
+ ...base,
3314
+ provider: ref.provider
3315
+ };
3316
+ }
3317
+ async function resolveFromDefinition(vaultId, definition, refs, processEnv, factories) {
3318
+ const runtimeDefinition = {
3319
+ provider: definition.provider,
3320
+ ...definition.auth ? { auth: definition.auth } : {},
3321
+ ...definition.mapping ? { mapping: definition.mapping } : {}
3322
+ };
3323
+ const provider = createSecretVaultProvider(vaultId, runtimeDefinition, processEnv, factories);
3324
+ const auth = await resolveVaultAuth(vaultId, runtimeDefinition, processEnv);
3325
+ await provider.authenticate(auth);
3326
+ return provider.batchGet(refs.map((entry) => entry.ref.ref));
3327
+ }
3328
+ async function batchResolveSecrets(graph, manifest, processEnv = process.env, factories = []) {
3203
3329
  const cache = new SecretCache();
3204
3330
  const descriptors = collectSecretDescriptors(graph);
3205
3331
  const grouped = descriptors.reduce((accumulator, descriptor) => {
3206
- const vaultId = descriptor.ref.vault ?? "default";
3207
- const bucket = accumulator.get(vaultId) ?? [];
3332
+ const key = secretGroupKey(manifest, descriptor);
3333
+ const bucket = accumulator.get(key) ?? [];
3208
3334
  bucket.push(descriptor);
3209
- accumulator.set(vaultId, bucket);
3335
+ accumulator.set(key, bucket);
3210
3336
  return accumulator;
3211
3337
  }, /* @__PURE__ */ new Map());
3212
- for (const [vaultId, refs] of grouped) {
3213
- const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
3214
- const provider = createSecretVaultProvider(vaultId, definition, processEnv);
3215
- const auth = await resolveVaultAuth(vaultId, definition, processEnv);
3216
- await provider.authenticate(auth);
3217
- const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
3338
+ for (const refs of grouped.values()) {
3339
+ const first = refs[0];
3340
+ if (!first) {
3341
+ continue;
3342
+ }
3343
+ const vaultId = first.ref.vault ?? "default";
3344
+ const definition = vaultDefinitionForRef(manifest, first.ref);
3345
+ const definitions = [definition, ...definition.fallback ?? []];
3346
+ const resolved = /* @__PURE__ */ new Map();
3347
+ let remaining = refs;
3348
+ let lastError;
3349
+ for (const candidate of definitions) {
3350
+ try {
3351
+ const candidateValues = await resolveFromDefinition(vaultId, candidate, remaining, processEnv, factories);
3352
+ for (const descriptor of remaining) {
3353
+ const value = candidateValues.get(descriptor.ref.ref);
3354
+ if (value !== void 0) {
3355
+ resolved.set(descriptor.ref.ref, value);
3356
+ }
3357
+ }
3358
+ remaining = remaining.filter((descriptor) => !resolved.has(descriptor.ref.ref));
3359
+ if (remaining.length === 0) {
3360
+ break;
3361
+ }
3362
+ } catch (error) {
3363
+ lastError = error;
3364
+ }
3365
+ }
3366
+ if (resolved.size === 0 && lastError) {
3367
+ throw lastError;
3368
+ }
3218
3369
  cache.load(vaultId, resolved);
3219
3370
  await appendAuditEvent(
3220
3371
  {
@@ -3235,7 +3386,11 @@ function resolveSecretEntryValue(key, value, cache) {
3235
3386
  return value;
3236
3387
  }
3237
3388
  const vaultId = value.vault ?? "default";
3238
- return cache.get(vaultId, value.ref) ?? value;
3389
+ const resolved = cache.get(vaultId, value.ref);
3390
+ if (resolved !== void 0 || cache.isVaultAuthenticated(vaultId)) {
3391
+ return resolved;
3392
+ }
3393
+ return value;
3239
3394
  }
3240
3395
 
3241
3396
  // ../core/src/runtime/projection.ts
@@ -3338,19 +3493,117 @@ function configHash(values) {
3338
3493
  function shouldProjectResolvedValue(sourceId) {
3339
3494
  return sourceId !== "process-env";
3340
3495
  }
3496
+ var SAFE_PROJECTED_CONFIG_KEYS = /* @__PURE__ */ new Set([
3497
+ "address",
3498
+ "audience",
3499
+ "clientid",
3500
+ "endpoint",
3501
+ "mount",
3502
+ "namespace",
3503
+ "path",
3504
+ "projectid",
3505
+ "region",
3506
+ "scope",
3507
+ "scopes",
3508
+ "serviceaccountemail",
3509
+ "tenant",
3510
+ "tenantid",
3511
+ "url",
3512
+ "version",
3513
+ "vaulturl"
3514
+ ]);
3515
+ function isSafeProjectedConfigKey(key) {
3516
+ return SAFE_PROJECTED_CONFIG_KEYS.has(key.replace(/[^A-Za-z0-9]/g, "").toLowerCase());
3517
+ }
3518
+ function sanitizeProjectedConfigValue(value) {
3519
+ if (Array.isArray(value)) {
3520
+ return value.map((item) => sanitizeProjectedConfigValue(item));
3521
+ }
3522
+ if (!value || typeof value !== "object") {
3523
+ return value;
3524
+ }
3525
+ return stableSortObject(
3526
+ Object.fromEntries(
3527
+ Object.entries(value).map(([key, item]) => [key, sanitizeProjectedConfigValue(item)]).filter(([key, item]) => {
3528
+ if (item && typeof item === "object" && !Array.isArray(item)) {
3529
+ return Object.keys(item).length > 0;
3530
+ }
3531
+ return isSafeProjectedConfigKey(key);
3532
+ })
3533
+ )
3534
+ );
3535
+ }
3536
+ function sanitizeProjectedConfig(config) {
3537
+ const sanitized = sanitizeProjectedConfigValue(config);
3538
+ if (!sanitized || typeof sanitized !== "object" || Array.isArray(sanitized)) {
3539
+ return void 0;
3540
+ }
3541
+ return Object.keys(sanitized).length > 0 ? sanitized : void 0;
3542
+ }
3543
+ function projectVaultAuth(definition) {
3544
+ const auth = definition.auth;
3545
+ if (!auth) {
3546
+ return void 0;
3547
+ }
3548
+ const config = auth.config ? sanitizeProjectedConfig(auth.config) : void 0;
3549
+ const projected = {
3550
+ ...auth.method ? { method: auth.method } : {},
3551
+ ...auth.passphrase?.from ? {
3552
+ passphrase: {
3553
+ from: [...auth.passphrase.from]
3554
+ }
3555
+ } : {},
3556
+ ...auth.token?.from ? {
3557
+ token: {
3558
+ from: [...auth.token.from]
3559
+ }
3560
+ } : {},
3561
+ ...config ? { config } : {}
3562
+ };
3563
+ return Object.keys(projected).length > 0 ? projected : void 0;
3564
+ }
3565
+ function projectVaultDefinition(definition) {
3566
+ const auth = projectVaultAuth(definition);
3567
+ const mapping = definition.mapping ? stableSortObject(definition.mapping) : void 0;
3568
+ const fallback = definition.fallback?.map((entry) => projectVaultDefinition({
3569
+ provider: entry.provider,
3570
+ ...entry.auth ? { auth: entry.auth } : {},
3571
+ ...entry.mapping ? { mapping: entry.mapping } : {}
3572
+ }));
3573
+ return {
3574
+ provider: definition.provider,
3575
+ ...auth ? { auth } : {},
3576
+ ...mapping && Object.keys(mapping).length > 0 ? { mapping } : {},
3577
+ ...fallback && fallback.length > 0 ? { fallback } : {}
3578
+ };
3579
+ }
3580
+ function projectReferencedVaults(manifest, vaultIds) {
3581
+ const projected = {};
3582
+ for (const vaultId of Array.from(vaultIds).sort((left, right) => left.localeCompare(right))) {
3583
+ const definition = manifest.vaults[vaultId];
3584
+ if (definition) {
3585
+ projected[vaultId] = projectVaultDefinition(definition);
3586
+ }
3587
+ }
3588
+ return Object.keys(projected).length > 0 ? projected : void 0;
3589
+ }
3341
3590
  function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev", helpers = {}) {
3342
3591
  const values = {};
3343
3592
  const derived = {};
3344
3593
  const secretRefs = {};
3594
+ const referencedVaultIds = /* @__PURE__ */ new Set();
3345
3595
  const namespaces = /* @__PURE__ */ new Set();
3346
3596
  const runtimeNamespaces = /* @__PURE__ */ new Set();
3347
3597
  const publicKeys = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").map((entry) => entry.key.slice("public.".length)).sort((left, right) => left.localeCompare(right));
3348
3598
  for (const [key, entry] of graph.entries) {
3349
3599
  if (entry.namespace === "secret" && isSecretReference(entry.value)) {
3600
+ assertSecretRefVaultProviderCompatible(manifest, entry.value, key);
3350
3601
  const vaultId = entry.value.vault ?? "default";
3602
+ const provider = entry.value.provider ?? manifest.vaults[vaultId]?.provider ?? "local";
3351
3603
  const envVar = resolveProjectedEnvVar(manifest, vaultId, entry.value.ref);
3604
+ referencedVaultIds.add(vaultId);
3352
3605
  secretRefs[key.slice("secret.".length)] = {
3353
- provider: entry.value.provider,
3606
+ provider,
3354
3607
  vault: vaultId,
3355
3608
  ref: entry.value.ref,
3356
3609
  ...envVar ? {
@@ -3396,6 +3649,7 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev", helpers
3396
3649
  namespaces.add(entry.namespace);
3397
3650
  }
3398
3651
  }
3652
+ const vaults = projectReferencedVaults(manifest, referencedVaultIds);
3399
3653
  return {
3400
3654
  version: 1,
3401
3655
  workspace: graph.workspace.workspaceId,
@@ -3405,6 +3659,7 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev", helpers
3405
3659
  values: stableSortObject(values),
3406
3660
  derived: stableSortObject(derived),
3407
3661
  secretRefs: stableSortObject(secretRefs),
3662
+ ...vaults ? { vaults } : {},
3408
3663
  publicKeys,
3409
3664
  runtimeNamespaces: Array.from(runtimeNamespaces).sort((left, right) => left.localeCompare(right)),
3410
3665
  meta: {
@@ -3519,9 +3774,10 @@ function toPublicEnv(graph, manifest, options = {}, helpers = {}) {
3519
3774
  }
3520
3775
 
3521
3776
  // ../core/src/orchestrator/runtime.ts
3522
- function createRuntime(manifest, graph, plugins = [], secretCache, processEnv = process.env, cnosVersion = "0.0.0-dev") {
3777
+ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv = process.env, cnosVersion = "0.0.0-dev", secretVaultProviders = []) {
3523
3778
  const runtimeProviders = createDefaultRuntimeProviders(manifest, processEnv);
3524
3779
  const derivedSupport = createDerivedRuntimeSupport(graph, manifest, runtimeProviders);
3780
+ let activeSecretCache = secretCache;
3525
3781
  function resolveProjectedSourceKey(key) {
3526
3782
  if (!key.startsWith("public.")) {
3527
3783
  return key;
@@ -3538,30 +3794,38 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
3538
3794
  if (!entry || entry.namespace !== "secret" || !isSecretReference(entry.value)) {
3539
3795
  return;
3540
3796
  }
3541
- if (!secretCache) {
3797
+ if (!activeSecretCache) {
3542
3798
  return;
3543
3799
  }
3544
3800
  const vaultId = entry.value.vault ?? "default";
3545
- const definition = manifest.vaults[vaultId] ?? {
3546
- provider: entry.value.provider,
3547
- auth: { passphrase: { from: [] } }
3548
- };
3549
- const provider = createSecretVaultProvider(vaultId, definition, processEnv);
3550
- const auth = await resolveVaultAuth(vaultId, definition, processEnv);
3551
- await provider.authenticate(auth);
3552
- const value = await provider.get(entry.value.ref);
3553
- if (value !== void 0) {
3554
- secretCache.load(vaultId, /* @__PURE__ */ new Map([[entry.value.ref, value]]));
3801
+ const refreshed = await batchResolveSecrets(
3802
+ {
3803
+ ...graph,
3804
+ entries: /* @__PURE__ */ new Map([[key, entry]])
3805
+ },
3806
+ manifest,
3807
+ processEnv,
3808
+ secretVaultProviders
3809
+ );
3810
+ const resolved = refreshed.get(vaultId, entry.value.ref);
3811
+ const existing = activeSecretCache.entriesForVault(vaultId);
3812
+ existing.delete(entry.value.ref);
3813
+ if (resolved !== void 0) {
3814
+ existing.set(entry.value.ref, resolved);
3555
3815
  }
3816
+ activeSecretCache.replace(vaultId, existing);
3556
3817
  }
3557
3818
  async function refreshAllSecrets() {
3558
- if (!secretCache) {
3819
+ if (!activeSecretCache) {
3559
3820
  return;
3560
3821
  }
3561
- const secretKeys = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => entry.key);
3562
- for (const key of secretKeys) {
3563
- await refreshSecretEntry(key);
3564
- }
3822
+ const refreshed = await batchResolveSecrets(
3823
+ graph,
3824
+ manifest,
3825
+ processEnv,
3826
+ secretVaultProviders
3827
+ );
3828
+ activeSecretCache = refreshed;
3565
3829
  }
3566
3830
  function readLogicalKey(key) {
3567
3831
  const resolved = derivedSupport.read(key, (ref) => {
@@ -3569,10 +3833,10 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
3569
3833
  if (!entry2) {
3570
3834
  return void 0;
3571
3835
  }
3572
- if (!secretCache) {
3836
+ if (!activeSecretCache) {
3573
3837
  return entry2.value;
3574
3838
  }
3575
- return resolveSecretEntryValue(ref, entry2.value, secretCache);
3839
+ return resolveSecretEntryValue(ref, entry2.value, activeSecretCache);
3576
3840
  });
3577
3841
  if (resolved !== void 0 || graph.entries.has(key) || manifest.runtimeNamespaces[key.split(".")[0] ?? ""]) {
3578
3842
  return resolved;
@@ -3581,10 +3845,10 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
3581
3845
  if (!entry) {
3582
3846
  return void 0;
3583
3847
  }
3584
- if (!secretCache) {
3848
+ if (!activeSecretCache) {
3585
3849
  return entry.value;
3586
3850
  }
3587
- return resolveSecretEntryValue(key, entry.value, secretCache);
3851
+ return resolveSecretEntryValue(key, entry.value, activeSecretCache);
3588
3852
  }
3589
3853
  return {
3590
3854
  manifest,
@@ -3621,10 +3885,10 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
3621
3885
  if (!entry) {
3622
3886
  return void 0;
3623
3887
  }
3624
- if (!secretCache) {
3888
+ if (!activeSecretCache) {
3625
3889
  return entry.value;
3626
3890
  }
3627
- return resolveSecretEntryValue(candidate, entry.value, secretCache);
3891
+ return resolveSecretEntryValue(candidate, entry.value, activeSecretCache);
3628
3892
  })
3629
3893
  });
3630
3894
  },
@@ -3815,7 +4079,12 @@ async function createCnos(options = {}) {
3815
4079
  });
3816
4080
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
3817
4081
  const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
3818
- const secretCache = options.secretResolution === "lazy" ? new SecretCache() : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
4082
+ const secretCache = options.secretResolution === "lazy" ? new SecretCache() : await batchResolveSecrets(
4083
+ promotedGraph,
4084
+ loadedManifest.manifest,
4085
+ options.processEnv,
4086
+ options.secretVaultProviders
4087
+ );
3819
4088
  return createRuntime(
3820
4089
  loadedManifest.manifest,
3821
4090
  appendMetaEntries({
@@ -3825,12 +4094,13 @@ async function createCnos(options = {}) {
3825
4094
  plugins,
3826
4095
  secretCache,
3827
4096
  options.processEnv,
3828
- options.cnosVersion
4097
+ options.cnosVersion,
4098
+ options.secretVaultProviders
3829
4099
  );
3830
4100
  }
3831
4101
 
3832
4102
  // ../core/src/runtime/dump.ts
3833
- var import_promises12 = require("fs/promises");
4103
+ var import_promises13 = require("fs/promises");
3834
4104
  var import_node_path13 = __toESM(require("path"), 1);
3835
4105
  function buildDumpFiles(graph, options = {}) {
3836
4106
  const basePath = options.flatten ? "" : import_node_path13.default.posix.join("workspaces", graph.workspace.workspaceId);
@@ -3866,8 +4136,8 @@ async function writeDump(graph, options) {
3866
4136
  const plan = planDump(graph, options);
3867
4137
  for (const file of plan.files) {
3868
4138
  const destination = import_node_path13.default.join(root, file.path);
3869
- await (0, import_promises12.mkdir)(import_node_path13.default.dirname(destination), { recursive: true });
3870
- await (0, import_promises12.writeFile)(destination, file.content, "utf8");
4139
+ await (0, import_promises13.mkdir)(import_node_path13.default.dirname(destination), { recursive: true });
4140
+ await (0, import_promises13.writeFile)(destination, file.content, "utf8");
3871
4141
  }
3872
4142
  return {
3873
4143
  ...plan,
@@ -3910,7 +4180,7 @@ function envVarToLogicalKey(envVar, config = {}) {
3910
4180
  // package.json
3911
4181
  var package_default = {
3912
4182
  name: "@kitsy/cnos",
3913
- version: "1.10.0",
4183
+ version: "1.11.0",
3914
4184
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
3915
4185
  type: "module",
3916
4186
  main: "./dist/index.cjs",
@@ -4109,7 +4379,7 @@ function createCliArgsPlugin() {
4109
4379
  }
4110
4380
 
4111
4381
  // ../../plugins/dotenv/src/index.ts
4112
- var import_promises13 = require("fs/promises");
4382
+ var import_promises14 = require("fs/promises");
4113
4383
  var import_node_path14 = __toESM(require("path"), 1);
4114
4384
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
4115
4385
  function parseDoubleQuoted(value) {
@@ -4196,7 +4466,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
4196
4466
  }
4197
4467
  async function readIfPresent(filePath) {
4198
4468
  try {
4199
- return await (0, import_promises13.readFile)(filePath, "utf8");
4469
+ return await (0, import_promises14.readFile)(filePath, "utf8");
4200
4470
  } catch {
4201
4471
  return void 0;
4202
4472
  }
@@ -4262,16 +4532,16 @@ function createPublicEnvExportPlugin() {
4262
4532
  }
4263
4533
 
4264
4534
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
4265
- var import_promises15 = require("fs/promises");
4535
+ var import_promises16 = require("fs/promises");
4266
4536
 
4267
4537
  // ../../plugins/filesystem/src/helpers.ts
4268
- var import_promises14 = require("fs/promises");
4538
+ var import_promises15 = require("fs/promises");
4269
4539
  var import_node_path15 = __toESM(require("path"), 1);
4270
4540
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
4271
4541
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
4272
4542
  async function existsDirectory(targetPath) {
4273
4543
  try {
4274
- const stat2 = await (0, import_promises14.readdir)(targetPath);
4544
+ const stat2 = await (0, import_promises15.readdir)(targetPath);
4275
4545
  void stat2;
4276
4546
  return true;
4277
4547
  } catch {
@@ -4279,7 +4549,7 @@ async function existsDirectory(targetPath) {
4279
4549
  }
4280
4550
  }
4281
4551
  async function collectYamlFiles(root) {
4282
- const entries = await (0, import_promises14.readdir)(root, { withFileTypes: true });
4552
+ const entries = await (0, import_promises15.readdir)(root, { withFileTypes: true });
4283
4553
  const results = [];
4284
4554
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
4285
4555
  const absolutePath = import_node_path15.default.join(root, entry.name);
@@ -4381,7 +4651,7 @@ function createFilesystemSecretsPlugin() {
4381
4651
  );
4382
4652
  const entries = [];
4383
4653
  for (const file of files) {
4384
- const document = await (0, import_promises15.readFile)(file.absolutePath, "utf8");
4654
+ const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
4385
4655
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
4386
4656
  for (const entry of fileEntries) {
4387
4657
  const metadata = toSecretReferenceMetadata(entry.value);
@@ -4397,7 +4667,7 @@ function createFilesystemSecretsPlugin() {
4397
4667
  }
4398
4668
 
4399
4669
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
4400
- var import_promises16 = require("fs/promises");
4670
+ var import_promises17 = require("fs/promises");
4401
4671
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
4402
4672
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
4403
4673
  }
@@ -4418,7 +4688,7 @@ function createFilesystemValuesPlugin() {
4418
4688
  ).map(([namespace]) => namespace);
4419
4689
  const entries = [];
4420
4690
  for (const file of files) {
4421
- const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
4691
+ const document = await (0, import_promises17.readFile)(file.absolutePath, "utf8");
4422
4692
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
4423
4693
  }
4424
4694
  for (const namespace of customNamespaces) {
@@ -4433,7 +4703,7 @@ function createFilesystemValuesPlugin() {
4433
4703
  layers
4434
4704
  );
4435
4705
  for (const file of namespaceFiles) {
4436
- const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
4706
+ const document = await (0, import_promises17.readFile)(file.absolutePath, "utf8");
4437
4707
  entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
4438
4708
  }
4439
4709
  }
@@ -1,6 +1,6 @@
1
- import { R as ResolvedGraph, D as DumpPlanOptions, d as DumpPlan, e as DumpOptions, f as DumpResult, C as CnosCreateOptions, g as CnosRuntime, h as CnosPlugin } from '../core-Ud1o2MBn.cjs';
2
- export { a as ConfigEntry, i as DerivedFormula, j as DerivedValue, k as ExprNode, I as InspectResult, L as LoaderPlugin, b as LogicalKey, M as ManifestFile, N as NormalizedManifest, P as ParsedDerivation, l as RuntimeProvider, T as ToEnvOptions, c as ToPublicEnvOptions } from '../core-Ud1o2MBn.cjs';
3
- export { t as toEnv, a as toPublicEnv } from '../toPublicEnv-fUZMRUOz.cjs';
1
+ import { R as ResolvedGraph, D as DumpPlanOptions, d as DumpPlan, e as DumpOptions, f as DumpResult, C as CnosCreateOptions, g as CnosRuntime, h as CnosPlugin } from '../core-BW8SLnRx.cjs';
2
+ export { a as ConfigEntry, i as DerivedFormula, j as DerivedValue, k as ExprNode, I as InspectResult, L as LoaderPlugin, b as LogicalKey, M as ManifestFile, N as NormalizedManifest, P as ParsedDerivation, l as RuntimeProvider, T as ToEnvOptions, c as ToPublicEnvOptions } from '../core-BW8SLnRx.cjs';
3
+ export { t as toEnv, a as toPublicEnv } from '../toPublicEnv-CZzpvhGg.cjs';
4
4
 
5
5
  declare function planDump(graph: ResolvedGraph, options?: DumpPlanOptions): DumpPlan;
6
6
  declare function writeDump(graph: ResolvedGraph, options: DumpOptions): Promise<DumpResult>;
@@ -1,6 +1,6 @@
1
- import { R as ResolvedGraph, D as DumpPlanOptions, d as DumpPlan, e as DumpOptions, f as DumpResult, C as CnosCreateOptions, g as CnosRuntime, h as CnosPlugin } from '../core-Ud1o2MBn.js';
2
- export { a as ConfigEntry, i as DerivedFormula, j as DerivedValue, k as ExprNode, I as InspectResult, L as LoaderPlugin, b as LogicalKey, M as ManifestFile, N as NormalizedManifest, P as ParsedDerivation, l as RuntimeProvider, T as ToEnvOptions, c as ToPublicEnvOptions } from '../core-Ud1o2MBn.js';
3
- export { t as toEnv, a as toPublicEnv } from '../toPublicEnv-C9wPSpRo.js';
1
+ import { R as ResolvedGraph, D as DumpPlanOptions, d as DumpPlan, e as DumpOptions, f as DumpResult, C as CnosCreateOptions, g as CnosRuntime, h as CnosPlugin } from '../core-BW8SLnRx.js';
2
+ export { a as ConfigEntry, i as DerivedFormula, j as DerivedValue, k as ExprNode, I as InspectResult, L as LoaderPlugin, b as LogicalKey, M as ManifestFile, N as NormalizedManifest, P as ParsedDerivation, l as RuntimeProvider, T as ToEnvOptions, c as ToPublicEnvOptions } from '../core-BW8SLnRx.js';
3
+ export { t as toEnv, a as toPublicEnv } from '../toPublicEnv-CmydGcxg.js';
4
4
 
5
5
  declare function planDump(graph: ResolvedGraph, options?: DumpPlanOptions): DumpPlan;
6
6
  declare function writeDump(graph: ResolvedGraph, options: DumpOptions): Promise<DumpResult>;