@agent-vm/secret-management 0.0.71 → 0.0.72
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/dist/contracts.d.ts +4 -0
- package/dist/index.js +92 -6
- package/dist/testing.js +1 -0
- package/package.json +1 -1
package/dist/contracts.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ type SecretRef = {
|
|
|
9
9
|
} | {
|
|
10
10
|
readonly source: 'environment';
|
|
11
11
|
readonly ref: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly source: 'config';
|
|
14
|
+
readonly ref?: never;
|
|
15
|
+
readonly value: string;
|
|
12
16
|
};
|
|
13
17
|
interface SecretResolver {
|
|
14
18
|
resolve(ref: SecretRef): Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,23 @@ function resolveEnvironmentSecret(ref, env) {
|
|
|
10
10
|
if (value.trim().length === 0) throw new Error(`Environment variable '${ref.ref}' is set but empty.`);
|
|
11
11
|
return value;
|
|
12
12
|
}
|
|
13
|
+
function resolveConfigSecret(ref) {
|
|
14
|
+
if (ref.value.trim().length === 0) throw new Error("Config secret value is empty.");
|
|
15
|
+
return ref.value;
|
|
16
|
+
}
|
|
13
17
|
function formatUnknownError$1(error) {
|
|
14
18
|
return error instanceof Error ? error.message : String(error);
|
|
15
19
|
}
|
|
20
|
+
function describeSecretRef(ref) {
|
|
21
|
+
switch (ref.source) {
|
|
22
|
+
case "1password":
|
|
23
|
+
case "environment": return ref.ref;
|
|
24
|
+
case "config": return "<config>";
|
|
25
|
+
default: return JSON.stringify(ref);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
function createSecretResolutionError(options) {
|
|
17
|
-
return new Error(`Failed to resolve secret '${options.secretName}' from '${options.ref
|
|
29
|
+
return new Error(`Failed to resolve secret '${options.secretName}' from '${describeSecretRef(options.ref)}': ${formatUnknownError$1(options.cause)}`, { cause: options.cause });
|
|
18
30
|
}
|
|
19
31
|
function throwAggregateSecretResolutionError(failures) {
|
|
20
32
|
if (failures.length > 0) throw new AggregateError(failures, `Failed to resolve ${String(failures.length)} secret(s).`);
|
|
@@ -27,6 +39,7 @@ function createCompositeSecretResolver(onePasswordResolver, env = process.env) {
|
|
|
27
39
|
async resolve(ref) {
|
|
28
40
|
switch (ref.source) {
|
|
29
41
|
case "environment": return resolveEnvironmentSecret(ref, env);
|
|
42
|
+
case "config": return resolveConfigSecret(ref);
|
|
30
43
|
case "1password":
|
|
31
44
|
if (!onePasswordResolver) throw new Error("Secret with source '1password' requires host.secretsProvider to be configured.");
|
|
32
45
|
return await onePasswordResolver.resolve(ref);
|
|
@@ -49,6 +62,17 @@ function createCompositeSecretResolver(onePasswordResolver, env = process.env) {
|
|
|
49
62
|
}));
|
|
50
63
|
}
|
|
51
64
|
break;
|
|
65
|
+
case "config":
|
|
66
|
+
try {
|
|
67
|
+
resolved[name] = resolveConfigSecret(ref);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
failures.push(createSecretResolutionError({
|
|
70
|
+
cause: error,
|
|
71
|
+
ref,
|
|
72
|
+
secretName: name
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
52
76
|
case "1password":
|
|
53
77
|
onePasswordRefs[name] = ref;
|
|
54
78
|
break;
|
|
@@ -218,6 +242,40 @@ async function resolveServiceAccountToken(source, dependencies) {
|
|
|
218
242
|
default: throw new Error(`Unsupported token source: ${JSON.stringify(source)}`);
|
|
219
243
|
}
|
|
220
244
|
}
|
|
245
|
+
function resolveConfigSecretValue(ref) {
|
|
246
|
+
if (ref.value.trim().length === 0) throw new Error("Config secret value is empty.");
|
|
247
|
+
return ref.value;
|
|
248
|
+
}
|
|
249
|
+
function createCompositeOnlySecretSourceError(source) {
|
|
250
|
+
return /* @__PURE__ */ new Error(`Secret source '${source}' must be resolved by the composite resolver before reaching the 1Password resolver.`);
|
|
251
|
+
}
|
|
252
|
+
function splitSecretRefs(refs) {
|
|
253
|
+
const onePasswordRefs = {};
|
|
254
|
+
const resolvedSecrets = {};
|
|
255
|
+
for (const [secretName, secretRef] of Object.entries(refs)) switch (secretRef.source) {
|
|
256
|
+
case "1password":
|
|
257
|
+
onePasswordRefs[secretName] = secretRef;
|
|
258
|
+
break;
|
|
259
|
+
case "config":
|
|
260
|
+
resolvedSecrets[secretName] = resolveConfigSecretValue(secretRef);
|
|
261
|
+
break;
|
|
262
|
+
case "environment": throw createCompositeOnlySecretSourceError(secretRef.source);
|
|
263
|
+
default: throw new Error(`Unsupported secret source: ${JSON.stringify(secretRef)}`);
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
onePasswordRefs,
|
|
267
|
+
resolvedSecrets
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function hasOnePasswordRefs(refs) {
|
|
271
|
+
return Object.keys(refs).length > 0;
|
|
272
|
+
}
|
|
273
|
+
function mergeResolvedSecrets(resolvedSecrets, onePasswordSecrets) {
|
|
274
|
+
return {
|
|
275
|
+
...resolvedSecrets,
|
|
276
|
+
...onePasswordSecrets
|
|
277
|
+
};
|
|
278
|
+
}
|
|
221
279
|
async function resolveSecretWithOpCli(serviceAccountToken, secretReference, exec) {
|
|
222
280
|
return stripOpReadStdoutTerminator((await exec("op", ["read", secretReference], {
|
|
223
281
|
env: createOpCliServiceAccountEnv(serviceAccountToken),
|
|
@@ -429,6 +487,12 @@ async function createSecretResolver(options, dependencies = {}) {
|
|
|
429
487
|
});
|
|
430
488
|
return {
|
|
431
489
|
resolve: async (ref) => {
|
|
490
|
+
switch (ref.source) {
|
|
491
|
+
case "config": return resolveConfigSecretValue(ref);
|
|
492
|
+
case "environment": throw createCompositeOnlySecretSourceError(ref.source);
|
|
493
|
+
case "1password": break;
|
|
494
|
+
default: throw new Error(`Unsupported secret source: ${JSON.stringify(ref)}`);
|
|
495
|
+
}
|
|
432
496
|
try {
|
|
433
497
|
return await client.secrets.resolve(ref.ref);
|
|
434
498
|
} catch (error) {
|
|
@@ -445,12 +509,15 @@ async function createSecretResolver(options, dependencies = {}) {
|
|
|
445
509
|
}
|
|
446
510
|
},
|
|
447
511
|
resolveAll: async (refs) => {
|
|
512
|
+
const splitRefs = splitSecretRefs(refs);
|
|
513
|
+
if (!hasOnePasswordRefs(splitRefs.onePasswordRefs)) return splitRefs.resolvedSecrets;
|
|
448
514
|
try {
|
|
449
|
-
|
|
515
|
+
const response = await client.secrets.resolveAll(Object.values(splitRefs.onePasswordRefs).map((secretRef) => secretRef.ref));
|
|
516
|
+
return mergeResolvedSecrets(splitRefs.resolvedSecrets, mapSdkResolveAllResponse(splitRefs.onePasswordRefs, response));
|
|
450
517
|
} catch (error) {
|
|
451
518
|
const sdkResolveAllError = createFallbackStageError("1Password SDK resolveAll", error);
|
|
452
519
|
try {
|
|
453
|
-
return await resolveAllSecretsWithOpCli(options.serviceAccountToken,
|
|
520
|
+
return mergeResolvedSecrets(splitRefs.resolvedSecrets, await resolveAllSecretsWithOpCli(options.serviceAccountToken, splitRefs.onePasswordRefs, exec));
|
|
454
521
|
} catch (fallbackError) {
|
|
455
522
|
throw createFallbackFailureError({
|
|
456
523
|
fallbackError,
|
|
@@ -465,6 +532,12 @@ async function createSecretResolver(options, dependencies = {}) {
|
|
|
465
532
|
const sdkClientCreationError = createFallbackStageError("1Password SDK client creation", error);
|
|
466
533
|
return {
|
|
467
534
|
resolve: async (ref) => {
|
|
535
|
+
switch (ref.source) {
|
|
536
|
+
case "config": return resolveConfigSecretValue(ref);
|
|
537
|
+
case "environment": throw createCompositeOnlySecretSourceError(ref.source);
|
|
538
|
+
case "1password": break;
|
|
539
|
+
default: throw new Error(`Unsupported secret source: ${JSON.stringify(ref)}`, { cause: error });
|
|
540
|
+
}
|
|
468
541
|
try {
|
|
469
542
|
return await resolveSecretWithOpCli(options.serviceAccountToken, ref.ref, exec);
|
|
470
543
|
} catch (fallbackError) {
|
|
@@ -476,8 +549,10 @@ async function createSecretResolver(options, dependencies = {}) {
|
|
|
476
549
|
}
|
|
477
550
|
},
|
|
478
551
|
resolveAll: async (refs) => {
|
|
552
|
+
const splitRefs = splitSecretRefs(refs);
|
|
553
|
+
if (!hasOnePasswordRefs(splitRefs.onePasswordRefs)) return splitRefs.resolvedSecrets;
|
|
479
554
|
try {
|
|
480
|
-
return await resolveAllSecretsWithOpCli(options.serviceAccountToken,
|
|
555
|
+
return mergeResolvedSecrets(splitRefs.resolvedSecrets, await resolveAllSecretsWithOpCli(options.serviceAccountToken, splitRefs.onePasswordRefs, exec));
|
|
481
556
|
} catch (fallbackError) {
|
|
482
557
|
throw createFallbackFailureError({
|
|
483
558
|
fallbackError,
|
|
@@ -492,8 +567,19 @@ async function createSecretResolver(options, dependencies = {}) {
|
|
|
492
567
|
async function createOpCliSecretResolver(options, dependencies = {}) {
|
|
493
568
|
const exec = dependencies.execFileAsync ?? execFileAsync;
|
|
494
569
|
return {
|
|
495
|
-
resolve: async (ref) =>
|
|
496
|
-
|
|
570
|
+
resolve: async (ref) => {
|
|
571
|
+
switch (ref.source) {
|
|
572
|
+
case "config": return resolveConfigSecretValue(ref);
|
|
573
|
+
case "environment": throw createCompositeOnlySecretSourceError(ref.source);
|
|
574
|
+
case "1password": return await resolveSecretWithOpCli(options.serviceAccountToken, ref.ref, exec);
|
|
575
|
+
default: throw new Error(`Unsupported secret source: ${JSON.stringify(ref)}`);
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
resolveAll: async (refs) => {
|
|
579
|
+
const splitRefs = splitSecretRefs(refs);
|
|
580
|
+
if (!hasOnePasswordRefs(splitRefs.onePasswordRefs)) return splitRefs.resolvedSecrets;
|
|
581
|
+
return mergeResolvedSecrets(splitRefs.resolvedSecrets, await resolveAllSecretsWithOpCli(options.serviceAccountToken, splitRefs.onePasswordRefs, exec));
|
|
582
|
+
}
|
|
497
583
|
};
|
|
498
584
|
}
|
|
499
585
|
//#endregion
|
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//#region src/testing.ts
|
|
2
2
|
function createStaticSecretResolver(values) {
|
|
3
3
|
const resolve = async (ref) => {
|
|
4
|
+
if (ref.source === "config") return ref.value;
|
|
4
5
|
const value = values[ref.ref];
|
|
5
6
|
if (value === void 0) throw new Error(`No test secret value configured for '${ref.ref}'.`);
|
|
6
7
|
return value;
|
package/package.json
CHANGED