@splashcodex/api-key-manager 2.0.0 → 4.0.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 +175 -38
- package/dist/index.d.ts +122 -5
- package/dist/index.js +355 -22
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/src/index.ts +485 -30
package/README.md
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
# @splashcodex/
|
|
1
|
+
# @splashcodex/ApiKeyManager v4.0 — Mastermind Edition
|
|
2
2
|
|
|
3
|
-
> Universal API Key Rotation System with Load Balancing
|
|
3
|
+
> Universal API Key Rotation System with Resilience, Load Balancing, Semantic Caching & AI Gateway Features
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@splashcodex/ApiKeyManager)
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Circuit Breaker** — Keys transition through `CLOSED → OPEN → HALF_OPEN → DEAD`
|
|
10
|
-
- **Error Classification** — Automatic detection of 429 (Quota), 403 (Auth), 5xx (Transient), Safety blocks
|
|
10
|
+
- **Error Classification** — Automatic detection of 429 (Quota), 403 (Auth), 5xx (Transient), Timeout, Safety blocks
|
|
11
11
|
- **Pluggable Strategies** — `StandardStrategy`, `WeightedStrategy`, `LatencyStrategy`
|
|
12
|
-
-
|
|
13
|
-
- **
|
|
12
|
+
- **`execute()` Wrapper** — Single method: get key → call → latency → retry → fallback
|
|
13
|
+
- **Event Emitter** — Typed lifecycle hooks for monitoring & alerting
|
|
14
|
+
- **Auto-Retry with Backoff** — Built-in retry loop with exponential backoff + jitter
|
|
15
|
+
- **Request Timeout** — `AbortController`-based timeout per attempt
|
|
16
|
+
- **Fallback Function** — Graceful degradation when all keys fail
|
|
17
|
+
- **Provider Tagging** — Multi-provider routing (`openai`, `gemini`, etc.)
|
|
18
|
+
- **Health Checks** — Periodic key validation and auto-recovery
|
|
19
|
+
- **Bulkhead / Concurrency** — Limits concurrent `execute()` calls
|
|
20
|
+
- **Semantic Cache** *(v4 NEW)* — Cosine-similarity cache with pluggable embeddings
|
|
21
|
+
- **Recursion Guard** *(v4 NEW)* — Prevents infinite loops when `getEmbedding` calls `execute()`
|
|
14
22
|
- **State Persistence** — Survives restarts via pluggable storage
|
|
15
|
-
- **
|
|
16
|
-
- **100% Backward Compatible** — v1.x code works without changes
|
|
23
|
+
- **100% Backward Compatible** — v1.x, v2.x, and v3.x code works without changes
|
|
17
24
|
|
|
18
25
|
## Installation
|
|
19
26
|
|
|
@@ -21,61 +28,151 @@
|
|
|
21
28
|
npm install @splashcodex/api-key-manager
|
|
22
29
|
```
|
|
23
30
|
|
|
24
|
-
## Quick Start
|
|
31
|
+
## Quick Start
|
|
25
32
|
|
|
26
33
|
```typescript
|
|
27
34
|
import { ApiKeyManager } from '@splashcodex/api-key-manager';
|
|
28
35
|
|
|
36
|
+
// Simple (v1/v2 compatible)
|
|
29
37
|
const manager = new ApiKeyManager(['key1', 'key2', 'key3']);
|
|
38
|
+
const key = manager.getKey();
|
|
39
|
+
manager.markSuccess(key!);
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
manager.
|
|
41
|
+
// v3+ — Full power
|
|
42
|
+
const result = await manager.execute(
|
|
43
|
+
(key) => fetch(`https://api.example.com?key=${key}`),
|
|
44
|
+
{ maxRetries: 3, timeoutMs: 5000 }
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## v4.0 — Semantic Cache (Mastermind Edition)
|
|
49
|
+
|
|
50
|
+
Automatically cache API responses by semantic similarity. Identical or near-identical prompts return cached results without consuming API quota.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { ApiKeyManager } from '@splashcodex/api-key-manager';
|
|
54
|
+
|
|
55
|
+
const manager = new ApiKeyManager(['key1', 'key2'], {
|
|
56
|
+
semanticCache: {
|
|
57
|
+
threshold: 0.92, // 92% cosine similarity to match
|
|
58
|
+
ttlMs: 86400000, // 24-hour TTL (default)
|
|
59
|
+
getEmbedding: async (text) => {
|
|
60
|
+
// Your embedding function (e.g. OpenAI, Gemini, local model)
|
|
61
|
+
return await myEmbeddingModel.embed(text);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// First call → API hit, cached
|
|
67
|
+
const r1 = await manager.execute(apiFn, { prompt: 'What is the weather?' });
|
|
68
|
+
|
|
69
|
+
// Second call → Semantic Cache HIT (no API call)
|
|
70
|
+
const r2 = await manager.execute(apiFn, { prompt: 'How is the weather today?' });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> **Recursion Guard**: If your `getEmbedding` callback internally calls `execute()`,
|
|
74
|
+
> the cache automatically skips on nested calls to prevent infinite recursion.
|
|
75
|
+
|
|
76
|
+
## execute() Wrapper
|
|
77
|
+
|
|
78
|
+
Wraps the entire lifecycle into one method:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const manager = new ApiKeyManager(keys, {
|
|
82
|
+
storage: localStorage,
|
|
83
|
+
strategy: new WeightedStrategy(),
|
|
84
|
+
fallbackFn: () => cachedResponse,
|
|
85
|
+
concurrency: 10
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const result = await manager.execute(
|
|
89
|
+
async (key, signal) => {
|
|
90
|
+
const res = await fetch(url, { headers: { 'x-api-key': key }, signal });
|
|
91
|
+
return res.json();
|
|
92
|
+
},
|
|
93
|
+
{ maxRetries: 3, timeoutMs: 10000 }
|
|
94
|
+
);
|
|
95
|
+
// Handles: key selection → cache → timeout → retry → fallback → latency tracking
|
|
33
96
|
```
|
|
34
97
|
|
|
35
|
-
##
|
|
98
|
+
## Event Emitter
|
|
99
|
+
|
|
100
|
+
Monitor every state change:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
manager.on('keyDead', (key) => alertTeam(`Key ${key} permanently dead`));
|
|
104
|
+
manager.on('circuitOpen', (key) => metrics.increment('circuit_opens'));
|
|
105
|
+
manager.on('keyRecovered', (key) => log(`Key ${key} recovered`));
|
|
106
|
+
manager.on('retry', (key, attempt, delay) => log(`Retry #${attempt} in ${delay}ms`));
|
|
107
|
+
manager.on('fallback', (reason) => log(`Fallback triggered: ${reason}`));
|
|
108
|
+
manager.on('allKeysExhausted', () => alert('No healthy keys!'));
|
|
109
|
+
manager.on('bulkheadRejected', () => metrics.increment('rejected'));
|
|
110
|
+
manager.on('healthCheckPassed', (key) => log(`${key} healthy`));
|
|
111
|
+
manager.on('healthCheckFailed', (key, err) => log(`${key} unhealthy`));
|
|
112
|
+
```
|
|
36
113
|
|
|
37
|
-
|
|
114
|
+
## Load Balancing Strategies
|
|
38
115
|
|
|
39
|
-
|
|
116
|
+
### Weighted (Cost Optimization)
|
|
40
117
|
|
|
41
118
|
```typescript
|
|
42
119
|
import { ApiKeyManager, WeightedStrategy } from '@splashcodex/api-key-manager';
|
|
43
120
|
|
|
44
121
|
const manager = new ApiKeyManager(
|
|
45
122
|
[
|
|
46
|
-
{ key: 'free-
|
|
47
|
-
{ key: 'free-
|
|
48
|
-
{ key: 'paid-
|
|
123
|
+
{ key: 'free-key-1', weight: 1.0 },
|
|
124
|
+
{ key: 'free-key-2', weight: 1.0 },
|
|
125
|
+
{ key: 'paid-backup', weight: 0.1 },
|
|
49
126
|
],
|
|
50
|
-
|
|
51
|
-
new WeightedStrategy()
|
|
127
|
+
{ strategy: new WeightedStrategy() }
|
|
52
128
|
);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Latency (Performance)
|
|
53
132
|
|
|
54
|
-
|
|
133
|
+
```typescript
|
|
134
|
+
import { ApiKeyManager, LatencyStrategy } from '@splashcodex/api-key-manager';
|
|
135
|
+
|
|
136
|
+
const manager = new ApiKeyManager(keys, { strategy: new LatencyStrategy() });
|
|
137
|
+
// After execute(), latency is tracked automatically
|
|
55
138
|
```
|
|
56
139
|
|
|
57
|
-
|
|
140
|
+
## Provider Tagging
|
|
58
141
|
|
|
59
|
-
|
|
142
|
+
Route requests to specific providers:
|
|
60
143
|
|
|
61
144
|
```typescript
|
|
62
|
-
|
|
145
|
+
const manager = new ApiKeyManager([
|
|
146
|
+
{ key: 'sk-openai-1', weight: 1.0, provider: 'openai' },
|
|
147
|
+
{ key: 'sk-openai-2', weight: 1.0, provider: 'openai' },
|
|
148
|
+
{ key: 'AIza-gemini', weight: 0.5, provider: 'gemini' },
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
const openaiKey = manager.getKeyByProvider('openai');
|
|
152
|
+
const geminiKey = manager.getKeyByProvider('gemini');
|
|
153
|
+
```
|
|
63
154
|
|
|
64
|
-
|
|
155
|
+
## Health Checks
|
|
65
156
|
|
|
66
|
-
|
|
67
|
-
const start = Date.now();
|
|
68
|
-
await callApi(key);
|
|
69
|
-
manager.markSuccess(key, Date.now() - start); // Records latency
|
|
157
|
+
Proactively detect recovered keys:
|
|
70
158
|
|
|
71
|
-
|
|
159
|
+
```typescript
|
|
160
|
+
manager.setHealthCheck(async (key) => {
|
|
161
|
+
const res = await fetch(`https://api.openai.com/v1/models`, {
|
|
162
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
163
|
+
});
|
|
164
|
+
return res.ok;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
manager.startHealthChecks(60_000); // Check every 60 seconds
|
|
168
|
+
// manager.stopHealthChecks(); // Stop when done
|
|
72
169
|
```
|
|
73
170
|
|
|
74
|
-
|
|
171
|
+
## Error Handling
|
|
75
172
|
|
|
76
173
|
```typescript
|
|
77
174
|
try {
|
|
78
|
-
const result = await
|
|
175
|
+
const result = await callApi(key);
|
|
79
176
|
manager.markSuccess(key, duration);
|
|
80
177
|
} catch (error) {
|
|
81
178
|
const classification = manager.classifyError(error);
|
|
@@ -84,31 +181,71 @@ try {
|
|
|
84
181
|
if (classification.retryable) {
|
|
85
182
|
const delay = manager.calculateBackoff(attempt);
|
|
86
183
|
await sleep(delay);
|
|
87
|
-
// retry with manager.getKey()
|
|
88
184
|
}
|
|
89
185
|
}
|
|
90
186
|
```
|
|
91
187
|
|
|
92
|
-
|
|
188
|
+
## API Reference
|
|
189
|
+
|
|
190
|
+
### Constructor
|
|
93
191
|
|
|
94
192
|
```typescript
|
|
95
|
-
|
|
96
|
-
|
|
193
|
+
// Legacy (v1/v2)
|
|
194
|
+
new ApiKeyManager(keys, storage?, strategy?)
|
|
195
|
+
|
|
196
|
+
// v3+ Options
|
|
197
|
+
new ApiKeyManager(keys, {
|
|
198
|
+
storage?, // Pluggable storage { getItem, setItem }
|
|
199
|
+
strategy?, // LoadBalancingStrategy instance
|
|
200
|
+
fallbackFn?, // () => any — called when all keys exhausted
|
|
201
|
+
concurrency?, // Max concurrent execute() calls
|
|
202
|
+
semanticCache?, // v4: { threshold, ttlMs, getEmbedding }
|
|
203
|
+
})
|
|
97
204
|
```
|
|
98
205
|
|
|
99
|
-
|
|
206
|
+
### Methods
|
|
100
207
|
|
|
101
208
|
| Method | Description |
|
|
102
209
|
|--------|-------------|
|
|
103
210
|
| `getKey()` | Returns best available key via strategy |
|
|
211
|
+
| `getKeyByProvider(provider)` | Get key filtered by provider tag |
|
|
104
212
|
| `markSuccess(key, durationMs?)` | Report success + optional latency |
|
|
105
213
|
| `markFailed(key, classification)` | Report failure with error type |
|
|
106
214
|
| `classifyError(error, finishReason?)` | Classify an error automatically |
|
|
215
|
+
| `execute(fn, options?)` | Full lifecycle wrapper with retry/timeout |
|
|
107
216
|
| `calculateBackoff(attempt)` | Get backoff delay with jitter |
|
|
108
217
|
| `getStats()` | Get pool health statistics |
|
|
109
218
|
| `getKeyCount()` | Count of non-DEAD keys |
|
|
110
|
-
|
|
111
|
-
|
|
219
|
+
| `setHealthCheck(fn)` | Set health check function |
|
|
220
|
+
| `startHealthChecks(ms)` | Start periodic health checks |
|
|
221
|
+
| `stopHealthChecks()` | Stop health checks |
|
|
222
|
+
|
|
223
|
+
### Events
|
|
224
|
+
|
|
225
|
+
| Event | Payload | Trigger |
|
|
226
|
+
|-------|---------|---------|
|
|
227
|
+
| `keyDead` | `key: string` | Key marked as permanently dead |
|
|
228
|
+
| `circuitOpen` | `key: string` | Key circuit opened (cooldown) |
|
|
229
|
+
| `circuitHalfOpen` | `key: string` | Key entering test phase |
|
|
230
|
+
| `keyRecovered` | `key: string` | Key recovered from failure |
|
|
231
|
+
| `fallback` | `reason: string` | Fallback function invoked |
|
|
232
|
+
| `allKeysExhausted` | — | All keys dead, no fallback |
|
|
233
|
+
| `retry` | `key, attempt, delayMs` | Retry attempt starting |
|
|
234
|
+
| `executeSuccess` | `key, durationMs` | execute() completed successfully |
|
|
235
|
+
| `executeFailed` | `key, error` | execute() attempt failed |
|
|
236
|
+
| `bulkheadRejected` | — | Concurrency limit reached |
|
|
237
|
+
| `healthCheckPassed` | `key: string` | Health check succeeded |
|
|
238
|
+
| `healthCheckFailed` | `key, error` | Health check failed |
|
|
239
|
+
|
|
240
|
+
### Custom Errors
|
|
241
|
+
|
|
242
|
+
| Error | When |
|
|
243
|
+
|-------|------|
|
|
244
|
+
| `TimeoutError` | Request exceeded `timeoutMs` |
|
|
245
|
+
| `BulkheadRejectionError` | Concurrency limit exceeded |
|
|
246
|
+
| `AllKeysExhaustedError` | All keys dead, no fallback |
|
|
247
|
+
|
|
248
|
+
### Strategies
|
|
112
249
|
|
|
113
250
|
| Strategy | Algorithm | Best For |
|
|
114
251
|
|----------|-----------|----------|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Universal ApiKeyManager
|
|
3
|
-
* Implements: Rotation, Circuit Breaker, Persistence, Exponential Backoff, Strategies
|
|
2
|
+
* Universal ApiKeyManager v4.0 — Mastermind Edition
|
|
3
|
+
* Implements: Rotation, Circuit Breaker, Persistence, Exponential Backoff, Strategies,
|
|
4
|
+
* Event Emitter, Fallback, execute(), Timeout, Auto-Retry, Provider Tags,
|
|
5
|
+
* Health Checks, Bulkhead/Concurrency
|
|
4
6
|
* Gemini-Specific: finishReason handling, Safety blocks, RECITATION detection
|
|
5
7
|
*/
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
6
9
|
export interface KeyState {
|
|
7
10
|
key: string;
|
|
8
11
|
failCount: number;
|
|
@@ -18,8 +21,9 @@ export interface KeyState {
|
|
|
18
21
|
averageLatency: number;
|
|
19
22
|
totalLatency: number;
|
|
20
23
|
latencySamples: number;
|
|
24
|
+
provider: string;
|
|
21
25
|
}
|
|
22
|
-
export type ErrorType = 'QUOTA' | 'TRANSIENT' | 'AUTH' | 'BAD_REQUEST' | 'SAFETY' | 'RECITATION' | 'UNKNOWN';
|
|
26
|
+
export type ErrorType = 'QUOTA' | 'TRANSIENT' | 'AUTH' | 'BAD_REQUEST' | 'SAFETY' | 'RECITATION' | 'TIMEOUT' | 'UNKNOWN';
|
|
23
27
|
export interface ErrorClassification {
|
|
24
28
|
type: ErrorType;
|
|
25
29
|
retryable: boolean;
|
|
@@ -33,6 +37,52 @@ export interface ApiKeyManagerStats {
|
|
|
33
37
|
cooling: number;
|
|
34
38
|
dead: number;
|
|
35
39
|
}
|
|
40
|
+
export interface ExecuteOptions {
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
maxRetries?: number;
|
|
43
|
+
finishReason?: string;
|
|
44
|
+
provider?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface ApiKeyManagerOptions {
|
|
47
|
+
storage?: any;
|
|
48
|
+
strategy?: LoadBalancingStrategy;
|
|
49
|
+
fallbackFn?: () => any;
|
|
50
|
+
concurrency?: number;
|
|
51
|
+
semanticCache?: {
|
|
52
|
+
threshold?: number;
|
|
53
|
+
ttlMs?: number;
|
|
54
|
+
getEmbedding: (text: string) => Promise<number[]>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface CacheEntry {
|
|
58
|
+
vector: number[];
|
|
59
|
+
prompt: string;
|
|
60
|
+
response: any;
|
|
61
|
+
timestamp: number;
|
|
62
|
+
}
|
|
63
|
+
export interface ApiKeyManagerEventMap {
|
|
64
|
+
keyDead: (key: string) => void;
|
|
65
|
+
circuitOpen: (key: string) => void;
|
|
66
|
+
circuitHalfOpen: (key: string) => void;
|
|
67
|
+
keyRecovered: (key: string) => void;
|
|
68
|
+
fallback: (reason: string) => void;
|
|
69
|
+
allKeysExhausted: () => void;
|
|
70
|
+
retry: (key: string, attempt: number, delayMs: number) => void;
|
|
71
|
+
healthCheckFailed: (key: string, error: any) => void;
|
|
72
|
+
healthCheckPassed: (key: string) => void;
|
|
73
|
+
executeSuccess: (key: string, durationMs: number) => void;
|
|
74
|
+
executeFailed: (key: string, error: any) => void;
|
|
75
|
+
bulkheadRejected: () => void;
|
|
76
|
+
}
|
|
77
|
+
export declare class TimeoutError extends Error {
|
|
78
|
+
constructor(ms: number);
|
|
79
|
+
}
|
|
80
|
+
export declare class BulkheadRejectionError extends Error {
|
|
81
|
+
constructor();
|
|
82
|
+
}
|
|
83
|
+
export declare class AllKeysExhaustedError extends Error {
|
|
84
|
+
constructor();
|
|
85
|
+
}
|
|
36
86
|
/**
|
|
37
87
|
* Strategy Interface for selecting the next key
|
|
38
88
|
*/
|
|
@@ -58,15 +108,49 @@ export declare class WeightedStrategy implements LoadBalancingStrategy {
|
|
|
58
108
|
export declare class LatencyStrategy implements LoadBalancingStrategy {
|
|
59
109
|
next(candidates: KeyState[]): KeyState | null;
|
|
60
110
|
}
|
|
61
|
-
|
|
111
|
+
/**
|
|
112
|
+
* High-performance Vanilla Semantic Cache
|
|
113
|
+
* Implements Cosine Similarity math from scratch.
|
|
114
|
+
*/
|
|
115
|
+
export declare class SemanticCache {
|
|
116
|
+
private entries;
|
|
117
|
+
private threshold;
|
|
118
|
+
private ttlMs;
|
|
119
|
+
constructor(threshold?: number, ttlMs?: number);
|
|
120
|
+
set(prompt: string, vector: number[], response: any): void;
|
|
121
|
+
get(vector: number[]): any | null;
|
|
122
|
+
/**
|
|
123
|
+
* Vanilla Cosine Similarity: (A·B) / (||A|| * ||B||)
|
|
124
|
+
*/
|
|
125
|
+
private calculateCosineSimilarity;
|
|
126
|
+
}
|
|
127
|
+
export declare class ApiKeyManager extends EventEmitter {
|
|
62
128
|
private keys;
|
|
63
129
|
private storageKey;
|
|
64
130
|
private storage;
|
|
65
131
|
private strategy;
|
|
132
|
+
private fallbackFn?;
|
|
133
|
+
private maxConcurrency;
|
|
134
|
+
private activeCalls;
|
|
135
|
+
private healthCheckFn?;
|
|
136
|
+
private healthCheckInterval?;
|
|
137
|
+
private semanticCache?;
|
|
138
|
+
private getEmbeddingFn?;
|
|
139
|
+
private _isResolvingEmbedding;
|
|
140
|
+
/**
|
|
141
|
+
* Constructor supports both legacy positional args and new options object.
|
|
142
|
+
*
|
|
143
|
+
* @example Legacy (v1/v2 — still works):
|
|
144
|
+
* new ApiKeyManager(['key1', 'key2'], storage, strategy)
|
|
145
|
+
*
|
|
146
|
+
* @example New (v3):
|
|
147
|
+
* new ApiKeyManager(keys, { storage, strategy, fallbackFn, concurrency })
|
|
148
|
+
*/
|
|
66
149
|
constructor(initialKeys: string[] | {
|
|
67
150
|
key: string;
|
|
68
151
|
weight?: number;
|
|
69
|
-
|
|
152
|
+
provider?: string;
|
|
153
|
+
}[], storageOrOptions?: any | ApiKeyManagerOptions, strategy?: LoadBalancingStrategy);
|
|
70
154
|
/**
|
|
71
155
|
* CLASSIFIES an error to determine handling strategy
|
|
72
156
|
*/
|
|
@@ -74,6 +158,10 @@ export declare class ApiKeyManager {
|
|
|
74
158
|
private parseRetryAfter;
|
|
75
159
|
private isOnCooldown;
|
|
76
160
|
getKey(): string | null;
|
|
161
|
+
/**
|
|
162
|
+
* Get a key filtered by provider tag
|
|
163
|
+
*/
|
|
164
|
+
getKeyByProvider(provider: string): string | null;
|
|
77
165
|
getKeyCount(): number;
|
|
78
166
|
/**
|
|
79
167
|
* Mark success AND update latency stats
|
|
@@ -85,6 +173,35 @@ export declare class ApiKeyManager {
|
|
|
85
173
|
calculateBackoff(attempt: number): number;
|
|
86
174
|
getStats(): ApiKeyManagerStats;
|
|
87
175
|
_getKeys(): KeyState[];
|
|
176
|
+
/**
|
|
177
|
+
* Wraps the entire API call lifecycle into a single method.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const result = await manager.execute(
|
|
181
|
+
* (key) => fetch(`https://api.example.com?key=${key}`),
|
|
182
|
+
* { maxRetries: 3, timeoutMs: 5000 }
|
|
183
|
+
* );
|
|
184
|
+
*/
|
|
185
|
+
execute<T>(fn: (key: string, signal?: AbortSignal) => Promise<T>, options?: ExecuteOptions & {
|
|
186
|
+
prompt?: string;
|
|
187
|
+
}): Promise<T>;
|
|
188
|
+
private _executeWithRetry;
|
|
189
|
+
private _executeWithTimeout;
|
|
190
|
+
private _sleep;
|
|
191
|
+
/**
|
|
192
|
+
* Set a health check function that tests if a key is operational
|
|
193
|
+
*/
|
|
194
|
+
setHealthCheck(fn: (key: string) => Promise<boolean>): void;
|
|
195
|
+
/**
|
|
196
|
+
* Start periodic health checks
|
|
197
|
+
* @param intervalMs How often to run health checks (default: 60s)
|
|
198
|
+
*/
|
|
199
|
+
startHealthChecks(intervalMs?: number): void;
|
|
200
|
+
/**
|
|
201
|
+
* Stop periodic health checks
|
|
202
|
+
*/
|
|
203
|
+
stopHealthChecks(): void;
|
|
204
|
+
private _runHealthChecks;
|
|
88
205
|
private saveState;
|
|
89
206
|
private loadState;
|
|
90
207
|
}
|