antigravity-auth 1.6.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/README.md +61 -0
- package/dist/antigravity/oauth.d.ts +30 -0
- package/dist/antigravity/oauth.js +170 -0
- package/dist/claude/login.d.ts +7 -0
- package/dist/claude/login.js +480 -0
- package/dist/claude/menu-helpers.d.ts +22 -0
- package/dist/claude/menu-helpers.js +281 -0
- package/dist/claude/proxy-manager.d.ts +11 -0
- package/dist/claude/proxy-manager.js +129 -0
- package/dist/claude/proxy.d.ts +1 -0
- package/dist/claude/proxy.js +733 -0
- package/dist/constants.d.ts +138 -0
- package/dist/constants.js +216 -0
- package/dist/hooks/auto-update-checker/cache.d.ts +2 -0
- package/dist/hooks/auto-update-checker/cache.js +70 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +15 -0
- package/dist/hooks/auto-update-checker/checker.js +233 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +8 -0
- package/dist/hooks/auto-update-checker/constants.js +22 -0
- package/dist/hooks/auto-update-checker/index.d.ts +33 -0
- package/dist/hooks/auto-update-checker/index.js +121 -0
- package/dist/hooks/auto-update-checker/logging.d.ts +2 -0
- package/dist/hooks/auto-update-checker/logging.js +8 -0
- package/dist/hooks/auto-update-checker/types.d.ts +24 -0
- package/dist/hooks/auto-update-checker/types.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/opencode/hooks/auto-update-checker/cache.d.ts +2 -0
- package/dist/opencode/hooks/auto-update-checker/cache.js +70 -0
- package/dist/opencode/hooks/auto-update-checker/checker.d.ts +15 -0
- package/dist/opencode/hooks/auto-update-checker/checker.js +233 -0
- package/dist/opencode/hooks/auto-update-checker/constants.d.ts +8 -0
- package/dist/opencode/hooks/auto-update-checker/constants.js +22 -0
- package/dist/opencode/hooks/auto-update-checker/index.d.ts +33 -0
- package/dist/opencode/hooks/auto-update-checker/index.js +121 -0
- package/dist/opencode/hooks/auto-update-checker/logging.d.ts +2 -0
- package/dist/opencode/hooks/auto-update-checker/logging.js +8 -0
- package/dist/opencode/hooks/auto-update-checker/types.d.ts +24 -0
- package/dist/opencode/hooks/auto-update-checker/types.js +1 -0
- package/dist/opencode/plugin.d.ts +29 -0
- package/dist/opencode/plugin.js +2954 -0
- package/dist/plugin/accounts.d.ts +173 -0
- package/dist/plugin/accounts.js +966 -0
- package/dist/plugin/auth.d.ts +20 -0
- package/dist/plugin/auth.js +44 -0
- package/dist/plugin/cache/index.d.ts +4 -0
- package/dist/plugin/cache/index.js +4 -0
- package/dist/plugin/cache/signature-cache.d.ts +110 -0
- package/dist/plugin/cache/signature-cache.js +347 -0
- package/dist/plugin/cache.d.ts +43 -0
- package/dist/plugin/cache.js +180 -0
- package/dist/plugin/cli.d.ts +26 -0
- package/dist/plugin/cli.js +126 -0
- package/dist/plugin/config/index.d.ts +15 -0
- package/dist/plugin/config/index.js +15 -0
- package/dist/plugin/config/loader.d.ts +38 -0
- package/dist/plugin/config/loader.js +150 -0
- package/dist/plugin/config/models.d.ts +26 -0
- package/dist/plugin/config/models.js +95 -0
- package/dist/plugin/config/schema.d.ts +144 -0
- package/dist/plugin/config/schema.js +458 -0
- package/dist/plugin/config/updater.d.ts +76 -0
- package/dist/plugin/config/updater.js +205 -0
- package/dist/plugin/core/streaming/index.d.ts +2 -0
- package/dist/plugin/core/streaming/index.js +2 -0
- package/dist/plugin/core/streaming/transformer.d.ts +9 -0
- package/dist/plugin/core/streaming/transformer.js +301 -0
- package/dist/plugin/core/streaming/types.d.ts +28 -0
- package/dist/plugin/core/streaming/types.js +1 -0
- package/dist/plugin/debug.d.ts +93 -0
- package/dist/plugin/debug.js +375 -0
- package/dist/plugin/errors.d.ts +27 -0
- package/dist/plugin/errors.js +41 -0
- package/dist/plugin/fingerprint.d.ts +69 -0
- package/dist/plugin/fingerprint.js +137 -0
- package/dist/plugin/image-saver.d.ts +24 -0
- package/dist/plugin/image-saver.js +78 -0
- package/dist/plugin/logger.d.ts +35 -0
- package/dist/plugin/logger.js +67 -0
- package/dist/plugin/logging-utils.d.ts +22 -0
- package/dist/plugin/logging-utils.js +91 -0
- package/dist/plugin/project.d.ts +32 -0
- package/dist/plugin/project.js +229 -0
- package/dist/plugin/quota.d.ts +34 -0
- package/dist/plugin/quota.js +261 -0
- package/dist/plugin/recovery/constants.d.ts +21 -0
- package/dist/plugin/recovery/constants.js +42 -0
- package/dist/plugin/recovery/index.d.ts +11 -0
- package/dist/plugin/recovery/index.js +11 -0
- package/dist/plugin/recovery/storage.d.ts +23 -0
- package/dist/plugin/recovery/storage.js +340 -0
- package/dist/plugin/recovery/types.d.ts +115 -0
- package/dist/plugin/recovery/types.js +6 -0
- package/dist/plugin/recovery.d.ts +60 -0
- package/dist/plugin/recovery.js +360 -0
- package/dist/plugin/refresh-queue.d.ts +99 -0
- package/dist/plugin/refresh-queue.js +235 -0
- package/dist/plugin/request-helpers.d.ts +281 -0
- package/dist/plugin/request-helpers.js +2200 -0
- package/dist/plugin/request.d.ts +110 -0
- package/dist/plugin/request.js +1489 -0
- package/dist/plugin/rotation.d.ts +182 -0
- package/dist/plugin/rotation.js +364 -0
- package/dist/plugin/search.d.ts +31 -0
- package/dist/plugin/search.js +185 -0
- package/dist/plugin/server.d.ts +22 -0
- package/dist/plugin/server.js +306 -0
- package/dist/plugin/storage.d.ts +136 -0
- package/dist/plugin/storage.js +599 -0
- package/dist/plugin/stores/signature-store.d.ts +4 -0
- package/dist/plugin/stores/signature-store.js +24 -0
- package/dist/plugin/thinking-recovery.d.ts +89 -0
- package/dist/plugin/thinking-recovery.js +289 -0
- package/dist/plugin/token.d.ts +18 -0
- package/dist/plugin/token.js +127 -0
- package/dist/plugin/transform/claude.d.ts +79 -0
- package/dist/plugin/transform/claude.js +256 -0
- package/dist/plugin/transform/cross-model-sanitizer.d.ts +34 -0
- package/dist/plugin/transform/cross-model-sanitizer.js +224 -0
- package/dist/plugin/transform/gemini.d.ts +132 -0
- package/dist/plugin/transform/gemini.js +659 -0
- package/dist/plugin/transform/index.d.ts +14 -0
- package/dist/plugin/transform/index.js +9 -0
- package/dist/plugin/transform/model-resolver.d.ts +98 -0
- package/dist/plugin/transform/model-resolver.js +320 -0
- package/dist/plugin/transform/types.d.ts +110 -0
- package/dist/plugin/transform/types.js +1 -0
- package/dist/plugin/types.d.ts +95 -0
- package/dist/plugin/types.js +1 -0
- package/dist/plugin/ui/ansi.d.ts +31 -0
- package/dist/plugin/ui/ansi.js +45 -0
- package/dist/plugin/ui/auth-menu.d.ts +47 -0
- package/dist/plugin/ui/auth-menu.js +199 -0
- package/dist/plugin/ui/confirm.d.ts +1 -0
- package/dist/plugin/ui/confirm.js +14 -0
- package/dist/plugin/ui/select.d.ts +22 -0
- package/dist/plugin/ui/select.js +243 -0
- package/dist/plugin/version.d.ts +18 -0
- package/dist/plugin/version.js +79 -0
- package/dist/src/antigravity/oauth.d.ts +30 -0
- package/dist/src/antigravity/oauth.js +170 -0
- package/dist/src/constants.d.ts +138 -0
- package/dist/src/constants.js +216 -0
- package/dist/src/hooks/auto-update-checker/cache.d.ts +2 -0
- package/dist/src/hooks/auto-update-checker/cache.js +70 -0
- package/dist/src/hooks/auto-update-checker/checker.d.ts +15 -0
- package/dist/src/hooks/auto-update-checker/checker.js +233 -0
- package/dist/src/hooks/auto-update-checker/constants.d.ts +8 -0
- package/dist/src/hooks/auto-update-checker/constants.js +22 -0
- package/dist/src/hooks/auto-update-checker/index.d.ts +33 -0
- package/dist/src/hooks/auto-update-checker/index.js +121 -0
- package/dist/src/hooks/auto-update-checker/logging.d.ts +2 -0
- package/dist/src/hooks/auto-update-checker/logging.js +8 -0
- package/dist/src/hooks/auto-update-checker/types.d.ts +24 -0
- package/dist/src/hooks/auto-update-checker/types.js +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +5 -0
- package/dist/src/plugin/accounts.d.ts +173 -0
- package/dist/src/plugin/accounts.js +966 -0
- package/dist/src/plugin/auth.d.ts +20 -0
- package/dist/src/plugin/auth.js +44 -0
- package/dist/src/plugin/cache/index.d.ts +4 -0
- package/dist/src/plugin/cache/index.js +4 -0
- package/dist/src/plugin/cache/signature-cache.d.ts +110 -0
- package/dist/src/plugin/cache/signature-cache.js +347 -0
- package/dist/src/plugin/cache.d.ts +43 -0
- package/dist/src/plugin/cache.js +180 -0
- package/dist/src/plugin/cli.d.ts +26 -0
- package/dist/src/plugin/cli.js +126 -0
- package/dist/src/plugin/config/index.d.ts +15 -0
- package/dist/src/plugin/config/index.js +15 -0
- package/dist/src/plugin/config/loader.d.ts +38 -0
- package/dist/src/plugin/config/loader.js +150 -0
- package/dist/src/plugin/config/models.d.ts +26 -0
- package/dist/src/plugin/config/models.js +95 -0
- package/dist/src/plugin/config/schema.d.ts +144 -0
- package/dist/src/plugin/config/schema.js +458 -0
- package/dist/src/plugin/config/updater.d.ts +76 -0
- package/dist/src/plugin/config/updater.js +205 -0
- package/dist/src/plugin/core/streaming/index.d.ts +2 -0
- package/dist/src/plugin/core/streaming/index.js +2 -0
- package/dist/src/plugin/core/streaming/transformer.d.ts +9 -0
- package/dist/src/plugin/core/streaming/transformer.js +301 -0
- package/dist/src/plugin/core/streaming/types.d.ts +28 -0
- package/dist/src/plugin/core/streaming/types.js +1 -0
- package/dist/src/plugin/debug.d.ts +93 -0
- package/dist/src/plugin/debug.js +375 -0
- package/dist/src/plugin/errors.d.ts +27 -0
- package/dist/src/plugin/errors.js +41 -0
- package/dist/src/plugin/fingerprint.d.ts +69 -0
- package/dist/src/plugin/fingerprint.js +137 -0
- package/dist/src/plugin/image-saver.d.ts +24 -0
- package/dist/src/plugin/image-saver.js +78 -0
- package/dist/src/plugin/logger.d.ts +35 -0
- package/dist/src/plugin/logger.js +67 -0
- package/dist/src/plugin/logging-utils.d.ts +22 -0
- package/dist/src/plugin/logging-utils.js +91 -0
- package/dist/src/plugin/project.d.ts +32 -0
- package/dist/src/plugin/project.js +229 -0
- package/dist/src/plugin/quota.d.ts +34 -0
- package/dist/src/plugin/quota.js +261 -0
- package/dist/src/plugin/recovery/constants.d.ts +21 -0
- package/dist/src/plugin/recovery/constants.js +42 -0
- package/dist/src/plugin/recovery/index.d.ts +11 -0
- package/dist/src/plugin/recovery/index.js +11 -0
- package/dist/src/plugin/recovery/storage.d.ts +23 -0
- package/dist/src/plugin/recovery/storage.js +340 -0
- package/dist/src/plugin/recovery/types.d.ts +115 -0
- package/dist/src/plugin/recovery/types.js +6 -0
- package/dist/src/plugin/recovery.d.ts +60 -0
- package/dist/src/plugin/recovery.js +360 -0
- package/dist/src/plugin/refresh-queue.d.ts +99 -0
- package/dist/src/plugin/refresh-queue.js +235 -0
- package/dist/src/plugin/request-helpers.d.ts +281 -0
- package/dist/src/plugin/request-helpers.js +2200 -0
- package/dist/src/plugin/request.d.ts +110 -0
- package/dist/src/plugin/request.js +1489 -0
- package/dist/src/plugin/rotation.d.ts +182 -0
- package/dist/src/plugin/rotation.js +364 -0
- package/dist/src/plugin/search.d.ts +31 -0
- package/dist/src/plugin/search.js +185 -0
- package/dist/src/plugin/server.d.ts +22 -0
- package/dist/src/plugin/server.js +306 -0
- package/dist/src/plugin/storage.d.ts +136 -0
- package/dist/src/plugin/storage.js +599 -0
- package/dist/src/plugin/stores/signature-store.d.ts +4 -0
- package/dist/src/plugin/stores/signature-store.js +24 -0
- package/dist/src/plugin/thinking-recovery.d.ts +89 -0
- package/dist/src/plugin/thinking-recovery.js +289 -0
- package/dist/src/plugin/token.d.ts +18 -0
- package/dist/src/plugin/token.js +127 -0
- package/dist/src/plugin/transform/claude.d.ts +79 -0
- package/dist/src/plugin/transform/claude.js +256 -0
- package/dist/src/plugin/transform/cross-model-sanitizer.d.ts +34 -0
- package/dist/src/plugin/transform/cross-model-sanitizer.js +224 -0
- package/dist/src/plugin/transform/gemini.d.ts +132 -0
- package/dist/src/plugin/transform/gemini.js +659 -0
- package/dist/src/plugin/transform/index.d.ts +14 -0
- package/dist/src/plugin/transform/index.js +9 -0
- package/dist/src/plugin/transform/model-resolver.d.ts +98 -0
- package/dist/src/plugin/transform/model-resolver.js +320 -0
- package/dist/src/plugin/transform/types.d.ts +110 -0
- package/dist/src/plugin/transform/types.js +1 -0
- package/dist/src/plugin/types.d.ts +95 -0
- package/dist/src/plugin/types.js +1 -0
- package/dist/src/plugin/ui/ansi.d.ts +31 -0
- package/dist/src/plugin/ui/ansi.js +45 -0
- package/dist/src/plugin/ui/auth-menu.d.ts +47 -0
- package/dist/src/plugin/ui/auth-menu.js +199 -0
- package/dist/src/plugin/ui/confirm.d.ts +1 -0
- package/dist/src/plugin/ui/confirm.js +14 -0
- package/dist/src/plugin/ui/select.d.ts +22 -0
- package/dist/src/plugin/ui/select.js +243 -0
- package/dist/src/plugin/version.d.ts +18 -0
- package/dist/src/plugin/version.js +79 -0
- package/package.json +54 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session recovery hook for handling recoverable errors.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - tool_result_missing: When ESC is pressed during tool execution
|
|
6
|
+
* - thinking_block_order: When thinking blocks are corrupted/stripped
|
|
7
|
+
* - thinking_disabled_violation: Thinking in non-thinking model
|
|
8
|
+
*
|
|
9
|
+
* Based on oh-my-opencode/src/hooks/session-recovery/index.ts
|
|
10
|
+
*/
|
|
11
|
+
import { createLogger } from "./logger";
|
|
12
|
+
import { logToast } from "./debug";
|
|
13
|
+
import { readParts, findMessagesWithThinkingBlocks, findMessagesWithOrphanThinking, findMessageByIndexNeedingThinking, prependThinkingPart, stripThinkingParts, } from "./recovery/storage";
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// =============================================================================
|
|
16
|
+
const RECOVERY_RESUME_TEXT = "[session recovered - continuing previous task]";
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// =============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Extract a normalized error message string from an unknown error.
|
|
21
|
+
*/
|
|
22
|
+
function getErrorMessage(error) {
|
|
23
|
+
if (!error)
|
|
24
|
+
return "";
|
|
25
|
+
if (typeof error === "string")
|
|
26
|
+
return error.toLowerCase();
|
|
27
|
+
const errorObj = error;
|
|
28
|
+
const paths = [
|
|
29
|
+
errorObj.data,
|
|
30
|
+
errorObj.error,
|
|
31
|
+
errorObj,
|
|
32
|
+
errorObj.data?.error,
|
|
33
|
+
];
|
|
34
|
+
for (const obj of paths) {
|
|
35
|
+
if (obj && typeof obj === "object") {
|
|
36
|
+
const msg = obj.message;
|
|
37
|
+
if (typeof msg === "string" && msg.length > 0) {
|
|
38
|
+
return msg.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return JSON.stringify(error).toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract the message index from an error message (e.g., "messages.79").
|
|
51
|
+
*/
|
|
52
|
+
function extractMessageIndex(error) {
|
|
53
|
+
const message = getErrorMessage(error);
|
|
54
|
+
const match = message.match(/messages\.(\d+)/);
|
|
55
|
+
if (!match || !match[1])
|
|
56
|
+
return null;
|
|
57
|
+
return parseInt(match[1], 10);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Detect the type of recoverable error from an error object.
|
|
61
|
+
*/
|
|
62
|
+
export function detectErrorType(error) {
|
|
63
|
+
const message = getErrorMessage(error);
|
|
64
|
+
const hasExpectedFoundThinkingOrder = (message.includes("expected thinking") || message.includes("expected a thinking")) &&
|
|
65
|
+
message.includes("found");
|
|
66
|
+
// tool_result_missing: Happens when ESC is pressed during tool execution
|
|
67
|
+
if (message.includes("tool_use") && message.includes("tool_result")) {
|
|
68
|
+
return "tool_result_missing";
|
|
69
|
+
}
|
|
70
|
+
// thinking_block_order: Happens when thinking blocks are corrupted
|
|
71
|
+
if (message.includes("thinking") &&
|
|
72
|
+
(message.includes("first block") ||
|
|
73
|
+
message.includes("must start with") ||
|
|
74
|
+
message.includes("preceeding") ||
|
|
75
|
+
message.includes("preceding") ||
|
|
76
|
+
hasExpectedFoundThinkingOrder)) {
|
|
77
|
+
return "thinking_block_order";
|
|
78
|
+
}
|
|
79
|
+
// thinking_disabled_violation: Thinking in non-thinking model
|
|
80
|
+
if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
|
|
81
|
+
return "thinking_disabled_violation";
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if an error is recoverable.
|
|
87
|
+
*/
|
|
88
|
+
export function isRecoverableError(error) {
|
|
89
|
+
return detectErrorType(error) !== null;
|
|
90
|
+
}
|
|
91
|
+
function extractToolUseIds(parts) {
|
|
92
|
+
return parts
|
|
93
|
+
.filter((p) => p.type === "tool_use" && !!p.id)
|
|
94
|
+
.map((p) => p.id);
|
|
95
|
+
}
|
|
96
|
+
// =============================================================================
|
|
97
|
+
// =============================================================================
|
|
98
|
+
/**
|
|
99
|
+
* Recover from tool_result_missing error by injecting synthetic tool_result blocks.
|
|
100
|
+
*/
|
|
101
|
+
async function recoverToolResultMissing(client, sessionID, failedMsg) {
|
|
102
|
+
let parts = failedMsg.parts || [];
|
|
103
|
+
if (parts.length === 0 && failedMsg.info?.id) {
|
|
104
|
+
const storedParts = readParts(failedMsg.info.id);
|
|
105
|
+
parts = storedParts.map((p) => ({
|
|
106
|
+
type: p.type === "tool" ? "tool_use" : p.type,
|
|
107
|
+
id: "callID" in p ? p.callID : p.id,
|
|
108
|
+
name: "tool" in p ? p.tool : undefined,
|
|
109
|
+
input: "state" in p ? p.state?.input : undefined,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
const toolUseIds = extractToolUseIds(parts);
|
|
113
|
+
if (toolUseIds.length === 0) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const toolResultParts = toolUseIds.map((id) => ({
|
|
117
|
+
type: "tool_result",
|
|
118
|
+
tool_use_id: id,
|
|
119
|
+
content: "Operation cancelled by user (ESC pressed)",
|
|
120
|
+
}));
|
|
121
|
+
try {
|
|
122
|
+
await client.session.prompt({
|
|
123
|
+
path: { id: sessionID },
|
|
124
|
+
// @ts-expect-error - SDK types may not include tool_result parts
|
|
125
|
+
body: { parts: toolResultParts },
|
|
126
|
+
});
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Recover from thinking_block_order error by prepending thinking parts.
|
|
135
|
+
*/
|
|
136
|
+
async function recoverThinkingBlockOrder(sessionID, _failedMsg, error) {
|
|
137
|
+
const targetIndex = extractMessageIndex(error);
|
|
138
|
+
if (targetIndex !== null) {
|
|
139
|
+
const targetMessageID = findMessageByIndexNeedingThinking(sessionID, targetIndex);
|
|
140
|
+
if (targetMessageID) {
|
|
141
|
+
return prependThinkingPart(sessionID, targetMessageID);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const orphanMessages = findMessagesWithOrphanThinking(sessionID);
|
|
145
|
+
if (orphanMessages.length === 0) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
let anySuccess = false;
|
|
149
|
+
for (const messageID of orphanMessages) {
|
|
150
|
+
if (prependThinkingPart(sessionID, messageID)) {
|
|
151
|
+
anySuccess = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return anySuccess;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Recover from thinking_disabled_violation by stripping thinking parts.
|
|
158
|
+
*/
|
|
159
|
+
async function recoverThinkingDisabledViolation(sessionID, _failedMsg) {
|
|
160
|
+
const messagesWithThinking = findMessagesWithThinkingBlocks(sessionID);
|
|
161
|
+
if (messagesWithThinking.length === 0) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
let anySuccess = false;
|
|
165
|
+
for (const messageID of messagesWithThinking) {
|
|
166
|
+
if (stripThinkingParts(messageID)) {
|
|
167
|
+
anySuccess = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return anySuccess;
|
|
171
|
+
}
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// =============================================================================
|
|
174
|
+
function findLastUserMessage(messages) {
|
|
175
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
176
|
+
if (messages[i]?.info?.role === "user") {
|
|
177
|
+
return messages[i];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
function extractResumeConfig(userMessage, sessionID) {
|
|
183
|
+
return {
|
|
184
|
+
sessionID,
|
|
185
|
+
agent: userMessage?.info?.agent,
|
|
186
|
+
model: userMessage?.info?.model,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async function resumeSession(client, config, directory) {
|
|
190
|
+
try {
|
|
191
|
+
await client.session.prompt({
|
|
192
|
+
path: { id: config.sessionID },
|
|
193
|
+
body: {
|
|
194
|
+
parts: [{ type: "text", text: RECOVERY_RESUME_TEXT }],
|
|
195
|
+
agent: config.agent,
|
|
196
|
+
model: config.model,
|
|
197
|
+
},
|
|
198
|
+
query: { directory },
|
|
199
|
+
});
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// =============================================================================
|
|
208
|
+
const TOAST_TITLES = {
|
|
209
|
+
tool_result_missing: "Tool Crash Recovery",
|
|
210
|
+
thinking_block_order: "Thinking Block Recovery",
|
|
211
|
+
thinking_disabled_violation: "Thinking Strip Recovery",
|
|
212
|
+
};
|
|
213
|
+
const TOAST_MESSAGES = {
|
|
214
|
+
tool_result_missing: "Injecting cancelled tool results...",
|
|
215
|
+
thinking_block_order: "Fixing message structure...",
|
|
216
|
+
thinking_disabled_violation: "Stripping thinking blocks...",
|
|
217
|
+
};
|
|
218
|
+
export function getRecoveryToastContent(errorType) {
|
|
219
|
+
if (!errorType) {
|
|
220
|
+
return {
|
|
221
|
+
title: "Session Recovery",
|
|
222
|
+
message: "Attempting to recover session...",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
title: TOAST_TITLES[errorType] || "Session Recovery",
|
|
227
|
+
message: TOAST_MESSAGES[errorType] || "Attempting to recover session...",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export function getRecoverySuccessToast() {
|
|
231
|
+
return {
|
|
232
|
+
title: "Session Recovered",
|
|
233
|
+
message: "Continuing where you left off...",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
export function getRecoveryFailureToast() {
|
|
237
|
+
return {
|
|
238
|
+
title: "Recovery Failed",
|
|
239
|
+
message: "Please retry or start a new session.",
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Create a session recovery hook with the given configuration.
|
|
244
|
+
*/
|
|
245
|
+
export function createSessionRecoveryHook(ctx, config) {
|
|
246
|
+
if (!config.session_recovery) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const { client, directory } = ctx;
|
|
250
|
+
const processingErrors = new Set();
|
|
251
|
+
let onAbortCallback = null;
|
|
252
|
+
let onRecoveryCompleteCallback = null;
|
|
253
|
+
const setOnAbortCallback = (callback) => {
|
|
254
|
+
onAbortCallback = callback;
|
|
255
|
+
};
|
|
256
|
+
const setOnRecoveryCompleteCallback = (callback) => {
|
|
257
|
+
onRecoveryCompleteCallback = callback;
|
|
258
|
+
};
|
|
259
|
+
const handleSessionRecovery = async (info) => {
|
|
260
|
+
if (!info || info.role !== "assistant" || !info.error)
|
|
261
|
+
return false;
|
|
262
|
+
const errorType = detectErrorType(info.error);
|
|
263
|
+
if (!errorType)
|
|
264
|
+
return false;
|
|
265
|
+
const sessionID = info.sessionID;
|
|
266
|
+
if (!sessionID)
|
|
267
|
+
return false;
|
|
268
|
+
let assistantMsgID = info.id;
|
|
269
|
+
let msgs;
|
|
270
|
+
const log = createLogger("session-recovery");
|
|
271
|
+
log.debug("Recovery attempt started", {
|
|
272
|
+
errorType,
|
|
273
|
+
sessionID,
|
|
274
|
+
providedMsgID: assistantMsgID ?? "none",
|
|
275
|
+
});
|
|
276
|
+
if (onAbortCallback) {
|
|
277
|
+
onAbortCallback(sessionID);
|
|
278
|
+
}
|
|
279
|
+
await client.session.abort({ path: { id: sessionID } }).catch(() => { });
|
|
280
|
+
const messagesResp = await client.session.messages({
|
|
281
|
+
path: { id: sessionID },
|
|
282
|
+
query: { directory },
|
|
283
|
+
});
|
|
284
|
+
msgs = messagesResp.data;
|
|
285
|
+
if (!assistantMsgID && msgs && msgs.length > 0) {
|
|
286
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
287
|
+
const m = msgs[i];
|
|
288
|
+
if (m && m.info?.role === "assistant" && m.info?.id) {
|
|
289
|
+
assistantMsgID = m.info.id;
|
|
290
|
+
log.debug("Found assistant message ID from session messages", {
|
|
291
|
+
msgID: assistantMsgID,
|
|
292
|
+
msgIndex: i,
|
|
293
|
+
});
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!assistantMsgID) {
|
|
299
|
+
log.debug("No assistant message ID found, cannot recover");
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (processingErrors.has(assistantMsgID))
|
|
303
|
+
return false;
|
|
304
|
+
processingErrors.add(assistantMsgID);
|
|
305
|
+
try {
|
|
306
|
+
const failedMsg = msgs?.find((m) => m.info?.id === assistantMsgID);
|
|
307
|
+
if (!failedMsg) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
const toastContent = getRecoveryToastContent(errorType);
|
|
311
|
+
logToast(`${toastContent.title}: ${toastContent.message}`, "warning");
|
|
312
|
+
await client.tui
|
|
313
|
+
.showToast({
|
|
314
|
+
body: {
|
|
315
|
+
title: toastContent.title,
|
|
316
|
+
message: toastContent.message,
|
|
317
|
+
variant: "warning",
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
.catch(() => { });
|
|
321
|
+
let success = false;
|
|
322
|
+
if (errorType === "tool_result_missing") {
|
|
323
|
+
success = await recoverToolResultMissing(client, sessionID, failedMsg);
|
|
324
|
+
}
|
|
325
|
+
else if (errorType === "thinking_block_order") {
|
|
326
|
+
success = await recoverThinkingBlockOrder(sessionID, failedMsg, info.error);
|
|
327
|
+
if (success && config.auto_resume) {
|
|
328
|
+
const lastUser = findLastUserMessage(msgs ?? []);
|
|
329
|
+
const resumeConfig = extractResumeConfig(lastUser, sessionID);
|
|
330
|
+
await resumeSession(client, resumeConfig, directory);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else if (errorType === "thinking_disabled_violation") {
|
|
334
|
+
success = await recoverThinkingDisabledViolation(sessionID, failedMsg);
|
|
335
|
+
if (success && config.auto_resume) {
|
|
336
|
+
const lastUser = findLastUserMessage(msgs ?? []);
|
|
337
|
+
const resumeConfig = extractResumeConfig(lastUser, sessionID);
|
|
338
|
+
await resumeSession(client, resumeConfig, directory);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return success;
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
log.error("Recovery failed", { error: String(err) });
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
processingErrors.delete(assistantMsgID);
|
|
349
|
+
if (sessionID && onRecoveryCompleteCallback) {
|
|
350
|
+
onRecoveryCompleteCallback(sessionID);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
return {
|
|
355
|
+
handleSessionRecovery,
|
|
356
|
+
isRecoverableError,
|
|
357
|
+
setOnAbortCallback,
|
|
358
|
+
setOnRecoveryCompleteCallback,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive Token Refresh Queue
|
|
3
|
+
*
|
|
4
|
+
* Ported from LLM-API-Key-Proxy's BackgroundRefresher.
|
|
5
|
+
*
|
|
6
|
+
* This module provides background token refresh to ensure OAuth tokens
|
|
7
|
+
* remain valid without blocking user requests. It periodically checks
|
|
8
|
+
* all accounts and refreshes tokens that are approaching expiry.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Non-blocking background refresh (doesn't block requests)
|
|
12
|
+
* - Configurable refresh buffer (default: 30 minutes before expiry)
|
|
13
|
+
* - Configurable check interval (default: 5 minutes)
|
|
14
|
+
* - Serialized refresh to prevent concurrent refresh storms
|
|
15
|
+
* - Integrates with existing AccountManager and token refresh logic
|
|
16
|
+
* - Silent operation: no console output, uses structured logger
|
|
17
|
+
*/
|
|
18
|
+
import type { AccountManager, ManagedAccount } from "./accounts";
|
|
19
|
+
import type { PluginClient } from "./types";
|
|
20
|
+
/** Configuration for the proactive refresh queue */
|
|
21
|
+
export interface ProactiveRefreshConfig {
|
|
22
|
+
/** Enable proactive token refresh (default: true) */
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
/** Seconds before expiry to trigger proactive refresh (default: 1800 = 30 minutes) */
|
|
25
|
+
bufferSeconds: number;
|
|
26
|
+
/** Interval between refresh checks in seconds (default: 300 = 5 minutes) */
|
|
27
|
+
checkIntervalSeconds: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const DEFAULT_PROACTIVE_REFRESH_CONFIG: ProactiveRefreshConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Proactive Token Refresh Queue
|
|
32
|
+
*
|
|
33
|
+
* Runs in the background and proactively refreshes tokens before they expire.
|
|
34
|
+
* This ensures that user requests never block on token refresh.
|
|
35
|
+
*
|
|
36
|
+
* All logging is silent by default - uses structured logger with TUI integration.
|
|
37
|
+
*/
|
|
38
|
+
export declare class ProactiveRefreshQueue {
|
|
39
|
+
private readonly config;
|
|
40
|
+
private readonly client;
|
|
41
|
+
private readonly providerId;
|
|
42
|
+
private accountManager;
|
|
43
|
+
private state;
|
|
44
|
+
constructor(client: PluginClient, providerId: string, config?: Partial<ProactiveRefreshConfig>);
|
|
45
|
+
/**
|
|
46
|
+
* Set the account manager to use for refresh operations.
|
|
47
|
+
* Must be called before start().
|
|
48
|
+
*/
|
|
49
|
+
setAccountManager(manager: AccountManager): void;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a token needs proactive refresh.
|
|
52
|
+
* Returns true if the token expires within the buffer period.
|
|
53
|
+
*/
|
|
54
|
+
needsRefresh(account: ManagedAccount): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a token is already expired.
|
|
57
|
+
*/
|
|
58
|
+
isExpired(account: ManagedAccount): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get all accounts that need proactive refresh.
|
|
61
|
+
*/
|
|
62
|
+
getAccountsNeedingRefresh(): ManagedAccount[];
|
|
63
|
+
/**
|
|
64
|
+
* Perform a single refresh check iteration.
|
|
65
|
+
* This is called periodically by the background interval.
|
|
66
|
+
*/
|
|
67
|
+
private runRefreshCheck;
|
|
68
|
+
/**
|
|
69
|
+
* Refresh a single token.
|
|
70
|
+
*/
|
|
71
|
+
private refreshToken;
|
|
72
|
+
/**
|
|
73
|
+
* Start the background refresh queue.
|
|
74
|
+
*/
|
|
75
|
+
start(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Stop the background refresh queue.
|
|
78
|
+
*/
|
|
79
|
+
stop(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Get current queue statistics.
|
|
82
|
+
*/
|
|
83
|
+
getStats(): {
|
|
84
|
+
isRunning: boolean;
|
|
85
|
+
isRefreshing: boolean;
|
|
86
|
+
lastCheckTime: number;
|
|
87
|
+
lastRefreshTime: number;
|
|
88
|
+
refreshCount: number;
|
|
89
|
+
errorCount: number;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Check if the queue is currently running.
|
|
93
|
+
*/
|
|
94
|
+
isRunning(): boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create a proactive refresh queue instance.
|
|
98
|
+
*/
|
|
99
|
+
export declare function createProactiveRefreshQueue(client: PluginClient, providerId: string, config?: Partial<ProactiveRefreshConfig>): ProactiveRefreshQueue;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive Token Refresh Queue
|
|
3
|
+
*
|
|
4
|
+
* Ported from LLM-API-Key-Proxy's BackgroundRefresher.
|
|
5
|
+
*
|
|
6
|
+
* This module provides background token refresh to ensure OAuth tokens
|
|
7
|
+
* remain valid without blocking user requests. It periodically checks
|
|
8
|
+
* all accounts and refreshes tokens that are approaching expiry.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Non-blocking background refresh (doesn't block requests)
|
|
12
|
+
* - Configurable refresh buffer (default: 30 minutes before expiry)
|
|
13
|
+
* - Configurable check interval (default: 5 minutes)
|
|
14
|
+
* - Serialized refresh to prevent concurrent refresh storms
|
|
15
|
+
* - Integrates with existing AccountManager and token refresh logic
|
|
16
|
+
* - Silent operation: no console output, uses structured logger
|
|
17
|
+
*/
|
|
18
|
+
import { refreshAccessToken } from "./token";
|
|
19
|
+
import { createLogger } from "./logger";
|
|
20
|
+
const log = createLogger("refresh-queue");
|
|
21
|
+
export const DEFAULT_PROACTIVE_REFRESH_CONFIG = {
|
|
22
|
+
enabled: true,
|
|
23
|
+
bufferSeconds: 1800, // 30 minutes
|
|
24
|
+
checkIntervalSeconds: 300, // 5 minutes
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Proactive Token Refresh Queue
|
|
28
|
+
*
|
|
29
|
+
* Runs in the background and proactively refreshes tokens before they expire.
|
|
30
|
+
* This ensures that user requests never block on token refresh.
|
|
31
|
+
*
|
|
32
|
+
* All logging is silent by default - uses structured logger with TUI integration.
|
|
33
|
+
*/
|
|
34
|
+
export class ProactiveRefreshQueue {
|
|
35
|
+
config;
|
|
36
|
+
client;
|
|
37
|
+
providerId;
|
|
38
|
+
accountManager = null;
|
|
39
|
+
state = {
|
|
40
|
+
isRunning: false,
|
|
41
|
+
intervalHandle: null,
|
|
42
|
+
isRefreshing: false,
|
|
43
|
+
lastCheckTime: 0,
|
|
44
|
+
lastRefreshTime: 0,
|
|
45
|
+
refreshCount: 0,
|
|
46
|
+
errorCount: 0,
|
|
47
|
+
};
|
|
48
|
+
constructor(client, providerId, config) {
|
|
49
|
+
this.client = client;
|
|
50
|
+
this.providerId = providerId;
|
|
51
|
+
this.config = {
|
|
52
|
+
...DEFAULT_PROACTIVE_REFRESH_CONFIG,
|
|
53
|
+
...config,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Set the account manager to use for refresh operations.
|
|
58
|
+
* Must be called before start().
|
|
59
|
+
*/
|
|
60
|
+
setAccountManager(manager) {
|
|
61
|
+
this.accountManager = manager;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a token needs proactive refresh.
|
|
65
|
+
* Returns true if the token expires within the buffer period.
|
|
66
|
+
*/
|
|
67
|
+
needsRefresh(account) {
|
|
68
|
+
if (!account.expires) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const bufferMs = this.config.bufferSeconds * 1000;
|
|
73
|
+
const refreshThreshold = now + bufferMs;
|
|
74
|
+
return account.expires <= refreshThreshold;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a token is already expired.
|
|
78
|
+
*/
|
|
79
|
+
isExpired(account) {
|
|
80
|
+
if (!account.expires) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return account.expires <= Date.now();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get all accounts that need proactive refresh.
|
|
87
|
+
*/
|
|
88
|
+
getAccountsNeedingRefresh() {
|
|
89
|
+
if (!this.accountManager) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return this.accountManager.getAccounts().filter((account) => {
|
|
93
|
+
if (account.enabled === false) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (this.isExpired(account)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return this.needsRefresh(account);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Perform a single refresh check iteration.
|
|
104
|
+
* This is called periodically by the background interval.
|
|
105
|
+
*/
|
|
106
|
+
async runRefreshCheck() {
|
|
107
|
+
if (this.state.isRefreshing) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!this.accountManager) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.state.isRefreshing = true;
|
|
114
|
+
this.state.lastCheckTime = Date.now();
|
|
115
|
+
try {
|
|
116
|
+
const accountsToRefresh = this.getAccountsNeedingRefresh();
|
|
117
|
+
if (accountsToRefresh.length === 0) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
log.debug("Found accounts needing refresh", { count: accountsToRefresh.length });
|
|
121
|
+
for (const account of accountsToRefresh) {
|
|
122
|
+
if (!this.state.isRunning) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const auth = this.accountManager.toAuthDetails(account);
|
|
127
|
+
const refreshed = await this.refreshToken(auth, account);
|
|
128
|
+
if (refreshed) {
|
|
129
|
+
this.accountManager.updateFromAuth(account, refreshed);
|
|
130
|
+
this.state.refreshCount++;
|
|
131
|
+
this.state.lastRefreshTime = Date.now();
|
|
132
|
+
try {
|
|
133
|
+
await this.accountManager.saveToDisk();
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
this.state.errorCount++;
|
|
141
|
+
log.warn("Failed to refresh account", {
|
|
142
|
+
accountIndex: account.index,
|
|
143
|
+
error: error instanceof Error ? error.message : String(error),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
this.state.isRefreshing = false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Refresh a single token.
|
|
154
|
+
*/
|
|
155
|
+
async refreshToken(auth, account) {
|
|
156
|
+
const minutesUntilExpiry = account.expires
|
|
157
|
+
? Math.round((account.expires - Date.now()) / 60000)
|
|
158
|
+
: "unknown";
|
|
159
|
+
log.debug("Proactively refreshing token", {
|
|
160
|
+
accountIndex: account.index,
|
|
161
|
+
email: account.email ?? "unknown",
|
|
162
|
+
minutesUntilExpiry,
|
|
163
|
+
});
|
|
164
|
+
return refreshAccessToken(auth, this.client, this.providerId);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Start the background refresh queue.
|
|
168
|
+
*/
|
|
169
|
+
start() {
|
|
170
|
+
if (this.state.isRunning) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!this.config.enabled) {
|
|
174
|
+
log.debug("Proactive refresh disabled by config");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.state.isRunning = true;
|
|
178
|
+
const intervalMs = this.config.checkIntervalSeconds * 1000;
|
|
179
|
+
log.debug("Started proactive refresh queue", {
|
|
180
|
+
checkIntervalSeconds: this.config.checkIntervalSeconds,
|
|
181
|
+
bufferSeconds: this.config.bufferSeconds,
|
|
182
|
+
});
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
if (this.state.isRunning) {
|
|
185
|
+
this.runRefreshCheck().catch((error) => {
|
|
186
|
+
log.error("Initial check failed", {
|
|
187
|
+
error: error instanceof Error ? error.message : String(error),
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}, 5000);
|
|
192
|
+
this.state.intervalHandle = setInterval(() => {
|
|
193
|
+
this.runRefreshCheck().catch((error) => {
|
|
194
|
+
log.error("Check failed", {
|
|
195
|
+
error: error instanceof Error ? error.message : String(error),
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}, intervalMs);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Stop the background refresh queue.
|
|
202
|
+
*/
|
|
203
|
+
stop() {
|
|
204
|
+
if (!this.state.isRunning) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.state.isRunning = false;
|
|
208
|
+
if (this.state.intervalHandle) {
|
|
209
|
+
clearInterval(this.state.intervalHandle);
|
|
210
|
+
this.state.intervalHandle = null;
|
|
211
|
+
}
|
|
212
|
+
log.debug("Stopped proactive refresh queue", {
|
|
213
|
+
refreshCount: this.state.refreshCount,
|
|
214
|
+
errorCount: this.state.errorCount,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get current queue statistics.
|
|
219
|
+
*/
|
|
220
|
+
getStats() {
|
|
221
|
+
return { ...this.state };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Check if the queue is currently running.
|
|
225
|
+
*/
|
|
226
|
+
isRunning() {
|
|
227
|
+
return this.state.isRunning;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Create a proactive refresh queue instance.
|
|
232
|
+
*/
|
|
233
|
+
export function createProactiveRefreshQueue(client, providerId, config) {
|
|
234
|
+
return new ProactiveRefreshQueue(client, providerId, config);
|
|
235
|
+
}
|