@kitsy/cnos 1.6.0 → 1.7.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 (63) hide show
  1. package/dist/build/index.cjs +1332 -156
  2. package/dist/build/index.d.cts +1 -1
  3. package/dist/build/index.d.ts +1 -1
  4. package/dist/build/index.js +36 -14
  5. package/dist/{chunk-JYWQFMW5.js → chunk-2DGT7N7E.js} +1 -1
  6. package/dist/{chunk-S7H2UULC.js → chunk-2TL42I6M.js} +1323 -139
  7. package/dist/{chunk-MW4OVAT3.js → chunk-5KIQCYFH.js} +1 -1
  8. package/dist/{chunk-UOKVLCFL.js → chunk-CV3SLBYZ.js} +10 -10
  9. package/dist/{chunk-XSUP7JKH.js → chunk-GHGJFRDL.js} +6 -2
  10. package/dist/{chunk-BMAD24KC.js → chunk-OA7FQGAG.js} +1 -1
  11. package/dist/{chunk-QU5CXL47.js → chunk-PFT56ID2.js} +195 -28
  12. package/dist/{chunk-VGZREX5D.js → chunk-RYIARE4M.js} +1 -1
  13. package/dist/{chunk-UR7CHHNN.js → chunk-TT4NV56Z.js} +3 -2
  14. package/dist/{chunk-UJBQS7CJ.js → chunk-UL63DFLS.js} +1 -1
  15. package/dist/configure/index.cjs +1309 -155
  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/{plugin-CKrBlWGI.d.cts → core-BJ8xewez.d.cts} +142 -60
  20. package/dist/{plugin-CKrBlWGI.d.ts → core-BJ8xewez.d.ts} +142 -60
  21. package/dist/{envNaming-B7Mztkcf.d.ts → envNaming-BRyiuPoI.d.ts} +1 -1
  22. package/dist/{envNaming-gMVnPOfe.d.cts → envNaming-rx71gpi0.d.cts} +1 -1
  23. package/dist/index.cjs +1548 -227
  24. package/dist/index.d.cts +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +10 -10
  27. package/dist/internal.cjs +831 -122
  28. package/dist/internal.d.cts +56 -3
  29. package/dist/internal.d.ts +56 -3
  30. package/dist/internal.js +30 -3
  31. package/dist/plugin/basic-schema.cjs +49 -23
  32. package/dist/plugin/basic-schema.d.cts +1 -1
  33. package/dist/plugin/basic-schema.d.ts +1 -1
  34. package/dist/plugin/basic-schema.js +2 -2
  35. package/dist/plugin/cli-args.cjs +38 -23
  36. package/dist/plugin/cli-args.d.cts +1 -1
  37. package/dist/plugin/cli-args.d.ts +1 -1
  38. package/dist/plugin/cli-args.js +2 -2
  39. package/dist/plugin/dotenv.cjs +46 -31
  40. package/dist/plugin/dotenv.d.cts +2 -2
  41. package/dist/plugin/dotenv.d.ts +2 -2
  42. package/dist/plugin/dotenv.js +2 -2
  43. package/dist/plugin/env-export.cjs +56 -27
  44. package/dist/plugin/env-export.d.cts +2 -2
  45. package/dist/plugin/env-export.d.ts +2 -2
  46. package/dist/plugin/env-export.js +2 -2
  47. package/dist/plugin/filesystem.cjs +61 -39
  48. package/dist/plugin/filesystem.d.cts +1 -1
  49. package/dist/plugin/filesystem.d.ts +1 -1
  50. package/dist/plugin/filesystem.js +2 -2
  51. package/dist/plugin/process-env.cjs +40 -25
  52. package/dist/plugin/process-env.d.cts +2 -2
  53. package/dist/plugin/process-env.d.ts +2 -2
  54. package/dist/plugin/process-env.js +2 -2
  55. package/dist/runtime/index.cjs +1548 -227
  56. package/dist/runtime/index.d.cts +3 -1
  57. package/dist/runtime/index.d.ts +3 -1
  58. package/dist/runtime/index.js +10 -10
  59. package/dist/toPublicEnv-CCSgdvI9.d.ts +13 -0
  60. package/dist/toPublicEnv-ivRtLjcw.d.cts +13 -0
  61. package/package.json +1 -1
  62. package/dist/toPublicEnv-CmBsy53P.d.cts +0 -7
  63. package/dist/toPublicEnv-q6VwWxXZ.d.ts +0 -7
@@ -75,16 +75,673 @@ var CnosKeyNotFoundError = class extends CnosError {
75
75
  }
76
76
  key;
77
77
  };
78
+ var CnosDerivedExpressionError = class extends CnosError {
79
+ constructor(message, expression) {
80
+ super(expression ? `${message} (${expression})` : message);
81
+ this.expression = expression;
82
+ }
83
+ expression;
84
+ };
85
+ var CnosDerivedCycleError = class extends CnosError {
86
+ constructor(message) {
87
+ super(message);
88
+ }
89
+ };
90
+ var CnosDerivedResolutionError = class extends CnosError {
91
+ constructor(key, message) {
92
+ super(message);
93
+ this.key = key;
94
+ }
95
+ key;
96
+ };
97
+ var CnosRuntimeProviderError = class extends CnosError {
98
+ constructor(message) {
99
+ super(message);
100
+ }
101
+ };
102
+
103
+ // ../core/src/derive/builtins.ts
104
+ function stringify(value) {
105
+ if (value === void 0 || value === null) {
106
+ return "";
107
+ }
108
+ if (typeof value === "string") {
109
+ return value;
110
+ }
111
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
112
+ return String(value);
113
+ }
114
+ return JSON.stringify(value);
115
+ }
116
+ var DERIVE_BUILTINS = {
117
+ concat: (...args) => args.map((value) => stringify(value)).join(""),
118
+ coalesce: (...args) => args.find((value) => value !== void 0 && value !== null),
119
+ when: (condition, whenTrue, whenFalse) => condition ? whenTrue : whenFalse,
120
+ exists: (value) => value !== void 0 && value !== null,
121
+ eq: (left, right) => left === right,
122
+ ne: (left, right) => left !== right
123
+ };
124
+
125
+ // ../core/src/derive/depGraph.ts
126
+ function detectDerivationCycles(dependencyMap) {
127
+ const visiting = /* @__PURE__ */ new Set();
128
+ const visited = /* @__PURE__ */ new Set();
129
+ const stack = [];
130
+ const visit = (key) => {
131
+ if (visited.has(key)) {
132
+ return;
133
+ }
134
+ if (visiting.has(key)) {
135
+ const start = stack.indexOf(key);
136
+ const cycle = [...stack.slice(start), key].join(" -> ");
137
+ throw new CnosDerivedCycleError(`Derivation cycle detected: ${cycle}`);
138
+ }
139
+ visiting.add(key);
140
+ stack.push(key);
141
+ for (const dependency of dependencyMap.get(key) ?? []) {
142
+ visit(dependency);
143
+ }
144
+ stack.pop();
145
+ visiting.delete(key);
146
+ visited.add(key);
147
+ };
148
+ for (const key of dependencyMap.keys()) {
149
+ visit(key);
150
+ }
151
+ }
152
+
153
+ // ../core/src/derive/parser.ts
154
+ function isWhitespace(value) {
155
+ return value === " " || value === "\n" || value === "\r" || value === " ";
156
+ }
157
+ function skipWhitespace(state) {
158
+ while (isWhitespace(state.source[state.index])) {
159
+ state.index += 1;
160
+ }
161
+ }
162
+ function isIdentifierStart(value) {
163
+ return typeof value === "string" && /[A-Za-z_]/.test(value);
164
+ }
165
+ function isIdentifierPart(value) {
166
+ return typeof value === "string" && /[A-Za-z0-9_.-]/.test(value);
167
+ }
168
+ function errorAt(state, message) {
169
+ throw new CnosDerivedExpressionError(`${message} at position ${state.index + 1}`, state.source);
170
+ }
171
+ function parseStringLiteral(state) {
172
+ const quote = state.source[state.index];
173
+ if (quote !== "'") {
174
+ errorAt(state, "Expected string literal");
175
+ }
176
+ state.index += 1;
177
+ let value = "";
178
+ while (state.index < state.source.length) {
179
+ const current = state.source[state.index];
180
+ if (current === "\\") {
181
+ const next = state.source[state.index + 1];
182
+ if (next === void 0) {
183
+ errorAt(state, "Unterminated escape sequence");
184
+ }
185
+ value += next;
186
+ state.index += 2;
187
+ continue;
188
+ }
189
+ if (current === "'") {
190
+ state.index += 1;
191
+ return {
192
+ type: "literal",
193
+ value
194
+ };
195
+ }
196
+ value += current;
197
+ state.index += 1;
198
+ }
199
+ errorAt(state, "Unterminated string literal");
200
+ }
201
+ function parseNumberLiteral(state) {
202
+ const start = state.index;
203
+ while (/[0-9]/.test(state.source[state.index] ?? "")) {
204
+ state.index += 1;
205
+ }
206
+ if (state.source[state.index] === ".") {
207
+ state.index += 1;
208
+ while (/[0-9]/.test(state.source[state.index] ?? "")) {
209
+ state.index += 1;
210
+ }
211
+ }
212
+ return {
213
+ type: "literal",
214
+ value: Number(state.source.slice(start, state.index))
215
+ };
216
+ }
217
+ function parseIdentifier(state) {
218
+ if (!isIdentifierStart(state.source[state.index])) {
219
+ errorAt(state, "Expected identifier");
220
+ }
221
+ const start = state.index;
222
+ state.index += 1;
223
+ while (isIdentifierPart(state.source[state.index])) {
224
+ state.index += 1;
225
+ }
226
+ return state.source.slice(start, state.index);
227
+ }
228
+ function parseArguments(state) {
229
+ const args = [];
230
+ skipWhitespace(state);
231
+ if (state.source[state.index] === ")") {
232
+ state.index += 1;
233
+ return args;
234
+ }
235
+ while (state.index < state.source.length) {
236
+ args.push(parseExpressionNode(state));
237
+ skipWhitespace(state);
238
+ const current = state.source[state.index];
239
+ if (current === ",") {
240
+ state.index += 1;
241
+ skipWhitespace(state);
242
+ continue;
243
+ }
244
+ if (current === ")") {
245
+ state.index += 1;
246
+ return args;
247
+ }
248
+ errorAt(state, 'Expected "," or ")"');
249
+ }
250
+ errorAt(state, "Unterminated function call");
251
+ }
252
+ function parseIdentifierOrCall(state) {
253
+ const identifier = parseIdentifier(state);
254
+ skipWhitespace(state);
255
+ if (state.source[state.index] === "(") {
256
+ if (!(identifier in DERIVE_BUILTINS)) {
257
+ throw new CnosDerivedExpressionError(`Unknown derive function: ${identifier}`, state.source);
258
+ }
259
+ state.index += 1;
260
+ return {
261
+ type: "call",
262
+ name: identifier,
263
+ args: parseArguments(state)
264
+ };
265
+ }
266
+ if (identifier === "true" || identifier === "false") {
267
+ return {
268
+ type: "literal",
269
+ value: identifier === "true"
270
+ };
271
+ }
272
+ if (identifier === "null") {
273
+ return {
274
+ type: "literal",
275
+ value: null
276
+ };
277
+ }
278
+ return {
279
+ type: "ref",
280
+ path: identifier
281
+ };
282
+ }
283
+ function parseExpressionNode(state) {
284
+ skipWhitespace(state);
285
+ const current = state.source[state.index];
286
+ if (current === "'") {
287
+ return parseStringLiteral(state);
288
+ }
289
+ if (/[0-9]/.test(current ?? "")) {
290
+ return parseNumberLiteral(state);
291
+ }
292
+ if (isIdentifierStart(current)) {
293
+ return parseIdentifierOrCall(state);
294
+ }
295
+ errorAt(state, "Unexpected token");
296
+ }
297
+ function parseExpression(source) {
298
+ const state = {
299
+ source,
300
+ index: 0
301
+ };
302
+ const ast = parseExpressionNode(state);
303
+ skipWhitespace(state);
304
+ if (state.index !== source.length) {
305
+ errorAt(state, "Unexpected trailing input");
306
+ }
307
+ return ast;
308
+ }
309
+
310
+ // ../core/src/derive/templateParser.ts
311
+ function toLiteral(value) {
312
+ return {
313
+ type: "literal",
314
+ value
315
+ };
316
+ }
317
+ function toRef(path16) {
318
+ return {
319
+ type: "ref",
320
+ path: path16
321
+ };
322
+ }
323
+ function parseTemplate(source) {
324
+ const parts = [];
325
+ let cursor = 0;
326
+ while (cursor < source.length) {
327
+ const start = source.indexOf("${", cursor);
328
+ if (start < 0) {
329
+ if (cursor < source.length) {
330
+ parts.push(toLiteral(source.slice(cursor)));
331
+ }
332
+ break;
333
+ }
334
+ if (start > cursor) {
335
+ parts.push(toLiteral(source.slice(cursor, start)));
336
+ }
337
+ const end = source.indexOf("}", start + 2);
338
+ if (end < 0) {
339
+ throw new CnosDerivedExpressionError(`Invalid derivation template: unclosed \${...} at position ${start + 1}`, source);
340
+ }
341
+ const ref = source.slice(start + 2, end).trim();
342
+ if (!ref) {
343
+ throw new CnosDerivedExpressionError(`Invalid derivation template: empty reference at position ${start + 1}`, source);
344
+ }
345
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(ref)) {
346
+ throw new CnosDerivedExpressionError(`Invalid derivation template reference "${ref}"`, source);
347
+ }
348
+ parts.push(toRef(ref));
349
+ cursor = end + 1;
350
+ }
351
+ if (parts.length === 0) {
352
+ return toLiteral("");
353
+ }
354
+ if (parts.length === 1) {
355
+ return parts[0];
356
+ }
357
+ return {
358
+ type: "call",
359
+ name: "concat",
360
+ args: parts
361
+ };
362
+ }
363
+
364
+ // ../core/src/derive/evaluator.ts
365
+ function isDerivedValue(value) {
366
+ return Boolean(
367
+ value && typeof value === "object" && !Array.isArray(value) && "$derive" in value
368
+ );
369
+ }
370
+ function extractRefs(node, refs = /* @__PURE__ */ new Set()) {
371
+ if (node.type === "ref") {
372
+ refs.add(node.path);
373
+ return refs;
374
+ }
375
+ if (node.type === "call") {
376
+ for (const arg of node.args) {
377
+ extractRefs(arg, refs);
378
+ }
379
+ }
380
+ return refs;
381
+ }
382
+ function parseDerivation(value) {
383
+ const source = typeof value.$derive === "string" ? value.$derive : value.$derive?.expr;
384
+ if (typeof source !== "string") {
385
+ throw new CnosDerivedExpressionError("Derived value requires either a template string or { expr } object");
386
+ }
387
+ const type = typeof value.$derive === "string" ? "template" : "expression";
388
+ const ast = type === "template" ? parseTemplate(source) : parseExpression(source);
389
+ const refs = Array.from(extractRefs(ast)).sort((left, right) => left.localeCompare(right));
390
+ return {
391
+ type,
392
+ raw: source,
393
+ ast,
394
+ refs,
395
+ runtimeRefs: [],
396
+ isRuntimeDependent: false
397
+ };
398
+ }
399
+ function normalizeConcatValue(value) {
400
+ if (value === void 0 || value === null) {
401
+ return "";
402
+ }
403
+ if (typeof value === "string") {
404
+ return value;
405
+ }
406
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
407
+ return String(value);
408
+ }
409
+ return JSON.stringify(value);
410
+ }
411
+ function evaluateNode(node, resolveRef) {
412
+ switch (node.type) {
413
+ case "literal":
414
+ return node.value;
415
+ case "ref":
416
+ return resolveRef(node.path);
417
+ case "call": {
418
+ const args = node.args.map((arg) => evaluateNode(arg, resolveRef));
419
+ switch (node.name) {
420
+ case "concat":
421
+ return args.map((value) => normalizeConcatValue(value)).join("");
422
+ case "coalesce":
423
+ return DERIVE_BUILTINS.coalesce(...args);
424
+ case "when":
425
+ return DERIVE_BUILTINS.when(args[0], args[1], args[2]);
426
+ case "exists":
427
+ return DERIVE_BUILTINS.exists(args[0]);
428
+ case "eq":
429
+ return DERIVE_BUILTINS.eq(args[0], args[1]);
430
+ case "ne":
431
+ return DERIVE_BUILTINS.ne(args[0], args[1]);
432
+ default:
433
+ throw new CnosDerivedExpressionError(`Unknown derive function: ${String(node.name)}`);
434
+ }
435
+ }
436
+ default:
437
+ throw new CnosDerivedExpressionError(`Unsupported derive AST node ${node.type ?? "unknown"}`);
438
+ }
439
+ }
440
+ function evaluateDerivation(options) {
441
+ const missingRefs = /* @__PURE__ */ new Set();
442
+ const value = evaluateNode(options.parsed.ast, (ref) => {
443
+ const resolved = options.resolveRef(ref);
444
+ if (resolved === void 0) {
445
+ missingRefs.add(ref);
446
+ options.onMissing?.(ref);
447
+ }
448
+ return resolved;
449
+ });
450
+ if (missingRefs.size > 0 && options.parsed.ast.type === "ref") {
451
+ throw new CnosDerivedResolutionError(
452
+ options.key,
453
+ `Unable to resolve derived config key ${options.key} because ${Array.from(missingRefs).join(", ")} is missing.`
454
+ );
455
+ }
456
+ return value;
457
+ }
458
+
459
+ // ../core/src/derive/validate.ts
460
+ var FORBIDDEN_TARGET_NAMESPACES = /* @__PURE__ */ new Set(["public", "meta", "secret"]);
461
+ var FORBIDDEN_REF_NAMESPACES = /* @__PURE__ */ new Set(["public", "secret"]);
462
+ function validateDerivedTargetNamespace(manifest, namespace) {
463
+ if (FORBIDDEN_TARGET_NAMESPACES.has(namespace)) {
464
+ throw new CnosManifestError(`Cannot define derived values under namespace "${namespace}".`);
465
+ }
466
+ if (manifest.runtimeNamespaces[namespace]) {
467
+ throw new CnosManifestError(`Cannot define derived values under runtime namespace "${namespace}".`);
468
+ }
469
+ }
470
+ function validateParsedDerivation(manifest, parsed) {
471
+ for (const ref of parsed.refs) {
472
+ const namespace = ref.split(".")[0] ?? "";
473
+ if (FORBIDDEN_REF_NAMESPACES.has(namespace)) {
474
+ throw new CnosDerivedExpressionError(`Derived expressions cannot reference ${namespace}.* keys.`, parsed.raw);
475
+ }
476
+ if (manifest.runtimeNamespaces[namespace]) {
477
+ continue;
478
+ }
479
+ if (!manifest.namespaces[namespace] && namespace !== "value" && namespace !== "meta") {
480
+ throw new CnosDerivedExpressionError(`Unknown derive reference namespace: ${namespace}`, parsed.raw);
481
+ }
482
+ }
483
+ }
484
+
485
+ // ../core/src/derive/runtime.ts
486
+ function namespaceForKey(key) {
487
+ return key.split(".")[0] ?? "";
488
+ }
489
+ function dependencyNamespaces(key, entries, memo) {
490
+ if (memo.has(key)) {
491
+ return memo.get(key);
492
+ }
493
+ const entry = entries.get(key);
494
+ if (!entry) {
495
+ memo.set(key, []);
496
+ return [];
497
+ }
498
+ const namespaces = /* @__PURE__ */ new Set();
499
+ for (const ref of entry.parsed.refs) {
500
+ const namespace = namespaceForKey(ref);
501
+ if (!entries.has(ref)) {
502
+ namespaces.add(namespace);
503
+ continue;
504
+ }
505
+ for (const dependencyNamespace of dependencyNamespaces(ref, entries, memo)) {
506
+ namespaces.add(dependencyNamespace);
507
+ }
508
+ }
509
+ const result = Array.from(namespaces).sort((left, right) => left.localeCompare(right));
510
+ memo.set(key, result);
511
+ return result;
512
+ }
513
+ function isRuntimeDependentKey(key, entries, manifest, memo) {
514
+ if (memo.has(key)) {
515
+ return memo.get(key);
516
+ }
517
+ const entry = entries.get(key);
518
+ if (!entry) {
519
+ memo.set(key, false);
520
+ return false;
521
+ }
522
+ for (const ref of entry.parsed.refs) {
523
+ const namespace = namespaceForKey(ref);
524
+ if (manifest.runtimeNamespaces[namespace]) {
525
+ memo.set(key, true);
526
+ return true;
527
+ }
528
+ if (entries.has(ref) && isRuntimeDependentKey(ref, entries, manifest, memo)) {
529
+ memo.set(key, true);
530
+ return true;
531
+ }
532
+ }
533
+ memo.set(key, false);
534
+ return false;
535
+ }
536
+ function prepareEntries(graph, manifest) {
537
+ const entries = /* @__PURE__ */ new Map();
538
+ for (const [key, entry] of graph.entries) {
539
+ if (!isDerivedValue(entry.value)) {
540
+ continue;
541
+ }
542
+ const namespaceDefinition = manifest.namespaces[entry.namespace];
543
+ if (!namespaceDefinition || namespaceDefinition.kind === "data") {
544
+ validateDerivedTargetNamespace(manifest, entry.namespace);
545
+ }
546
+ const parsed = parseDerivation(entry.value);
547
+ validateParsedDerivation(manifest, parsed);
548
+ entries.set(key, {
549
+ key,
550
+ namespace: entry.namespace,
551
+ value: entry.value,
552
+ parsed
553
+ });
554
+ }
555
+ detectDerivationCycles(
556
+ new Map(
557
+ Array.from(entries.values()).map((entry) => [
558
+ entry.key,
559
+ entry.parsed.refs.filter((ref) => entries.has(ref))
560
+ ])
561
+ )
562
+ );
563
+ const runtimeMemo = /* @__PURE__ */ new Map();
564
+ const namespaceMemo = /* @__PURE__ */ new Map();
565
+ for (const entry of entries.values()) {
566
+ entry.parsed.isRuntimeDependent = isRuntimeDependentKey(entry.key, entries, manifest, runtimeMemo);
567
+ entry.parsed.runtimeRefs = entry.parsed.refs.filter((ref) => manifest.runtimeNamespaces[namespaceForKey(ref)]);
568
+ if (entry.parsed.runtimeRefs.length === 0 && entry.parsed.isRuntimeDependent) {
569
+ entry.parsed.runtimeRefs = dependencyNamespaces(entry.key, entries, namespaceMemo).filter((namespace) => manifest.runtimeNamespaces[namespace]).map((namespace) => `${namespace}.*`);
570
+ }
571
+ }
572
+ return entries;
573
+ }
574
+ function createDerivedRuntimeSupport(graph, manifest, runtimeProviders) {
575
+ const entries = prepareEntries(graph, manifest);
576
+ const configCache = /* @__PURE__ */ new Map();
577
+ const runtimeDependencyMemo = /* @__PURE__ */ new Map();
578
+ const support = {};
579
+ Object.assign(support, {
580
+ runtimeProviders,
581
+ read(key, readBase) {
582
+ const namespace = namespaceForKey(key);
583
+ if (runtimeProviders.has(namespace)) {
584
+ const provider = runtimeProviders.get(namespace);
585
+ return provider(key.slice(namespace.length + 1));
586
+ }
587
+ if (entries.has(key)) {
588
+ return readDerived(key, readBase);
589
+ }
590
+ return readBase(key);
591
+ },
592
+ describe(key, readBase) {
593
+ const entry = entries.get(key);
594
+ if (!entry) {
595
+ return void 0;
596
+ }
597
+ const runtimeNamespaces = Array.from(
598
+ new Set(
599
+ entry.parsed.refs.map((ref) => namespaceForKey(ref)).filter((namespace) => manifest.runtimeNamespaces[namespace])
600
+ )
601
+ ).sort((left, right) => left.localeCompare(right));
602
+ return {
603
+ key,
604
+ type: entry.parsed.type,
605
+ expression: entry.parsed.raw,
606
+ dependencies: entry.parsed.refs.map((ref) => {
607
+ const namespace = namespaceForKey(ref);
608
+ return {
609
+ key: ref,
610
+ value: support.read(ref, readBase),
611
+ ...manifest.runtimeNamespaces[namespace] ? {
612
+ runtimeNamespace: namespace
613
+ } : {}
614
+ };
615
+ }),
616
+ runtimeDependent: entry.parsed.isRuntimeDependent,
617
+ runtimeNamespaces,
618
+ ...entry.parsed.isRuntimeDependent ? {
619
+ promotionWarning: "Cannot be promoted to browser/public."
620
+ } : {}
621
+ };
622
+ },
623
+ isDerivedKey(key) {
624
+ return entries.has(key);
625
+ },
626
+ isRuntimeDependentKey(key) {
627
+ if (runtimeDependencyMemo.has(key)) {
628
+ return runtimeDependencyMemo.get(key);
629
+ }
630
+ const value = entries.get(key)?.parsed.isRuntimeDependent ?? false;
631
+ runtimeDependencyMemo.set(key, value);
632
+ return value;
633
+ },
634
+ toConcreteValue(key, readBase, mode) {
635
+ const entry = entries.get(key);
636
+ if (!entry) {
637
+ return support.read(key, readBase);
638
+ }
639
+ if (!entry.parsed.isRuntimeDependent) {
640
+ return support.read(key, readBase);
641
+ }
642
+ if (mode === "server" || mode === "runtime") {
643
+ return support.read(key, readBase);
644
+ }
645
+ for (const ref of entry.parsed.refs) {
646
+ const namespace = namespaceForKey(ref);
647
+ const runtimeNamespace = manifest.runtimeNamespaces[namespace];
648
+ if (!runtimeNamespace) {
649
+ continue;
650
+ }
651
+ if (runtimeNamespace.serverOnly) {
652
+ throw new CnosDerivedResolutionError(
653
+ key,
654
+ `Cannot resolve ${key} for ${mode} output because it depends on runtime namespace ${namespace}.`
655
+ );
656
+ }
657
+ if (!runtimeProviders.has(namespace)) {
658
+ if (mode === "env") {
659
+ return void 0;
660
+ }
661
+ throw new CnosDerivedResolutionError(
662
+ key,
663
+ `Cannot resolve ${key} for ${mode} output because runtime namespace ${namespace} has no registered provider.`
664
+ );
665
+ }
666
+ }
667
+ return support.read(key, readBase);
668
+ },
669
+ toServerFormula(key) {
670
+ const entry = entries.get(key);
671
+ if (!entry || !entry.parsed.isRuntimeDependent) {
672
+ return void 0;
673
+ }
674
+ return {
675
+ expr: entry.parsed.raw,
676
+ deps: entry.parsed.refs.filter((ref) => !manifest.runtimeNamespaces[namespaceForKey(ref)]),
677
+ runtimeRefs: entry.parsed.refs.filter((ref) => manifest.runtimeNamespaces[namespaceForKey(ref)])
678
+ };
679
+ },
680
+ derivedKeys: Array.from(entries.keys()).sort((left, right) => left.localeCompare(right))
681
+ });
682
+ const readDerived = (key, readBase, evaluationStack = /* @__PURE__ */ new Set()) => {
683
+ const entry = entries.get(key);
684
+ if (!entry) {
685
+ return readBase(key);
686
+ }
687
+ if (!entry.parsed.isRuntimeDependent && configCache.has(key)) {
688
+ return configCache.get(key);
689
+ }
690
+ const value = evaluateDerivation({
691
+ key,
692
+ parsed: entry.parsed,
693
+ resolveRef: (ref) => {
694
+ const namespace = namespaceForKey(ref);
695
+ if (runtimeProviders.has(namespace)) {
696
+ const provider = runtimeProviders.get(namespace);
697
+ return provider(ref.slice(namespace.length + 1));
698
+ }
699
+ if (entries.has(ref)) {
700
+ if (evaluationStack.has(ref)) {
701
+ throw new CnosDerivedResolutionError(key, `Unable to resolve derived config key ${key} because of a recursive dependency on ${ref}.`);
702
+ }
703
+ evaluationStack.add(ref);
704
+ const resolved = readDerived(ref, readBase, evaluationStack);
705
+ evaluationStack.delete(ref);
706
+ return resolved;
707
+ }
708
+ return readBase(ref);
709
+ }
710
+ });
711
+ if (!entry.parsed.isRuntimeDependent) {
712
+ configCache.set(key, value);
713
+ }
714
+ return value;
715
+ };
716
+ for (const key of Array.from(entries.keys()).sort((left, right) => left.localeCompare(right))) {
717
+ const entry = entries.get(key);
718
+ if (!entry.parsed.isRuntimeDependent) {
719
+ readDerived(key, (ref) => graph.entries.get(ref)?.value);
720
+ }
721
+ }
722
+ return support;
723
+ }
724
+ function registerRuntimeProvider(manifest, runtimeProviders, namespace, provider) {
725
+ const definition = manifest.runtimeNamespaces[namespace];
726
+ if (!definition) {
727
+ throw new CnosRuntimeProviderError(`Cannot register runtime provider for undeclared namespace "${namespace}".`);
728
+ }
729
+ if (definition.builtIn) {
730
+ throw new CnosRuntimeProviderError(`Cannot override built-in runtime namespace "${namespace}".`);
731
+ }
732
+ runtimeProviders.set(namespace, provider);
733
+ }
78
734
 
79
735
  // ../core/src/runtime/inspect.ts
80
- function inspectValue(graph, key) {
736
+ function inspectValue(graph, key, helpers = {}) {
81
737
  const entry = graph.entries.get(key);
82
738
  if (!entry) {
83
739
  throw new CnosKeyNotFoundError(key);
84
740
  }
741
+ const derived = helpers.describeDerived?.(key);
85
742
  return {
86
743
  key: entry.key,
87
- value: entry.value,
744
+ value: helpers.read ? helpers.read(entry.key) : entry.value,
88
745
  namespace: entry.namespace,
89
746
  profile: graph.profile,
90
747
  profileSource: graph.profileSource,
@@ -105,7 +762,10 @@ function inspectValue(graph, key) {
105
762
  workspaceId: override.workspaceId,
106
763
  value: override.value,
107
764
  ...override.origin ? { origin: override.origin } : {}
108
- }))
765
+ })),
766
+ ...derived ? {
767
+ derived
768
+ } : {}
109
769
  };
110
770
  }
111
771
 
@@ -183,17 +843,17 @@ async function readKeychain(entry) {
183
843
  }
184
844
 
185
845
  // ../core/src/manifest/loadManifest.ts
186
- var import_promises3 = require("fs/promises");
187
- var import_node_path3 = __toESM(require("path"), 1);
846
+ var import_promises5 = require("fs/promises");
847
+ var import_node_path6 = __toESM(require("path"), 1);
188
848
 
189
849
  // ../core/src/utils/path.ts
190
- var import_promises2 = require("fs/promises");
191
- var import_node_os = __toESM(require("os"), 1);
192
- var import_node_path2 = __toESM(require("path"), 1);
850
+ var import_promises4 = require("fs/promises");
851
+ var import_node_os3 = __toESM(require("os"), 1);
852
+ var import_node_path5 = __toESM(require("path"), 1);
193
853
 
194
854
  // ../core/src/discovery/findCnosrc.ts
195
- var import_promises = require("fs/promises");
196
- var import_node_path = __toESM(require("path"), 1);
855
+ var import_promises3 = require("fs/promises");
856
+ var import_node_path4 = __toESM(require("path"), 1);
197
857
 
198
858
  // ../core/src/utils/yaml.ts
199
859
  var import_yaml = require("yaml");
@@ -204,10 +864,315 @@ function stringifyYaml(value) {
204
864
  return (0, import_yaml.stringify)(value);
205
865
  }
206
866
 
867
+ // ../core/src/discovery/resolveRoot.ts
868
+ var import_promises2 = require("fs/promises");
869
+ var import_node_path3 = __toESM(require("path"), 1);
870
+ var import_node_child_process4 = require("child_process");
871
+ var import_node_os2 = __toESM(require("os"), 1);
872
+
873
+ // ../core/src/discovery/parseGitUri.ts
874
+ function isGitRootUri(uri) {
875
+ return /^git\+[a-z]+:\/\//i.test(uri);
876
+ }
877
+ function parseGitUri(uri) {
878
+ if (!isGitRootUri(uri)) {
879
+ throw new CnosDiscoveryError(`Unsupported git root URI: ${uri}`);
880
+ }
881
+ const withoutPrefix = uri.slice("git+".length);
882
+ const hashIndex = withoutPrefix.indexOf("#");
883
+ if (hashIndex < 0) {
884
+ throw new CnosDiscoveryError(
885
+ `Git root URI must include a #ref (tag, branch, or commit). Got: ${uri}`
886
+ );
887
+ }
888
+ const cloneUrl = withoutPrefix.slice(0, hashIndex);
889
+ const fragment = withoutPrefix.slice(hashIndex + 1);
890
+ const separatorIndex = fragment.indexOf(":");
891
+ const ref = (separatorIndex >= 0 ? fragment.slice(0, separatorIndex) : fragment).trim();
892
+ const subpath = (separatorIndex >= 0 ? fragment.slice(separatorIndex + 1) : ".cnos").trim() || ".cnos";
893
+ const protocol = cloneUrl.slice(0, cloneUrl.indexOf("://"));
894
+ if (!cloneUrl || !ref) {
895
+ throw new CnosDiscoveryError(
896
+ `Git root URI must include both a clone URL and #ref. Got: ${uri}`
897
+ );
898
+ }
899
+ return {
900
+ uri,
901
+ cloneUrl,
902
+ ref,
903
+ subpath,
904
+ transport: protocol === "https" ? "https" : protocol === "ssh" ? "ssh" : protocol === "file" ? "file" : "custom"
905
+ };
906
+ }
907
+
908
+ // ../core/src/discovery/cache/cacheManager.ts
909
+ var SEMVER_TAG_RE = /^v?\d+\.\d+(?:\.\d+)?(?:[-+][A-Za-z0-9.-]+)?$/;
910
+ var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
911
+ function isImmutableGitRef(ref) {
912
+ return SEMVER_TAG_RE.test(ref) || COMMIT_SHA_RE.test(ref);
913
+ }
914
+ function resolveRemoteRootCacheTtlSeconds(mode = "runtime", processEnv = process.env, override) {
915
+ if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
916
+ return override;
917
+ }
918
+ const fromEnv = Number(processEnv.CNOS_CACHE_TTL ?? "");
919
+ if (Number.isFinite(fromEnv) && fromEnv >= 0) {
920
+ return fromEnv;
921
+ }
922
+ switch (mode) {
923
+ case "build":
924
+ return 0;
925
+ case "dev":
926
+ return 30;
927
+ case "runtime":
928
+ default:
929
+ return 300;
930
+ }
931
+ }
932
+ function isRemoteRootCacheFresh(metadata, options) {
933
+ if (!metadata || options.forceRefresh) {
934
+ return false;
935
+ }
936
+ if (metadata.uri !== options.uri || metadata.ref !== options.ref) {
937
+ return false;
938
+ }
939
+ if (metadata.isImmutable) {
940
+ return true;
941
+ }
942
+ const ttlSeconds = resolveRemoteRootCacheTtlSeconds(
943
+ options.mode,
944
+ options.processEnv,
945
+ options.ttlSeconds
946
+ );
947
+ if (ttlSeconds <= 0) {
948
+ return false;
949
+ }
950
+ const cachedAtMs = Date.parse(metadata.cachedAt);
951
+ if (Number.isNaN(cachedAtMs)) {
952
+ return false;
953
+ }
954
+ return Date.now() - cachedAtMs <= ttlSeconds * 1e3;
955
+ }
956
+
957
+ // ../core/src/discovery/cache/cacheMetadata.ts
958
+ var import_promises = require("fs/promises");
959
+ var import_node_path = __toESM(require("path"), 1);
960
+ async function readRemoteRootCacheMetadata(metaPath) {
961
+ try {
962
+ const source = await (0, import_promises.readFile)(metaPath, "utf8");
963
+ const parsed = JSON.parse(source);
964
+ if (!parsed || typeof parsed.uri !== "string" || typeof parsed.cloneUrl !== "string" || typeof parsed.ref !== "string" || typeof parsed.subpath !== "string" || typeof parsed.resolvedCommit !== "string" || typeof parsed.cachedAt !== "string" || typeof parsed.isImmutable !== "boolean") {
965
+ return void 0;
966
+ }
967
+ return parsed;
968
+ } catch {
969
+ return void 0;
970
+ }
971
+ }
972
+ async function writeRemoteRootCacheMetadata(metaPath, metadata) {
973
+ await (0, import_promises.mkdir)(import_node_path.default.dirname(metaPath), { recursive: true });
974
+ await (0, import_promises.writeFile)(metaPath, `${JSON.stringify(metadata, null, 2)}
975
+ `, "utf8");
976
+ }
977
+
978
+ // ../core/src/discovery/cache/cachePaths.ts
979
+ var import_node_crypto = require("crypto");
980
+ var import_node_os = __toESM(require("os"), 1);
981
+ var import_node_path2 = __toESM(require("path"), 1);
982
+ function resolveCnosCacheRoot(processEnv = process.env) {
983
+ return import_node_path2.default.resolve(
984
+ expandHomePath(processEnv.CNOS_CACHE_DIR ?? import_node_path2.default.join(import_node_os.default.homedir(), ".cnos", "cache"))
985
+ );
986
+ }
987
+ function createRemoteRootCacheKey(uri) {
988
+ return (0, import_node_crypto.createHash)("sha256").update(uri).digest("hex");
989
+ }
990
+ function resolveRemoteRootCachePaths(uri, processEnv = process.env) {
991
+ const cacheRoot = resolveCnosCacheRoot(processEnv);
992
+ const cacheDir = import_node_path2.default.join(cacheRoot, "roots", createRemoteRootCacheKey(uri));
993
+ return {
994
+ cacheRoot,
995
+ cacheDir,
996
+ repoDir: import_node_path2.default.join(cacheDir, "repo"),
997
+ metaPath: import_node_path2.default.join(cacheDir, ".cnos-cache-meta.json")
998
+ };
999
+ }
1000
+
1001
+ // ../core/src/discovery/resolveRoot.ts
1002
+ async function pathExists(targetPath) {
1003
+ try {
1004
+ await (0, import_promises2.access)(targetPath);
1005
+ return true;
1006
+ } catch {
1007
+ return false;
1008
+ }
1009
+ }
1010
+ function expandHomePath2(targetPath) {
1011
+ if (targetPath === "~") {
1012
+ return import_node_os2.default.homedir();
1013
+ }
1014
+ if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
1015
+ return import_node_path3.default.join(import_node_os2.default.homedir(), targetPath.slice(2));
1016
+ }
1017
+ return targetPath;
1018
+ }
1019
+ function isLocalRootUri(rootUri) {
1020
+ return !isGitRootUri2(rootUri) && !isCnosHostedRootUri(rootUri);
1021
+ }
1022
+ function isCnosHostedRootUri(rootUri) {
1023
+ return rootUri.startsWith("cnos://");
1024
+ }
1025
+ function isGitRootUri2(rootUri) {
1026
+ return rootUri.startsWith("git+");
1027
+ }
1028
+ async function runGitCommand(args, options = {}) {
1029
+ return new Promise((resolve, reject) => {
1030
+ const child = (0, import_node_child_process4.spawn)("git", args, {
1031
+ cwd: options.cwd,
1032
+ env: options.processEnv ?? process.env,
1033
+ shell: process.platform === "win32",
1034
+ stdio: ["ignore", "pipe", "pipe"]
1035
+ });
1036
+ let stdout = "";
1037
+ let stderr = "";
1038
+ child.stdout.on("data", (chunk) => {
1039
+ stdout += chunk.toString();
1040
+ });
1041
+ child.stderr.on("data", (chunk) => {
1042
+ stderr += chunk.toString();
1043
+ });
1044
+ child.on("error", (error) => {
1045
+ reject(
1046
+ new CnosDiscoveryError(
1047
+ `Failed to run git. Make sure git is installed and available on PATH. ${error.message}`
1048
+ )
1049
+ );
1050
+ });
1051
+ child.on("close", (code) => {
1052
+ if (code === 0) {
1053
+ resolve(stdout.trim());
1054
+ return;
1055
+ }
1056
+ const details = stderr.trim() || stdout.trim();
1057
+ reject(
1058
+ new CnosDiscoveryError(
1059
+ details ? `Git command failed: ${details}` : `Git command failed with exit code ${code ?? 1}`
1060
+ )
1061
+ );
1062
+ });
1063
+ });
1064
+ }
1065
+ async function resolveLocalRoot(rootUri, cnosrcDir) {
1066
+ const candidateRoot = rootUri.startsWith("~") ? expandHomePath2(rootUri) : rootUri;
1067
+ const manifestRoot = import_node_path3.default.resolve(cnosrcDir, candidateRoot);
1068
+ const manifestPath = import_node_path3.default.join(manifestRoot, "cnos.yml");
1069
+ if (!await pathExists(manifestPath)) {
1070
+ throw new CnosDiscoveryError(`.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`);
1071
+ }
1072
+ return {
1073
+ manifestRoot,
1074
+ resolution: {
1075
+ rootUri,
1076
+ protocol: "local",
1077
+ remote: false,
1078
+ readOnly: false
1079
+ }
1080
+ };
1081
+ }
1082
+ async function ensureGitCheckout(parsed, repoDir, processEnv) {
1083
+ const hasRepo = await pathExists(import_node_path3.default.join(repoDir, ".git"));
1084
+ if (!hasRepo) {
1085
+ await (0, import_promises2.mkdir)(import_node_path3.default.dirname(repoDir), { recursive: true });
1086
+ await runGitCommand(["clone", "--no-checkout", parsed.cloneUrl, repoDir], { processEnv });
1087
+ } else {
1088
+ await runGitCommand(["-C", repoDir, "remote", "set-url", "origin", parsed.cloneUrl], {
1089
+ processEnv
1090
+ });
1091
+ }
1092
+ await runGitCommand(["-C", repoDir, "fetch", "--tags", "--force", "origin"], { processEnv });
1093
+ await runGitCommand(["-C", repoDir, "checkout", "--force", parsed.ref], { processEnv });
1094
+ await runGitCommand(["-C", repoDir, "clean", "-fdx"], { processEnv });
1095
+ }
1096
+ async function resolveGitRoot(rootUri, options = {}) {
1097
+ const processEnv = options.processEnv ?? process.env;
1098
+ const parsed = parseGitUri(rootUri);
1099
+ const cachePaths = resolveRemoteRootCachePaths(rootUri, processEnv);
1100
+ const metadata = await readRemoteRootCacheMetadata(cachePaths.metaPath);
1101
+ const immutable = isImmutableGitRef(parsed.ref);
1102
+ const cacheFresh = isRemoteRootCacheFresh(metadata, {
1103
+ uri: rootUri,
1104
+ ref: parsed.ref,
1105
+ ...options.cacheMode ? { mode: options.cacheMode } : {},
1106
+ processEnv,
1107
+ ...typeof options.cacheTtlSeconds === "number" ? { ttlSeconds: options.cacheTtlSeconds } : {},
1108
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1109
+ });
1110
+ if (!cacheFresh) {
1111
+ try {
1112
+ await ensureGitCheckout(parsed, cachePaths.repoDir, processEnv);
1113
+ } catch (error) {
1114
+ const message = error instanceof Error ? error.message : String(error);
1115
+ const authHint = parsed.transport === "ssh" ? " Check your SSH key and git access." : " Check the URL and your git credential helper or token setup.";
1116
+ throw new CnosDiscoveryError(`Failed to resolve remote git root ${rootUri}. ${message}${authHint}`);
1117
+ }
1118
+ const resolvedCommit = await runGitCommand(["-C", cachePaths.repoDir, "rev-parse", "HEAD"], {
1119
+ processEnv
1120
+ });
1121
+ await writeRemoteRootCacheMetadata(cachePaths.metaPath, {
1122
+ uri: rootUri,
1123
+ cloneUrl: parsed.cloneUrl,
1124
+ ref: parsed.ref,
1125
+ subpath: parsed.subpath,
1126
+ resolvedCommit,
1127
+ cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
1128
+ isImmutable: immutable
1129
+ });
1130
+ }
1131
+ const nextMetadata = metadata && cacheFresh ? metadata : await readRemoteRootCacheMetadata(cachePaths.metaPath);
1132
+ const manifestRoot = import_node_path3.default.join(cachePaths.repoDir, parsed.subpath);
1133
+ if (!await pathExists(import_node_path3.default.join(manifestRoot, "cnos.yml"))) {
1134
+ throw new CnosDiscoveryError(
1135
+ `Git root ${rootUri} resolved to ${manifestRoot} but no cnos.yml was found there. Check the :subpath segment.`
1136
+ );
1137
+ }
1138
+ return {
1139
+ manifestRoot,
1140
+ resolution: {
1141
+ rootUri,
1142
+ protocol: "git",
1143
+ remote: true,
1144
+ readOnly: true,
1145
+ cacheDir: cachePaths.cacheDir,
1146
+ cacheMetaPath: cachePaths.metaPath,
1147
+ ref: parsed.ref,
1148
+ subpath: parsed.subpath,
1149
+ immutable,
1150
+ ...nextMetadata?.resolvedCommit ? { resolvedCommit: nextMetadata.resolvedCommit } : {},
1151
+ ...nextMetadata?.cachedAt ? { cachedAt: nextMetadata.cachedAt } : {}
1152
+ }
1153
+ };
1154
+ }
1155
+ async function resolveRootUri(rootUri, cnosrcDir, options = {}) {
1156
+ if (isLocalRootUri(rootUri)) {
1157
+ return resolveLocalRoot(rootUri, cnosrcDir);
1158
+ }
1159
+ if (isGitRootUri2(rootUri)) {
1160
+ return resolveGitRoot(rootUri, options);
1161
+ }
1162
+ if (isCnosHostedRootUri(rootUri)) {
1163
+ throw new CnosDiscoveryError(
1164
+ `The cnos:// remote root protocol is reserved but not implemented yet. Use git+https:// or git+ssh:// for now.`
1165
+ );
1166
+ }
1167
+ throw new CnosDiscoveryError(
1168
+ `Unknown root protocol: ${rootUri}. Supported root protocols are local paths, git+https://..., and git+ssh://....`
1169
+ );
1170
+ }
1171
+
207
1172
  // ../core/src/discovery/findCnosrc.ts
208
1173
  async function exists(targetPath) {
209
1174
  try {
210
- await (0, import_promises.access)(targetPath);
1175
+ await (0, import_promises3.access)(targetPath);
211
1176
  return true;
212
1177
  } catch {
213
1178
  return false;
@@ -228,13 +1193,13 @@ function validateCnosrc(value, filePath) {
228
1193
  };
229
1194
  }
230
1195
  async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
231
- let current = import_node_path.default.resolve(startDir);
1196
+ let current = import_node_path4.default.resolve(startDir);
232
1197
  for (let depth = 0; depth <= maxLevels; depth += 1) {
233
- const candidate = import_node_path.default.join(current, ".cnosrc.yml");
1198
+ const candidate = import_node_path4.default.join(current, ".cnosrc.yml");
234
1199
  if (await exists(candidate)) {
235
1200
  return candidate;
236
1201
  }
237
- const parent = import_node_path.default.dirname(current);
1202
+ const parent = import_node_path4.default.dirname(current);
238
1203
  if (parent === current) {
239
1204
  break;
240
1205
  }
@@ -242,27 +1207,22 @@ async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
242
1207
  }
243
1208
  return void 0;
244
1209
  }
245
- async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3) {
1210
+ async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3, options = {}) {
246
1211
  const anchorPath = await findCnosrc(startDir, maxLevels);
247
1212
  if (!anchorPath) {
248
1213
  throw new CnosDiscoveryError(
249
1214
  "No .cnosrc.yml found. Run cnos init or create .cnosrc.yml in your package root."
250
1215
  );
251
1216
  }
252
- const source = await (0, import_promises.readFile)(anchorPath, "utf8");
1217
+ const source = await (0, import_promises3.readFile)(anchorPath, "utf8");
253
1218
  const parsed = validateCnosrc(parseYaml(source), anchorPath);
254
- const consumerRoot = import_node_path.default.dirname(anchorPath);
255
- const manifestRoot = import_node_path.default.resolve(consumerRoot, parsed.root);
256
- const manifestPath = import_node_path.default.join(manifestRoot, "cnos.yml");
257
- if (!await exists(manifestPath)) {
258
- throw new CnosDiscoveryError(
259
- `.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`
260
- );
261
- }
1219
+ const consumerRoot = import_node_path4.default.dirname(anchorPath);
1220
+ const resolvedRoot = await resolveRootUri(parsed.root, consumerRoot, options);
262
1221
  return {
263
1222
  anchorPath,
264
1223
  consumerRoot,
265
- manifestRoot,
1224
+ manifestRoot: resolvedRoot.manifestRoot,
1225
+ rootResolution: resolvedRoot.resolution,
266
1226
  ...parsed.workspace ? { workspace: parsed.workspace } : {}
267
1227
  };
268
1228
  }
@@ -272,21 +1232,21 @@ var PRIMARY_CNOS_DIR = ".cnos";
272
1232
  var LEGACY_CNOS_DIR = "cnos";
273
1233
  async function exists2(filePath) {
274
1234
  try {
275
- await (0, import_promises2.access)(filePath);
1235
+ await (0, import_promises4.access)(filePath);
276
1236
  return true;
277
1237
  } catch {
278
1238
  return false;
279
1239
  }
280
1240
  }
281
1241
  async function resolveCnosRoot(root = process.cwd()) {
282
- const basePath = import_node_path2.default.resolve(root);
1242
+ const basePath = import_node_path5.default.resolve(root);
283
1243
  const candidates = [
284
- import_node_path2.default.join(basePath, PRIMARY_CNOS_DIR),
285
- import_node_path2.default.join(basePath, LEGACY_CNOS_DIR),
1244
+ import_node_path5.default.join(basePath, PRIMARY_CNOS_DIR),
1245
+ import_node_path5.default.join(basePath, LEGACY_CNOS_DIR),
286
1246
  basePath
287
1247
  ];
288
1248
  for (const candidate of candidates) {
289
- if (await exists2(import_node_path2.default.join(candidate, "cnos.yml"))) {
1249
+ if (await exists2(import_node_path5.default.join(candidate, "cnos.yml"))) {
290
1250
  return candidate;
291
1251
  }
292
1252
  }
@@ -296,18 +1256,44 @@ async function resolveCnosRoot(root = process.cwd()) {
296
1256
  }
297
1257
  async function resolveManifestRoot(options = {}) {
298
1258
  if (options.root) {
1259
+ if (options.root.startsWith("git+") || options.root.startsWith("cnos://")) {
1260
+ const consumerRoot2 = import_node_path5.default.resolve(options.cwd ?? process.cwd());
1261
+ const resolvedRoot2 = await resolveRootUri(options.root, consumerRoot2, {
1262
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1263
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1264
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1265
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1266
+ });
1267
+ return {
1268
+ manifestRoot: resolvedRoot2.manifestRoot,
1269
+ consumerRoot: consumerRoot2,
1270
+ rootResolution: resolvedRoot2.resolution
1271
+ };
1272
+ }
299
1273
  const manifestRoot = await resolveCnosRoot(options.root);
300
- const resolvedRoot = import_node_path2.default.resolve(options.root);
301
- const consumerRoot = import_node_path2.default.basename(manifestRoot) === PRIMARY_CNOS_DIR || import_node_path2.default.basename(manifestRoot) === LEGACY_CNOS_DIR ? import_node_path2.default.dirname(manifestRoot) : resolvedRoot;
1274
+ const resolvedRoot = import_node_path5.default.resolve(options.root);
1275
+ const consumerRoot = import_node_path5.default.basename(manifestRoot) === PRIMARY_CNOS_DIR || import_node_path5.default.basename(manifestRoot) === LEGACY_CNOS_DIR ? import_node_path5.default.dirname(manifestRoot) : resolvedRoot;
302
1276
  return {
303
1277
  manifestRoot,
304
- consumerRoot
1278
+ consumerRoot,
1279
+ rootResolution: {
1280
+ rootUri: manifestRoot,
1281
+ protocol: "local",
1282
+ remote: false,
1283
+ readOnly: false
1284
+ }
305
1285
  };
306
1286
  }
307
- const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd());
1287
+ const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd(), 3, {
1288
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1289
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1290
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1291
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1292
+ });
308
1293
  return {
309
1294
  manifestRoot: discovered.manifestRoot,
310
1295
  consumerRoot: discovered.consumerRoot,
1296
+ rootResolution: discovered.rootResolution,
311
1297
  anchorPath: discovered.anchorPath,
312
1298
  ...discovered.workspace ? { workspace: discovered.workspace } : {}
313
1299
  };
@@ -320,10 +1306,10 @@ function interpolatePathTemplate(template, tokens) {
320
1306
  }
321
1307
  function expandHomePath(targetPath) {
322
1308
  if (targetPath === "~") {
323
- return import_node_os.default.homedir();
1309
+ return import_node_os3.default.homedir();
324
1310
  }
325
1311
  if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
326
- return import_node_path2.default.join(import_node_os.default.homedir(), targetPath.slice(2));
1312
+ return import_node_path5.default.join(import_node_os3.default.homedir(), targetPath.slice(2));
327
1313
  }
328
1314
  return targetPath;
329
1315
  }
@@ -341,7 +1327,7 @@ function stripWorkspaceTemplatePrefix(template) {
341
1327
  function resolveWorkspaceScopedPath(workspaceRoot, template, tokens) {
342
1328
  const relativeTemplate = stripWorkspaceTemplatePrefix(template);
343
1329
  const interpolated = interpolatePathTemplate(relativeTemplate, tokens);
344
- return import_node_path2.default.resolve(workspaceRoot, interpolated);
1330
+ return import_node_path5.default.resolve(workspaceRoot, interpolated);
345
1331
  }
346
1332
  function toPortablePath(targetPath) {
347
1333
  return targetPath.replace(/\\/g, "/");
@@ -407,6 +1393,13 @@ var DEFAULT_NAMESPACES = {
407
1393
  readonly: true
408
1394
  }
409
1395
  };
1396
+ var DEFAULT_RUNTIME_NAMESPACES = {
1397
+ process: {
1398
+ description: "Live process runtime values.",
1399
+ serverOnly: true,
1400
+ builtIn: true
1401
+ }
1402
+ };
410
1403
  function validateResolveFrom(resolveFrom) {
411
1404
  const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
412
1405
  for (const entry of resolveFrom) {
@@ -429,7 +1422,7 @@ function normalizeWorkspaceItems(items) {
429
1422
  }
430
1423
  function normalizeNamespaces(namespaces) {
431
1424
  const normalized = Object.fromEntries(
432
- Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
1425
+ Object.entries(namespaces ?? {}).filter(([namespace]) => namespace !== "runtime").map(([namespace, definition]) => [
433
1426
  namespace,
434
1427
  {
435
1428
  kind: definition.kind ?? "data",
@@ -445,6 +1438,29 @@ function normalizeNamespaces(namespaces) {
445
1438
  ...normalized
446
1439
  };
447
1440
  }
1441
+ function normalizeRuntimeNamespaces(namespaces) {
1442
+ const runtimeEntries = namespaces?.runtime ?? {};
1443
+ const normalized = Object.fromEntries(
1444
+ Object.entries(runtimeEntries).map(([namespace, definition]) => [
1445
+ namespace,
1446
+ {
1447
+ ...definition.description?.trim() ? {
1448
+ description: definition.description.trim()
1449
+ } : {},
1450
+ serverOnly: definition.server_only ?? true
1451
+ }
1452
+ ])
1453
+ );
1454
+ for (const namespace of Object.keys(normalized)) {
1455
+ if (DEFAULT_NAMESPACES[namespace] || namespace === "runtime") {
1456
+ throw new CnosManifestError(`Runtime namespace "${namespace}" conflicts with a built-in or reserved namespace.`);
1457
+ }
1458
+ }
1459
+ return {
1460
+ ...DEFAULT_RUNTIME_NAMESPACES,
1461
+ ...normalized
1462
+ };
1463
+ }
448
1464
  function normalizeVaults(vaults) {
449
1465
  return Object.fromEntries(
450
1466
  Object.entries(vaults ?? {}).map(([name, definition]) => {
@@ -536,6 +1552,7 @@ function normalizeManifest(manifest) {
536
1552
  const defaultProfile = manifest.profiles?.default?.trim() || "base";
537
1553
  const workspaceItems = normalizeWorkspaceItems(manifest.workspaces?.items);
538
1554
  const resolveFrom = validateResolveFrom(manifest.profiles?.resolveFrom ?? DEFAULT_RESOLVE_FROM);
1555
+ const runtimeNamespaces = normalizeRuntimeNamespaces(manifest.namespaces);
539
1556
  const filesystemValues = {
540
1557
  root: "./",
541
1558
  format: "yaml",
@@ -609,6 +1626,7 @@ function normalizeManifest(manifest) {
609
1626
  }
610
1627
  },
611
1628
  namespaces: normalizeNamespaces(manifest.namespaces),
1629
+ runtimeNamespaces,
612
1630
  vaults: normalizeVaults(manifest.vaults),
613
1631
  writePolicy: {
614
1632
  define: {
@@ -627,13 +1645,17 @@ function normalizeManifest(manifest) {
627
1645
  async function loadManifest(options = {}) {
628
1646
  const resolved = await resolveManifestRoot({
629
1647
  ...options.root ? { root: options.root } : {},
630
- ...options.cwd ? { cwd: options.cwd } : {}
1648
+ ...options.cwd ? { cwd: options.cwd } : {},
1649
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1650
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1651
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1652
+ ...options.forceRefresh ? { forceRefresh: true } : {}
631
1653
  });
632
1654
  const manifestRoot = resolved.manifestRoot;
633
- const manifestPath = import_node_path3.default.join(manifestRoot, "cnos.yml");
1655
+ const manifestPath = import_node_path6.default.join(manifestRoot, "cnos.yml");
634
1656
  let source;
635
1657
  try {
636
- source = await (0, import_promises3.readFile)(manifestPath, "utf8");
1658
+ source = await (0, import_promises5.readFile)(manifestPath, "utf8");
637
1659
  } catch {
638
1660
  throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
639
1661
  }
@@ -643,10 +1665,11 @@ async function loadManifest(options = {}) {
643
1665
  }
644
1666
  return {
645
1667
  manifestRoot,
646
- repoRoot: import_node_path3.default.dirname(manifestRoot),
1668
+ repoRoot: import_node_path6.default.dirname(manifestRoot),
647
1669
  consumerRoot: resolved.consumerRoot,
648
1670
  ...resolved.anchorPath ? { anchorPath: resolved.anchorPath } : {},
649
1671
  ...resolved.workspace ? { anchoredWorkspace: resolved.workspace } : {},
1672
+ rootResolution: resolved.rootResolution,
650
1673
  manifestPath,
651
1674
  manifest: normalizeManifest(rawManifest),
652
1675
  rawManifest
@@ -654,12 +1677,12 @@ async function loadManifest(options = {}) {
654
1677
  }
655
1678
 
656
1679
  // ../core/src/manifest/loadWorkspaceFile.ts
657
- var import_promises4 = require("fs/promises");
658
- var import_node_path4 = __toESM(require("path"), 1);
1680
+ var import_promises6 = require("fs/promises");
1681
+ var import_node_path7 = __toESM(require("path"), 1);
659
1682
  async function loadWorkspaceFile(repoRoot) {
660
- const workspaceFilePath = import_node_path4.default.join(repoRoot, ".cnos-workspace.yml");
1683
+ const workspaceFilePath = import_node_path7.default.join(repoRoot, ".cnos-workspace.yml");
661
1684
  try {
662
- const source = await (0, import_promises4.readFile)(workspaceFilePath, "utf8");
1685
+ const source = await (0, import_promises6.readFile)(workspaceFilePath, "utf8");
663
1686
  const parsed = parseYaml(source);
664
1687
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
665
1688
  throw new CnosManifestError(".cnos-workspace.yml must be a YAML object", workspaceFilePath);
@@ -682,11 +1705,11 @@ async function loadWorkspaceFile(repoRoot) {
682
1705
  }
683
1706
 
684
1707
  // ../core/src/profiles/expandProfileChain.ts
685
- var import_promises5 = require("fs/promises");
686
- var import_node_path5 = __toESM(require("path"), 1);
1708
+ var import_promises7 = require("fs/promises");
1709
+ var import_node_path8 = __toESM(require("path"), 1);
687
1710
  async function fileExists(targetPath) {
688
1711
  try {
689
- await (0, import_promises5.access)(targetPath);
1712
+ await (0, import_promises7.access)(targetPath);
690
1713
  return true;
691
1714
  } catch {
692
1715
  return false;
@@ -720,11 +1743,11 @@ async function loadProfileDefinition(profileName, options) {
720
1743
  return normalizeProfileDefinition(profileName, void 0);
721
1744
  }
722
1745
  for (const workspaceRoot of [...workspaceRoots].reverse()) {
723
- const profilePath = import_node_path5.default.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
1746
+ const profilePath = import_node_path8.default.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
724
1747
  if (!await fileExists(profilePath)) {
725
1748
  continue;
726
1749
  }
727
- const document = await (0, import_promises5.readFile)(profilePath, "utf8");
1750
+ const document = await (0, import_promises7.readFile)(profilePath, "utf8");
728
1751
  const parsed = parseYaml(document);
729
1752
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
730
1753
  throw new CnosManifestError("Profile definition must be a YAML object", profilePath);
@@ -732,7 +1755,7 @@ async function loadProfileDefinition(profileName, options) {
732
1755
  const definition = normalizeProfileDefinition(
733
1756
  profileName,
734
1757
  parsed,
735
- options.manifestRoot ? toPortablePath(import_node_path5.default.relative(import_node_path5.default.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
1758
+ options.manifestRoot ? toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
736
1759
  );
737
1760
  if (definition.name !== profileName) {
738
1761
  throw new CnosManifestError(
@@ -1014,8 +2037,8 @@ function createProfileAwareResolver() {
1014
2037
  }
1015
2038
 
1016
2039
  // ../core/src/workspaces/resolveWorkspaceContext.ts
1017
- var import_promises6 = require("fs/promises");
1018
- var import_node_path6 = __toESM(require("path"), 1);
2040
+ var import_promises8 = require("fs/promises");
2041
+ var import_node_path9 = __toESM(require("path"), 1);
1019
2042
 
1020
2043
  // ../core/src/workspaces/expandWorkspaceChain.ts
1021
2044
  function expandWorkspaceChain(workspaceId, items) {
@@ -1054,14 +2077,14 @@ function expandWorkspaceChain(workspaceId, items) {
1054
2077
  // ../core/src/workspaces/resolveWorkspaceContext.ts
1055
2078
  async function exists3(targetPath) {
1056
2079
  try {
1057
- await (0, import_promises6.access)(targetPath);
2080
+ await (0, import_promises8.access)(targetPath);
1058
2081
  return true;
1059
2082
  } catch {
1060
2083
  return false;
1061
2084
  }
1062
2085
  }
1063
2086
  async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
1064
- const workspaceRoot = import_node_path6.default.join(manifestRoot, "workspaces", workspaceId);
2087
+ const workspaceRoot = import_node_path9.default.join(manifestRoot, "workspaces", workspaceId);
1065
2088
  if (await exists3(workspaceRoot)) {
1066
2089
  return workspaceRoot;
1067
2090
  }
@@ -1069,7 +2092,7 @@ async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
1069
2092
  ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
1070
2093
  ).map(([namespace]) => namespace);
1071
2094
  const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
1072
- (segment) => import_node_path6.default.join(manifestRoot, segment)
2095
+ (segment) => import_node_path9.default.join(manifestRoot, segment)
1073
2096
  );
1074
2097
  if ((await Promise.all(legacyMarkers.map((marker) => exists3(marker)))).some(Boolean)) {
1075
2098
  return manifestRoot;
@@ -1117,26 +2140,26 @@ function resolveGlobalRoot(manifest, workspaceFile, options) {
1117
2140
  }
1118
2141
  if (options.globalRoot) {
1119
2142
  return {
1120
- value: import_node_path6.default.resolve(expandHomePath(options.globalRoot)),
2143
+ value: import_node_path9.default.resolve(expandHomePath(options.globalRoot)),
1121
2144
  source: "cli"
1122
2145
  };
1123
2146
  }
1124
2147
  if (workspaceFile?.globalRoot) {
1125
2148
  return {
1126
- value: import_node_path6.default.resolve(expandHomePath(workspaceFile.globalRoot)),
2149
+ value: import_node_path9.default.resolve(expandHomePath(workspaceFile.globalRoot)),
1127
2150
  source: "workspace-file"
1128
2151
  };
1129
2152
  }
1130
2153
  if (manifest.workspaces.global.root) {
1131
2154
  return {
1132
- value: import_node_path6.default.resolve(expandHomePath(manifest.workspaces.global.root)),
2155
+ value: import_node_path9.default.resolve(expandHomePath(manifest.workspaces.global.root)),
1133
2156
  source: "manifest"
1134
2157
  };
1135
2158
  }
1136
2159
  const cnosHome = options.processEnv?.CNOS_HOME;
1137
2160
  if (cnosHome) {
1138
2161
  return {
1139
- value: import_node_path6.default.resolve(expandHomePath(cnosHome)),
2162
+ value: import_node_path9.default.resolve(expandHomePath(cnosHome)),
1140
2163
  source: "CNOS_HOME"
1141
2164
  };
1142
2165
  }
@@ -1158,7 +2181,7 @@ async function resolveWorkspaceContext(manifest, options) {
1158
2181
  workspaceRoots.push({
1159
2182
  scope: "global",
1160
2183
  workspaceId: chainWorkspaceId,
1161
- path: import_node_path6.default.join(globalRoot.value, "workspaces", globalWorkspaceId)
2184
+ path: import_node_path9.default.join(globalRoot.value, "workspaces", globalWorkspaceId)
1162
2185
  });
1163
2186
  }
1164
2187
  }
@@ -1281,6 +2304,10 @@ function applySchemaRules(graph, schema) {
1281
2304
  }
1282
2305
  continue;
1283
2306
  }
2307
+ if (isDerivedValue(resolvedEntry.value)) {
2308
+ nextEntries.set(key, resolvedEntry);
2309
+ continue;
2310
+ }
1284
2311
  const coercedValue = coerceValue(resolvedEntry.value, rule);
1285
2312
  const nextResolvedEntry = coercedValue === resolvedEntry.value ? resolvedEntry : {
1286
2313
  ...resolvedEntry,
@@ -1350,26 +2377,26 @@ async function runPipeline(options) {
1350
2377
  }
1351
2378
 
1352
2379
  // ../core/src/secrets/auditLog.ts
1353
- var import_promises9 = require("fs/promises");
1354
- var import_node_path9 = __toESM(require("path"), 1);
2380
+ var import_promises11 = require("fs/promises");
2381
+ var import_node_path12 = __toESM(require("path"), 1);
1355
2382
 
1356
2383
  // ../core/src/utils/secretStore.ts
1357
- var import_node_crypto = require("crypto");
1358
- var import_promises8 = require("fs/promises");
1359
- var import_node_path8 = __toESM(require("path"), 1);
2384
+ var import_node_crypto2 = require("crypto");
2385
+ var import_promises10 = require("fs/promises");
2386
+ var import_node_path11 = __toESM(require("path"), 1);
1360
2387
 
1361
2388
  // ../core/src/secrets/sessionStore.ts
1362
- var import_promises7 = require("fs/promises");
1363
- var import_node_path7 = __toESM(require("path"), 1);
2389
+ var import_promises9 = require("fs/promises");
2390
+ var import_node_path10 = __toESM(require("path"), 1);
1364
2391
  function buildSessionRoot(processEnv = process.env) {
1365
- return import_node_path7.default.join(import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
2392
+ return import_node_path10.default.join(import_node_path10.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
1366
2393
  }
1367
2394
  function buildSessionPath(vault, processEnv) {
1368
- return import_node_path7.default.join(buildSessionRoot(processEnv), `${vault}.json`);
2395
+ return import_node_path10.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1369
2396
  }
1370
2397
  async function readVaultSessionKey(vault, processEnv) {
1371
2398
  try {
1372
- const source = await (0, import_promises7.readFile)(buildSessionPath(vault, processEnv), "utf8");
2399
+ const source = await (0, import_promises9.readFile)(buildSessionPath(vault, processEnv), "utf8");
1373
2400
  const document = JSON.parse(source);
1374
2401
  if (document.version !== 1 || typeof document.derivedKey !== "string") {
1375
2402
  return void 0;
@@ -1398,7 +2425,7 @@ function isSecretReference(value) {
1398
2425
  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));
1399
2426
  }
1400
2427
  function resolveSecretStoreRoot(processEnv = process.env) {
1401
- return import_node_path8.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
2428
+ return import_node_path11.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1402
2429
  }
1403
2430
  function normalizeVaultToken(vault = "default") {
1404
2431
  return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
@@ -1427,19 +2454,19 @@ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1427
2454
  }
1428
2455
  }
1429
2456
  function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1430
- return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
2457
+ return (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1431
2458
  }
1432
2459
  function buildMetaPath(storeRoot, vault = "default") {
1433
- return import_node_path8.default.join(storeRoot, "vaults", vault, META_FILENAME);
2460
+ return import_node_path11.default.join(storeRoot, "vaults", vault, META_FILENAME);
1434
2461
  }
1435
2462
  function buildKeystorePath(storeRoot, vault = "default") {
1436
- return import_node_path8.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
2463
+ return import_node_path11.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1437
2464
  }
1438
2465
  function buildLegacyVaultFile(storeRoot, vault = "default") {
1439
- return import_node_path8.default.join(storeRoot, "vaults", `${vault}.json`);
2466
+ return import_node_path11.default.join(storeRoot, "vaults", `${vault}.json`);
1440
2467
  }
1441
2468
  function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1442
- return import_node_path8.default.join(storeRoot, "vaults", vault, "store");
2469
+ return import_node_path11.default.join(storeRoot, "vaults", vault, "store");
1443
2470
  }
1444
2471
  function assertVaultMetadata(value, filePath) {
1445
2472
  if (!isObject(value)) {
@@ -1452,7 +2479,7 @@ function assertVaultMetadata(value, filePath) {
1452
2479
  }
1453
2480
  async function exists4(targetPath) {
1454
2481
  try {
1455
- await (0, import_promises8.stat)(targetPath);
2482
+ await (0, import_promises10.stat)(targetPath);
1456
2483
  return true;
1457
2484
  } catch {
1458
2485
  return false;
@@ -1479,8 +2506,8 @@ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1479
2506
  );
1480
2507
  }
1481
2508
  function encryptPayload(payload, key) {
1482
- const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1483
- const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
2509
+ const iv = (0, import_node_crypto2.randomBytes)(IV_LENGTH);
2510
+ const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key, iv);
1484
2511
  const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1485
2512
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1486
2513
  const tag = cipher.getAuthTag();
@@ -1505,7 +2532,7 @@ function decryptPayload(buffer, key) {
1505
2532
  const iv = buffer.subarray(ivOffset, tagOffset);
1506
2533
  const tag = buffer.subarray(tagOffset, cipherOffset);
1507
2534
  const ciphertext = buffer.subarray(cipherOffset);
1508
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
2535
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
1509
2536
  decipher.setAuthTag(tag);
1510
2537
  try {
1511
2538
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
@@ -1536,15 +2563,15 @@ function buildInitialPayload() {
1536
2563
  async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1537
2564
  const metaPath = buildMetaPath(storeRoot, vault);
1538
2565
  const keystorePath = buildKeystorePath(storeRoot, vault);
1539
- await (0, import_promises8.mkdir)(import_node_path8.default.dirname(metaPath), { recursive: true });
1540
- await (0, import_promises8.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1541
- await (0, import_promises8.writeFile)(keystorePath, encryptPayload(payload, key));
2566
+ await (0, import_promises10.mkdir)(import_node_path11.default.dirname(metaPath), { recursive: true });
2567
+ await (0, import_promises10.writeFile)(metaPath, stringifyYaml(meta), "utf8");
2568
+ await (0, import_promises10.writeFile)(keystorePath, encryptPayload(payload, key));
1542
2569
  }
1543
2570
  async function readVaultMetadata(storeRoot, vault = "default") {
1544
2571
  await assertNoLegacyVaultFormat(storeRoot, vault);
1545
2572
  const metaPath = buildMetaPath(storeRoot, vault);
1546
2573
  try {
1547
- const source = await (0, import_promises8.readFile)(metaPath, "utf8");
2574
+ const source = await (0, import_promises10.readFile)(metaPath, "utf8");
1548
2575
  return assertVaultMetadata(parseYaml(source), metaPath);
1549
2576
  } catch (error) {
1550
2577
  if (error.code === "ENOENT") {
@@ -1556,7 +2583,7 @@ async function readVaultMetadata(storeRoot, vault = "default") {
1556
2583
  async function createSecretVault(storeRoot, vault, passphrase) {
1557
2584
  const normalizedVault = vault.trim() || "default";
1558
2585
  await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1559
- const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
2586
+ const salt = (0, import_node_crypto2.randomBytes)(SALT_LENGTH);
1560
2587
  const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1561
2588
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1562
2589
  const meta = {
@@ -1635,7 +2662,7 @@ async function loadVaultPayload(storeRoot, vault, auth) {
1635
2662
  if (!key) {
1636
2663
  throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1637
2664
  }
1638
- const buffer = await (0, import_promises8.readFile)(buildKeystorePath(storeRoot, vault));
2665
+ const buffer = await (0, import_promises10.readFile)(buildKeystorePath(storeRoot, vault));
1639
2666
  return {
1640
2667
  meta,
1641
2668
  payload: decryptPayload(buffer, key),
@@ -1710,9 +2737,9 @@ function resolveVaultDefinition(vaults, vault = "default") {
1710
2737
 
1711
2738
  // ../core/src/secrets/auditLog.ts
1712
2739
  async function appendAuditEvent(event, processEnv = process.env) {
1713
- const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path9.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1714
- await (0, import_promises9.mkdir)(import_node_path9.default.dirname(auditFile), { recursive: true });
1715
- await (0, import_promises9.appendFile)(
2740
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path12.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
2741
+ await (0, import_promises11.mkdir)(import_node_path12.default.dirname(auditFile), { recursive: true });
2742
+ await (0, import_promises11.appendFile)(
1716
2743
  auditFile,
1717
2744
  `${JSON.stringify({
1718
2745
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2070,7 +3097,7 @@ function setNestedValue(target, pathSegments, value) {
2070
3097
  target[head] = nextTarget;
2071
3098
  setNestedValue(nextTarget, tail, value);
2072
3099
  }
2073
- function toNamespaceObject(graph, namespace) {
3100
+ function toNamespaceObject(graph, namespace, readValueForKey = (key) => graph.entries.get(key)?.value) {
2074
3101
  const output = {};
2075
3102
  const resolvedEntries = Array.from(graph.entries.values()).sort(
2076
3103
  (left, right) => left.key.localeCompare(right.key)
@@ -2080,7 +3107,11 @@ function toNamespaceObject(graph, namespace) {
2080
3107
  continue;
2081
3108
  }
2082
3109
  const valuePath = namespace ? stripNamespace(entry.key) : entry.key;
2083
- setNestedValue(output, valuePath.split("."), entry.value);
3110
+ const value = readValueForKey(entry.key);
3111
+ if (value === void 0) {
3112
+ continue;
3113
+ }
3114
+ setNestedValue(output, valuePath.split("."), value);
2084
3115
  }
2085
3116
  return output;
2086
3117
  }
@@ -2090,12 +3121,6 @@ function readValue(graph, key) {
2090
3121
  return graph.entries.get(key)?.value;
2091
3122
  }
2092
3123
 
2093
- // ../core/src/runtime/readOr.ts
2094
- function readOrValue(graph, key, fallback) {
2095
- const value = readValue(graph, key);
2096
- return value === void 0 ? fallback : value;
2097
- }
2098
-
2099
3124
  // ../core/src/runtime/require.ts
2100
3125
  function requireValue(graph, key) {
2101
3126
  const value = readValue(graph, key);
@@ -2105,8 +3130,38 @@ function requireValue(graph, key) {
2105
3130
  return value;
2106
3131
  }
2107
3132
 
3133
+ // ../core/src/runtime/runtimeProviders.ts
3134
+ function createDefaultRuntimeProviders(manifest, processEnv) {
3135
+ const providers = /* @__PURE__ */ new Map();
3136
+ if (manifest.runtimeNamespaces.process) {
3137
+ providers.set("process", (key) => {
3138
+ const segments = key.split(".");
3139
+ if (segments[0] === "env") {
3140
+ return processEnv[segments.slice(1).join(".")];
3141
+ }
3142
+ if (key === "cwd") {
3143
+ return process.cwd();
3144
+ }
3145
+ if (key === "platform") {
3146
+ return process.platform;
3147
+ }
3148
+ if (key === "arch") {
3149
+ return process.arch;
3150
+ }
3151
+ if (key === "pid") {
3152
+ return process.pid;
3153
+ }
3154
+ if (key === "node.version") {
3155
+ return process.version;
3156
+ }
3157
+ return void 0;
3158
+ });
3159
+ }
3160
+ return providers;
3161
+ }
3162
+
2108
3163
  // ../core/src/runtime/toServerProjection.ts
2109
- var import_node_crypto2 = require("crypto");
3164
+ var import_node_crypto3 = require("crypto");
2110
3165
  function stableSortObject(value) {
2111
3166
  return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
2112
3167
  }
@@ -2115,12 +3170,14 @@ function stripValuePrefix(key) {
2115
3170
  }
2116
3171
  function configHash(values) {
2117
3172
  const serialized = JSON.stringify(stableSortObject(values));
2118
- return (0, import_node_crypto2.createHash)("sha256").update(serialized).digest("hex");
3173
+ return (0, import_node_crypto3.createHash)("sha256").update(serialized).digest("hex");
2119
3174
  }
2120
- function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
3175
+ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev", helpers = {}) {
2121
3176
  const values = {};
3177
+ const derived = {};
2122
3178
  const secretRefs = {};
2123
3179
  const namespaces = /* @__PURE__ */ new Set();
3180
+ const runtimeNamespaces = /* @__PURE__ */ new Set();
2124
3181
  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));
2125
3182
  for (const [key, entry] of graph.entries) {
2126
3183
  if (entry.namespace === "secret" && isSecretReference(entry.value)) {
@@ -2132,12 +3189,33 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
2132
3189
  continue;
2133
3190
  }
2134
3191
  if (entry.namespace === "value") {
2135
- values[stripValuePrefix(key)] = entry.value;
3192
+ if (helpers.isRuntimeDependent?.(key)) {
3193
+ const formula = helpers.toServerFormula?.(key);
3194
+ if (formula) {
3195
+ derived[stripValuePrefix(key)] = formula;
3196
+ for (const ref of formula.runtimeRefs) {
3197
+ runtimeNamespaces.add(ref.split(".")[0] ?? "");
3198
+ }
3199
+ }
3200
+ continue;
3201
+ }
3202
+ const value = helpers.read ? helpers.read(key) : entry.value;
3203
+ values[stripValuePrefix(key)] = value;
2136
3204
  continue;
2137
3205
  }
2138
3206
  const namespaceDefinition = manifest.namespaces[entry.namespace];
2139
3207
  if (namespaceDefinition && namespaceDefinition.kind === "data" && !namespaceDefinition.sensitive && entry.namespace !== "public") {
2140
- values[key] = entry.value;
3208
+ if (helpers.isRuntimeDependent?.(key)) {
3209
+ const formula = helpers.toServerFormula?.(key);
3210
+ if (formula) {
3211
+ derived[key] = formula;
3212
+ for (const ref of formula.runtimeRefs) {
3213
+ runtimeNamespaces.add(ref.split(".")[0] ?? "");
3214
+ }
3215
+ }
3216
+ continue;
3217
+ }
3218
+ values[key] = helpers.read ? helpers.read(key) : entry.value;
2141
3219
  namespaces.add(entry.namespace);
2142
3220
  }
2143
3221
  }
@@ -2148,8 +3226,10 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
2148
3226
  resolvedAt: graph.resolvedAt,
2149
3227
  configHash: configHash(values),
2150
3228
  values: stableSortObject(values),
3229
+ derived: stableSortObject(derived),
2151
3230
  secretRefs: stableSortObject(secretRefs),
2152
3231
  publicKeys,
3232
+ runtimeNamespaces: Array.from(runtimeNamespaces).sort((left, right) => left.localeCompare(right)),
2153
3233
  meta: {
2154
3234
  workspace: graph.workspace.workspaceId,
2155
3235
  profile: graph.profile,
@@ -2172,7 +3252,7 @@ function normalizeEnvValue(value) {
2172
3252
  }
2173
3253
  return JSON.stringify(value);
2174
3254
  }
2175
- function toEnv(graph, manifest, options = {}) {
3255
+ function toEnv(graph, manifest, options = {}, helpers = {}) {
2176
3256
  const includeSecrets = options.includeSecrets ?? true;
2177
3257
  const output = {};
2178
3258
  const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
@@ -2193,7 +3273,11 @@ function toEnv(graph, manifest, options = {}) {
2193
3273
  if (isSecretReference(entry.value)) {
2194
3274
  continue;
2195
3275
  }
2196
- output[envVar] = normalizeEnvValue(entry.value);
3276
+ const value = helpers.read ? helpers.read(logicalKey) : entry.value;
3277
+ if (value === void 0) {
3278
+ continue;
3279
+ }
3280
+ output[envVar] = normalizeEnvValue(value);
2197
3281
  }
2198
3282
  return output;
2199
3283
  }
@@ -2230,20 +3314,43 @@ function resolvePublicPrefix(manifest, options) {
2230
3314
  }
2231
3315
  return manifest.public.frameworks[options.framework] ?? "";
2232
3316
  }
2233
- function toPublicEnv(graph, manifest, options = {}) {
3317
+ function toPublicEnv(graph, manifest, options = {}, helpers = {}) {
2234
3318
  const prefix = resolvePublicPrefix(manifest, options);
2235
3319
  const output = {};
2236
3320
  const promotions = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").sort((left, right) => left.key.localeCompare(right.key));
2237
3321
  for (const resolved of promotions) {
3322
+ if (helpers.isRuntimeDependent?.(resolved.key)) {
3323
+ const value2 = helpers.read?.(resolved.key);
3324
+ if (value2 === void 0) {
3325
+ throw new CnosManifestError(`Cannot build public output for ${resolved.key} because it depends on runtime-only values.`);
3326
+ }
3327
+ }
2238
3328
  const baseEnvVar = fallbackPublicEnvVar(stripNamespace(resolved.key));
2239
3329
  const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
2240
- output[envVar] = normalizeEnvValue2(resolved.value);
3330
+ const value = helpers.read ? helpers.read(resolved.key) : resolved.value;
3331
+ if (value === void 0) {
3332
+ continue;
3333
+ }
3334
+ output[envVar] = normalizeEnvValue2(value);
2241
3335
  }
2242
3336
  return output;
2243
3337
  }
2244
3338
 
2245
3339
  // ../core/src/orchestrator/runtime.ts
2246
3340
  function createRuntime(manifest, graph, plugins = [], secretCache, processEnv = process.env, cnosVersion = "0.0.0-dev") {
3341
+ const runtimeProviders = createDefaultRuntimeProviders(manifest, processEnv);
3342
+ const derivedSupport = createDerivedRuntimeSupport(graph, manifest, runtimeProviders);
3343
+ function resolveProjectedSourceKey(key) {
3344
+ if (!key.startsWith("public.")) {
3345
+ return key;
3346
+ }
3347
+ const promotedFrom = graph.entries.get(key)?.winner.metadata?.promotedFrom;
3348
+ if (typeof promotedFrom === "string") {
3349
+ return promotedFrom;
3350
+ }
3351
+ const fallback = `value.${key.slice("public.".length)}`;
3352
+ return graph.entries.has(fallback) ? fallback : key;
3353
+ }
2247
3354
  async function refreshSecretEntry(key) {
2248
3355
  const entry = graph.entries.get(key);
2249
3356
  if (!entry || entry.namespace !== "secret" || !isSecretReference(entry.value)) {
@@ -2275,6 +3382,19 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
2275
3382
  }
2276
3383
  }
2277
3384
  function readLogicalKey(key) {
3385
+ const resolved = derivedSupport.read(key, (ref) => {
3386
+ const entry2 = graph.entries.get(ref);
3387
+ if (!entry2) {
3388
+ return void 0;
3389
+ }
3390
+ if (!secretCache) {
3391
+ return entry2.value;
3392
+ }
3393
+ return resolveSecretEntryValue(ref, entry2.value, secretCache);
3394
+ });
3395
+ if (resolved !== void 0 || graph.entries.has(key) || manifest.runtimeNamespaces[key.split(".")[0] ?? ""]) {
3396
+ return resolved;
3397
+ }
2278
3398
  const entry = graph.entries.get(key);
2279
3399
  if (!entry) {
2280
3400
  return void 0;
@@ -2299,34 +3419,64 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
2299
3419
  return value;
2300
3420
  },
2301
3421
  readOr(key, fallback) {
2302
- return readOrValue(graph, key, fallback);
3422
+ const value = readLogicalKey(key);
3423
+ return value === void 0 ? fallback : value;
2303
3424
  },
2304
- value(path13) {
2305
- return readLogicalKey(toLogicalKey("value", path13));
3425
+ value(path16) {
3426
+ return readLogicalKey(toLogicalKey("value", path16));
2306
3427
  },
2307
- secret(path13) {
2308
- return readLogicalKey(toLogicalKey("secret", path13));
3428
+ secret(path16) {
3429
+ return readLogicalKey(toLogicalKey("secret", path16));
2309
3430
  },
2310
- meta(path13) {
2311
- return readLogicalKey(toLogicalKey("meta", path13));
3431
+ meta(path16) {
3432
+ return readLogicalKey(toLogicalKey("meta", path16));
2312
3433
  },
2313
3434
  inspect(key) {
2314
- return inspectValue(graph, key);
3435
+ return inspectValue(graph, key, {
3436
+ read: (ref) => readLogicalKey(ref),
3437
+ describeDerived: (ref) => derivedSupport.describe(ref, (candidate) => {
3438
+ const entry = graph.entries.get(candidate);
3439
+ if (!entry) {
3440
+ return void 0;
3441
+ }
3442
+ if (!secretCache) {
3443
+ return entry.value;
3444
+ }
3445
+ return resolveSecretEntryValue(candidate, entry.value, secretCache);
3446
+ })
3447
+ });
2315
3448
  },
2316
3449
  toObject() {
2317
- return toNamespaceObject(graph);
3450
+ return toNamespaceObject(graph, void 0, (key) => readLogicalKey(key));
2318
3451
  },
2319
3452
  toNamespace(namespace) {
2320
- return toNamespaceObject(graph, namespace);
3453
+ return toNamespaceObject(graph, namespace, (key) => readLogicalKey(key));
2321
3454
  },
2322
3455
  toEnv(options) {
2323
- return toEnv(graph, manifest, options);
3456
+ return toEnv(graph, manifest, options, {
3457
+ read: (key) => readLogicalKey(key),
3458
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key)
3459
+ });
2324
3460
  },
2325
3461
  toPublicEnv(options) {
2326
- return toPublicEnv(graph, manifest, options);
3462
+ return toPublicEnv(graph, manifest, options, {
3463
+ read: (key) => derivedSupport.toConcreteValue(
3464
+ resolveProjectedSourceKey(key),
3465
+ (candidate) => readLogicalKey(candidate),
3466
+ "public"
3467
+ ),
3468
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(resolveProjectedSourceKey(key))
3469
+ });
2327
3470
  },
2328
3471
  toServerProjection() {
2329
- return toServerProjection(graph, manifest, cnosVersion);
3472
+ return toServerProjection(graph, manifest, cnosVersion, {
3473
+ read: (key) => derivedSupport.toConcreteValue(key, (candidate) => readLogicalKey(candidate), "server"),
3474
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key),
3475
+ toServerFormula: (key) => derivedSupport.toServerFormula(key)
3476
+ });
3477
+ },
3478
+ registerRuntimeProvider(namespace, provider) {
3479
+ registerRuntimeProvider(manifest, runtimeProviders, namespace, provider);
2330
3480
  },
2331
3481
  async refreshSecrets() {
2332
3482
  await refreshAllSecrets();
@@ -2433,7 +3583,11 @@ function appendMetaEntries(graph, cnosVersion) {
2433
3583
  async function createCnos(options = {}) {
2434
3584
  const loadedManifest = await loadManifest({
2435
3585
  ...options.root ? { root: options.root } : {},
2436
- ...options.cwd ? { cwd: options.cwd } : {}
3586
+ ...options.cwd ? { cwd: options.cwd } : {},
3587
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
3588
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
3589
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
3590
+ ...options.forceRefresh ? { forceRefresh: true } : {}
2437
3591
  });
2438
3592
  for (const key of loadedManifest.manifest.public.promote) {
2439
3593
  ensureProjectionAllowed(loadedManifest.manifest, key, "public");
@@ -2494,23 +3648,23 @@ async function createCnos(options = {}) {
2494
3648
  }
2495
3649
 
2496
3650
  // ../core/src/runtime/dump.ts
2497
- var import_promises10 = require("fs/promises");
2498
- var import_node_path10 = __toESM(require("path"), 1);
3651
+ var import_promises12 = require("fs/promises");
3652
+ var import_node_path13 = __toESM(require("path"), 1);
2499
3653
  function buildDumpFiles(graph, options = {}) {
2500
- const basePath = options.flatten ? "" : import_node_path10.default.posix.join("workspaces", graph.workspace.workspaceId);
3654
+ const basePath = options.flatten ? "" : import_node_path13.default.posix.join("workspaces", graph.workspace.workspaceId);
2501
3655
  const values = toNamespaceObject(graph, "value");
2502
3656
  const secrets = toNamespaceObject(graph, "secret");
2503
3657
  const files = [];
2504
3658
  if (Object.keys(values).length > 0) {
2505
3659
  files.push({
2506
- path: import_node_path10.default.posix.join(basePath, "values", graph.profile, "app.yml"),
3660
+ path: import_node_path13.default.posix.join(basePath, "values", graph.profile, "app.yml"),
2507
3661
  namespace: "value",
2508
3662
  content: stringifyYaml(values)
2509
3663
  });
2510
3664
  }
2511
3665
  if (Object.keys(secrets).length > 0) {
2512
3666
  files.push({
2513
- path: import_node_path10.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
3667
+ path: import_node_path13.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
2514
3668
  namespace: "secret",
2515
3669
  content: stringifyYaml(secrets)
2516
3670
  });
@@ -2526,12 +3680,12 @@ function planDump(graph, options = {}) {
2526
3680
  };
2527
3681
  }
2528
3682
  async function writeDump(graph, options) {
2529
- const root = import_node_path10.default.resolve(options.to);
3683
+ const root = import_node_path13.default.resolve(options.to);
2530
3684
  const plan = planDump(graph, options);
2531
3685
  for (const file of plan.files) {
2532
- const destination = import_node_path10.default.join(root, file.path);
2533
- await (0, import_promises10.mkdir)(import_node_path10.default.dirname(destination), { recursive: true });
2534
- await (0, import_promises10.writeFile)(destination, file.content, "utf8");
3686
+ const destination = import_node_path13.default.join(root, file.path);
3687
+ await (0, import_promises12.mkdir)(import_node_path13.default.dirname(destination), { recursive: true });
3688
+ await (0, import_promises12.writeFile)(destination, file.content, "utf8");
2535
3689
  }
2536
3690
  return {
2537
3691
  ...plan,
@@ -2546,8 +3700,8 @@ function normalizeMappingConfig(config = {}) {
2546
3700
  explicit: config.explicit ?? {}
2547
3701
  };
2548
3702
  }
2549
- function fromScreamingSnake(path13) {
2550
- return path13.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
3703
+ function fromScreamingSnake(path16) {
3704
+ return path16.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2551
3705
  }
2552
3706
  function envVarToLogicalKey(envVar, config = {}) {
2553
3707
  const normalized = normalizeMappingConfig(config);
@@ -2574,7 +3728,7 @@ function envVarToLogicalKey(envVar, config = {}) {
2574
3728
  // package.json
2575
3729
  var package_default = {
2576
3730
  name: "@kitsy/cnos",
2577
- version: "1.6.0",
3731
+ version: "1.7.0",
2578
3732
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
2579
3733
  type: "module",
2580
3734
  main: "./dist/index.cjs",
@@ -2773,8 +3927,8 @@ function createCliArgsPlugin() {
2773
3927
  }
2774
3928
 
2775
3929
  // ../../plugins/dotenv/src/index.ts
2776
- var import_promises11 = require("fs/promises");
2777
- var import_node_path11 = __toESM(require("path"), 1);
3930
+ var import_promises13 = require("fs/promises");
3931
+ var import_node_path14 = __toESM(require("path"), 1);
2778
3932
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
2779
3933
  function parseDoubleQuoted(value) {
2780
3934
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -2831,7 +3985,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
2831
3985
  }
2832
3986
  async function readIfPresent(filePath) {
2833
3987
  try {
2834
- return await (0, import_promises11.readFile)(filePath, "utf8");
3988
+ return await (0, import_promises13.readFile)(filePath, "utf8");
2835
3989
  } catch {
2836
3990
  return void 0;
2837
3991
  }
@@ -2850,7 +4004,7 @@ function createDotenvPlugin() {
2850
4004
  workspace: workspaceRoot.workspaceId
2851
4005
  });
2852
4006
  for (const fileName of fileNames) {
2853
- const absolutePath = import_node_path11.default.join(envRoot, fileName);
4007
+ const absolutePath = import_node_path14.default.join(envRoot, fileName);
2854
4008
  const document = await readIfPresent(absolutePath);
2855
4009
  if (!document) {
2856
4010
  continue;
@@ -2859,7 +4013,7 @@ function createDotenvPlugin() {
2859
4013
  ...dotenvEntriesFromObject(
2860
4014
  parseDotenv(document),
2861
4015
  config.envMapping,
2862
- toPortablePath(import_node_path11.default.relative(import_node_path11.default.dirname(context.manifestRoot), absolutePath)),
4016
+ toPortablePath(import_node_path14.default.relative(import_node_path14.default.dirname(context.manifestRoot), absolutePath)),
2863
4017
  workspaceRoot.workspaceId
2864
4018
  )
2865
4019
  );
@@ -2897,16 +4051,16 @@ function createPublicEnvExportPlugin() {
2897
4051
  }
2898
4052
 
2899
4053
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
2900
- var import_promises13 = require("fs/promises");
4054
+ var import_promises15 = require("fs/promises");
2901
4055
 
2902
4056
  // ../../plugins/filesystem/src/helpers.ts
2903
- var import_promises12 = require("fs/promises");
2904
- var import_node_path12 = __toESM(require("path"), 1);
4057
+ var import_promises14 = require("fs/promises");
4058
+ var import_node_path15 = __toESM(require("path"), 1);
2905
4059
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
2906
4060
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
2907
4061
  async function existsDirectory(targetPath) {
2908
4062
  try {
2909
- const stat2 = await (0, import_promises12.readdir)(targetPath);
4063
+ const stat2 = await (0, import_promises14.readdir)(targetPath);
2910
4064
  void stat2;
2911
4065
  return true;
2912
4066
  } catch {
@@ -2914,15 +4068,15 @@ async function existsDirectory(targetPath) {
2914
4068
  }
2915
4069
  }
2916
4070
  async function collectYamlFiles(root) {
2917
- const entries = await (0, import_promises12.readdir)(root, { withFileTypes: true });
4071
+ const entries = await (0, import_promises14.readdir)(root, { withFileTypes: true });
2918
4072
  const results = [];
2919
4073
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
2920
- const absolutePath = import_node_path12.default.join(root, entry.name);
4074
+ const absolutePath = import_node_path15.default.join(root, entry.name);
2921
4075
  if (entry.isDirectory()) {
2922
4076
  results.push(...await collectYamlFiles(absolutePath));
2923
4077
  continue;
2924
4078
  }
2925
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path12.default.extname(entry.name).toLowerCase())) {
4079
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path15.default.extname(entry.name).toLowerCase())) {
2926
4080
  results.push(absolutePath);
2927
4081
  }
2928
4082
  }
@@ -2930,16 +4084,16 @@ async function collectYamlFiles(root) {
2930
4084
  }
2931
4085
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
2932
4086
  const files = [];
2933
- const repoRoot = import_node_path12.default.dirname(manifestRoot);
4087
+ const repoRoot = import_node_path15.default.dirname(manifestRoot);
2934
4088
  for (const workspaceRoot of workspaceRoots) {
2935
- const resolvedRoot = import_node_path12.default.resolve(workspaceRoot.path, sourceRoot);
4089
+ const resolvedRoot = import_node_path15.default.resolve(workspaceRoot.path, sourceRoot);
2936
4090
  for (const layer of activeLayers) {
2937
- const layerRoot = import_node_path12.default.join(resolvedRoot, layer);
4091
+ const layerRoot = import_node_path15.default.join(resolvedRoot, layer);
2938
4092
  if (!await existsDirectory(layerRoot)) {
2939
4093
  continue;
2940
4094
  }
2941
4095
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
2942
- const relativePath = import_node_path12.default.relative(repoRoot, absolutePath);
4096
+ const relativePath = import_node_path15.default.relative(repoRoot, absolutePath);
2943
4097
  files.push({
2944
4098
  absolutePath,
2945
4099
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -2959,7 +4113,7 @@ function assertObjectDocument(value, filePath) {
2959
4113
  function flattenConfigObject(value, options = {}, prefix = "") {
2960
4114
  return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
2961
4115
  const nextKey = prefix ? `${prefix}.${key}` : key;
2962
- if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue) && !options.stopAtLeaf?.(nestedValue)) {
4116
+ if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue) && !isDerivedValue(nestedValue) && !options.stopAtLeaf?.(nestedValue)) {
2963
4117
  Object.assign(
2964
4118
  accumulator,
2965
4119
  flattenConfigObject(nestedValue, options, nextKey)
@@ -3016,7 +4170,7 @@ function createFilesystemSecretsPlugin() {
3016
4170
  );
3017
4171
  const entries = [];
3018
4172
  for (const file of files) {
3019
- const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
4173
+ const document = await (0, import_promises15.readFile)(file.absolutePath, "utf8");
3020
4174
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
3021
4175
  for (const entry of fileEntries) {
3022
4176
  const metadata = toSecretReferenceMetadata(entry.value);
@@ -3032,7 +4186,7 @@ function createFilesystemSecretsPlugin() {
3032
4186
  }
3033
4187
 
3034
4188
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
3035
- var import_promises14 = require("fs/promises");
4189
+ var import_promises16 = require("fs/promises");
3036
4190
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
3037
4191
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
3038
4192
  }
@@ -3053,7 +4207,7 @@ function createFilesystemValuesPlugin() {
3053
4207
  ).map(([namespace]) => namespace);
3054
4208
  const entries = [];
3055
4209
  for (const file of files) {
3056
- const document = await (0, import_promises14.readFile)(file.absolutePath, "utf8");
4210
+ const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
3057
4211
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
3058
4212
  }
3059
4213
  for (const namespace of customNamespaces) {
@@ -3068,7 +4222,7 @@ function createFilesystemValuesPlugin() {
3068
4222
  layers
3069
4223
  );
3070
4224
  for (const file of namespaceFiles) {
3071
- const document = await (0, import_promises14.readFile)(file.absolutePath, "utf8");
4225
+ const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
3072
4226
  entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
3073
4227
  }
3074
4228
  }