@kitsy/cnos 1.6.0 → 1.7.0

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