@indigoai-us/hq-cli 5.342.10 → 5.342.11

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.342.11] — 2026-10-04
6
+ - Release the CLI package-use lease before interactive login waits after resident modules preload.
7
+ - Show the cached version update nudge after interactive commands when a newer cached release remains and its feature flag is enabled; avoid waiting for registry readiness when there is no nudge to show or repeating guidance after the updater already reported its result.
8
+
5
9
  ## [5.342.10] — 2026-10-04
6
10
  - Attach bounded status, gateway error-class, and integrations subcommand tags to captured gateway faults.
7
11
 
@@ -14,6 +14,8 @@
14
14
  * session is kept valid by `hq auth refresh` / `hq-auth-refresh` and consumed
15
15
  * by the deploy + sync skills.
16
16
  */
17
+ import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
18
+ import { preloadResidentPackageModules, reportResidentPackagePreloadFailure } from "../utils/resident-package-preload.js";
17
19
  import * as fs from "node:fs";
18
20
  import { open, realpath } from "node:fs/promises";
19
21
  import path from "node:path";
@@ -231,6 +233,7 @@ export function registerAuthCommands(program) {
231
233
  }
232
234
  }
233
235
  const selection = resolveLoginProviderSelection(options.provider, process.env, rememberedProvider);
236
+ await releasePackageUseLeaseAfterResidentPreload(() => preloadResidentPackageModules("auth-login"), reportResidentPackagePreloadFailure, releaseCurrentPackageUseLease);
234
237
  const tokens = await browserLogin(selection.config);
235
238
  try {
236
239
  storeLastLoginProvider(providerFromIdToken(tokens.idToken) ?? selection.providerToRemember);
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * hq login — opens browser for Cognito auth.
3
3
  */
4
+ import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
5
+ import { preloadResidentPackageModules, reportResidentPackagePreloadFailure } from "../utils/resident-package-preload.js";
4
6
  import chalk from 'chalk';
5
7
  import { browserLogin, loadCachedTokens, isExpiring } from '@indigoai-us/hq-cloud';
6
8
  import { cognitoConfigForLoginProvider } from '../utils/login-provider.js';
@@ -30,6 +32,15 @@ export function registerLoginCommand(program) {
30
32
  .option('--provider <provider>', 'OAuth provider to use: google, microsoft, or picker')
31
33
  .action(async (options) => {
32
34
  let accessToken;
35
+ let packageUseLeaseReleased = false;
36
+ const releaseForInteractiveWait = async () => {
37
+ if (packageUseLeaseReleased)
38
+ return;
39
+ await releasePackageUseLeaseAfterResidentPreload(async () => {
40
+ await preloadResidentPackageModules("login");
41
+ packageUseLeaseReleased = true;
42
+ }, reportResidentPackagePreloadFailure, releaseCurrentPackageUseLease);
43
+ };
33
44
  try {
34
45
  const existing = loadCachedTokens();
35
46
  if (existing && !isExpiring(existing, 120)) {
@@ -38,6 +49,7 @@ export function registerLoginCommand(program) {
38
49
  accessToken = existing.accessToken;
39
50
  }
40
51
  else {
52
+ await releaseForInteractiveWait();
41
53
  console.log('Opening browser for authentication...');
42
54
  const tokens = await browserLogin(cognitoConfigForLoginProvider(options.provider));
43
55
  const who = peekIdToken(tokens.idToken).email ?? 'HQ';
@@ -63,6 +75,7 @@ export function registerLoginCommand(program) {
63
75
  .join("\n");
64
76
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
65
77
  try {
78
+ await releaseForInteractiveWait();
66
79
  const answer = await rl.question(`Choose this device's default company:\n${choices}\nSelection: `);
67
80
  const selected = activation.memberships[Number.parseInt(answer, 10) - 1];
68
81
  if (selected) {
@@ -47,6 +47,7 @@ let refreshCompletion = Promise.resolve();
47
47
  let completeRefresh = null;
48
48
  let refreshWorker = null;
49
49
  let refreshCompanyUid;
50
+ let refreshHasCompanyContext = false;
50
51
  let refreshPendingSnapshot = null;
51
52
  let refreshSentry;
52
53
  /** Wait briefly for the current company refresh to persist or report completion. */
@@ -144,13 +145,12 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
144
145
  completeRefresh = null;
145
146
  refreshWorker = null;
146
147
  refreshCompanyUid = companyUid;
148
+ refreshHasCompanyContext = Boolean(companyUid || company.companySlug);
147
149
  refreshPendingSnapshot = null;
148
150
  refreshSentry = sentry;
149
- if (companyUid || company.companySlug) {
150
- refreshCompletion = new Promise((resolve) => {
151
- completeRefresh = resolve;
152
- });
153
- }
151
+ refreshCompletion = new Promise((resolve) => {
152
+ completeRefresh = resolve;
153
+ });
154
154
  const finishRefresh = () => {
155
155
  completeRefresh?.();
156
156
  completeRefresh = null;
@@ -220,6 +220,10 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
220
220
  refreshPendingSnapshot = null;
221
221
  finishRefresh();
222
222
  }
223
+ else if (!refreshHasCompanyContext) {
224
+ refreshPendingSnapshot = null;
225
+ finishRefresh();
226
+ }
223
227
  }
224
228
  else {
225
229
  finishRefresh();
@@ -316,6 +320,7 @@ export function _resetFlagRegistryForTests() {
316
320
  completeRefresh = null;
317
321
  refreshWorker = null;
318
322
  refreshCompanyUid = undefined;
323
+ refreshHasCompanyContext = false;
319
324
  refreshPendingSnapshot = null;
320
325
  refreshSentry = undefined;
321
326
  }
package/dist/main.js CHANGED
@@ -41,16 +41,17 @@ import { DEFAULT_COGNITO, isMachineIdentity, loadMachineCachedTokens, } from "./
41
41
  import { isCompanySelectionError } from "./utils/company-selection-error.js";
42
42
  import { canOfferTeamUpgrade, formatPlanGateError, offerTeamUpgrade, offerUpgradeLink, planGateErrorFromVaultError, } from "./utils/plan-gate-error.js";
43
43
  import { upgradeToTeam } from "./utils/team-upgrade.js";
44
- import { printVersionAndNudge, refreshVersionCache, staleAgainstCachedLatest, } from "./utils/version-check.js";
45
- import { enforceVersionGate, shouldSkipGate, } from "./utils/version-gate.js";
44
+ import { printCachedVersionNudge, printVersionAndNudge, refreshVersionCache, staleAgainstCachedLatest, } from "./utils/version-check.js";
45
+ import { enforceVersionGate, isLocalDependencyInstall, resolveRunningInstall, shouldSkipGate, } from "./utils/version-gate.js";
46
46
  import { autoUpdateAndReexec } from "./utils/self-update.js";
47
47
  import { CLI_VERSION } from "./cli-version.js";
48
48
  import { findLazyCommand, registerCommandCatalog } from "./lazy-commands.js";
49
49
  import { emitCliFirstSyncNextStepOutcome, emitCliLimitNoticeEngagement, emitCliLimitNoticeExposure, emitCliInviteNudgeEvent, emitCliSessionStarted, emitPlanLimitInviteTeammateExposure, isCliLimitResourceKind, queueCliLimitNoticeEngagement, queueCliLimitNoticeExposure, takeFirstSyncStepOutcomes, takeCliLimitNoticeEngagements, takeCliLimitNoticeExposures, takeCliInviteNudgeEvents, takePlanLimitInviteTeammateExposures, maybeShowFirstSyncNextStep, } from "./utils/cli-telemetry.js";
50
50
  import { reportCliClientHealthInvocation } from "./utils/client-health.js";
51
51
  import { settleWithin } from "./utils/settle-with-timeout.js";
52
+ import { isVersionRequest } from "./version-request.js";
52
53
  import { emitPlanLimitNag } from "./lib/plan-limit-nag.js";
53
- import { kickFlagRegistryReadiness } from "./lib/flag-registry.js";
54
+ import { kickFlagRegistryReadiness, waitForFlagRegistryRefresh, } from "./lib/flag-registry.js";
54
55
  import { isPackageRootResolutionError, packageRootCaptureContext, } from "./utils/package-root-diagnostics.js";
55
56
  import { registerCommandsWithRecovery } from "./startup-registration.js";
56
57
  import { releasePackageUseLeaseAfterCommandLoad } from "./utils/package-use-lease.js";
@@ -185,6 +186,8 @@ export async function runCli() {
185
186
  // out (after the finally block's telemetry, hence not process.exit here).
186
187
  let reexecStatus = null;
187
188
  let versionNudgeHandled = false;
189
+ let versionUpdateHandled = false;
190
+ let commandParsed = false;
188
191
  try {
189
192
  runGlobalNetworkLifecycle(process.argv, () => Sentry.addBreadcrumb({
190
193
  category: "command",
@@ -209,6 +212,9 @@ export async function runCli() {
209
212
  return "continue";
210
213
  const gate = (await foregroundNetworkBudget.waitFor(VERSION_GATE_FOREGROUND_WAIT_MS, FINALIZATION_FOREGROUND_WAIT_MS, (timeoutMs) => enforceVersionGate(async (decision) => {
211
214
  const outcome = await autoUpdateAndReexec(process.argv, decision.latestVersion);
215
+ if (outcome.action === "update-failed" || outcome.action === "updated-no-reexec") {
216
+ versionUpdateHandled = true;
217
+ }
212
218
  if (outcome.action === "reexec")
213
219
  reexecStatus = outcome.reexecStatus ?? 0;
214
220
  return outcome.action === "reexec";
@@ -218,6 +224,9 @@ export async function runCli() {
218
224
  const cachedLatest = staleAgainstCachedLatest();
219
225
  if (cachedLatest) {
220
226
  const outcome = await autoUpdateAndReexec(process.argv, cachedLatest);
227
+ if (outcome.action === "update-failed" || outcome.action === "updated-no-reexec") {
228
+ versionUpdateHandled = true;
229
+ }
221
230
  if (outcome.action === "reexec") {
222
231
  reexecStatus = outcome.reexecStatus ?? 0;
223
232
  return "reexec";
@@ -264,6 +273,7 @@ export async function runCli() {
264
273
  }
265
274
  await releasePackageUseLeaseAfterCommandLoad(process.argv);
266
275
  await program.parseAsync();
276
+ commandParsed = true;
267
277
  maybeShowFirstSyncNextStep();
268
278
  }
269
279
  catch (err) {
@@ -287,10 +297,26 @@ export async function runCli() {
287
297
  const inviteTeammateExposureFlushes = takePlanLimitInviteTeammateExposures().map((companyUid) => emitPlanLimitInviteTeammateExposure(companyUid, releaseHealthTimeoutMs));
288
298
  const limitNoticeExposureFlushes = takeCliLimitNoticeExposures().map((exposure) => emitCliLimitNoticeExposure(exposure, releaseHealthTimeoutMs));
289
299
  const limitNoticeEngagementFlushes = takeCliLimitNoticeEngagements().map((engagement) => emitCliLimitNoticeEngagement(engagement, releaseHealthTimeoutMs));
300
+ const commandArgs = process.argv.slice(2);
301
+ const canShowCachedVersionNudge = commandParsed &&
302
+ !versionNudgeHandled &&
303
+ !versionUpdateHandled &&
304
+ process.stdout.isTTY === true &&
305
+ !commandArgs.includes("--json") &&
306
+ !isVersionRequest(process.argv) &&
307
+ !shouldSkipGate(process.argv) &&
308
+ !isLocalDependencyInstall(resolveRunningInstall());
309
+ const cachedVersionNudge = canShowCachedVersionNudge && staleAgainstCachedLatest() !== null
310
+ ? (async () => {
311
+ await waitForFlagRegistryRefresh(releaseHealthTimeoutMs);
312
+ await printCachedVersionNudge(process.stderr);
313
+ })()
314
+ : undefined;
290
315
  await settleWithin([
291
316
  ...(versionNudgeHandled
292
317
  ? []
293
318
  : [refreshVersionCache({ timeoutMs: releaseHealthTimeoutMs })]),
319
+ ...(cachedVersionNudge ? [cachedVersionNudge] : []),
294
320
  Sentry.flush(releaseHealthTimeoutMs),
295
321
  ...firstSyncOutcomeFlushes,
296
322
  ...inviteNudgeOutcomeFlushes,
@@ -1,4 +1,4 @@
1
- export type ResidentPackagePath = "bot" | "mcp" | "daemon" | "childWrapper" | "coreJobRun";
1
+ export type ResidentPackagePath = "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon";
2
2
  /** Package imports reachable lazily from each resident command's runtime graph. */
3
3
  export declare const RESIDENT_PACKAGE_MODULES: Record<ResidentPackagePath, readonly string[]>;
4
4
  export declare class ResidentPackagePreloadError extends Error {
@@ -1,5 +1,23 @@
1
1
  /** Package imports reachable lazily from each resident command's runtime graph. */
2
2
  export const RESIDENT_PACKAGE_MODULES = {
3
+ login: [
4
+ "../lib/mesh/api.js",
5
+ "../lib/work-context/config.js",
6
+ "../lib/work-context/default-company.js",
7
+ "../lib/work-context/paths.js",
8
+ "../lib/plan-limit-nag.js",
9
+ "../utils/plan-gate-error.js",
10
+ "../sentry.js",
11
+ "open",
12
+ ],
13
+ "auth-login": [
14
+ "../lib/auth-login-provider-state.js",
15
+ "../lib/daemon/mcp-oauth.js",
16
+ "../lib/plan-limit-nag.js",
17
+ "../utils/plan-gate-error.js",
18
+ "../sentry.js",
19
+ "open",
20
+ ],
3
21
  bot: [
4
22
  "../lib/flag-registry.js",
5
23
  "../commands/access.js",
@@ -24,6 +42,30 @@ export const RESIDENT_PACKAGE_MODULES = {
24
42
  ],
25
43
  };
26
44
  const LOADERS = {
45
+ login: [
46
+ () => import("../lib/mesh/api.js"),
47
+ () => import("../lib/work-context/config.js"),
48
+ () => import("../lib/work-context/default-company.js"),
49
+ () => import("../lib/work-context/paths.js"),
50
+ () => import("../lib/plan-limit-nag.js"),
51
+ () => import("../utils/plan-gate-error.js"),
52
+ async () => {
53
+ const { preloadSentrySdk } = await import("../sentry.js");
54
+ preloadSentrySdk();
55
+ },
56
+ () => import("open"),
57
+ ],
58
+ "auth-login": [
59
+ () => import("../lib/auth-login-provider-state.js"),
60
+ () => import("../lib/daemon/mcp-oauth.js"),
61
+ () => import("../lib/plan-limit-nag.js"),
62
+ () => import("../utils/plan-gate-error.js"),
63
+ async () => {
64
+ const { preloadSentrySdk } = await import("../sentry.js");
65
+ preloadSentrySdk();
66
+ },
67
+ () => import("open"),
68
+ ],
27
69
  bot: [
28
70
  () => import("../lib/flag-registry.js"),
29
71
  () => import("../commands/access.js"),
@@ -37,6 +37,7 @@ import { fileURLToPath } from "node:url";
37
37
  import chalk from "chalk";
38
38
  import semver from "semver";
39
39
  import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
40
+ import { isVersionRequest } from "../version-request.js";
40
41
  import { Sentry } from "../sentry.js";
41
42
  import { isCliTelemetryDisabled } from "./cli-telemetry.js";
42
43
  import { DEFAULT_VAULT_API_URL } from "./cognito-session.js";
@@ -315,11 +316,6 @@ export function isLocalDependencyPackageDir(pkgDir, platform = process.platform)
315
316
  // `<dir>/lib/node_modules` while the `.bin` shim keeps resolving
316
317
  // `<dir>/node_modules`.
317
318
  //
318
- // Windows is the exception and must keep the old behaviour: its global
319
- // layout is `<prefix>\node_modules` with no `lib` segment, so the same test
320
- // would misread a genuine global install as local.
321
- if (platform === "win32")
322
- return false;
323
319
  const segments = pkgDir
324
320
  .replace(/\\/g, "/")
325
321
  .replace(/\/+$/, "")
@@ -328,6 +324,23 @@ export function isLocalDependencyPackageDir(pkgDir, platform = process.platform)
328
324
  const nodeModulesIndex = segments.lastIndexOf("node_modules");
329
325
  if (nodeModulesIndex <= 0)
330
326
  return false;
327
+ if (platform === "win32") {
328
+ const prefix = segments.slice(0, nodeModulesIndex).join("/");
329
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
330
+ const configuredPrefix = process.env.npm_config_prefix ?? process.env.NPM_CONFIG_PREFIX;
331
+ if (configuredPrefix && normalize(configuredPrefix) === normalize(prefix))
332
+ return false;
333
+ const appData = process.env.APPDATA;
334
+ const defaultPrefix = appData
335
+ ? path.join(appData, "npm")
336
+ : path.join(os.homedir(), "AppData", "Roaming", "npm");
337
+ if (normalize(defaultPrefix) === normalize(prefix))
338
+ return false;
339
+ const pathEntries = (process.env.PATH ?? "").split(";");
340
+ if (pathEntries.some((entry) => normalize(entry) === normalize(prefix)))
341
+ return false;
342
+ return true;
343
+ }
331
344
  return segments[nodeModulesIndex - 1] !== "lib";
332
345
  }
333
346
  export function isLocalDependencyInstall(install, platform = process.platform) {
@@ -1588,12 +1601,11 @@ export async function enforceVersionGate(onUpdateRecommended, options = {}) {
1588
1601
  * force-upgraded.
1589
1602
  */
1590
1603
  export function shouldSkipGate(argv) {
1591
- return argv.some((a) => a === "--version" ||
1604
+ return isVersionRequest(argv) || argv.some((a) => a === "--version" ||
1592
1605
  a === "-V" ||
1593
1606
  a === "-v" ||
1594
1607
  a === "--help" ||
1595
- a === "-h" ||
1596
- a === "version");
1608
+ a === "-h");
1597
1609
  }
1598
1610
  export const __test__ = {
1599
1611
  CLIENT_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.342.10",
3
+ "version": "5.342.11",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {