@matthias-hausberger/beige 0.0.1
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.md +8 -0
- package/README.md +183 -0
- package/dist/channels/registry.d.ts +14 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +14 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/telegram.d.ts +92 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +469 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/tui.d.ts +8 -0
- package/dist/channels/tui.d.ts.map +1 -0
- package/dist/channels/tui.js +574 -0
- package/dist/channels/tui.js.map +1 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +571 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +6 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +103 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.spec.d.ts +2 -0
- package/dist/config/loader.spec.d.ts.map +1 -0
- package/dist/config/loader.spec.js +195 -0
- package/dist/config/loader.spec.js.map +1 -0
- package/dist/config/schema.d.ts +107 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +42 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/schema.spec.d.ts +2 -0
- package/dist/config/schema.spec.d.ts.map +1 -0
- package/dist/config/schema.spec.js +180 -0
- package/dist/config/schema.spec.js.map +1 -0
- package/dist/gateway/agent-manager.d.ts +138 -0
- package/dist/gateway/agent-manager.d.ts.map +1 -0
- package/dist/gateway/agent-manager.js +532 -0
- package/dist/gateway/agent-manager.js.map +1 -0
- package/dist/gateway/api.d.ts +43 -0
- package/dist/gateway/api.d.ts.map +1 -0
- package/dist/gateway/api.js +256 -0
- package/dist/gateway/api.js.map +1 -0
- package/dist/gateway/api.spec.d.ts +2 -0
- package/dist/gateway/api.spec.d.ts.map +1 -0
- package/dist/gateway/api.spec.js +256 -0
- package/dist/gateway/api.spec.js.map +1 -0
- package/dist/gateway/audit.d.ts +38 -0
- package/dist/gateway/audit.d.ts.map +1 -0
- package/dist/gateway/audit.js +82 -0
- package/dist/gateway/audit.js.map +1 -0
- package/dist/gateway/audit.spec.d.ts +2 -0
- package/dist/gateway/audit.spec.d.ts.map +1 -0
- package/dist/gateway/audit.spec.js +212 -0
- package/dist/gateway/audit.spec.js.map +1 -0
- package/dist/gateway/gateway.d.ts +27 -0
- package/dist/gateway/gateway.d.ts.map +1 -0
- package/dist/gateway/gateway.js +158 -0
- package/dist/gateway/gateway.js.map +1 -0
- package/dist/gateway/policy.d.ts +27 -0
- package/dist/gateway/policy.d.ts.map +1 -0
- package/dist/gateway/policy.js +40 -0
- package/dist/gateway/policy.js.map +1 -0
- package/dist/gateway/policy.spec.d.ts +2 -0
- package/dist/gateway/policy.spec.d.ts.map +1 -0
- package/dist/gateway/policy.spec.js +121 -0
- package/dist/gateway/policy.spec.js.map +1 -0
- package/dist/gateway/provider-health.d.ts +83 -0
- package/dist/gateway/provider-health.d.ts.map +1 -0
- package/dist/gateway/provider-health.js +219 -0
- package/dist/gateway/provider-health.js.map +1 -0
- package/dist/gateway/provider-health.spec.d.ts +2 -0
- package/dist/gateway/provider-health.spec.d.ts.map +1 -0
- package/dist/gateway/provider-health.spec.js +278 -0
- package/dist/gateway/provider-health.spec.js.map +1 -0
- package/dist/gateway/session-settings.d.ts +62 -0
- package/dist/gateway/session-settings.d.ts.map +1 -0
- package/dist/gateway/session-settings.js +91 -0
- package/dist/gateway/session-settings.js.map +1 -0
- package/dist/gateway/session-settings.spec.d.ts +2 -0
- package/dist/gateway/session-settings.spec.d.ts.map +1 -0
- package/dist/gateway/session-settings.spec.js +141 -0
- package/dist/gateway/session-settings.spec.js.map +1 -0
- package/dist/gateway/sessions.d.ts +68 -0
- package/dist/gateway/sessions.d.ts.map +1 -0
- package/dist/gateway/sessions.js +177 -0
- package/dist/gateway/sessions.js.map +1 -0
- package/dist/gateway/sessions.spec.d.ts +2 -0
- package/dist/gateway/sessions.spec.d.ts.map +1 -0
- package/dist/gateway/sessions.spec.js +190 -0
- package/dist/gateway/sessions.spec.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +39 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +144 -0
- package/dist/install.js.map +1 -0
- package/dist/sandbox/manager.d.ts +63 -0
- package/dist/sandbox/manager.d.ts.map +1 -0
- package/dist/sandbox/manager.js +294 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/skills/index.d.ts +2 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/registry.d.ts +11 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +86 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/registry.spec.d.ts +2 -0
- package/dist/skills/registry.spec.d.ts.map +1 -0
- package/dist/skills/registry.spec.js +220 -0
- package/dist/skills/registry.spec.js.map +1 -0
- package/dist/socket/protocol.d.ts +21 -0
- package/dist/socket/protocol.d.ts.map +1 -0
- package/dist/socket/protocol.js +7 -0
- package/dist/socket/protocol.js.map +1 -0
- package/dist/socket/protocol.spec.d.ts +2 -0
- package/dist/socket/protocol.spec.d.ts.map +1 -0
- package/dist/socket/protocol.spec.js +135 -0
- package/dist/socket/protocol.spec.js.map +1 -0
- package/dist/socket/server.d.ts +21 -0
- package/dist/socket/server.d.ts.map +1 -0
- package/dist/socket/server.js +133 -0
- package/dist/socket/server.js.map +1 -0
- package/dist/socket/server.spec.d.ts +2 -0
- package/dist/socket/server.spec.d.ts.map +1 -0
- package/dist/socket/server.spec.js +333 -0
- package/dist/socket/server.spec.js.map +1 -0
- package/dist/test/fixtures.d.ts +47 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +144 -0
- package/dist/test/fixtures.js.map +1 -0
- package/dist/toolkit/index.d.ts +4 -0
- package/dist/toolkit/index.d.ts.map +1 -0
- package/dist/toolkit/index.js +4 -0
- package/dist/toolkit/index.js.map +1 -0
- package/dist/toolkit/installer.d.ts +26 -0
- package/dist/toolkit/installer.d.ts.map +1 -0
- package/dist/toolkit/installer.js +247 -0
- package/dist/toolkit/installer.js.map +1 -0
- package/dist/toolkit/registry.d.ts +19 -0
- package/dist/toolkit/registry.d.ts.map +1 -0
- package/dist/toolkit/registry.js +119 -0
- package/dist/toolkit/registry.js.map +1 -0
- package/dist/toolkit/registry.spec.d.ts +2 -0
- package/dist/toolkit/registry.spec.d.ts.map +1 -0
- package/dist/toolkit/registry.spec.js +194 -0
- package/dist/toolkit/registry.spec.js.map +1 -0
- package/dist/toolkit/schema.d.ts +61 -0
- package/dist/toolkit/schema.d.ts.map +1 -0
- package/dist/toolkit/schema.js +116 -0
- package/dist/toolkit/schema.js.map +1 -0
- package/dist/toolkit/schema.spec.d.ts +2 -0
- package/dist/toolkit/schema.spec.d.ts.map +1 -0
- package/dist/toolkit/schema.spec.js +202 -0
- package/dist/toolkit/schema.spec.js.map +1 -0
- package/dist/tools/core.d.ts +10 -0
- package/dist/tools/core.d.ts.map +1 -0
- package/dist/tools/core.js +246 -0
- package/dist/tools/core.js.map +1 -0
- package/dist/tools/core.spec.d.ts +2 -0
- package/dist/tools/core.spec.d.ts.map +1 -0
- package/dist/tools/core.spec.js +315 -0
- package/dist/tools/core.spec.js.map +1 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +62 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/registry.spec.d.ts +2 -0
- package/dist/tools/registry.spec.d.ts.map +1 -0
- package/dist/tools/registry.spec.js +228 -0
- package/dist/tools/registry.spec.js.map +1 -0
- package/dist/tools/runner.d.ts +25 -0
- package/dist/tools/runner.d.ts.map +1 -0
- package/dist/tools/runner.js +35 -0
- package/dist/tools/runner.js.map +1 -0
- package/dist/tools/runner.spec.d.ts +2 -0
- package/dist/tools/runner.spec.d.ts.map +1 -0
- package/dist/tools/runner.spec.js +129 -0
- package/dist/tools/runner.spec.js.map +1 -0
- package/dist/types/session.d.ts +8 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +23 -0
- package/dist/types/session.js.map +1 -0
- package/package.json +76 -0
- package/tools/README.md +1 -0
- package/tools/kv/README.md +150 -0
- package/tools/kv/index.ts +149 -0
- package/tools/kv/tool.json +23 -0
- package/tools/message/README.md +53 -0
- package/tools/message/index.ts +183 -0
- package/tools/message/tool.json +11 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { PolicyEngine } from "./policy.js";
|
|
3
|
+
import { createFullConfig, createMinimalConfig } from "../test/fixtures.js";
|
|
4
|
+
describe("PolicyEngine", () => {
|
|
5
|
+
describe("with full config", () => {
|
|
6
|
+
const config = createFullConfig();
|
|
7
|
+
const engine = new PolicyEngine(config);
|
|
8
|
+
describe("isCoreTool", () => {
|
|
9
|
+
it("returns true for 'read'", () => {
|
|
10
|
+
expect(engine.isCoreTool("read")).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it("returns true for 'write'", () => {
|
|
13
|
+
expect(engine.isCoreTool("write")).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it("returns true for 'patch'", () => {
|
|
16
|
+
expect(engine.isCoreTool("patch")).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it("returns true for 'exec'", () => {
|
|
19
|
+
expect(engine.isCoreTool("exec")).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("returns false for custom tools", () => {
|
|
22
|
+
expect(engine.isCoreTool("kv")).toBe(false);
|
|
23
|
+
expect(engine.isCoreTool("browser")).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
it("returns false for unknown tools", () => {
|
|
26
|
+
expect(engine.isCoreTool("unknown")).toBe(false);
|
|
27
|
+
expect(engine.isCoreTool("")).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("isToolAllowed", () => {
|
|
31
|
+
it("allows configured tools for agent", () => {
|
|
32
|
+
expect(engine.isToolAllowed("assistant", "kv")).toBe(true);
|
|
33
|
+
expect(engine.isToolAllowed("researcher", "kv")).toBe(true);
|
|
34
|
+
expect(engine.isToolAllowed("researcher", "browser")).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it("denies unconfigured tools for agent", () => {
|
|
37
|
+
expect(engine.isToolAllowed("assistant", "browser")).toBe(false);
|
|
38
|
+
expect(engine.isToolAllowed("restricted", "kv")).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it("denies all tools for unknown agent", () => {
|
|
41
|
+
expect(engine.isToolAllowed("unknown-agent", "kv")).toBe(false);
|
|
42
|
+
expect(engine.isToolAllowed("unknown-agent", "browser")).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it("denies when agent has empty tools array", () => {
|
|
45
|
+
expect(engine.isToolAllowed("restricted", "kv")).toBe(false);
|
|
46
|
+
expect(engine.isToolAllowed("restricted", "browser")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe("isAgentValid", () => {
|
|
50
|
+
it("returns true for configured agents", () => {
|
|
51
|
+
expect(engine.isAgentValid("assistant")).toBe(true);
|
|
52
|
+
expect(engine.isAgentValid("researcher")).toBe(true);
|
|
53
|
+
expect(engine.isAgentValid("restricted")).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it("returns false for unknown agents", () => {
|
|
56
|
+
expect(engine.isAgentValid("unknown")).toBe(false);
|
|
57
|
+
expect(engine.isAgentValid("")).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("getToolTarget", () => {
|
|
61
|
+
it("returns 'gateway' for gateway-targeted tools", () => {
|
|
62
|
+
expect(engine.getToolTarget("kv")).toBe("gateway");
|
|
63
|
+
});
|
|
64
|
+
it("returns 'sandbox' for sandbox-targeted tools", () => {
|
|
65
|
+
expect(engine.getToolTarget("browser")).toBe("sandbox");
|
|
66
|
+
});
|
|
67
|
+
it("returns undefined for unknown tools", () => {
|
|
68
|
+
expect(engine.getToolTarget("unknown")).toBeUndefined();
|
|
69
|
+
expect(engine.getToolTarget("")).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("with minimal config (no tools)", () => {
|
|
74
|
+
const config = createMinimalConfig();
|
|
75
|
+
const engine = new PolicyEngine(config);
|
|
76
|
+
it("denies all custom tools when none are configured", () => {
|
|
77
|
+
expect(engine.isToolAllowed("assistant", "kv")).toBe(false);
|
|
78
|
+
expect(engine.isToolAllowed("assistant", "browser")).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it("still recognizes core tools", () => {
|
|
81
|
+
expect(engine.isCoreTool("read")).toBe(true);
|
|
82
|
+
expect(engine.isCoreTool("write")).toBe(true);
|
|
83
|
+
expect(engine.isCoreTool("patch")).toBe(true);
|
|
84
|
+
expect(engine.isCoreTool("exec")).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it("returns undefined for tool targets when no tools configured", () => {
|
|
87
|
+
expect(engine.getToolTarget("kv")).toBeUndefined();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("edge cases", () => {
|
|
91
|
+
it("handles agent with empty tools array", () => {
|
|
92
|
+
const config = createMinimalConfig({
|
|
93
|
+
agents: {
|
|
94
|
+
empty: {
|
|
95
|
+
model: { provider: "anthropic", model: "claude" },
|
|
96
|
+
tools: [],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const engine = new PolicyEngine(config);
|
|
101
|
+
expect(engine.isToolAllowed("empty", "any-tool")).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
it("handles tool names with special characters", () => {
|
|
104
|
+
const config = createMinimalConfig({
|
|
105
|
+
tools: {
|
|
106
|
+
"my-custom-tool": { path: "/tools/my-custom-tool", target: "gateway" },
|
|
107
|
+
},
|
|
108
|
+
agents: {
|
|
109
|
+
assistant: {
|
|
110
|
+
model: { provider: "anthropic", model: "claude" },
|
|
111
|
+
tools: ["my-custom-tool"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const engine = new PolicyEngine(config);
|
|
116
|
+
expect(engine.isToolAllowed("assistant", "my-custom-tool")).toBe(true);
|
|
117
|
+
expect(engine.getToolTarget("my-custom-tool")).toBe("gateway");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=policy.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.spec.js","sourceRoot":"","sources":["../../src/gateway/policy.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider health tracking for rate limit handling and fallback logic.
|
|
3
|
+
*
|
|
4
|
+
* Tracks per-provider rate limits and cooldown periods. When a provider
|
|
5
|
+
* hits a rate limit, it's marked as "cooling down" until the retry-after
|
|
6
|
+
* time expires. If no retry-after is provided, a default cooldown is used.
|
|
7
|
+
*
|
|
8
|
+
* Persisted to ~/.beige/data/provider-health.json so it survives restarts.
|
|
9
|
+
*/
|
|
10
|
+
export interface ProviderHealthEntry {
|
|
11
|
+
/** ISO timestamp when the provider hit a rate limit. */
|
|
12
|
+
rateLimitedAt?: string;
|
|
13
|
+
/** ISO timestamp when we can retry (from retry-after header or default). */
|
|
14
|
+
retryAfter?: string;
|
|
15
|
+
/** Number of consecutive failures (resets on success). */
|
|
16
|
+
consecutiveFailures: number;
|
|
17
|
+
/** Last error message (for debugging). */
|
|
18
|
+
lastError?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ProviderHealthData {
|
|
21
|
+
/** provider/model → health entry */
|
|
22
|
+
providers: Record<string, ProviderHealthEntry>;
|
|
23
|
+
/** When the data was last persisted */
|
|
24
|
+
lastUpdated: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Tracks provider health and rate limits.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ProviderHealthTracker {
|
|
30
|
+
private filePath;
|
|
31
|
+
private data;
|
|
32
|
+
constructor();
|
|
33
|
+
/**
|
|
34
|
+
* Get the health entry for a provider/model.
|
|
35
|
+
*/
|
|
36
|
+
get(provider: string, model: string): ProviderHealthEntry | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a provider/model is currently in cooldown (rate limited).
|
|
39
|
+
*/
|
|
40
|
+
isCoolingDown(provider: string, model: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get the remaining cooldown time in milliseconds, or 0 if not cooling down.
|
|
43
|
+
*/
|
|
44
|
+
getRemainingCooldown(provider: string, model: string): number;
|
|
45
|
+
/**
|
|
46
|
+
* Mark a provider/model as rate limited.
|
|
47
|
+
* @param retryAfterMs Optional retry-after time in ms. If not provided, uses default.
|
|
48
|
+
* @param error Optional error message for debugging.
|
|
49
|
+
*/
|
|
50
|
+
markRateLimited(provider: string, model: string, retryAfterMs?: number, error?: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Mark a provider/model as failed (but not necessarily rate limited).
|
|
53
|
+
*/
|
|
54
|
+
markFailed(provider: string, model: string, error?: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Mark a provider/model as healthy (successful response).
|
|
57
|
+
*/
|
|
58
|
+
markHealthy(provider: string, model: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Clear the cooldown for a provider/model.
|
|
61
|
+
*/
|
|
62
|
+
clearCooldown(provider: string, model: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Get all provider/model keys that are currently in cooldown.
|
|
65
|
+
*/
|
|
66
|
+
getCoolingDown(): Array<{
|
|
67
|
+
provider: string;
|
|
68
|
+
model: string;
|
|
69
|
+
retryAfter: Date;
|
|
70
|
+
}>;
|
|
71
|
+
private makeKey;
|
|
72
|
+
private load;
|
|
73
|
+
private save;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract rate limit info from an error response.
|
|
77
|
+
* Checks for common patterns in API error responses.
|
|
78
|
+
*/
|
|
79
|
+
export declare function extractRateLimitInfo(error: unknown): {
|
|
80
|
+
isRateLimit: boolean;
|
|
81
|
+
retryAfterMs?: number;
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=provider-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-health.d.ts","sourceRoot":"","sources":["../../src/gateway/provider-health.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/C,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAqB;;IASjC;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAKrE;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAgBvD;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAW7D;;;;OAIG;IACH,eAAe,CACb,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI;IAuBP;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAajE;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAiBlD;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAcpD;;OAEG;IACH,cAAc,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,IAAI,CAAA;KAAE,CAAC;IAmB9E,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,IAAI;IAaZ,OAAO,CAAC,IAAI;CAIb;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAyCA"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
/** Default cooldown when no retry-after header is provided (30 minutes) */
|
|
5
|
+
const DEFAULT_COOLDOWN_MS = 30 * 60 * 1000;
|
|
6
|
+
/**
|
|
7
|
+
* Tracks provider health and rate limits.
|
|
8
|
+
*/
|
|
9
|
+
export class ProviderHealthTracker {
|
|
10
|
+
filePath;
|
|
11
|
+
data;
|
|
12
|
+
constructor() {
|
|
13
|
+
const dir = resolve(homedir(), ".beige", "data");
|
|
14
|
+
mkdirSync(dir, { recursive: true });
|
|
15
|
+
this.filePath = resolve(dir, "provider-health.json");
|
|
16
|
+
this.data = this.load();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the health entry for a provider/model.
|
|
20
|
+
*/
|
|
21
|
+
get(provider, model) {
|
|
22
|
+
const key = this.makeKey(provider, model);
|
|
23
|
+
return this.data.providers[key];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a provider/model is currently in cooldown (rate limited).
|
|
27
|
+
*/
|
|
28
|
+
isCoolingDown(provider, model) {
|
|
29
|
+
const entry = this.get(provider, model);
|
|
30
|
+
if (!entry?.retryAfter)
|
|
31
|
+
return false;
|
|
32
|
+
const retryAfter = new Date(entry.retryAfter).getTime();
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (now >= retryAfter) {
|
|
35
|
+
// Cooldown expired — clear it
|
|
36
|
+
this.clearCooldown(provider, model);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the remaining cooldown time in milliseconds, or 0 if not cooling down.
|
|
43
|
+
*/
|
|
44
|
+
getRemainingCooldown(provider, model) {
|
|
45
|
+
const entry = this.get(provider, model);
|
|
46
|
+
if (!entry?.retryAfter)
|
|
47
|
+
return 0;
|
|
48
|
+
const retryAfter = new Date(entry.retryAfter).getTime();
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const remaining = retryAfter - now;
|
|
51
|
+
return Math.max(0, remaining);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Mark a provider/model as rate limited.
|
|
55
|
+
* @param retryAfterMs Optional retry-after time in ms. If not provided, uses default.
|
|
56
|
+
* @param error Optional error message for debugging.
|
|
57
|
+
*/
|
|
58
|
+
markRateLimited(provider, model, retryAfterMs, error) {
|
|
59
|
+
const key = this.makeKey(provider, model);
|
|
60
|
+
const now = new Date();
|
|
61
|
+
const cooldownMs = retryAfterMs ?? DEFAULT_COOLDOWN_MS;
|
|
62
|
+
const retryAfter = new Date(now.getTime() + cooldownMs);
|
|
63
|
+
const existing = this.data.providers[key] ?? { consecutiveFailures: 0 };
|
|
64
|
+
this.data.providers[key] = {
|
|
65
|
+
...existing,
|
|
66
|
+
rateLimitedAt: now.toISOString(),
|
|
67
|
+
retryAfter: retryAfter.toISOString(),
|
|
68
|
+
consecutiveFailures: existing.consecutiveFailures + 1,
|
|
69
|
+
lastError: error,
|
|
70
|
+
};
|
|
71
|
+
this.save();
|
|
72
|
+
console.log(`[PROVIDER_HEALTH] ${key} rate limited until ${retryAfter.toISOString()}` +
|
|
73
|
+
(error ? ` (${error})` : ""));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Mark a provider/model as failed (but not necessarily rate limited).
|
|
77
|
+
*/
|
|
78
|
+
markFailed(provider, model, error) {
|
|
79
|
+
const key = this.makeKey(provider, model);
|
|
80
|
+
const existing = this.data.providers[key] ?? { consecutiveFailures: 0 };
|
|
81
|
+
this.data.providers[key] = {
|
|
82
|
+
...existing,
|
|
83
|
+
consecutiveFailures: existing.consecutiveFailures + 1,
|
|
84
|
+
lastError: error,
|
|
85
|
+
};
|
|
86
|
+
this.save();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Mark a provider/model as healthy (successful response).
|
|
90
|
+
*/
|
|
91
|
+
markHealthy(provider, model) {
|
|
92
|
+
const key = this.makeKey(provider, model);
|
|
93
|
+
const existing = this.data.providers[key];
|
|
94
|
+
if (existing && (existing.consecutiveFailures > 0 || existing.retryAfter)) {
|
|
95
|
+
this.data.providers[key] = {
|
|
96
|
+
consecutiveFailures: 0,
|
|
97
|
+
// Clear rate limit info on success
|
|
98
|
+
rateLimitedAt: undefined,
|
|
99
|
+
retryAfter: undefined,
|
|
100
|
+
lastError: undefined,
|
|
101
|
+
};
|
|
102
|
+
this.save();
|
|
103
|
+
console.log(`[PROVIDER_HEALTH] ${key} marked healthy`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Clear the cooldown for a provider/model.
|
|
108
|
+
*/
|
|
109
|
+
clearCooldown(provider, model) {
|
|
110
|
+
const key = this.makeKey(provider, model);
|
|
111
|
+
const existing = this.data.providers[key];
|
|
112
|
+
if (existing) {
|
|
113
|
+
this.data.providers[key] = {
|
|
114
|
+
...existing,
|
|
115
|
+
rateLimitedAt: undefined,
|
|
116
|
+
retryAfter: undefined,
|
|
117
|
+
};
|
|
118
|
+
this.save();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get all provider/model keys that are currently in cooldown.
|
|
123
|
+
*/
|
|
124
|
+
getCoolingDown() {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
const result = [];
|
|
127
|
+
for (const [key, entry] of Object.entries(this.data.providers)) {
|
|
128
|
+
if (entry.retryAfter) {
|
|
129
|
+
const retryAfter = new Date(entry.retryAfter);
|
|
130
|
+
if (retryAfter.getTime() > now) {
|
|
131
|
+
const [provider, model] = key.split("/");
|
|
132
|
+
result.push({ provider, model, retryAfter });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
// ── Private ────────────────────────────────────────────────────────
|
|
139
|
+
makeKey(provider, model) {
|
|
140
|
+
return `${provider}/${model}`;
|
|
141
|
+
}
|
|
142
|
+
load() {
|
|
143
|
+
if (!existsSync(this.filePath)) {
|
|
144
|
+
return { providers: {}, lastUpdated: new Date().toISOString() };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
148
|
+
return JSON.parse(raw);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return { providers: {}, lastUpdated: new Date().toISOString() };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
save() {
|
|
155
|
+
this.data.lastUpdated = new Date().toISOString();
|
|
156
|
+
writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Extract rate limit info from an error response.
|
|
161
|
+
* Checks for common patterns in API error responses.
|
|
162
|
+
*/
|
|
163
|
+
export function extractRateLimitInfo(error) {
|
|
164
|
+
if (!error || typeof error !== "object") {
|
|
165
|
+
return { isRateLimit: false };
|
|
166
|
+
}
|
|
167
|
+
const err = error;
|
|
168
|
+
// Check for HTTP status 429
|
|
169
|
+
if (err.status === 429 || err.statusCode === 429) {
|
|
170
|
+
const retryAfter = err.headers?.["retry-after"] ?? err.headers?.["Retry-After"];
|
|
171
|
+
return {
|
|
172
|
+
isRateLimit: true,
|
|
173
|
+
retryAfterMs: parseRetryAfter(retryAfter),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// Check for error type/message patterns
|
|
177
|
+
const errorType = err.error?.type ?? err.type ?? "";
|
|
178
|
+
const errorMessage = err.message ?? err.error?.message ?? "";
|
|
179
|
+
const rateLimitPatterns = [
|
|
180
|
+
"rate_limit",
|
|
181
|
+
"rate limit",
|
|
182
|
+
"too many requests",
|
|
183
|
+
"overloaded",
|
|
184
|
+
"capacity",
|
|
185
|
+
"temporarily unavailable",
|
|
186
|
+
];
|
|
187
|
+
const combined = `${errorType} ${errorMessage}`.toLowerCase();
|
|
188
|
+
const isRateLimit = rateLimitPatterns.some((p) => combined.includes(p));
|
|
189
|
+
if (isRateLimit) {
|
|
190
|
+
const retryAfter = err.headers?.["retry-after"] ?? err.headers?.["Retry-After"];
|
|
191
|
+
return {
|
|
192
|
+
isRateLimit: true,
|
|
193
|
+
retryAfterMs: parseRetryAfter(retryAfter),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { isRateLimit: false };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Parse a retry-after header value.
|
|
200
|
+
* Can be either a number of seconds or an HTTP date.
|
|
201
|
+
*/
|
|
202
|
+
function parseRetryAfter(value) {
|
|
203
|
+
if (value === undefined || value === null)
|
|
204
|
+
return undefined;
|
|
205
|
+
if (typeof value === "number") {
|
|
206
|
+
return value * 1000; // seconds to ms
|
|
207
|
+
}
|
|
208
|
+
const num = parseInt(value, 10);
|
|
209
|
+
if (!isNaN(num)) {
|
|
210
|
+
return num * 1000; // seconds to ms
|
|
211
|
+
}
|
|
212
|
+
// Try parsing as HTTP date
|
|
213
|
+
const date = new Date(value);
|
|
214
|
+
if (!isNaN(date.getTime())) {
|
|
215
|
+
return Math.max(0, date.getTime() - Date.now());
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=provider-health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-health.js","sourceRoot":"","sources":["../../src/gateway/provider-health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-health.spec.d.ts","sourceRoot":"","sources":["../../src/gateway/provider-health.spec.ts"],"names":[],"mappings":""}
|