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