@agent-vm/secret-management 0.0.94 → 0.0.95

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/index.d.ts CHANGED
@@ -6,6 +6,17 @@ import { ResolveAllResponse } from "@1password/sdk";
6
6
  type SecretEnvironment = Readonly<Record<string, string | undefined>>;
7
7
  declare function createCompositeSecretResolver(onePasswordResolver: SecretResolver | null, env?: SecretEnvironment): SecretResolver;
8
8
  //#endregion
9
+ //#region src/redacted-exec-file.d.ts
10
+ interface ExecFileOptions {
11
+ readonly env?: Readonly<Record<string, string | undefined>>;
12
+ readonly input?: string | undefined;
13
+ readonly redactErrorOutput?: boolean | undefined;
14
+ }
15
+ interface ExecFileResult {
16
+ readonly stdout: string;
17
+ readonly stderr: string;
18
+ }
19
+ //#endregion
9
20
  //#region src/onepassword-secret-resolver.d.ts
10
21
  interface SecretResolverClient {
11
22
  readonly secrets: {
@@ -13,10 +24,11 @@ interface SecretResolverClient {
13
24
  resolveAll(secretReferences: readonly string[]): Promise<ResolveAllResponse>;
14
25
  };
15
26
  }
27
+ interface OnePasswordServiceAccountHeadlessAuthProbeResult {
28
+ readonly hint: string;
29
+ readonly ok: boolean;
30
+ }
16
31
  type TokenSource = {
17
- readonly type: 'op-cli';
18
- readonly ref: string;
19
- } | {
20
32
  readonly type: 'env';
21
33
  readonly envVar?: string | undefined;
22
34
  } | {
@@ -24,15 +36,6 @@ type TokenSource = {
24
36
  readonly service: string;
25
37
  readonly account: string;
26
38
  };
27
- interface ExecFileOptions {
28
- readonly env?: Readonly<Record<string, string | undefined>>;
29
- readonly input?: string | undefined;
30
- readonly redactErrorOutput?: boolean | undefined;
31
- }
32
- interface ExecFileResult {
33
- readonly stdout: string;
34
- readonly stderr: string;
35
- }
36
39
  declare function resolveServiceAccountToken(source: TokenSource, dependencies?: {
37
40
  readonly execFileAsync?: (command: string, args: readonly string[], options?: ExecFileOptions) => Promise<ExecFileResult>;
38
41
  }): Promise<string>;
@@ -46,6 +49,11 @@ interface CreateSecretResolverDependencies {
46
49
  readonly integrationName?: string;
47
50
  readonly integrationVersion?: string;
48
51
  }
52
+ declare function probeOnePasswordServiceAccountHeadlessAuth(options: {
53
+ readonly serviceAccountToken: string;
54
+ }, dependencies?: {
55
+ readonly execFileAsync?: (command: string, args: readonly string[], options?: ExecFileOptions) => Promise<ExecFileResult>;
56
+ }): Promise<OnePasswordServiceAccountHeadlessAuthProbeResult>;
49
57
  declare function createSecretResolver(options: {
50
58
  readonly serviceAccountToken: string;
51
59
  }, dependencies?: CreateSecretResolverDependencies): Promise<SecretResolver>;
@@ -63,4 +71,7 @@ type OnePasswordE2eTestEnvironment = Partial<Record<'AGENT_VM_TEST_OP_REFS' | 'A
63
71
  declare const defaultOnePasswordE2eVaultPrefix = "op://agent-vm-testing/";
64
72
  declare function readOnePasswordE2eTestConfig(env?: OnePasswordE2eTestEnvironment): OnePasswordE2eTestConfig;
65
73
  //#endregion
66
- export { CreateSecretResolverDependencies, ExecFileOptions, ExecFileResult, MediatedSecretSpec, OnePasswordE2eTestConfig, OnePasswordE2eTestEnvironment, SecretRef, SecretResolver, SecretResolverClient, type TokenSource, createCompositeSecretResolver, createOpCliSecretResolver, createSecretResolver, createStaticSecretResolver, defaultOnePasswordE2eVaultPrefix, readOnePasswordE2eTestConfig, resolveServiceAccountToken };
74
+ //#region src/secret-redaction.d.ts
75
+ declare function redactOnePasswordReferences(text: string): string;
76
+ //#endregion
77
+ export { CreateSecretResolverDependencies, type ExecFileOptions, type ExecFileResult, MediatedSecretSpec, OnePasswordE2eTestConfig, OnePasswordE2eTestEnvironment, OnePasswordServiceAccountHeadlessAuthProbeResult, SecretRef, SecretResolver, SecretResolverClient, type TokenSource, createCompositeSecretResolver, createOpCliSecretResolver, createSecretResolver, createStaticSecretResolver, defaultOnePasswordE2eVaultPrefix, probeOnePasswordServiceAccountHeadlessAuth, readOnePasswordE2eTestConfig, redactOnePasswordReferences, resolveServiceAccountToken };
package/dist/index.js CHANGED
@@ -1,8 +1,21 @@
1
1
  import "./contracts.js";
2
2
  import { createStaticSecretResolver } from "./testing.js";
3
- import { execFile } from "node:child_process";
4
3
  import { randomUUID } from "node:crypto";
5
4
  import { createClient } from "@1password/sdk";
5
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
6
+ import { tmpdir } from "node:os";
7
+ import path from "node:path";
8
+ import { execFile } from "node:child_process";
9
+ //#region src/secret-redaction.ts
10
+ const quotedOnePasswordReferencePattern = /(["'`])(?:(?!\1)[\s\S])*?op:\/\/(?:(?!\1)[\s\S])*?\1/giu;
11
+ const unquotedOnePasswordReferencePattern = /op:\/\/[^\r\n]*/giu;
12
+ function redactOnePasswordReferences(text) {
13
+ return text.replaceAll(quotedOnePasswordReferencePattern, (quotedReference) => {
14
+ const quote = quotedReference.slice(0, 1);
15
+ return `${quote}<1password-ref>${quote}`;
16
+ }).replaceAll(unquotedOnePasswordReferencePattern, "<1password-ref>");
17
+ }
18
+ //#endregion
6
19
  //#region src/composite-secret-resolver.ts
7
20
  function resolveEnvironmentSecret(ref, env) {
8
21
  const value = env[ref.ref];
@@ -14,25 +27,25 @@ function resolveConfigSecret(ref) {
14
27
  if (ref.value.trim().length === 0) throw new Error("Config secret value is empty.");
15
28
  return ref.value;
16
29
  }
17
- function formatUnknownError$1(error) {
18
- return error instanceof Error ? error.message : String(error);
30
+ function formatUnknownError$2(error) {
31
+ return redactOnePasswordReferences(error instanceof Error ? error.message : String(error));
19
32
  }
20
33
  function describeSecretRef(ref) {
21
34
  switch (ref.source) {
22
- case "1password":
35
+ case "1password": return "<1password-ref>";
23
36
  case "environment": return ref.ref;
24
37
  case "config": return "<config>";
25
38
  default: return JSON.stringify(ref);
26
39
  }
27
40
  }
28
41
  function createSecretResolutionError(options) {
29
- return new Error(`Failed to resolve secret '${options.secretName}' from '${describeSecretRef(options.ref)}': ${formatUnknownError$1(options.cause)}`, { cause: options.cause });
42
+ return new Error(`Failed to resolve secret '${options.secretName}' from '${describeSecretRef(options.ref)}': ${formatUnknownError$2(options.cause)}`, { cause: options.cause });
30
43
  }
31
44
  function throwAggregateSecretResolutionError(failures) {
32
45
  if (failures.length > 0) throw new AggregateError(failures, `Failed to resolve ${String(failures.length)} secret(s).`);
33
46
  }
34
47
  function extractAggregateErrors(error) {
35
- return (Array.isArray(error.errors) ? error.errors : [error]).map((failure) => failure instanceof Error ? failure : new Error(formatUnknownError$1(failure), { cause: failure }));
48
+ return (Array.isArray(error.errors) ? error.errors : [error]).map((failure) => new Error(formatUnknownError$2(failure)));
36
49
  }
37
50
  function createCompositeSecretResolver(onePasswordResolver, env = process.env) {
38
51
  return {
@@ -102,16 +115,74 @@ function createCompositeSecretResolver(onePasswordResolver, env = process.env) {
102
115
  };
103
116
  }
104
117
  //#endregion
105
- //#region src/onepassword-secret-resolver.ts
106
- function formatUnknownError(error) {
118
+ //#region src/op-cli-service-account-env.ts
119
+ const opCliProcessPlumbingEnvNames = [
120
+ "APPDATA",
121
+ "ALL_PROXY",
122
+ "all_proxy",
123
+ "COMSPEC",
124
+ "HOME",
125
+ "HTTP_PROXY",
126
+ "http_proxy",
127
+ "HTTPS_PROXY",
128
+ "https_proxy",
129
+ "LANG",
130
+ "LC_ALL",
131
+ "LC_CTYPE",
132
+ "LOCALAPPDATA",
133
+ "NO_PROXY",
134
+ "no_proxy",
135
+ "PATH",
136
+ "SSL_CERT_DIR",
137
+ "SSL_CERT_FILE",
138
+ "TEMP",
139
+ "TMP",
140
+ "TMPDIR",
141
+ "TZ",
142
+ "USERPROFILE",
143
+ "WINDIR",
144
+ "XDG_RUNTIME_DIR"
145
+ ];
146
+ function createOpCliServiceAccountEnv(serviceAccountToken, opConfigDir) {
147
+ const env = {};
148
+ for (const envName of opCliProcessPlumbingEnvNames) {
149
+ const envValue = process.env[envName];
150
+ if (envValue !== void 0) env[envName] = envValue;
151
+ }
152
+ env.OP_BIOMETRIC_UNLOCK_ENABLED = "false";
153
+ env.OP_CACHE = "false";
154
+ env.OP_CONFIG_DIR = opConfigDir;
155
+ env.OP_SERVICE_ACCOUNT_TOKEN = serviceAccountToken;
156
+ return env;
157
+ }
158
+ async function withOpCliServiceAccountEnv(serviceAccountToken, callback) {
159
+ const opConfigDirParent = tmpdir();
160
+ await mkdir(opConfigDirParent, { recursive: true });
161
+ const opConfigDir = await mkdtemp(path.join(opConfigDirParent, "agent-vm-op-config-"));
162
+ try {
163
+ return await callback(createOpCliServiceAccountEnv(serviceAccountToken, opConfigDir));
164
+ } finally {
165
+ await rm(opConfigDir, {
166
+ force: true,
167
+ recursive: true
168
+ });
169
+ }
170
+ }
171
+ //#endregion
172
+ //#region src/redacted-exec-file.ts
173
+ function formatUnknownError$1(error) {
107
174
  if (error instanceof AggregateError) {
108
- const childMessages = readAggregateErrorChildren(error).map(formatUnknownError);
175
+ const childMessages = readAggregateErrorChildren$1(error).map(formatUnknownError$1);
109
176
  if (childMessages.length === 0) return error.message;
110
177
  const separator = error.message.endsWith(".") ? "" : ".";
111
178
  return `${error.message}${separator} Details: ${childMessages.join("; ")}`;
112
179
  }
113
180
  return error instanceof Error ? error.message : String(error);
114
181
  }
182
+ function readAggregateErrorChildren$1(error) {
183
+ const errorChildren = error.errors;
184
+ return Array.isArray(errorChildren) ? errorChildren : [];
185
+ }
115
186
  var RedactedExecFileError = class extends Error {
116
187
  safeDetail;
117
188
  constructor(message, safeDetail, options) {
@@ -120,12 +191,6 @@ var RedactedExecFileError = class extends Error {
120
191
  this.name = "RedactedExecFileError";
121
192
  }
122
193
  };
123
- var OpInjectOutputError = class extends Error {
124
- constructor(message) {
125
- super(message);
126
- this.name = "OpInjectOutputError";
127
- }
128
- };
129
194
  function formatErrorMetadataValue(value) {
130
195
  if (typeof value === "number" || typeof value === "string") return String(value);
131
196
  }
@@ -137,14 +202,48 @@ function readErrorSignal(error) {
137
202
  if (!("signal" in error)) return;
138
203
  return formatErrorMetadataValue(error.signal);
139
204
  }
140
- function formatRedactedExecErrorDetail(error) {
205
+ function readErrorKilled(error) {
206
+ if (!("killed" in error) || typeof error.killed !== "boolean") return;
207
+ return error.killed;
208
+ }
209
+ function hasEnvironmentPrefix(env, prefix) {
210
+ return Object.keys(env).some((envName) => envName.startsWith(prefix));
211
+ }
212
+ function formatOpCliAuthContext(env) {
213
+ if (env === void 0) return ["opEnvIsolation=disabled", "opAuth=ambient-process"];
214
+ return [
215
+ "opEnvIsolation=enabled",
216
+ `opAuth=${env.OP_SERVICE_ACCOUNT_TOKEN === void 0 ? "missing" : "service-account-token"}`,
217
+ `opConfig=${env.OP_CONFIG_DIR === void 0 ? "default" : "isolated"}`,
218
+ `opBiometricUnlock=${env.OP_BIOMETRIC_UNLOCK_ENABLED ?? "unset"}`,
219
+ `opCache=${env.OP_CACHE ?? "unset"}`,
220
+ `opConnectEnv=${env.OP_CONNECT_HOST !== void 0 || env.OP_CONNECT_TOKEN !== void 0 ? "present" : "absent"}`,
221
+ `opSessionEnv=${hasEnvironmentPrefix(env, "OP_SESSION") ? "present" : "absent"}`,
222
+ `opAccountEnv=${env.OP_ACCOUNT === void 0 ? "absent" : "present"}`
223
+ ];
224
+ }
225
+ function formatRedactedExecErrorDetail(options) {
226
+ const error = options.error;
141
227
  const exitCode = readErrorCode(error) ?? "unknown";
142
228
  const signal = readErrorSignal(error);
143
- return signal === void 0 ? `exit code ${exitCode}` : `exit code ${exitCode}, signal ${signal}`;
229
+ const details = [
230
+ signal === void 0 ? `exit code ${exitCode}` : `exit code ${exitCode}, signal ${signal}`,
231
+ `elapsedMs=${String(options.elapsedMs)}`,
232
+ "output=redacted"
233
+ ];
234
+ const killed = readErrorKilled(error);
235
+ if (killed !== void 0) details.push(`killed=${String(killed)}`);
236
+ if (options.command === "op") details.push(...formatOpCliAuthContext(options.env));
237
+ return details.join("; ");
144
238
  }
145
239
  function createExecFileError(options) {
146
240
  if (options.redactErrorOutput) {
147
- const safeDetail = formatRedactedExecErrorDetail(options.error);
241
+ const safeDetail = formatRedactedExecErrorDetail({
242
+ command: options.command,
243
+ elapsedMs: options.elapsedMs,
244
+ ...options.env ? { env: options.env } : {},
245
+ error: options.error
246
+ });
148
247
  return new RedactedExecFileError(`${options.command} failed: ${safeDetail}`, safeDetail);
149
248
  }
150
249
  const errorDetail = options.stderr.trim() || options.error.message;
@@ -157,15 +256,13 @@ function formatStdinWriteErrorDetail(error) {
157
256
  function createStdinWriteError(command, error, redactErrorOutput) {
158
257
  if (redactErrorOutput) {
159
258
  const safeDetail = formatStdinWriteErrorDetail(error);
160
- return new RedactedExecFileError(`${command} failed writing stdin: ${safeDetail}`, safeDetail, { cause: error });
259
+ return new RedactedExecFileError(`${command} failed writing stdin: ${safeDetail}`, safeDetail);
161
260
  }
162
- return new Error(`${command} failed writing stdin: ${formatUnknownError(error)}`, { cause: error });
163
- }
164
- function ensureMacOsForKeychain() {
165
- if (process.platform !== "darwin") throw new Error("Keychain token source is only supported on macOS. Use an env or op-cli token source on this platform so cmd-ts can surface a clear startup error.");
261
+ return new Error(`${command} failed writing stdin: ${formatUnknownError$1(error)}`, { cause: error });
166
262
  }
167
263
  function execFileAsync(command, args, options) {
168
264
  return new Promise((resolve, reject) => {
265
+ const startedAtMs = Date.now();
169
266
  let hasSettled = false;
170
267
  const rejectOnce = (error) => {
171
268
  if (hasSettled) return;
@@ -184,6 +281,8 @@ function execFileAsync(command, args, options) {
184
281
  if (error) {
185
282
  rejectOnce(createExecFileError({
186
283
  command,
284
+ elapsedMs: Date.now() - startedAtMs,
285
+ env: options?.env,
187
286
  error,
188
287
  redactErrorOutput: options?.redactErrorOutput,
189
288
  stderr
@@ -209,15 +308,39 @@ function execFileAsync(command, args, options) {
209
308
  }
210
309
  });
211
310
  }
311
+ //#endregion
312
+ //#region src/onepassword-secret-resolver.ts
313
+ function formatUnknownError(error) {
314
+ if (error instanceof AggregateError) {
315
+ const childMessages = readAggregateErrorChildren(error).map(formatUnknownError);
316
+ if (childMessages.length === 0) return error.message;
317
+ const separator = error.message.endsWith(".") ? "" : ".";
318
+ return `${error.message}${separator} Details: ${childMessages.join("; ")}`;
319
+ }
320
+ return error instanceof Error ? error.message : String(error);
321
+ }
322
+ function redactKnownSecretValues(message, secretValues) {
323
+ return secretValues.reduce((redactedMessage, secretValue) => {
324
+ if (secretValue.length === 0) return redactedMessage;
325
+ return redactedMessage.replaceAll(secretValue, "<redacted>");
326
+ }, message);
327
+ }
328
+ function formatUnknownErrorWithRedactions(error, secretValues) {
329
+ return redactOnePasswordReferences(redactKnownSecretValues(formatUnknownError(error), secretValues));
330
+ }
331
+ var OpInjectOutputError = class extends Error {
332
+ constructor(message) {
333
+ super(message);
334
+ this.name = "OpInjectOutputError";
335
+ }
336
+ };
337
+ function ensureMacOsForKeychain() {
338
+ if (process.platform !== "darwin") throw new Error("Keychain token source is only supported on macOS. Use an env token source on this platform so cmd-ts can surface a clear startup error.");
339
+ }
212
340
  const SAFE_IDENTIFIER_PATTERN = /^[\w.@-]+$/u;
213
341
  async function resolveServiceAccountToken(source, dependencies) {
214
342
  const exec = dependencies?.execFileAsync ?? execFileAsync;
215
343
  switch (source.type) {
216
- case "op-cli": {
217
- const token = (await exec("op", ["read", source.ref], { redactErrorOutput: true })).stdout.trim();
218
- if (token.length === 0) throw new Error("op-cli token resolution returned empty value");
219
- return token;
220
- }
221
344
  case "env": {
222
345
  const envVar = source.envVar ?? "OP_SERVICE_ACCOUNT_TOKEN";
223
346
  const token = process.env[envVar]?.trim();
@@ -277,54 +400,43 @@ function mergeResolvedSecrets(resolvedSecrets, onePasswordSecrets) {
277
400
  };
278
401
  }
279
402
  async function resolveSecretWithOpCli(serviceAccountToken, secretReference, exec) {
280
- return stripOpReadStdoutTerminator((await exec("op", ["read", secretReference], {
281
- env: createOpCliServiceAccountEnv(serviceAccountToken),
282
- redactErrorOutput: true
283
- })).stdout);
403
+ return await withOpCliServiceAccountEnv(serviceAccountToken, async (env) => {
404
+ return stripOpReadStdoutTerminator((await exec("op", ["read", secretReference], {
405
+ env,
406
+ redactErrorOutput: true
407
+ })).stdout);
408
+ });
284
409
  }
285
410
  function stripOpReadStdoutTerminator(stdout) {
286
411
  if (stdout.endsWith("\r\n")) return stdout.slice(0, -2);
287
412
  if (stdout.endsWith("\n")) return stdout.slice(0, -1);
288
413
  return stdout;
289
414
  }
290
- const opCliProcessPlumbingEnvNames = [
291
- "APPDATA",
292
- "ALL_PROXY",
293
- "all_proxy",
294
- "COMSPEC",
295
- "HOME",
296
- "HTTP_PROXY",
297
- "http_proxy",
298
- "HTTPS_PROXY",
299
- "https_proxy",
300
- "LANG",
301
- "LC_ALL",
302
- "LC_CTYPE",
303
- "LOCALAPPDATA",
304
- "NO_PROXY",
305
- "no_proxy",
306
- "PATH",
307
- "SSL_CERT_DIR",
308
- "SSL_CERT_FILE",
309
- "TEMP",
310
- "TMP",
311
- "TMPDIR",
312
- "TZ",
313
- "USERPROFILE",
314
- "WINDIR",
315
- "XDG_CACHE_HOME",
316
- "XDG_CONFIG_HOME",
317
- "XDG_DATA_HOME",
318
- "XDG_RUNTIME_DIR"
319
- ];
320
- function createOpCliServiceAccountEnv(serviceAccountToken) {
321
- const env = {};
322
- for (const envName of opCliProcessPlumbingEnvNames) {
323
- const envValue = process.env[envName];
324
- if (envValue !== void 0) env[envName] = envValue;
415
+ function opWhoamiReportsServiceAccount(stdout) {
416
+ return /^User Type:\s*SERVICE_ACCOUNT\s*$/imu.test(stdout);
417
+ }
418
+ async function probeOnePasswordServiceAccountHeadlessAuth(options, dependencies = {}) {
419
+ const exec = dependencies.execFileAsync ?? execFileAsync;
420
+ try {
421
+ return await withOpCliServiceAccountEnv(options.serviceAccountToken, async (env) => {
422
+ if (opWhoamiReportsServiceAccount((await exec("op", ["whoami"], {
423
+ env,
424
+ redactErrorOutput: true
425
+ })).stdout)) return {
426
+ hint: "op whoami returned SERVICE_ACCOUNT with isolated service-account env",
427
+ ok: true
428
+ };
429
+ return {
430
+ hint: "op whoami did not report SERVICE_ACCOUNT with isolated service-account env",
431
+ ok: false
432
+ };
433
+ });
434
+ } catch (error) {
435
+ return {
436
+ hint: `op whoami failed with isolated service-account env: ${formatUnknownErrorWithRedactions(error, [options.serviceAccountToken])}`,
437
+ ok: false
438
+ };
325
439
  }
326
- env.OP_SERVICE_ACCOUNT_TOKEN = serviceAccountToken;
327
- return env;
328
440
  }
329
441
  const opInjectTemplateDelimiterPattern = /(?:\{\{|\}\})/u;
330
442
  function assertOpInjectTemplateSafeReference(entry) {
@@ -343,18 +455,21 @@ function createAggregateErrorWithCause(options) {
343
455
  aggregateError.cause = options.cause;
344
456
  return aggregateError;
345
457
  }
346
- function createFallbackStageError(stage, error) {
347
- return new Error(`${stage} failed before op CLI fallback: ${formatUnknownError(error)}`, { cause: error });
458
+ function createFallbackStageError(stage, error, secretValues = []) {
459
+ const message = `${stage} failed before op CLI fallback: ${formatUnknownErrorWithRedactions(error, secretValues)}`;
460
+ return secretValues.length === 0 ? new Error(message, { cause: error }) : new Error(message);
348
461
  }
349
462
  function createFallbackFailureError(options) {
463
+ const fallbackMessage = formatUnknownErrorWithRedactions(options.fallbackError, options.secretValues ?? []);
464
+ const redactedFallbackError = options.secretValues === void 0 || options.secretValues.length === 0 ? new Error(fallbackMessage, { cause: options.fallbackError }) : new Error(fallbackMessage);
350
465
  if (options.fallbackError instanceof AggregateError) return createAggregateErrorWithCause({
351
- cause: options.fallbackError,
352
- errors: [options.stageError, ...readAggregateErrorChildren(options.fallbackError)],
353
- message: options.fallbackError.message
466
+ cause: redactedFallbackError,
467
+ errors: [options.stageError, redactedFallbackError],
468
+ message: fallbackMessage
354
469
  });
355
470
  return createAggregateErrorWithCause({
356
- cause: options.fallbackError,
357
- errors: [options.stageError, options.fallbackError],
471
+ cause: redactedFallbackError,
472
+ errors: [options.stageError, redactedFallbackError],
358
473
  message: options.message
359
474
  });
360
475
  }
@@ -383,8 +498,8 @@ function buildOpInjectTemplate(entries) {
383
498
  }
384
499
  function findUniqueOpInjectMarker(options) {
385
500
  const markerIndex = options.output.indexOf(options.marker);
386
- if (markerIndex === -1) throw new OpInjectOutputError(`op inject output omitted ${options.markerDescription} marker for secret '${options.secretName}' (${options.secretReference}).`);
387
- if (options.output.indexOf(options.marker, markerIndex + options.marker.length) !== -1) throw new OpInjectOutputError(`op inject output for secret '${options.secretName}' (${options.secretReference}) contained repeated ${options.markerDescription} marker.`);
501
+ if (markerIndex === -1) throw new OpInjectOutputError(`op inject output omitted ${options.markerDescription} marker for secret '${options.secretName}'.`);
502
+ if (options.output.indexOf(options.marker, markerIndex + options.marker.length) !== -1) throw new OpInjectOutputError(`op inject output for secret '${options.secretName}' contained repeated ${options.markerDescription} marker.`);
388
503
  return markerIndex;
389
504
  }
390
505
  function extractInjectedSecret(options) {
@@ -394,15 +509,13 @@ function extractInjectedSecret(options) {
394
509
  marker: startToken,
395
510
  markerDescription: "start",
396
511
  output: options.output,
397
- secretName: options.entry.secretName,
398
- secretReference: options.entry.secretRef.ref
512
+ secretName: options.entry.secretName
399
513
  }) + startToken.length;
400
514
  const secretEndIndex = findUniqueOpInjectMarker({
401
515
  marker: endToken,
402
516
  markerDescription: "end",
403
517
  output: options.output,
404
- secretName: options.entry.secretName,
405
- secretReference: options.entry.secretRef.ref
518
+ secretName: options.entry.secretName
406
519
  });
407
520
  return options.output.slice(secretStartIndex, secretEndIndex);
408
521
  }
@@ -415,25 +528,27 @@ function mapOpInjectOutput(entries, output) {
415
528
  async function resolveAllSecretsWithOpInject(serviceAccountToken, refs, exec) {
416
529
  const entries = createOpInjectEntries(refs);
417
530
  if (entries.length === 0) return {};
418
- return mapOpInjectOutput(entries, (await exec("op", [
419
- "inject",
420
- "--in-file",
421
- "/dev/stdin"
422
- ], {
423
- env: createOpCliServiceAccountEnv(serviceAccountToken),
424
- input: buildOpInjectTemplate(entries),
425
- redactErrorOutput: true
426
- })).stdout);
531
+ return await withOpCliServiceAccountEnv(serviceAccountToken, async (env) => {
532
+ return mapOpInjectOutput(entries, (await exec("op", [
533
+ "inject",
534
+ "--in-file",
535
+ "/dev/stdin"
536
+ ], {
537
+ env,
538
+ input: buildOpInjectTemplate(entries),
539
+ redactErrorOutput: true
540
+ })).stdout);
541
+ });
427
542
  }
428
543
  function formatResolveReferenceError(error) {
429
544
  return "message" in error && typeof error.message === "string" ? `${error.type}: ${error.message}` : error.type;
430
545
  }
431
546
  function readSdkBatchSecret(options) {
432
547
  const individualResponse = options.response.individualResponses[options.secretReference];
433
- if (!individualResponse) throw new Error(`1Password SDK resolveAll response omitted '${options.secretName}' (${options.secretReference}).`);
548
+ if (!individualResponse) throw new Error(`1Password SDK resolveAll response omitted '${options.secretName}'.`);
434
549
  if (individualResponse.content !== void 0) return individualResponse.content.secret;
435
- if (individualResponse.error !== void 0) throw new Error(`1Password SDK resolveAll failed for '${options.secretName}' (${options.secretReference}): ${formatResolveReferenceError(individualResponse.error)}`);
436
- throw new Error(`1Password SDK resolveAll returned neither content nor error for '${options.secretName}' (${options.secretReference}).`);
550
+ if (individualResponse.error !== void 0) throw new Error(`1Password SDK resolveAll failed for '${options.secretName}': ${formatResolveReferenceError(individualResponse.error)}`);
551
+ throw new Error(`1Password SDK resolveAll returned neither content nor error for '${options.secretName}'.`);
437
552
  }
438
553
  function mapSdkResolveAllResponse(refs, response) {
439
554
  return Object.fromEntries(Object.entries(refs).map(([secretName, secretRef]) => [secretName, readSdkBatchSecret({
@@ -461,13 +576,14 @@ async function createSecretResolver(options, dependencies = {}) {
461
576
  try {
462
577
  return await client.secrets.resolve(ref.ref);
463
578
  } catch (error) {
464
- const sdkResolveError = createFallbackStageError("1Password SDK resolve", error);
579
+ const sdkResolveError = createFallbackStageError("1Password SDK resolve", error, [options.serviceAccountToken]);
465
580
  try {
466
581
  return await resolveSecretWithOpCli(options.serviceAccountToken, ref.ref, exec);
467
582
  } catch (fallbackError) {
468
583
  throw createFallbackFailureError({
469
584
  fallbackError,
470
585
  message: "1Password SDK resolve and op CLI fallback both failed.",
586
+ secretValues: [options.serviceAccountToken],
471
587
  stageError: sdkResolveError
472
588
  });
473
589
  }
@@ -480,13 +596,14 @@ async function createSecretResolver(options, dependencies = {}) {
480
596
  const response = await client.secrets.resolveAll(Object.values(splitRefs.onePasswordRefs).map((secretRef) => secretRef.ref));
481
597
  return mergeResolvedSecrets(splitRefs.resolvedSecrets, mapSdkResolveAllResponse(splitRefs.onePasswordRefs, response));
482
598
  } catch (error) {
483
- const sdkResolveAllError = createFallbackStageError("1Password SDK resolveAll", error);
599
+ const sdkResolveAllError = createFallbackStageError("1Password SDK resolveAll", error, [options.serviceAccountToken]);
484
600
  try {
485
601
  return mergeResolvedSecrets(splitRefs.resolvedSecrets, await resolveAllSecretsWithOpCli(options.serviceAccountToken, splitRefs.onePasswordRefs, exec));
486
602
  } catch (fallbackError) {
487
603
  throw createFallbackFailureError({
488
604
  fallbackError,
489
605
  message: "1Password SDK resolveAll and op CLI fallback both failed.",
606
+ secretValues: [options.serviceAccountToken],
490
607
  stageError: sdkResolveAllError
491
608
  });
492
609
  }
@@ -494,7 +611,7 @@ async function createSecretResolver(options, dependencies = {}) {
494
611
  }
495
612
  };
496
613
  } catch (error) {
497
- const sdkClientCreationError = createFallbackStageError("1Password SDK client creation", error);
614
+ const sdkClientCreationError = createFallbackStageError("1Password SDK client creation", error, [options.serviceAccountToken]);
498
615
  return {
499
616
  resolve: async (ref) => {
500
617
  switch (ref.source) {
@@ -509,6 +626,7 @@ async function createSecretResolver(options, dependencies = {}) {
509
626
  throw createFallbackFailureError({
510
627
  fallbackError,
511
628
  message: "1Password SDK client creation and op CLI fallback both failed.",
629
+ secretValues: [options.serviceAccountToken],
512
630
  stageError: sdkClientCreationError
513
631
  });
514
632
  }
@@ -522,6 +640,7 @@ async function createSecretResolver(options, dependencies = {}) {
522
640
  throw createFallbackFailureError({
523
641
  fallbackError,
524
642
  message: "1Password SDK client creation and op CLI fallback both failed.",
643
+ secretValues: [options.serviceAccountToken],
525
644
  stageError: sdkClientCreationError
526
645
  });
527
646
  }
@@ -571,4 +690,4 @@ function readOnePasswordE2eTestConfig(env = process.env) {
571
690
  };
572
691
  }
573
692
  //#endregion
574
- export { createCompositeSecretResolver, createOpCliSecretResolver, createSecretResolver, createStaticSecretResolver, defaultOnePasswordE2eVaultPrefix, readOnePasswordE2eTestConfig, resolveServiceAccountToken };
693
+ export { createCompositeSecretResolver, createOpCliSecretResolver, createSecretResolver, createStaticSecretResolver, defaultOnePasswordE2eVaultPrefix, probeOnePasswordServiceAccountHeadlessAuth, readOnePasswordE2eTestConfig, redactOnePasswordReferences, resolveServiceAccountToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-vm/secret-management",
3
- "version": "0.0.94",
3
+ "version": "0.0.95",
4
4
  "description": "Shared secret reference contracts and resolvers for agent-vm packages.",
5
5
  "homepage": "https://github.com/ShravanSunder/agent-vm#readme",
6
6
  "bugs": {