@indigoai-us/hq-cli 5.47.12 → 5.47.13

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.
@@ -6,8 +6,9 @@
6
6
  * shell out to a plain binary instead of a subcommand. Equivalent to
7
7
  * `hq auth refresh`.
8
8
  *
9
- * Exit 0 on refresh, exit 1 if no cached session or refresh failed.
10
- * Writes the refreshed tokens to ~/.hq/cognito-tokens.json.
9
+ * Exit 0 if a valid session is ensured now (healthy cache hit, refresh, or
10
+ * machine-token mint). Exit 1 if no valid session could be ensured
11
+ * non-interactively.
11
12
  */
12
13
  export {};
13
14
  //# sourceMappingURL=hq-auth-refresh.d.ts.map
@@ -6,11 +6,12 @@
6
6
  * shell out to a plain binary instead of a subcommand. Equivalent to
7
7
  * `hq auth refresh`.
8
8
  *
9
- * Exit 0 on refresh, exit 1 if no cached session or refresh failed.
10
- * Writes the refreshed tokens to ~/.hq/cognito-tokens.json.
9
+ * Exit 0 if a valid session is ensured now (healthy cache hit, refresh, or
10
+ * machine-token mint). Exit 1 if no valid session could be ensured
11
+ * non-interactively.
11
12
  */
12
13
 
13
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="28774b80-becb-5fb9-b676-dee74c26d73d")}catch(e){}}();
14
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="03d2c531-f620-5d93-a87b-9a87264ad77b")}catch(e){}}();
14
15
  import { initSentry, Sentry } from "../sentry.js";
15
16
  import { refreshCachedSession } from "../utils/cognito-session.js";
16
17
  initSentry();
@@ -37,4 +38,4 @@ initSentry();
37
38
  }
38
39
  })();
39
40
  //# sourceMappingURL=hq-auth-refresh.js.map
40
- //# debugId=28774b80-becb-5fb9-b676-dee74c26d73d
41
+ //# debugId=03d2c531-f620-5d93-a87b-9a87264ad77b
@@ -4,13 +4,13 @@
4
4
  * Subcommands:
5
5
  * hq auth login — open the Cognito Hosted UI in the browser and cache tokens
6
6
  * hq auth logout — clear the cached HQ session
7
- * hq auth refresh — refresh the cached Cognito session (non-interactive)
7
+ * hq auth refresh — ensure a valid cached Cognito session (non-interactive)
8
8
  * hq auth status — show whether a valid session is cached + expiry
9
9
  *
10
10
  * Sign-up is owned by the onboarding web app at
11
11
  * https://onboarding.indigo-hq.com. `hq auth login` signs an existing account
12
12
  * into this machine by writing ~/.hq/cognito-tokens.json; once cached, the
13
- * session is kept fresh by `hq auth refresh` / `hq-auth-refresh` and consumed
13
+ * session is kept valid by `hq auth refresh` / `hq-auth-refresh` and consumed
14
14
  * by the deploy + sync skills.
15
15
  */
16
16
  import { Command } from "commander";
@@ -4,17 +4,17 @@
4
4
  * Subcommands:
5
5
  * hq auth login — open the Cognito Hosted UI in the browser and cache tokens
6
6
  * hq auth logout — clear the cached HQ session
7
- * hq auth refresh — refresh the cached Cognito session (non-interactive)
7
+ * hq auth refresh — ensure a valid cached Cognito session (non-interactive)
8
8
  * hq auth status — show whether a valid session is cached + expiry
9
9
  *
10
10
  * Sign-up is owned by the onboarding web app at
11
11
  * https://onboarding.indigo-hq.com. `hq auth login` signs an existing account
12
12
  * into this machine by writing ~/.hq/cognito-tokens.json; once cached, the
13
- * session is kept fresh by `hq auth refresh` / `hq-auth-refresh` and consumed
13
+ * session is kept valid by `hq auth refresh` / `hq-auth-refresh` and consumed
14
14
  * by the deploy + sync skills.
15
15
  */
16
16
 
17
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b9690c95-1d23-5c58-8cb3-0e3bdc8b35e8")}catch(e){}}();
17
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="66a74720-f5d1-58ed-93fd-ead15fc18146")}catch(e){}}();
18
18
  import chalk from "chalk";
19
19
  import { browserLogin, clearCachedTokens, loadCachedTokens, isExpiring, isMachineIdentity, loadMachineCreds, CognitoAuthError, } from "@indigoai-us/hq-cloud";
20
20
  import { refreshCachedSession, } from "../utils/cognito-session.js";
@@ -98,14 +98,14 @@ export function registerAuthCommands(program) {
98
98
  });
99
99
  authCmd
100
100
  .command("refresh")
101
- .description("Refresh the cached Cognito session using the stored refresh token")
101
+ .description("Ensure a valid cached Cognito session (refresh only if expiring; no-op if already valid)")
102
102
  .action(async () => {
103
103
  const result = await refreshCachedSession();
104
104
  if (result.refreshed) {
105
- console.log(chalk.green("HQ session refreshed"));
105
+ console.log(chalk.green("HQ session ensured"));
106
106
  return;
107
107
  }
108
- console.error(chalk.yellow(`No refresh: ${result.reason ?? "unknown"}`));
108
+ console.error(chalk.yellow(`No valid session: ${result.reason ?? "unknown"}`));
109
109
  process.exit(1);
110
110
  });
111
111
  authCmd
@@ -136,4 +136,4 @@ export function registerAuthCommands(program) {
136
136
  });
137
137
  }
138
138
  //# sourceMappingURL=auth.js.map
139
- //# debugId=b9690c95-1d23-5c58-8cb3-0e3bdc8b35e8
139
+ //# debugId=66a74720-f5d1-58ed-93fd-ead15fc18146
@@ -109,10 +109,12 @@ export declare const CLI_CLIENT_INFO: ClientInfo;
109
109
  /** Build a VaultServiceConfig with the given access token. */
110
110
  export declare function buildVaultConfig(authToken: string): VaultServiceConfig;
111
111
  /**
112
- * Refresh the cached Cognito session once and return the result. Used by
113
- * `hq auth refresh` and the `hq-auth-refresh` bin. Never opens a browser —
114
- * if no cached tokens exist or the refresh fails, returns `refreshed: false`
115
- * with a reason string so the caller can decide what to do.
112
+ * Ensure a valid cached Cognito session exists right now, non-interactively,
113
+ * and return the result. Used by `hq auth refresh` and the `hq-auth-refresh`
114
+ * bin. `refreshed: true` means a valid session is ensured now from a healthy
115
+ * cache hit, a token refresh, or a machine-token mint. `refreshed: false`
116
+ * means no valid session could be ensured non-interactively; the caller gets a
117
+ * reason string and can decide what to do next. Never opens a browser.
116
118
  */
117
119
  export declare function refreshCachedSession(): Promise<{
118
120
  refreshed: boolean;
@@ -19,13 +19,13 @@
19
19
  * HQ_VAULT_API_URL — vault-service API Gateway URL
20
20
  */
21
21
 
22
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="21e260f0-50fd-54c7-944a-c15630c42f3e")}catch(e){}}();
22
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ab9733a5-3984-553b-8fcc-91b86d112c79")}catch(e){}}();
23
23
  import * as fs from "fs";
24
24
  import * as os from "os";
25
25
  import * as path from "path";
26
26
  import * as yaml from "js-yaml";
27
27
  import chalk from "chalk";
28
- import { loadCachedTokens, isExpiring, refreshTokens, browserLogin, detectHqCoreVersion, isMachineIdentity, getValidMachineTokens, mintMachineTokens, } from "@indigoai-us/hq-cloud";
28
+ import { loadCachedTokens, isExpiring, refreshTokens, browserLogin, detectHqCoreVersion, isMachineIdentity, getValidMachineTokens, } from "@indigoai-us/hq-cloud";
29
29
  import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
30
30
  export const DEFAULT_COGNITO = {
31
31
  region: process.env.AWS_REGION ?? "us-east-1",
@@ -334,17 +334,19 @@ export function buildVaultConfig(authToken) {
334
334
  };
335
335
  }
336
336
  /**
337
- * Refresh the cached Cognito session once and return the result. Used by
338
- * `hq auth refresh` and the `hq-auth-refresh` bin. Never opens a browser —
339
- * if no cached tokens exist or the refresh fails, returns `refreshed: false`
340
- * with a reason string so the caller can decide what to do.
337
+ * Ensure a valid cached Cognito session exists right now, non-interactively,
338
+ * and return the result. Used by `hq auth refresh` and the `hq-auth-refresh`
339
+ * bin. `refreshed: true` means a valid session is ensured now from a healthy
340
+ * cache hit, a token refresh, or a machine-token mint. `refreshed: false`
341
+ * means no valid session could be ensured non-interactively; the caller gets a
342
+ * reason string and can decide what to do next. Never opens a browser.
341
343
  */
342
344
  export async function refreshCachedSession() {
343
- // Machine identities have no refresh token a "refresh" is a fresh mint
344
- // from the long-lived machine creds.
345
+ // Machine identities have no refresh token; ensure a valid cached machine
346
+ // session without forcing a re-mint when the cache is already healthy.
345
347
  if (isMachineIdentity()) {
346
348
  try {
347
- await mintMachineTokens(DEFAULT_COGNITO);
349
+ await getValidMachineTokens(DEFAULT_COGNITO);
348
350
  return { refreshed: true };
349
351
  }
350
352
  catch (err) {
@@ -358,6 +360,9 @@ export async function refreshCachedSession() {
358
360
  if (!cached) {
359
361
  return { refreshed: false, reason: "no cached session" };
360
362
  }
363
+ if (!isExpiring(cached, 120)) {
364
+ return { refreshed: true };
365
+ }
361
366
  try {
362
367
  await refreshTokens(DEFAULT_COGNITO, cached.refreshToken);
363
368
  return { refreshed: true };
@@ -370,4 +375,4 @@ export async function refreshCachedSession() {
370
375
  }
371
376
  }
372
377
  //# sourceMappingURL=cognito-session.js.map
373
- //# debugId=21e260f0-50fd-54c7-944a-c15630c42f3e
378
+ //# debugId=ab9733a5-3984-553b-8fcc-91b86d112c79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.47.12",
3
+ "version": "5.47.13",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -7,8 +7,9 @@
7
7
  * shell out to a plain binary instead of a subcommand. Equivalent to
8
8
  * `hq auth refresh`.
9
9
  *
10
- * Exit 0 on refresh, exit 1 if no cached session or refresh failed.
11
- * Writes the refreshed tokens to ~/.hq/cognito-tokens.json.
10
+ * Exit 0 if a valid session is ensured now (healthy cache hit, refresh, or
11
+ * machine-token mint). Exit 1 if no valid session could be ensured
12
+ * non-interactively.
12
13
  */
13
14
 
14
15
  import { initSentry, Sentry } from "../sentry.js";
@@ -4,13 +4,13 @@
4
4
  * Subcommands:
5
5
  * hq auth login — open the Cognito Hosted UI in the browser and cache tokens
6
6
  * hq auth logout — clear the cached HQ session
7
- * hq auth refresh — refresh the cached Cognito session (non-interactive)
7
+ * hq auth refresh — ensure a valid cached Cognito session (non-interactive)
8
8
  * hq auth status — show whether a valid session is cached + expiry
9
9
  *
10
10
  * Sign-up is owned by the onboarding web app at
11
11
  * https://onboarding.indigo-hq.com. `hq auth login` signs an existing account
12
12
  * into this machine by writing ~/.hq/cognito-tokens.json; once cached, the
13
- * session is kept fresh by `hq auth refresh` / `hq-auth-refresh` and consumed
13
+ * session is kept valid by `hq auth refresh` / `hq-auth-refresh` and consumed
14
14
  * by the deploy + sync skills.
15
15
  */
16
16
 
@@ -137,16 +137,16 @@ export function registerAuthCommands(program: Command): void {
137
137
  authCmd
138
138
  .command("refresh")
139
139
  .description(
140
- "Refresh the cached Cognito session using the stored refresh token",
140
+ "Ensure a valid cached Cognito session (refresh only if expiring; no-op if already valid)",
141
141
  )
142
142
  .action(async () => {
143
143
  const result = await refreshCachedSession();
144
144
  if (result.refreshed) {
145
- console.log(chalk.green("HQ session refreshed"));
145
+ console.log(chalk.green("HQ session ensured"));
146
146
  return;
147
147
  }
148
148
  console.error(
149
- chalk.yellow(`No refresh: ${result.reason ?? "unknown"}`),
149
+ chalk.yellow(`No valid session: ${result.reason ?? "unknown"}`),
150
150
  );
151
151
  process.exit(1);
152
152
  });
@@ -6,7 +6,7 @@
6
6
  * authorizer accepts ID tokens and the agent claims
7
7
  * (custom:entityType/custom:entityUid) ride the ID token only
8
8
  * - never refresh or open a browser — sessions re-mint on demand
9
- * - treat `hq auth refresh` as a fresh mint instead of "no cached session"
9
+ * - treat `hq auth refresh` as a non-interactive ensure-valid-session call
10
10
  */
11
11
 
12
12
  import { describe, it, expect, beforeEach, vi } from "vitest";
@@ -81,34 +81,29 @@ describe("ensureCognitoToken in machine mode", () => {
81
81
  });
82
82
 
83
83
  describe("refreshCachedSession in machine mode", () => {
84
- it("re-mints instead of answering 'no cached session'", async () => {
84
+ it("ensures a healthy machine session via getValidMachineTokens", async () => {
85
85
  mocks.isMachineIdentity.mockReturnValue(true);
86
- mocks.mintMachineTokens.mockResolvedValue(MACHINE_TOKENS);
87
- mocks.loadCachedTokens.mockReturnValue(null);
86
+ mocks.getValidMachineTokens.mockResolvedValue(MACHINE_TOKENS);
88
87
 
89
88
  const result = await refreshCachedSession();
90
89
 
91
90
  expect(result).toEqual({ refreshed: true });
92
- expect(mocks.mintMachineTokens).toHaveBeenCalledTimes(1);
91
+ expect(mocks.getValidMachineTokens).toHaveBeenCalledTimes(1);
92
+ expect(mocks.mintMachineTokens).not.toHaveBeenCalled();
93
93
  expect(mocks.refreshTokens).not.toHaveBeenCalled();
94
+ expect(mocks.browserLogin).not.toHaveBeenCalled();
95
+ expect(mocks.loadCachedTokens).not.toHaveBeenCalled();
94
96
  });
95
97
 
96
- it("surfaces the mint error as the reason on failure", async () => {
98
+ it("surfaces the machine-session error as the reason on failure", async () => {
97
99
  mocks.isMachineIdentity.mockReturnValue(true);
98
- mocks.mintMachineTokens.mockRejectedValue(new Error("NotAuthorized"));
100
+ mocks.getValidMachineTokens.mockRejectedValue(new Error("NotAuthorized"));
99
101
 
100
102
  const result = await refreshCachedSession();
101
103
 
102
104
  expect(result.refreshed).toBe(false);
103
105
  expect(result.reason).toContain("NotAuthorized");
104
- });
105
-
106
- it("keeps the human refresh path unchanged", async () => {
107
- mocks.isMachineIdentity.mockReturnValue(false);
108
- mocks.loadCachedTokens.mockReturnValue(null);
109
-
110
- const result = await refreshCachedSession();
111
-
112
- expect(result).toEqual({ refreshed: false, reason: "no cached session" });
106
+ expect(mocks.refreshTokens).not.toHaveBeenCalled();
107
+ expect(mocks.browserLogin).not.toHaveBeenCalled();
113
108
  });
114
109
  });
@@ -0,0 +1,84 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => ({
4
+ isMachineIdentity: vi.fn(),
5
+ getValidMachineTokens: vi.fn(),
6
+ mintMachineTokens: vi.fn(),
7
+ loadCachedTokens: vi.fn(),
8
+ refreshTokens: vi.fn(),
9
+ browserLogin: vi.fn(),
10
+ isExpiring: vi.fn(),
11
+ }));
12
+
13
+ vi.mock("@indigoai-us/hq-cloud", async (importOriginal) => {
14
+ const actual = await importOriginal<typeof import("@indigoai-us/hq-cloud")>();
15
+ return { ...actual, ...mocks };
16
+ });
17
+
18
+ import { refreshCachedSession } from "./cognito-session.js";
19
+
20
+ const HUMAN_TOKENS = {
21
+ accessToken: "human-access-token",
22
+ idToken: "human-id-token",
23
+ refreshToken: "human-refresh-token",
24
+ expiresAt: Date.now() + 3600_000,
25
+ tokenType: "Bearer" as const,
26
+ };
27
+
28
+ beforeEach(() => {
29
+ vi.resetAllMocks();
30
+ });
31
+
32
+ describe("refreshCachedSession in human mode", () => {
33
+ it("returns no cached session without attempting a refresh", async () => {
34
+ mocks.isMachineIdentity.mockReturnValue(false);
35
+ mocks.loadCachedTokens.mockReturnValue(null);
36
+
37
+ const result = await refreshCachedSession();
38
+
39
+ expect(result).toEqual({ refreshed: false, reason: "no cached session" });
40
+ expect(mocks.refreshTokens).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it("returns success on a healthy cached session without refreshing", async () => {
44
+ mocks.isMachineIdentity.mockReturnValue(false);
45
+ mocks.loadCachedTokens.mockReturnValue(HUMAN_TOKENS);
46
+ mocks.isExpiring.mockReturnValue(false);
47
+
48
+ const result = await refreshCachedSession();
49
+
50
+ expect(result).toEqual({ refreshed: true });
51
+ expect(mocks.isExpiring).toHaveBeenCalledWith(HUMAN_TOKENS, 120);
52
+ expect(mocks.refreshTokens).not.toHaveBeenCalled();
53
+ });
54
+
55
+ it("refreshes once when the cached session is expiring", async () => {
56
+ mocks.isMachineIdentity.mockReturnValue(false);
57
+ mocks.loadCachedTokens.mockReturnValue(HUMAN_TOKENS);
58
+ mocks.isExpiring.mockReturnValue(true);
59
+ mocks.refreshTokens.mockResolvedValue({
60
+ ...HUMAN_TOKENS,
61
+ accessToken: "refreshed-access-token",
62
+ });
63
+
64
+ const result = await refreshCachedSession();
65
+
66
+ expect(result).toEqual({ refreshed: true });
67
+ expect(mocks.refreshTokens).toHaveBeenCalledTimes(1);
68
+ expect(mocks.refreshTokens).toHaveBeenCalledWith(
69
+ expect.any(Object),
70
+ HUMAN_TOKENS.refreshToken,
71
+ );
72
+ });
73
+
74
+ it("surfaces refresh failures as a non-throwing result", async () => {
75
+ mocks.isMachineIdentity.mockReturnValue(false);
76
+ mocks.loadCachedTokens.mockReturnValue(HUMAN_TOKENS);
77
+ mocks.isExpiring.mockReturnValue(true);
78
+ mocks.refreshTokens.mockRejectedValue(new Error("token expired"));
79
+
80
+ const result = await refreshCachedSession();
81
+
82
+ expect(result).toEqual({ refreshed: false, reason: "token expired" });
83
+ });
84
+ });
@@ -32,7 +32,6 @@ import {
32
32
  detectHqCoreVersion,
33
33
  isMachineIdentity,
34
34
  getValidMachineTokens,
35
- mintMachineTokens,
36
35
  type CognitoAuthConfig,
37
36
  type ClientInfo,
38
37
  type VaultServiceConfig,
@@ -399,20 +398,22 @@ export function buildVaultConfig(authToken: string): VaultServiceConfig {
399
398
  }
400
399
 
401
400
  /**
402
- * Refresh the cached Cognito session once and return the result. Used by
403
- * `hq auth refresh` and the `hq-auth-refresh` bin. Never opens a browser —
404
- * if no cached tokens exist or the refresh fails, returns `refreshed: false`
405
- * with a reason string so the caller can decide what to do.
401
+ * Ensure a valid cached Cognito session exists right now, non-interactively,
402
+ * and return the result. Used by `hq auth refresh` and the `hq-auth-refresh`
403
+ * bin. `refreshed: true` means a valid session is ensured now from a healthy
404
+ * cache hit, a token refresh, or a machine-token mint. `refreshed: false`
405
+ * means no valid session could be ensured non-interactively; the caller gets a
406
+ * reason string and can decide what to do next. Never opens a browser.
406
407
  */
407
408
  export async function refreshCachedSession(): Promise<{
408
409
  refreshed: boolean;
409
410
  reason?: string;
410
411
  }> {
411
- // Machine identities have no refresh token a "refresh" is a fresh mint
412
- // from the long-lived machine creds.
412
+ // Machine identities have no refresh token; ensure a valid cached machine
413
+ // session without forcing a re-mint when the cache is already healthy.
413
414
  if (isMachineIdentity()) {
414
415
  try {
415
- await mintMachineTokens(DEFAULT_COGNITO);
416
+ await getValidMachineTokens(DEFAULT_COGNITO);
416
417
  return { refreshed: true };
417
418
  } catch (err) {
418
419
  return {
@@ -426,6 +427,11 @@ export async function refreshCachedSession(): Promise<{
426
427
  if (!cached) {
427
428
  return { refreshed: false, reason: "no cached session" };
428
429
  }
430
+
431
+ if (!isExpiring(cached, 120)) {
432
+ return { refreshed: true };
433
+ }
434
+
429
435
  try {
430
436
  await refreshTokens(DEFAULT_COGNITO, cached.refreshToken);
431
437
  return { refreshed: true };