@indigoai-us/hq-cli 5.77.10 → 5.77.12

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.
@@ -471,6 +471,52 @@ export async function listActiveMembers(
471
471
  return data?.members ?? [];
472
472
  }
473
473
 
474
+ /**
475
+ * Resolve a role-change target to an active membership key.
476
+ *
477
+ * Pending invites are email-keyed, but claimed active memberships are keyed by
478
+ * personUid. Consequently, role changes must look up email targets in the
479
+ * active member list instead of synthesizing an `email:...#company` key.
480
+ */
481
+ export async function resolveRoleChangeTarget(
482
+ token: string,
483
+ companyUid: string,
484
+ target: string,
485
+ ): Promise<string> {
486
+ if (target.includes("#")) return target;
487
+
488
+ const detected = detectTarget(target);
489
+ if (detected?.type === "person" || detected?.type === "agent") {
490
+ return `${detected.value}#${companyUid}`;
491
+ }
492
+ if (detected?.type === "email") {
493
+ // Active fleet-agent guest memberships are keyed agt_…#cmp_…, not by
494
+ // personEmail enrichment. Map machine emails the same way revoke does
495
+ // before falling through to the human roster lookup.
496
+ if (detected.isAgent) {
497
+ const local = detected.value.split("@")[0] ?? "";
498
+ const m = local.match(/^agt-(.+)$/i);
499
+ if (m) return `agt_${m[1].toUpperCase()}#${companyUid}`;
500
+ }
501
+
502
+ const members = await listActiveMembers(token, companyUid);
503
+ const member = members.find(
504
+ (candidate) =>
505
+ candidate.personEmail?.trim().toLowerCase() === detected.value,
506
+ );
507
+ if (member) return member.membershipKey;
508
+
509
+ throw new Error(
510
+ `No active member has email '${detected.value}' in this company. ` +
511
+ "Run `hq members list` to find the member, or use their prs_ personUid.",
512
+ );
513
+ }
514
+
515
+ throw new Error(
516
+ `Invalid target '${target}': use an email, prs_ personUid, agt_ agentUid, or full membership key`,
517
+ );
518
+ }
519
+
474
520
  /**
475
521
  * Resolve a `revoke` CLI argument into the canonical `membershipKey` shape
476
522
  * the server requires. Accepts three input forms:
@@ -631,9 +677,10 @@ export function registerMembersCommand(program: Command): void {
631
677
  const token = await ensureCognitoToken();
632
678
  const companySlug = members.opts().company as string | undefined;
633
679
  const companyUid = await getCompanyUid(token, companySlug);
634
- const membershipKey = resolveRevokeTargetToMembershipKey(
635
- target,
680
+ const membershipKey = await resolveRoleChangeTarget(
681
+ token,
636
682
  companyUid,
683
+ target,
637
684
  );
638
685
 
639
686
  await changeMemberRole(token, companyUid, membershipKey, role as Role);
@@ -708,10 +755,13 @@ export function registerMembersCommand(program: Command): void {
708
755
  resend?: boolean;
709
756
  },
710
757
  ) => {
758
+ let token: string | undefined;
759
+ let companyUid: string | undefined;
760
+ let companySlug: string | undefined;
711
761
  try {
712
- const token = await ensureCognitoToken();
713
- const companySlug = members.opts().company as string | undefined;
714
- const companyUid = await getCompanyUid(token, companySlug);
762
+ token = await ensureCognitoToken();
763
+ companySlug = members.opts().company as string | undefined;
764
+ companyUid = await getCompanyUid(token, companySlug);
715
765
  const callerUid = await getCallerPersonUid(token);
716
766
 
717
767
  // --resend short-circuits to the server's re-fire path. No new row.
@@ -887,15 +937,79 @@ export function registerMembersCommand(program: Command): void {
887
937
  );
888
938
  }
889
939
  } catch (err) {
890
- // The target is ALREADY an active member (409 MEMBERSHIP_ALREADY_EXISTS).
891
- // Terminal, not a failure: the person is already in the company, so
892
- // there's nothing to invite or retry. Print a plain note and exit 0 —
893
- // re-attempting just re-fires the server's (correctly captured) 409,
894
- // and a bulk-invite script shouldn't abort on an already-member. HQ-11.
940
+ // A 409 may refer to either an active membership or an existing
941
+ // pending invite. Probe both views so email targets get actionable
942
+ // guidance while preserving the terminal exit-0 behavior for bulk
943
+ // invite scripts.
895
944
  if (
896
945
  err instanceof InviteHttpError &&
897
946
  err.code === "MEMBERSHIP_ALREADY_EXISTS"
898
947
  ) {
948
+ const detected = detectTarget(target);
949
+ const normalizedEmail =
950
+ detected?.type === "email" ? detected.value : undefined;
951
+
952
+ if (normalizedEmail && token && companyUid) {
953
+ // Preserve --company in copy-paste hints so multi-company
954
+ // callers don't hit "Multiple active companies" on --resend.
955
+ const companyFlag = companySlug
956
+ ? ` --company ${companySlug}`
957
+ : "";
958
+ const resendHint =
959
+ `hq members invite ${normalizedEmail}${companyFlag} --resend`;
960
+
961
+ try {
962
+ const pending = await listPendingInvites(token, companyUid);
963
+ if (
964
+ pending.some(
965
+ (invite) =>
966
+ invite.inviteeEmail?.trim().toLowerCase() ===
967
+ normalizedEmail,
968
+ )
969
+ ) {
970
+ console.log(
971
+ chalk.yellow(
972
+ `${normalizedEmail} already has a pending invite. ` +
973
+ `Use \`${resendHint}\` to re-send it.`,
974
+ ),
975
+ );
976
+ return;
977
+ }
978
+ } catch {
979
+ // Pending-list visibility can vary by server version or role.
980
+ // Fall through to the active-member probe and generic copy.
981
+ }
982
+
983
+ try {
984
+ const activeMembers = await listActiveMembers(token, companyUid);
985
+ if (
986
+ activeMembers.some(
987
+ (member) =>
988
+ member.personEmail?.trim().toLowerCase() ===
989
+ normalizedEmail,
990
+ )
991
+ ) {
992
+ console.log(
993
+ chalk.yellow(
994
+ `${normalizedEmail} is already a member of this company — nothing to do.`,
995
+ ),
996
+ );
997
+ return;
998
+ }
999
+ } catch {
1000
+ // Preserve the original conflict as the source of truth and
1001
+ // use copy that remains accurate when the probes are hidden.
1002
+ }
1003
+
1004
+ console.log(
1005
+ chalk.yellow(
1006
+ `${normalizedEmail} already has a membership or pending invite for this company. ` +
1007
+ `If the invite is pending, use \`${resendHint}\`.`,
1008
+ ),
1009
+ );
1010
+ return;
1011
+ }
1012
+
899
1013
  console.log(
900
1014
  chalk.yellow(
901
1015
  `${target.toLowerCase()} is already a member of this company — nothing to do.`,
@@ -1902,3 +1902,136 @@ describe("secrets reveal and policy controls", () => {
1902
1902
  );
1903
1903
  });
1904
1904
  });
1905
+
1906
+ describe("HQ_API_KEY consume path", () => {
1907
+ let exitSpy: MockInstance<typeof process.exit>;
1908
+
1909
+ beforeEach(() => {
1910
+ exitSpy = vi
1911
+ .spyOn(process, "exit")
1912
+ .mockImplementation(((code?: number) => {
1913
+ throw new Error(`__EXIT__:${code ?? 0}`);
1914
+ }) as never);
1915
+ });
1916
+
1917
+ afterEach(() => {
1918
+ delete process.env.HQ_API_KEY;
1919
+ });
1920
+
1921
+ it("rejects secrets list when HQ_API_KEY is set (no Cognito fallback)", async () => {
1922
+ process.env.HQ_API_KEY = "hqk_probe";
1923
+ const program = buildProgram();
1924
+ await expect(
1925
+ program.parseAsync(["node", "hq", "secrets", "list"]),
1926
+ ).rejects.toThrow(/__EXIT__:1/);
1927
+ expect(ensureCognitoToken).not.toHaveBeenCalled();
1928
+ expect(vaultApiFetch).not.toHaveBeenCalled();
1929
+ const errText = errSpy.mock.calls
1930
+ .map((call) => call.map(String).join(" "))
1931
+ .join("\n");
1932
+ expect(errText).toMatch(/not supported for API keys/);
1933
+ });
1934
+
1935
+ it("rejects invalid HQ_API_KEY prefix without Cognito fallback", async () => {
1936
+ process.env.HQ_API_KEY = "hqd_not_a_vault_key";
1937
+ const program = buildProgram();
1938
+ await expect(
1939
+ program.parseAsync(["node", "hq", "secrets", "get", "FOO"]),
1940
+ ).rejects.toThrow(/__EXIT__:1/);
1941
+ expect(ensureCognitoToken).not.toHaveBeenCalled();
1942
+ const errText = errSpy.mock.calls
1943
+ .map((call) => call.map(String).join(" "))
1944
+ .join("\n");
1945
+ expect(errText).toMatch(/must start with 'hqk_'/);
1946
+ });
1947
+
1948
+ it("gets a secret via /v1/keys/secrets/fetch when HQ_API_KEY is set", async () => {
1949
+ process.env.HQ_API_KEY = "hqk_valid_key";
1950
+ vi.mocked(vaultApiFetch).mockResolvedValueOnce(
1951
+ jsonRes({
1952
+ secret: {
1953
+ name: "FOO",
1954
+ value: "secret-value",
1955
+ version: 1,
1956
+ tier: "standard",
1957
+ },
1958
+ }),
1959
+ );
1960
+
1961
+ const program = buildProgram();
1962
+ await program.parseAsync([
1963
+ "node",
1964
+ "hq",
1965
+ "secrets",
1966
+ "get",
1967
+ "FOO",
1968
+ "--reveal",
1969
+ ]);
1970
+
1971
+ expect(ensureCognitoToken).not.toHaveBeenCalled();
1972
+ expect(vaultApiFetch).toHaveBeenCalledWith(
1973
+ expect.objectContaining({
1974
+ token: "hqk_valid_key",
1975
+ path: "/v1/keys/secrets/fetch",
1976
+ method: "POST",
1977
+ body: { name: "FOO" },
1978
+ }),
1979
+ );
1980
+ const printed = logSpy.mock.calls
1981
+ .map((call) => call.map(String).join(" "))
1982
+ .join("\n");
1983
+ expect(printed).toContain("FOO");
1984
+ expect(printed).toContain("secret-value");
1985
+ expect(exitSpy).not.toHaveBeenCalled();
1986
+ });
1987
+
1988
+ it("fails --reveal when API-key fetch omits secret.value", async () => {
1989
+ process.env.HQ_API_KEY = "hqk_valid_key";
1990
+ vi.mocked(vaultApiFetch).mockResolvedValueOnce(
1991
+ jsonRes({
1992
+ secret: {
1993
+ name: "FOO",
1994
+ version: 1,
1995
+ tier: "standard",
1996
+ },
1997
+ }),
1998
+ );
1999
+
2000
+ const program = buildProgram();
2001
+ await expect(
2002
+ program.parseAsync([
2003
+ "node",
2004
+ "hq",
2005
+ "secrets",
2006
+ "get",
2007
+ "FOO",
2008
+ "--reveal",
2009
+ ]),
2010
+ ).rejects.toThrow(/__EXIT__:1/);
2011
+ const errText = errSpy.mock.calls
2012
+ .map((call) => call.map(String).join(" "))
2013
+ .join("\n");
2014
+ expect(errText).toMatch(/omitted secret\.value/);
2015
+ const printed = logSpy.mock.calls
2016
+ .map((call) => call.map(String).join(" "))
2017
+ .join("\n");
2018
+ expect(printed).not.toContain("[REDACTED]");
2019
+ });
2020
+
2021
+ it("loadRevealedSecrets uses fetch endpoint for hqk_ tokens", async () => {
2022
+ vi.mocked(vaultApiFetch).mockResolvedValueOnce(
2023
+ jsonRes({
2024
+ secret: { name: "A", value: "va" },
2025
+ }),
2026
+ );
2027
+ const out = await loadRevealedSecrets("hqk_tok", "cmp_x", ["A"]);
2028
+ expect(out.get("A")).toBe("va");
2029
+ expect(vaultApiFetch).toHaveBeenCalledWith(
2030
+ expect.objectContaining({
2031
+ path: "/v1/keys/secrets/fetch",
2032
+ method: "POST",
2033
+ body: { name: "A" },
2034
+ }),
2035
+ );
2036
+ });
2037
+ });
@@ -23,6 +23,11 @@ import {
23
23
  getCompanyUid,
24
24
  getEntityUid,
25
25
  } from "../utils/vault-api.js";
26
+ import {
27
+ HQ_API_KEY_PREFIX,
28
+ assertCognitoOnlyCommand,
29
+ resolveVaultCredential,
30
+ } from "../utils/resolve-vault-credential.js";
26
31
  import {
27
32
  SandboxRunnerClient,
28
33
  type SandboxRunnerJob,
@@ -30,6 +35,14 @@ import {
30
35
  export type { VaultApiOptions } from "../utils/vault-api.js";
31
36
  export { vaultApiFetch, getCompanyUid, getEntityUid };
32
37
 
38
+ /** Cognito session for secrets commands that do not support HQ_API_KEY. */
39
+ async function requireCognitoTokenForSecrets(
40
+ commandLabel: string,
41
+ ): Promise<string> {
42
+ assertCognitoOnlyCommand(commandLabel);
43
+ return ensureCognitoToken();
44
+ }
45
+
33
46
  interface SecretsScopeOpts {
34
47
  company?: string;
35
48
  personal?: boolean;
@@ -654,12 +667,76 @@ function renderPolicyScripts(scripts: SecretPolicyScript[]): void {
654
667
  // Requests are chunked at MAX_BATCH_NAMES and throw on the FIRST unresolved key
655
668
  // with the same `Failed to fetch secret '<k>': <reason>` shape the per-key GET
656
669
  // path used — never swallows a failure.
670
+ async function loadRevealedSecretsViaApiKey(
671
+ token: string,
672
+ keys: string[],
673
+ ): Promise<Map<string, string>> {
674
+ const resolved = new Map<string, string>();
675
+ const requested = [...new Set(keys)];
676
+ const cacheScope = "__api_key__";
677
+
678
+ for (const name of requested) {
679
+ const res = await vaultApiFetch({
680
+ token,
681
+ path: "/v1/keys/secrets/fetch",
682
+ method: "POST",
683
+ body: { name },
684
+ signal: AbortSignal.timeout(SECRET_LOAD_TIMEOUT_MS),
685
+ });
686
+ const body = (await res.json().catch(() => ({}))) as Record<string, unknown>;
687
+
688
+ if (!res.ok) {
689
+ if (res.status === 404) {
690
+ throw new Error(`Failed to fetch secret '${name}': Secret not found`);
691
+ }
692
+ if (res.status === 403) {
693
+ const message =
694
+ typeof body.error === "string"
695
+ ? body.error
696
+ : typeof body.message === "string"
697
+ ? body.message
698
+ : "No read permission";
699
+ if (body.highSecurity === true) {
700
+ throw new Error(highSecuritySandboxOnlyMessage(name));
701
+ }
702
+ throw new Error(`Failed to fetch secret '${name}': ${message}`);
703
+ }
704
+ if (res.status === 401) {
705
+ throw new Error(
706
+ `Failed to fetch secret '${name}': Invalid or missing API key`,
707
+ );
708
+ }
709
+ throw new Error(
710
+ `Failed to fetch secret '${name}': ${extractApiMessage(body, res.statusText)}`,
711
+ );
712
+ }
713
+
714
+ const secret =
715
+ typeof body.secret === "object" && body.secret !== null
716
+ ? (body.secret as { value?: unknown; name?: unknown })
717
+ : null;
718
+ if (typeof secret?.value !== "string") {
719
+ throw new Error(
720
+ `Failed to fetch secret '${name}': malformed fetch response`,
721
+ );
722
+ }
723
+ removeCacheEntry(cacheScope, name);
724
+ resolved.set(name, secret.value);
725
+ }
726
+
727
+ return resolved;
728
+ }
729
+
657
730
  export async function loadRevealedSecrets(
658
731
  token: string,
659
732
  companyUid: string,
660
733
  keys: string[],
661
734
  usage?: SecretUsage,
662
735
  ): Promise<Map<string, string>> {
736
+ if (token.startsWith(HQ_API_KEY_PREFIX)) {
737
+ return loadRevealedSecretsViaApiKey(token, keys);
738
+ }
739
+
663
740
  const resolved = new Map<string, string>();
664
741
  const requested = [...new Set(keys)];
665
742
  try {
@@ -918,7 +995,7 @@ export function registerSecretsCommand(program: Command): void {
918
995
  process.exit(1);
919
996
  }
920
997
 
921
- const token = await ensureCognitoToken();
998
+ const token = await requireCognitoTokenForSecrets("secrets set");
922
999
  const scope = scopeOpts(secrets.opts());
923
1000
  const companyUid = await getEntityUid(token, scope);
924
1001
  const scopeLabel = describeSecretsScope({
@@ -975,7 +1052,69 @@ export function registerSecretsCommand(program: Command): void {
975
1052
  .option("--reveal", "Include the decrypted secret value")
976
1053
  .action(async (name: string, opts: { reveal?: boolean }) => {
977
1054
  try {
978
- const token = await ensureCognitoToken();
1055
+ const cred = await resolveVaultCredential();
1056
+
1057
+ if (cred.kind === "api-key") {
1058
+ const res = await vaultApiFetch({
1059
+ token: cred.token,
1060
+ path: "/v1/keys/secrets/fetch",
1061
+ method: "POST",
1062
+ body: { name },
1063
+ });
1064
+ const body = (await res.json().catch(() => ({}))) as Record<
1065
+ string,
1066
+ unknown
1067
+ >;
1068
+ if (!res.ok) {
1069
+ if (res.status === 403 && body.highSecurity === true) {
1070
+ console.error(chalk.red(highSecuritySandboxOnlyMessage(name)));
1071
+ process.exit(1);
1072
+ }
1073
+ console.error(
1074
+ chalk.red(
1075
+ `Failed to get secret: ${extractApiMessage(body, res.statusText)}`,
1076
+ ),
1077
+ );
1078
+ process.exit(1);
1079
+ }
1080
+ const secret =
1081
+ typeof body.secret === "object" && body.secret !== null
1082
+ ? (body.secret as SecretGetResponse["secret"] & {
1083
+ value?: string;
1084
+ })
1085
+ : null;
1086
+ if (!secret || typeof secret.name !== "string") {
1087
+ console.error(chalk.red("Failed to get secret: malformed response"));
1088
+ process.exit(1);
1089
+ }
1090
+ console.log(chalk.bold(`Secret: ${secret.name}`));
1091
+ if (secret.lastModifiedDate) {
1092
+ console.log(` Last Modified: ${secret.lastModifiedDate}`);
1093
+ }
1094
+ if (secret.version != null) {
1095
+ console.log(` Version: ${secret.version}`);
1096
+ }
1097
+ console.log(` Tier: ${normalizeSecretTier(secret.tier)}`);
1098
+ console.log(
1099
+ ` Script Lock: ${normalizeScriptLockMode(secret.scriptLock?.mode)}`,
1100
+ );
1101
+ if (opts.reveal) {
1102
+ if (typeof secret.value !== "string") {
1103
+ console.error(
1104
+ chalk.red(
1105
+ "Failed to get secret: reveal requested but response omitted secret.value",
1106
+ ),
1107
+ );
1108
+ process.exit(1);
1109
+ }
1110
+ console.log(` Value: ${secret.value}`);
1111
+ } else {
1112
+ console.log(` Value: ${chalk.dim("[REDACTED]")}`);
1113
+ }
1114
+ return;
1115
+ }
1116
+
1117
+ const token = cred.token;
979
1118
  const companyUid = await getEntityUid(
980
1119
  token,
981
1120
  scopeOpts(secrets.opts()),
@@ -1061,7 +1200,7 @@ export function registerSecretsCommand(program: Command): void {
1061
1200
  .option("--quiet", "Suppress the present/absent line (use the exit code only)")
1062
1201
  .action(async (name: string, opts: { quiet?: boolean }) => {
1063
1202
  try {
1064
- const token = await ensureCognitoToken();
1203
+ const token = await requireCognitoTokenForSecrets("secrets exists");
1065
1204
  const companyUid = await getEntityUid(
1066
1205
  token,
1067
1206
  scopeOpts(secrets.opts()),
@@ -1124,7 +1263,7 @@ export function registerSecretsCommand(program: Command): void {
1124
1263
  normalizedPrefix = normalized;
1125
1264
  }
1126
1265
 
1127
- const token = await ensureCognitoToken();
1266
+ const token = await requireCognitoTokenForSecrets("secrets list");
1128
1267
  const scope = scopeOpts(secrets.opts());
1129
1268
  const companyUid = await getEntityUid(token, scope);
1130
1269
  const scopeLabel = describeSecretsScope({
@@ -1226,7 +1365,7 @@ export function registerSecretsCommand(program: Command): void {
1226
1365
  process.exit(1);
1227
1366
  }
1228
1367
 
1229
- const token = await ensureCognitoToken();
1368
+ const token = await requireCognitoTokenForSecrets("secrets");
1230
1369
  const companyUid = await getEntityUid(
1231
1370
  token,
1232
1371
  scopeOpts(secrets.opts()),
@@ -1309,7 +1448,7 @@ export function registerSecretsCommand(program: Command): void {
1309
1448
  process.exit(1);
1310
1449
  }
1311
1450
 
1312
- const token = await ensureCognitoToken();
1451
+ const token = await requireCognitoTokenForSecrets("secrets");
1313
1452
  const companyUid = await getEntityUid(
1314
1453
  token,
1315
1454
  scopeOpts(secrets.opts()),
@@ -1392,7 +1531,7 @@ export function registerSecretsCommand(program: Command): void {
1392
1531
  opts.id,
1393
1532
  opts.attestation,
1394
1533
  );
1395
- const token = await ensureCognitoToken();
1534
+ const token = await requireCognitoTokenForSecrets("secrets");
1396
1535
  const companyUid = await getEntityUid(
1397
1536
  token,
1398
1537
  scopeOpts(secrets.opts()),
@@ -1442,7 +1581,7 @@ export function registerSecretsCommand(program: Command): void {
1442
1581
  process.exit(1);
1443
1582
  }
1444
1583
 
1445
- const token = await ensureCognitoToken();
1584
+ const token = await requireCognitoTokenForSecrets("secrets");
1446
1585
  const companyUid = await getEntityUid(
1447
1586
  token,
1448
1587
  scopeOpts(secrets.opts()),
@@ -1485,7 +1624,7 @@ export function registerSecretsCommand(program: Command): void {
1485
1624
  process.exit(1);
1486
1625
  }
1487
1626
 
1488
- const token = await ensureCognitoToken();
1627
+ const token = await requireCognitoTokenForSecrets("secrets");
1489
1628
  const companyUid = await getEntityUid(
1490
1629
  token,
1491
1630
  scopeOpts(secrets.opts()),
@@ -1546,7 +1685,7 @@ export function registerSecretsCommand(program: Command): void {
1546
1685
  }
1547
1686
  }
1548
1687
 
1549
- const token = await ensureCognitoToken();
1688
+ const token = await requireCognitoTokenForSecrets("secrets");
1550
1689
  const companyUid = await getEntityUid(
1551
1690
  token,
1552
1691
  scopeOpts(secrets.opts()),
@@ -1606,7 +1745,7 @@ export function registerSecretsCommand(program: Command): void {
1606
1745
  }
1607
1746
 
1608
1747
  const keys = parseSecretNameList(opts.only);
1609
- const token = await ensureCognitoToken();
1748
+ const token = await requireCognitoTokenForSecrets("secrets");
1610
1749
  const scope = scopeOpts(mergeScopeOpts(secrets.opts(), opts));
1611
1750
  const companyUid = await getEntityUid(token, scope);
1612
1751
  const client = new SandboxRunnerClient();
@@ -1673,17 +1812,19 @@ export function registerSecretsCommand(program: Command): void {
1673
1812
 
1674
1813
  const keys = parseSecretNameList(_opts.only);
1675
1814
 
1676
- const token = await ensureCognitoToken();
1677
- const companyUid = await getEntityUid(
1678
- token,
1679
- scopeOpts(secrets.opts()),
1680
- );
1815
+ const cred = await resolveVaultCredential();
1816
+ const companyUid =
1817
+ cred.kind === "api-key"
1818
+ ? "__api_key__"
1819
+ : await getEntityUid(cred.token, scopeOpts(secrets.opts()));
1681
1820
 
1682
1821
  const revealed = await loadRevealedSecrets(
1683
- token,
1822
+ cred.token,
1684
1823
  companyUid,
1685
1824
  keys,
1686
- await buildSecretUsage("exec", _opts.script, _opts.scriptId),
1825
+ cred.kind === "cognito"
1826
+ ? await buildSecretUsage("exec", _opts.script, _opts.scriptId)
1827
+ : undefined,
1687
1828
  );
1688
1829
 
1689
1830
  const secretEnv: Record<string, string> = {};
@@ -1744,17 +1885,19 @@ export function registerSecretsCommand(program: Command): void {
1744
1885
 
1745
1886
  const keys = parseSecretNameList(opts.only);
1746
1887
 
1747
- const token = await ensureCognitoToken();
1748
- const companyUid = await getEntityUid(
1749
- token,
1750
- scopeOpts(secrets.opts()),
1751
- );
1888
+ const cred = await resolveVaultCredential();
1889
+ const companyUid =
1890
+ cred.kind === "api-key"
1891
+ ? "__api_key__"
1892
+ : await getEntityUid(cred.token, scopeOpts(secrets.opts()));
1752
1893
 
1753
1894
  const revealed = await loadRevealedSecrets(
1754
- token,
1895
+ cred.token,
1755
1896
  companyUid,
1756
1897
  keys,
1757
- await buildSecretUsage("env", opts.script, opts.scriptId),
1898
+ cred.kind === "cognito"
1899
+ ? await buildSecretUsage("env", opts.script, opts.scriptId)
1900
+ : undefined,
1758
1901
  );
1759
1902
 
1760
1903
  for (const key of keys) {
@@ -1799,7 +1942,7 @@ export function registerSecretsCommand(program: Command): void {
1799
1942
  process.exit(1);
1800
1943
  }
1801
1944
 
1802
- const token = await ensureCognitoToken();
1945
+ const token = await requireCognitoTokenForSecrets("secrets");
1803
1946
  const companyUid = await getEntityUid(
1804
1947
  token,
1805
1948
  scopeOpts(secrets.opts()),
@@ -1865,7 +2008,7 @@ export function registerSecretsCommand(program: Command): void {
1865
2008
  process.exit(1);
1866
2009
  }
1867
2010
 
1868
- const token = await ensureCognitoToken();
2011
+ const token = await requireCognitoTokenForSecrets("secrets");
1869
2012
  const companyUid = await getEntityUid(
1870
2013
  token,
1871
2014
  scopeOpts(secrets.opts()),
@@ -1929,7 +2072,7 @@ export function registerSecretsCommand(program: Command): void {
1929
2072
  process.exit(1);
1930
2073
  }
1931
2074
 
1932
- const token = await ensureCognitoToken();
2075
+ const token = await requireCognitoTokenForSecrets("secrets");
1933
2076
  const companyUid = await getEntityUid(
1934
2077
  token,
1935
2078
  scopeOpts(secrets.opts()),
@@ -1985,7 +2128,7 @@ export function registerSecretsCommand(program: Command): void {
1985
2128
  process.exit(1);
1986
2129
  }
1987
2130
 
1988
- const token = await ensureCognitoToken();
2131
+ const token = await requireCognitoTokenForSecrets("secrets");
1989
2132
  const companyUid = await getEntityUid(
1990
2133
  token,
1991
2134
  scopeOpts(secrets.opts()),