@kitsy/cnos 1.2.0 → 1.4.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 +3 -3
- package/dist/build/index.cjs +1003 -121
- package/dist/build/index.d.cts +1 -1
- package/dist/build/index.d.ts +1 -1
- package/dist/build/index.js +22 -10
- package/dist/{chunk-APCTXRUN.js → chunk-APIU4GTB.js} +1012 -195
- package/dist/chunk-EQSKV3DP.js +105 -0
- package/dist/{chunk-MLQGYCO7.js → chunk-FWJC4Y2D.js} +1 -1
- package/dist/{chunk-RD5WMHPM.js → chunk-HMM76UYZ.js} +1 -1
- package/dist/{chunk-EIN55XXA.js → chunk-J4K4JUJL.js} +1 -1
- package/dist/{chunk-SO5XREEU.js → chunk-JSBVYK2T.js} +32 -11
- package/dist/chunk-LJD4SM32.js +189 -0
- package/dist/{chunk-SXTMTACL.js → chunk-T6Y57KTT.js} +20 -31
- package/dist/chunk-WCHX2QFY.js +115 -0
- package/dist/{chunk-ZA74BO47.js → chunk-ZTPSFXWP.js} +1 -1
- package/dist/configure/index.cjs +3021 -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-CcsqAel3.d.ts → envNaming-Dvm_LP2D.d.ts} +1 -1
- package/dist/{envNaming-BTJpH93W.d.cts → envNaming-S4B-dHUx.d.cts} +1 -1
- package/dist/index.cjs +1243 -186
- package/dist/index.d.cts +2 -13
- package/dist/index.d.ts +2 -13
- package/dist/index.js +13 -25
- package/dist/internal.cjs +1525 -81
- package/dist/internal.d.cts +171 -14
- package/dist/internal.d.ts +171 -14
- package/dist/internal.js +652 -5
- 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 +36 -9
- 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 +31 -2
- 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 +65 -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 +105 -11
- package/dist/plugin/process-env.d.cts +4 -3
- package/dist/plugin/process-env.d.ts +4 -3
- package/dist/plugin/process-env.js +6 -4
- package/dist/{plugin-DkOIT5uI.d.cts → plugin-B4xwySxw.d.cts} +15 -2
- package/dist/{plugin-DkOIT5uI.d.ts → plugin-B4xwySxw.d.ts} +15 -2
- package/dist/runtime/index.cjs +1057 -136
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +11 -186
- package/dist/{toPublicEnv-C9clvXLo.d.ts → toPublicEnv-CvhGAfsB.d.ts} +1 -1
- package/dist/{toPublicEnv-DvFeV3qG.d.cts → toPublicEnv-ggmphZFs.d.cts} +1 -1
- package/package.json +11 -1
- package/dist/chunk-JUHPBAEH.js +0 -20
- package/dist/chunk-PQ4KSV76.js +0 -50
- package/dist/chunk-WHUGFPE4.js +0 -49
package/dist/index.cjs
CHANGED
|
@@ -30,13 +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
|
-
resolveBrowserData: () => resolveBrowserData,
|
|
37
|
-
toEnv: () => toEnv,
|
|
38
|
-
toPublicEnv: () => toPublicEnv,
|
|
39
|
-
writeDump: () => writeDump
|
|
33
|
+
cnos: () => runtime_default,
|
|
34
|
+
default: () => runtime_default
|
|
40
35
|
});
|
|
41
36
|
module.exports = __toCommonJS(index_exports);
|
|
42
37
|
|
|
@@ -59,6 +54,11 @@ var CnosSecurityError = class extends CnosError {
|
|
|
59
54
|
super(message);
|
|
60
55
|
}
|
|
61
56
|
};
|
|
57
|
+
var CnosAuthenticationError = class extends CnosError {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
62
|
var CnosKeyNotFoundError = class extends CnosError {
|
|
63
63
|
constructor(key) {
|
|
64
64
|
super(`Missing required CNOS config key: ${key}`);
|
|
@@ -111,6 +111,68 @@ function createProvenanceInspector() {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
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
|
+
|
|
114
176
|
// ../core/src/manifest/loadManifest.ts
|
|
115
177
|
var import_promises2 = require("fs/promises");
|
|
116
178
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
@@ -233,6 +295,11 @@ var DEFAULT_NAMESPACES = {
|
|
|
233
295
|
shareable: false,
|
|
234
296
|
readonly: true
|
|
235
297
|
},
|
|
298
|
+
process: {
|
|
299
|
+
kind: "system",
|
|
300
|
+
shareable: false,
|
|
301
|
+
readonly: true
|
|
302
|
+
},
|
|
236
303
|
public: {
|
|
237
304
|
kind: "projection",
|
|
238
305
|
source: "promote",
|
|
@@ -287,22 +354,82 @@ function normalizeNamespaces(namespaces) {
|
|
|
287
354
|
function normalizeVaults(vaults) {
|
|
288
355
|
return Object.fromEntries(
|
|
289
356
|
Object.entries(vaults ?? {}).map(([name, definition]) => {
|
|
357
|
+
const legacyPassphrase = definition.passphrase;
|
|
358
|
+
if (legacyPassphrase !== void 0) {
|
|
359
|
+
throw new CnosManifestError(
|
|
360
|
+
`Vault "${name}" uses legacy passphrase configuration. Use vaults.${name}.auth instead.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
290
363
|
const provider = definition.provider?.trim();
|
|
291
364
|
if (!provider) {
|
|
292
365
|
throw new CnosManifestError(`Vault "${name}" requires a provider`);
|
|
293
366
|
}
|
|
367
|
+
const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
|
|
368
|
+
const normalizedMapping = Object.fromEntries(
|
|
369
|
+
Object.entries(definition.mapping ?? {}).filter(
|
|
370
|
+
(entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
|
|
371
|
+
).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
|
|
372
|
+
);
|
|
294
373
|
return [
|
|
295
374
|
name,
|
|
296
375
|
{
|
|
297
376
|
provider,
|
|
298
|
-
|
|
299
|
-
|
|
377
|
+
auth: normalizedAuth,
|
|
378
|
+
...Object.keys(normalizedMapping).length > 0 ? {
|
|
379
|
+
mapping: normalizedMapping
|
|
300
380
|
} : {}
|
|
301
381
|
}
|
|
302
382
|
];
|
|
303
383
|
})
|
|
304
384
|
);
|
|
305
385
|
}
|
|
386
|
+
function normalizeAuthSources(value) {
|
|
387
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
388
|
+
return void 0;
|
|
389
|
+
}
|
|
390
|
+
const sources = Array.isArray(value.from) ? value.from : void 0;
|
|
391
|
+
const normalized = (sources ?? []).map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
392
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
393
|
+
}
|
|
394
|
+
function normalizeVaultAuth(vaultName, provider, auth) {
|
|
395
|
+
if (provider === "local") {
|
|
396
|
+
const passphraseSources = normalizeAuthSources(auth?.passphrase);
|
|
397
|
+
const defaultToken = vaultName.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
398
|
+
const defaultSources = [
|
|
399
|
+
...defaultToken ? [`env:CNOS_SECRET_PASSPHRASE_${defaultToken}`] : [],
|
|
400
|
+
"env:CNOS_SECRET_PASSPHRASE",
|
|
401
|
+
`keychain:cnos/${vaultName}`,
|
|
402
|
+
"prompt"
|
|
403
|
+
];
|
|
404
|
+
return {
|
|
405
|
+
method: auth?.method ?? "passphrase",
|
|
406
|
+
passphrase: {
|
|
407
|
+
from: passphraseSources ?? defaultSources
|
|
408
|
+
},
|
|
409
|
+
...auth?.config ? { config: auth.config } : {}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (provider === "github-secrets") {
|
|
413
|
+
return {
|
|
414
|
+
method: auth?.method ?? "environment",
|
|
415
|
+
...auth?.config ? { config: auth.config } : {}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
...auth?.method ? { method: auth.method } : {},
|
|
420
|
+
...normalizeAuthSources(auth?.passphrase) ? {
|
|
421
|
+
passphrase: {
|
|
422
|
+
from: normalizeAuthSources(auth?.passphrase) ?? []
|
|
423
|
+
}
|
|
424
|
+
} : {},
|
|
425
|
+
...normalizeAuthSources(auth?.token) ? {
|
|
426
|
+
token: {
|
|
427
|
+
from: normalizeAuthSources(auth?.token) ?? []
|
|
428
|
+
}
|
|
429
|
+
} : {},
|
|
430
|
+
...auth?.config ? { config: auth.config } : {}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
306
433
|
function normalizeManifest(manifest) {
|
|
307
434
|
const version = manifest.version ?? 1;
|
|
308
435
|
if (version !== 1) {
|
|
@@ -832,12 +959,15 @@ async function exists2(targetPath) {
|
|
|
832
959
|
return false;
|
|
833
960
|
}
|
|
834
961
|
}
|
|
835
|
-
async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId) {
|
|
962
|
+
async function resolveLocalWorkspaceRoot(manifestRoot, workspaceId, manifest) {
|
|
836
963
|
const workspaceRoot = import_node_path5.default.join(manifestRoot, "workspaces", workspaceId);
|
|
837
964
|
if (await exists2(workspaceRoot)) {
|
|
838
965
|
return workspaceRoot;
|
|
839
966
|
}
|
|
840
|
-
const
|
|
967
|
+
const customDataNamespaceRoots = Object.entries(manifest.namespaces).filter(
|
|
968
|
+
([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
|
|
969
|
+
).map(([namespace]) => namespace);
|
|
970
|
+
const legacyMarkers = ["values", "secrets", "env", "profiles", ...customDataNamespaceRoots].map(
|
|
841
971
|
(segment) => import_node_path5.default.join(manifestRoot, segment)
|
|
842
972
|
);
|
|
843
973
|
if ((await Promise.all(legacyMarkers.map((marker) => exists2(marker)))).some(Boolean)) {
|
|
@@ -924,7 +1054,7 @@ async function resolveWorkspaceContext(manifest, options) {
|
|
|
924
1054
|
workspaceRoots.push({
|
|
925
1055
|
scope: "local",
|
|
926
1056
|
workspaceId: chainWorkspaceId,
|
|
927
|
-
path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId)
|
|
1057
|
+
path: await resolveLocalWorkspaceRoot(options.manifestRoot, chainWorkspaceId, manifest)
|
|
928
1058
|
});
|
|
929
1059
|
}
|
|
930
1060
|
return {
|
|
@@ -1089,6 +1219,7 @@ async function runPipeline(options) {
|
|
|
1089
1219
|
const collectedEntries = await Promise.all(
|
|
1090
1220
|
options.plugins.map(
|
|
1091
1221
|
(plugin) => plugin.load({
|
|
1222
|
+
manifest: options.manifest,
|
|
1092
1223
|
manifestConfig: {
|
|
1093
1224
|
...options.manifest.sources[plugin.id] ?? {},
|
|
1094
1225
|
envMapping: options.manifest.envMapping
|
|
@@ -1106,6 +1237,712 @@ async function runPipeline(options) {
|
|
|
1106
1237
|
return collectedEntries.flat();
|
|
1107
1238
|
}
|
|
1108
1239
|
|
|
1240
|
+
// ../core/src/secrets/auditLog.ts
|
|
1241
|
+
var import_promises8 = require("fs/promises");
|
|
1242
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
1243
|
+
|
|
1244
|
+
// ../core/src/utils/secretStore.ts
|
|
1245
|
+
var import_node_crypto = require("crypto");
|
|
1246
|
+
var import_promises7 = require("fs/promises");
|
|
1247
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1248
|
+
|
|
1249
|
+
// ../core/src/secrets/sessionStore.ts
|
|
1250
|
+
var import_promises6 = require("fs/promises");
|
|
1251
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1252
|
+
function buildSessionRoot(processEnv = process.env) {
|
|
1253
|
+
return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
|
|
1254
|
+
}
|
|
1255
|
+
function buildSessionPath(vault, processEnv) {
|
|
1256
|
+
return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
|
|
1257
|
+
}
|
|
1258
|
+
async function readVaultSessionKey(vault, processEnv) {
|
|
1259
|
+
try {
|
|
1260
|
+
const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
|
|
1261
|
+
const document = JSON.parse(source);
|
|
1262
|
+
if (document.version !== 1 || typeof document.derivedKey !== "string") {
|
|
1263
|
+
return void 0;
|
|
1264
|
+
}
|
|
1265
|
+
const key = Buffer.from(document.derivedKey, "hex");
|
|
1266
|
+
return key.length > 0 ? key : void 0;
|
|
1267
|
+
} catch {
|
|
1268
|
+
return void 0;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// ../core/src/utils/secretStore.ts
|
|
1273
|
+
var KEY_LENGTH = 32;
|
|
1274
|
+
var SALT_LENGTH = 32;
|
|
1275
|
+
var IV_LENGTH = 12;
|
|
1276
|
+
var AUTH_TAG_LENGTH = 16;
|
|
1277
|
+
var PBKDF2_ITERATIONS = 6e5;
|
|
1278
|
+
var KEYSTORE_VERSION = 1;
|
|
1279
|
+
var METADATA_VERSION = 1;
|
|
1280
|
+
var META_FILENAME = "meta.yml";
|
|
1281
|
+
var KEYSTORE_FILENAME = "keystore.enc";
|
|
1282
|
+
function isObject(value) {
|
|
1283
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1284
|
+
}
|
|
1285
|
+
function isSecretReference(value) {
|
|
1286
|
+
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));
|
|
1287
|
+
}
|
|
1288
|
+
function resolveSecretStoreRoot(processEnv = process.env) {
|
|
1289
|
+
return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
|
|
1290
|
+
}
|
|
1291
|
+
function normalizeVaultToken(vault = "default") {
|
|
1292
|
+
return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
1293
|
+
}
|
|
1294
|
+
function getVaultPassphraseEnvVar(vault = "default") {
|
|
1295
|
+
const vaultToken = normalizeVaultToken(vault);
|
|
1296
|
+
return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
|
|
1297
|
+
}
|
|
1298
|
+
function getVaultSessionKeyEnvVar(vault = "default") {
|
|
1299
|
+
const vaultToken = normalizeVaultToken(vault);
|
|
1300
|
+
return `__CNOS_VAULT_KEY_${vaultToken || "DEFAULT"}__`;
|
|
1301
|
+
}
|
|
1302
|
+
function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
|
|
1303
|
+
return processEnv[getVaultPassphraseEnvVar(vault)] ?? processEnv.CNOS_SECRET_PASSPHRASE;
|
|
1304
|
+
}
|
|
1305
|
+
function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
|
|
1306
|
+
const encoded = processEnv[getVaultSessionKeyEnvVar(vault)];
|
|
1307
|
+
if (!encoded) {
|
|
1308
|
+
return readVaultSessionKey(vault, processEnv);
|
|
1309
|
+
}
|
|
1310
|
+
try {
|
|
1311
|
+
const key = Buffer.from(encoded, "hex");
|
|
1312
|
+
return key.length === KEY_LENGTH ? key : void 0;
|
|
1313
|
+
} catch {
|
|
1314
|
+
return void 0;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
|
|
1318
|
+
return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
|
|
1319
|
+
}
|
|
1320
|
+
function buildMetaPath(storeRoot, vault = "default") {
|
|
1321
|
+
return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
|
|
1322
|
+
}
|
|
1323
|
+
function buildKeystorePath(storeRoot, vault = "default") {
|
|
1324
|
+
return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
|
|
1325
|
+
}
|
|
1326
|
+
function buildLegacyVaultFile(storeRoot, vault = "default") {
|
|
1327
|
+
return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
|
|
1328
|
+
}
|
|
1329
|
+
function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
|
|
1330
|
+
return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
|
|
1331
|
+
}
|
|
1332
|
+
function assertVaultMetadata(value, filePath) {
|
|
1333
|
+
if (!isObject(value)) {
|
|
1334
|
+
throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
|
|
1335
|
+
}
|
|
1336
|
+
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") {
|
|
1337
|
+
throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
|
|
1338
|
+
}
|
|
1339
|
+
return value;
|
|
1340
|
+
}
|
|
1341
|
+
async function exists3(targetPath) {
|
|
1342
|
+
try {
|
|
1343
|
+
await (0, import_promises7.stat)(targetPath);
|
|
1344
|
+
return true;
|
|
1345
|
+
} catch {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
async function detectLegacyVaultFormat(storeRoot, vault = "default") {
|
|
1350
|
+
const legacyFile = buildLegacyVaultFile(storeRoot, vault);
|
|
1351
|
+
const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
|
|
1352
|
+
if (await exists3(legacyFile)) {
|
|
1353
|
+
return legacyFile;
|
|
1354
|
+
}
|
|
1355
|
+
if (await exists3(legacyStore)) {
|
|
1356
|
+
return legacyStore;
|
|
1357
|
+
}
|
|
1358
|
+
return void 0;
|
|
1359
|
+
}
|
|
1360
|
+
async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
|
|
1361
|
+
const legacyPath = await detectLegacyVaultFormat(storeRoot, vault);
|
|
1362
|
+
if (!legacyPath) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
throw new CnosSecurityError(
|
|
1366
|
+
`Legacy CNOS local vault format detected for vault "${vault}" at ${legacyPath}. CNOS 1.4 requires the new keystore format. Remove and recreate the vault.`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
function encryptPayload(payload, key) {
|
|
1370
|
+
const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
|
|
1371
|
+
const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
|
|
1372
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
1373
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
1374
|
+
const tag = cipher.getAuthTag();
|
|
1375
|
+
return Buffer.concat([
|
|
1376
|
+
Buffer.from(Uint32Array.of(KEYSTORE_VERSION).buffer),
|
|
1377
|
+
iv,
|
|
1378
|
+
tag,
|
|
1379
|
+
ciphertext
|
|
1380
|
+
]);
|
|
1381
|
+
}
|
|
1382
|
+
function decryptPayload(buffer, key) {
|
|
1383
|
+
if (buffer.length < 4 + IV_LENGTH + AUTH_TAG_LENGTH) {
|
|
1384
|
+
throw new CnosSecurityError("Invalid CNOS local vault keystore");
|
|
1385
|
+
}
|
|
1386
|
+
const version = buffer.readUInt32LE(0);
|
|
1387
|
+
if (version !== KEYSTORE_VERSION) {
|
|
1388
|
+
throw new CnosSecurityError(`Unsupported CNOS local vault keystore version: ${version}`);
|
|
1389
|
+
}
|
|
1390
|
+
const ivOffset = 4;
|
|
1391
|
+
const tagOffset = ivOffset + IV_LENGTH;
|
|
1392
|
+
const cipherOffset = tagOffset + AUTH_TAG_LENGTH;
|
|
1393
|
+
const iv = buffer.subarray(ivOffset, tagOffset);
|
|
1394
|
+
const tag = buffer.subarray(tagOffset, cipherOffset);
|
|
1395
|
+
const ciphertext = buffer.subarray(cipherOffset);
|
|
1396
|
+
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
|
|
1397
|
+
decipher.setAuthTag(tag);
|
|
1398
|
+
try {
|
|
1399
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
1400
|
+
const payload = JSON.parse(plaintext);
|
|
1401
|
+
if (!payload || !isObject(payload.secrets) || !isObject(payload.metadata)) {
|
|
1402
|
+
throw new Error("invalid");
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
secrets: Object.fromEntries(
|
|
1406
|
+
Object.entries(payload.secrets).filter((entry) => typeof entry[1] === "string")
|
|
1407
|
+
),
|
|
1408
|
+
metadata: Object.fromEntries(
|
|
1409
|
+
Object.entries(payload.metadata).filter(
|
|
1410
|
+
(entry) => isObject(entry[1]) && typeof entry[1].createdAt === "string" && typeof entry[1].updatedAt === "string"
|
|
1411
|
+
)
|
|
1412
|
+
)
|
|
1413
|
+
};
|
|
1414
|
+
} catch {
|
|
1415
|
+
throw new CnosAuthenticationError("Failed to decrypt CNOS local vault. Check vault authentication.");
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function buildInitialPayload() {
|
|
1419
|
+
return {
|
|
1420
|
+
secrets: {},
|
|
1421
|
+
metadata: {}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
|
|
1425
|
+
const metaPath = buildMetaPath(storeRoot, vault);
|
|
1426
|
+
const keystorePath = buildKeystorePath(storeRoot, vault);
|
|
1427
|
+
await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
|
|
1428
|
+
await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
|
|
1429
|
+
await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
|
|
1430
|
+
}
|
|
1431
|
+
async function readVaultMetadata(storeRoot, vault = "default") {
|
|
1432
|
+
await assertNoLegacyVaultFormat(storeRoot, vault);
|
|
1433
|
+
const metaPath = buildMetaPath(storeRoot, vault);
|
|
1434
|
+
try {
|
|
1435
|
+
const source = await (0, import_promises7.readFile)(metaPath, "utf8");
|
|
1436
|
+
return assertVaultMetadata(parseYaml(source), metaPath);
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
if (error.code === "ENOENT") {
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1441
|
+
throw error;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
async function createSecretVault(storeRoot, vault, passphrase) {
|
|
1445
|
+
const normalizedVault = vault.trim() || "default";
|
|
1446
|
+
await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
|
|
1447
|
+
const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
|
|
1448
|
+
const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
|
|
1449
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1450
|
+
const meta = {
|
|
1451
|
+
version: METADATA_VERSION,
|
|
1452
|
+
algorithm: "aes-256-gcm",
|
|
1453
|
+
kdf: "pbkdf2-sha512",
|
|
1454
|
+
iterations: PBKDF2_ITERATIONS,
|
|
1455
|
+
salt: salt.toString("base64"),
|
|
1456
|
+
createdAt,
|
|
1457
|
+
secretCount: 0
|
|
1458
|
+
};
|
|
1459
|
+
await writeVaultFiles(storeRoot, normalizedVault, meta, buildInitialPayload(), key);
|
|
1460
|
+
return buildMetaPath(storeRoot, normalizedVault);
|
|
1461
|
+
}
|
|
1462
|
+
async function ensureSecretVault(storeRoot, vault, passphrase) {
|
|
1463
|
+
const normalizedVault = vault.trim() || "default";
|
|
1464
|
+
const meta = await readVaultMetadata(storeRoot, normalizedVault);
|
|
1465
|
+
if (meta) {
|
|
1466
|
+
return buildMetaPath(storeRoot, normalizedVault);
|
|
1467
|
+
}
|
|
1468
|
+
return createSecretVault(storeRoot, normalizedVault, passphrase);
|
|
1469
|
+
}
|
|
1470
|
+
function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
|
|
1471
|
+
if (definition?.provider !== "local") {
|
|
1472
|
+
return void 0;
|
|
1473
|
+
}
|
|
1474
|
+
const configuredSources = definition.auth?.passphrase?.from ?? [];
|
|
1475
|
+
for (const source of configuredSources) {
|
|
1476
|
+
if (source.startsWith("env:")) {
|
|
1477
|
+
const value = processEnv[source.slice(4)];
|
|
1478
|
+
if (value) {
|
|
1479
|
+
return value;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return resolveSecretPassphrase(vault, processEnv);
|
|
1484
|
+
}
|
|
1485
|
+
async function resolveVaultAccessKey(storeRoot, definition, vault = "default", processEnv = process.env) {
|
|
1486
|
+
if (definition?.provider !== "local") {
|
|
1487
|
+
return definition?.provider === "github-secrets" ? {
|
|
1488
|
+
method: definition.auth?.method ?? "environment",
|
|
1489
|
+
...definition?.auth?.config ? { config: definition.auth.config } : {}
|
|
1490
|
+
} : void 0;
|
|
1491
|
+
}
|
|
1492
|
+
const sessionKey = await resolveVaultSessionKey(vault, processEnv);
|
|
1493
|
+
if (sessionKey) {
|
|
1494
|
+
return {
|
|
1495
|
+
derivedKey: sessionKey,
|
|
1496
|
+
method: "keychain",
|
|
1497
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
const passphrase = resolveConfiguredVaultPassphrase(definition, vault, processEnv);
|
|
1501
|
+
if (passphrase) {
|
|
1502
|
+
return {
|
|
1503
|
+
passphrase,
|
|
1504
|
+
method: "passphrase",
|
|
1505
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
const metadata = await readVaultMetadata(storeRoot, vault);
|
|
1509
|
+
if (!metadata) {
|
|
1510
|
+
return void 0;
|
|
1511
|
+
}
|
|
1512
|
+
throw new CnosAuthenticationError(
|
|
1513
|
+
`Cannot authenticate to vault "${vault}". Set ${getVaultPassphraseEnvVar(vault)} or run cnos vault auth ${vault}.`
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
async function loadVaultPayload(storeRoot, vault, auth) {
|
|
1517
|
+
const meta = await readVaultMetadata(storeRoot, vault);
|
|
1518
|
+
if (!meta) {
|
|
1519
|
+
throw new CnosManifestError(`Missing CNOS vault metadata for "${vault}"`);
|
|
1520
|
+
}
|
|
1521
|
+
const salt = Buffer.from(meta.salt, "base64");
|
|
1522
|
+
const key = auth.derivedKey ?? (auth.passphrase ? deriveVaultKey(auth.passphrase, salt, meta.iterations) : void 0);
|
|
1523
|
+
if (!key) {
|
|
1524
|
+
throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
|
|
1525
|
+
}
|
|
1526
|
+
const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
|
|
1527
|
+
return {
|
|
1528
|
+
meta,
|
|
1529
|
+
payload: decryptPayload(buffer, key),
|
|
1530
|
+
key
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
async function writeLocalSecret(storeRoot, ref, value, authOrPassphrase, vault = "default") {
|
|
1534
|
+
const auth = typeof authOrPassphrase === "string" ? {
|
|
1535
|
+
passphrase: authOrPassphrase,
|
|
1536
|
+
method: "passphrase"
|
|
1537
|
+
} : authOrPassphrase;
|
|
1538
|
+
if (auth.passphrase) {
|
|
1539
|
+
await ensureSecretVault(storeRoot, vault, auth.passphrase);
|
|
1540
|
+
} else {
|
|
1541
|
+
const meta2 = await readVaultMetadata(storeRoot, vault);
|
|
1542
|
+
if (!meta2) {
|
|
1543
|
+
throw new CnosAuthenticationError(`Vault "${vault}" requires passphrase-based authentication for initial creation.`);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
|
|
1547
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1548
|
+
const existing = payload.metadata[ref];
|
|
1549
|
+
payload.secrets[ref] = value;
|
|
1550
|
+
payload.metadata[ref] = {
|
|
1551
|
+
createdAt: existing?.createdAt ?? now,
|
|
1552
|
+
updatedAt: now
|
|
1553
|
+
};
|
|
1554
|
+
const nextMeta = {
|
|
1555
|
+
...meta,
|
|
1556
|
+
secretCount: Object.keys(payload.secrets).length
|
|
1557
|
+
};
|
|
1558
|
+
await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
|
|
1559
|
+
return buildKeystorePath(storeRoot, vault);
|
|
1560
|
+
}
|
|
1561
|
+
async function deleteLocalSecret(storeRoot, ref, auth, vault = "default") {
|
|
1562
|
+
const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
|
|
1563
|
+
if (!(ref in payload.secrets)) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
delete payload.secrets[ref];
|
|
1567
|
+
delete payload.metadata[ref];
|
|
1568
|
+
const nextMeta = {
|
|
1569
|
+
...meta,
|
|
1570
|
+
secretCount: Object.keys(payload.secrets).length
|
|
1571
|
+
};
|
|
1572
|
+
await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
async function readLocalSecret(storeRoot, ref, auth, vault = "default") {
|
|
1576
|
+
const { payload } = await loadVaultPayload(storeRoot, vault, auth);
|
|
1577
|
+
const value = payload.secrets[ref];
|
|
1578
|
+
if (value === void 0) {
|
|
1579
|
+
throw new CnosManifestError(`Missing local secret ref "${ref}" in vault "${vault}"`);
|
|
1580
|
+
}
|
|
1581
|
+
return value;
|
|
1582
|
+
}
|
|
1583
|
+
async function listLocalSecrets(storeRoot, auth, vault = "default") {
|
|
1584
|
+
const { payload } = await loadVaultPayload(storeRoot, vault, auth);
|
|
1585
|
+
return Object.keys(payload.secrets).sort((left, right) => left.localeCompare(right));
|
|
1586
|
+
}
|
|
1587
|
+
function resolveVaultDefinition(vaults, vault = "default") {
|
|
1588
|
+
const definition = vaults?.[vault];
|
|
1589
|
+
const provider = definition?.provider ?? "local";
|
|
1590
|
+
return {
|
|
1591
|
+
name: vault,
|
|
1592
|
+
provider,
|
|
1593
|
+
...definition?.auth ? { auth: definition.auth } : {},
|
|
1594
|
+
...definition?.mapping ? { mapping: definition.mapping } : {},
|
|
1595
|
+
requiresAuthentication: provider === "local"
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// ../core/src/secrets/auditLog.ts
|
|
1600
|
+
async function appendAuditEvent(event, processEnv = process.env) {
|
|
1601
|
+
const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
|
|
1602
|
+
await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
|
|
1603
|
+
await (0, import_promises8.appendFile)(
|
|
1604
|
+
auditFile,
|
|
1605
|
+
`${JSON.stringify({
|
|
1606
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1607
|
+
...event
|
|
1608
|
+
})}
|
|
1609
|
+
`,
|
|
1610
|
+
"utf8"
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// ../core/src/secrets/secretCache.ts
|
|
1615
|
+
var SecretCache = class {
|
|
1616
|
+
cache = /* @__PURE__ */ new Map();
|
|
1617
|
+
authenticated = /* @__PURE__ */ new Set();
|
|
1618
|
+
load(vaultId, secrets) {
|
|
1619
|
+
this.authenticated.add(vaultId);
|
|
1620
|
+
for (const [ref, value] of secrets) {
|
|
1621
|
+
this.cache.set(`${vaultId}:${ref}`, value);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
isVaultAuthenticated(vaultId) {
|
|
1625
|
+
return this.authenticated.has(vaultId);
|
|
1626
|
+
}
|
|
1627
|
+
get(vaultId, ref) {
|
|
1628
|
+
return this.cache.get(`${vaultId}:${ref}`);
|
|
1629
|
+
}
|
|
1630
|
+
clear(vaultId) {
|
|
1631
|
+
if (!vaultId) {
|
|
1632
|
+
this.cache.clear();
|
|
1633
|
+
this.authenticated.clear();
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
this.authenticated.delete(vaultId);
|
|
1637
|
+
for (const key of Array.from(this.cache.keys())) {
|
|
1638
|
+
if (key.startsWith(`${vaultId}:`)) {
|
|
1639
|
+
this.cache.delete(key);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
|
|
1645
|
+
// ../core/src/secrets/providers/github.ts
|
|
1646
|
+
var GithubSecretsVaultProvider = class {
|
|
1647
|
+
constructor(vaultId, definition, processEnv = process.env) {
|
|
1648
|
+
this.vaultId = vaultId;
|
|
1649
|
+
this.definition = definition;
|
|
1650
|
+
this.processEnv = processEnv;
|
|
1651
|
+
}
|
|
1652
|
+
vaultId;
|
|
1653
|
+
definition;
|
|
1654
|
+
processEnv;
|
|
1655
|
+
authenticated = false;
|
|
1656
|
+
async authenticate(_authConfig) {
|
|
1657
|
+
void _authConfig;
|
|
1658
|
+
this.authenticated = true;
|
|
1659
|
+
}
|
|
1660
|
+
isAuthenticated() {
|
|
1661
|
+
return this.authenticated;
|
|
1662
|
+
}
|
|
1663
|
+
resolveEnvVar(ref) {
|
|
1664
|
+
if (this.processEnv[ref] !== void 0) {
|
|
1665
|
+
return ref;
|
|
1666
|
+
}
|
|
1667
|
+
return Object.entries(this.definition.mapping ?? {}).find(([, logicalRef]) => logicalRef === ref)?.[0];
|
|
1668
|
+
}
|
|
1669
|
+
async batchGet(refs) {
|
|
1670
|
+
this.authenticated = true;
|
|
1671
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1672
|
+
for (const ref of Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right))) {
|
|
1673
|
+
const envVar = this.resolveEnvVar(ref);
|
|
1674
|
+
const value = envVar ? this.processEnv[envVar] : void 0;
|
|
1675
|
+
if (value !== void 0) {
|
|
1676
|
+
resolved.set(ref, value);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return resolved;
|
|
1680
|
+
}
|
|
1681
|
+
async get(ref) {
|
|
1682
|
+
const envVar = this.resolveEnvVar(ref);
|
|
1683
|
+
this.authenticated = true;
|
|
1684
|
+
return envVar ? this.processEnv[envVar] : void 0;
|
|
1685
|
+
}
|
|
1686
|
+
async set(ref, value) {
|
|
1687
|
+
void ref;
|
|
1688
|
+
void value;
|
|
1689
|
+
throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be written by CNOS.`);
|
|
1690
|
+
}
|
|
1691
|
+
async delete(ref) {
|
|
1692
|
+
void ref;
|
|
1693
|
+
throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be mutated by CNOS.`);
|
|
1694
|
+
}
|
|
1695
|
+
async list() {
|
|
1696
|
+
return Object.values(this.definition.mapping ?? {}).sort((left, right) => left.localeCompare(right));
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
// ../core/src/secrets/providers/local.ts
|
|
1701
|
+
var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
|
|
1702
|
+
constructor(vaultId, definition, processEnv = process.env, storeRoot = resolveSecretStoreRoot(processEnv)) {
|
|
1703
|
+
this.vaultId = vaultId;
|
|
1704
|
+
this.processEnv = processEnv;
|
|
1705
|
+
this.storeRoot = storeRoot;
|
|
1706
|
+
this.definition = definition;
|
|
1707
|
+
}
|
|
1708
|
+
vaultId;
|
|
1709
|
+
processEnv;
|
|
1710
|
+
storeRoot;
|
|
1711
|
+
authConfig;
|
|
1712
|
+
definition;
|
|
1713
|
+
static fromVaults(vaults, vaultId, processEnv) {
|
|
1714
|
+
const definition = resolveVaultDefinition(vaults, vaultId);
|
|
1715
|
+
return new _LocalSecretVaultProvider(vaultId, definition, processEnv);
|
|
1716
|
+
}
|
|
1717
|
+
async authenticate(authConfig) {
|
|
1718
|
+
this.authConfig = authConfig;
|
|
1719
|
+
await this.list();
|
|
1720
|
+
}
|
|
1721
|
+
isAuthenticated() {
|
|
1722
|
+
return Boolean(this.authConfig);
|
|
1723
|
+
}
|
|
1724
|
+
async requireAuth() {
|
|
1725
|
+
if (this.authConfig) {
|
|
1726
|
+
return this.authConfig;
|
|
1727
|
+
}
|
|
1728
|
+
const resolved = await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
|
|
1729
|
+
if (!resolved) {
|
|
1730
|
+
throw new CnosAuthenticationError(
|
|
1731
|
+
`Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
this.authConfig = resolved;
|
|
1735
|
+
return resolved;
|
|
1736
|
+
}
|
|
1737
|
+
async batchGet(refs) {
|
|
1738
|
+
const auth = await this.requireAuth();
|
|
1739
|
+
const entries = await Promise.all(
|
|
1740
|
+
Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right)).map(async (ref) => [ref, await readLocalSecret(this.storeRoot, ref, auth, this.vaultId)])
|
|
1741
|
+
);
|
|
1742
|
+
return new Map(entries);
|
|
1743
|
+
}
|
|
1744
|
+
async get(ref) {
|
|
1745
|
+
const auth = await this.requireAuth();
|
|
1746
|
+
try {
|
|
1747
|
+
return await readLocalSecret(this.storeRoot, ref, auth, this.vaultId);
|
|
1748
|
+
} catch {
|
|
1749
|
+
return void 0;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
async set(ref, value) {
|
|
1753
|
+
const auth = await this.requireAuth();
|
|
1754
|
+
await writeLocalSecret(this.storeRoot, ref, value, auth, this.vaultId);
|
|
1755
|
+
await appendAuditEvent(
|
|
1756
|
+
{
|
|
1757
|
+
action: "write",
|
|
1758
|
+
vault: this.vaultId,
|
|
1759
|
+
ref,
|
|
1760
|
+
caller: "cli"
|
|
1761
|
+
},
|
|
1762
|
+
this.processEnv
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
async delete(ref) {
|
|
1766
|
+
const auth = await this.requireAuth();
|
|
1767
|
+
await deleteLocalSecret(this.storeRoot, ref, auth, this.vaultId);
|
|
1768
|
+
await appendAuditEvent(
|
|
1769
|
+
{
|
|
1770
|
+
action: "delete",
|
|
1771
|
+
vault: this.vaultId,
|
|
1772
|
+
ref,
|
|
1773
|
+
caller: "cli"
|
|
1774
|
+
},
|
|
1775
|
+
this.processEnv
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
async list() {
|
|
1779
|
+
const auth = this.authConfig ?? await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
|
|
1780
|
+
if (!auth) {
|
|
1781
|
+
throw new CnosAuthenticationError(
|
|
1782
|
+
`Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
this.authConfig = auth;
|
|
1786
|
+
return listLocalSecrets(this.storeRoot, auth, this.vaultId);
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1790
|
+
// ../core/src/secrets/providers/registry.ts
|
|
1791
|
+
function createSecretVaultProvider(vaultId, definition, processEnv) {
|
|
1792
|
+
if (definition.provider === "local") {
|
|
1793
|
+
return new LocalSecretVaultProvider(vaultId, definition, processEnv);
|
|
1794
|
+
}
|
|
1795
|
+
if (definition.provider === "github-secrets") {
|
|
1796
|
+
return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
|
|
1797
|
+
}
|
|
1798
|
+
throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// ../core/src/secrets/prompt.ts
|
|
1802
|
+
var import_node_readline = __toESM(require("readline"), 1);
|
|
1803
|
+
var import_node_stream = require("stream");
|
|
1804
|
+
async function promptHidden(message) {
|
|
1805
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1806
|
+
return void 0;
|
|
1807
|
+
}
|
|
1808
|
+
const mutableStdout = new WritableMask();
|
|
1809
|
+
const rl = import_node_readline.default.createInterface({
|
|
1810
|
+
input: process.stdin,
|
|
1811
|
+
output: mutableStdout,
|
|
1812
|
+
terminal: true
|
|
1813
|
+
});
|
|
1814
|
+
try {
|
|
1815
|
+
mutableStdout.muted = true;
|
|
1816
|
+
const value = await new Promise((resolve) => {
|
|
1817
|
+
rl.question(message, resolve);
|
|
1818
|
+
});
|
|
1819
|
+
process.stdout.write("\n");
|
|
1820
|
+
return value;
|
|
1821
|
+
} finally {
|
|
1822
|
+
rl.close();
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
var WritableMask = class extends import_node_stream.Writable {
|
|
1826
|
+
muted = false;
|
|
1827
|
+
_write(chunk, _encoding, callback) {
|
|
1828
|
+
if (!this.muted) {
|
|
1829
|
+
process.stdout.write(chunk);
|
|
1830
|
+
}
|
|
1831
|
+
callback();
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
// ../core/src/secrets/resolveAuth.ts
|
|
1836
|
+
function toAuthError(vaultId, sources) {
|
|
1837
|
+
return new CnosAuthenticationError(
|
|
1838
|
+
`Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
|
|
1842
|
+
const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
|
|
1843
|
+
if (sessionKey) {
|
|
1844
|
+
return {
|
|
1845
|
+
derivedKey: sessionKey,
|
|
1846
|
+
method: "keychain",
|
|
1847
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
if (definition.provider === "github-secrets") {
|
|
1851
|
+
return {
|
|
1852
|
+
method: definition.auth?.method ?? "environment",
|
|
1853
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
|
|
1857
|
+
for (const source of sources) {
|
|
1858
|
+
if (source.startsWith("env:")) {
|
|
1859
|
+
const value = processEnv[source.slice(4)];
|
|
1860
|
+
if (value) {
|
|
1861
|
+
return {
|
|
1862
|
+
passphrase: value,
|
|
1863
|
+
method: "passphrase",
|
|
1864
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
if (source.startsWith("keychain:")) {
|
|
1869
|
+
const value = await readKeychain(source.slice("keychain:".length));
|
|
1870
|
+
if (value) {
|
|
1871
|
+
return {
|
|
1872
|
+
derivedKey: Buffer.from(value, "hex"),
|
|
1873
|
+
method: "keychain",
|
|
1874
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (source === "prompt") {
|
|
1879
|
+
const value = await promptHidden(`Enter passphrase for vault "${vaultId}": `);
|
|
1880
|
+
if (value) {
|
|
1881
|
+
return {
|
|
1882
|
+
passphrase: value,
|
|
1883
|
+
method: "passphrase",
|
|
1884
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const fallback = resolveSecretPassphrase(vaultId, processEnv);
|
|
1890
|
+
if (fallback) {
|
|
1891
|
+
return {
|
|
1892
|
+
passphrase: fallback,
|
|
1893
|
+
method: "passphrase",
|
|
1894
|
+
...definition.auth?.config ? { config: definition.auth.config } : {}
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// ../core/src/secrets/batchResolve.ts
|
|
1901
|
+
function collectSecretDescriptors(graph) {
|
|
1902
|
+
return Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => ({
|
|
1903
|
+
logicalKey: entry.key,
|
|
1904
|
+
ref: entry.value
|
|
1905
|
+
}));
|
|
1906
|
+
}
|
|
1907
|
+
async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
|
|
1908
|
+
const cache = new SecretCache();
|
|
1909
|
+
const descriptors = collectSecretDescriptors(graph);
|
|
1910
|
+
const grouped = descriptors.reduce((accumulator, descriptor) => {
|
|
1911
|
+
const vaultId = descriptor.ref.vault ?? "default";
|
|
1912
|
+
const bucket = accumulator.get(vaultId) ?? [];
|
|
1913
|
+
bucket.push(descriptor);
|
|
1914
|
+
accumulator.set(vaultId, bucket);
|
|
1915
|
+
return accumulator;
|
|
1916
|
+
}, /* @__PURE__ */ new Map());
|
|
1917
|
+
for (const [vaultId, refs] of grouped) {
|
|
1918
|
+
const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
|
|
1919
|
+
const provider = createSecretVaultProvider(vaultId, definition, processEnv);
|
|
1920
|
+
const auth = await resolveVaultAuth(vaultId, definition, processEnv);
|
|
1921
|
+
await provider.authenticate(auth);
|
|
1922
|
+
const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
|
|
1923
|
+
cache.load(vaultId, resolved);
|
|
1924
|
+
await appendAuditEvent(
|
|
1925
|
+
{
|
|
1926
|
+
action: "batch_read",
|
|
1927
|
+
vault: vaultId,
|
|
1928
|
+
refs: Array.from(resolved.keys()).sort((left, right) => left.localeCompare(right)),
|
|
1929
|
+
caller: "runtime",
|
|
1930
|
+
workspace: graph.workspace.workspaceId,
|
|
1931
|
+
profile: graph.profile
|
|
1932
|
+
},
|
|
1933
|
+
processEnv
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
return cache;
|
|
1937
|
+
}
|
|
1938
|
+
function resolveSecretEntryValue(key, value, cache) {
|
|
1939
|
+
if (!key.startsWith("secret.") || !isSecretReference(value)) {
|
|
1940
|
+
return value;
|
|
1941
|
+
}
|
|
1942
|
+
const vaultId = value.vault ?? "default";
|
|
1943
|
+
return cache.get(vaultId, value.ref) ?? value;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1109
1946
|
// ../core/src/runtime/projection.ts
|
|
1110
1947
|
function setNestedValue(target, pathSegments, value) {
|
|
1111
1948
|
const [head, ...tail] = pathSegments;
|
|
@@ -1156,55 +1993,6 @@ function requireValue(graph, key) {
|
|
|
1156
1993
|
return value;
|
|
1157
1994
|
}
|
|
1158
1995
|
|
|
1159
|
-
// ../core/src/utils/secretStore.ts
|
|
1160
|
-
var import_node_crypto = require("crypto");
|
|
1161
|
-
var import_promises6 = require("fs/promises");
|
|
1162
|
-
var import_node_path6 = __toESM(require("path"), 1);
|
|
1163
|
-
function isObject(value) {
|
|
1164
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1165
|
-
}
|
|
1166
|
-
function isSecretReference(value) {
|
|
1167
|
-
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));
|
|
1168
|
-
}
|
|
1169
|
-
function resolveSecretStoreRoot(processEnv = process.env) {
|
|
1170
|
-
return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
|
|
1171
|
-
}
|
|
1172
|
-
function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
|
|
1173
|
-
return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
|
|
1174
|
-
}
|
|
1175
|
-
function deriveKey(passphrase, salt) {
|
|
1176
|
-
return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
|
|
1177
|
-
}
|
|
1178
|
-
function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
|
|
1179
|
-
const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
1180
|
-
return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
|
|
1181
|
-
}
|
|
1182
|
-
function decryptDocument(document, passphrase) {
|
|
1183
|
-
const salt = Buffer.from(document.salt, "base64");
|
|
1184
|
-
const iv = Buffer.from(document.iv, "base64");
|
|
1185
|
-
const tag = Buffer.from(document.tag, "base64");
|
|
1186
|
-
const ciphertext = Buffer.from(document.ciphertext, "base64");
|
|
1187
|
-
const key = deriveKey(passphrase, salt);
|
|
1188
|
-
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
|
|
1189
|
-
decipher.setAuthTag(tag);
|
|
1190
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1191
|
-
return plaintext.toString("utf8");
|
|
1192
|
-
}
|
|
1193
|
-
async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
|
|
1194
|
-
if (!passphrase) {
|
|
1195
|
-
throw new CnosManifestError(
|
|
1196
|
-
`Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
|
|
1197
|
-
);
|
|
1198
|
-
}
|
|
1199
|
-
const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
|
|
1200
|
-
const source = await (0, import_promises6.readFile)(filePath, "utf8");
|
|
1201
|
-
const document = JSON.parse(source);
|
|
1202
|
-
if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
|
|
1203
|
-
throw new CnosManifestError("Invalid local secret document", filePath);
|
|
1204
|
-
}
|
|
1205
|
-
return decryptDocument(document, passphrase);
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
1996
|
// ../core/src/runtime/toEnv.ts
|
|
1209
1997
|
function normalizeEnvValue(value) {
|
|
1210
1998
|
if (value === void 0 || value === null) {
|
|
@@ -1286,28 +2074,42 @@ function toPublicEnv(graph, manifest, options = {}) {
|
|
|
1286
2074
|
}
|
|
1287
2075
|
|
|
1288
2076
|
// ../core/src/orchestrator/runtime.ts
|
|
1289
|
-
function createRuntime(manifest, graph, plugins = []) {
|
|
2077
|
+
function createRuntime(manifest, graph, plugins = [], secretCache) {
|
|
2078
|
+
function readLogicalKey(key) {
|
|
2079
|
+
const entry = graph.entries.get(key);
|
|
2080
|
+
if (!entry) {
|
|
2081
|
+
return void 0;
|
|
2082
|
+
}
|
|
2083
|
+
if (!secretCache) {
|
|
2084
|
+
return entry.value;
|
|
2085
|
+
}
|
|
2086
|
+
return resolveSecretEntryValue(key, entry.value, secretCache);
|
|
2087
|
+
}
|
|
1290
2088
|
return {
|
|
1291
2089
|
manifest,
|
|
1292
2090
|
plugins,
|
|
1293
2091
|
graph,
|
|
1294
2092
|
read(key) {
|
|
1295
|
-
return
|
|
2093
|
+
return readLogicalKey(key);
|
|
1296
2094
|
},
|
|
1297
2095
|
require(key) {
|
|
1298
|
-
|
|
2096
|
+
const value = readLogicalKey(key);
|
|
2097
|
+
if (value === void 0) {
|
|
2098
|
+
return requireValue(graph, key);
|
|
2099
|
+
}
|
|
2100
|
+
return value;
|
|
1299
2101
|
},
|
|
1300
2102
|
readOr(key, fallback) {
|
|
1301
2103
|
return readOrValue(graph, key, fallback);
|
|
1302
2104
|
},
|
|
1303
|
-
value(
|
|
1304
|
-
return
|
|
2105
|
+
value(path12) {
|
|
2106
|
+
return readLogicalKey(toLogicalKey("value", path12));
|
|
1305
2107
|
},
|
|
1306
|
-
secret(
|
|
1307
|
-
return
|
|
2108
|
+
secret(path12) {
|
|
2109
|
+
return readLogicalKey(toLogicalKey("secret", path12));
|
|
1308
2110
|
},
|
|
1309
|
-
meta(
|
|
1310
|
-
return
|
|
2111
|
+
meta(path12) {
|
|
2112
|
+
return readLogicalKey(toLogicalKey("meta", path12));
|
|
1311
2113
|
},
|
|
1312
2114
|
inspect(key) {
|
|
1313
2115
|
return inspectValue(graph, key);
|
|
@@ -1465,61 +2267,21 @@ async function createCnos(options = {}) {
|
|
|
1465
2267
|
});
|
|
1466
2268
|
const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
|
|
1467
2269
|
const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
|
|
2270
|
+
const secretCache = options.secretResolution === "lazy" ? void 0 : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
|
|
1468
2271
|
return createRuntime(
|
|
1469
2272
|
loadedManifest.manifest,
|
|
1470
2273
|
appendMetaEntries({
|
|
1471
2274
|
...promotedGraph,
|
|
1472
2275
|
profileSource: activeProfile.source
|
|
1473
2276
|
}, options.cnosVersion),
|
|
1474
|
-
plugins
|
|
2277
|
+
plugins,
|
|
2278
|
+
secretCache
|
|
1475
2279
|
);
|
|
1476
2280
|
}
|
|
1477
2281
|
|
|
1478
2282
|
// ../core/src/runtime/dump.ts
|
|
1479
|
-
var
|
|
1480
|
-
var
|
|
1481
|
-
function buildDumpFiles(graph, options = {}) {
|
|
1482
|
-
const basePath = options.flatten ? "" : import_node_path7.default.posix.join("workspaces", graph.workspace.workspaceId);
|
|
1483
|
-
const values = toNamespaceObject(graph, "value");
|
|
1484
|
-
const secrets = toNamespaceObject(graph, "secret");
|
|
1485
|
-
const files = [];
|
|
1486
|
-
if (Object.keys(values).length > 0) {
|
|
1487
|
-
files.push({
|
|
1488
|
-
path: import_node_path7.default.posix.join(basePath, "values", graph.profile, "app.yml"),
|
|
1489
|
-
namespace: "value",
|
|
1490
|
-
content: stringifyYaml(values)
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
if (Object.keys(secrets).length > 0) {
|
|
1494
|
-
files.push({
|
|
1495
|
-
path: import_node_path7.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
|
|
1496
|
-
namespace: "secret",
|
|
1497
|
-
content: stringifyYaml(secrets)
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
return files.sort((left, right) => left.path.localeCompare(right.path));
|
|
1501
|
-
}
|
|
1502
|
-
function planDump(graph, options = {}) {
|
|
1503
|
-
return {
|
|
1504
|
-
workspaceId: graph.workspace.workspaceId,
|
|
1505
|
-
profile: graph.profile,
|
|
1506
|
-
flatten: options.flatten ?? false,
|
|
1507
|
-
files: buildDumpFiles(graph, options)
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
async function writeDump(graph, options) {
|
|
1511
|
-
const root = import_node_path7.default.resolve(options.to);
|
|
1512
|
-
const plan = planDump(graph, options);
|
|
1513
|
-
for (const file of plan.files) {
|
|
1514
|
-
const destination = import_node_path7.default.join(root, file.path);
|
|
1515
|
-
await (0, import_promises7.mkdir)(import_node_path7.default.dirname(destination), { recursive: true });
|
|
1516
|
-
await (0, import_promises7.writeFile)(destination, file.content, "utf8");
|
|
1517
|
-
}
|
|
1518
|
-
return {
|
|
1519
|
-
...plan,
|
|
1520
|
-
root
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
2283
|
+
var import_promises9 = require("fs/promises");
|
|
2284
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
1523
2285
|
|
|
1524
2286
|
// ../core/src/utils/envNaming.ts
|
|
1525
2287
|
function normalizeMappingConfig(config = {}) {
|
|
@@ -1528,8 +2290,8 @@ function normalizeMappingConfig(config = {}) {
|
|
|
1528
2290
|
explicit: config.explicit ?? {}
|
|
1529
2291
|
};
|
|
1530
2292
|
}
|
|
1531
|
-
function fromScreamingSnake(
|
|
1532
|
-
return
|
|
2293
|
+
function fromScreamingSnake(path12) {
|
|
2294
|
+
return path12.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
|
|
1533
2295
|
}
|
|
1534
2296
|
function envVarToLogicalKey(envVar, config = {}) {
|
|
1535
2297
|
const normalized = normalizeMappingConfig(config);
|
|
@@ -1556,7 +2318,7 @@ function envVarToLogicalKey(envVar, config = {}) {
|
|
|
1556
2318
|
// package.json
|
|
1557
2319
|
var package_default = {
|
|
1558
2320
|
name: "@kitsy/cnos",
|
|
1559
|
-
version: "1.
|
|
2321
|
+
version: "1.4.0",
|
|
1560
2322
|
description: "Batteries-included CNOS runtime package wired with the official plugins.",
|
|
1561
2323
|
type: "module",
|
|
1562
2324
|
main: "./dist/index.cjs",
|
|
@@ -1568,6 +2330,16 @@ var package_default = {
|
|
|
1568
2330
|
import: "./dist/index.js",
|
|
1569
2331
|
require: "./dist/index.cjs"
|
|
1570
2332
|
},
|
|
2333
|
+
"./configure": {
|
|
2334
|
+
types: "./dist/configure/index.d.ts",
|
|
2335
|
+
import: "./dist/configure/index.js",
|
|
2336
|
+
require: "./dist/configure/index.cjs"
|
|
2337
|
+
},
|
|
2338
|
+
"./create": {
|
|
2339
|
+
types: "./dist/configure/index.d.ts",
|
|
2340
|
+
import: "./dist/configure/index.js",
|
|
2341
|
+
require: "./dist/configure/index.cjs"
|
|
2342
|
+
},
|
|
1571
2343
|
"./internal": {
|
|
1572
2344
|
types: "./dist/internal.d.ts",
|
|
1573
2345
|
import: "./dist/internal.js",
|
|
@@ -1745,8 +2517,8 @@ function createCliArgsPlugin() {
|
|
|
1745
2517
|
}
|
|
1746
2518
|
|
|
1747
2519
|
// ../../plugins/dotenv/src/index.ts
|
|
1748
|
-
var
|
|
1749
|
-
var
|
|
2520
|
+
var import_promises10 = require("fs/promises");
|
|
2521
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
1750
2522
|
var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
|
|
1751
2523
|
function parseDoubleQuoted(value) {
|
|
1752
2524
|
return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
@@ -1803,7 +2575,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
|
|
|
1803
2575
|
}
|
|
1804
2576
|
async function readIfPresent(filePath) {
|
|
1805
2577
|
try {
|
|
1806
|
-
return await (0,
|
|
2578
|
+
return await (0, import_promises10.readFile)(filePath, "utf8");
|
|
1807
2579
|
} catch {
|
|
1808
2580
|
return void 0;
|
|
1809
2581
|
}
|
|
@@ -1822,7 +2594,7 @@ function createDotenvPlugin() {
|
|
|
1822
2594
|
workspace: workspaceRoot.workspaceId
|
|
1823
2595
|
});
|
|
1824
2596
|
for (const fileName of fileNames) {
|
|
1825
|
-
const absolutePath =
|
|
2597
|
+
const absolutePath = import_node_path10.default.join(envRoot, fileName);
|
|
1826
2598
|
const document = await readIfPresent(absolutePath);
|
|
1827
2599
|
if (!document) {
|
|
1828
2600
|
continue;
|
|
@@ -1831,7 +2603,7 @@ function createDotenvPlugin() {
|
|
|
1831
2603
|
...dotenvEntriesFromObject(
|
|
1832
2604
|
parseDotenv(document),
|
|
1833
2605
|
config.envMapping,
|
|
1834
|
-
toPortablePath(
|
|
2606
|
+
toPortablePath(import_node_path10.default.relative(import_node_path10.default.dirname(context.manifestRoot), absolutePath)),
|
|
1835
2607
|
workspaceRoot.workspaceId
|
|
1836
2608
|
)
|
|
1837
2609
|
);
|
|
@@ -1869,32 +2641,32 @@ function createPublicEnvExportPlugin() {
|
|
|
1869
2641
|
}
|
|
1870
2642
|
|
|
1871
2643
|
// ../../plugins/filesystem/src/filesystemSecretsReader.ts
|
|
1872
|
-
var
|
|
2644
|
+
var import_promises12 = require("fs/promises");
|
|
1873
2645
|
|
|
1874
2646
|
// ../../plugins/filesystem/src/helpers.ts
|
|
1875
|
-
var
|
|
1876
|
-
var
|
|
2647
|
+
var import_promises11 = require("fs/promises");
|
|
2648
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
1877
2649
|
var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
|
|
1878
2650
|
var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
|
|
1879
2651
|
async function existsDirectory(targetPath) {
|
|
1880
2652
|
try {
|
|
1881
|
-
const
|
|
1882
|
-
void
|
|
2653
|
+
const stat2 = await (0, import_promises11.readdir)(targetPath);
|
|
2654
|
+
void stat2;
|
|
1883
2655
|
return true;
|
|
1884
2656
|
} catch {
|
|
1885
2657
|
return false;
|
|
1886
2658
|
}
|
|
1887
2659
|
}
|
|
1888
2660
|
async function collectYamlFiles(root) {
|
|
1889
|
-
const entries = await (0,
|
|
2661
|
+
const entries = await (0, import_promises11.readdir)(root, { withFileTypes: true });
|
|
1890
2662
|
const results = [];
|
|
1891
2663
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1892
|
-
const absolutePath =
|
|
2664
|
+
const absolutePath = import_node_path11.default.join(root, entry.name);
|
|
1893
2665
|
if (entry.isDirectory()) {
|
|
1894
2666
|
results.push(...await collectYamlFiles(absolutePath));
|
|
1895
2667
|
continue;
|
|
1896
2668
|
}
|
|
1897
|
-
if (entry.isFile() && YAML_EXTENSIONS.has(
|
|
2669
|
+
if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path11.default.extname(entry.name).toLowerCase())) {
|
|
1898
2670
|
results.push(absolutePath);
|
|
1899
2671
|
}
|
|
1900
2672
|
}
|
|
@@ -1902,16 +2674,16 @@ async function collectYamlFiles(root) {
|
|
|
1902
2674
|
}
|
|
1903
2675
|
async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
|
|
1904
2676
|
const files = [];
|
|
1905
|
-
const repoRoot =
|
|
2677
|
+
const repoRoot = import_node_path11.default.dirname(manifestRoot);
|
|
1906
2678
|
for (const workspaceRoot of workspaceRoots) {
|
|
1907
|
-
const resolvedRoot =
|
|
2679
|
+
const resolvedRoot = import_node_path11.default.resolve(workspaceRoot.path, sourceRoot);
|
|
1908
2680
|
for (const layer of activeLayers) {
|
|
1909
|
-
const layerRoot =
|
|
2681
|
+
const layerRoot = import_node_path11.default.join(resolvedRoot, layer);
|
|
1910
2682
|
if (!await existsDirectory(layerRoot)) {
|
|
1911
2683
|
continue;
|
|
1912
2684
|
}
|
|
1913
2685
|
for (const absolutePath of await collectYamlFiles(layerRoot)) {
|
|
1914
|
-
const relativePath =
|
|
2686
|
+
const relativePath = import_node_path11.default.relative(repoRoot, absolutePath);
|
|
1915
2687
|
files.push({
|
|
1916
2688
|
absolutePath,
|
|
1917
2689
|
relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
|
|
@@ -1961,31 +2733,6 @@ function yamlObjectToEntries(document, filePath, namespace, sourceId, workspaceI
|
|
|
1961
2733
|
}
|
|
1962
2734
|
}));
|
|
1963
2735
|
}
|
|
1964
|
-
async function resolveSecretValue(value, processEnv) {
|
|
1965
|
-
if (!isSecretReference(value)) {
|
|
1966
|
-
return value;
|
|
1967
|
-
}
|
|
1968
|
-
if (value.provider === "local") {
|
|
1969
|
-
const passphrase = resolveSecretPassphrase(value.vault, processEnv);
|
|
1970
|
-
if (!passphrase) {
|
|
1971
|
-
return value;
|
|
1972
|
-
}
|
|
1973
|
-
return readLocalSecret(
|
|
1974
|
-
resolveSecretStoreRoot(processEnv),
|
|
1975
|
-
value.ref,
|
|
1976
|
-
passphrase,
|
|
1977
|
-
value.vault
|
|
1978
|
-
);
|
|
1979
|
-
}
|
|
1980
|
-
if (value.provider === "env" || value.provider === "github-secrets") {
|
|
1981
|
-
const resolved = processEnv?.[value.ref];
|
|
1982
|
-
if (resolved === void 0) {
|
|
1983
|
-
return value;
|
|
1984
|
-
}
|
|
1985
|
-
return resolved;
|
|
1986
|
-
}
|
|
1987
|
-
return value;
|
|
1988
|
-
}
|
|
1989
2736
|
function toSecretReferenceMetadata(value) {
|
|
1990
2737
|
if (!isSecretReference(value)) {
|
|
1991
2738
|
return void 0;
|
|
@@ -2013,14 +2760,12 @@ function createFilesystemSecretsPlugin() {
|
|
|
2013
2760
|
);
|
|
2014
2761
|
const entries = [];
|
|
2015
2762
|
for (const file of files) {
|
|
2016
|
-
const document = await (0,
|
|
2763
|
+
const document = await (0, import_promises12.readFile)(file.absolutePath, "utf8");
|
|
2017
2764
|
const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
|
|
2018
2765
|
for (const entry of fileEntries) {
|
|
2019
2766
|
const metadata = toSecretReferenceMetadata(entry.value);
|
|
2020
|
-
const resolvedValue = await resolveSecretValue(entry.value, context.processEnv);
|
|
2021
2767
|
entries.push({
|
|
2022
2768
|
...entry,
|
|
2023
|
-
value: resolvedValue,
|
|
2024
2769
|
...metadata ? { metadata } : {}
|
|
2025
2770
|
});
|
|
2026
2771
|
}
|
|
@@ -2031,7 +2776,7 @@ function createFilesystemSecretsPlugin() {
|
|
|
2031
2776
|
}
|
|
2032
2777
|
|
|
2033
2778
|
// ../../plugins/filesystem/src/filesystemValuesReader.ts
|
|
2034
|
-
var
|
|
2779
|
+
var import_promises13 = require("fs/promises");
|
|
2035
2780
|
function filesystemValuesReader(filePath, document, workspaceId = "default") {
|
|
2036
2781
|
return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
|
|
2037
2782
|
}
|
|
@@ -2047,11 +2792,30 @@ function createFilesystemValuesPlugin() {
|
|
|
2047
2792
|
sourceRoot,
|
|
2048
2793
|
context.profileActivation.values
|
|
2049
2794
|
);
|
|
2795
|
+
const customNamespaces = Object.entries(context.manifest.namespaces).filter(
|
|
2796
|
+
([namespace, definition]) => namespace !== "value" && namespace !== "secret" && definition.kind === "data" && !definition.sensitive
|
|
2797
|
+
).map(([namespace]) => namespace);
|
|
2050
2798
|
const entries = [];
|
|
2051
2799
|
for (const file of files) {
|
|
2052
|
-
const document = await (0,
|
|
2800
|
+
const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
|
|
2053
2801
|
entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
|
|
2054
2802
|
}
|
|
2803
|
+
for (const namespace of customNamespaces) {
|
|
2804
|
+
const layers = [
|
|
2805
|
+
namespace,
|
|
2806
|
+
...context.profileChain.filter((profile) => profile !== "base").map((profile) => `profiles/${profile}/${namespace}`)
|
|
2807
|
+
];
|
|
2808
|
+
const namespaceFiles = await collectFilesystemLayerFiles(
|
|
2809
|
+
context.manifestRoot,
|
|
2810
|
+
context.workspace.workspaceRoots,
|
|
2811
|
+
sourceRoot,
|
|
2812
|
+
layers
|
|
2813
|
+
);
|
|
2814
|
+
for (const file of namespaceFiles) {
|
|
2815
|
+
const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
|
|
2816
|
+
entries.push(...yamlObjectToEntries(document, file.relativePath, namespace, "filesystem-values", file.workspaceId));
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2055
2819
|
return entries;
|
|
2056
2820
|
}
|
|
2057
2821
|
};
|
|
@@ -2059,6 +2823,11 @@ function createFilesystemValuesPlugin() {
|
|
|
2059
2823
|
|
|
2060
2824
|
// ../../plugins/process-env/src/index.ts
|
|
2061
2825
|
var PROCESS_ENV_PLUGIN_ID = "@kitsy/cnos/plugins/process-env";
|
|
2826
|
+
var PROCESS_GRAPH_OMIT = /* @__PURE__ */ new Set([
|
|
2827
|
+
"__CNOS_GRAPH__",
|
|
2828
|
+
"__CNOS_SECRET_PAYLOAD__",
|
|
2829
|
+
"__CNOS_SESSION_KEY__"
|
|
2830
|
+
]);
|
|
2062
2831
|
function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default") {
|
|
2063
2832
|
return Object.entries(env).flatMap(([envVar, value]) => {
|
|
2064
2833
|
if (typeof value !== "string") {
|
|
@@ -2083,17 +2852,77 @@ function processEnvEntriesFromObject(env, mapping = {}, workspaceId = "default")
|
|
|
2083
2852
|
];
|
|
2084
2853
|
});
|
|
2085
2854
|
}
|
|
2855
|
+
function processNamespaceEntriesFromContext(env, workspaceId = "default") {
|
|
2856
|
+
const envEntries = Object.entries(env).filter((entry) => typeof entry[1] === "string").filter(([envVar]) => !PROCESS_GRAPH_OMIT.has(envVar)).map(([envVar, value]) => ({
|
|
2857
|
+
key: `process.env.${envVar}`,
|
|
2858
|
+
value,
|
|
2859
|
+
namespace: "process",
|
|
2860
|
+
sourceId: "process-runtime",
|
|
2861
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2862
|
+
workspaceId,
|
|
2863
|
+
origin: {
|
|
2864
|
+
envVar
|
|
2865
|
+
}
|
|
2866
|
+
}));
|
|
2867
|
+
const runtimeEntries = [
|
|
2868
|
+
{
|
|
2869
|
+
key: "process.cwd",
|
|
2870
|
+
value: process.cwd(),
|
|
2871
|
+
namespace: "process",
|
|
2872
|
+
sourceId: "process-runtime",
|
|
2873
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2874
|
+
workspaceId
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
key: "process.platform",
|
|
2878
|
+
value: process.platform,
|
|
2879
|
+
namespace: "process",
|
|
2880
|
+
sourceId: "process-runtime",
|
|
2881
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2882
|
+
workspaceId
|
|
2883
|
+
},
|
|
2884
|
+
{
|
|
2885
|
+
key: "process.arch",
|
|
2886
|
+
value: process.arch,
|
|
2887
|
+
namespace: "process",
|
|
2888
|
+
sourceId: "process-runtime",
|
|
2889
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2890
|
+
workspaceId
|
|
2891
|
+
},
|
|
2892
|
+
{
|
|
2893
|
+
key: "process.node.version",
|
|
2894
|
+
value: process.version,
|
|
2895
|
+
namespace: "process",
|
|
2896
|
+
sourceId: "process-runtime",
|
|
2897
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2898
|
+
workspaceId
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
key: "process.args.raw",
|
|
2902
|
+
value: process.argv.slice(2),
|
|
2903
|
+
namespace: "process",
|
|
2904
|
+
sourceId: "process-runtime",
|
|
2905
|
+
pluginId: PROCESS_ENV_PLUGIN_ID,
|
|
2906
|
+
workspaceId
|
|
2907
|
+
}
|
|
2908
|
+
];
|
|
2909
|
+
return [...runtimeEntries, ...envEntries];
|
|
2910
|
+
}
|
|
2086
2911
|
function createProcessEnvPlugin() {
|
|
2087
2912
|
return {
|
|
2088
2913
|
id: "process-env",
|
|
2089
2914
|
kind: "loader",
|
|
2090
2915
|
async load(context) {
|
|
2091
2916
|
const config = context.manifestConfig;
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2917
|
+
const env = context.processEnv ?? process.env;
|
|
2918
|
+
return [
|
|
2919
|
+
...processEnvEntriesFromObject(
|
|
2920
|
+
env,
|
|
2921
|
+
config.envMapping,
|
|
2922
|
+
context.workspace.workspaceId
|
|
2923
|
+
),
|
|
2924
|
+
...processNamespaceEntriesFromContext(env, context.workspace.workspaceId)
|
|
2925
|
+
];
|
|
2097
2926
|
}
|
|
2098
2927
|
};
|
|
2099
2928
|
}
|
|
@@ -2116,16 +2945,35 @@ function defaultPlugins() {
|
|
|
2116
2945
|
// src/runtime/state.ts
|
|
2117
2946
|
var singletonRuntime;
|
|
2118
2947
|
var singletonReady;
|
|
2948
|
+
var bootstrappedSecretHydrationRequired = false;
|
|
2949
|
+
function getSingletonRuntime() {
|
|
2950
|
+
return singletonRuntime;
|
|
2951
|
+
}
|
|
2119
2952
|
function setSingletonRuntime(runtime) {
|
|
2120
2953
|
singletonRuntime = runtime;
|
|
2121
2954
|
singletonReady = Promise.resolve(runtime);
|
|
2955
|
+
bootstrappedSecretHydrationRequired = false;
|
|
2122
2956
|
return runtime;
|
|
2123
2957
|
}
|
|
2958
|
+
function getSingletonReady() {
|
|
2959
|
+
return singletonReady;
|
|
2960
|
+
}
|
|
2961
|
+
function setSingletonReady(promise) {
|
|
2962
|
+
singletonReady = promise;
|
|
2963
|
+
return promise;
|
|
2964
|
+
}
|
|
2965
|
+
function getBootstrappedSecretHydrationRequired() {
|
|
2966
|
+
return bootstrappedSecretHydrationRequired;
|
|
2967
|
+
}
|
|
2968
|
+
function setBootstrappedSecretHydrationRequired(value) {
|
|
2969
|
+
bootstrappedSecretHydrationRequired = value;
|
|
2970
|
+
}
|
|
2124
2971
|
|
|
2125
2972
|
// src/createCnos.ts
|
|
2126
2973
|
async function createCnos2(options = {}) {
|
|
2127
2974
|
const runtime = await createCnos({
|
|
2128
2975
|
...options,
|
|
2976
|
+
processEnv: options.processEnv ?? process.env,
|
|
2129
2977
|
cnosVersion: package_default.version,
|
|
2130
2978
|
plugins: [...defaultPlugins(), ...options.plugins ?? []]
|
|
2131
2979
|
});
|
|
@@ -2133,25 +2981,234 @@ async function createCnos2(options = {}) {
|
|
|
2133
2981
|
return runtime;
|
|
2134
2982
|
}
|
|
2135
2983
|
|
|
2136
|
-
// src/
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2984
|
+
// src/runtime/bootstrap.ts
|
|
2985
|
+
var import_node_crypto2 = require("crypto");
|
|
2986
|
+
var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
|
|
2987
|
+
var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
|
|
2988
|
+
var CNOS_SESSION_KEY_ENV_VAR = "__CNOS_SESSION_KEY__";
|
|
2989
|
+
function deserializeRuntimeGraph(source) {
|
|
2990
|
+
const payload = JSON.parse(source);
|
|
2991
|
+
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)) {
|
|
2992
|
+
throw new Error("Invalid CNOS runtime bootstrap payload");
|
|
2993
|
+
}
|
|
2994
|
+
return {
|
|
2995
|
+
entries: new Map(
|
|
2996
|
+
payload.entries.map((entry) => [
|
|
2997
|
+
entry.key,
|
|
2998
|
+
{
|
|
2999
|
+
key: entry.key,
|
|
3000
|
+
value: entry.value,
|
|
3001
|
+
namespace: entry.namespace,
|
|
3002
|
+
winner: entry.winner,
|
|
3003
|
+
overridden: entry.overridden ?? []
|
|
3004
|
+
}
|
|
3005
|
+
])
|
|
3006
|
+
),
|
|
3007
|
+
profile: payload.profile,
|
|
3008
|
+
resolvedAt: payload.resolvedAt,
|
|
3009
|
+
profileSource: payload.profileSource,
|
|
3010
|
+
workspace: payload.workspace
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
function decryptSecretPayload(serialized, sessionKey) {
|
|
3014
|
+
const payload = JSON.parse(serialized);
|
|
3015
|
+
if (!payload || typeof payload.iv !== "string" || typeof payload.tag !== "string" || typeof payload.ciphertext !== "string") {
|
|
3016
|
+
throw new Error("Invalid CNOS secret payload");
|
|
3017
|
+
}
|
|
3018
|
+
const key = Buffer.from(sessionKey, "hex");
|
|
3019
|
+
const iv = Buffer.from(payload.iv, "base64");
|
|
3020
|
+
const tag = Buffer.from(payload.tag, "base64");
|
|
3021
|
+
const ciphertext = Buffer.from(payload.ciphertext, "base64");
|
|
3022
|
+
const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
|
|
3023
|
+
decipher.setAuthTag(tag);
|
|
3024
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
3025
|
+
return JSON.parse(plaintext);
|
|
3026
|
+
}
|
|
3027
|
+
function readRuntimeGraphFromEnv(processEnv = process.env) {
|
|
3028
|
+
const serialized = processEnv[CNOS_GRAPH_ENV_VAR];
|
|
3029
|
+
if (!serialized) {
|
|
3030
|
+
return void 0;
|
|
3031
|
+
}
|
|
3032
|
+
const graph = deserializeRuntimeGraph(serialized);
|
|
3033
|
+
const secretPayload = processEnv[CNOS_SECRET_PAYLOAD_ENV_VAR];
|
|
3034
|
+
const sessionKey = processEnv[CNOS_SESSION_KEY_ENV_VAR];
|
|
3035
|
+
if (secretPayload && sessionKey) {
|
|
3036
|
+
const decrypted = decryptSecretPayload(secretPayload, sessionKey);
|
|
3037
|
+
for (const [key, value] of Object.entries(decrypted)) {
|
|
3038
|
+
const entry = graph.entries.get(key);
|
|
3039
|
+
if (entry) {
|
|
3040
|
+
entry.value = value;
|
|
3041
|
+
}
|
|
2143
3042
|
}
|
|
2144
|
-
browserData[key] = entry.value;
|
|
2145
3043
|
}
|
|
2146
|
-
return
|
|
3044
|
+
return graph;
|
|
2147
3045
|
}
|
|
3046
|
+
function graphRequiresSecretHydration(graph) {
|
|
3047
|
+
return Array.from(graph.entries.values()).some((entry) => entry.namespace === "secret" && isSecretReference(entry.value));
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
// src/runtime/index.ts
|
|
3051
|
+
var NOT_READY_MESSAGE = "CNOS not initialized. Call await cnos.ready() or use cnos run.";
|
|
3052
|
+
function getRuntimeOrThrow() {
|
|
3053
|
+
const runtime = getSingletonRuntime();
|
|
3054
|
+
if (!runtime) {
|
|
3055
|
+
throw new Error(NOT_READY_MESSAGE);
|
|
3056
|
+
}
|
|
3057
|
+
return runtime;
|
|
3058
|
+
}
|
|
3059
|
+
function attachBootstrappedGraph(graph) {
|
|
3060
|
+
if (getSingletonRuntime()) {
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
3063
|
+
const bootstrappedManifest = {
|
|
3064
|
+
version: 1,
|
|
3065
|
+
project: {
|
|
3066
|
+
name: "bootstrapped"
|
|
3067
|
+
},
|
|
3068
|
+
workspaces: {
|
|
3069
|
+
global: {
|
|
3070
|
+
enabled: Boolean(graph.workspace.globalRoot),
|
|
3071
|
+
...graph.workspace.globalRoot ? {
|
|
3072
|
+
root: graph.workspace.globalRoot
|
|
3073
|
+
} : {},
|
|
3074
|
+
allowWrite: false
|
|
3075
|
+
},
|
|
3076
|
+
items: {},
|
|
3077
|
+
...graph.workspace.workspaceSource === "implicit" ? {} : {
|
|
3078
|
+
default: graph.workspace.workspaceId
|
|
3079
|
+
}
|
|
3080
|
+
},
|
|
3081
|
+
profiles: {
|
|
3082
|
+
default: graph.profile,
|
|
3083
|
+
resolveFrom: ["default"]
|
|
3084
|
+
},
|
|
3085
|
+
plugins: {
|
|
3086
|
+
loaders: [],
|
|
3087
|
+
resolver: "profile-aware",
|
|
3088
|
+
validators: [],
|
|
3089
|
+
exporters: [],
|
|
3090
|
+
inspectors: []
|
|
3091
|
+
},
|
|
3092
|
+
sources: {},
|
|
3093
|
+
resolution: {
|
|
3094
|
+
precedence: [],
|
|
3095
|
+
arrayPolicy: "replace"
|
|
3096
|
+
},
|
|
3097
|
+
envMapping: {
|
|
3098
|
+
explicit: {}
|
|
3099
|
+
},
|
|
3100
|
+
public: {
|
|
3101
|
+
promote: [],
|
|
3102
|
+
frameworks: {}
|
|
3103
|
+
},
|
|
3104
|
+
namespaces: {},
|
|
3105
|
+
vaults: {},
|
|
3106
|
+
writePolicy: {
|
|
3107
|
+
define: {
|
|
3108
|
+
defaultProfile: graph.profile,
|
|
3109
|
+
targets: {
|
|
3110
|
+
value: "./values/app.yml",
|
|
3111
|
+
secret: "./secrets/app.yml"
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
},
|
|
3115
|
+
schema: {}
|
|
3116
|
+
};
|
|
3117
|
+
const runtime = {
|
|
3118
|
+
manifest: bootstrappedManifest,
|
|
3119
|
+
plugins: [],
|
|
3120
|
+
graph,
|
|
3121
|
+
read(key) {
|
|
3122
|
+
return readValue(graph, key);
|
|
3123
|
+
},
|
|
3124
|
+
require(key) {
|
|
3125
|
+
return requireValue(graph, key);
|
|
3126
|
+
},
|
|
3127
|
+
readOr(key, fallback) {
|
|
3128
|
+
return readOrValue(graph, key, fallback);
|
|
3129
|
+
},
|
|
3130
|
+
value(path12) {
|
|
3131
|
+
return readValue(graph, toLogicalKey("value", path12));
|
|
3132
|
+
},
|
|
3133
|
+
secret(path12) {
|
|
3134
|
+
return readValue(graph, toLogicalKey("secret", path12));
|
|
3135
|
+
},
|
|
3136
|
+
meta(path12) {
|
|
3137
|
+
return readValue(graph, toLogicalKey("meta", path12));
|
|
3138
|
+
},
|
|
3139
|
+
inspect(key) {
|
|
3140
|
+
return inspectValue(graph, key);
|
|
3141
|
+
},
|
|
3142
|
+
toObject() {
|
|
3143
|
+
return toNamespaceObject(graph);
|
|
3144
|
+
},
|
|
3145
|
+
toNamespace(namespace) {
|
|
3146
|
+
return toNamespaceObject(graph, namespace);
|
|
3147
|
+
},
|
|
3148
|
+
toEnv(options) {
|
|
3149
|
+
return toEnv(graph, bootstrappedManifest, options);
|
|
3150
|
+
},
|
|
3151
|
+
toPublicEnv(options) {
|
|
3152
|
+
return toPublicEnv(graph, bootstrappedManifest, options);
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
setSingletonRuntime(runtime);
|
|
3156
|
+
setBootstrappedSecretHydrationRequired(graphRequiresSecretHydration(graph));
|
|
3157
|
+
}
|
|
3158
|
+
function bootstrapFromProcessEnv() {
|
|
3159
|
+
if (typeof process === "undefined") {
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
try {
|
|
3163
|
+
const graph = readRuntimeGraphFromEnv(process.env);
|
|
3164
|
+
if (graph) {
|
|
3165
|
+
attachBootstrappedGraph(graph);
|
|
3166
|
+
}
|
|
3167
|
+
} catch {
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
bootstrapFromProcessEnv();
|
|
3171
|
+
var cnos = Object.assign(
|
|
3172
|
+
((key) => readValue(getRuntimeOrThrow().graph, key)),
|
|
3173
|
+
{
|
|
3174
|
+
read(key) {
|
|
3175
|
+
return readValue(getRuntimeOrThrow().graph, key);
|
|
3176
|
+
},
|
|
3177
|
+
require(key) {
|
|
3178
|
+
return requireValue(getRuntimeOrThrow().graph, key);
|
|
3179
|
+
},
|
|
3180
|
+
readOr(key, fallback) {
|
|
3181
|
+
return readOrValue(getRuntimeOrThrow().graph, key, fallback);
|
|
3182
|
+
},
|
|
3183
|
+
value(path12) {
|
|
3184
|
+
return readValue(getRuntimeOrThrow().graph, toLogicalKey("value", path12));
|
|
3185
|
+
},
|
|
3186
|
+
secret(path12) {
|
|
3187
|
+
return readValue(getRuntimeOrThrow().graph, toLogicalKey("secret", path12));
|
|
3188
|
+
},
|
|
3189
|
+
meta(path12) {
|
|
3190
|
+
return readValue(getRuntimeOrThrow().graph, toLogicalKey("meta", path12));
|
|
3191
|
+
},
|
|
3192
|
+
async ready() {
|
|
3193
|
+
if (getSingletonRuntime() && !getBootstrappedSecretHydrationRequired()) {
|
|
3194
|
+
return;
|
|
3195
|
+
}
|
|
3196
|
+
const existing = getSingletonReady();
|
|
3197
|
+
if (existing && !getBootstrappedSecretHydrationRequired()) {
|
|
3198
|
+
await existing;
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
const readyPromise = createCnos2().then((runtime) => {
|
|
3202
|
+
setSingletonRuntime(runtime);
|
|
3203
|
+
return runtime;
|
|
3204
|
+
});
|
|
3205
|
+
setSingletonReady(readyPromise);
|
|
3206
|
+
await readyPromise;
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
);
|
|
3210
|
+
var runtime_default = cnos;
|
|
2148
3211
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2149
3212
|
0 && (module.exports = {
|
|
2150
|
-
|
|
2151
|
-
defaultPlugins,
|
|
2152
|
-
planDump,
|
|
2153
|
-
resolveBrowserData,
|
|
2154
|
-
toEnv,
|
|
2155
|
-
toPublicEnv,
|
|
2156
|
-
writeDump
|
|
3213
|
+
cnos
|
|
2157
3214
|
});
|