@kitsy/cnos 1.5.1 → 1.6.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 (62) hide show
  1. package/dist/build/index.cjs +325 -113
  2. package/dist/build/index.d.cts +3 -2
  3. package/dist/build/index.d.ts +3 -2
  4. package/dist/build/index.js +13 -8
  5. package/dist/{chunk-UWFE4JE2.js → chunk-BMAD24KC.js} +1 -1
  6. package/dist/{chunk-7EI3RFUE.js → chunk-JYWQFMW5.js} +1 -1
  7. package/dist/{chunk-F2ZAIZNH.js → chunk-MW4OVAT3.js} +1 -1
  8. package/dist/chunk-QU5CXL47.js +577 -0
  9. package/dist/{chunk-BS33AW4Y.js → chunk-S7H2UULC.js} +307 -103
  10. package/dist/{chunk-H53ZRQLX.js → chunk-UJBQS7CJ.js} +1 -1
  11. package/dist/{chunk-CMQK2AEF.js → chunk-UOKVLCFL.js} +10 -10
  12. package/dist/{chunk-EJAXWFNT.js → chunk-UR7CHHNN.js} +1 -1
  13. package/dist/{chunk-SZKQVA2M.js → chunk-VGZREX5D.js} +1 -1
  14. package/dist/{chunk-5F2OFKND.js → chunk-XSUP7JKH.js} +23 -1
  15. package/dist/configure/index.cjs +324 -118
  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/{envNaming-Dvm_LP2D.d.ts → envNaming-B7Mztkcf.d.ts} +1 -1
  20. package/dist/{envNaming-S4B-dHUx.d.cts → envNaming-gMVnPOfe.d.cts} +1 -1
  21. package/dist/index.cjs +698 -136
  22. package/dist/index.d.cts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +10 -10
  25. package/dist/internal.cjs +227 -102
  26. package/dist/internal.d.cts +16 -28
  27. package/dist/internal.d.ts +16 -28
  28. package/dist/internal.js +11 -3
  29. package/dist/plugin/basic-schema.cjs +22 -15
  30. package/dist/plugin/basic-schema.d.cts +1 -1
  31. package/dist/plugin/basic-schema.d.ts +1 -1
  32. package/dist/plugin/basic-schema.js +2 -2
  33. package/dist/plugin/cli-args.cjs +27 -18
  34. package/dist/plugin/cli-args.d.cts +1 -1
  35. package/dist/plugin/cli-args.d.ts +1 -1
  36. package/dist/plugin/cli-args.js +2 -2
  37. package/dist/plugin/dotenv.cjs +35 -26
  38. package/dist/plugin/dotenv.d.cts +2 -2
  39. package/dist/plugin/dotenv.d.ts +2 -2
  40. package/dist/plugin/dotenv.js +2 -2
  41. package/dist/plugin/env-export.cjs +28 -19
  42. package/dist/plugin/env-export.d.cts +2 -2
  43. package/dist/plugin/env-export.d.ts +2 -2
  44. package/dist/plugin/env-export.js +2 -2
  45. package/dist/plugin/filesystem.cjs +42 -33
  46. package/dist/plugin/filesystem.d.cts +1 -1
  47. package/dist/plugin/filesystem.d.ts +1 -1
  48. package/dist/plugin/filesystem.js +2 -2
  49. package/dist/plugin/process-env.cjs +24 -17
  50. package/dist/plugin/process-env.d.cts +2 -2
  51. package/dist/plugin/process-env.d.ts +2 -2
  52. package/dist/plugin/process-env.js +2 -2
  53. package/dist/{plugin-B4xwySxw.d.cts → plugin-CKrBlWGI.d.cts} +52 -3
  54. package/dist/{plugin-B4xwySxw.d.ts → plugin-CKrBlWGI.d.ts} +52 -3
  55. package/dist/runtime/index.cjs +696 -136
  56. package/dist/runtime/index.d.cts +7 -1
  57. package/dist/runtime/index.d.ts +7 -1
  58. package/dist/runtime/index.js +10 -10
  59. package/dist/{toPublicEnv-ggmphZFs.d.cts → toPublicEnv-CmBsy53P.d.cts} +1 -1
  60. package/dist/{toPublicEnv-CvhGAfsB.d.ts → toPublicEnv-q6VwWxXZ.d.ts} +1 -1
  61. package/package.json +1 -1
  62. package/dist/chunk-TUMR7JA3.js +0 -234
@@ -12,6 +12,11 @@ var CnosManifestError = class extends CnosError {
12
12
  }
13
13
  manifestPath;
14
14
  };
15
+ var CnosDiscoveryError = class extends CnosError {
16
+ constructor(message) {
17
+ super(message);
18
+ }
19
+ };
15
20
  var CnosSecurityError = class extends CnosError {
16
21
  constructor(message) {
17
22
  super(message);
@@ -169,29 +174,105 @@ async function writeKeychain(entry, value) {
169
174
  throw new CnosAuthenticationError(`OS keychain is not supported on platform "${process.platform}".`);
170
175
  }
171
176
 
177
+ // ../core/src/utils/yaml.ts
178
+ import { parse, stringify } from "yaml";
179
+ function parseYaml(source) {
180
+ return parse(source);
181
+ }
182
+ function stringifyYaml(value) {
183
+ return stringify(value);
184
+ }
185
+
172
186
  // ../core/src/utils/path.ts
173
- import { access } from "fs/promises";
187
+ import { access as access2 } from "fs/promises";
174
188
  import os from "os";
189
+ import path2 from "path";
190
+
191
+ // ../core/src/discovery/findCnosrc.ts
192
+ import { access, readFile } from "fs/promises";
175
193
  import path from "path";
194
+ async function exists(targetPath) {
195
+ try {
196
+ await access(targetPath);
197
+ return true;
198
+ } catch {
199
+ return false;
200
+ }
201
+ }
202
+ function validateCnosrc(value, filePath) {
203
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
204
+ throw new CnosManifestError(".cnosrc.yml must be a YAML object", filePath);
205
+ }
206
+ const root = typeof value.root === "string" ? value.root.trim() : "";
207
+ const workspace = typeof value.workspace === "string" ? value.workspace.trim() : void 0;
208
+ if (!root) {
209
+ throw new CnosManifestError(".cnosrc.yml requires root", filePath);
210
+ }
211
+ return {
212
+ root,
213
+ ...workspace ? { workspace } : {}
214
+ };
215
+ }
216
+ async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
217
+ let current = path.resolve(startDir);
218
+ for (let depth = 0; depth <= maxLevels; depth += 1) {
219
+ const candidate = path.join(current, ".cnosrc.yml");
220
+ if (await exists(candidate)) {
221
+ return candidate;
222
+ }
223
+ const parent = path.dirname(current);
224
+ if (parent === current) {
225
+ break;
226
+ }
227
+ current = parent;
228
+ }
229
+ return void 0;
230
+ }
231
+ async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3) {
232
+ const anchorPath = await findCnosrc(startDir, maxLevels);
233
+ if (!anchorPath) {
234
+ throw new CnosDiscoveryError(
235
+ "No .cnosrc.yml found. Run cnos init or create .cnosrc.yml in your package root."
236
+ );
237
+ }
238
+ const source = await readFile(anchorPath, "utf8");
239
+ const parsed = validateCnosrc(parseYaml(source), anchorPath);
240
+ const consumerRoot = path.dirname(anchorPath);
241
+ const manifestRoot = path.resolve(consumerRoot, parsed.root);
242
+ const manifestPath = path.join(manifestRoot, "cnos.yml");
243
+ if (!await exists(manifestPath)) {
244
+ throw new CnosDiscoveryError(
245
+ `.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`
246
+ );
247
+ }
248
+ return {
249
+ anchorPath,
250
+ consumerRoot,
251
+ manifestRoot,
252
+ ...parsed.workspace ? { workspace: parsed.workspace } : {}
253
+ };
254
+ }
255
+
256
+ // ../core/src/utils/path.ts
176
257
  var PRIMARY_CNOS_DIR = ".cnos";
177
258
  var LEGACY_CNOS_DIR = "cnos";
178
- async function exists(filePath) {
259
+ async function exists2(filePath) {
179
260
  try {
180
- await access(filePath);
261
+ await access2(filePath);
181
262
  return true;
182
263
  } catch {
183
264
  return false;
184
265
  }
185
266
  }
186
267
  async function resolveCnosRoot(root = process.cwd()) {
187
- const basePath = path.resolve(root);
268
+ const basePath = path2.resolve(root);
188
269
  const candidates = [
189
- path.join(basePath, PRIMARY_CNOS_DIR),
190
- path.join(basePath, LEGACY_CNOS_DIR),
270
+ path2.join(basePath, PRIMARY_CNOS_DIR),
271
+ path2.join(basePath, LEGACY_CNOS_DIR),
191
272
  basePath
192
273
  ];
193
274
  for (const candidate of candidates) {
194
- if (await exists(path.join(candidate, "cnos.yml"))) {
275
+ if (await exists2(path2.join(candidate, "cnos.yml"))) {
195
276
  return candidate;
196
277
  }
197
278
  }
@@ -199,8 +280,23 @@ async function resolveCnosRoot(root = process.cwd()) {
199
280
  `Could not locate .cnos/cnos.yml or cnos/cnos.yml from root: ${basePath}`
200
281
  );
201
282
  }
202
- async function resolveManifestRoot(root = process.cwd()) {
203
- return resolveCnosRoot(root);
283
+ async function resolveManifestRoot(options = {}) {
284
+ if (options.root) {
285
+ const manifestRoot = await resolveCnosRoot(options.root);
286
+ const resolvedRoot = path2.resolve(options.root);
287
+ const consumerRoot = path2.basename(manifestRoot) === PRIMARY_CNOS_DIR || path2.basename(manifestRoot) === LEGACY_CNOS_DIR ? path2.dirname(manifestRoot) : resolvedRoot;
288
+ return {
289
+ manifestRoot,
290
+ consumerRoot
291
+ };
292
+ }
293
+ const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd());
294
+ return {
295
+ manifestRoot: discovered.manifestRoot,
296
+ consumerRoot: discovered.consumerRoot,
297
+ anchorPath: discovered.anchorPath,
298
+ ...discovered.workspace ? { workspace: discovered.workspace } : {}
299
+ };
204
300
  }
205
301
  function interpolatePathTemplate(template, tokens) {
206
302
  return Object.entries(tokens).reduce(
@@ -213,7 +309,7 @@ function expandHomePath(targetPath) {
213
309
  return os.homedir();
214
310
  }
215
311
  if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
216
- return path.join(os.homedir(), targetPath.slice(2));
312
+ return path2.join(os.homedir(), targetPath.slice(2));
217
313
  }
218
314
  return targetPath;
219
315
  }
@@ -231,19 +327,19 @@ function stripWorkspaceTemplatePrefix(template) {
231
327
  function resolveWorkspaceScopedPath(workspaceRoot, template, tokens) {
232
328
  const relativeTemplate = stripWorkspaceTemplatePrefix(template);
233
329
  const interpolated = interpolatePathTemplate(relativeTemplate, tokens);
234
- return path.resolve(workspaceRoot, interpolated);
330
+ return path2.resolve(workspaceRoot, interpolated);
235
331
  }
236
332
  function resolveNamespaceDirectory(workspaceRoot, namespace, profile) {
237
333
  const rootFolder = namespace === "value" ? "values" : namespace === "secret" ? "secrets" : namespace;
238
334
  if (profile && profile !== "base") {
239
- return path.resolve(workspaceRoot, "profiles", profile, rootFolder);
335
+ return path2.resolve(workspaceRoot, "profiles", profile, rootFolder);
240
336
  }
241
- return path.resolve(workspaceRoot, rootFolder);
337
+ return path2.resolve(workspaceRoot, rootFolder);
242
338
  }
243
339
  function resolveConfigDocumentPath(workspaceRoot, namespace, configPath, profile) {
244
340
  const namespaceRoot = resolveNamespaceDirectory(workspaceRoot, namespace, profile);
245
341
  const fileName = `${configPath.split(".").shift() ?? "app"}.yml`;
246
- return path.resolve(namespaceRoot, fileName);
342
+ return path2.resolve(namespaceRoot, fileName);
247
343
  }
248
344
  function toPortablePath(targetPath) {
249
345
  return targetPath.replace(/\\/g, "/");
@@ -258,18 +354,9 @@ function stripNamespace(key) {
258
354
  return key.split(".").slice(1).join(".");
259
355
  }
260
356
 
261
- // ../core/src/utils/yaml.ts
262
- import { parse, stringify } from "yaml";
263
- function parseYaml(source) {
264
- return parse(source);
265
- }
266
- function stringifyYaml(value) {
267
- return stringify(value);
268
- }
269
-
270
357
  // ../core/src/manifest/loadManifest.ts
271
- import { readFile } from "fs/promises";
272
- import path2 from "path";
358
+ import { readFile as readFile2 } from "fs/promises";
359
+ import path3 from "path";
273
360
 
274
361
  // ../core/src/manifest/normalizeManifest.ts
275
362
  var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
@@ -540,11 +627,15 @@ function normalizeManifest(manifest) {
540
627
 
541
628
  // ../core/src/manifest/loadManifest.ts
542
629
  async function loadManifest(options = {}) {
543
- const manifestRoot = await resolveManifestRoot(options.root);
544
- const manifestPath = path2.join(manifestRoot, "cnos.yml");
630
+ const resolved = await resolveManifestRoot({
631
+ ...options.root ? { root: options.root } : {},
632
+ ...options.cwd ? { cwd: options.cwd } : {}
633
+ });
634
+ const manifestRoot = resolved.manifestRoot;
635
+ const manifestPath = path3.join(manifestRoot, "cnos.yml");
545
636
  let source;
546
637
  try {
547
- source = await readFile(manifestPath, "utf8");
638
+ source = await readFile2(manifestPath, "utf8");
548
639
  } catch {
549
640
  throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
550
641
  }
@@ -554,7 +645,10 @@ async function loadManifest(options = {}) {
554
645
  }
555
646
  return {
556
647
  manifestRoot,
557
- repoRoot: path2.dirname(manifestRoot),
648
+ repoRoot: path3.dirname(manifestRoot),
649
+ consumerRoot: resolved.consumerRoot,
650
+ ...resolved.anchorPath ? { anchorPath: resolved.anchorPath } : {},
651
+ ...resolved.workspace ? { anchoredWorkspace: resolved.workspace } : {},
558
652
  manifestPath,
559
653
  manifest: normalizeManifest(rawManifest),
560
654
  rawManifest
@@ -610,17 +704,17 @@ function validateProjectionIssue(manifest, key, target) {
610
704
  }
611
705
 
612
706
  // ../core/src/secrets/sessionStore.ts
613
- import { mkdir, readFile as readFile2, readdir, rm, writeFile } from "fs/promises";
614
- import path3 from "path";
707
+ import { mkdir, readFile as readFile3, readdir, rm, writeFile } from "fs/promises";
708
+ import path4 from "path";
615
709
  function buildSessionRoot(processEnv = process.env) {
616
- return path3.join(path3.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
710
+ return path4.join(path4.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
617
711
  }
618
712
  function buildSessionPath(vault, processEnv) {
619
- return path3.join(buildSessionRoot(processEnv), `${vault}.json`);
713
+ return path4.join(buildSessionRoot(processEnv), `${vault}.json`);
620
714
  }
621
715
  async function writeVaultSessionKey(vault, derivedKey, processEnv) {
622
716
  const filePath = buildSessionPath(vault, processEnv);
623
- await mkdir(path3.dirname(filePath), { recursive: true });
717
+ await mkdir(path4.dirname(filePath), { recursive: true });
624
718
  const document = {
625
719
  version: 1,
626
720
  vault,
@@ -632,7 +726,7 @@ async function writeVaultSessionKey(vault, derivedKey, processEnv) {
632
726
  }
633
727
  async function readVaultSessionKey(vault, processEnv) {
634
728
  try {
635
- const source = await readFile2(buildSessionPath(vault, processEnv), "utf8");
729
+ const source = await readFile3(buildSessionPath(vault, processEnv), "utf8");
636
730
  const document = JSON.parse(source);
637
731
  if (document.version !== 1 || typeof document.derivedKey !== "string") {
638
732
  return void 0;
@@ -650,15 +744,15 @@ async function clearAllVaultSessionKeys(processEnv) {
650
744
  const root = buildSessionRoot(processEnv);
651
745
  try {
652
746
  const entries = await readdir(root);
653
- await Promise.all(entries.map((entry) => rm(path3.join(root, entry), { force: true })));
747
+ await Promise.all(entries.map((entry) => rm(path4.join(root, entry), { force: true })));
654
748
  } catch {
655
749
  }
656
750
  }
657
751
 
658
752
  // ../core/src/utils/secretStore.ts
659
753
  import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from "crypto";
660
- import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, rm as rm2, stat, writeFile as writeFile2 } from "fs/promises";
661
- import path4 from "path";
754
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile4, rm as rm2, stat, writeFile as writeFile2 } from "fs/promises";
755
+ import path5 from "path";
662
756
  var KEY_LENGTH = 32;
663
757
  var SALT_LENGTH = 32;
664
758
  var IV_LENGTH = 12;
@@ -675,7 +769,7 @@ function isSecretReference(value) {
675
769
  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));
676
770
  }
677
771
  function resolveSecretStoreRoot(processEnv = process.env) {
678
- return path4.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
772
+ return path5.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
679
773
  }
680
774
  function normalizeVaultToken(vault = "default") {
681
775
  return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
@@ -710,19 +804,19 @@ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
710
804
  return pbkdf2Sync(passphrase, salt, iterations, KEY_LENGTH, "sha512");
711
805
  }
712
806
  function buildMetaPath(storeRoot, vault = "default") {
713
- return path4.join(storeRoot, "vaults", vault, META_FILENAME);
807
+ return path5.join(storeRoot, "vaults", vault, META_FILENAME);
714
808
  }
715
809
  function resolveSecretVaultFile(storeRoot, vault = "default") {
716
810
  return buildMetaPath(storeRoot, vault);
717
811
  }
718
812
  function buildKeystorePath(storeRoot, vault = "default") {
719
- return path4.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
813
+ return path5.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
720
814
  }
721
815
  function buildLegacyVaultFile(storeRoot, vault = "default") {
722
- return path4.join(storeRoot, "vaults", `${vault}.json`);
816
+ return path5.join(storeRoot, "vaults", `${vault}.json`);
723
817
  }
724
818
  function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
725
- return path4.join(storeRoot, "vaults", vault, "store");
819
+ return path5.join(storeRoot, "vaults", vault, "store");
726
820
  }
727
821
  function assertVaultMetadata(value, filePath) {
728
822
  if (!isObject(value)) {
@@ -733,7 +827,7 @@ function assertVaultMetadata(value, filePath) {
733
827
  }
734
828
  return value;
735
829
  }
736
- async function exists2(targetPath) {
830
+ async function exists3(targetPath) {
737
831
  try {
738
832
  await stat(targetPath);
739
833
  return true;
@@ -744,10 +838,10 @@ async function exists2(targetPath) {
744
838
  async function detectLegacyVaultFormat(storeRoot, vault = "default") {
745
839
  const legacyFile = buildLegacyVaultFile(storeRoot, vault);
746
840
  const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
747
- if (await exists2(legacyFile)) {
841
+ if (await exists3(legacyFile)) {
748
842
  return legacyFile;
749
843
  }
750
- if (await exists2(legacyStore)) {
844
+ if (await exists3(legacyStore)) {
751
845
  return legacyStore;
752
846
  }
753
847
  return void 0;
@@ -819,7 +913,7 @@ function buildInitialPayload() {
819
913
  async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
820
914
  const metaPath = buildMetaPath(storeRoot, vault);
821
915
  const keystorePath = buildKeystorePath(storeRoot, vault);
822
- await mkdir2(path4.dirname(metaPath), { recursive: true });
916
+ await mkdir2(path5.dirname(metaPath), { recursive: true });
823
917
  await writeFile2(metaPath, stringifyYaml(meta), "utf8");
824
918
  await writeFile2(keystorePath, encryptPayload(payload, key));
825
919
  }
@@ -827,7 +921,7 @@ async function readVaultMetadata(storeRoot, vault = "default") {
827
921
  await assertNoLegacyVaultFormat(storeRoot, vault);
828
922
  const metaPath = buildMetaPath(storeRoot, vault);
829
923
  try {
830
- const source = await readFile3(metaPath, "utf8");
924
+ const source = await readFile4(metaPath, "utf8");
831
925
  return assertVaultMetadata(parseYaml(source), metaPath);
832
926
  } catch (error) {
833
927
  if (error.code === "ENOENT") {
@@ -837,11 +931,11 @@ async function readVaultMetadata(storeRoot, vault = "default") {
837
931
  }
838
932
  }
839
933
  async function listSecretVaults(storeRoot) {
840
- const vaultRoot = path4.join(storeRoot, "vaults");
934
+ const vaultRoot = path5.join(storeRoot, "vaults");
841
935
  try {
842
936
  const entries = await readdir2(vaultRoot, { withFileTypes: true });
843
937
  const vaults = await Promise.all(
844
- entries.filter((entry) => entry.isDirectory()).map(async (entry) => await exists2(path4.join(vaultRoot, entry.name, META_FILENAME)) ? entry.name : void 0)
938
+ entries.filter((entry) => entry.isDirectory()).map(async (entry) => await exists3(path5.join(vaultRoot, entry.name, META_FILENAME)) ? entry.name : void 0)
845
939
  );
846
940
  return vaults.filter((value) => Boolean(value)).sort((left, right) => left.localeCompare(right));
847
941
  } catch {
@@ -930,7 +1024,7 @@ async function loadVaultPayload(storeRoot, vault, auth) {
930
1024
  if (!key) {
931
1025
  throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
932
1026
  }
933
- const buffer = await readFile3(buildKeystorePath(storeRoot, vault));
1027
+ const buffer = await readFile4(buildKeystorePath(storeRoot, vault));
934
1028
  return {
935
1029
  meta,
936
1030
  payload: decryptPayload(buffer, key),
@@ -1003,7 +1097,7 @@ function resolveVaultDefinition(vaults, vault = "default") {
1003
1097
  };
1004
1098
  }
1005
1099
  async function removeLocalVaultFiles(storeRoot, vault = "default") {
1006
- await rm2(path4.join(storeRoot, "vaults", vault), { recursive: true, force: true });
1100
+ await rm2(path5.join(storeRoot, "vaults", vault), { recursive: true, force: true });
1007
1101
  }
1008
1102
 
1009
1103
  // ../core/src/secrets/providers/github.ts
@@ -1063,10 +1157,10 @@ var GithubSecretsVaultProvider = class {
1063
1157
 
1064
1158
  // ../core/src/secrets/auditLog.ts
1065
1159
  import { appendFile, mkdir as mkdir3 } from "fs/promises";
1066
- import path5 from "path";
1160
+ import path6 from "path";
1067
1161
  async function appendAuditEvent(event, processEnv = process.env) {
1068
- const auditFile = processEnv.CNOS_AUDIT_FILE ?? path5.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1069
- await mkdir3(path5.dirname(auditFile), { recursive: true });
1162
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? path6.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1163
+ await mkdir3(path6.dirname(auditFile), { recursive: true });
1070
1164
  await appendFile(
1071
1165
  auditFile,
1072
1166
  `${JSON.stringify({
@@ -1413,22 +1507,22 @@ function toPublicEnv(graph, manifest, options = {}) {
1413
1507
 
1414
1508
  // ../core/src/runtime/dump.ts
1415
1509
  import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
1416
- import path6 from "path";
1510
+ import path7 from "path";
1417
1511
  function buildDumpFiles(graph, options = {}) {
1418
- const basePath = options.flatten ? "" : path6.posix.join("workspaces", graph.workspace.workspaceId);
1512
+ const basePath = options.flatten ? "" : path7.posix.join("workspaces", graph.workspace.workspaceId);
1419
1513
  const values = toNamespaceObject(graph, "value");
1420
1514
  const secrets = toNamespaceObject(graph, "secret");
1421
1515
  const files = [];
1422
1516
  if (Object.keys(values).length > 0) {
1423
1517
  files.push({
1424
- path: path6.posix.join(basePath, "values", graph.profile, "app.yml"),
1518
+ path: path7.posix.join(basePath, "values", graph.profile, "app.yml"),
1425
1519
  namespace: "value",
1426
1520
  content: stringifyYaml(values)
1427
1521
  });
1428
1522
  }
1429
1523
  if (Object.keys(secrets).length > 0) {
1430
1524
  files.push({
1431
- path: path6.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1525
+ path: path7.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1432
1526
  namespace: "secret",
1433
1527
  content: stringifyYaml(secrets)
1434
1528
  });
@@ -1444,11 +1538,11 @@ function planDump(graph, options = {}) {
1444
1538
  };
1445
1539
  }
1446
1540
  async function writeDump(graph, options) {
1447
- const root = path6.resolve(options.to);
1541
+ const root = path7.resolve(options.to);
1448
1542
  const plan = planDump(graph, options);
1449
1543
  for (const file of plan.files) {
1450
- const destination = path6.join(root, file.path);
1451
- await mkdir4(path6.dirname(destination), { recursive: true });
1544
+ const destination = path7.join(root, file.path);
1545
+ await mkdir4(path7.dirname(destination), { recursive: true });
1452
1546
  await writeFile3(destination, file.content, "utf8");
1453
1547
  }
1454
1548
  return {
@@ -1480,11 +1574,11 @@ function normalizeMappingConfig(config = {}) {
1480
1574
  function toScreamingSnakeSegment(segment) {
1481
1575
  return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1482
1576
  }
1483
- function toScreamingSnake(path10) {
1484
- return path10.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1577
+ function toScreamingSnake(path11) {
1578
+ return path11.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1485
1579
  }
1486
- function fromScreamingSnake(path10) {
1487
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1580
+ function fromScreamingSnake(path11) {
1581
+ return path11.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1488
1582
  }
1489
1583
  function logicalKeyToEnvVar(key, config = {}) {
1490
1584
  const normalized = normalizeMappingConfig(config);
@@ -1636,12 +1730,12 @@ function createProvenanceInspector() {
1636
1730
  }
1637
1731
 
1638
1732
  // ../core/src/manifest/loadWorkspaceFile.ts
1639
- import { readFile as readFile4 } from "fs/promises";
1640
- import path7 from "path";
1733
+ import { readFile as readFile5 } from "fs/promises";
1734
+ import path8 from "path";
1641
1735
  async function loadWorkspaceFile(repoRoot) {
1642
- const workspaceFilePath = path7.join(repoRoot, ".cnos-workspace.yml");
1736
+ const workspaceFilePath = path8.join(repoRoot, ".cnos-workspace.yml");
1643
1737
  try {
1644
- const source = await readFile4(workspaceFilePath, "utf8");
1738
+ const source = await readFile5(workspaceFilePath, "utf8");
1645
1739
  const parsed = parseYaml(source);
1646
1740
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1647
1741
  throw new CnosManifestError(".cnos-workspace.yml must be a YAML object", workspaceFilePath);
@@ -1664,11 +1758,11 @@ async function loadWorkspaceFile(repoRoot) {
1664
1758
  }
1665
1759
 
1666
1760
  // ../core/src/profiles/expandProfileChain.ts
1667
- import { access as access2, readFile as readFile5 } from "fs/promises";
1668
- import path8 from "path";
1761
+ import { access as access3, readFile as readFile6 } from "fs/promises";
1762
+ import path9 from "path";
1669
1763
  async function fileExists(targetPath) {
1670
1764
  try {
1671
- await access2(targetPath);
1765
+ await access3(targetPath);
1672
1766
  return true;
1673
1767
  } catch {
1674
1768
  return false;
@@ -1702,11 +1796,11 @@ async function loadProfileDefinition(profileName, options) {
1702
1796
  return normalizeProfileDefinition(profileName, void 0);
1703
1797
  }
1704
1798
  for (const workspaceRoot of [...workspaceRoots].reverse()) {
1705
- const profilePath = path8.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
1799
+ const profilePath = path9.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
1706
1800
  if (!await fileExists(profilePath)) {
1707
1801
  continue;
1708
1802
  }
1709
- const document = await readFile5(profilePath, "utf8");
1803
+ const document = await readFile6(profilePath, "utf8");
1710
1804
  const parsed = parseYaml(document);
1711
1805
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1712
1806
  throw new CnosManifestError("Profile definition must be a YAML object", profilePath);
@@ -1714,7 +1808,7 @@ async function loadProfileDefinition(profileName, options) {
1714
1808
  const definition = normalizeProfileDefinition(
1715
1809
  profileName,
1716
1810
  parsed,
1717
- options.manifestRoot ? toPortablePath(path8.relative(path8.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
1811
+ options.manifestRoot ? toPortablePath(path9.relative(path9.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
1718
1812
  );
1719
1813
  if (definition.name !== profileName) {
1720
1814
  throw new CnosManifestError(
@@ -1960,8 +2054,8 @@ function createProfileAwareResolver() {
1960
2054
  }
1961
2055
 
1962
2056
  // ../core/src/workspaces/resolveWorkspaceContext.ts
1963
- import { access as access3 } from "fs/promises";
1964
- import path9 from "path";
2057
+ import { access as access4 } from "fs/promises";
2058
+ import path10 from "path";
1965
2059
 
1966
2060
  // ../core/src/workspaces/expandWorkspaceChain.ts
1967
2061
  function expandWorkspaceChain(workspaceId, items) {
@@ -1998,31 +2092,31 @@ function expandWorkspaceChain(workspaceId, items) {
1998
2092
  }
1999
2093
 
2000
2094
  // ../core/src/workspaces/resolveWorkspaceContext.ts
2001
- async function exists3(targetPath) {
2095
+ async function exists4(targetPath) {
2002
2096
  try {
2003
- await access3(targetPath);
2097
+ await access4(targetPath);
2004
2098
  return true;
2005
2099
  } catch {
2006
2100
  return false;
2007
2101
  }
2008
2102
  }
2009
2103
  async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
2010
- const workspaceRoot = path9.join(manifestRoot, "workspaces", workspaceId);
2011
- if (await exists3(workspaceRoot)) {
2104
+ const workspaceRoot = path10.join(manifestRoot, "workspaces", workspaceId);
2105
+ if (await exists4(workspaceRoot)) {
2012
2106
  return workspaceRoot;
2013
2107
  }
2014
2108
  const customDataNamespaceRoots = Object.entries(manifest.namespaces).filter(
2015
2109
  ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
2016
2110
  ).map(([namespace]) => namespace);
2017
2111
  const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
2018
- (segment) => path9.join(manifestRoot, segment)
2112
+ (segment) => path10.join(manifestRoot, segment)
2019
2113
  );
2020
- if ((await Promise.all(legacyMarkers.map((marker) => exists3(marker)))).some(Boolean)) {
2114
+ if ((await Promise.all(legacyMarkers.map((marker) => exists4(marker)))).some(Boolean)) {
2021
2115
  return manifestRoot;
2022
2116
  }
2023
2117
  return workspaceRoot;
2024
2118
  }
2025
- function resolveWorkspaceSelection(manifest, workspaceFile, workspaceOption) {
2119
+ function resolveWorkspaceSelection(manifest, workspaceFile, anchoredWorkspace, workspaceOption) {
2026
2120
  if (workspaceOption) {
2027
2121
  return {
2028
2122
  workspaceId: workspaceOption,
@@ -2035,6 +2129,12 @@ function resolveWorkspaceSelection(manifest, workspaceFile, workspaceOption) {
2035
2129
  source: "workspace-file"
2036
2130
  };
2037
2131
  }
2132
+ if (anchoredWorkspace) {
2133
+ return {
2134
+ workspaceId: anchoredWorkspace,
2135
+ source: "anchor-file"
2136
+ };
2137
+ }
2038
2138
  if (manifest.workspaces.default) {
2039
2139
  return {
2040
2140
  workspaceId: manifest.workspaces.default,
@@ -2057,33 +2157,38 @@ function resolveGlobalRoot(manifest, workspaceFile, options) {
2057
2157
  }
2058
2158
  if (options.globalRoot) {
2059
2159
  return {
2060
- value: path9.resolve(expandHomePath(options.globalRoot)),
2160
+ value: path10.resolve(expandHomePath(options.globalRoot)),
2061
2161
  source: "cli"
2062
2162
  };
2063
2163
  }
2064
2164
  if (workspaceFile?.globalRoot) {
2065
2165
  return {
2066
- value: path9.resolve(expandHomePath(workspaceFile.globalRoot)),
2166
+ value: path10.resolve(expandHomePath(workspaceFile.globalRoot)),
2067
2167
  source: "workspace-file"
2068
2168
  };
2069
2169
  }
2070
2170
  if (manifest.workspaces.global.root) {
2071
2171
  return {
2072
- value: path9.resolve(expandHomePath(manifest.workspaces.global.root)),
2172
+ value: path10.resolve(expandHomePath(manifest.workspaces.global.root)),
2073
2173
  source: "manifest"
2074
2174
  };
2075
2175
  }
2076
2176
  const cnosHome = options.processEnv?.CNOS_HOME;
2077
2177
  if (cnosHome) {
2078
2178
  return {
2079
- value: path9.resolve(expandHomePath(cnosHome)),
2179
+ value: path10.resolve(expandHomePath(cnosHome)),
2080
2180
  source: "CNOS_HOME"
2081
2181
  };
2082
2182
  }
2083
2183
  return {};
2084
2184
  }
2085
2185
  async function resolveWorkspaceContext(manifest, options) {
2086
- const selectedWorkspace = resolveWorkspaceSelection(manifest, options.workspaceFile, options.workspace);
2186
+ const selectedWorkspace = resolveWorkspaceSelection(
2187
+ manifest,
2188
+ options.workspaceFile,
2189
+ options.anchoredWorkspace,
2190
+ options.workspace
2191
+ );
2087
2192
  const workspaceChain = expandWorkspaceChain(selectedWorkspace.workspaceId, manifest.workspaces.items);
2088
2193
  const globalRoot = resolveGlobalRoot(manifest, options.workspaceFile, options);
2089
2194
  const workspaceRoots = [];
@@ -2093,7 +2198,7 @@ async function resolveWorkspaceContext(manifest, options) {
2093
2198
  workspaceRoots.push({
2094
2199
  scope: "global",
2095
2200
  workspaceId: chainWorkspaceId,
2096
- path: path9.join(globalRoot.value, "workspaces", globalWorkspaceId)
2201
+ path: path10.join(globalRoot.value, "workspaces", globalWorkspaceId)
2097
2202
  });
2098
2203
  }
2099
2204
  }
@@ -2361,8 +2466,92 @@ function resolveSecretEntryValue(key, value, cache) {
2361
2466
  return cache.get(vaultId, value.ref) ?? value;
2362
2467
  }
2363
2468
 
2469
+ // ../core/src/runtime/toServerProjection.ts
2470
+ import { createHash } from "crypto";
2471
+ function stableSortObject(value) {
2472
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
2473
+ }
2474
+ function stripValuePrefix(key) {
2475
+ return key.startsWith("value.") ? key.slice("value.".length) : key;
2476
+ }
2477
+ function configHash(values) {
2478
+ const serialized = JSON.stringify(stableSortObject(values));
2479
+ return createHash("sha256").update(serialized).digest("hex");
2480
+ }
2481
+ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
2482
+ const values = {};
2483
+ const secretRefs = {};
2484
+ const namespaces = /* @__PURE__ */ new Set();
2485
+ 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));
2486
+ for (const [key, entry] of graph.entries) {
2487
+ if (entry.namespace === "secret" && isSecretReference(entry.value)) {
2488
+ secretRefs[key.slice("secret.".length)] = {
2489
+ provider: entry.value.provider,
2490
+ vault: entry.value.vault ?? "default",
2491
+ ref: entry.value.ref
2492
+ };
2493
+ continue;
2494
+ }
2495
+ if (entry.namespace === "value") {
2496
+ values[stripValuePrefix(key)] = entry.value;
2497
+ continue;
2498
+ }
2499
+ const namespaceDefinition = manifest.namespaces[entry.namespace];
2500
+ if (namespaceDefinition && namespaceDefinition.kind === "data" && !namespaceDefinition.sensitive && entry.namespace !== "public") {
2501
+ values[key] = entry.value;
2502
+ namespaces.add(entry.namespace);
2503
+ }
2504
+ }
2505
+ return {
2506
+ version: 1,
2507
+ workspace: graph.workspace.workspaceId,
2508
+ profile: graph.profile,
2509
+ resolvedAt: graph.resolvedAt,
2510
+ configHash: configHash(values),
2511
+ values: stableSortObject(values),
2512
+ secretRefs: stableSortObject(secretRefs),
2513
+ publicKeys,
2514
+ meta: {
2515
+ workspace: graph.workspace.workspaceId,
2516
+ profile: graph.profile,
2517
+ cnos_version: cnosVersion,
2518
+ ...namespaces.size > 0 ? { namespaces: Array.from(namespaces).sort((left, right) => left.localeCompare(right)) } : {}
2519
+ }
2520
+ };
2521
+ }
2522
+
2364
2523
  // ../core/src/orchestrator/runtime.ts
2365
- function createRuntime(manifest, graph, plugins = [], secretCache) {
2524
+ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv = process.env, cnosVersion = "0.0.0-dev") {
2525
+ async function refreshSecretEntry(key) {
2526
+ const entry = graph.entries.get(key);
2527
+ if (!entry || entry.namespace !== "secret" || !isSecretReference(entry.value)) {
2528
+ return;
2529
+ }
2530
+ if (!secretCache) {
2531
+ return;
2532
+ }
2533
+ const vaultId = entry.value.vault ?? "default";
2534
+ const definition = manifest.vaults[vaultId] ?? {
2535
+ provider: entry.value.provider,
2536
+ auth: { passphrase: { from: [] } }
2537
+ };
2538
+ const provider = createSecretVaultProvider(vaultId, definition, processEnv);
2539
+ const auth = await resolveVaultAuth(vaultId, definition, processEnv);
2540
+ await provider.authenticate(auth);
2541
+ const value = await provider.get(entry.value.ref);
2542
+ if (value !== void 0) {
2543
+ secretCache.load(vaultId, /* @__PURE__ */ new Map([[entry.value.ref, value]]));
2544
+ }
2545
+ }
2546
+ async function refreshAllSecrets() {
2547
+ if (!secretCache) {
2548
+ return;
2549
+ }
2550
+ const secretKeys = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => entry.key);
2551
+ for (const key of secretKeys) {
2552
+ await refreshSecretEntry(key);
2553
+ }
2554
+ }
2366
2555
  function readLogicalKey(key) {
2367
2556
  const entry = graph.entries.get(key);
2368
2557
  if (!entry) {
@@ -2390,14 +2579,14 @@ function createRuntime(manifest, graph, plugins = [], secretCache) {
2390
2579
  readOr(key, fallback) {
2391
2580
  return readOrValue(graph, key, fallback);
2392
2581
  },
2393
- value(path10) {
2394
- return readLogicalKey(toLogicalKey("value", path10));
2582
+ value(path11) {
2583
+ return readLogicalKey(toLogicalKey("value", path11));
2395
2584
  },
2396
- secret(path10) {
2397
- return readLogicalKey(toLogicalKey("secret", path10));
2585
+ secret(path11) {
2586
+ return readLogicalKey(toLogicalKey("secret", path11));
2398
2587
  },
2399
- meta(path10) {
2400
- return readLogicalKey(toLogicalKey("meta", path10));
2588
+ meta(path11) {
2589
+ return readLogicalKey(toLogicalKey("meta", path11));
2401
2590
  },
2402
2591
  inspect(key) {
2403
2592
  return inspectValue(graph, key);
@@ -2413,6 +2602,15 @@ function createRuntime(manifest, graph, plugins = [], secretCache) {
2413
2602
  },
2414
2603
  toPublicEnv(options) {
2415
2604
  return toPublicEnv(graph, manifest, options);
2605
+ },
2606
+ toServerProjection() {
2607
+ return toServerProjection(graph, manifest, cnosVersion);
2608
+ },
2609
+ async refreshSecrets() {
2610
+ await refreshAllSecrets();
2611
+ },
2612
+ async refreshSecret(key) {
2613
+ await refreshSecretEntry(key);
2416
2614
  }
2417
2615
  };
2418
2616
  }
@@ -2511,14 +2709,18 @@ function appendMetaEntries(graph, cnosVersion) {
2511
2709
  };
2512
2710
  }
2513
2711
  async function createCnos(options = {}) {
2514
- const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
2712
+ const loadedManifest = await loadManifest({
2713
+ ...options.root ? { root: options.root } : {},
2714
+ ...options.cwd ? { cwd: options.cwd } : {}
2715
+ });
2515
2716
  for (const key of loadedManifest.manifest.public.promote) {
2516
2717
  ensureProjectionAllowed(loadedManifest.manifest, key, "public");
2517
2718
  }
2518
- const workspaceFile = await loadWorkspaceFile(loadedManifest.repoRoot);
2719
+ const workspaceFile = await loadWorkspaceFile(loadedManifest.consumerRoot);
2519
2720
  const workspace = await resolveWorkspaceContext(loadedManifest.manifest, {
2520
2721
  manifestRoot: loadedManifest.manifestRoot,
2521
2722
  ...workspaceFile ? { workspaceFile: workspaceFile.config } : {},
2723
+ ...loadedManifest.anchoredWorkspace ? { anchoredWorkspace: loadedManifest.anchoredWorkspace } : {},
2522
2724
  ...options.workspace ? { workspace: options.workspace } : {},
2523
2725
  ...options.globalRoot ? { globalRoot: options.globalRoot } : {},
2524
2726
  ...options.processEnv ? { processEnv: options.processEnv } : {}
@@ -2563,7 +2765,9 @@ async function createCnos(options = {}) {
2563
2765
  profileSource: activeProfile.source
2564
2766
  }, options.cnosVersion),
2565
2767
  plugins,
2566
- secretCache
2768
+ secretCache,
2769
+ options.processEnv,
2770
+ options.cnosVersion
2567
2771
  );
2568
2772
  }
2569
2773
 
@@ -2575,14 +2779,14 @@ export {
2575
2779
  createProvenanceInspector,
2576
2780
  readKeychain,
2577
2781
  writeKeychain,
2782
+ parseYaml,
2783
+ stringifyYaml,
2578
2784
  resolveManifestRoot,
2579
2785
  resolveWorkspaceScopedPath,
2580
2786
  resolveConfigDocumentPath,
2581
2787
  toPortablePath,
2582
2788
  joinConfigPath,
2583
2789
  toLogicalKey,
2584
- parseYaml,
2585
- stringifyYaml,
2586
2790
  loadManifest,
2587
2791
  getNamespaceDefinition,
2588
2792
  ensureProjectionAllowed,