aura-code 0.3.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/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/agent/context.d.ts +13 -0
- package/dist/agent/context.js +183 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/loop.d.ts +60 -0
- package/dist/agent/loop.js +228 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session-store.d.ts +30 -0
- package/dist/agent/session-store.js +178 -0
- package/dist/agent/session-store.js.map +1 -0
- package/dist/agent/spawner.d.ts +57 -0
- package/dist/agent/spawner.js +112 -0
- package/dist/agent/spawner.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +3 -0
- package/dist/agent/system-prompt.js +100 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/architect/engine.d.ts +47 -0
- package/dist/architect/engine.js +217 -0
- package/dist/architect/engine.js.map +1 -0
- package/dist/architect/types.d.ts +61 -0
- package/dist/architect/types.js +6 -0
- package/dist/architect/types.js.map +1 -0
- package/dist/cli/diamond.d.ts +1 -0
- package/dist/cli/diamond.js +23 -0
- package/dist/cli/diamond.js.map +1 -0
- package/dist/cli/display.d.ts +44 -0
- package/dist/cli/display.js +234 -0
- package/dist/cli/display.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1734 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/defaults.d.ts +46 -0
- package/dist/config/defaults.js +106 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/project-config.d.ts +90 -0
- package/dist/config/project-config.js +141 -0
- package/dist/config/project-config.js.map +1 -0
- package/dist/harness/proposer.d.ts +26 -0
- package/dist/harness/proposer.js +204 -0
- package/dist/harness/proposer.js.map +1 -0
- package/dist/harness/weakness-miner.d.ts +28 -0
- package/dist/harness/weakness-miner.js +359 -0
- package/dist/harness/weakness-miner.js.map +1 -0
- package/dist/integrations/harnesses.d.ts +23 -0
- package/dist/integrations/harnesses.js +161 -0
- package/dist/integrations/harnesses.js.map +1 -0
- package/dist/orchestration/competence.d.ts +53 -0
- package/dist/orchestration/competence.js +181 -0
- package/dist/orchestration/competence.js.map +1 -0
- package/dist/orchestration/executor.d.ts +42 -0
- package/dist/orchestration/executor.js +206 -0
- package/dist/orchestration/executor.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.js +39 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
- package/dist/orchestration/orchestrator-prompts.js +153 -0
- package/dist/orchestration/orchestrator-prompts.js.map +1 -0
- package/dist/orchestration/orchestrator.d.ts +27 -0
- package/dist/orchestration/orchestrator.js +169 -0
- package/dist/orchestration/orchestrator.js.map +1 -0
- package/dist/orchestration/plan-store.d.ts +53 -0
- package/dist/orchestration/plan-store.js +176 -0
- package/dist/orchestration/plan-store.js.map +1 -0
- package/dist/orchestration/router-prompts.d.ts +6 -0
- package/dist/orchestration/router-prompts.js +55 -0
- package/dist/orchestration/router-prompts.js.map +1 -0
- package/dist/orchestration/router.d.ts +23 -0
- package/dist/orchestration/router.js +70 -0
- package/dist/orchestration/router.js.map +1 -0
- package/dist/orchestration/ruby-detect.d.ts +6 -0
- package/dist/orchestration/ruby-detect.js +130 -0
- package/dist/orchestration/ruby-detect.js.map +1 -0
- package/dist/orchestration/ruby-types.d.ts +44 -0
- package/dist/orchestration/ruby-types.js +6 -0
- package/dist/orchestration/ruby-types.js.map +1 -0
- package/dist/orchestration/specialist-prompts.d.ts +19 -0
- package/dist/orchestration/specialist-prompts.js +143 -0
- package/dist/orchestration/specialist-prompts.js.map +1 -0
- package/dist/orchestration/specialists.d.ts +66 -0
- package/dist/orchestration/specialists.js +175 -0
- package/dist/orchestration/specialists.js.map +1 -0
- package/dist/orchestration/types.d.ts +82 -0
- package/dist/orchestration/types.js +6 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/perception/extractor.d.ts +7 -0
- package/dist/perception/extractor.js +521 -0
- package/dist/perception/extractor.js.map +1 -0
- package/dist/perception/graph-store.d.ts +25 -0
- package/dist/perception/graph-store.js +117 -0
- package/dist/perception/graph-store.js.map +1 -0
- package/dist/perception/index.d.ts +4 -0
- package/dist/perception/index.js +18 -0
- package/dist/perception/index.js.map +1 -0
- package/dist/perception/queries.d.ts +29 -0
- package/dist/perception/queries.js +354 -0
- package/dist/perception/queries.js.map +1 -0
- package/dist/perception/types.d.ts +103 -0
- package/dist/perception/types.js +6 -0
- package/dist/perception/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +11 -0
- package/dist/providers/anthropic.js +172 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +58 -0
- package/dist/providers/factory.js +354 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/fallback.d.ts +24 -0
- package/dist/providers/fallback.js +72 -0
- package/dist/providers/fallback.js.map +1 -0
- package/dist/providers/google.d.ts +11 -0
- package/dist/providers/google.js +129 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +24 -0
- package/dist/providers/openai-compatible.js +216 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/resilient-factory.d.ts +24 -0
- package/dist/providers/resilient-factory.js +57 -0
- package/dist/providers/resilient-factory.js.map +1 -0
- package/dist/providers/resilient.d.ts +63 -0
- package/dist/providers/resilient.js +145 -0
- package/dist/providers/resilient.js.map +1 -0
- package/dist/providers/types.d.ts +84 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/ruby/alternator.d.ts +40 -0
- package/dist/ruby/alternator.js +233 -0
- package/dist/ruby/alternator.js.map +1 -0
- package/dist/ruby/competence.d.ts +34 -0
- package/dist/ruby/competence.js +264 -0
- package/dist/ruby/competence.js.map +1 -0
- package/dist/ruby/episode-capture.d.ts +56 -0
- package/dist/ruby/episode-capture.js +193 -0
- package/dist/ruby/episode-capture.js.map +1 -0
- package/dist/ruby/fine-tune.d.ts +16 -0
- package/dist/ruby/fine-tune.js +253 -0
- package/dist/ruby/fine-tune.js.map +1 -0
- package/dist/ruby/index.d.ts +10 -0
- package/dist/ruby/index.js +26 -0
- package/dist/ruby/index.js.map +1 -0
- package/dist/ruby/ruby-model.d.ts +36 -0
- package/dist/ruby/ruby-model.js +81 -0
- package/dist/ruby/ruby-model.js.map +1 -0
- package/dist/ruby/training-data.d.ts +11 -0
- package/dist/ruby/training-data.js +107 -0
- package/dist/ruby/training-data.js.map +1 -0
- package/dist/ruby/types.d.ts +136 -0
- package/dist/ruby/types.js +20 -0
- package/dist/ruby/types.js.map +1 -0
- package/dist/safety/permissions.d.ts +32 -0
- package/dist/safety/permissions.js +304 -0
- package/dist/safety/permissions.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +342 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/session.d.ts +22 -0
- package/dist/server/session.js +16 -0
- package/dist/server/session.js.map +1 -0
- package/dist/setup/first-run.d.ts +41 -0
- package/dist/setup/first-run.js +386 -0
- package/dist/setup/first-run.js.map +1 -0
- package/dist/setup/global-config.d.ts +19 -0
- package/dist/setup/global-config.js +97 -0
- package/dist/setup/global-config.js.map +1 -0
- package/dist/tools/audio-transcribe.d.ts +8 -0
- package/dist/tools/audio-transcribe.js +156 -0
- package/dist/tools/audio-transcribe.js.map +1 -0
- package/dist/tools/browser.d.ts +15 -0
- package/dist/tools/browser.js +241 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/calendar.d.ts +12 -0
- package/dist/tools/calendar.js +196 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/clipboard.d.ts +7 -0
- package/dist/tools/clipboard.js +90 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/cron.d.ts +10 -0
- package/dist/tools/cron.js +167 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/edit-file.d.ts +6 -0
- package/dist/tools/edit-file.js +98 -0
- package/dist/tools/edit-file.js.map +1 -0
- package/dist/tools/email.d.ts +11 -0
- package/dist/tools/email.js +153 -0
- package/dist/tools/email.js.map +1 -0
- package/dist/tools/git.d.ts +1 -0
- package/dist/tools/git.js +7 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/http-request.d.ts +12 -0
- package/dist/tools/http-request.js +91 -0
- package/dist/tools/http-request.js.map +1 -0
- package/dist/tools/image-read.d.ts +7 -0
- package/dist/tools/image-read.js +125 -0
- package/dist/tools/image-read.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +201 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir.d.ts +1 -0
- package/dist/tools/list-dir.js +6 -0
- package/dist/tools/list-dir.js.map +1 -0
- package/dist/tools/mcp.d.ts +11 -0
- package/dist/tools/mcp.js +356 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.js +124 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/notify.d.ts +9 -0
- package/dist/tools/notify.js +40 -0
- package/dist/tools/notify.js.map +1 -0
- package/dist/tools/read-file.d.ts +6 -0
- package/dist/tools/read-file.js +76 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/run-shell.d.ts +1 -0
- package/dist/tools/run-shell.js +6 -0
- package/dist/tools/run-shell.js.map +1 -0
- package/dist/tools/run-tests.d.ts +1 -0
- package/dist/tools/run-tests.js +6 -0
- package/dist/tools/run-tests.js.map +1 -0
- package/dist/tools/search-code.d.ts +1 -0
- package/dist/tools/search-code.js +6 -0
- package/dist/tools/search-code.js.map +1 -0
- package/dist/tools/telegram-bot.d.ts +2 -0
- package/dist/tools/telegram-bot.js +693 -0
- package/dist/tools/telegram-bot.js.map +1 -0
- package/dist/tools/telegram-safety.d.ts +52 -0
- package/dist/tools/telegram-safety.js +189 -0
- package/dist/tools/telegram-safety.js.map +1 -0
- package/dist/tools/telegram.d.ts +16 -0
- package/dist/tools/telegram.js +227 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/tools/tools.d.ts +48 -0
- package/dist/tools/tools.js +328 -0
- package/dist/tools/tools.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.js +119 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +8 -0
- package/dist/tools/web-search.js +81 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/whatsapp.d.ts +10 -0
- package/dist/tools/whatsapp.js +222 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/write-file.d.ts +1 -0
- package/dist/tools/write-file.js +6 -0
- package/dist/tools/write-file.js.map +1 -0
- package/dist/tools/youtube-transcript.d.ts +9 -0
- package/dist/tools/youtube-transcript.js +200 -0
- package/dist/tools/youtube-transcript.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +44 -0
- package/dist/util/circuit-breaker.js +78 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/env.d.ts +18 -0
- package/dist/util/env.js +39 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/errors.d.ts +42 -0
- package/dist/util/errors.js +121 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/rate-limiter.d.ts +71 -0
- package/dist/util/rate-limiter.js +141 -0
- package/dist/util/rate-limiter.js.map +1 -0
- package/dist/util/retry.d.ts +30 -0
- package/dist/util/retry.js +57 -0
- package/dist/util/retry.js.map +1 -0
- package/dist/util/sanitize.d.ts +16 -0
- package/dist/util/sanitize.js +255 -0
- package/dist/util/sanitize.js.map +1 -0
- package/dist/verify/checks.d.ts +21 -0
- package/dist/verify/checks.js +311 -0
- package/dist/verify/checks.js.map +1 -0
- package/dist/verify/index.d.ts +33 -0
- package/dist/verify/index.js +127 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/types.d.ts +20 -0
- package/dist/verify/types.js +3 -0
- package/dist/verify/types.js.map +1 -0
- package/dist/versioncheck.d.ts +22 -0
- package/dist/versioncheck.js +203 -0
- package/dist/versioncheck.js.map +1 -0
- package/dist/viz/index.d.ts +2 -0
- package/dist/viz/index.js +733 -0
- package/dist/viz/index.js.map +1 -0
- package/dist/workflows/engine.d.ts +46 -0
- package/dist/workflows/engine.js +320 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/types.d.ts +92 -0
- package/dist/workflows/types.js +6 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +86 -0
package/dist/util/env.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getApiKey = getApiKey;
|
|
4
|
+
exports.getEnv = getEnv;
|
|
5
|
+
/**
|
|
6
|
+
* Provider-agnostic env-var reader.
|
|
7
|
+
*
|
|
8
|
+
* Tries, in order:
|
|
9
|
+
* 1. The canonical UPPER_SNAKE_CASE var (most SDKs read this)
|
|
10
|
+
* 2. The lowercase variant (some shells / dotenv loaders normalise to this)
|
|
11
|
+
* 3. Common alternates passed as `aliases`
|
|
12
|
+
*
|
|
13
|
+
* Returns `undefined` if none are set, never throws. Returns `undefined`
|
|
14
|
+
* (NOT '') for empty / whitespace / placeholder values, so callers can use
|
|
15
|
+
* the `??` operator and have it fall through to the next fallback.
|
|
16
|
+
*/
|
|
17
|
+
function getApiKey(canonical, ...aliases) {
|
|
18
|
+
const names = [canonical, canonical.toLowerCase(), ...aliases];
|
|
19
|
+
for (const name of names) {
|
|
20
|
+
const v = process.env[name];
|
|
21
|
+
if (v && v.trim() && v !== 'your_api_key_here')
|
|
22
|
+
return v;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Same idea for non-secret env vars (base URLs, model names).
|
|
28
|
+
* Returns `undefined` for unset / empty / whitespace, so `??` chains work.
|
|
29
|
+
*/
|
|
30
|
+
function getEnv(canonical, ...aliases) {
|
|
31
|
+
const names = [canonical, canonical.toLowerCase(), ...aliases];
|
|
32
|
+
for (const name of names) {
|
|
33
|
+
const v = process.env[name];
|
|
34
|
+
if (v && v.trim())
|
|
35
|
+
return v;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/util/env.ts"],"names":[],"mappings":";;AAYA,8BAOC;AAMD,wBAOC;AAhCD;;;;;;;;;;;GAWG;AACH,SAAgB,SAAS,CAAC,SAAiB,EAAE,GAAG,OAAiB;IAC/D,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;IAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,mBAAmB;YAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAgB,MAAM,CAAC,SAAiB,EAAE,GAAG,OAAiB;IAC5D,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;IAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;YAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified error type for API calls. All provider SDK errors are normalised
|
|
3
|
+
* into this so retry / circuit-breaker / fallback layers can reason about
|
|
4
|
+
* them uniformly.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApiError extends Error {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly retriable: boolean;
|
|
9
|
+
readonly provider: string;
|
|
10
|
+
readonly retryAfterMs?: number;
|
|
11
|
+
/** Tokens used in the failing request, if known (for TPM tracking) */
|
|
12
|
+
readonly tokens?: {
|
|
13
|
+
input: number;
|
|
14
|
+
output: number;
|
|
15
|
+
};
|
|
16
|
+
/** Cause chain — original SDK error preserved for debugging */
|
|
17
|
+
readonly cause?: unknown;
|
|
18
|
+
constructor(opts: {
|
|
19
|
+
message: string;
|
|
20
|
+
status?: number;
|
|
21
|
+
retriable?: boolean;
|
|
22
|
+
provider: string;
|
|
23
|
+
retryAfterMs?: number;
|
|
24
|
+
tokens?: {
|
|
25
|
+
input: number;
|
|
26
|
+
output: number;
|
|
27
|
+
};
|
|
28
|
+
cause?: unknown;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Classify an HTTP status as retriable. 429 and 5xx are retriable; 4xx (except 429) are not.
|
|
33
|
+
* Network errors (status 0) default to retriable.
|
|
34
|
+
*/
|
|
35
|
+
export declare function defaultRetriable(status: number): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Normalise any error from any provider SDK into an ApiError. We inspect the
|
|
38
|
+
* error shape, pull out the HTTP status, and read Retry-After if present.
|
|
39
|
+
*/
|
|
40
|
+
export declare function normaliseError(e: unknown, provider: string): ApiError;
|
|
41
|
+
/** Did the error come from Gemini specifically? */
|
|
42
|
+
export declare function isGoogle(e: unknown): boolean;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiError = void 0;
|
|
4
|
+
exports.defaultRetriable = defaultRetriable;
|
|
5
|
+
exports.normaliseError = normaliseError;
|
|
6
|
+
exports.isGoogle = isGoogle;
|
|
7
|
+
/**
|
|
8
|
+
* Unified error type for API calls. All provider SDK errors are normalised
|
|
9
|
+
* into this so retry / circuit-breaker / fallback layers can reason about
|
|
10
|
+
* them uniformly.
|
|
11
|
+
*/
|
|
12
|
+
class ApiError extends Error {
|
|
13
|
+
status;
|
|
14
|
+
retriable;
|
|
15
|
+
provider;
|
|
16
|
+
retryAfterMs;
|
|
17
|
+
/** Tokens used in the failing request, if known (for TPM tracking) */
|
|
18
|
+
tokens;
|
|
19
|
+
/** Cause chain — original SDK error preserved for debugging */
|
|
20
|
+
cause;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
super(opts.message);
|
|
23
|
+
this.name = 'ApiError';
|
|
24
|
+
this.status = opts.status ?? 0;
|
|
25
|
+
this.retriable = opts.retriable ?? defaultRetriable(opts.status ?? 0);
|
|
26
|
+
this.provider = opts.provider;
|
|
27
|
+
this.retryAfterMs = opts.retryAfterMs;
|
|
28
|
+
this.tokens = opts.tokens;
|
|
29
|
+
this.cause = opts.cause;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.ApiError = ApiError;
|
|
33
|
+
/**
|
|
34
|
+
* Classify an HTTP status as retriable. 429 and 5xx are retriable; 4xx (except 429) are not.
|
|
35
|
+
* Network errors (status 0) default to retriable.
|
|
36
|
+
*/
|
|
37
|
+
function defaultRetriable(status) {
|
|
38
|
+
if (status === 0)
|
|
39
|
+
return true; // network/timeout
|
|
40
|
+
if (status === 429)
|
|
41
|
+
return true; // rate limited
|
|
42
|
+
if (status === 408)
|
|
43
|
+
return true; // request timeout
|
|
44
|
+
if (status === 529)
|
|
45
|
+
return true; // Anthropic overloaded
|
|
46
|
+
if (status >= 500 && status < 600)
|
|
47
|
+
return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalise any error from any provider SDK into an ApiError. We inspect the
|
|
52
|
+
* error shape, pull out the HTTP status, and read Retry-After if present.
|
|
53
|
+
*/
|
|
54
|
+
function normaliseError(e, provider) {
|
|
55
|
+
// Already normalised
|
|
56
|
+
if (e instanceof ApiError)
|
|
57
|
+
return e;
|
|
58
|
+
// Anthropic / OpenAI / Google SDKs all throw errors with a `status` and sometimes `headers`.
|
|
59
|
+
const err = e;
|
|
60
|
+
const status = err.status ?? err.statusCode ?? 0;
|
|
61
|
+
const message = err.message ?? String(e);
|
|
62
|
+
const retryAfterMs = parseRetryAfter(err.headers) ?? parseGoogleRetryAfter(err.errorDetails);
|
|
63
|
+
// Network/timeout: status 0
|
|
64
|
+
const isNetwork = status === 0 || err.code === 'ETIMEDOUT' || err.code === 'ECONNRESET' || err.code === 'ENOTFOUND' || err.name === 'AbortError';
|
|
65
|
+
return new ApiError({
|
|
66
|
+
message: isNetwork ? `Network error: ${message}` : `HTTP ${status}: ${message}`,
|
|
67
|
+
status: isNetwork ? 0 : status,
|
|
68
|
+
retriable: isNetwork || defaultRetriable(status),
|
|
69
|
+
provider,
|
|
70
|
+
retryAfterMs,
|
|
71
|
+
cause: e,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function parseRetryAfter(headers) {
|
|
75
|
+
if (!headers)
|
|
76
|
+
return undefined;
|
|
77
|
+
const v = headers['retry-after'] ?? headers['Retry-After'] ?? headers['x-ratelimit-reset'];
|
|
78
|
+
if (!v)
|
|
79
|
+
return undefined;
|
|
80
|
+
// Could be "120" (seconds) or an HTTP date
|
|
81
|
+
const seconds = Number(v);
|
|
82
|
+
if (!isNaN(seconds))
|
|
83
|
+
return Math.round(seconds * 1000);
|
|
84
|
+
const date = Date.parse(v);
|
|
85
|
+
if (!isNaN(date))
|
|
86
|
+
return Math.max(0, date - Date.now());
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Google Gemini's error responses include an `errorDetails` array. The relevant
|
|
91
|
+
* entry has `@type: type.googleapis.com/google.rpc.RetryInfo` and a `retryDelay`
|
|
92
|
+
* string like "27s". We parse it into milliseconds.
|
|
93
|
+
*/
|
|
94
|
+
function parseGoogleRetryAfter(details) {
|
|
95
|
+
if (!details || details.length === 0)
|
|
96
|
+
return undefined;
|
|
97
|
+
for (const d of details) {
|
|
98
|
+
const type = d['@type'] ?? '';
|
|
99
|
+
if (type.includes('RetryInfo')) {
|
|
100
|
+
const raw = d.retryDelay ?? d.RetryInfo?.retryDelay;
|
|
101
|
+
if (!raw)
|
|
102
|
+
continue;
|
|
103
|
+
// Formats: "27s", "1500ms", "27.5s"
|
|
104
|
+
const m = String(raw).trim().match(/^(\d+(?:\.\d+)?)(ms|s)?$/);
|
|
105
|
+
if (m) {
|
|
106
|
+
const n = parseFloat(m[1]);
|
|
107
|
+
const unit = m[2] ?? 's';
|
|
108
|
+
return Math.round(unit === 'ms' ? n : n * 1000);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
/** Did the error come from Gemini specifically? */
|
|
115
|
+
function isGoogle(e) {
|
|
116
|
+
if (e instanceof ApiError)
|
|
117
|
+
return e.provider === 'Google';
|
|
118
|
+
const err = e;
|
|
119
|
+
return Boolean(err.message?.includes('GoogleGenerativeAI') || err.name?.includes('GoogleGenerative'));
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/util/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-bucket rate limiter. Each bucket starts full and refills at a
|
|
3
|
+
* steady rate. `acquire()` waits until a token is available, then consumes
|
|
4
|
+
* one. Pass `consume(n)` to take multiple tokens at once (used by TPM).
|
|
5
|
+
*/
|
|
6
|
+
export interface RateLimiterOptions {
|
|
7
|
+
/** Maximum tokens in the bucket (burst size). */
|
|
8
|
+
capacity: number;
|
|
9
|
+
/** Tokens added per millisecond. e.g. 50 rpm → 50/60000 ≈ 0.000833 */
|
|
10
|
+
refillPerMs: number;
|
|
11
|
+
/** Initial tokens (defaults to capacity). */
|
|
12
|
+
initialTokens?: number;
|
|
13
|
+
/** Sleep function for testing. */
|
|
14
|
+
sleep?: (ms: number) => Promise<void>;
|
|
15
|
+
/** Called when acquire must wait — useful for observability. */
|
|
16
|
+
onWait?: (info: {
|
|
17
|
+
needed: number;
|
|
18
|
+
waitMs: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare class RateLimiter {
|
|
22
|
+
private tokens;
|
|
23
|
+
private lastRefill;
|
|
24
|
+
private readonly capacity;
|
|
25
|
+
private readonly refillPerMs;
|
|
26
|
+
private readonly sleep;
|
|
27
|
+
private readonly onWait?;
|
|
28
|
+
constructor(opts: RateLimiterOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Wait until `n` tokens are available, then consume them.
|
|
31
|
+
* Returns the milliseconds waited (0 if instant).
|
|
32
|
+
*/
|
|
33
|
+
acquire(n?: number): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Consume tokens without waiting. Returns true if successful, false if
|
|
36
|
+
* there aren't enough. The bucket is unchanged on false.
|
|
37
|
+
*/
|
|
38
|
+
tryAcquire(n?: number): boolean;
|
|
39
|
+
/** Force a token count (e.g. record a successful API call's token cost). */
|
|
40
|
+
recordUsage(n: number): void;
|
|
41
|
+
/** Reset the bucket to full. */
|
|
42
|
+
reset(): void;
|
|
43
|
+
/** Current token count (after refill). */
|
|
44
|
+
available(): number;
|
|
45
|
+
private refill;
|
|
46
|
+
}
|
|
47
|
+
/** RPM → token-bucket config. capacity allows short bursts. */
|
|
48
|
+
export declare function rpmLimiter(rpm: number, opts?: {
|
|
49
|
+
burst?: number;
|
|
50
|
+
onWait?: RateLimiterOptions['onWait'];
|
|
51
|
+
}): RateLimiter;
|
|
52
|
+
/**
|
|
53
|
+
* TPM (tokens per minute) tracker using a sliding window.
|
|
54
|
+
* Different from RateLimiter: it doesn't block — it just records usage
|
|
55
|
+
* and exposes whether the current window is under the limit. Used to
|
|
56
|
+
* inform the retry layer (don't hammer Google when we're already near TPM).
|
|
57
|
+
*/
|
|
58
|
+
export declare class TpmTracker {
|
|
59
|
+
private window;
|
|
60
|
+
private readonly windowMs;
|
|
61
|
+
private readonly limit;
|
|
62
|
+
constructor(tokensPerMinute: number, windowMs?: number);
|
|
63
|
+
record(tokens: number, now?: number): void;
|
|
64
|
+
used(now?: number): number;
|
|
65
|
+
remaining(now?: number): number;
|
|
66
|
+
/** True if there's room for `n` more tokens right now. */
|
|
67
|
+
hasRoomFor(n: number, now?: number): boolean;
|
|
68
|
+
/** Suggested wait ms before the requested tokens fit, given current usage. */
|
|
69
|
+
waitMsFor(n: number, now?: number): number;
|
|
70
|
+
private prune;
|
|
71
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TpmTracker = exports.RateLimiter = void 0;
|
|
4
|
+
exports.rpmLimiter = rpmLimiter;
|
|
5
|
+
class RateLimiter {
|
|
6
|
+
tokens;
|
|
7
|
+
lastRefill;
|
|
8
|
+
capacity;
|
|
9
|
+
refillPerMs;
|
|
10
|
+
sleep;
|
|
11
|
+
onWait;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.capacity = opts.capacity;
|
|
14
|
+
this.refillPerMs = opts.refillPerMs;
|
|
15
|
+
this.tokens = opts.initialTokens ?? opts.capacity;
|
|
16
|
+
this.lastRefill = Date.now();
|
|
17
|
+
this.sleep = opts.sleep ?? defaultSleep;
|
|
18
|
+
this.onWait = opts.onWait;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wait until `n` tokens are available, then consume them.
|
|
22
|
+
* Returns the milliseconds waited (0 if instant).
|
|
23
|
+
*/
|
|
24
|
+
async acquire(n = 1) {
|
|
25
|
+
if (n > this.capacity) {
|
|
26
|
+
throw new Error(`Cannot acquire ${n} tokens — bucket capacity is ${this.capacity}`);
|
|
27
|
+
}
|
|
28
|
+
const start = Date.now();
|
|
29
|
+
while (true) {
|
|
30
|
+
this.refill();
|
|
31
|
+
if (this.tokens >= n) {
|
|
32
|
+
this.tokens -= n;
|
|
33
|
+
return Date.now() - start;
|
|
34
|
+
}
|
|
35
|
+
// Tokens needed minus what we have, divided by refill rate = wait time
|
|
36
|
+
const deficit = n - this.tokens;
|
|
37
|
+
const waitMs = Math.ceil(deficit / this.refillPerMs) + 5;
|
|
38
|
+
this.onWait?.({ needed: n, waitMs });
|
|
39
|
+
await this.sleep(waitMs);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Consume tokens without waiting. Returns true if successful, false if
|
|
44
|
+
* there aren't enough. The bucket is unchanged on false.
|
|
45
|
+
*/
|
|
46
|
+
tryAcquire(n = 1) {
|
|
47
|
+
this.refill();
|
|
48
|
+
if (this.tokens >= n) {
|
|
49
|
+
this.tokens -= n;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/** Force a token count (e.g. record a successful API call's token cost). */
|
|
55
|
+
recordUsage(n) {
|
|
56
|
+
this.refill();
|
|
57
|
+
this.tokens = Math.max(0, this.tokens - n);
|
|
58
|
+
}
|
|
59
|
+
/** Reset the bucket to full. */
|
|
60
|
+
reset() {
|
|
61
|
+
this.tokens = this.capacity;
|
|
62
|
+
this.lastRefill = Date.now();
|
|
63
|
+
}
|
|
64
|
+
/** Current token count (after refill). */
|
|
65
|
+
available() {
|
|
66
|
+
this.refill();
|
|
67
|
+
return this.tokens;
|
|
68
|
+
}
|
|
69
|
+
refill() {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
const elapsed = now - this.lastRefill;
|
|
72
|
+
if (elapsed <= 0)
|
|
73
|
+
return;
|
|
74
|
+
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerMs);
|
|
75
|
+
this.lastRefill = now;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.RateLimiter = RateLimiter;
|
|
79
|
+
function defaultSleep(ms) {
|
|
80
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
81
|
+
}
|
|
82
|
+
// ── Factory helpers ─────────────────────────────────────────────────────────
|
|
83
|
+
/** RPM → token-bucket config. capacity allows short bursts. */
|
|
84
|
+
function rpmLimiter(rpm, opts) {
|
|
85
|
+
return new RateLimiter({
|
|
86
|
+
capacity: opts?.burst ?? Math.max(1, Math.ceil(rpm / 6)), // ~10s burst
|
|
87
|
+
refillPerMs: rpm / 60_000,
|
|
88
|
+
onWait: opts?.onWait,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* TPM (tokens per minute) tracker using a sliding window.
|
|
93
|
+
* Different from RateLimiter: it doesn't block — it just records usage
|
|
94
|
+
* and exposes whether the current window is under the limit. Used to
|
|
95
|
+
* inform the retry layer (don't hammer Google when we're already near TPM).
|
|
96
|
+
*/
|
|
97
|
+
class TpmTracker {
|
|
98
|
+
window = [];
|
|
99
|
+
windowMs;
|
|
100
|
+
limit;
|
|
101
|
+
constructor(tokensPerMinute, windowMs = 60_000) {
|
|
102
|
+
this.limit = tokensPerMinute;
|
|
103
|
+
this.windowMs = windowMs;
|
|
104
|
+
}
|
|
105
|
+
record(tokens, now = Date.now()) {
|
|
106
|
+
this.window.push({ t: now, tokens });
|
|
107
|
+
this.prune(now);
|
|
108
|
+
}
|
|
109
|
+
used(now = Date.now()) {
|
|
110
|
+
this.prune(now);
|
|
111
|
+
return this.window.reduce((s, e) => s + e.tokens, 0);
|
|
112
|
+
}
|
|
113
|
+
remaining(now = Date.now()) {
|
|
114
|
+
return Math.max(0, this.limit - this.used(now));
|
|
115
|
+
}
|
|
116
|
+
/** True if there's room for `n` more tokens right now. */
|
|
117
|
+
hasRoomFor(n, now = Date.now()) {
|
|
118
|
+
return this.used(now) + n <= this.limit;
|
|
119
|
+
}
|
|
120
|
+
/** Suggested wait ms before the requested tokens fit, given current usage. */
|
|
121
|
+
waitMsFor(n, now = Date.now()) {
|
|
122
|
+
if (this.hasRoomFor(n, now))
|
|
123
|
+
return 0;
|
|
124
|
+
// Find the oldest entry that, when evicted, would free enough room
|
|
125
|
+
let freed = 0;
|
|
126
|
+
for (const entry of this.window) {
|
|
127
|
+
freed += entry.tokens;
|
|
128
|
+
if (this.used(now) - freed + n <= this.limit) {
|
|
129
|
+
return Math.max(0, entry.t + this.windowMs - now);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return this.windowMs;
|
|
133
|
+
}
|
|
134
|
+
prune(now) {
|
|
135
|
+
const cutoff = now - this.windowMs;
|
|
136
|
+
while (this.window.length > 0 && this.window[0].t < cutoff)
|
|
137
|
+
this.window.shift();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.TpmTracker = TpmTracker;
|
|
141
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/util/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ApiError } from './errors.js';
|
|
2
|
+
export interface RetryOptions {
|
|
3
|
+
/** Maximum number of attempts (not retries — total tries including the first). Default 5. */
|
|
4
|
+
maxAttempts?: number;
|
|
5
|
+
/** Base delay in ms for the backoff. Default 1000. */
|
|
6
|
+
baseDelayMs?: number;
|
|
7
|
+
/** Maximum delay between retries. Default 30000 (30s). */
|
|
8
|
+
maxDelayMs?: number;
|
|
9
|
+
/** Multiplier per attempt. Default 2. */
|
|
10
|
+
factor?: number;
|
|
11
|
+
/** Jitter factor 0..1. Default 0.25. */
|
|
12
|
+
jitter?: number;
|
|
13
|
+
/** Called before each retry with the attempt number and the upcoming delay. */
|
|
14
|
+
onRetry?: (info: {
|
|
15
|
+
attempt: number;
|
|
16
|
+
delayMs: number;
|
|
17
|
+
error: ApiError;
|
|
18
|
+
}) => void;
|
|
19
|
+
/** Abort signal — abort waits between retries. */
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
/** Sleep function for testing. Default global setTimeout. */
|
|
22
|
+
sleep?: (ms: number) => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wrap an async function with exponential backoff and jitter.
|
|
26
|
+
* - Honours the error's `retryAfterMs` if set (e.g. parsed from Retry-After header).
|
|
27
|
+
* - Only retries ApiError instances where `retriable === true`.
|
|
28
|
+
* - Re-throws the last error if all attempts fail.
|
|
29
|
+
*/
|
|
30
|
+
export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withRetry = withRetry;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
/**
|
|
6
|
+
* Wrap an async function with exponential backoff and jitter.
|
|
7
|
+
* - Honours the error's `retryAfterMs` if set (e.g. parsed from Retry-After header).
|
|
8
|
+
* - Only retries ApiError instances where `retriable === true`.
|
|
9
|
+
* - Re-throws the last error if all attempts fail.
|
|
10
|
+
*/
|
|
11
|
+
async function withRetry(fn, opts = {}) {
|
|
12
|
+
const maxAttempts = opts.maxAttempts ?? 5;
|
|
13
|
+
const base = opts.baseDelayMs ?? 1000;
|
|
14
|
+
const max = opts.maxDelayMs ?? 30_000;
|
|
15
|
+
const factor = opts.factor ?? 2;
|
|
16
|
+
const jitter = opts.jitter ?? 0.25;
|
|
17
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
18
|
+
let lastErr;
|
|
19
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
20
|
+
if (opts.signal?.aborted)
|
|
21
|
+
throw new Error('Aborted');
|
|
22
|
+
try {
|
|
23
|
+
return await fn();
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
const err = e instanceof errors_js_1.ApiError ? e : new errors_js_1.ApiError({
|
|
27
|
+
message: String(e), provider: 'unknown', retriable: false, cause: e,
|
|
28
|
+
});
|
|
29
|
+
lastErr = err;
|
|
30
|
+
if (!err.retriable || attempt === maxAttempts)
|
|
31
|
+
throw err;
|
|
32
|
+
const delay = computeDelay({
|
|
33
|
+
attempt, base, max, factor, jitter,
|
|
34
|
+
retryAfter: err.retryAfterMs,
|
|
35
|
+
});
|
|
36
|
+
opts.onRetry?.({ attempt: attempt + 1, delayMs: delay, error: err });
|
|
37
|
+
if (opts.signal?.aborted)
|
|
38
|
+
throw new Error('Aborted');
|
|
39
|
+
await sleep(delay);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw lastErr ?? new errors_js_1.ApiError({ message: 'withRetry exhausted', provider: 'unknown' });
|
|
43
|
+
}
|
|
44
|
+
function computeDelay(opts) {
|
|
45
|
+
// If the server told us when to retry, prefer that (capped to max).
|
|
46
|
+
if (opts.retryAfter !== undefined) {
|
|
47
|
+
return Math.min(opts.max, Math.max(opts.retryAfter, 0));
|
|
48
|
+
}
|
|
49
|
+
const exp = opts.base * Math.pow(opts.factor, opts.attempt - 1);
|
|
50
|
+
const capped = Math.min(opts.max, exp);
|
|
51
|
+
const jittered = capped * (1 - opts.jitter + Math.random() * opts.jitter * 2);
|
|
52
|
+
return Math.round(jittered);
|
|
53
|
+
}
|
|
54
|
+
function defaultSleep(ms) {
|
|
55
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/util/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip HTML tags from a string using a character-level state machine.
|
|
3
|
+
* Also removes <script> and <style> blocks entirely (including content).
|
|
4
|
+
* Preserves text content. Decodes common HTML entities afterward.
|
|
5
|
+
*/
|
|
6
|
+
export declare function stripHtml(html: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Decode HTML entities in a single pass.
|
|
9
|
+
* Uses a single regex replacement to avoid sequential decode issues
|
|
10
|
+
* (e.g., &lt; should become <, not <).
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeEntities(text: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Escape HTML special characters to prevent XSS.
|
|
15
|
+
*/
|
|
16
|
+
export declare function escapeHtml(text: string): string;
|