@kitsy/cnos 1.1.2 → 1.3.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 (66) hide show
  1. package/README.md +6 -3
  2. package/dist/browser/index.cjs +94 -0
  3. package/dist/browser/index.d.cts +16 -0
  4. package/dist/browser/index.d.ts +16 -0
  5. package/dist/browser/index.js +67 -0
  6. package/dist/build/index.cjs +2889 -0
  7. package/dist/build/index.d.cts +5 -0
  8. package/dist/build/index.d.ts +5 -0
  9. package/dist/build/index.js +26 -0
  10. package/dist/{chunk-53HXUSM6.js → chunk-CDXJISGB.js} +1 -1
  11. package/dist/{chunk-33ZDYDQJ.js → chunk-DRKDNY4I.js} +1470 -462
  12. package/dist/chunk-E7SE6N26.js +189 -0
  13. package/dist/chunk-EDCLLCNL.js +200 -0
  14. package/dist/{chunk-7FBRVJD6.js → chunk-FC3IV6A7.js} +1 -31
  15. package/dist/{chunk-JQGGSNCL.js → chunk-JDII6O72.js} +1 -1
  16. package/dist/chunk-K6QYI2T4.js +105 -0
  17. package/dist/{chunk-IHSV5AFX.js → chunk-OOKFRWTN.js} +1 -1
  18. package/dist/{chunk-HOS4E7XO.js → chunk-OWUZQ4OH.js} +1 -1
  19. package/dist/{chunk-IQOUWY6T.js → chunk-QTKXPY3N.js} +1 -1
  20. package/dist/configure/index.cjs +2928 -0
  21. package/dist/configure/index.d.cts +12 -0
  22. package/dist/configure/index.d.ts +12 -0
  23. package/dist/configure/index.js +24 -0
  24. package/dist/{envNaming-BrOk5ndZ.d.cts → envNaming-D6k66myh.d.cts} +1 -1
  25. package/dist/{envNaming-DCaNdnrF.d.ts → envNaming-Dy3WYiGK.d.ts} +1 -1
  26. package/dist/index.cjs +1396 -264
  27. package/dist/index.d.cts +2 -12
  28. package/dist/index.d.ts +2 -12
  29. package/dist/index.js +13 -143
  30. package/dist/internal.cjs +1913 -63
  31. package/dist/internal.d.cts +190 -8
  32. package/dist/internal.d.ts +190 -8
  33. package/dist/internal.js +669 -3
  34. package/dist/plugin/basic-schema.cjs +29 -2
  35. package/dist/plugin/basic-schema.d.cts +1 -1
  36. package/dist/plugin/basic-schema.d.ts +1 -1
  37. package/dist/plugin/basic-schema.js +2 -2
  38. package/dist/plugin/cli-args.cjs +29 -2
  39. package/dist/plugin/cli-args.d.cts +1 -1
  40. package/dist/plugin/cli-args.d.ts +1 -1
  41. package/dist/plugin/cli-args.js +2 -2
  42. package/dist/plugin/dotenv.cjs +38 -11
  43. package/dist/plugin/dotenv.d.cts +2 -2
  44. package/dist/plugin/dotenv.d.ts +2 -2
  45. package/dist/plugin/dotenv.js +2 -2
  46. package/dist/plugin/env-export.cjs +60 -48
  47. package/dist/plugin/env-export.d.cts +2 -2
  48. package/dist/plugin/env-export.d.ts +2 -2
  49. package/dist/plugin/env-export.js +2 -2
  50. package/dist/plugin/filesystem.cjs +46 -91
  51. package/dist/plugin/filesystem.d.cts +1 -1
  52. package/dist/plugin/filesystem.d.ts +1 -1
  53. package/dist/plugin/filesystem.js +2 -2
  54. package/dist/plugin/process-env.cjs +33 -6
  55. package/dist/plugin/process-env.d.cts +2 -2
  56. package/dist/plugin/process-env.d.ts +2 -2
  57. package/dist/plugin/process-env.js +2 -2
  58. package/dist/{plugin-BVNEHj19.d.cts → plugin-CyNkf7Dm.d.cts} +42 -2
  59. package/dist/{plugin-BVNEHj19.d.ts → plugin-CyNkf7Dm.d.ts} +42 -2
  60. package/dist/runtime/index.cjs +3116 -0
  61. package/dist/runtime/index.d.cts +23 -0
  62. package/dist/runtime/index.d.ts +23 -0
  63. package/dist/runtime/index.js +15 -0
  64. package/dist/{toPublicEnv-Dd152fFy.d.cts → toPublicEnv-Cz72m6y0.d.cts} +1 -1
  65. package/dist/{toPublicEnv-Gwz3xTK0.d.ts → toPublicEnv-D2PZkaN-.d.ts} +1 -1
  66. package/package.json +26 -1
package/dist/index.cjs CHANGED
@@ -30,12 +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
- toEnv: () => toEnv,
37
- toPublicEnv: () => toPublicEnv,
38
- writeDump: () => writeDump
33
+ cnos: () => runtime_default,
34
+ default: () => runtime_default
39
35
  });
40
36
  module.exports = __toCommonJS(index_exports);
41
37
 
@@ -53,6 +49,16 @@ var CnosManifestError = class extends CnosError {
53
49
  }
54
50
  manifestPath;
55
51
  };
52
+ var CnosSecurityError = class extends CnosError {
53
+ constructor(message) {
54
+ super(message);
55
+ }
56
+ };
57
+ var CnosAuthenticationError = class extends CnosError {
58
+ constructor(message) {
59
+ super(message);
60
+ }
61
+ };
56
62
  var CnosKeyNotFoundError = class extends CnosError {
57
63
  constructor(key) {
58
64
  super(`Missing required CNOS config key: ${key}`);
@@ -105,6 +111,68 @@ function createProvenanceInspector() {
105
111
  };
106
112
  }
107
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
+
108
176
  // ../core/src/manifest/loadManifest.ts
109
177
  var import_promises2 = require("fs/promises");
110
178
  var import_node_path2 = __toESM(require("path"), 1);
@@ -212,6 +280,34 @@ var DEFAULT_FRAMEWORK_PREFIXES = {
212
280
  vite: "VITE_",
213
281
  nuxt: "NUXT_PUBLIC_"
214
282
  };
283
+ var DEFAULT_NAMESPACES = {
284
+ value: {
285
+ kind: "data",
286
+ shareable: true
287
+ },
288
+ secret: {
289
+ kind: "data",
290
+ shareable: false,
291
+ sensitive: true
292
+ },
293
+ meta: {
294
+ kind: "system",
295
+ shareable: false,
296
+ readonly: true
297
+ },
298
+ public: {
299
+ kind: "projection",
300
+ source: "promote",
301
+ shareable: true,
302
+ readonly: true
303
+ },
304
+ env: {
305
+ kind: "projection",
306
+ source: "envMapping",
307
+ shareable: true,
308
+ readonly: true
309
+ }
310
+ };
215
311
  function validateResolveFrom(resolveFrom) {
216
312
  const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
217
313
  for (const entry of resolveFrom) {
@@ -232,6 +328,103 @@ function normalizeWorkspaceItems(items) {
232
328
  ])
233
329
  );
234
330
  }
331
+ function normalizeNamespaces(namespaces) {
332
+ const normalized = Object.fromEntries(
333
+ Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
334
+ namespace,
335
+ {
336
+ kind: definition.kind ?? "data",
337
+ shareable: definition.shareable ?? false,
338
+ ...definition.sensitive !== void 0 ? { sensitive: definition.sensitive } : {},
339
+ ...definition.readonly !== void 0 ? { readonly: definition.readonly } : {},
340
+ ...definition.source ? { source: definition.source } : {}
341
+ }
342
+ ])
343
+ );
344
+ return {
345
+ ...DEFAULT_NAMESPACES,
346
+ ...normalized
347
+ };
348
+ }
349
+ function normalizeVaults(vaults) {
350
+ return Object.fromEntries(
351
+ Object.entries(vaults ?? {}).map(([name, definition]) => {
352
+ const legacyPassphrase = definition.passphrase;
353
+ if (legacyPassphrase !== void 0) {
354
+ throw new CnosManifestError(
355
+ `Vault "${name}" uses legacy passphrase configuration. Use vaults.${name}.auth instead.`
356
+ );
357
+ }
358
+ const provider = definition.provider?.trim();
359
+ if (!provider) {
360
+ throw new CnosManifestError(`Vault "${name}" requires a provider`);
361
+ }
362
+ const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
363
+ const normalizedMapping = Object.fromEntries(
364
+ Object.entries(definition.mapping ?? {}).filter(
365
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
366
+ ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
367
+ );
368
+ return [
369
+ name,
370
+ {
371
+ provider,
372
+ auth: normalizedAuth,
373
+ ...Object.keys(normalizedMapping).length > 0 ? {
374
+ mapping: normalizedMapping
375
+ } : {}
376
+ }
377
+ ];
378
+ })
379
+ );
380
+ }
381
+ function normalizeAuthSources(value) {
382
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
383
+ return void 0;
384
+ }
385
+ const sources = Array.isArray(value.from) ? value.from : void 0;
386
+ const normalized = (sources ?? []).map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
387
+ return normalized.length > 0 ? normalized : void 0;
388
+ }
389
+ function normalizeVaultAuth(vaultName, provider, auth) {
390
+ if (provider === "local") {
391
+ const passphraseSources = normalizeAuthSources(auth?.passphrase);
392
+ const defaultToken = vaultName.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
393
+ const defaultSources = [
394
+ ...defaultToken ? [`env:CNOS_SECRET_PASSPHRASE_${defaultToken}`] : [],
395
+ "env:CNOS_SECRET_PASSPHRASE",
396
+ `keychain:cnos/${vaultName}`,
397
+ "prompt"
398
+ ];
399
+ return {
400
+ method: auth?.method ?? "passphrase",
401
+ passphrase: {
402
+ from: passphraseSources ?? defaultSources
403
+ },
404
+ ...auth?.config ? { config: auth.config } : {}
405
+ };
406
+ }
407
+ if (provider === "github-secrets") {
408
+ return {
409
+ method: auth?.method ?? "environment",
410
+ ...auth?.config ? { config: auth.config } : {}
411
+ };
412
+ }
413
+ return {
414
+ ...auth?.method ? { method: auth.method } : {},
415
+ ...normalizeAuthSources(auth?.passphrase) ? {
416
+ passphrase: {
417
+ from: normalizeAuthSources(auth?.passphrase) ?? []
418
+ }
419
+ } : {},
420
+ ...normalizeAuthSources(auth?.token) ? {
421
+ token: {
422
+ from: normalizeAuthSources(auth?.token) ?? []
423
+ }
424
+ } : {},
425
+ ...auth?.config ? { config: auth.config } : {}
426
+ };
427
+ }
235
428
  function normalizeManifest(manifest) {
236
429
  const version = manifest.version ?? 1;
237
430
  if (version !== 1) {
@@ -316,6 +509,8 @@ function normalizeManifest(manifest) {
316
509
  ...manifest.public?.frameworks ?? {}
317
510
  }
318
511
  },
512
+ namespaces: normalizeNamespaces(manifest.namespaces),
513
+ vaults: normalizeVaults(manifest.vaults),
319
514
  writePolicy: {
320
515
  define: {
321
516
  defaultProfile: manifest.writePolicy?.define?.defaultProfile ?? defaultProfile,
@@ -519,6 +714,86 @@ async function expandProfileChain(activeProfile, options = {}) {
519
714
  };
520
715
  }
521
716
 
717
+ // ../core/src/promotions/validatePromotion.ts
718
+ var DEFAULT_DATA_NAMESPACE = {
719
+ kind: "data",
720
+ shareable: false
721
+ };
722
+ function getNamespaceNameForKey(key) {
723
+ const [namespace] = key.split(".");
724
+ if (!namespace || !key.includes(".")) {
725
+ throw new CnosManifestError(`Logical key must be namespace-qualified: ${key}`);
726
+ }
727
+ return namespace;
728
+ }
729
+ function getNamespaceDefinition(manifest, namespaceOrKey) {
730
+ const namespace = namespaceOrKey.includes(".") ? getNamespaceNameForKey(namespaceOrKey) : namespaceOrKey;
731
+ return manifest.namespaces[namespace] ?? DEFAULT_DATA_NAMESPACE;
732
+ }
733
+ function ensureProjectionAllowed(manifest, key, target) {
734
+ const namespace = getNamespaceNameForKey(key);
735
+ const definition = getNamespaceDefinition(manifest, namespace);
736
+ if (definition.kind !== "data") {
737
+ throw new CnosManifestError(
738
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not a data namespace.`
739
+ );
740
+ }
741
+ if (definition.sensitive) {
742
+ throw new CnosSecurityError(
743
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is sensitive.`
744
+ );
745
+ }
746
+ if (!definition.shareable) {
747
+ throw new CnosSecurityError(
748
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not shareable.`
749
+ );
750
+ }
751
+ }
752
+
753
+ // ../core/src/promotions/promoteToPublic.ts
754
+ function toPublicKey(key) {
755
+ const namespace = getNamespaceNameForKey(key);
756
+ return namespace === "value" ? `public.${stripNamespace(key)}` : `public.${key}`;
757
+ }
758
+ function toPromotedConfigEntry(entry, key, promotedFrom) {
759
+ return {
760
+ ...entry,
761
+ key,
762
+ namespace: "public",
763
+ sourceId: "public-promote",
764
+ pluginId: "core",
765
+ metadata: {
766
+ ...entry.metadata ?? {},
767
+ promotedFrom
768
+ }
769
+ };
770
+ }
771
+ function toPromotedResolvedEntry(entry) {
772
+ const key = toPublicKey(entry.key);
773
+ return {
774
+ key,
775
+ value: entry.value,
776
+ namespace: "public",
777
+ winner: toPromotedConfigEntry(entry.winner, key, entry.key),
778
+ overridden: entry.overridden.map((override) => toPromotedConfigEntry(override, key, entry.key))
779
+ };
780
+ }
781
+ function promoteToPublic(graph, manifest) {
782
+ const entries = new Map(graph.entries);
783
+ for (const key of manifest.public.promote) {
784
+ ensureProjectionAllowed(manifest, key, "public");
785
+ const resolved = graph.entries.get(key);
786
+ if (!resolved) {
787
+ continue;
788
+ }
789
+ entries.set(toPublicKey(key), toPromotedResolvedEntry(resolved));
790
+ }
791
+ return {
792
+ ...graph,
793
+ entries
794
+ };
795
+ }
796
+
522
797
  // ../core/src/profiles/resolveActiveProfile.ts
523
798
  function resolveActiveProfile(manifest, options = {}) {
524
799
  for (const source of manifest.profiles.resolveFrom) {
@@ -953,60 +1228,48 @@ async function runPipeline(options) {
953
1228
  return collectedEntries.flat();
954
1229
  }
955
1230
 
956
- // ../core/src/runtime/projection.ts
957
- function setNestedValue(target, pathSegments, value) {
958
- const [head, ...tail] = pathSegments;
959
- if (!head) {
960
- return;
961
- }
962
- if (tail.length === 0) {
963
- target[head] = value;
964
- return;
965
- }
966
- const current = target[head];
967
- const nextTarget = current && typeof current === "object" && !Array.isArray(current) ? current : {};
968
- target[head] = nextTarget;
969
- setNestedValue(nextTarget, tail, value);
970
- }
971
- function toNamespaceObject(graph, namespace) {
972
- const output = {};
973
- const resolvedEntries = Array.from(graph.entries.values()).sort(
974
- (left, right) => left.key.localeCompare(right.key)
975
- );
976
- for (const entry of resolvedEntries) {
977
- if (namespace && entry.namespace !== namespace) {
978
- continue;
979
- }
980
- const valuePath = namespace ? stripNamespace(entry.key) : entry.key;
981
- setNestedValue(output, valuePath.split("."), entry.value);
982
- }
983
- return output;
984
- }
1231
+ // ../core/src/secrets/auditLog.ts
1232
+ var import_promises8 = require("fs/promises");
1233
+ var import_node_path8 = __toESM(require("path"), 1);
985
1234
 
986
- // ../core/src/runtime/read.ts
987
- function readValue(graph, key) {
988
- return graph.entries.get(key)?.value;
989
- }
1235
+ // ../core/src/utils/secretStore.ts
1236
+ var import_node_crypto = require("crypto");
1237
+ var import_promises7 = require("fs/promises");
1238
+ var import_node_path7 = __toESM(require("path"), 1);
990
1239
 
991
- // ../core/src/runtime/readOr.ts
992
- function readOrValue(graph, key, fallback) {
993
- const value = readValue(graph, key);
994
- return value === void 0 ? fallback : value;
1240
+ // ../core/src/secrets/sessionStore.ts
1241
+ var import_promises6 = require("fs/promises");
1242
+ var import_node_path6 = __toESM(require("path"), 1);
1243
+ function buildSessionRoot(processEnv = process.env) {
1244
+ return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
995
1245
  }
996
-
997
- // ../core/src/runtime/require.ts
998
- function requireValue(graph, key) {
999
- const value = readValue(graph, key);
1000
- if (value === void 0) {
1001
- throw new CnosKeyNotFoundError(key);
1246
+ function buildSessionPath(vault, processEnv) {
1247
+ return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1248
+ }
1249
+ async function readVaultSessionKey(vault, processEnv) {
1250
+ try {
1251
+ const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
1252
+ const document = JSON.parse(source);
1253
+ if (document.version !== 1 || typeof document.derivedKey !== "string") {
1254
+ return void 0;
1255
+ }
1256
+ const key = Buffer.from(document.derivedKey, "hex");
1257
+ return key.length > 0 ? key : void 0;
1258
+ } catch {
1259
+ return void 0;
1002
1260
  }
1003
- return value;
1004
1261
  }
1005
1262
 
1006
1263
  // ../core/src/utils/secretStore.ts
1007
- var import_node_crypto = require("crypto");
1008
- var import_promises6 = require("fs/promises");
1009
- var import_node_path6 = __toESM(require("path"), 1);
1264
+ var KEY_LENGTH = 32;
1265
+ var SALT_LENGTH = 32;
1266
+ var IV_LENGTH = 12;
1267
+ var AUTH_TAG_LENGTH = 16;
1268
+ var PBKDF2_ITERATIONS = 6e5;
1269
+ var KEYSTORE_VERSION = 1;
1270
+ var METADATA_VERSION = 1;
1271
+ var META_FILENAME = "meta.yml";
1272
+ var KEYSTORE_FILENAME = "keystore.enc";
1010
1273
  function isObject(value) {
1011
1274
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1012
1275
  }
@@ -1014,137 +1277,755 @@ function isSecretReference(value) {
1014
1277
  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));
1015
1278
  }
1016
1279
  function resolveSecretStoreRoot(processEnv = process.env) {
1017
- return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1280
+ return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1018
1281
  }
1019
- function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1020
- return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1282
+ function normalizeVaultToken(vault = "default") {
1283
+ return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1021
1284
  }
1022
- function deriveKey(passphrase, salt) {
1023
- return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1285
+ function getVaultPassphraseEnvVar(vault = "default") {
1286
+ const vaultToken = normalizeVaultToken(vault);
1287
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
1288
+ }
1289
+ function getVaultSessionKeyEnvVar(vault = "default") {
1290
+ const vaultToken = normalizeVaultToken(vault);
1291
+ return `__CNOS_VAULT_KEY_${vaultToken || "DEFAULT"}__`;
1024
1292
  }
1025
1293
  function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1026
- const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1027
- return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1028
- }
1029
- function decryptDocument(document, passphrase) {
1030
- const salt = Buffer.from(document.salt, "base64");
1031
- const iv = Buffer.from(document.iv, "base64");
1032
- const tag = Buffer.from(document.tag, "base64");
1033
- const ciphertext = Buffer.from(document.ciphertext, "base64");
1034
- const key = deriveKey(passphrase, salt);
1035
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1036
- decipher.setAuthTag(tag);
1037
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1038
- return plaintext.toString("utf8");
1294
+ return processEnv[getVaultPassphraseEnvVar(vault)] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1039
1295
  }
1040
- async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1041
- if (!passphrase) {
1042
- throw new CnosManifestError(
1043
- `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1044
- );
1296
+ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1297
+ const encoded = processEnv[getVaultSessionKeyEnvVar(vault)];
1298
+ if (!encoded) {
1299
+ return readVaultSessionKey(vault, processEnv);
1045
1300
  }
1046
- const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1047
- const source = await (0, import_promises6.readFile)(filePath, "utf8");
1048
- const document = JSON.parse(source);
1049
- 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") {
1050
- throw new CnosManifestError("Invalid local secret document", filePath);
1301
+ try {
1302
+ const key = Buffer.from(encoded, "hex");
1303
+ return key.length === KEY_LENGTH ? key : void 0;
1304
+ } catch {
1305
+ return void 0;
1051
1306
  }
1052
- return decryptDocument(document, passphrase);
1053
1307
  }
1054
-
1055
- // ../core/src/runtime/toEnv.ts
1056
- function normalizeEnvValue(value) {
1057
- if (value === void 0 || value === null) {
1058
- return "";
1308
+ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1309
+ return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1310
+ }
1311
+ function buildMetaPath(storeRoot, vault = "default") {
1312
+ return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
1313
+ }
1314
+ function buildKeystorePath(storeRoot, vault = "default") {
1315
+ return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1316
+ }
1317
+ function buildLegacyVaultFile(storeRoot, vault = "default") {
1318
+ return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
1319
+ }
1320
+ function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1321
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
1322
+ }
1323
+ function assertVaultMetadata(value, filePath) {
1324
+ if (!isObject(value)) {
1325
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1059
1326
  }
1060
- if (typeof value === "string") {
1061
- return value;
1327
+ 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") {
1328
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1062
1329
  }
1063
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1064
- return String(value);
1330
+ return value;
1331
+ }
1332
+ async function exists3(targetPath) {
1333
+ try {
1334
+ await (0, import_promises7.stat)(targetPath);
1335
+ return true;
1336
+ } catch {
1337
+ return false;
1065
1338
  }
1066
- return JSON.stringify(value);
1067
1339
  }
1068
- function toEnv(graph, manifest, options = {}) {
1069
- const includeSecrets = options.includeSecrets ?? true;
1070
- const output = {};
1071
- const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
1072
- ([left], [right]) => left.localeCompare(right)
1340
+ async function detectLegacyVaultFormat(storeRoot, vault = "default") {
1341
+ const legacyFile = buildLegacyVaultFile(storeRoot, vault);
1342
+ const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
1343
+ if (await exists3(legacyFile)) {
1344
+ return legacyFile;
1345
+ }
1346
+ if (await exists3(legacyStore)) {
1347
+ return legacyStore;
1348
+ }
1349
+ return void 0;
1350
+ }
1351
+ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1352
+ const legacyPath = await detectLegacyVaultFormat(storeRoot, vault);
1353
+ if (!legacyPath) {
1354
+ return;
1355
+ }
1356
+ throw new CnosSecurityError(
1357
+ `Legacy CNOS local vault format detected for vault "${vault}" at ${legacyPath}. CNOS 1.4 requires the new keystore format. Remove and recreate the vault.`
1073
1358
  );
1074
- for (const [envVar, logicalKey] of mappedEntries) {
1075
- const entry = graph.entries.get(logicalKey);
1076
- if (!entry) {
1077
- continue;
1078
- }
1079
- if (entry.namespace === "secret" && !includeSecrets) {
1080
- continue;
1081
- }
1082
- if (isSecretReference(entry.value)) {
1083
- continue;
1359
+ }
1360
+ function encryptPayload(payload, key) {
1361
+ const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1362
+ const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
1363
+ const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1364
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1365
+ const tag = cipher.getAuthTag();
1366
+ return Buffer.concat([
1367
+ Buffer.from(Uint32Array.of(KEYSTORE_VERSION).buffer),
1368
+ iv,
1369
+ tag,
1370
+ ciphertext
1371
+ ]);
1372
+ }
1373
+ function decryptPayload(buffer, key) {
1374
+ if (buffer.length < 4 + IV_LENGTH + AUTH_TAG_LENGTH) {
1375
+ throw new CnosSecurityError("Invalid CNOS local vault keystore");
1376
+ }
1377
+ const version = buffer.readUInt32LE(0);
1378
+ if (version !== KEYSTORE_VERSION) {
1379
+ throw new CnosSecurityError(`Unsupported CNOS local vault keystore version: ${version}`);
1380
+ }
1381
+ const ivOffset = 4;
1382
+ const tagOffset = ivOffset + IV_LENGTH;
1383
+ const cipherOffset = tagOffset + AUTH_TAG_LENGTH;
1384
+ const iv = buffer.subarray(ivOffset, tagOffset);
1385
+ const tag = buffer.subarray(tagOffset, cipherOffset);
1386
+ const ciphertext = buffer.subarray(cipherOffset);
1387
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1388
+ decipher.setAuthTag(tag);
1389
+ try {
1390
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
1391
+ const payload = JSON.parse(plaintext);
1392
+ if (!payload || !isObject(payload.secrets) || !isObject(payload.metadata)) {
1393
+ throw new Error("invalid");
1084
1394
  }
1085
- output[envVar] = normalizeEnvValue(entry.value);
1395
+ return {
1396
+ secrets: Object.fromEntries(
1397
+ Object.entries(payload.secrets).filter((entry) => typeof entry[1] === "string")
1398
+ ),
1399
+ metadata: Object.fromEntries(
1400
+ Object.entries(payload.metadata).filter(
1401
+ (entry) => isObject(entry[1]) && typeof entry[1].createdAt === "string" && typeof entry[1].updatedAt === "string"
1402
+ )
1403
+ )
1404
+ };
1405
+ } catch {
1406
+ throw new CnosAuthenticationError("Failed to decrypt CNOS local vault. Check vault authentication.");
1086
1407
  }
1087
- return output;
1088
1408
  }
1089
-
1090
- // ../core/src/utils/envNaming.ts
1091
- function normalizeMappingConfig(config = {}) {
1409
+ function buildInitialPayload() {
1092
1410
  return {
1093
- convention: config.convention,
1094
- explicit: config.explicit ?? {}
1411
+ secrets: {},
1412
+ metadata: {}
1095
1413
  };
1096
1414
  }
1097
- function toScreamingSnakeSegment(segment) {
1098
- return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1415
+ async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1416
+ const metaPath = buildMetaPath(storeRoot, vault);
1417
+ const keystorePath = buildKeystorePath(storeRoot, vault);
1418
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
1419
+ await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1420
+ await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
1099
1421
  }
1100
- function toScreamingSnake(path10) {
1101
- return path10.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1422
+ async function readVaultMetadata(storeRoot, vault = "default") {
1423
+ await assertNoLegacyVaultFormat(storeRoot, vault);
1424
+ const metaPath = buildMetaPath(storeRoot, vault);
1425
+ try {
1426
+ const source = await (0, import_promises7.readFile)(metaPath, "utf8");
1427
+ return assertVaultMetadata(parseYaml(source), metaPath);
1428
+ } catch (error) {
1429
+ if (error.code === "ENOENT") {
1430
+ return void 0;
1431
+ }
1432
+ throw error;
1433
+ }
1102
1434
  }
1103
- function fromScreamingSnake(path10) {
1104
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1435
+ async function createSecretVault(storeRoot, vault, passphrase) {
1436
+ const normalizedVault = vault.trim() || "default";
1437
+ await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1438
+ const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
1439
+ const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1440
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1441
+ const meta = {
1442
+ version: METADATA_VERSION,
1443
+ algorithm: "aes-256-gcm",
1444
+ kdf: "pbkdf2-sha512",
1445
+ iterations: PBKDF2_ITERATIONS,
1446
+ salt: salt.toString("base64"),
1447
+ createdAt,
1448
+ secretCount: 0
1449
+ };
1450
+ await writeVaultFiles(storeRoot, normalizedVault, meta, buildInitialPayload(), key);
1451
+ return buildMetaPath(storeRoot, normalizedVault);
1105
1452
  }
1106
- function logicalKeyToEnvVar(key, config = {}) {
1107
- const normalized = normalizeMappingConfig(config);
1108
- const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
1109
- if (explicitEntry) {
1110
- return explicitEntry[0];
1453
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
1454
+ const normalizedVault = vault.trim() || "default";
1455
+ const meta = await readVaultMetadata(storeRoot, normalizedVault);
1456
+ if (meta) {
1457
+ return buildMetaPath(storeRoot, normalizedVault);
1111
1458
  }
1112
- if (normalized.convention !== "SCREAMING_SNAKE") {
1459
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
1460
+ }
1461
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
1462
+ if (definition?.provider !== "local") {
1113
1463
  return void 0;
1114
1464
  }
1115
- if (key.startsWith("value.")) {
1116
- return toScreamingSnake(key.slice("value.".length));
1117
- }
1118
- if (key.startsWith("secret.")) {
1119
- return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
1465
+ const configuredSources = definition.auth?.passphrase?.from ?? [];
1466
+ for (const source of configuredSources) {
1467
+ if (source.startsWith("env:")) {
1468
+ const value = processEnv[source.slice(4)];
1469
+ if (value) {
1470
+ return value;
1471
+ }
1472
+ }
1120
1473
  }
1121
- return void 0;
1474
+ return resolveSecretPassphrase(vault, processEnv);
1122
1475
  }
1123
- function envVarToLogicalKey(envVar, config = {}) {
1124
- const normalized = normalizeMappingConfig(config);
1125
- const explicitMatch = normalized.explicit[envVar];
1126
- if (explicitMatch) {
1127
- return explicitMatch;
1476
+ async function resolveVaultAccessKey(storeRoot, definition, vault = "default", processEnv = process.env) {
1477
+ if (definition?.provider !== "local") {
1478
+ return definition?.provider === "github-secrets" ? {
1479
+ method: definition.auth?.method ?? "environment",
1480
+ ...definition?.auth?.config ? { config: definition.auth.config } : {}
1481
+ } : void 0;
1128
1482
  }
1129
- if (normalized.convention !== "SCREAMING_SNAKE") {
1483
+ const sessionKey = await resolveVaultSessionKey(vault, processEnv);
1484
+ if (sessionKey) {
1485
+ return {
1486
+ derivedKey: sessionKey,
1487
+ method: "keychain",
1488
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1489
+ };
1490
+ }
1491
+ const passphrase = resolveConfiguredVaultPassphrase(definition, vault, processEnv);
1492
+ if (passphrase) {
1493
+ return {
1494
+ passphrase,
1495
+ method: "passphrase",
1496
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1497
+ };
1498
+ }
1499
+ const metadata = await readVaultMetadata(storeRoot, vault);
1500
+ if (!metadata) {
1130
1501
  return void 0;
1131
1502
  }
1132
- if (envVar.startsWith("SECRET_")) {
1133
- const stripped = envVar.slice("SECRET_".length);
1134
- if (!stripped) {
1503
+ throw new CnosAuthenticationError(
1504
+ `Cannot authenticate to vault "${vault}". Set ${getVaultPassphraseEnvVar(vault)} or run cnos vault auth ${vault}.`
1505
+ );
1506
+ }
1507
+ async function loadVaultPayload(storeRoot, vault, auth) {
1508
+ const meta = await readVaultMetadata(storeRoot, vault);
1509
+ if (!meta) {
1510
+ throw new CnosManifestError(`Missing CNOS vault metadata for "${vault}"`);
1511
+ }
1512
+ const salt = Buffer.from(meta.salt, "base64");
1513
+ const key = auth.derivedKey ?? (auth.passphrase ? deriveVaultKey(auth.passphrase, salt, meta.iterations) : void 0);
1514
+ if (!key) {
1515
+ throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1516
+ }
1517
+ const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
1518
+ return {
1519
+ meta,
1520
+ payload: decryptPayload(buffer, key),
1521
+ key
1522
+ };
1523
+ }
1524
+ async function writeLocalSecret(storeRoot, ref, value, authOrPassphrase, vault = "default") {
1525
+ const auth = typeof authOrPassphrase === "string" ? {
1526
+ passphrase: authOrPassphrase,
1527
+ method: "passphrase"
1528
+ } : authOrPassphrase;
1529
+ if (auth.passphrase) {
1530
+ await ensureSecretVault(storeRoot, vault, auth.passphrase);
1531
+ } else {
1532
+ const meta2 = await readVaultMetadata(storeRoot, vault);
1533
+ if (!meta2) {
1534
+ throw new CnosAuthenticationError(`Vault "${vault}" requires passphrase-based authentication for initial creation.`);
1535
+ }
1536
+ }
1537
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1538
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1539
+ const existing = payload.metadata[ref];
1540
+ payload.secrets[ref] = value;
1541
+ payload.metadata[ref] = {
1542
+ createdAt: existing?.createdAt ?? now,
1543
+ updatedAt: now
1544
+ };
1545
+ const nextMeta = {
1546
+ ...meta,
1547
+ secretCount: Object.keys(payload.secrets).length
1548
+ };
1549
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1550
+ return buildKeystorePath(storeRoot, vault);
1551
+ }
1552
+ async function deleteLocalSecret(storeRoot, ref, auth, vault = "default") {
1553
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1554
+ if (!(ref in payload.secrets)) {
1555
+ return false;
1556
+ }
1557
+ delete payload.secrets[ref];
1558
+ delete payload.metadata[ref];
1559
+ const nextMeta = {
1560
+ ...meta,
1561
+ secretCount: Object.keys(payload.secrets).length
1562
+ };
1563
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1564
+ return true;
1565
+ }
1566
+ async function readLocalSecret(storeRoot, ref, auth, vault = "default") {
1567
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1568
+ const value = payload.secrets[ref];
1569
+ if (value === void 0) {
1570
+ throw new CnosManifestError(`Missing local secret ref "${ref}" in vault "${vault}"`);
1571
+ }
1572
+ return value;
1573
+ }
1574
+ async function listLocalSecrets(storeRoot, auth, vault = "default") {
1575
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1576
+ return Object.keys(payload.secrets).sort((left, right) => left.localeCompare(right));
1577
+ }
1578
+ function resolveVaultDefinition(vaults, vault = "default") {
1579
+ const definition = vaults?.[vault];
1580
+ const provider = definition?.provider ?? "local";
1581
+ return {
1582
+ name: vault,
1583
+ provider,
1584
+ ...definition?.auth ? { auth: definition.auth } : {},
1585
+ ...definition?.mapping ? { mapping: definition.mapping } : {},
1586
+ requiresAuthentication: provider === "local"
1587
+ };
1588
+ }
1589
+
1590
+ // ../core/src/secrets/auditLog.ts
1591
+ async function appendAuditEvent(event, processEnv = process.env) {
1592
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1593
+ await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
1594
+ await (0, import_promises8.appendFile)(
1595
+ auditFile,
1596
+ `${JSON.stringify({
1597
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1598
+ ...event
1599
+ })}
1600
+ `,
1601
+ "utf8"
1602
+ );
1603
+ }
1604
+
1605
+ // ../core/src/secrets/secretCache.ts
1606
+ var SecretCache = class {
1607
+ cache = /* @__PURE__ */ new Map();
1608
+ authenticated = /* @__PURE__ */ new Set();
1609
+ load(vaultId, secrets) {
1610
+ this.authenticated.add(vaultId);
1611
+ for (const [ref, value] of secrets) {
1612
+ this.cache.set(`${vaultId}:${ref}`, value);
1613
+ }
1614
+ }
1615
+ isVaultAuthenticated(vaultId) {
1616
+ return this.authenticated.has(vaultId);
1617
+ }
1618
+ get(vaultId, ref) {
1619
+ return this.cache.get(`${vaultId}:${ref}`);
1620
+ }
1621
+ clear(vaultId) {
1622
+ if (!vaultId) {
1623
+ this.cache.clear();
1624
+ this.authenticated.clear();
1625
+ return;
1626
+ }
1627
+ this.authenticated.delete(vaultId);
1628
+ for (const key of Array.from(this.cache.keys())) {
1629
+ if (key.startsWith(`${vaultId}:`)) {
1630
+ this.cache.delete(key);
1631
+ }
1632
+ }
1633
+ }
1634
+ };
1635
+
1636
+ // ../core/src/secrets/providers/github.ts
1637
+ var GithubSecretsVaultProvider = class {
1638
+ constructor(vaultId, definition, processEnv = process.env) {
1639
+ this.vaultId = vaultId;
1640
+ this.definition = definition;
1641
+ this.processEnv = processEnv;
1642
+ }
1643
+ vaultId;
1644
+ definition;
1645
+ processEnv;
1646
+ authenticated = false;
1647
+ async authenticate(_authConfig) {
1648
+ void _authConfig;
1649
+ this.authenticated = true;
1650
+ }
1651
+ isAuthenticated() {
1652
+ return this.authenticated;
1653
+ }
1654
+ resolveEnvVar(ref) {
1655
+ if (this.processEnv[ref] !== void 0) {
1656
+ return ref;
1657
+ }
1658
+ return Object.entries(this.definition.mapping ?? {}).find(([, logicalRef]) => logicalRef === ref)?.[0];
1659
+ }
1660
+ async batchGet(refs) {
1661
+ this.authenticated = true;
1662
+ const resolved = /* @__PURE__ */ new Map();
1663
+ for (const ref of Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right))) {
1664
+ const envVar = this.resolveEnvVar(ref);
1665
+ const value = envVar ? this.processEnv[envVar] : void 0;
1666
+ if (value !== void 0) {
1667
+ resolved.set(ref, value);
1668
+ }
1669
+ }
1670
+ return resolved;
1671
+ }
1672
+ async get(ref) {
1673
+ const envVar = this.resolveEnvVar(ref);
1674
+ this.authenticated = true;
1675
+ return envVar ? this.processEnv[envVar] : void 0;
1676
+ }
1677
+ async set(ref, value) {
1678
+ void ref;
1679
+ void value;
1680
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be written by CNOS.`);
1681
+ }
1682
+ async delete(ref) {
1683
+ void ref;
1684
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be mutated by CNOS.`);
1685
+ }
1686
+ async list() {
1687
+ return Object.values(this.definition.mapping ?? {}).sort((left, right) => left.localeCompare(right));
1688
+ }
1689
+ };
1690
+
1691
+ // ../core/src/secrets/providers/local.ts
1692
+ var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
1693
+ constructor(vaultId, definition, processEnv = process.env, storeRoot = resolveSecretStoreRoot(processEnv)) {
1694
+ this.vaultId = vaultId;
1695
+ this.processEnv = processEnv;
1696
+ this.storeRoot = storeRoot;
1697
+ this.definition = definition;
1698
+ }
1699
+ vaultId;
1700
+ processEnv;
1701
+ storeRoot;
1702
+ authConfig;
1703
+ definition;
1704
+ static fromVaults(vaults, vaultId, processEnv) {
1705
+ const definition = resolveVaultDefinition(vaults, vaultId);
1706
+ return new _LocalSecretVaultProvider(vaultId, definition, processEnv);
1707
+ }
1708
+ async authenticate(authConfig) {
1709
+ this.authConfig = authConfig;
1710
+ await this.list();
1711
+ }
1712
+ isAuthenticated() {
1713
+ return Boolean(this.authConfig);
1714
+ }
1715
+ async requireAuth() {
1716
+ if (this.authConfig) {
1717
+ return this.authConfig;
1718
+ }
1719
+ const resolved = await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1720
+ if (!resolved) {
1721
+ throw new CnosAuthenticationError(
1722
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1723
+ );
1724
+ }
1725
+ this.authConfig = resolved;
1726
+ return resolved;
1727
+ }
1728
+ async batchGet(refs) {
1729
+ const auth = await this.requireAuth();
1730
+ const entries = await Promise.all(
1731
+ Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right)).map(async (ref) => [ref, await readLocalSecret(this.storeRoot, ref, auth, this.vaultId)])
1732
+ );
1733
+ return new Map(entries);
1734
+ }
1735
+ async get(ref) {
1736
+ const auth = await this.requireAuth();
1737
+ try {
1738
+ return await readLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1739
+ } catch {
1135
1740
  return void 0;
1136
1741
  }
1137
- return `secret.${fromScreamingSnake(stripped)}`;
1138
1742
  }
1139
- if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
1743
+ async set(ref, value) {
1744
+ const auth = await this.requireAuth();
1745
+ await writeLocalSecret(this.storeRoot, ref, value, auth, this.vaultId);
1746
+ await appendAuditEvent(
1747
+ {
1748
+ action: "write",
1749
+ vault: this.vaultId,
1750
+ ref,
1751
+ caller: "cli"
1752
+ },
1753
+ this.processEnv
1754
+ );
1755
+ }
1756
+ async delete(ref) {
1757
+ const auth = await this.requireAuth();
1758
+ await deleteLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1759
+ await appendAuditEvent(
1760
+ {
1761
+ action: "delete",
1762
+ vault: this.vaultId,
1763
+ ref,
1764
+ caller: "cli"
1765
+ },
1766
+ this.processEnv
1767
+ );
1768
+ }
1769
+ async list() {
1770
+ const auth = this.authConfig ?? await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1771
+ if (!auth) {
1772
+ throw new CnosAuthenticationError(
1773
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1774
+ );
1775
+ }
1776
+ this.authConfig = auth;
1777
+ return listLocalSecrets(this.storeRoot, auth, this.vaultId);
1778
+ }
1779
+ };
1780
+
1781
+ // ../core/src/secrets/providers/registry.ts
1782
+ function createSecretVaultProvider(vaultId, definition, processEnv) {
1783
+ if (definition.provider === "local") {
1784
+ return new LocalSecretVaultProvider(vaultId, definition, processEnv);
1785
+ }
1786
+ if (definition.provider === "github-secrets") {
1787
+ return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
1788
+ }
1789
+ throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
1790
+ }
1791
+
1792
+ // ../core/src/secrets/prompt.ts
1793
+ var import_node_readline = __toESM(require("readline"), 1);
1794
+ var import_node_stream = require("stream");
1795
+ async function promptHidden(message) {
1796
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1140
1797
  return void 0;
1141
1798
  }
1142
- return `value.${fromScreamingSnake(envVar)}`;
1799
+ const mutableStdout = new WritableMask();
1800
+ const rl = import_node_readline.default.createInterface({
1801
+ input: process.stdin,
1802
+ output: mutableStdout,
1803
+ terminal: true
1804
+ });
1805
+ try {
1806
+ mutableStdout.muted = true;
1807
+ const value = await new Promise((resolve) => {
1808
+ rl.question(message, resolve);
1809
+ });
1810
+ process.stdout.write("\n");
1811
+ return value;
1812
+ } finally {
1813
+ rl.close();
1814
+ }
1815
+ }
1816
+ var WritableMask = class extends import_node_stream.Writable {
1817
+ muted = false;
1818
+ _write(chunk, _encoding, callback) {
1819
+ if (!this.muted) {
1820
+ process.stdout.write(chunk);
1821
+ }
1822
+ callback();
1823
+ }
1824
+ };
1825
+
1826
+ // ../core/src/secrets/resolveAuth.ts
1827
+ function toAuthError(vaultId, sources) {
1828
+ return new CnosAuthenticationError(
1829
+ `Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
1830
+ );
1831
+ }
1832
+ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
1833
+ const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
1834
+ if (sessionKey) {
1835
+ return {
1836
+ derivedKey: sessionKey,
1837
+ method: "keychain",
1838
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1839
+ };
1840
+ }
1841
+ if (definition.provider === "github-secrets") {
1842
+ return {
1843
+ method: definition.auth?.method ?? "environment",
1844
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1845
+ };
1846
+ }
1847
+ const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
1848
+ for (const source of sources) {
1849
+ if (source.startsWith("env:")) {
1850
+ const value = processEnv[source.slice(4)];
1851
+ if (value) {
1852
+ return {
1853
+ passphrase: value,
1854
+ method: "passphrase",
1855
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1856
+ };
1857
+ }
1858
+ }
1859
+ if (source.startsWith("keychain:")) {
1860
+ const value = await readKeychain(source.slice("keychain:".length));
1861
+ if (value) {
1862
+ return {
1863
+ derivedKey: Buffer.from(value, "hex"),
1864
+ method: "keychain",
1865
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1866
+ };
1867
+ }
1868
+ }
1869
+ if (source === "prompt") {
1870
+ const value = await promptHidden(`Enter passphrase for vault "${vaultId}": `);
1871
+ if (value) {
1872
+ return {
1873
+ passphrase: value,
1874
+ method: "passphrase",
1875
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1876
+ };
1877
+ }
1878
+ }
1879
+ }
1880
+ const fallback = resolveSecretPassphrase(vaultId, processEnv);
1881
+ if (fallback) {
1882
+ return {
1883
+ passphrase: fallback,
1884
+ method: "passphrase",
1885
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1886
+ };
1887
+ }
1888
+ throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
1889
+ }
1890
+
1891
+ // ../core/src/secrets/batchResolve.ts
1892
+ function collectSecretDescriptors(graph) {
1893
+ return Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => ({
1894
+ logicalKey: entry.key,
1895
+ ref: entry.value
1896
+ }));
1897
+ }
1898
+ async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
1899
+ const cache = new SecretCache();
1900
+ const descriptors = collectSecretDescriptors(graph);
1901
+ const grouped = descriptors.reduce((accumulator, descriptor) => {
1902
+ const vaultId = descriptor.ref.vault ?? "default";
1903
+ const bucket = accumulator.get(vaultId) ?? [];
1904
+ bucket.push(descriptor);
1905
+ accumulator.set(vaultId, bucket);
1906
+ return accumulator;
1907
+ }, /* @__PURE__ */ new Map());
1908
+ for (const [vaultId, refs] of grouped) {
1909
+ const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
1910
+ const provider = createSecretVaultProvider(vaultId, definition, processEnv);
1911
+ const auth = await resolveVaultAuth(vaultId, definition, processEnv);
1912
+ await provider.authenticate(auth);
1913
+ const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
1914
+ cache.load(vaultId, resolved);
1915
+ await appendAuditEvent(
1916
+ {
1917
+ action: "batch_read",
1918
+ vault: vaultId,
1919
+ refs: Array.from(resolved.keys()).sort((left, right) => left.localeCompare(right)),
1920
+ caller: "runtime",
1921
+ workspace: graph.workspace.workspaceId,
1922
+ profile: graph.profile
1923
+ },
1924
+ processEnv
1925
+ );
1926
+ }
1927
+ return cache;
1928
+ }
1929
+ function resolveSecretEntryValue(key, value, cache) {
1930
+ if (!key.startsWith("secret.") || !isSecretReference(value)) {
1931
+ return value;
1932
+ }
1933
+ const vaultId = value.vault ?? "default";
1934
+ return cache.get(vaultId, value.ref) ?? value;
1935
+ }
1936
+
1937
+ // ../core/src/runtime/projection.ts
1938
+ function setNestedValue(target, pathSegments, value) {
1939
+ const [head, ...tail] = pathSegments;
1940
+ if (!head) {
1941
+ return;
1942
+ }
1943
+ if (tail.length === 0) {
1944
+ target[head] = value;
1945
+ return;
1946
+ }
1947
+ const current = target[head];
1948
+ const nextTarget = current && typeof current === "object" && !Array.isArray(current) ? current : {};
1949
+ target[head] = nextTarget;
1950
+ setNestedValue(nextTarget, tail, value);
1951
+ }
1952
+ function toNamespaceObject(graph, namespace) {
1953
+ const output = {};
1954
+ const resolvedEntries = Array.from(graph.entries.values()).sort(
1955
+ (left, right) => left.key.localeCompare(right.key)
1956
+ );
1957
+ for (const entry of resolvedEntries) {
1958
+ if (namespace && entry.namespace !== namespace) {
1959
+ continue;
1960
+ }
1961
+ const valuePath = namespace ? stripNamespace(entry.key) : entry.key;
1962
+ setNestedValue(output, valuePath.split("."), entry.value);
1963
+ }
1964
+ return output;
1965
+ }
1966
+
1967
+ // ../core/src/runtime/read.ts
1968
+ function readValue(graph, key) {
1969
+ return graph.entries.get(key)?.value;
1970
+ }
1971
+
1972
+ // ../core/src/runtime/readOr.ts
1973
+ function readOrValue(graph, key, fallback) {
1974
+ const value = readValue(graph, key);
1975
+ return value === void 0 ? fallback : value;
1976
+ }
1977
+
1978
+ // ../core/src/runtime/require.ts
1979
+ function requireValue(graph, key) {
1980
+ const value = readValue(graph, key);
1981
+ if (value === void 0) {
1982
+ throw new CnosKeyNotFoundError(key);
1983
+ }
1984
+ return value;
1985
+ }
1986
+
1987
+ // ../core/src/runtime/toEnv.ts
1988
+ function normalizeEnvValue(value) {
1989
+ if (value === void 0 || value === null) {
1990
+ return "";
1991
+ }
1992
+ if (typeof value === "string") {
1993
+ return value;
1994
+ }
1995
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1996
+ return String(value);
1997
+ }
1998
+ return JSON.stringify(value);
1999
+ }
2000
+ function toEnv(graph, manifest, options = {}) {
2001
+ const includeSecrets = options.includeSecrets ?? true;
2002
+ const output = {};
2003
+ const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
2004
+ ([left], [right]) => left.localeCompare(right)
2005
+ );
2006
+ for (const [envVar, logicalKey] of mappedEntries) {
2007
+ const entry = graph.entries.get(logicalKey);
2008
+ if (!entry) {
2009
+ continue;
2010
+ }
2011
+ const namespaceDefinition = getNamespaceDefinition(manifest, entry.namespace);
2012
+ if (namespaceDefinition.kind !== "data" || !namespaceDefinition.shareable || namespaceDefinition.sensitive) {
2013
+ continue;
2014
+ }
2015
+ if (entry.namespace === "secret" && !includeSecrets) {
2016
+ continue;
2017
+ }
2018
+ if (isSecretReference(entry.value)) {
2019
+ continue;
2020
+ }
2021
+ output[envVar] = normalizeEnvValue(entry.value);
2022
+ }
2023
+ return output;
1143
2024
  }
1144
2025
 
1145
2026
  // ../core/src/runtime/toPublicEnv.ts
1146
- function fallbackValueEnvVar(key) {
1147
- return key.replace(/^value\./, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
2027
+ function fallbackPublicEnvVar(valuePath) {
2028
+ return valuePath.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1148
2029
  }
1149
2030
  function normalizeEnvValue2(value) {
1150
2031
  if (value === void 0 || value === null) {
@@ -1171,22 +2052,12 @@ function resolvePublicPrefix(manifest, options) {
1171
2052
  }
1172
2053
  return configuredPrefix;
1173
2054
  }
1174
- function ensurePublicPromotionKey(key) {
1175
- if (!key.startsWith("value.")) {
1176
- throw new CnosManifestError(`public.promote may only contain value.* keys: ${key}`);
1177
- }
1178
- }
1179
2055
  function toPublicEnv(graph, manifest, options = {}) {
1180
2056
  const prefix = resolvePublicPrefix(manifest, options);
1181
2057
  const output = {};
1182
- const promotions = [...manifest.public.promote].sort((left, right) => left.localeCompare(right));
1183
- for (const key of promotions) {
1184
- ensurePublicPromotionKey(key);
1185
- const resolved = graph.entries.get(key);
1186
- if (!resolved) {
1187
- continue;
1188
- }
1189
- const baseEnvVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? fallbackValueEnvVar(key);
2058
+ const promotions = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").sort((left, right) => left.key.localeCompare(right.key));
2059
+ for (const resolved of promotions) {
2060
+ const baseEnvVar = fallbackPublicEnvVar(stripNamespace(resolved.key));
1190
2061
  const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
1191
2062
  output[envVar] = normalizeEnvValue2(resolved.value);
1192
2063
  }
@@ -1194,28 +2065,42 @@ function toPublicEnv(graph, manifest, options = {}) {
1194
2065
  }
1195
2066
 
1196
2067
  // ../core/src/orchestrator/runtime.ts
1197
- function createRuntime(manifest, graph, plugins = []) {
2068
+ function createRuntime(manifest, graph, plugins = [], secretCache) {
2069
+ function readLogicalKey(key) {
2070
+ const entry = graph.entries.get(key);
2071
+ if (!entry) {
2072
+ return void 0;
2073
+ }
2074
+ if (!secretCache) {
2075
+ return entry.value;
2076
+ }
2077
+ return resolveSecretEntryValue(key, entry.value, secretCache);
2078
+ }
1198
2079
  return {
1199
2080
  manifest,
1200
2081
  plugins,
1201
2082
  graph,
1202
2083
  read(key) {
1203
- return readValue(graph, key);
2084
+ return readLogicalKey(key);
1204
2085
  },
1205
2086
  require(key) {
1206
- return requireValue(graph, key);
2087
+ const value = readLogicalKey(key);
2088
+ if (value === void 0) {
2089
+ return requireValue(graph, key);
2090
+ }
2091
+ return value;
1207
2092
  },
1208
2093
  readOr(key, fallback) {
1209
2094
  return readOrValue(graph, key, fallback);
1210
2095
  },
1211
- value(path10) {
1212
- return readValue(graph, toLogicalKey("value", path10));
2096
+ value(path12) {
2097
+ return readLogicalKey(toLogicalKey("value", path12));
1213
2098
  },
1214
- secret(path10) {
1215
- return readValue(graph, toLogicalKey("secret", path10));
2099
+ secret(path12) {
2100
+ return readLogicalKey(toLogicalKey("secret", path12));
1216
2101
  },
1217
- meta(path10) {
1218
- return readValue(graph, toLogicalKey("meta", path10));
2102
+ meta(path12) {
2103
+ return readLogicalKey(toLogicalKey("meta", path12));
1219
2104
  },
1220
2105
  inspect(key) {
1221
2106
  return inspectValue(graph, key);
@@ -1330,6 +2215,9 @@ function appendMetaEntries(graph, cnosVersion) {
1330
2215
  }
1331
2216
  async function createCnos(options = {}) {
1332
2217
  const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
2218
+ for (const key of loadedManifest.manifest.public.promote) {
2219
+ ensureProjectionAllowed(loadedManifest.manifest, key, "public");
2220
+ }
1333
2221
  const workspaceFile = await loadWorkspaceFile(loadedManifest.repoRoot);
1334
2222
  const workspace = await resolveWorkspaceContext(loadedManifest.manifest, {
1335
2223
  manifestRoot: loadedManifest.manifestRoot,
@@ -1369,66 +2257,59 @@ async function createCnos(options = {}) {
1369
2257
  workspace
1370
2258
  });
1371
2259
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
2260
+ const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
2261
+ const secretCache = options.secretResolution === "lazy" ? void 0 : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
1372
2262
  return createRuntime(
1373
2263
  loadedManifest.manifest,
1374
2264
  appendMetaEntries({
1375
- ...schemaApplied.graph,
2265
+ ...promotedGraph,
1376
2266
  profileSource: activeProfile.source
1377
2267
  }, options.cnosVersion),
1378
- plugins
2268
+ plugins,
2269
+ secretCache
1379
2270
  );
1380
2271
  }
1381
2272
 
1382
2273
  // ../core/src/runtime/dump.ts
1383
- var import_promises7 = require("fs/promises");
1384
- var import_node_path7 = __toESM(require("path"), 1);
1385
- function buildDumpFiles(graph, options = {}) {
1386
- const basePath = options.flatten ? "" : import_node_path7.default.posix.join("workspaces", graph.workspace.workspaceId);
1387
- const values = toNamespaceObject(graph, "value");
1388
- const secrets = toNamespaceObject(graph, "secret");
1389
- const files = [];
1390
- if (Object.keys(values).length > 0) {
1391
- files.push({
1392
- path: import_node_path7.default.posix.join(basePath, "values", graph.profile, "app.yml"),
1393
- namespace: "value",
1394
- content: stringifyYaml(values)
1395
- });
1396
- }
1397
- if (Object.keys(secrets).length > 0) {
1398
- files.push({
1399
- path: import_node_path7.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1400
- namespace: "secret",
1401
- content: stringifyYaml(secrets)
1402
- });
1403
- }
1404
- return files.sort((left, right) => left.path.localeCompare(right.path));
1405
- }
1406
- function planDump(graph, options = {}) {
2274
+ var import_promises9 = require("fs/promises");
2275
+ var import_node_path9 = __toESM(require("path"), 1);
2276
+
2277
+ // ../core/src/utils/envNaming.ts
2278
+ function normalizeMappingConfig(config = {}) {
1407
2279
  return {
1408
- workspaceId: graph.workspace.workspaceId,
1409
- profile: graph.profile,
1410
- flatten: options.flatten ?? false,
1411
- files: buildDumpFiles(graph, options)
2280
+ convention: config.convention,
2281
+ explicit: config.explicit ?? {}
1412
2282
  };
1413
2283
  }
1414
- async function writeDump(graph, options) {
1415
- const root = import_node_path7.default.resolve(options.to);
1416
- const plan = planDump(graph, options);
1417
- for (const file of plan.files) {
1418
- const destination = import_node_path7.default.join(root, file.path);
1419
- await (0, import_promises7.mkdir)(import_node_path7.default.dirname(destination), { recursive: true });
1420
- await (0, import_promises7.writeFile)(destination, file.content, "utf8");
2284
+ function fromScreamingSnake(path12) {
2285
+ return path12.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2286
+ }
2287
+ function envVarToLogicalKey(envVar, config = {}) {
2288
+ const normalized = normalizeMappingConfig(config);
2289
+ const explicitMatch = normalized.explicit[envVar];
2290
+ if (explicitMatch) {
2291
+ return explicitMatch;
1421
2292
  }
1422
- return {
1423
- ...plan,
1424
- root
1425
- };
2293
+ if (normalized.convention !== "SCREAMING_SNAKE") {
2294
+ return void 0;
2295
+ }
2296
+ if (envVar.startsWith("SECRET_")) {
2297
+ const stripped = envVar.slice("SECRET_".length);
2298
+ if (!stripped) {
2299
+ return void 0;
2300
+ }
2301
+ return `secret.${fromScreamingSnake(stripped)}`;
2302
+ }
2303
+ if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
2304
+ return void 0;
2305
+ }
2306
+ return `value.${fromScreamingSnake(envVar)}`;
1426
2307
  }
1427
2308
 
1428
2309
  // package.json
1429
2310
  var package_default = {
1430
2311
  name: "@kitsy/cnos",
1431
- version: "1.1.2",
2312
+ version: "1.3.0",
1432
2313
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1433
2314
  type: "module",
1434
2315
  main: "./dist/index.cjs",
@@ -1440,11 +2321,36 @@ var package_default = {
1440
2321
  import: "./dist/index.js",
1441
2322
  require: "./dist/index.cjs"
1442
2323
  },
2324
+ "./configure": {
2325
+ types: "./dist/configure/index.d.ts",
2326
+ import: "./dist/configure/index.js",
2327
+ require: "./dist/configure/index.cjs"
2328
+ },
2329
+ "./create": {
2330
+ types: "./dist/configure/index.d.ts",
2331
+ import: "./dist/configure/index.js",
2332
+ require: "./dist/configure/index.cjs"
2333
+ },
1443
2334
  "./internal": {
1444
2335
  types: "./dist/internal.d.ts",
1445
2336
  import: "./dist/internal.js",
1446
2337
  require: "./dist/internal.cjs"
1447
2338
  },
2339
+ "./runtime": {
2340
+ types: "./dist/runtime/index.d.ts",
2341
+ import: "./dist/runtime/index.js",
2342
+ require: "./dist/runtime/index.cjs"
2343
+ },
2344
+ "./browser": {
2345
+ types: "./dist/browser/index.d.ts",
2346
+ import: "./dist/browser/index.js",
2347
+ require: "./dist/browser/index.cjs"
2348
+ },
2349
+ "./build": {
2350
+ types: "./dist/build/index.d.ts",
2351
+ import: "./dist/build/index.js",
2352
+ require: "./dist/build/index.cjs"
2353
+ },
1448
2354
  "./plugins/filesystem": {
1449
2355
  types: "./dist/plugin/filesystem.d.ts",
1450
2356
  import: "./dist/plugin/filesystem.js",
@@ -1602,8 +2508,8 @@ function createCliArgsPlugin() {
1602
2508
  }
1603
2509
 
1604
2510
  // ../../plugins/dotenv/src/index.ts
1605
- var import_promises8 = require("fs/promises");
1606
- var import_node_path8 = __toESM(require("path"), 1);
2511
+ var import_promises10 = require("fs/promises");
2512
+ var import_node_path10 = __toESM(require("path"), 1);
1607
2513
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
1608
2514
  function parseDoubleQuoted(value) {
1609
2515
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -1660,7 +2566,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
1660
2566
  }
1661
2567
  async function readIfPresent(filePath) {
1662
2568
  try {
1663
- return await (0, import_promises8.readFile)(filePath, "utf8");
2569
+ return await (0, import_promises10.readFile)(filePath, "utf8");
1664
2570
  } catch {
1665
2571
  return void 0;
1666
2572
  }
@@ -1679,7 +2585,7 @@ function createDotenvPlugin() {
1679
2585
  workspace: workspaceRoot.workspaceId
1680
2586
  });
1681
2587
  for (const fileName of fileNames) {
1682
- const absolutePath = import_node_path8.default.join(envRoot, fileName);
2588
+ const absolutePath = import_node_path10.default.join(envRoot, fileName);
1683
2589
  const document = await readIfPresent(absolutePath);
1684
2590
  if (!document) {
1685
2591
  continue;
@@ -1688,7 +2594,7 @@ function createDotenvPlugin() {
1688
2594
  ...dotenvEntriesFromObject(
1689
2595
  parseDotenv(document),
1690
2596
  config.envMapping,
1691
- toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(context.manifestRoot), absolutePath)),
2597
+ toPortablePath(import_node_path10.default.relative(import_node_path10.default.dirname(context.manifestRoot), absolutePath)),
1692
2598
  workspaceRoot.workspaceId
1693
2599
  )
1694
2600
  );
@@ -1726,32 +2632,32 @@ function createPublicEnvExportPlugin() {
1726
2632
  }
1727
2633
 
1728
2634
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
1729
- var import_promises10 = require("fs/promises");
2635
+ var import_promises12 = require("fs/promises");
1730
2636
 
1731
2637
  // ../../plugins/filesystem/src/helpers.ts
1732
- var import_promises9 = require("fs/promises");
1733
- var import_node_path9 = __toESM(require("path"), 1);
2638
+ var import_promises11 = require("fs/promises");
2639
+ var import_node_path11 = __toESM(require("path"), 1);
1734
2640
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
1735
2641
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
1736
2642
  async function existsDirectory(targetPath) {
1737
2643
  try {
1738
- const stat = await (0, import_promises9.readdir)(targetPath);
1739
- void stat;
2644
+ const stat2 = await (0, import_promises11.readdir)(targetPath);
2645
+ void stat2;
1740
2646
  return true;
1741
2647
  } catch {
1742
2648
  return false;
1743
2649
  }
1744
2650
  }
1745
2651
  async function collectYamlFiles(root) {
1746
- const entries = await (0, import_promises9.readdir)(root, { withFileTypes: true });
2652
+ const entries = await (0, import_promises11.readdir)(root, { withFileTypes: true });
1747
2653
  const results = [];
1748
2654
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
1749
- const absolutePath = import_node_path9.default.join(root, entry.name);
2655
+ const absolutePath = import_node_path11.default.join(root, entry.name);
1750
2656
  if (entry.isDirectory()) {
1751
2657
  results.push(...await collectYamlFiles(absolutePath));
1752
2658
  continue;
1753
2659
  }
1754
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path9.default.extname(entry.name).toLowerCase())) {
2660
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path11.default.extname(entry.name).toLowerCase())) {
1755
2661
  results.push(absolutePath);
1756
2662
  }
1757
2663
  }
@@ -1759,16 +2665,16 @@ async function collectYamlFiles(root) {
1759
2665
  }
1760
2666
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
1761
2667
  const files = [];
1762
- const repoRoot = import_node_path9.default.dirname(manifestRoot);
2668
+ const repoRoot = import_node_path11.default.dirname(manifestRoot);
1763
2669
  for (const workspaceRoot of workspaceRoots) {
1764
- const resolvedRoot = import_node_path9.default.resolve(workspaceRoot.path, sourceRoot);
2670
+ const resolvedRoot = import_node_path11.default.resolve(workspaceRoot.path, sourceRoot);
1765
2671
  for (const layer of activeLayers) {
1766
- const layerRoot = import_node_path9.default.join(resolvedRoot, layer);
2672
+ const layerRoot = import_node_path11.default.join(resolvedRoot, layer);
1767
2673
  if (!await existsDirectory(layerRoot)) {
1768
2674
  continue;
1769
2675
  }
1770
2676
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
1771
- const relativePath = import_node_path9.default.relative(repoRoot, absolutePath);
2677
+ const relativePath = import_node_path11.default.relative(repoRoot, absolutePath);
1772
2678
  files.push({
1773
2679
  absolutePath,
1774
2680
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -1818,31 +2724,6 @@ function yamlObjectToEntries(document, filePath, namespace, sourceId, workspaceI
1818
2724
  }
1819
2725
  }));
1820
2726
  }
1821
- async function resolveSecretValue(value, processEnv) {
1822
- if (!isSecretReference(value)) {
1823
- return value;
1824
- }
1825
- if (value.provider === "local") {
1826
- const passphrase = resolveSecretPassphrase(value.vault, processEnv);
1827
- if (!passphrase) {
1828
- return value;
1829
- }
1830
- return readLocalSecret(
1831
- resolveSecretStoreRoot(processEnv),
1832
- value.ref,
1833
- passphrase,
1834
- value.vault
1835
- );
1836
- }
1837
- if (value.provider === "env") {
1838
- const resolved = processEnv?.[value.ref];
1839
- if (resolved === void 0) {
1840
- return value;
1841
- }
1842
- return resolved;
1843
- }
1844
- return value;
1845
- }
1846
2727
  function toSecretReferenceMetadata(value) {
1847
2728
  if (!isSecretReference(value)) {
1848
2729
  return void 0;
@@ -1870,14 +2751,12 @@ function createFilesystemSecretsPlugin() {
1870
2751
  );
1871
2752
  const entries = [];
1872
2753
  for (const file of files) {
1873
- const document = await (0, import_promises10.readFile)(file.absolutePath, "utf8");
2754
+ const document = await (0, import_promises12.readFile)(file.absolutePath, "utf8");
1874
2755
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
1875
2756
  for (const entry of fileEntries) {
1876
2757
  const metadata = toSecretReferenceMetadata(entry.value);
1877
- const resolvedValue = await resolveSecretValue(entry.value, context.processEnv);
1878
2758
  entries.push({
1879
2759
  ...entry,
1880
- value: resolvedValue,
1881
2760
  ...metadata ? { metadata } : {}
1882
2761
  });
1883
2762
  }
@@ -1888,7 +2767,7 @@ function createFilesystemSecretsPlugin() {
1888
2767
  }
1889
2768
 
1890
2769
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
1891
- var import_promises11 = require("fs/promises");
2770
+ var import_promises13 = require("fs/promises");
1892
2771
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
1893
2772
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
1894
2773
  }
@@ -1906,7 +2785,7 @@ function createFilesystemValuesPlugin() {
1906
2785
  );
1907
2786
  const entries = [];
1908
2787
  for (const file of files) {
1909
- const document = await (0, import_promises11.readFile)(file.absolutePath, "utf8");
2788
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
1910
2789
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
1911
2790
  }
1912
2791
  return entries;
@@ -1970,20 +2849,273 @@ function defaultPlugins() {
1970
2849
  ];
1971
2850
  }
1972
2851
 
2852
+ // src/runtime/state.ts
2853
+ var singletonRuntime;
2854
+ var singletonReady;
2855
+ var bootstrappedSecretHydrationRequired = false;
2856
+ function getSingletonRuntime() {
2857
+ return singletonRuntime;
2858
+ }
2859
+ function setSingletonRuntime(runtime) {
2860
+ singletonRuntime = runtime;
2861
+ singletonReady = Promise.resolve(runtime);
2862
+ bootstrappedSecretHydrationRequired = false;
2863
+ return runtime;
2864
+ }
2865
+ function getSingletonReady() {
2866
+ return singletonReady;
2867
+ }
2868
+ function setSingletonReady(promise) {
2869
+ singletonReady = promise;
2870
+ return promise;
2871
+ }
2872
+ function getBootstrappedSecretHydrationRequired() {
2873
+ return bootstrappedSecretHydrationRequired;
2874
+ }
2875
+ function setBootstrappedSecretHydrationRequired(value) {
2876
+ bootstrappedSecretHydrationRequired = value;
2877
+ }
2878
+
1973
2879
  // src/createCnos.ts
1974
2880
  async function createCnos2(options = {}) {
1975
- return createCnos({
2881
+ const runtime = await createCnos({
1976
2882
  ...options,
2883
+ processEnv: options.processEnv ?? process.env,
1977
2884
  cnosVersion: package_default.version,
1978
2885
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
1979
2886
  });
2887
+ setSingletonRuntime(runtime);
2888
+ return runtime;
1980
2889
  }
2890
+
2891
+ // src/runtime/bootstrap.ts
2892
+ var import_node_crypto2 = require("crypto");
2893
+ var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
2894
+ var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
2895
+ var CNOS_SESSION_KEY_ENV_VAR = "__CNOS_SESSION_KEY__";
2896
+ function deserializeRuntimeGraph(source) {
2897
+ const payload = JSON.parse(source);
2898
+ 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)) {
2899
+ throw new Error("Invalid CNOS runtime bootstrap payload");
2900
+ }
2901
+ return {
2902
+ entries: new Map(
2903
+ payload.entries.map((entry) => [
2904
+ entry.key,
2905
+ {
2906
+ key: entry.key,
2907
+ value: entry.value,
2908
+ namespace: entry.namespace,
2909
+ winner: entry.winner,
2910
+ overridden: entry.overridden ?? []
2911
+ }
2912
+ ])
2913
+ ),
2914
+ profile: payload.profile,
2915
+ resolvedAt: payload.resolvedAt,
2916
+ profileSource: payload.profileSource,
2917
+ workspace: payload.workspace
2918
+ };
2919
+ }
2920
+ function decryptSecretPayload(serialized, sessionKey) {
2921
+ const payload = JSON.parse(serialized);
2922
+ if (!payload || typeof payload.iv !== "string" || typeof payload.tag !== "string" || typeof payload.ciphertext !== "string") {
2923
+ throw new Error("Invalid CNOS secret payload");
2924
+ }
2925
+ const key = Buffer.from(sessionKey, "hex");
2926
+ const iv = Buffer.from(payload.iv, "base64");
2927
+ const tag = Buffer.from(payload.tag, "base64");
2928
+ const ciphertext = Buffer.from(payload.ciphertext, "base64");
2929
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
2930
+ decipher.setAuthTag(tag);
2931
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
2932
+ return JSON.parse(plaintext);
2933
+ }
2934
+ function readRuntimeGraphFromEnv(processEnv = process.env) {
2935
+ const serialized = processEnv[CNOS_GRAPH_ENV_VAR];
2936
+ if (!serialized) {
2937
+ return void 0;
2938
+ }
2939
+ const graph = deserializeRuntimeGraph(serialized);
2940
+ const secretPayload = processEnv[CNOS_SECRET_PAYLOAD_ENV_VAR];
2941
+ const sessionKey = processEnv[CNOS_SESSION_KEY_ENV_VAR];
2942
+ if (secretPayload && sessionKey) {
2943
+ const decrypted = decryptSecretPayload(secretPayload, sessionKey);
2944
+ for (const [key, value] of Object.entries(decrypted)) {
2945
+ const entry = graph.entries.get(key);
2946
+ if (entry) {
2947
+ entry.value = value;
2948
+ }
2949
+ }
2950
+ }
2951
+ return graph;
2952
+ }
2953
+ function graphRequiresSecretHydration(graph) {
2954
+ return Array.from(graph.entries.values()).some((entry) => entry.namespace === "secret" && isSecretReference(entry.value));
2955
+ }
2956
+
2957
+ // src/runtime/index.ts
2958
+ var NOT_READY_MESSAGE = "CNOS not initialized. Call await cnos.ready() or use cnos run.";
2959
+ function getRuntimeOrThrow() {
2960
+ const runtime = getSingletonRuntime();
2961
+ if (!runtime) {
2962
+ throw new Error(NOT_READY_MESSAGE);
2963
+ }
2964
+ return runtime;
2965
+ }
2966
+ function attachBootstrappedGraph(graph) {
2967
+ if (getSingletonRuntime()) {
2968
+ return;
2969
+ }
2970
+ const bootstrappedManifest = {
2971
+ version: 1,
2972
+ project: {
2973
+ name: "bootstrapped"
2974
+ },
2975
+ workspaces: {
2976
+ global: {
2977
+ enabled: Boolean(graph.workspace.globalRoot),
2978
+ ...graph.workspace.globalRoot ? {
2979
+ root: graph.workspace.globalRoot
2980
+ } : {},
2981
+ allowWrite: false
2982
+ },
2983
+ items: {},
2984
+ ...graph.workspace.workspaceSource === "implicit" ? {} : {
2985
+ default: graph.workspace.workspaceId
2986
+ }
2987
+ },
2988
+ profiles: {
2989
+ default: graph.profile,
2990
+ resolveFrom: ["default"]
2991
+ },
2992
+ plugins: {
2993
+ loaders: [],
2994
+ resolver: "profile-aware",
2995
+ validators: [],
2996
+ exporters: [],
2997
+ inspectors: []
2998
+ },
2999
+ sources: {},
3000
+ resolution: {
3001
+ precedence: [],
3002
+ arrayPolicy: "replace"
3003
+ },
3004
+ envMapping: {
3005
+ explicit: {}
3006
+ },
3007
+ public: {
3008
+ promote: [],
3009
+ frameworks: {}
3010
+ },
3011
+ namespaces: {},
3012
+ vaults: {},
3013
+ writePolicy: {
3014
+ define: {
3015
+ defaultProfile: graph.profile,
3016
+ targets: {
3017
+ value: "./values/app.yml",
3018
+ secret: "./secrets/app.yml"
3019
+ }
3020
+ }
3021
+ },
3022
+ schema: {}
3023
+ };
3024
+ const runtime = {
3025
+ manifest: bootstrappedManifest,
3026
+ plugins: [],
3027
+ graph,
3028
+ read(key) {
3029
+ return readValue(graph, key);
3030
+ },
3031
+ require(key) {
3032
+ return requireValue(graph, key);
3033
+ },
3034
+ readOr(key, fallback) {
3035
+ return readOrValue(graph, key, fallback);
3036
+ },
3037
+ value(path12) {
3038
+ return readValue(graph, toLogicalKey("value", path12));
3039
+ },
3040
+ secret(path12) {
3041
+ return readValue(graph, toLogicalKey("secret", path12));
3042
+ },
3043
+ meta(path12) {
3044
+ return readValue(graph, toLogicalKey("meta", path12));
3045
+ },
3046
+ inspect(key) {
3047
+ return inspectValue(graph, key);
3048
+ },
3049
+ toObject() {
3050
+ return toNamespaceObject(graph);
3051
+ },
3052
+ toNamespace(namespace) {
3053
+ return toNamespaceObject(graph, namespace);
3054
+ },
3055
+ toEnv(options) {
3056
+ return toEnv(graph, bootstrappedManifest, options);
3057
+ },
3058
+ toPublicEnv(options) {
3059
+ return toPublicEnv(graph, bootstrappedManifest, options);
3060
+ }
3061
+ };
3062
+ setSingletonRuntime(runtime);
3063
+ setBootstrappedSecretHydrationRequired(graphRequiresSecretHydration(graph));
3064
+ }
3065
+ function bootstrapFromProcessEnv() {
3066
+ if (typeof process === "undefined") {
3067
+ return;
3068
+ }
3069
+ try {
3070
+ const graph = readRuntimeGraphFromEnv(process.env);
3071
+ if (graph) {
3072
+ attachBootstrappedGraph(graph);
3073
+ }
3074
+ } catch {
3075
+ }
3076
+ }
3077
+ bootstrapFromProcessEnv();
3078
+ var cnos = Object.assign(
3079
+ ((key) => readValue(getRuntimeOrThrow().graph, key)),
3080
+ {
3081
+ read(key) {
3082
+ return readValue(getRuntimeOrThrow().graph, key);
3083
+ },
3084
+ require(key) {
3085
+ return requireValue(getRuntimeOrThrow().graph, key);
3086
+ },
3087
+ readOr(key, fallback) {
3088
+ return readOrValue(getRuntimeOrThrow().graph, key, fallback);
3089
+ },
3090
+ value(path12) {
3091
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("value", path12));
3092
+ },
3093
+ secret(path12) {
3094
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("secret", path12));
3095
+ },
3096
+ meta(path12) {
3097
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("meta", path12));
3098
+ },
3099
+ async ready() {
3100
+ if (getSingletonRuntime() && !getBootstrappedSecretHydrationRequired()) {
3101
+ return;
3102
+ }
3103
+ const existing = getSingletonReady();
3104
+ if (existing && !getBootstrappedSecretHydrationRequired()) {
3105
+ await existing;
3106
+ return;
3107
+ }
3108
+ const readyPromise = createCnos2().then((runtime) => {
3109
+ setSingletonRuntime(runtime);
3110
+ return runtime;
3111
+ });
3112
+ setSingletonReady(readyPromise);
3113
+ await readyPromise;
3114
+ }
3115
+ }
3116
+ );
3117
+ var runtime_default = cnos;
1981
3118
  // Annotate the CommonJS export names for ESM import in node:
1982
3119
  0 && (module.exports = {
1983
- createCnos,
1984
- defaultPlugins,
1985
- planDump,
1986
- toEnv,
1987
- toPublicEnv,
1988
- writeDump
3120
+ cnos
1989
3121
  });