@kitsy/cnos 1.6.1 → 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-N32UN66E.js → chunk-CV3SLBYZ.js} +8 -8
  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-6FAX2VKQ.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
@@ -34,7 +34,7 @@ __export(runtime_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(runtime_exports);
36
36
  var import_node_fs = require("fs");
37
- var import_node_path13 = __toESM(require("path"), 1);
37
+ var import_node_path16 = __toESM(require("path"), 1);
38
38
 
39
39
  // ../core/src/errors.ts
40
40
  var CnosError = class extends Error {
@@ -72,16 +72,673 @@ var CnosKeyNotFoundError = class extends CnosError {
72
72
  }
73
73
  key;
74
74
  };
75
+ var CnosDerivedExpressionError = class extends CnosError {
76
+ constructor(message, expression) {
77
+ super(expression ? `${message} (${expression})` : message);
78
+ this.expression = expression;
79
+ }
80
+ expression;
81
+ };
82
+ var CnosDerivedCycleError = class extends CnosError {
83
+ constructor(message) {
84
+ super(message);
85
+ }
86
+ };
87
+ var CnosDerivedResolutionError = class extends CnosError {
88
+ constructor(key, message) {
89
+ super(message);
90
+ this.key = key;
91
+ }
92
+ key;
93
+ };
94
+ var CnosRuntimeProviderError = class extends CnosError {
95
+ constructor(message) {
96
+ super(message);
97
+ }
98
+ };
99
+
100
+ // ../core/src/derive/builtins.ts
101
+ function stringify(value) {
102
+ if (value === void 0 || value === null) {
103
+ return "";
104
+ }
105
+ if (typeof value === "string") {
106
+ return value;
107
+ }
108
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
109
+ return String(value);
110
+ }
111
+ return JSON.stringify(value);
112
+ }
113
+ var DERIVE_BUILTINS = {
114
+ concat: (...args) => args.map((value) => stringify(value)).join(""),
115
+ coalesce: (...args) => args.find((value) => value !== void 0 && value !== null),
116
+ when: (condition, whenTrue, whenFalse) => condition ? whenTrue : whenFalse,
117
+ exists: (value) => value !== void 0 && value !== null,
118
+ eq: (left, right) => left === right,
119
+ ne: (left, right) => left !== right
120
+ };
121
+
122
+ // ../core/src/derive/depGraph.ts
123
+ function detectDerivationCycles(dependencyMap) {
124
+ const visiting = /* @__PURE__ */ new Set();
125
+ const visited = /* @__PURE__ */ new Set();
126
+ const stack = [];
127
+ const visit = (key) => {
128
+ if (visited.has(key)) {
129
+ return;
130
+ }
131
+ if (visiting.has(key)) {
132
+ const start = stack.indexOf(key);
133
+ const cycle = [...stack.slice(start), key].join(" -> ");
134
+ throw new CnosDerivedCycleError(`Derivation cycle detected: ${cycle}`);
135
+ }
136
+ visiting.add(key);
137
+ stack.push(key);
138
+ for (const dependency of dependencyMap.get(key) ?? []) {
139
+ visit(dependency);
140
+ }
141
+ stack.pop();
142
+ visiting.delete(key);
143
+ visited.add(key);
144
+ };
145
+ for (const key of dependencyMap.keys()) {
146
+ visit(key);
147
+ }
148
+ }
149
+
150
+ // ../core/src/derive/parser.ts
151
+ function isWhitespace(value) {
152
+ return value === " " || value === "\n" || value === "\r" || value === " ";
153
+ }
154
+ function skipWhitespace(state) {
155
+ while (isWhitespace(state.source[state.index])) {
156
+ state.index += 1;
157
+ }
158
+ }
159
+ function isIdentifierStart(value) {
160
+ return typeof value === "string" && /[A-Za-z_]/.test(value);
161
+ }
162
+ function isIdentifierPart(value) {
163
+ return typeof value === "string" && /[A-Za-z0-9_.-]/.test(value);
164
+ }
165
+ function errorAt(state, message) {
166
+ throw new CnosDerivedExpressionError(`${message} at position ${state.index + 1}`, state.source);
167
+ }
168
+ function parseStringLiteral(state) {
169
+ const quote = state.source[state.index];
170
+ if (quote !== "'") {
171
+ errorAt(state, "Expected string literal");
172
+ }
173
+ state.index += 1;
174
+ let value = "";
175
+ while (state.index < state.source.length) {
176
+ const current = state.source[state.index];
177
+ if (current === "\\") {
178
+ const next = state.source[state.index + 1];
179
+ if (next === void 0) {
180
+ errorAt(state, "Unterminated escape sequence");
181
+ }
182
+ value += next;
183
+ state.index += 2;
184
+ continue;
185
+ }
186
+ if (current === "'") {
187
+ state.index += 1;
188
+ return {
189
+ type: "literal",
190
+ value
191
+ };
192
+ }
193
+ value += current;
194
+ state.index += 1;
195
+ }
196
+ errorAt(state, "Unterminated string literal");
197
+ }
198
+ function parseNumberLiteral(state) {
199
+ const start = state.index;
200
+ while (/[0-9]/.test(state.source[state.index] ?? "")) {
201
+ state.index += 1;
202
+ }
203
+ if (state.source[state.index] === ".") {
204
+ state.index += 1;
205
+ while (/[0-9]/.test(state.source[state.index] ?? "")) {
206
+ state.index += 1;
207
+ }
208
+ }
209
+ return {
210
+ type: "literal",
211
+ value: Number(state.source.slice(start, state.index))
212
+ };
213
+ }
214
+ function parseIdentifier(state) {
215
+ if (!isIdentifierStart(state.source[state.index])) {
216
+ errorAt(state, "Expected identifier");
217
+ }
218
+ const start = state.index;
219
+ state.index += 1;
220
+ while (isIdentifierPart(state.source[state.index])) {
221
+ state.index += 1;
222
+ }
223
+ return state.source.slice(start, state.index);
224
+ }
225
+ function parseArguments(state) {
226
+ const args = [];
227
+ skipWhitespace(state);
228
+ if (state.source[state.index] === ")") {
229
+ state.index += 1;
230
+ return args;
231
+ }
232
+ while (state.index < state.source.length) {
233
+ args.push(parseExpressionNode(state));
234
+ skipWhitespace(state);
235
+ const current = state.source[state.index];
236
+ if (current === ",") {
237
+ state.index += 1;
238
+ skipWhitespace(state);
239
+ continue;
240
+ }
241
+ if (current === ")") {
242
+ state.index += 1;
243
+ return args;
244
+ }
245
+ errorAt(state, 'Expected "," or ")"');
246
+ }
247
+ errorAt(state, "Unterminated function call");
248
+ }
249
+ function parseIdentifierOrCall(state) {
250
+ const identifier = parseIdentifier(state);
251
+ skipWhitespace(state);
252
+ if (state.source[state.index] === "(") {
253
+ if (!(identifier in DERIVE_BUILTINS)) {
254
+ throw new CnosDerivedExpressionError(`Unknown derive function: ${identifier}`, state.source);
255
+ }
256
+ state.index += 1;
257
+ return {
258
+ type: "call",
259
+ name: identifier,
260
+ args: parseArguments(state)
261
+ };
262
+ }
263
+ if (identifier === "true" || identifier === "false") {
264
+ return {
265
+ type: "literal",
266
+ value: identifier === "true"
267
+ };
268
+ }
269
+ if (identifier === "null") {
270
+ return {
271
+ type: "literal",
272
+ value: null
273
+ };
274
+ }
275
+ return {
276
+ type: "ref",
277
+ path: identifier
278
+ };
279
+ }
280
+ function parseExpressionNode(state) {
281
+ skipWhitespace(state);
282
+ const current = state.source[state.index];
283
+ if (current === "'") {
284
+ return parseStringLiteral(state);
285
+ }
286
+ if (/[0-9]/.test(current ?? "")) {
287
+ return parseNumberLiteral(state);
288
+ }
289
+ if (isIdentifierStart(current)) {
290
+ return parseIdentifierOrCall(state);
291
+ }
292
+ errorAt(state, "Unexpected token");
293
+ }
294
+ function parseExpression(source) {
295
+ const state = {
296
+ source,
297
+ index: 0
298
+ };
299
+ const ast = parseExpressionNode(state);
300
+ skipWhitespace(state);
301
+ if (state.index !== source.length) {
302
+ errorAt(state, "Unexpected trailing input");
303
+ }
304
+ return ast;
305
+ }
306
+
307
+ // ../core/src/derive/templateParser.ts
308
+ function toLiteral(value) {
309
+ return {
310
+ type: "literal",
311
+ value
312
+ };
313
+ }
314
+ function toRef(path17) {
315
+ return {
316
+ type: "ref",
317
+ path: path17
318
+ };
319
+ }
320
+ function parseTemplate(source) {
321
+ const parts = [];
322
+ let cursor = 0;
323
+ while (cursor < source.length) {
324
+ const start = source.indexOf("${", cursor);
325
+ if (start < 0) {
326
+ if (cursor < source.length) {
327
+ parts.push(toLiteral(source.slice(cursor)));
328
+ }
329
+ break;
330
+ }
331
+ if (start > cursor) {
332
+ parts.push(toLiteral(source.slice(cursor, start)));
333
+ }
334
+ const end = source.indexOf("}", start + 2);
335
+ if (end < 0) {
336
+ throw new CnosDerivedExpressionError(`Invalid derivation template: unclosed \${...} at position ${start + 1}`, source);
337
+ }
338
+ const ref = source.slice(start + 2, end).trim();
339
+ if (!ref) {
340
+ throw new CnosDerivedExpressionError(`Invalid derivation template: empty reference at position ${start + 1}`, source);
341
+ }
342
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(ref)) {
343
+ throw new CnosDerivedExpressionError(`Invalid derivation template reference "${ref}"`, source);
344
+ }
345
+ parts.push(toRef(ref));
346
+ cursor = end + 1;
347
+ }
348
+ if (parts.length === 0) {
349
+ return toLiteral("");
350
+ }
351
+ if (parts.length === 1) {
352
+ return parts[0];
353
+ }
354
+ return {
355
+ type: "call",
356
+ name: "concat",
357
+ args: parts
358
+ };
359
+ }
360
+
361
+ // ../core/src/derive/evaluator.ts
362
+ function isDerivedValue(value) {
363
+ return Boolean(
364
+ value && typeof value === "object" && !Array.isArray(value) && "$derive" in value
365
+ );
366
+ }
367
+ function extractRefs(node, refs = /* @__PURE__ */ new Set()) {
368
+ if (node.type === "ref") {
369
+ refs.add(node.path);
370
+ return refs;
371
+ }
372
+ if (node.type === "call") {
373
+ for (const arg of node.args) {
374
+ extractRefs(arg, refs);
375
+ }
376
+ }
377
+ return refs;
378
+ }
379
+ function parseDerivation(value) {
380
+ const source = typeof value.$derive === "string" ? value.$derive : value.$derive?.expr;
381
+ if (typeof source !== "string") {
382
+ throw new CnosDerivedExpressionError("Derived value requires either a template string or { expr } object");
383
+ }
384
+ const type = typeof value.$derive === "string" ? "template" : "expression";
385
+ const ast = type === "template" ? parseTemplate(source) : parseExpression(source);
386
+ const refs = Array.from(extractRefs(ast)).sort((left, right) => left.localeCompare(right));
387
+ return {
388
+ type,
389
+ raw: source,
390
+ ast,
391
+ refs,
392
+ runtimeRefs: [],
393
+ isRuntimeDependent: false
394
+ };
395
+ }
396
+ function normalizeConcatValue(value) {
397
+ if (value === void 0 || value === null) {
398
+ return "";
399
+ }
400
+ if (typeof value === "string") {
401
+ return value;
402
+ }
403
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
404
+ return String(value);
405
+ }
406
+ return JSON.stringify(value);
407
+ }
408
+ function evaluateNode(node, resolveRef) {
409
+ switch (node.type) {
410
+ case "literal":
411
+ return node.value;
412
+ case "ref":
413
+ return resolveRef(node.path);
414
+ case "call": {
415
+ const args = node.args.map((arg) => evaluateNode(arg, resolveRef));
416
+ switch (node.name) {
417
+ case "concat":
418
+ return args.map((value) => normalizeConcatValue(value)).join("");
419
+ case "coalesce":
420
+ return DERIVE_BUILTINS.coalesce(...args);
421
+ case "when":
422
+ return DERIVE_BUILTINS.when(args[0], args[1], args[2]);
423
+ case "exists":
424
+ return DERIVE_BUILTINS.exists(args[0]);
425
+ case "eq":
426
+ return DERIVE_BUILTINS.eq(args[0], args[1]);
427
+ case "ne":
428
+ return DERIVE_BUILTINS.ne(args[0], args[1]);
429
+ default:
430
+ throw new CnosDerivedExpressionError(`Unknown derive function: ${String(node.name)}`);
431
+ }
432
+ }
433
+ default:
434
+ throw new CnosDerivedExpressionError(`Unsupported derive AST node ${node.type ?? "unknown"}`);
435
+ }
436
+ }
437
+ function evaluateDerivation(options) {
438
+ const missingRefs = /* @__PURE__ */ new Set();
439
+ const value = evaluateNode(options.parsed.ast, (ref) => {
440
+ const resolved = options.resolveRef(ref);
441
+ if (resolved === void 0) {
442
+ missingRefs.add(ref);
443
+ options.onMissing?.(ref);
444
+ }
445
+ return resolved;
446
+ });
447
+ if (missingRefs.size > 0 && options.parsed.ast.type === "ref") {
448
+ throw new CnosDerivedResolutionError(
449
+ options.key,
450
+ `Unable to resolve derived config key ${options.key} because ${Array.from(missingRefs).join(", ")} is missing.`
451
+ );
452
+ }
453
+ return value;
454
+ }
455
+
456
+ // ../core/src/derive/validate.ts
457
+ var FORBIDDEN_TARGET_NAMESPACES = /* @__PURE__ */ new Set(["public", "meta", "secret"]);
458
+ var FORBIDDEN_REF_NAMESPACES = /* @__PURE__ */ new Set(["public", "secret"]);
459
+ function validateDerivedTargetNamespace(manifest, namespace) {
460
+ if (FORBIDDEN_TARGET_NAMESPACES.has(namespace)) {
461
+ throw new CnosManifestError(`Cannot define derived values under namespace "${namespace}".`);
462
+ }
463
+ if (manifest.runtimeNamespaces[namespace]) {
464
+ throw new CnosManifestError(`Cannot define derived values under runtime namespace "${namespace}".`);
465
+ }
466
+ }
467
+ function validateParsedDerivation(manifest, parsed) {
468
+ for (const ref of parsed.refs) {
469
+ const namespace = ref.split(".")[0] ?? "";
470
+ if (FORBIDDEN_REF_NAMESPACES.has(namespace)) {
471
+ throw new CnosDerivedExpressionError(`Derived expressions cannot reference ${namespace}.* keys.`, parsed.raw);
472
+ }
473
+ if (manifest.runtimeNamespaces[namespace]) {
474
+ continue;
475
+ }
476
+ if (!manifest.namespaces[namespace] && namespace !== "value" && namespace !== "meta") {
477
+ throw new CnosDerivedExpressionError(`Unknown derive reference namespace: ${namespace}`, parsed.raw);
478
+ }
479
+ }
480
+ }
481
+
482
+ // ../core/src/derive/runtime.ts
483
+ function namespaceForKey(key) {
484
+ return key.split(".")[0] ?? "";
485
+ }
486
+ function dependencyNamespaces(key, entries, memo) {
487
+ if (memo.has(key)) {
488
+ return memo.get(key);
489
+ }
490
+ const entry = entries.get(key);
491
+ if (!entry) {
492
+ memo.set(key, []);
493
+ return [];
494
+ }
495
+ const namespaces = /* @__PURE__ */ new Set();
496
+ for (const ref of entry.parsed.refs) {
497
+ const namespace = namespaceForKey(ref);
498
+ if (!entries.has(ref)) {
499
+ namespaces.add(namespace);
500
+ continue;
501
+ }
502
+ for (const dependencyNamespace of dependencyNamespaces(ref, entries, memo)) {
503
+ namespaces.add(dependencyNamespace);
504
+ }
505
+ }
506
+ const result = Array.from(namespaces).sort((left, right) => left.localeCompare(right));
507
+ memo.set(key, result);
508
+ return result;
509
+ }
510
+ function isRuntimeDependentKey(key, entries, manifest, memo) {
511
+ if (memo.has(key)) {
512
+ return memo.get(key);
513
+ }
514
+ const entry = entries.get(key);
515
+ if (!entry) {
516
+ memo.set(key, false);
517
+ return false;
518
+ }
519
+ for (const ref of entry.parsed.refs) {
520
+ const namespace = namespaceForKey(ref);
521
+ if (manifest.runtimeNamespaces[namespace]) {
522
+ memo.set(key, true);
523
+ return true;
524
+ }
525
+ if (entries.has(ref) && isRuntimeDependentKey(ref, entries, manifest, memo)) {
526
+ memo.set(key, true);
527
+ return true;
528
+ }
529
+ }
530
+ memo.set(key, false);
531
+ return false;
532
+ }
533
+ function prepareEntries(graph, manifest) {
534
+ const entries = /* @__PURE__ */ new Map();
535
+ for (const [key, entry] of graph.entries) {
536
+ if (!isDerivedValue(entry.value)) {
537
+ continue;
538
+ }
539
+ const namespaceDefinition = manifest.namespaces[entry.namespace];
540
+ if (!namespaceDefinition || namespaceDefinition.kind === "data") {
541
+ validateDerivedTargetNamespace(manifest, entry.namespace);
542
+ }
543
+ const parsed = parseDerivation(entry.value);
544
+ validateParsedDerivation(manifest, parsed);
545
+ entries.set(key, {
546
+ key,
547
+ namespace: entry.namespace,
548
+ value: entry.value,
549
+ parsed
550
+ });
551
+ }
552
+ detectDerivationCycles(
553
+ new Map(
554
+ Array.from(entries.values()).map((entry) => [
555
+ entry.key,
556
+ entry.parsed.refs.filter((ref) => entries.has(ref))
557
+ ])
558
+ )
559
+ );
560
+ const runtimeMemo = /* @__PURE__ */ new Map();
561
+ const namespaceMemo = /* @__PURE__ */ new Map();
562
+ for (const entry of entries.values()) {
563
+ entry.parsed.isRuntimeDependent = isRuntimeDependentKey(entry.key, entries, manifest, runtimeMemo);
564
+ entry.parsed.runtimeRefs = entry.parsed.refs.filter((ref) => manifest.runtimeNamespaces[namespaceForKey(ref)]);
565
+ if (entry.parsed.runtimeRefs.length === 0 && entry.parsed.isRuntimeDependent) {
566
+ entry.parsed.runtimeRefs = dependencyNamespaces(entry.key, entries, namespaceMemo).filter((namespace) => manifest.runtimeNamespaces[namespace]).map((namespace) => `${namespace}.*`);
567
+ }
568
+ }
569
+ return entries;
570
+ }
571
+ function createDerivedRuntimeSupport(graph, manifest, runtimeProviders) {
572
+ const entries = prepareEntries(graph, manifest);
573
+ const configCache = /* @__PURE__ */ new Map();
574
+ const runtimeDependencyMemo = /* @__PURE__ */ new Map();
575
+ const support = {};
576
+ Object.assign(support, {
577
+ runtimeProviders,
578
+ read(key, readBase) {
579
+ const namespace = namespaceForKey(key);
580
+ if (runtimeProviders.has(namespace)) {
581
+ const provider = runtimeProviders.get(namespace);
582
+ return provider(key.slice(namespace.length + 1));
583
+ }
584
+ if (entries.has(key)) {
585
+ return readDerived(key, readBase);
586
+ }
587
+ return readBase(key);
588
+ },
589
+ describe(key, readBase) {
590
+ const entry = entries.get(key);
591
+ if (!entry) {
592
+ return void 0;
593
+ }
594
+ const runtimeNamespaces = Array.from(
595
+ new Set(
596
+ entry.parsed.refs.map((ref) => namespaceForKey(ref)).filter((namespace) => manifest.runtimeNamespaces[namespace])
597
+ )
598
+ ).sort((left, right) => left.localeCompare(right));
599
+ return {
600
+ key,
601
+ type: entry.parsed.type,
602
+ expression: entry.parsed.raw,
603
+ dependencies: entry.parsed.refs.map((ref) => {
604
+ const namespace = namespaceForKey(ref);
605
+ return {
606
+ key: ref,
607
+ value: support.read(ref, readBase),
608
+ ...manifest.runtimeNamespaces[namespace] ? {
609
+ runtimeNamespace: namespace
610
+ } : {}
611
+ };
612
+ }),
613
+ runtimeDependent: entry.parsed.isRuntimeDependent,
614
+ runtimeNamespaces,
615
+ ...entry.parsed.isRuntimeDependent ? {
616
+ promotionWarning: "Cannot be promoted to browser/public."
617
+ } : {}
618
+ };
619
+ },
620
+ isDerivedKey(key) {
621
+ return entries.has(key);
622
+ },
623
+ isRuntimeDependentKey(key) {
624
+ if (runtimeDependencyMemo.has(key)) {
625
+ return runtimeDependencyMemo.get(key);
626
+ }
627
+ const value = entries.get(key)?.parsed.isRuntimeDependent ?? false;
628
+ runtimeDependencyMemo.set(key, value);
629
+ return value;
630
+ },
631
+ toConcreteValue(key, readBase, mode) {
632
+ const entry = entries.get(key);
633
+ if (!entry) {
634
+ return support.read(key, readBase);
635
+ }
636
+ if (!entry.parsed.isRuntimeDependent) {
637
+ return support.read(key, readBase);
638
+ }
639
+ if (mode === "server" || mode === "runtime") {
640
+ return support.read(key, readBase);
641
+ }
642
+ for (const ref of entry.parsed.refs) {
643
+ const namespace = namespaceForKey(ref);
644
+ const runtimeNamespace = manifest.runtimeNamespaces[namespace];
645
+ if (!runtimeNamespace) {
646
+ continue;
647
+ }
648
+ if (runtimeNamespace.serverOnly) {
649
+ throw new CnosDerivedResolutionError(
650
+ key,
651
+ `Cannot resolve ${key} for ${mode} output because it depends on runtime namespace ${namespace}.`
652
+ );
653
+ }
654
+ if (!runtimeProviders.has(namespace)) {
655
+ if (mode === "env") {
656
+ return void 0;
657
+ }
658
+ throw new CnosDerivedResolutionError(
659
+ key,
660
+ `Cannot resolve ${key} for ${mode} output because runtime namespace ${namespace} has no registered provider.`
661
+ );
662
+ }
663
+ }
664
+ return support.read(key, readBase);
665
+ },
666
+ toServerFormula(key) {
667
+ const entry = entries.get(key);
668
+ if (!entry || !entry.parsed.isRuntimeDependent) {
669
+ return void 0;
670
+ }
671
+ return {
672
+ expr: entry.parsed.raw,
673
+ deps: entry.parsed.refs.filter((ref) => !manifest.runtimeNamespaces[namespaceForKey(ref)]),
674
+ runtimeRefs: entry.parsed.refs.filter((ref) => manifest.runtimeNamespaces[namespaceForKey(ref)])
675
+ };
676
+ },
677
+ derivedKeys: Array.from(entries.keys()).sort((left, right) => left.localeCompare(right))
678
+ });
679
+ const readDerived = (key, readBase, evaluationStack = /* @__PURE__ */ new Set()) => {
680
+ const entry = entries.get(key);
681
+ if (!entry) {
682
+ return readBase(key);
683
+ }
684
+ if (!entry.parsed.isRuntimeDependent && configCache.has(key)) {
685
+ return configCache.get(key);
686
+ }
687
+ const value = evaluateDerivation({
688
+ key,
689
+ parsed: entry.parsed,
690
+ resolveRef: (ref) => {
691
+ const namespace = namespaceForKey(ref);
692
+ if (runtimeProviders.has(namespace)) {
693
+ const provider = runtimeProviders.get(namespace);
694
+ return provider(ref.slice(namespace.length + 1));
695
+ }
696
+ if (entries.has(ref)) {
697
+ if (evaluationStack.has(ref)) {
698
+ throw new CnosDerivedResolutionError(key, `Unable to resolve derived config key ${key} because of a recursive dependency on ${ref}.`);
699
+ }
700
+ evaluationStack.add(ref);
701
+ const resolved = readDerived(ref, readBase, evaluationStack);
702
+ evaluationStack.delete(ref);
703
+ return resolved;
704
+ }
705
+ return readBase(ref);
706
+ }
707
+ });
708
+ if (!entry.parsed.isRuntimeDependent) {
709
+ configCache.set(key, value);
710
+ }
711
+ return value;
712
+ };
713
+ for (const key of Array.from(entries.keys()).sort((left, right) => left.localeCompare(right))) {
714
+ const entry = entries.get(key);
715
+ if (!entry.parsed.isRuntimeDependent) {
716
+ readDerived(key, (ref) => graph.entries.get(ref)?.value);
717
+ }
718
+ }
719
+ return support;
720
+ }
721
+ function registerRuntimeProvider(manifest, runtimeProviders, namespace, provider) {
722
+ const definition = manifest.runtimeNamespaces[namespace];
723
+ if (!definition) {
724
+ throw new CnosRuntimeProviderError(`Cannot register runtime provider for undeclared namespace "${namespace}".`);
725
+ }
726
+ if (definition.builtIn) {
727
+ throw new CnosRuntimeProviderError(`Cannot override built-in runtime namespace "${namespace}".`);
728
+ }
729
+ runtimeProviders.set(namespace, provider);
730
+ }
75
731
 
76
732
  // ../core/src/runtime/inspect.ts
77
- function inspectValue(graph, key) {
733
+ function inspectValue(graph, key, helpers = {}) {
78
734
  const entry = graph.entries.get(key);
79
735
  if (!entry) {
80
736
  throw new CnosKeyNotFoundError(key);
81
737
  }
738
+ const derived = helpers.describeDerived?.(key);
82
739
  return {
83
740
  key: entry.key,
84
- value: entry.value,
741
+ value: helpers.read ? helpers.read(entry.key) : entry.value,
85
742
  namespace: entry.namespace,
86
743
  profile: graph.profile,
87
744
  profileSource: graph.profileSource,
@@ -102,7 +759,10 @@ function inspectValue(graph, key) {
102
759
  workspaceId: override.workspaceId,
103
760
  value: override.value,
104
761
  ...override.origin ? { origin: override.origin } : {}
105
- }))
762
+ })),
763
+ ...derived ? {
764
+ derived
765
+ } : {}
106
766
  };
107
767
  }
108
768
 
@@ -152,59 +812,364 @@ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3
152
812
  function wrap(script) {
153
813
  return ["-NoProfile", "-Command", script];
154
814
  }
155
- async function readWindowsKeychain(entry) {
156
- try {
157
- const { stdout } = await execFileAsync3(
158
- "powershell",
159
- wrap(`Add-Type -AssemblyName System.Runtime.WindowsRuntime; [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime] > $null; $vault = New-Object Windows.Security.Credentials.PasswordVault; $credential = $vault.Retrieve('cnos','${entry}'); $credential.RetrievePassword(); Write-Output $credential.Password`)
815
+ async function readWindowsKeychain(entry) {
816
+ try {
817
+ const { stdout } = await execFileAsync3(
818
+ "powershell",
819
+ wrap(`Add-Type -AssemblyName System.Runtime.WindowsRuntime; [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime] > $null; $vault = New-Object Windows.Security.Credentials.PasswordVault; $credential = $vault.Retrieve('cnos','${entry}'); $credential.RetrievePassword(); Write-Output $credential.Password`)
820
+ );
821
+ const value = stdout.trim();
822
+ return value.length > 0 ? value : void 0;
823
+ } catch {
824
+ return void 0;
825
+ }
826
+ }
827
+
828
+ // ../core/src/keychain/index.ts
829
+ async function readKeychain(entry) {
830
+ if (process.platform === "win32") {
831
+ return readWindowsKeychain(entry);
832
+ }
833
+ if (process.platform === "darwin") {
834
+ return readMacosKeychain(entry);
835
+ }
836
+ if (process.platform === "linux") {
837
+ return readLinuxKeychain(entry);
838
+ }
839
+ return void 0;
840
+ }
841
+
842
+ // ../core/src/manifest/loadManifest.ts
843
+ var import_promises5 = require("fs/promises");
844
+ var import_node_path6 = __toESM(require("path"), 1);
845
+
846
+ // ../core/src/utils/path.ts
847
+ var import_promises4 = require("fs/promises");
848
+ var import_node_os3 = __toESM(require("os"), 1);
849
+ var import_node_path5 = __toESM(require("path"), 1);
850
+
851
+ // ../core/src/discovery/findCnosrc.ts
852
+ var import_promises3 = require("fs/promises");
853
+ var import_node_path4 = __toESM(require("path"), 1);
854
+
855
+ // ../core/src/utils/yaml.ts
856
+ var import_yaml = require("yaml");
857
+ function parseYaml(source) {
858
+ return (0, import_yaml.parse)(source);
859
+ }
860
+ function stringifyYaml(value) {
861
+ return (0, import_yaml.stringify)(value);
862
+ }
863
+
864
+ // ../core/src/discovery/resolveRoot.ts
865
+ var import_promises2 = require("fs/promises");
866
+ var import_node_path3 = __toESM(require("path"), 1);
867
+ var import_node_child_process4 = require("child_process");
868
+ var import_node_os2 = __toESM(require("os"), 1);
869
+
870
+ // ../core/src/discovery/parseGitUri.ts
871
+ function isGitRootUri(uri) {
872
+ return /^git\+[a-z]+:\/\//i.test(uri);
873
+ }
874
+ function parseGitUri(uri) {
875
+ if (!isGitRootUri(uri)) {
876
+ throw new CnosDiscoveryError(`Unsupported git root URI: ${uri}`);
877
+ }
878
+ const withoutPrefix = uri.slice("git+".length);
879
+ const hashIndex = withoutPrefix.indexOf("#");
880
+ if (hashIndex < 0) {
881
+ throw new CnosDiscoveryError(
882
+ `Git root URI must include a #ref (tag, branch, or commit). Got: ${uri}`
883
+ );
884
+ }
885
+ const cloneUrl = withoutPrefix.slice(0, hashIndex);
886
+ const fragment = withoutPrefix.slice(hashIndex + 1);
887
+ const separatorIndex = fragment.indexOf(":");
888
+ const ref = (separatorIndex >= 0 ? fragment.slice(0, separatorIndex) : fragment).trim();
889
+ const subpath = (separatorIndex >= 0 ? fragment.slice(separatorIndex + 1) : ".cnos").trim() || ".cnos";
890
+ const protocol = cloneUrl.slice(0, cloneUrl.indexOf("://"));
891
+ if (!cloneUrl || !ref) {
892
+ throw new CnosDiscoveryError(
893
+ `Git root URI must include both a clone URL and #ref. Got: ${uri}`
894
+ );
895
+ }
896
+ return {
897
+ uri,
898
+ cloneUrl,
899
+ ref,
900
+ subpath,
901
+ transport: protocol === "https" ? "https" : protocol === "ssh" ? "ssh" : protocol === "file" ? "file" : "custom"
902
+ };
903
+ }
904
+
905
+ // ../core/src/discovery/cache/cacheManager.ts
906
+ var SEMVER_TAG_RE = /^v?\d+\.\d+(?:\.\d+)?(?:[-+][A-Za-z0-9.-]+)?$/;
907
+ var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
908
+ function isImmutableGitRef(ref) {
909
+ return SEMVER_TAG_RE.test(ref) || COMMIT_SHA_RE.test(ref);
910
+ }
911
+ function resolveRemoteRootCacheTtlSeconds(mode = "runtime", processEnv = process.env, override) {
912
+ if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
913
+ return override;
914
+ }
915
+ const fromEnv = Number(processEnv.CNOS_CACHE_TTL ?? "");
916
+ if (Number.isFinite(fromEnv) && fromEnv >= 0) {
917
+ return fromEnv;
918
+ }
919
+ switch (mode) {
920
+ case "build":
921
+ return 0;
922
+ case "dev":
923
+ return 30;
924
+ case "runtime":
925
+ default:
926
+ return 300;
927
+ }
928
+ }
929
+ function isRemoteRootCacheFresh(metadata, options) {
930
+ if (!metadata || options.forceRefresh) {
931
+ return false;
932
+ }
933
+ if (metadata.uri !== options.uri || metadata.ref !== options.ref) {
934
+ return false;
935
+ }
936
+ if (metadata.isImmutable) {
937
+ return true;
938
+ }
939
+ const ttlSeconds = resolveRemoteRootCacheTtlSeconds(
940
+ options.mode,
941
+ options.processEnv,
942
+ options.ttlSeconds
943
+ );
944
+ if (ttlSeconds <= 0) {
945
+ return false;
946
+ }
947
+ const cachedAtMs = Date.parse(metadata.cachedAt);
948
+ if (Number.isNaN(cachedAtMs)) {
949
+ return false;
950
+ }
951
+ return Date.now() - cachedAtMs <= ttlSeconds * 1e3;
952
+ }
953
+
954
+ // ../core/src/discovery/cache/cacheMetadata.ts
955
+ var import_promises = require("fs/promises");
956
+ var import_node_path = __toESM(require("path"), 1);
957
+ async function readRemoteRootCacheMetadata(metaPath) {
958
+ try {
959
+ const source = await (0, import_promises.readFile)(metaPath, "utf8");
960
+ const parsed = JSON.parse(source);
961
+ 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") {
962
+ return void 0;
963
+ }
964
+ return parsed;
965
+ } catch {
966
+ return void 0;
967
+ }
968
+ }
969
+ async function writeRemoteRootCacheMetadata(metaPath, metadata) {
970
+ await (0, import_promises.mkdir)(import_node_path.default.dirname(metaPath), { recursive: true });
971
+ await (0, import_promises.writeFile)(metaPath, `${JSON.stringify(metadata, null, 2)}
972
+ `, "utf8");
973
+ }
974
+
975
+ // ../core/src/discovery/cache/cachePaths.ts
976
+ var import_node_crypto = require("crypto");
977
+ var import_node_os = __toESM(require("os"), 1);
978
+ var import_node_path2 = __toESM(require("path"), 1);
979
+ function resolveCnosCacheRoot(processEnv = process.env) {
980
+ return import_node_path2.default.resolve(
981
+ expandHomePath(processEnv.CNOS_CACHE_DIR ?? import_node_path2.default.join(import_node_os.default.homedir(), ".cnos", "cache"))
982
+ );
983
+ }
984
+ function createRemoteRootCacheKey(uri) {
985
+ return (0, import_node_crypto.createHash)("sha256").update(uri).digest("hex");
986
+ }
987
+ function resolveRemoteRootCachePaths(uri, processEnv = process.env) {
988
+ const cacheRoot = resolveCnosCacheRoot(processEnv);
989
+ const cacheDir = import_node_path2.default.join(cacheRoot, "roots", createRemoteRootCacheKey(uri));
990
+ return {
991
+ cacheRoot,
992
+ cacheDir,
993
+ repoDir: import_node_path2.default.join(cacheDir, "repo"),
994
+ metaPath: import_node_path2.default.join(cacheDir, ".cnos-cache-meta.json")
995
+ };
996
+ }
997
+
998
+ // ../core/src/discovery/resolveRoot.ts
999
+ async function pathExists(targetPath) {
1000
+ try {
1001
+ await (0, import_promises2.access)(targetPath);
1002
+ return true;
1003
+ } catch {
1004
+ return false;
1005
+ }
1006
+ }
1007
+ function expandHomePath2(targetPath) {
1008
+ if (targetPath === "~") {
1009
+ return import_node_os2.default.homedir();
1010
+ }
1011
+ if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
1012
+ return import_node_path3.default.join(import_node_os2.default.homedir(), targetPath.slice(2));
1013
+ }
1014
+ return targetPath;
1015
+ }
1016
+ function isLocalRootUri(rootUri) {
1017
+ return !isGitRootUri2(rootUri) && !isCnosHostedRootUri(rootUri);
1018
+ }
1019
+ function isCnosHostedRootUri(rootUri) {
1020
+ return rootUri.startsWith("cnos://");
1021
+ }
1022
+ function isGitRootUri2(rootUri) {
1023
+ return rootUri.startsWith("git+");
1024
+ }
1025
+ async function runGitCommand(args, options = {}) {
1026
+ return new Promise((resolve, reject) => {
1027
+ const child = (0, import_node_child_process4.spawn)("git", args, {
1028
+ cwd: options.cwd,
1029
+ env: options.processEnv ?? process.env,
1030
+ shell: process.platform === "win32",
1031
+ stdio: ["ignore", "pipe", "pipe"]
1032
+ });
1033
+ let stdout = "";
1034
+ let stderr = "";
1035
+ child.stdout.on("data", (chunk) => {
1036
+ stdout += chunk.toString();
1037
+ });
1038
+ child.stderr.on("data", (chunk) => {
1039
+ stderr += chunk.toString();
1040
+ });
1041
+ child.on("error", (error) => {
1042
+ reject(
1043
+ new CnosDiscoveryError(
1044
+ `Failed to run git. Make sure git is installed and available on PATH. ${error.message}`
1045
+ )
1046
+ );
1047
+ });
1048
+ child.on("close", (code) => {
1049
+ if (code === 0) {
1050
+ resolve(stdout.trim());
1051
+ return;
1052
+ }
1053
+ const details = stderr.trim() || stdout.trim();
1054
+ reject(
1055
+ new CnosDiscoveryError(
1056
+ details ? `Git command failed: ${details}` : `Git command failed with exit code ${code ?? 1}`
1057
+ )
1058
+ );
1059
+ });
1060
+ });
1061
+ }
1062
+ async function resolveLocalRoot(rootUri, cnosrcDir) {
1063
+ const candidateRoot = rootUri.startsWith("~") ? expandHomePath2(rootUri) : rootUri;
1064
+ const manifestRoot = import_node_path3.default.resolve(cnosrcDir, candidateRoot);
1065
+ const manifestPath = import_node_path3.default.join(manifestRoot, "cnos.yml");
1066
+ if (!await pathExists(manifestPath)) {
1067
+ throw new CnosDiscoveryError(`.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`);
1068
+ }
1069
+ return {
1070
+ manifestRoot,
1071
+ resolution: {
1072
+ rootUri,
1073
+ protocol: "local",
1074
+ remote: false,
1075
+ readOnly: false
1076
+ }
1077
+ };
1078
+ }
1079
+ async function ensureGitCheckout(parsed, repoDir, processEnv) {
1080
+ const hasRepo = await pathExists(import_node_path3.default.join(repoDir, ".git"));
1081
+ if (!hasRepo) {
1082
+ await (0, import_promises2.mkdir)(import_node_path3.default.dirname(repoDir), { recursive: true });
1083
+ await runGitCommand(["clone", "--no-checkout", parsed.cloneUrl, repoDir], { processEnv });
1084
+ } else {
1085
+ await runGitCommand(["-C", repoDir, "remote", "set-url", "origin", parsed.cloneUrl], {
1086
+ processEnv
1087
+ });
1088
+ }
1089
+ await runGitCommand(["-C", repoDir, "fetch", "--tags", "--force", "origin"], { processEnv });
1090
+ await runGitCommand(["-C", repoDir, "checkout", "--force", parsed.ref], { processEnv });
1091
+ await runGitCommand(["-C", repoDir, "clean", "-fdx"], { processEnv });
1092
+ }
1093
+ async function resolveGitRoot(rootUri, options = {}) {
1094
+ const processEnv = options.processEnv ?? process.env;
1095
+ const parsed = parseGitUri(rootUri);
1096
+ const cachePaths = resolveRemoteRootCachePaths(rootUri, processEnv);
1097
+ const metadata = await readRemoteRootCacheMetadata(cachePaths.metaPath);
1098
+ const immutable = isImmutableGitRef(parsed.ref);
1099
+ const cacheFresh = isRemoteRootCacheFresh(metadata, {
1100
+ uri: rootUri,
1101
+ ref: parsed.ref,
1102
+ ...options.cacheMode ? { mode: options.cacheMode } : {},
1103
+ processEnv,
1104
+ ...typeof options.cacheTtlSeconds === "number" ? { ttlSeconds: options.cacheTtlSeconds } : {},
1105
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1106
+ });
1107
+ if (!cacheFresh) {
1108
+ try {
1109
+ await ensureGitCheckout(parsed, cachePaths.repoDir, processEnv);
1110
+ } catch (error) {
1111
+ const message = error instanceof Error ? error.message : String(error);
1112
+ const authHint = parsed.transport === "ssh" ? " Check your SSH key and git access." : " Check the URL and your git credential helper or token setup.";
1113
+ throw new CnosDiscoveryError(`Failed to resolve remote git root ${rootUri}. ${message}${authHint}`);
1114
+ }
1115
+ const resolvedCommit = await runGitCommand(["-C", cachePaths.repoDir, "rev-parse", "HEAD"], {
1116
+ processEnv
1117
+ });
1118
+ await writeRemoteRootCacheMetadata(cachePaths.metaPath, {
1119
+ uri: rootUri,
1120
+ cloneUrl: parsed.cloneUrl,
1121
+ ref: parsed.ref,
1122
+ subpath: parsed.subpath,
1123
+ resolvedCommit,
1124
+ cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
1125
+ isImmutable: immutable
1126
+ });
1127
+ }
1128
+ const nextMetadata = metadata && cacheFresh ? metadata : await readRemoteRootCacheMetadata(cachePaths.metaPath);
1129
+ const manifestRoot = import_node_path3.default.join(cachePaths.repoDir, parsed.subpath);
1130
+ if (!await pathExists(import_node_path3.default.join(manifestRoot, "cnos.yml"))) {
1131
+ throw new CnosDiscoveryError(
1132
+ `Git root ${rootUri} resolved to ${manifestRoot} but no cnos.yml was found there. Check the :subpath segment.`
160
1133
  );
161
- const value = stdout.trim();
162
- return value.length > 0 ? value : void 0;
163
- } catch {
164
- return void 0;
165
1134
  }
1135
+ return {
1136
+ manifestRoot,
1137
+ resolution: {
1138
+ rootUri,
1139
+ protocol: "git",
1140
+ remote: true,
1141
+ readOnly: true,
1142
+ cacheDir: cachePaths.cacheDir,
1143
+ cacheMetaPath: cachePaths.metaPath,
1144
+ ref: parsed.ref,
1145
+ subpath: parsed.subpath,
1146
+ immutable,
1147
+ ...nextMetadata?.resolvedCommit ? { resolvedCommit: nextMetadata.resolvedCommit } : {},
1148
+ ...nextMetadata?.cachedAt ? { cachedAt: nextMetadata.cachedAt } : {}
1149
+ }
1150
+ };
166
1151
  }
167
-
168
- // ../core/src/keychain/index.ts
169
- async function readKeychain(entry) {
170
- if (process.platform === "win32") {
171
- return readWindowsKeychain(entry);
1152
+ async function resolveRootUri(rootUri, cnosrcDir, options = {}) {
1153
+ if (isLocalRootUri(rootUri)) {
1154
+ return resolveLocalRoot(rootUri, cnosrcDir);
172
1155
  }
173
- if (process.platform === "darwin") {
174
- return readMacosKeychain(entry);
1156
+ if (isGitRootUri2(rootUri)) {
1157
+ return resolveGitRoot(rootUri, options);
175
1158
  }
176
- if (process.platform === "linux") {
177
- return readLinuxKeychain(entry);
1159
+ if (isCnosHostedRootUri(rootUri)) {
1160
+ throw new CnosDiscoveryError(
1161
+ `The cnos:// remote root protocol is reserved but not implemented yet. Use git+https:// or git+ssh:// for now.`
1162
+ );
178
1163
  }
179
- return void 0;
180
- }
181
-
182
- // ../core/src/manifest/loadManifest.ts
183
- var import_promises3 = require("fs/promises");
184
- var import_node_path3 = __toESM(require("path"), 1);
185
-
186
- // ../core/src/utils/path.ts
187
- var import_promises2 = require("fs/promises");
188
- var import_node_os = __toESM(require("os"), 1);
189
- var import_node_path2 = __toESM(require("path"), 1);
190
-
191
- // ../core/src/discovery/findCnosrc.ts
192
- var import_promises = require("fs/promises");
193
- var import_node_path = __toESM(require("path"), 1);
194
-
195
- // ../core/src/utils/yaml.ts
196
- var import_yaml = require("yaml");
197
- function parseYaml(source) {
198
- return (0, import_yaml.parse)(source);
199
- }
200
- function stringifyYaml(value) {
201
- return (0, import_yaml.stringify)(value);
1164
+ throw new CnosDiscoveryError(
1165
+ `Unknown root protocol: ${rootUri}. Supported root protocols are local paths, git+https://..., and git+ssh://....`
1166
+ );
202
1167
  }
203
1168
 
204
1169
  // ../core/src/discovery/findCnosrc.ts
205
1170
  async function exists(targetPath) {
206
1171
  try {
207
- await (0, import_promises.access)(targetPath);
1172
+ await (0, import_promises3.access)(targetPath);
208
1173
  return true;
209
1174
  } catch {
210
1175
  return false;
@@ -225,13 +1190,13 @@ function validateCnosrc(value, filePath) {
225
1190
  };
226
1191
  }
227
1192
  async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
228
- let current = import_node_path.default.resolve(startDir);
1193
+ let current = import_node_path4.default.resolve(startDir);
229
1194
  for (let depth = 0; depth <= maxLevels; depth += 1) {
230
- const candidate = import_node_path.default.join(current, ".cnosrc.yml");
1195
+ const candidate = import_node_path4.default.join(current, ".cnosrc.yml");
231
1196
  if (await exists(candidate)) {
232
1197
  return candidate;
233
1198
  }
234
- const parent = import_node_path.default.dirname(current);
1199
+ const parent = import_node_path4.default.dirname(current);
235
1200
  if (parent === current) {
236
1201
  break;
237
1202
  }
@@ -239,27 +1204,22 @@ async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
239
1204
  }
240
1205
  return void 0;
241
1206
  }
242
- async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3) {
1207
+ async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3, options = {}) {
243
1208
  const anchorPath = await findCnosrc(startDir, maxLevels);
244
1209
  if (!anchorPath) {
245
1210
  throw new CnosDiscoveryError(
246
1211
  "No .cnosrc.yml found. Run cnos init or create .cnosrc.yml in your package root."
247
1212
  );
248
1213
  }
249
- const source = await (0, import_promises.readFile)(anchorPath, "utf8");
1214
+ const source = await (0, import_promises3.readFile)(anchorPath, "utf8");
250
1215
  const parsed = validateCnosrc(parseYaml(source), anchorPath);
251
- const consumerRoot = import_node_path.default.dirname(anchorPath);
252
- const manifestRoot = import_node_path.default.resolve(consumerRoot, parsed.root);
253
- const manifestPath = import_node_path.default.join(manifestRoot, "cnos.yml");
254
- if (!await exists(manifestPath)) {
255
- throw new CnosDiscoveryError(
256
- `.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`
257
- );
258
- }
1216
+ const consumerRoot = import_node_path4.default.dirname(anchorPath);
1217
+ const resolvedRoot = await resolveRootUri(parsed.root, consumerRoot, options);
259
1218
  return {
260
1219
  anchorPath,
261
1220
  consumerRoot,
262
- manifestRoot,
1221
+ manifestRoot: resolvedRoot.manifestRoot,
1222
+ rootResolution: resolvedRoot.resolution,
263
1223
  ...parsed.workspace ? { workspace: parsed.workspace } : {}
264
1224
  };
265
1225
  }
@@ -269,21 +1229,21 @@ var PRIMARY_CNOS_DIR = ".cnos";
269
1229
  var LEGACY_CNOS_DIR = "cnos";
270
1230
  async function exists2(filePath) {
271
1231
  try {
272
- await (0, import_promises2.access)(filePath);
1232
+ await (0, import_promises4.access)(filePath);
273
1233
  return true;
274
1234
  } catch {
275
1235
  return false;
276
1236
  }
277
1237
  }
278
1238
  async function resolveCnosRoot(root = process.cwd()) {
279
- const basePath = import_node_path2.default.resolve(root);
1239
+ const basePath = import_node_path5.default.resolve(root);
280
1240
  const candidates = [
281
- import_node_path2.default.join(basePath, PRIMARY_CNOS_DIR),
282
- import_node_path2.default.join(basePath, LEGACY_CNOS_DIR),
1241
+ import_node_path5.default.join(basePath, PRIMARY_CNOS_DIR),
1242
+ import_node_path5.default.join(basePath, LEGACY_CNOS_DIR),
283
1243
  basePath
284
1244
  ];
285
1245
  for (const candidate of candidates) {
286
- if (await exists2(import_node_path2.default.join(candidate, "cnos.yml"))) {
1246
+ if (await exists2(import_node_path5.default.join(candidate, "cnos.yml"))) {
287
1247
  return candidate;
288
1248
  }
289
1249
  }
@@ -293,18 +1253,44 @@ async function resolveCnosRoot(root = process.cwd()) {
293
1253
  }
294
1254
  async function resolveManifestRoot(options = {}) {
295
1255
  if (options.root) {
1256
+ if (options.root.startsWith("git+") || options.root.startsWith("cnos://")) {
1257
+ const consumerRoot2 = import_node_path5.default.resolve(options.cwd ?? process.cwd());
1258
+ const resolvedRoot2 = await resolveRootUri(options.root, consumerRoot2, {
1259
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1260
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1261
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1262
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1263
+ });
1264
+ return {
1265
+ manifestRoot: resolvedRoot2.manifestRoot,
1266
+ consumerRoot: consumerRoot2,
1267
+ rootResolution: resolvedRoot2.resolution
1268
+ };
1269
+ }
296
1270
  const manifestRoot = await resolveCnosRoot(options.root);
297
- const resolvedRoot = import_node_path2.default.resolve(options.root);
298
- 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;
1271
+ const resolvedRoot = import_node_path5.default.resolve(options.root);
1272
+ 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;
299
1273
  return {
300
1274
  manifestRoot,
301
- consumerRoot
1275
+ consumerRoot,
1276
+ rootResolution: {
1277
+ rootUri: manifestRoot,
1278
+ protocol: "local",
1279
+ remote: false,
1280
+ readOnly: false
1281
+ }
302
1282
  };
303
1283
  }
304
- const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd());
1284
+ const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd(), 3, {
1285
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1286
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1287
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1288
+ ...options.forceRefresh ? { forceRefresh: true } : {}
1289
+ });
305
1290
  return {
306
1291
  manifestRoot: discovered.manifestRoot,
307
1292
  consumerRoot: discovered.consumerRoot,
1293
+ rootResolution: discovered.rootResolution,
308
1294
  anchorPath: discovered.anchorPath,
309
1295
  ...discovered.workspace ? { workspace: discovered.workspace } : {}
310
1296
  };
@@ -317,10 +1303,10 @@ function interpolatePathTemplate(template, tokens) {
317
1303
  }
318
1304
  function expandHomePath(targetPath) {
319
1305
  if (targetPath === "~") {
320
- return import_node_os.default.homedir();
1306
+ return import_node_os3.default.homedir();
321
1307
  }
322
1308
  if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
323
- return import_node_path2.default.join(import_node_os.default.homedir(), targetPath.slice(2));
1309
+ return import_node_path5.default.join(import_node_os3.default.homedir(), targetPath.slice(2));
324
1310
  }
325
1311
  return targetPath;
326
1312
  }
@@ -338,7 +1324,7 @@ function stripWorkspaceTemplatePrefix(template) {
338
1324
  function resolveWorkspaceScopedPath(workspaceRoot, template, tokens) {
339
1325
  const relativeTemplate = stripWorkspaceTemplatePrefix(template);
340
1326
  const interpolated = interpolatePathTemplate(relativeTemplate, tokens);
341
- return import_node_path2.default.resolve(workspaceRoot, interpolated);
1327
+ return import_node_path5.default.resolve(workspaceRoot, interpolated);
342
1328
  }
343
1329
  function toPortablePath(targetPath) {
344
1330
  return targetPath.replace(/\\/g, "/");
@@ -404,6 +1390,13 @@ var DEFAULT_NAMESPACES = {
404
1390
  readonly: true
405
1391
  }
406
1392
  };
1393
+ var DEFAULT_RUNTIME_NAMESPACES = {
1394
+ process: {
1395
+ description: "Live process runtime values.",
1396
+ serverOnly: true,
1397
+ builtIn: true
1398
+ }
1399
+ };
407
1400
  function validateResolveFrom(resolveFrom) {
408
1401
  const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
409
1402
  for (const entry of resolveFrom) {
@@ -426,7 +1419,7 @@ function normalizeWorkspaceItems(items) {
426
1419
  }
427
1420
  function normalizeNamespaces(namespaces) {
428
1421
  const normalized = Object.fromEntries(
429
- Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
1422
+ Object.entries(namespaces ?? {}).filter(([namespace]) => namespace !== "runtime").map(([namespace, definition]) => [
430
1423
  namespace,
431
1424
  {
432
1425
  kind: definition.kind ?? "data",
@@ -442,6 +1435,29 @@ function normalizeNamespaces(namespaces) {
442
1435
  ...normalized
443
1436
  };
444
1437
  }
1438
+ function normalizeRuntimeNamespaces(namespaces) {
1439
+ const runtimeEntries = namespaces?.runtime ?? {};
1440
+ const normalized = Object.fromEntries(
1441
+ Object.entries(runtimeEntries).map(([namespace, definition]) => [
1442
+ namespace,
1443
+ {
1444
+ ...definition.description?.trim() ? {
1445
+ description: definition.description.trim()
1446
+ } : {},
1447
+ serverOnly: definition.server_only ?? true
1448
+ }
1449
+ ])
1450
+ );
1451
+ for (const namespace of Object.keys(normalized)) {
1452
+ if (DEFAULT_NAMESPACES[namespace] || namespace === "runtime") {
1453
+ throw new CnosManifestError(`Runtime namespace "${namespace}" conflicts with a built-in or reserved namespace.`);
1454
+ }
1455
+ }
1456
+ return {
1457
+ ...DEFAULT_RUNTIME_NAMESPACES,
1458
+ ...normalized
1459
+ };
1460
+ }
445
1461
  function normalizeVaults(vaults) {
446
1462
  return Object.fromEntries(
447
1463
  Object.entries(vaults ?? {}).map(([name, definition]) => {
@@ -533,6 +1549,7 @@ function normalizeManifest(manifest) {
533
1549
  const defaultProfile = manifest.profiles?.default?.trim() || "base";
534
1550
  const workspaceItems = normalizeWorkspaceItems(manifest.workspaces?.items);
535
1551
  const resolveFrom = validateResolveFrom(manifest.profiles?.resolveFrom ?? DEFAULT_RESOLVE_FROM);
1552
+ const runtimeNamespaces = normalizeRuntimeNamespaces(manifest.namespaces);
536
1553
  const filesystemValues = {
537
1554
  root: "./",
538
1555
  format: "yaml",
@@ -606,6 +1623,7 @@ function normalizeManifest(manifest) {
606
1623
  }
607
1624
  },
608
1625
  namespaces: normalizeNamespaces(manifest.namespaces),
1626
+ runtimeNamespaces,
609
1627
  vaults: normalizeVaults(manifest.vaults),
610
1628
  writePolicy: {
611
1629
  define: {
@@ -624,13 +1642,17 @@ function normalizeManifest(manifest) {
624
1642
  async function loadManifest(options = {}) {
625
1643
  const resolved = await resolveManifestRoot({
626
1644
  ...options.root ? { root: options.root } : {},
627
- ...options.cwd ? { cwd: options.cwd } : {}
1645
+ ...options.cwd ? { cwd: options.cwd } : {},
1646
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
1647
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
1648
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
1649
+ ...options.forceRefresh ? { forceRefresh: true } : {}
628
1650
  });
629
1651
  const manifestRoot = resolved.manifestRoot;
630
- const manifestPath = import_node_path3.default.join(manifestRoot, "cnos.yml");
1652
+ const manifestPath = import_node_path6.default.join(manifestRoot, "cnos.yml");
631
1653
  let source;
632
1654
  try {
633
- source = await (0, import_promises3.readFile)(manifestPath, "utf8");
1655
+ source = await (0, import_promises5.readFile)(manifestPath, "utf8");
634
1656
  } catch {
635
1657
  throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
636
1658
  }
@@ -640,10 +1662,11 @@ async function loadManifest(options = {}) {
640
1662
  }
641
1663
  return {
642
1664
  manifestRoot,
643
- repoRoot: import_node_path3.default.dirname(manifestRoot),
1665
+ repoRoot: import_node_path6.default.dirname(manifestRoot),
644
1666
  consumerRoot: resolved.consumerRoot,
645
1667
  ...resolved.anchorPath ? { anchorPath: resolved.anchorPath } : {},
646
1668
  ...resolved.workspace ? { anchoredWorkspace: resolved.workspace } : {},
1669
+ rootResolution: resolved.rootResolution,
647
1670
  manifestPath,
648
1671
  manifest: normalizeManifest(rawManifest),
649
1672
  rawManifest
@@ -651,12 +1674,12 @@ async function loadManifest(options = {}) {
651
1674
  }
652
1675
 
653
1676
  // ../core/src/manifest/loadWorkspaceFile.ts
654
- var import_promises4 = require("fs/promises");
655
- var import_node_path4 = __toESM(require("path"), 1);
1677
+ var import_promises6 = require("fs/promises");
1678
+ var import_node_path7 = __toESM(require("path"), 1);
656
1679
  async function loadWorkspaceFile(repoRoot) {
657
- const workspaceFilePath = import_node_path4.default.join(repoRoot, ".cnos-workspace.yml");
1680
+ const workspaceFilePath = import_node_path7.default.join(repoRoot, ".cnos-workspace.yml");
658
1681
  try {
659
- const source = await (0, import_promises4.readFile)(workspaceFilePath, "utf8");
1682
+ const source = await (0, import_promises6.readFile)(workspaceFilePath, "utf8");
660
1683
  const parsed = parseYaml(source);
661
1684
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
662
1685
  throw new CnosManifestError(".cnos-workspace.yml must be a YAML object", workspaceFilePath);
@@ -679,11 +1702,11 @@ async function loadWorkspaceFile(repoRoot) {
679
1702
  }
680
1703
 
681
1704
  // ../core/src/profiles/expandProfileChain.ts
682
- var import_promises5 = require("fs/promises");
683
- var import_node_path5 = __toESM(require("path"), 1);
1705
+ var import_promises7 = require("fs/promises");
1706
+ var import_node_path8 = __toESM(require("path"), 1);
684
1707
  async function fileExists(targetPath) {
685
1708
  try {
686
- await (0, import_promises5.access)(targetPath);
1709
+ await (0, import_promises7.access)(targetPath);
687
1710
  return true;
688
1711
  } catch {
689
1712
  return false;
@@ -717,11 +1740,11 @@ async function loadProfileDefinition(profileName, options) {
717
1740
  return normalizeProfileDefinition(profileName, void 0);
718
1741
  }
719
1742
  for (const workspaceRoot of [...workspaceRoots].reverse()) {
720
- const profilePath = import_node_path5.default.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
1743
+ const profilePath = import_node_path8.default.join(workspaceRoot.path, "profiles", `${profileName}.yml`);
721
1744
  if (!await fileExists(profilePath)) {
722
1745
  continue;
723
1746
  }
724
- const document = await (0, import_promises5.readFile)(profilePath, "utf8");
1747
+ const document = await (0, import_promises7.readFile)(profilePath, "utf8");
725
1748
  const parsed = parseYaml(document);
726
1749
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
727
1750
  throw new CnosManifestError("Profile definition must be a YAML object", profilePath);
@@ -729,7 +1752,7 @@ async function loadProfileDefinition(profileName, options) {
729
1752
  const definition = normalizeProfileDefinition(
730
1753
  profileName,
731
1754
  parsed,
732
- options.manifestRoot ? toPortablePath(import_node_path5.default.relative(import_node_path5.default.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
1755
+ options.manifestRoot ? toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(options.manifestRoot), profilePath)) : toPortablePath(profilePath)
733
1756
  );
734
1757
  if (definition.name !== profileName) {
735
1758
  throw new CnosManifestError(
@@ -1011,8 +2034,8 @@ function createProfileAwareResolver() {
1011
2034
  }
1012
2035
 
1013
2036
  // ../core/src/workspaces/resolveWorkspaceContext.ts
1014
- var import_promises6 = require("fs/promises");
1015
- var import_node_path6 = __toESM(require("path"), 1);
2037
+ var import_promises8 = require("fs/promises");
2038
+ var import_node_path9 = __toESM(require("path"), 1);
1016
2039
 
1017
2040
  // ../core/src/workspaces/expandWorkspaceChain.ts
1018
2041
  function expandWorkspaceChain(workspaceId, items) {
@@ -1051,14 +2074,14 @@ function expandWorkspaceChain(workspaceId, items) {
1051
2074
  // ../core/src/workspaces/resolveWorkspaceContext.ts
1052
2075
  async function exists3(targetPath) {
1053
2076
  try {
1054
- await (0, import_promises6.access)(targetPath);
2077
+ await (0, import_promises8.access)(targetPath);
1055
2078
  return true;
1056
2079
  } catch {
1057
2080
  return false;
1058
2081
  }
1059
2082
  }
1060
2083
  async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
1061
- const workspaceRoot = import_node_path6.default.join(manifestRoot, "workspaces", workspaceId);
2084
+ const workspaceRoot = import_node_path9.default.join(manifestRoot, "workspaces", workspaceId);
1062
2085
  if (await exists3(workspaceRoot)) {
1063
2086
  return workspaceRoot;
1064
2087
  }
@@ -1066,7 +2089,7 @@ async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
1066
2089
  ([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
1067
2090
  ).map(([namespace]) => namespace);
1068
2091
  const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
1069
- (segment) => import_node_path6.default.join(manifestRoot, segment)
2092
+ (segment) => import_node_path9.default.join(manifestRoot, segment)
1070
2093
  );
1071
2094
  if ((await Promise.all(legacyMarkers.map((marker) => exists3(marker)))).some(Boolean)) {
1072
2095
  return manifestRoot;
@@ -1114,26 +2137,26 @@ function resolveGlobalRoot(manifest, workspaceFile, options) {
1114
2137
  }
1115
2138
  if (options.globalRoot) {
1116
2139
  return {
1117
- value: import_node_path6.default.resolve(expandHomePath(options.globalRoot)),
2140
+ value: import_node_path9.default.resolve(expandHomePath(options.globalRoot)),
1118
2141
  source: "cli"
1119
2142
  };
1120
2143
  }
1121
2144
  if (workspaceFile?.globalRoot) {
1122
2145
  return {
1123
- value: import_node_path6.default.resolve(expandHomePath(workspaceFile.globalRoot)),
2146
+ value: import_node_path9.default.resolve(expandHomePath(workspaceFile.globalRoot)),
1124
2147
  source: "workspace-file"
1125
2148
  };
1126
2149
  }
1127
2150
  if (manifest.workspaces.global.root) {
1128
2151
  return {
1129
- value: import_node_path6.default.resolve(expandHomePath(manifest.workspaces.global.root)),
2152
+ value: import_node_path9.default.resolve(expandHomePath(manifest.workspaces.global.root)),
1130
2153
  source: "manifest"
1131
2154
  };
1132
2155
  }
1133
2156
  const cnosHome = options.processEnv?.CNOS_HOME;
1134
2157
  if (cnosHome) {
1135
2158
  return {
1136
- value: import_node_path6.default.resolve(expandHomePath(cnosHome)),
2159
+ value: import_node_path9.default.resolve(expandHomePath(cnosHome)),
1137
2160
  source: "CNOS_HOME"
1138
2161
  };
1139
2162
  }
@@ -1155,7 +2178,7 @@ async function resolveWorkspaceContext(manifest, options) {
1155
2178
  workspaceRoots.push({
1156
2179
  scope: "global",
1157
2180
  workspaceId: chainWorkspaceId,
1158
- path: import_node_path6.default.join(globalRoot.value, "workspaces", globalWorkspaceId)
2181
+ path: import_node_path9.default.join(globalRoot.value, "workspaces", globalWorkspaceId)
1159
2182
  });
1160
2183
  }
1161
2184
  }
@@ -1278,6 +2301,10 @@ function applySchemaRules(graph, schema) {
1278
2301
  }
1279
2302
  continue;
1280
2303
  }
2304
+ if (isDerivedValue(resolvedEntry.value)) {
2305
+ nextEntries.set(key, resolvedEntry);
2306
+ continue;
2307
+ }
1281
2308
  const coercedValue = coerceValue(resolvedEntry.value, rule);
1282
2309
  const nextResolvedEntry = coercedValue === resolvedEntry.value ? resolvedEntry : {
1283
2310
  ...resolvedEntry,
@@ -1347,26 +2374,26 @@ async function runPipeline(options) {
1347
2374
  }
1348
2375
 
1349
2376
  // ../core/src/secrets/auditLog.ts
1350
- var import_promises9 = require("fs/promises");
1351
- var import_node_path9 = __toESM(require("path"), 1);
2377
+ var import_promises11 = require("fs/promises");
2378
+ var import_node_path12 = __toESM(require("path"), 1);
1352
2379
 
1353
2380
  // ../core/src/utils/secretStore.ts
1354
- var import_node_crypto = require("crypto");
1355
- var import_promises8 = require("fs/promises");
1356
- var import_node_path8 = __toESM(require("path"), 1);
2381
+ var import_node_crypto2 = require("crypto");
2382
+ var import_promises10 = require("fs/promises");
2383
+ var import_node_path11 = __toESM(require("path"), 1);
1357
2384
 
1358
2385
  // ../core/src/secrets/sessionStore.ts
1359
- var import_promises7 = require("fs/promises");
1360
- var import_node_path7 = __toESM(require("path"), 1);
2386
+ var import_promises9 = require("fs/promises");
2387
+ var import_node_path10 = __toESM(require("path"), 1);
1361
2388
  function buildSessionRoot(processEnv = process.env) {
1362
- return import_node_path7.default.join(import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
2389
+ return import_node_path10.default.join(import_node_path10.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
1363
2390
  }
1364
2391
  function buildSessionPath(vault, processEnv) {
1365
- return import_node_path7.default.join(buildSessionRoot(processEnv), `${vault}.json`);
2392
+ return import_node_path10.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1366
2393
  }
1367
2394
  async function readVaultSessionKey(vault, processEnv) {
1368
2395
  try {
1369
- const source = await (0, import_promises7.readFile)(buildSessionPath(vault, processEnv), "utf8");
2396
+ const source = await (0, import_promises9.readFile)(buildSessionPath(vault, processEnv), "utf8");
1370
2397
  const document = JSON.parse(source);
1371
2398
  if (document.version !== 1 || typeof document.derivedKey !== "string") {
1372
2399
  return void 0;
@@ -1395,7 +2422,7 @@ function isSecretReference(value) {
1395
2422
  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));
1396
2423
  }
1397
2424
  function resolveSecretStoreRoot(processEnv = process.env) {
1398
- return import_node_path8.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
2425
+ return import_node_path11.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1399
2426
  }
1400
2427
  function normalizeVaultToken(vault = "default") {
1401
2428
  return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
@@ -1424,19 +2451,19 @@ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1424
2451
  }
1425
2452
  }
1426
2453
  function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1427
- return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
2454
+ return (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1428
2455
  }
1429
2456
  function buildMetaPath(storeRoot, vault = "default") {
1430
- return import_node_path8.default.join(storeRoot, "vaults", vault, META_FILENAME);
2457
+ return import_node_path11.default.join(storeRoot, "vaults", vault, META_FILENAME);
1431
2458
  }
1432
2459
  function buildKeystorePath(storeRoot, vault = "default") {
1433
- return import_node_path8.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
2460
+ return import_node_path11.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1434
2461
  }
1435
2462
  function buildLegacyVaultFile(storeRoot, vault = "default") {
1436
- return import_node_path8.default.join(storeRoot, "vaults", `${vault}.json`);
2463
+ return import_node_path11.default.join(storeRoot, "vaults", `${vault}.json`);
1437
2464
  }
1438
2465
  function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1439
- return import_node_path8.default.join(storeRoot, "vaults", vault, "store");
2466
+ return import_node_path11.default.join(storeRoot, "vaults", vault, "store");
1440
2467
  }
1441
2468
  function assertVaultMetadata(value, filePath) {
1442
2469
  if (!isObject(value)) {
@@ -1449,7 +2476,7 @@ function assertVaultMetadata(value, filePath) {
1449
2476
  }
1450
2477
  async function exists4(targetPath) {
1451
2478
  try {
1452
- await (0, import_promises8.stat)(targetPath);
2479
+ await (0, import_promises10.stat)(targetPath);
1453
2480
  return true;
1454
2481
  } catch {
1455
2482
  return false;
@@ -1476,8 +2503,8 @@ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1476
2503
  );
1477
2504
  }
1478
2505
  function encryptPayload(payload, key) {
1479
- const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1480
- const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
2506
+ const iv = (0, import_node_crypto2.randomBytes)(IV_LENGTH);
2507
+ const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key, iv);
1481
2508
  const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1482
2509
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1483
2510
  const tag = cipher.getAuthTag();
@@ -1502,7 +2529,7 @@ function decryptPayload(buffer, key) {
1502
2529
  const iv = buffer.subarray(ivOffset, tagOffset);
1503
2530
  const tag = buffer.subarray(tagOffset, cipherOffset);
1504
2531
  const ciphertext = buffer.subarray(cipherOffset);
1505
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
2532
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
1506
2533
  decipher.setAuthTag(tag);
1507
2534
  try {
1508
2535
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
@@ -1533,15 +2560,15 @@ function buildInitialPayload() {
1533
2560
  async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1534
2561
  const metaPath = buildMetaPath(storeRoot, vault);
1535
2562
  const keystorePath = buildKeystorePath(storeRoot, vault);
1536
- await (0, import_promises8.mkdir)(import_node_path8.default.dirname(metaPath), { recursive: true });
1537
- await (0, import_promises8.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1538
- await (0, import_promises8.writeFile)(keystorePath, encryptPayload(payload, key));
2563
+ await (0, import_promises10.mkdir)(import_node_path11.default.dirname(metaPath), { recursive: true });
2564
+ await (0, import_promises10.writeFile)(metaPath, stringifyYaml(meta), "utf8");
2565
+ await (0, import_promises10.writeFile)(keystorePath, encryptPayload(payload, key));
1539
2566
  }
1540
2567
  async function readVaultMetadata(storeRoot, vault = "default") {
1541
2568
  await assertNoLegacyVaultFormat(storeRoot, vault);
1542
2569
  const metaPath = buildMetaPath(storeRoot, vault);
1543
2570
  try {
1544
- const source = await (0, import_promises8.readFile)(metaPath, "utf8");
2571
+ const source = await (0, import_promises10.readFile)(metaPath, "utf8");
1545
2572
  return assertVaultMetadata(parseYaml(source), metaPath);
1546
2573
  } catch (error) {
1547
2574
  if (error.code === "ENOENT") {
@@ -1553,7 +2580,7 @@ async function readVaultMetadata(storeRoot, vault = "default") {
1553
2580
  async function createSecretVault(storeRoot, vault, passphrase) {
1554
2581
  const normalizedVault = vault.trim() || "default";
1555
2582
  await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1556
- const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
2583
+ const salt = (0, import_node_crypto2.randomBytes)(SALT_LENGTH);
1557
2584
  const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1558
2585
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1559
2586
  const meta = {
@@ -1632,7 +2659,7 @@ async function loadVaultPayload(storeRoot, vault, auth) {
1632
2659
  if (!key) {
1633
2660
  throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1634
2661
  }
1635
- const buffer = await (0, import_promises8.readFile)(buildKeystorePath(storeRoot, vault));
2662
+ const buffer = await (0, import_promises10.readFile)(buildKeystorePath(storeRoot, vault));
1636
2663
  return {
1637
2664
  meta,
1638
2665
  payload: decryptPayload(buffer, key),
@@ -1707,9 +2734,9 @@ function resolveVaultDefinition(vaults, vault = "default") {
1707
2734
 
1708
2735
  // ../core/src/secrets/auditLog.ts
1709
2736
  async function appendAuditEvent(event, processEnv = process.env) {
1710
- const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path9.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1711
- await (0, import_promises9.mkdir)(import_node_path9.default.dirname(auditFile), { recursive: true });
1712
- await (0, import_promises9.appendFile)(
2737
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path12.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
2738
+ await (0, import_promises11.mkdir)(import_node_path12.default.dirname(auditFile), { recursive: true });
2739
+ await (0, import_promises11.appendFile)(
1713
2740
  auditFile,
1714
2741
  `${JSON.stringify({
1715
2742
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2067,7 +3094,7 @@ function setNestedValue(target, pathSegments, value) {
2067
3094
  target[head] = nextTarget;
2068
3095
  setNestedValue(nextTarget, tail, value);
2069
3096
  }
2070
- function toNamespaceObject(graph, namespace) {
3097
+ function toNamespaceObject(graph, namespace, readValueForKey = (key) => graph.entries.get(key)?.value) {
2071
3098
  const output = {};
2072
3099
  const resolvedEntries = Array.from(graph.entries.values()).sort(
2073
3100
  (left, right) => left.key.localeCompare(right.key)
@@ -2077,7 +3104,11 @@ function toNamespaceObject(graph, namespace) {
2077
3104
  continue;
2078
3105
  }
2079
3106
  const valuePath = namespace ? stripNamespace(entry.key) : entry.key;
2080
- setNestedValue(output, valuePath.split("."), entry.value);
3107
+ const value = readValueForKey(entry.key);
3108
+ if (value === void 0) {
3109
+ continue;
3110
+ }
3111
+ setNestedValue(output, valuePath.split("."), value);
2081
3112
  }
2082
3113
  return output;
2083
3114
  }
@@ -2087,12 +3118,6 @@ function readValue(graph, key) {
2087
3118
  return graph.entries.get(key)?.value;
2088
3119
  }
2089
3120
 
2090
- // ../core/src/runtime/readOr.ts
2091
- function readOrValue(graph, key, fallback) {
2092
- const value = readValue(graph, key);
2093
- return value === void 0 ? fallback : value;
2094
- }
2095
-
2096
3121
  // ../core/src/runtime/require.ts
2097
3122
  function requireValue(graph, key) {
2098
3123
  const value = readValue(graph, key);
@@ -2102,8 +3127,38 @@ function requireValue(graph, key) {
2102
3127
  return value;
2103
3128
  }
2104
3129
 
3130
+ // ../core/src/runtime/runtimeProviders.ts
3131
+ function createDefaultRuntimeProviders(manifest, processEnv) {
3132
+ const providers = /* @__PURE__ */ new Map();
3133
+ if (manifest.runtimeNamespaces.process) {
3134
+ providers.set("process", (key) => {
3135
+ const segments = key.split(".");
3136
+ if (segments[0] === "env") {
3137
+ return processEnv[segments.slice(1).join(".")];
3138
+ }
3139
+ if (key === "cwd") {
3140
+ return process.cwd();
3141
+ }
3142
+ if (key === "platform") {
3143
+ return process.platform;
3144
+ }
3145
+ if (key === "arch") {
3146
+ return process.arch;
3147
+ }
3148
+ if (key === "pid") {
3149
+ return process.pid;
3150
+ }
3151
+ if (key === "node.version") {
3152
+ return process.version;
3153
+ }
3154
+ return void 0;
3155
+ });
3156
+ }
3157
+ return providers;
3158
+ }
3159
+
2105
3160
  // ../core/src/runtime/toServerProjection.ts
2106
- var import_node_crypto2 = require("crypto");
3161
+ var import_node_crypto3 = require("crypto");
2107
3162
  function stableSortObject(value) {
2108
3163
  return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
2109
3164
  }
@@ -2112,12 +3167,14 @@ function stripValuePrefix(key) {
2112
3167
  }
2113
3168
  function configHash(values) {
2114
3169
  const serialized = JSON.stringify(stableSortObject(values));
2115
- return (0, import_node_crypto2.createHash)("sha256").update(serialized).digest("hex");
3170
+ return (0, import_node_crypto3.createHash)("sha256").update(serialized).digest("hex");
2116
3171
  }
2117
- function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
3172
+ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev", helpers = {}) {
2118
3173
  const values = {};
3174
+ const derived = {};
2119
3175
  const secretRefs = {};
2120
3176
  const namespaces = /* @__PURE__ */ new Set();
3177
+ const runtimeNamespaces = /* @__PURE__ */ new Set();
2121
3178
  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));
2122
3179
  for (const [key, entry] of graph.entries) {
2123
3180
  if (entry.namespace === "secret" && isSecretReference(entry.value)) {
@@ -2129,12 +3186,33 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
2129
3186
  continue;
2130
3187
  }
2131
3188
  if (entry.namespace === "value") {
2132
- values[stripValuePrefix(key)] = entry.value;
3189
+ if (helpers.isRuntimeDependent?.(key)) {
3190
+ const formula = helpers.toServerFormula?.(key);
3191
+ if (formula) {
3192
+ derived[stripValuePrefix(key)] = formula;
3193
+ for (const ref of formula.runtimeRefs) {
3194
+ runtimeNamespaces.add(ref.split(".")[0] ?? "");
3195
+ }
3196
+ }
3197
+ continue;
3198
+ }
3199
+ const value = helpers.read ? helpers.read(key) : entry.value;
3200
+ values[stripValuePrefix(key)] = value;
2133
3201
  continue;
2134
3202
  }
2135
3203
  const namespaceDefinition = manifest.namespaces[entry.namespace];
2136
3204
  if (namespaceDefinition && namespaceDefinition.kind === "data" && !namespaceDefinition.sensitive && entry.namespace !== "public") {
2137
- values[key] = entry.value;
3205
+ if (helpers.isRuntimeDependent?.(key)) {
3206
+ const formula = helpers.toServerFormula?.(key);
3207
+ if (formula) {
3208
+ derived[key] = formula;
3209
+ for (const ref of formula.runtimeRefs) {
3210
+ runtimeNamespaces.add(ref.split(".")[0] ?? "");
3211
+ }
3212
+ }
3213
+ continue;
3214
+ }
3215
+ values[key] = helpers.read ? helpers.read(key) : entry.value;
2138
3216
  namespaces.add(entry.namespace);
2139
3217
  }
2140
3218
  }
@@ -2145,8 +3223,10 @@ function toServerProjection(graph, manifest, cnosVersion = "0.0.0-dev") {
2145
3223
  resolvedAt: graph.resolvedAt,
2146
3224
  configHash: configHash(values),
2147
3225
  values: stableSortObject(values),
3226
+ derived: stableSortObject(derived),
2148
3227
  secretRefs: stableSortObject(secretRefs),
2149
3228
  publicKeys,
3229
+ runtimeNamespaces: Array.from(runtimeNamespaces).sort((left, right) => left.localeCompare(right)),
2150
3230
  meta: {
2151
3231
  workspace: graph.workspace.workspaceId,
2152
3232
  profile: graph.profile,
@@ -2169,7 +3249,7 @@ function normalizeEnvValue(value) {
2169
3249
  }
2170
3250
  return JSON.stringify(value);
2171
3251
  }
2172
- function toEnv(graph, manifest, options = {}) {
3252
+ function toEnv(graph, manifest, options = {}, helpers = {}) {
2173
3253
  const includeSecrets = options.includeSecrets ?? true;
2174
3254
  const output = {};
2175
3255
  const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
@@ -2190,7 +3270,11 @@ function toEnv(graph, manifest, options = {}) {
2190
3270
  if (isSecretReference(entry.value)) {
2191
3271
  continue;
2192
3272
  }
2193
- output[envVar] = normalizeEnvValue(entry.value);
3273
+ const value = helpers.read ? helpers.read(logicalKey) : entry.value;
3274
+ if (value === void 0) {
3275
+ continue;
3276
+ }
3277
+ output[envVar] = normalizeEnvValue(value);
2194
3278
  }
2195
3279
  return output;
2196
3280
  }
@@ -2227,20 +3311,43 @@ function resolvePublicPrefix(manifest, options) {
2227
3311
  }
2228
3312
  return manifest.public.frameworks[options.framework] ?? "";
2229
3313
  }
2230
- function toPublicEnv(graph, manifest, options = {}) {
3314
+ function toPublicEnv(graph, manifest, options = {}, helpers = {}) {
2231
3315
  const prefix = resolvePublicPrefix(manifest, options);
2232
3316
  const output = {};
2233
3317
  const promotions = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").sort((left, right) => left.key.localeCompare(right.key));
2234
3318
  for (const resolved of promotions) {
3319
+ if (helpers.isRuntimeDependent?.(resolved.key)) {
3320
+ const value2 = helpers.read?.(resolved.key);
3321
+ if (value2 === void 0) {
3322
+ throw new CnosManifestError(`Cannot build public output for ${resolved.key} because it depends on runtime-only values.`);
3323
+ }
3324
+ }
2235
3325
  const baseEnvVar = fallbackPublicEnvVar(stripNamespace(resolved.key));
2236
3326
  const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
2237
- output[envVar] = normalizeEnvValue2(resolved.value);
3327
+ const value = helpers.read ? helpers.read(resolved.key) : resolved.value;
3328
+ if (value === void 0) {
3329
+ continue;
3330
+ }
3331
+ output[envVar] = normalizeEnvValue2(value);
2238
3332
  }
2239
3333
  return output;
2240
3334
  }
2241
3335
 
2242
3336
  // ../core/src/orchestrator/runtime.ts
2243
3337
  function createRuntime(manifest, graph, plugins = [], secretCache, processEnv = process.env, cnosVersion = "0.0.0-dev") {
3338
+ const runtimeProviders = createDefaultRuntimeProviders(manifest, processEnv);
3339
+ const derivedSupport = createDerivedRuntimeSupport(graph, manifest, runtimeProviders);
3340
+ function resolveProjectedSourceKey(key) {
3341
+ if (!key.startsWith("public.")) {
3342
+ return key;
3343
+ }
3344
+ const promotedFrom = graph.entries.get(key)?.winner.metadata?.promotedFrom;
3345
+ if (typeof promotedFrom === "string") {
3346
+ return promotedFrom;
3347
+ }
3348
+ const fallback = `value.${key.slice("public.".length)}`;
3349
+ return graph.entries.has(fallback) ? fallback : key;
3350
+ }
2244
3351
  async function refreshSecretEntry(key) {
2245
3352
  const entry = graph.entries.get(key);
2246
3353
  if (!entry || entry.namespace !== "secret" || !isSecretReference(entry.value)) {
@@ -2272,6 +3379,19 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
2272
3379
  }
2273
3380
  }
2274
3381
  function readLogicalKey2(key) {
3382
+ const resolved = derivedSupport.read(key, (ref) => {
3383
+ const entry2 = graph.entries.get(ref);
3384
+ if (!entry2) {
3385
+ return void 0;
3386
+ }
3387
+ if (!secretCache) {
3388
+ return entry2.value;
3389
+ }
3390
+ return resolveSecretEntryValue(ref, entry2.value, secretCache);
3391
+ });
3392
+ if (resolved !== void 0 || graph.entries.has(key) || manifest.runtimeNamespaces[key.split(".")[0] ?? ""]) {
3393
+ return resolved;
3394
+ }
2275
3395
  const entry = graph.entries.get(key);
2276
3396
  if (!entry) {
2277
3397
  return void 0;
@@ -2296,34 +3416,64 @@ function createRuntime(manifest, graph, plugins = [], secretCache, processEnv =
2296
3416
  return value;
2297
3417
  },
2298
3418
  readOr(key, fallback) {
2299
- return readOrValue(graph, key, fallback);
3419
+ const value = readLogicalKey2(key);
3420
+ return value === void 0 ? fallback : value;
2300
3421
  },
2301
- value(path14) {
2302
- return readLogicalKey2(toLogicalKey("value", path14));
3422
+ value(path17) {
3423
+ return readLogicalKey2(toLogicalKey("value", path17));
2303
3424
  },
2304
- secret(path14) {
2305
- return readLogicalKey2(toLogicalKey("secret", path14));
3425
+ secret(path17) {
3426
+ return readLogicalKey2(toLogicalKey("secret", path17));
2306
3427
  },
2307
- meta(path14) {
2308
- return readLogicalKey2(toLogicalKey("meta", path14));
3428
+ meta(path17) {
3429
+ return readLogicalKey2(toLogicalKey("meta", path17));
2309
3430
  },
2310
3431
  inspect(key) {
2311
- return inspectValue(graph, key);
3432
+ return inspectValue(graph, key, {
3433
+ read: (ref) => readLogicalKey2(ref),
3434
+ describeDerived: (ref) => derivedSupport.describe(ref, (candidate) => {
3435
+ const entry = graph.entries.get(candidate);
3436
+ if (!entry) {
3437
+ return void 0;
3438
+ }
3439
+ if (!secretCache) {
3440
+ return entry.value;
3441
+ }
3442
+ return resolveSecretEntryValue(candidate, entry.value, secretCache);
3443
+ })
3444
+ });
2312
3445
  },
2313
3446
  toObject() {
2314
- return toNamespaceObject(graph);
3447
+ return toNamespaceObject(graph, void 0, (key) => readLogicalKey2(key));
2315
3448
  },
2316
3449
  toNamespace(namespace) {
2317
- return toNamespaceObject(graph, namespace);
3450
+ return toNamespaceObject(graph, namespace, (key) => readLogicalKey2(key));
2318
3451
  },
2319
3452
  toEnv(options) {
2320
- return toEnv(graph, manifest, options);
3453
+ return toEnv(graph, manifest, options, {
3454
+ read: (key) => readLogicalKey2(key),
3455
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key)
3456
+ });
2321
3457
  },
2322
3458
  toPublicEnv(options) {
2323
- return toPublicEnv(graph, manifest, options);
3459
+ return toPublicEnv(graph, manifest, options, {
3460
+ read: (key) => derivedSupport.toConcreteValue(
3461
+ resolveProjectedSourceKey(key),
3462
+ (candidate) => readLogicalKey2(candidate),
3463
+ "public"
3464
+ ),
3465
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(resolveProjectedSourceKey(key))
3466
+ });
2324
3467
  },
2325
3468
  toServerProjection() {
2326
- return toServerProjection(graph, manifest, cnosVersion);
3469
+ return toServerProjection(graph, manifest, cnosVersion, {
3470
+ read: (key) => derivedSupport.toConcreteValue(key, (candidate) => readLogicalKey2(candidate), "server"),
3471
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key),
3472
+ toServerFormula: (key) => derivedSupport.toServerFormula(key)
3473
+ });
3474
+ },
3475
+ registerRuntimeProvider(namespace, provider) {
3476
+ registerRuntimeProvider(manifest, runtimeProviders, namespace, provider);
2327
3477
  },
2328
3478
  async refreshSecrets() {
2329
3479
  await refreshAllSecrets();
@@ -2430,7 +3580,11 @@ function appendMetaEntries(graph, cnosVersion) {
2430
3580
  async function createCnos(options = {}) {
2431
3581
  const loadedManifest = await loadManifest({
2432
3582
  ...options.root ? { root: options.root } : {},
2433
- ...options.cwd ? { cwd: options.cwd } : {}
3583
+ ...options.cwd ? { cwd: options.cwd } : {},
3584
+ ...options.processEnv ? { processEnv: options.processEnv } : {},
3585
+ ...options.cacheMode ? { cacheMode: options.cacheMode } : {},
3586
+ ...typeof options.cacheTtlSeconds === "number" ? { cacheTtlSeconds: options.cacheTtlSeconds } : {},
3587
+ ...options.forceRefresh ? { forceRefresh: true } : {}
2434
3588
  });
2435
3589
  for (const key of loadedManifest.manifest.public.promote) {
2436
3590
  ensureProjectionAllowed(loadedManifest.manifest, key, "public");
@@ -2491,8 +3645,8 @@ async function createCnos(options = {}) {
2491
3645
  }
2492
3646
 
2493
3647
  // ../core/src/runtime/dump.ts
2494
- var import_promises10 = require("fs/promises");
2495
- var import_node_path10 = __toESM(require("path"), 1);
3648
+ var import_promises12 = require("fs/promises");
3649
+ var import_node_path13 = __toESM(require("path"), 1);
2496
3650
 
2497
3651
  // ../core/src/utils/envNaming.ts
2498
3652
  function normalizeMappingConfig(config = {}) {
@@ -2501,8 +3655,8 @@ function normalizeMappingConfig(config = {}) {
2501
3655
  explicit: config.explicit ?? {}
2502
3656
  };
2503
3657
  }
2504
- function fromScreamingSnake(path14) {
2505
- return path14.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
3658
+ function fromScreamingSnake(path17) {
3659
+ return path17.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2506
3660
  }
2507
3661
  function envVarToLogicalKey(envVar, config = {}) {
2508
3662
  const normalized = normalizeMappingConfig(config);
@@ -2529,7 +3683,7 @@ function envVarToLogicalKey(envVar, config = {}) {
2529
3683
  // package.json
2530
3684
  var package_default = {
2531
3685
  name: "@kitsy/cnos",
2532
- version: "1.6.1",
3686
+ version: "1.7.0",
2533
3687
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
2534
3688
  type: "module",
2535
3689
  main: "./dist/index.cjs",
@@ -2728,8 +3882,8 @@ function createCliArgsPlugin() {
2728
3882
  }
2729
3883
 
2730
3884
  // ../../plugins/dotenv/src/index.ts
2731
- var import_promises11 = require("fs/promises");
2732
- var import_node_path11 = __toESM(require("path"), 1);
3885
+ var import_promises13 = require("fs/promises");
3886
+ var import_node_path14 = __toESM(require("path"), 1);
2733
3887
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
2734
3888
  function parseDoubleQuoted(value) {
2735
3889
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -2786,7 +3940,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
2786
3940
  }
2787
3941
  async function readIfPresent(filePath) {
2788
3942
  try {
2789
- return await (0, import_promises11.readFile)(filePath, "utf8");
3943
+ return await (0, import_promises13.readFile)(filePath, "utf8");
2790
3944
  } catch {
2791
3945
  return void 0;
2792
3946
  }
@@ -2805,7 +3959,7 @@ function createDotenvPlugin() {
2805
3959
  workspace: workspaceRoot.workspaceId
2806
3960
  });
2807
3961
  for (const fileName of fileNames) {
2808
- const absolutePath = import_node_path11.default.join(envRoot, fileName);
3962
+ const absolutePath = import_node_path14.default.join(envRoot, fileName);
2809
3963
  const document = await readIfPresent(absolutePath);
2810
3964
  if (!document) {
2811
3965
  continue;
@@ -2814,7 +3968,7 @@ function createDotenvPlugin() {
2814
3968
  ...dotenvEntriesFromObject(
2815
3969
  parseDotenv(document),
2816
3970
  config.envMapping,
2817
- toPortablePath(import_node_path11.default.relative(import_node_path11.default.dirname(context.manifestRoot), absolutePath)),
3971
+ toPortablePath(import_node_path14.default.relative(import_node_path14.default.dirname(context.manifestRoot), absolutePath)),
2818
3972
  workspaceRoot.workspaceId
2819
3973
  )
2820
3974
  );
@@ -2852,16 +4006,16 @@ function createPublicEnvExportPlugin() {
2852
4006
  }
2853
4007
 
2854
4008
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
2855
- var import_promises13 = require("fs/promises");
4009
+ var import_promises15 = require("fs/promises");
2856
4010
 
2857
4011
  // ../../plugins/filesystem/src/helpers.ts
2858
- var import_promises12 = require("fs/promises");
2859
- var import_node_path12 = __toESM(require("path"), 1);
4012
+ var import_promises14 = require("fs/promises");
4013
+ var import_node_path15 = __toESM(require("path"), 1);
2860
4014
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
2861
4015
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
2862
4016
  async function existsDirectory(targetPath) {
2863
4017
  try {
2864
- const stat2 = await (0, import_promises12.readdir)(targetPath);
4018
+ const stat2 = await (0, import_promises14.readdir)(targetPath);
2865
4019
  void stat2;
2866
4020
  return true;
2867
4021
  } catch {
@@ -2869,15 +4023,15 @@ async function existsDirectory(targetPath) {
2869
4023
  }
2870
4024
  }
2871
4025
  async function collectYamlFiles(root) {
2872
- const entries = await (0, import_promises12.readdir)(root, { withFileTypes: true });
4026
+ const entries = await (0, import_promises14.readdir)(root, { withFileTypes: true });
2873
4027
  const results = [];
2874
4028
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
2875
- const absolutePath = import_node_path12.default.join(root, entry.name);
4029
+ const absolutePath = import_node_path15.default.join(root, entry.name);
2876
4030
  if (entry.isDirectory()) {
2877
4031
  results.push(...await collectYamlFiles(absolutePath));
2878
4032
  continue;
2879
4033
  }
2880
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path12.default.extname(entry.name).toLowerCase())) {
4034
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path15.default.extname(entry.name).toLowerCase())) {
2881
4035
  results.push(absolutePath);
2882
4036
  }
2883
4037
  }
@@ -2885,16 +4039,16 @@ async function collectYamlFiles(root) {
2885
4039
  }
2886
4040
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
2887
4041
  const files = [];
2888
- const repoRoot = import_node_path12.default.dirname(manifestRoot);
4042
+ const repoRoot = import_node_path15.default.dirname(manifestRoot);
2889
4043
  for (const workspaceRoot of workspaceRoots) {
2890
- const resolvedRoot = import_node_path12.default.resolve(workspaceRoot.path, sourceRoot);
4044
+ const resolvedRoot = import_node_path15.default.resolve(workspaceRoot.path, sourceRoot);
2891
4045
  for (const layer of activeLayers) {
2892
- const layerRoot = import_node_path12.default.join(resolvedRoot, layer);
4046
+ const layerRoot = import_node_path15.default.join(resolvedRoot, layer);
2893
4047
  if (!await existsDirectory(layerRoot)) {
2894
4048
  continue;
2895
4049
  }
2896
4050
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
2897
- const relativePath = import_node_path12.default.relative(repoRoot, absolutePath);
4051
+ const relativePath = import_node_path15.default.relative(repoRoot, absolutePath);
2898
4052
  files.push({
2899
4053
  absolutePath,
2900
4054
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -2914,7 +4068,7 @@ function assertObjectDocument(value, filePath) {
2914
4068
  function flattenConfigObject(value, options = {}, prefix = "") {
2915
4069
  return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
2916
4070
  const nextKey = prefix ? `${prefix}.${key}` : key;
2917
- if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue) && !options.stopAtLeaf?.(nestedValue)) {
4071
+ if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue) && !isDerivedValue(nestedValue) && !options.stopAtLeaf?.(nestedValue)) {
2918
4072
  Object.assign(
2919
4073
  accumulator,
2920
4074
  flattenConfigObject(nestedValue, options, nextKey)
@@ -2971,7 +4125,7 @@ function createFilesystemSecretsPlugin() {
2971
4125
  );
2972
4126
  const entries = [];
2973
4127
  for (const file of files) {
2974
- const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
4128
+ const document = await (0, import_promises15.readFile)(file.absolutePath, "utf8");
2975
4129
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
2976
4130
  for (const entry of fileEntries) {
2977
4131
  const metadata = toSecretReferenceMetadata(entry.value);
@@ -2987,7 +4141,7 @@ function createFilesystemSecretsPlugin() {
2987
4141
  }
2988
4142
 
2989
4143
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
2990
- var import_promises14 = require("fs/promises");
4144
+ var import_promises16 = require("fs/promises");
2991
4145
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
2992
4146
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
2993
4147
  }
@@ -3008,7 +4162,7 @@ function createFilesystemValuesPlugin() {
3008
4162
  ).map(([namespace]) => namespace);
3009
4163
  const entries = [];
3010
4164
  for (const file of files) {
3011
- const document = await (0, import_promises14.readFile)(file.absolutePath, "utf8");
4165
+ const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
3012
4166
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
3013
4167
  }
3014
4168
  for (const namespace of customNamespaces) {
@@ -3023,7 +4177,7 @@ function createFilesystemValuesPlugin() {
3023
4177
  layers
3024
4178
  );
3025
4179
  for (const file of namespaceFiles) {
3026
- const document = await (0, import_promises14.readFile)(file.absolutePath, "utf8");
4180
+ const document = await (0, import_promises16.readFile)(file.absolutePath, "utf8");
3027
4181
  entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
3028
4182
  }
3029
4183
  }
@@ -3193,7 +4347,7 @@ async function createCnos2(options = {}) {
3193
4347
  }
3194
4348
 
3195
4349
  // src/runtime/bootstrap.ts
3196
- var import_node_crypto3 = require("crypto");
4350
+ var import_node_crypto4 = require("crypto");
3197
4351
  var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
3198
4352
  var CNOS_PROJECTION_ENV_VAR = "__CNOS_PROJECTION__";
3199
4353
  var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
@@ -3203,7 +4357,11 @@ function deserializeServerProjection(source) {
3203
4357
  if (!payload || payload.version !== 1 || typeof payload.workspace !== "string" || typeof payload.profile !== "string" || typeof payload.resolvedAt !== "string" || typeof payload.configHash !== "string" || !payload.values || typeof payload.values !== "object" || Array.isArray(payload.values) || !payload.secretRefs || typeof payload.secretRefs !== "object" || Array.isArray(payload.secretRefs) || !Array.isArray(payload.publicKeys) || !payload.meta || typeof payload.meta !== "object") {
3204
4358
  throw new Error("Invalid CNOS server projection payload");
3205
4359
  }
3206
- return payload;
4360
+ return {
4361
+ ...payload,
4362
+ derived: payload.derived && typeof payload.derived === "object" && !Array.isArray(payload.derived) ? payload.derived : {},
4363
+ runtimeNamespaces: Array.isArray(payload.runtimeNamespaces) ? payload.runtimeNamespaces : []
4364
+ };
3207
4365
  }
3208
4366
  function deserializeRuntimeGraph(source) {
3209
4367
  const payload = JSON.parse(source);
@@ -3238,7 +4396,7 @@ function decryptSecretPayload(serialized, sessionKey) {
3238
4396
  const iv = Buffer.from(payload.iv, "base64");
3239
4397
  const tag = Buffer.from(payload.tag, "base64");
3240
4398
  const ciphertext = Buffer.from(payload.ciphertext, "base64");
3241
- const decipher = (0, import_node_crypto3.createDecipheriv)("aes-256-gcm", key, iv);
4399
+ const decipher = (0, import_node_crypto4.createDecipheriv)("aes-256-gcm", key, iv);
3242
4400
  decipher.setAuthTag(tag);
3243
4401
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
3244
4402
  return JSON.parse(plaintext);
@@ -3313,6 +4471,27 @@ function formatMessage(runtime, message) {
3313
4471
  return value === void 0 ? match : stringifyLogValue(value);
3314
4472
  });
3315
4473
  }
4474
+ function discoverRuntimeNamespacesFromGraph(graph) {
4475
+ const configNamespaces = /* @__PURE__ */ new Set(["value", "secret", "meta", "public"]);
4476
+ for (const entry of graph.entries.values()) {
4477
+ configNamespaces.add(entry.namespace);
4478
+ }
4479
+ const runtimeNamespaces = /* @__PURE__ */ new Set();
4480
+ for (const entry of graph.entries.values()) {
4481
+ if (!isDerivedValue(entry.value)) {
4482
+ continue;
4483
+ }
4484
+ const parsed = parseDerivation(entry.value);
4485
+ for (const ref of parsed.refs) {
4486
+ const namespace = ref.split(".")[0] ?? "";
4487
+ if (!namespace || configNamespaces.has(namespace)) {
4488
+ continue;
4489
+ }
4490
+ runtimeNamespaces.add(namespace);
4491
+ }
4492
+ }
4493
+ return Array.from(runtimeNamespaces).sort((left, right) => left.localeCompare(right));
4494
+ }
3316
4495
  function attachBootstrappedGraph(graph) {
3317
4496
  if (getSingletonRuntime()) {
3318
4497
  return;
@@ -3359,6 +4538,21 @@ function attachBootstrappedGraph(graph) {
3359
4538
  frameworks: {}
3360
4539
  },
3361
4540
  namespaces: {},
4541
+ runtimeNamespaces: {
4542
+ process: {
4543
+ description: "Live process runtime values.",
4544
+ serverOnly: true,
4545
+ builtIn: true
4546
+ },
4547
+ ...Object.fromEntries(
4548
+ discoverRuntimeNamespacesFromGraph(graph).map((namespace) => [
4549
+ namespace,
4550
+ {
4551
+ serverOnly: true
4552
+ }
4553
+ ])
4554
+ )
4555
+ },
3362
4556
  vaults: {},
3363
4557
  writePolicy: {
3364
4558
  define: {
@@ -3371,46 +4565,76 @@ function attachBootstrappedGraph(graph) {
3371
4565
  },
3372
4566
  schema: {}
3373
4567
  };
4568
+ const runtimeProviders = createDefaultRuntimeProviders(bootstrappedManifest, process.env);
4569
+ const derivedSupport = createDerivedRuntimeSupport(graph, bootstrappedManifest, runtimeProviders);
4570
+ const resolveProjectedSourceKey = (key) => {
4571
+ if (!key.startsWith("public.")) {
4572
+ return key;
4573
+ }
4574
+ const promotedFrom = graph.entries.get(key)?.winner.metadata?.promotedFrom;
4575
+ if (typeof promotedFrom === "string") {
4576
+ return promotedFrom;
4577
+ }
4578
+ const fallback = `value.${key.slice("public.".length)}`;
4579
+ return graph.entries.has(fallback) ? fallback : key;
4580
+ };
3374
4581
  const runtime = {
3375
4582
  manifest: bootstrappedManifest,
3376
4583
  plugins: [],
3377
4584
  graph,
3378
4585
  read(key) {
3379
- return readValue(graph, key);
4586
+ return derivedSupport.read(key, (ref) => readValue(graph, ref));
3380
4587
  },
3381
4588
  require(key) {
3382
- return requireValue(graph, key);
4589
+ const value = this.read(key);
4590
+ if (value === void 0) {
4591
+ return requireValue(graph, key);
4592
+ }
4593
+ return value;
3383
4594
  },
3384
4595
  readOr(key, fallback) {
3385
- return readOrValue(graph, key, fallback);
4596
+ const value = this.read(key);
4597
+ return value === void 0 ? fallback : value;
3386
4598
  },
3387
- value(path14) {
3388
- return readValue(graph, toLogicalKey("value", path14));
4599
+ value(path17) {
4600
+ return readValue(graph, toLogicalKey("value", path17));
3389
4601
  },
3390
- secret(path14) {
3391
- return readValue(graph, toLogicalKey("secret", path14));
4602
+ secret(path17) {
4603
+ return readValue(graph, toLogicalKey("secret", path17));
3392
4604
  },
3393
- meta(path14) {
3394
- return readValue(graph, toLogicalKey("meta", path14));
4605
+ meta(path17) {
4606
+ return readValue(graph, toLogicalKey("meta", path17));
3395
4607
  },
3396
4608
  toNamespace(namespace) {
3397
- return toNamespaceObject(graph, namespace);
4609
+ return toNamespaceObject(graph, namespace, (key) => this.read(key));
3398
4610
  },
3399
4611
  toEnv(options) {
3400
- return toEnv(graph, bootstrappedManifest, options);
4612
+ return toEnv(graph, bootstrappedManifest, options, {
4613
+ read: (key) => this.read(key),
4614
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key)
4615
+ });
3401
4616
  },
3402
4617
  toPublicEnv(options) {
3403
- return toPublicEnv(graph, bootstrappedManifest, options);
4618
+ return toPublicEnv(graph, bootstrappedManifest, options, {
4619
+ read: (key) => derivedSupport.toConcreteValue(resolveProjectedSourceKey(key), (ref) => readValue(graph, ref), "public"),
4620
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(resolveProjectedSourceKey(key))
4621
+ });
3404
4622
  },
3405
4623
  inspect(key) {
3406
- return inspectValue(graph, key);
4624
+ return inspectValue(graph, key, {
4625
+ read: (ref) => this.read(ref),
4626
+ describeDerived: (ref) => derivedSupport.describe(ref, (candidate) => readValue(graph, candidate))
4627
+ });
3407
4628
  },
3408
4629
  toObject() {
3409
- return toNamespaceObject(graph);
4630
+ return toNamespaceObject(graph, void 0, (key) => this.read(key));
3410
4631
  },
3411
4632
  toServerProjection() {
3412
4633
  throw new Error("CNOS graph bootstrap payload does not support server projection export.");
3413
4634
  },
4635
+ registerRuntimeProvider(namespace, provider) {
4636
+ registerRuntimeProvider(bootstrappedManifest, runtimeProviders, namespace, provider);
4637
+ },
3414
4638
  async refreshSecrets() {
3415
4639
  return;
3416
4640
  },
@@ -3421,7 +4645,7 @@ function attachBootstrappedGraph(graph) {
3421
4645
  setSingletonRuntime(runtime);
3422
4646
  setBootstrappedSecretHydrationRequired(graphRequiresSecretHydration(graph));
3423
4647
  }
3424
- function toBootstrappedManifest(graph) {
4648
+ function toBootstrappedManifest(graph, runtimeNamespaces = []) {
3425
4649
  return {
3426
4650
  version: 1,
3427
4651
  project: {
@@ -3464,8 +4688,24 @@ function toBootstrappedManifest(graph) {
3464
4688
  value: { kind: "data", shareable: true },
3465
4689
  secret: { kind: "data", shareable: false, sensitive: true },
3466
4690
  meta: { kind: "system", shareable: false, readonly: true },
4691
+ process: { kind: "system", shareable: false, readonly: true },
3467
4692
  public: { kind: "projection", shareable: true, readonly: true, source: "promote" }
3468
4693
  },
4694
+ runtimeNamespaces: {
4695
+ process: {
4696
+ description: "Live process runtime values.",
4697
+ serverOnly: true,
4698
+ builtIn: true
4699
+ },
4700
+ ...Object.fromEntries(
4701
+ runtimeNamespaces.filter((namespace) => namespace !== "process").map((namespace) => [
4702
+ namespace,
4703
+ {
4704
+ serverOnly: true
4705
+ }
4706
+ ])
4707
+ )
4708
+ },
3469
4709
  vaults: {},
3470
4710
  writePolicy: {
3471
4711
  define: {
@@ -3504,6 +4744,31 @@ function graphFromProjection(projection) {
3504
4744
  overridden: []
3505
4745
  });
3506
4746
  }
4747
+ for (const [key, formula] of Object.entries(projection.derived)) {
4748
+ const firstSegment = key.split(".")[0] ?? "";
4749
+ const logicalKey = key.startsWith("value.") || key.startsWith("public.") || explicitNamespaces.has(firstSegment) ? key : `value.${key}`;
4750
+ const namespace = logicalKey.slice(0, logicalKey.indexOf("."));
4751
+ const winner = {
4752
+ key: logicalKey,
4753
+ value: {
4754
+ $derive: {
4755
+ expr: formula.expr
4756
+ }
4757
+ },
4758
+ namespace,
4759
+ sourceId: "server-projection",
4760
+ pluginId: "cnos",
4761
+ workspaceId: projection.workspace,
4762
+ profile: projection.profile
4763
+ };
4764
+ entries.set(logicalKey, {
4765
+ key: logicalKey,
4766
+ value: winner.value,
4767
+ namespace,
4768
+ winner,
4769
+ overridden: []
4770
+ });
4771
+ }
3507
4772
  for (const [key, ref] of Object.entries(projection.secretRefs)) {
3508
4773
  const logicalKey = `secret.${key}`;
3509
4774
  entries.set(logicalKey, {
@@ -3540,7 +4805,10 @@ function graphFromProjection(projection) {
3540
4805
  sourceId: "server-projection",
3541
4806
  pluginId: "cnos",
3542
4807
  workspaceId: projection.workspace,
3543
- profile: projection.profile
4808
+ profile: projection.profile,
4809
+ metadata: {
4810
+ promotedFrom: valueKey
4811
+ }
3544
4812
  },
3545
4813
  overridden: []
3546
4814
  });
@@ -3578,8 +4846,21 @@ function attachBootstrappedProjection(projection, force = false) {
3578
4846
  return;
3579
4847
  }
3580
4848
  const graph = graphFromProjection(projection);
3581
- const manifest = toBootstrappedManifest(graph);
4849
+ const manifest = toBootstrappedManifest(graph, projection.runtimeNamespaces);
3582
4850
  const hydratedSecrets = /* @__PURE__ */ new Map();
4851
+ const runtimeProviders = createDefaultRuntimeProviders(manifest, process.env);
4852
+ const derivedSupport = createDerivedRuntimeSupport(graph, manifest, runtimeProviders);
4853
+ const resolveProjectedSourceKey = (key) => {
4854
+ if (!key.startsWith("public.")) {
4855
+ return key;
4856
+ }
4857
+ const promotedFrom = graph.entries.get(key)?.winner.metadata?.promotedFrom;
4858
+ if (typeof promotedFrom === "string") {
4859
+ return promotedFrom;
4860
+ }
4861
+ const fallback = `value.${key.slice("public.".length)}`;
4862
+ return graph.entries.has(fallback) ? fallback : key;
4863
+ };
3583
4864
  const resolveSecretValue = async (key) => {
3584
4865
  const entry = graph.entries.get(key);
3585
4866
  if (!entry || entry.namespace !== "secret") {
@@ -3605,14 +4886,16 @@ function attachBootstrappedProjection(projection, force = false) {
3605
4886
  plugins: [],
3606
4887
  graph,
3607
4888
  read(key) {
3608
- const entry = graph.entries.get(key);
3609
- if (!entry) {
3610
- return void 0;
3611
- }
3612
- if (entry.namespace === "secret") {
3613
- return hydratedSecrets.get(key);
3614
- }
3615
- return entry.value;
4889
+ return derivedSupport.read(key, (ref) => {
4890
+ const entry = graph.entries.get(ref);
4891
+ if (!entry) {
4892
+ return void 0;
4893
+ }
4894
+ if (entry.namespace === "secret") {
4895
+ return hydratedSecrets.get(ref);
4896
+ }
4897
+ return entry.value;
4898
+ });
3616
4899
  },
3617
4900
  require(key) {
3618
4901
  const value = this.read(key);
@@ -3644,24 +4927,59 @@ function attachBootstrappedProjection(projection, force = false) {
3644
4927
  ])
3645
4928
  )
3646
4929
  },
3647
- key
4930
+ key,
4931
+ {
4932
+ read: (ref) => this.read(ref),
4933
+ describeDerived: (ref) => derivedSupport.describe(ref, (candidate) => {
4934
+ const entry = graph.entries.get(candidate);
4935
+ if (!entry) {
4936
+ return void 0;
4937
+ }
4938
+ if (entry.namespace === "secret") {
4939
+ return hydratedSecrets.get(candidate);
4940
+ }
4941
+ return entry.value;
4942
+ })
4943
+ }
3648
4944
  );
3649
4945
  },
3650
4946
  toObject() {
3651
- return toNamespaceObject(graph);
4947
+ return toNamespaceObject(graph, void 0, (key) => this.read(key));
3652
4948
  },
3653
4949
  toNamespace(namespace) {
3654
- return toNamespaceObject(graph, namespace);
4950
+ return toNamespaceObject(graph, namespace, (key) => this.read(key));
3655
4951
  },
3656
4952
  toEnv(options) {
3657
- return toEnv(graph, manifest, options);
4953
+ return toEnv(graph, manifest, options, {
4954
+ read: (key) => this.read(key),
4955
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(key)
4956
+ });
3658
4957
  },
3659
4958
  toPublicEnv(options) {
3660
- return toPublicEnv(graph, manifest, options);
4959
+ return toPublicEnv(graph, manifest, options, {
4960
+ read: (key) => derivedSupport.toConcreteValue(
4961
+ resolveProjectedSourceKey(key),
4962
+ (ref) => {
4963
+ const entry = graph.entries.get(ref);
4964
+ if (!entry) {
4965
+ return void 0;
4966
+ }
4967
+ if (entry.namespace === "secret") {
4968
+ return hydratedSecrets.get(ref);
4969
+ }
4970
+ return entry.value;
4971
+ },
4972
+ "public"
4973
+ ),
4974
+ isRuntimeDependent: (key) => derivedSupport.isRuntimeDependentKey(resolveProjectedSourceKey(key))
4975
+ });
3661
4976
  },
3662
4977
  toServerProjection() {
3663
4978
  return projection;
3664
4979
  },
4980
+ registerRuntimeProvider(namespace, provider) {
4981
+ registerRuntimeProvider(manifest, runtimeProviders, namespace, provider);
4982
+ },
3665
4983
  async refreshSecrets() {
3666
4984
  for (const key of Object.keys(projection.secretRefs).map((segment) => `secret.${segment}`)) {
3667
4985
  hydratedSecrets.delete(key);
@@ -3696,7 +5014,7 @@ function bootstrapFromProcessEnv() {
3696
5014
  function discoverProjectionPathSync() {
3697
5015
  const cwd = process.cwd();
3698
5016
  const directCandidates = [
3699
- import_node_path13.default.join(cwd, ".cnos-server.json")
5017
+ import_node_path16.default.join(cwd, ".cnos-server.json")
3700
5018
  ];
3701
5019
  for (const candidate of directCandidates) {
3702
5020
  if ((0, import_node_fs.existsSync)(candidate)) {
@@ -3705,14 +5023,14 @@ function discoverProjectionPathSync() {
3705
5023
  }
3706
5024
  let current = cwd;
3707
5025
  for (let depth = 0; depth <= 3; depth += 1) {
3708
- const rcCandidate = import_node_path13.default.join(current, ".cnosrc.yml");
5026
+ const rcCandidate = import_node_path16.default.join(current, ".cnosrc.yml");
3709
5027
  if ((0, import_node_fs.existsSync)(rcCandidate)) {
3710
- const projectionCandidate = import_node_path13.default.join(current, ".cnos-server.json");
5028
+ const projectionCandidate = import_node_path16.default.join(current, ".cnos-server.json");
3711
5029
  if ((0, import_node_fs.existsSync)(projectionCandidate)) {
3712
5030
  return projectionCandidate;
3713
5031
  }
3714
5032
  }
3715
- const parent = import_node_path13.default.dirname(current);
5033
+ const parent = import_node_path16.default.dirname(current);
3716
5034
  if (parent === current) {
3717
5035
  break;
3718
5036
  }
@@ -3748,14 +5066,14 @@ var cnos = Object.assign(
3748
5066
  readOr(key, fallback) {
3749
5067
  return getRuntimeOrThrow().readOr(key, fallback);
3750
5068
  },
3751
- value(path14) {
3752
- return getRuntimeOrThrow().value(path14);
5069
+ value(path17) {
5070
+ return getRuntimeOrThrow().value(path17);
3753
5071
  },
3754
- secret(path14) {
3755
- return getRuntimeOrThrow().secret(path14);
5072
+ secret(path17) {
5073
+ return getRuntimeOrThrow().secret(path17);
3756
5074
  },
3757
- meta(path14) {
3758
- return getRuntimeOrThrow().meta(path14);
5075
+ meta(path17) {
5076
+ return getRuntimeOrThrow().meta(path17);
3759
5077
  },
3760
5078
  inspect(key) {
3761
5079
  return getRuntimeOrThrow().inspect(key);
@@ -3778,11 +5096,14 @@ var cnos = Object.assign(
3778
5096
  return formatted;
3779
5097
  },
3780
5098
  async loadProjection(source) {
3781
- const resolvedSource = import_node_path13.default.resolve(source);
5099
+ const resolvedSource = import_node_path16.default.resolve(source);
3782
5100
  const projection = deserializeServerProjection((0, import_node_fs.readFileSync)(resolvedSource, "utf8"));
3783
5101
  attachBootstrappedProjection(projection, true);
3784
5102
  setBootstrappedSecretHydrationRequired(Object.keys(projection.secretRefs).length > 0);
3785
5103
  },
5104
+ registerRuntimeProvider(namespace, provider) {
5105
+ getRuntimeOrThrow().registerRuntimeProvider(namespace, provider);
5106
+ },
3786
5107
  async refreshSecrets() {
3787
5108
  await getRuntimeOrThrow().refreshSecrets();
3788
5109
  setBootstrappedSecretHydrationRequired(false);