@indigoai-us/hq-cli 5.335.0 → 5.335.1-beta.0
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 +4 -0
- package/dist/utils/cognito-session.js +77 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.335.1-beta.0] — 2026-10-03
|
|
6
|
+
- Client-health refresh failures now retain the bounded Cognito OAuth error class for accurate auth diagnostics.
|
|
7
|
+
- CI: Run main-push Linux ARM checks on the reserved self-hosted runner pool.
|
|
8
|
+
|
|
5
9
|
## [5.335.0] — 2026-10-03
|
|
6
10
|
- Keep expected invalid-refresh-token events countable at warning level and tag each rejection with its allowlisted Cognito OAuth error class.
|
|
7
11
|
- Keep the intentional HQ Anywhere flag-off refusal for `hq mcp login` out of Sentry while preserving its message and exit code.
|
|
@@ -531,6 +531,73 @@ function throwIfAborted(signal) {
|
|
|
531
531
|
if (signal.aborted)
|
|
532
532
|
throw signal.reason ?? new Error("Token acquisition was aborted");
|
|
533
533
|
}
|
|
534
|
+
const COGNITO_REFRESH_OAUTH_ERROR_CLASSES = new Set([
|
|
535
|
+
"invalid_grant",
|
|
536
|
+
"invalid_client",
|
|
537
|
+
"unauthorized_client",
|
|
538
|
+
"invalid_request",
|
|
539
|
+
"invalid_scope",
|
|
540
|
+
"NotAuthorizedException",
|
|
541
|
+
]);
|
|
542
|
+
const MAX_COGNITO_REFRESH_ERROR_BODY_BYTES = 4_096;
|
|
543
|
+
function classifyCognitoRefreshOAuthError(text) {
|
|
544
|
+
try {
|
|
545
|
+
const payload = JSON.parse(text);
|
|
546
|
+
if (!payload || typeof payload !== "object" || !("error" in payload))
|
|
547
|
+
return "unknown";
|
|
548
|
+
const oauthError = payload.error;
|
|
549
|
+
if (typeof oauthError !== "string")
|
|
550
|
+
return "unknown";
|
|
551
|
+
return COGNITO_REFRESH_OAUTH_ERROR_CLASSES.has(oauthError)
|
|
552
|
+
? oauthError
|
|
553
|
+
: "other";
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
return "unknown";
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function readCognitoRefreshOAuthError(response, signal) {
|
|
560
|
+
throwIfAborted(signal);
|
|
561
|
+
const reader = response.body?.getReader();
|
|
562
|
+
if (!reader)
|
|
563
|
+
return "unknown";
|
|
564
|
+
const decoder = new TextDecoder();
|
|
565
|
+
let text = "";
|
|
566
|
+
let bytesRead = 0;
|
|
567
|
+
let bodyComplete = false;
|
|
568
|
+
const cancelRead = () => {
|
|
569
|
+
void reader.cancel(signal.reason ?? new Error("Token acquisition was aborted")).catch(() => undefined);
|
|
570
|
+
};
|
|
571
|
+
signal.addEventListener("abort", cancelRead, { once: true });
|
|
572
|
+
try {
|
|
573
|
+
while (bytesRead < MAX_COGNITO_REFRESH_ERROR_BODY_BYTES) {
|
|
574
|
+
throwIfAborted(signal);
|
|
575
|
+
const chunk = await reader.read();
|
|
576
|
+
throwIfAborted(signal);
|
|
577
|
+
if (chunk.done) {
|
|
578
|
+
bodyComplete = true;
|
|
579
|
+
text += decoder.decode();
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
const boundedChunk = chunk.value.subarray(0, MAX_COGNITO_REFRESH_ERROR_BODY_BYTES - bytesRead);
|
|
583
|
+
bytesRead += boundedChunk.byteLength;
|
|
584
|
+
text += decoder.decode(boundedChunk, { stream: true });
|
|
585
|
+
}
|
|
586
|
+
throwIfAborted(signal);
|
|
587
|
+
text += decoder.decode();
|
|
588
|
+
return classifyCognitoRefreshOAuthError(text);
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
throwIfAborted(signal);
|
|
592
|
+
return "unknown";
|
|
593
|
+
}
|
|
594
|
+
finally {
|
|
595
|
+
signal.removeEventListener("abort", cancelRead);
|
|
596
|
+
if (!bodyComplete)
|
|
597
|
+
cancelRead();
|
|
598
|
+
reader.releaseLock();
|
|
599
|
+
}
|
|
600
|
+
}
|
|
534
601
|
function waitForRefreshLock(signal, delayMs) {
|
|
535
602
|
return new Promise((resolve, reject) => {
|
|
536
603
|
const finish = (error) => {
|
|
@@ -681,7 +748,16 @@ export async function refreshActivePersonTokensForClientHealth(cached, signal) {
|
|
|
681
748
|
continue;
|
|
682
749
|
const reauth = response.status === 401 ||
|
|
683
750
|
(response.status >= 400 && response.status < 500 && ![408, 429].includes(response.status));
|
|
684
|
-
|
|
751
|
+
const oauthError = reauth
|
|
752
|
+
? await readCognitoRefreshOAuthError(response, signal)
|
|
753
|
+
: "unknown";
|
|
754
|
+
const error = new CognitoRefreshError(`Cognito refresh failed (${response.status})`, reauth, response.status, fingerprint);
|
|
755
|
+
Object.defineProperty(error, "oauthError", {
|
|
756
|
+
configurable: true,
|
|
757
|
+
enumerable: true,
|
|
758
|
+
value: oauthError,
|
|
759
|
+
});
|
|
760
|
+
throw error;
|
|
685
761
|
}
|
|
686
762
|
if (!response?.ok) {
|
|
687
763
|
throw new CognitoRefreshError("Cognito refresh failed after retry", false, undefined, fingerprint);
|