llmapi-v2 2.1.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.
Files changed (162) hide show
  1. package/.env.example +40 -0
  2. package/Dockerfile +17 -0
  3. package/dist/config.d.ts +48 -0
  4. package/dist/config.js +98 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/converter/request.d.ts +6 -0
  7. package/dist/converter/request.js +184 -0
  8. package/dist/converter/request.js.map +1 -0
  9. package/dist/converter/response.d.ts +6 -0
  10. package/dist/converter/response.js +76 -0
  11. package/dist/converter/response.js.map +1 -0
  12. package/dist/converter/stream.d.ts +54 -0
  13. package/dist/converter/stream.js +318 -0
  14. package/dist/converter/stream.js.map +1 -0
  15. package/dist/converter/types.d.ts +239 -0
  16. package/dist/converter/types.js +6 -0
  17. package/dist/converter/types.js.map +1 -0
  18. package/dist/data/posts.d.ts +19 -0
  19. package/dist/data/posts.js +462 -0
  20. package/dist/data/posts.js.map +1 -0
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +233 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/middleware/api-key-auth.d.ts +6 -0
  25. package/dist/middleware/api-key-auth.js +76 -0
  26. package/dist/middleware/api-key-auth.js.map +1 -0
  27. package/dist/middleware/quota-guard.d.ts +10 -0
  28. package/dist/middleware/quota-guard.js +27 -0
  29. package/dist/middleware/quota-guard.js.map +1 -0
  30. package/dist/middleware/rate-limiter.d.ts +5 -0
  31. package/dist/middleware/rate-limiter.js +50 -0
  32. package/dist/middleware/rate-limiter.js.map +1 -0
  33. package/dist/middleware/request-logger.d.ts +6 -0
  34. package/dist/middleware/request-logger.js +37 -0
  35. package/dist/middleware/request-logger.js.map +1 -0
  36. package/dist/middleware/session-auth.d.ts +19 -0
  37. package/dist/middleware/session-auth.js +99 -0
  38. package/dist/middleware/session-auth.js.map +1 -0
  39. package/dist/providers/aliyun.d.ts +13 -0
  40. package/dist/providers/aliyun.js +20 -0
  41. package/dist/providers/aliyun.js.map +1 -0
  42. package/dist/providers/base-provider.d.ts +36 -0
  43. package/dist/providers/base-provider.js +133 -0
  44. package/dist/providers/base-provider.js.map +1 -0
  45. package/dist/providers/deepseek.d.ts +11 -0
  46. package/dist/providers/deepseek.js +18 -0
  47. package/dist/providers/deepseek.js.map +1 -0
  48. package/dist/providers/registry.d.ts +18 -0
  49. package/dist/providers/registry.js +98 -0
  50. package/dist/providers/registry.js.map +1 -0
  51. package/dist/providers/types.d.ts +17 -0
  52. package/dist/providers/types.js +3 -0
  53. package/dist/providers/types.js.map +1 -0
  54. package/dist/routes/admin.d.ts +1 -0
  55. package/dist/routes/admin.js +153 -0
  56. package/dist/routes/admin.js.map +1 -0
  57. package/dist/routes/auth.d.ts +2 -0
  58. package/dist/routes/auth.js +318 -0
  59. package/dist/routes/auth.js.map +1 -0
  60. package/dist/routes/blog.d.ts +1 -0
  61. package/dist/routes/blog.js +29 -0
  62. package/dist/routes/blog.js.map +1 -0
  63. package/dist/routes/dashboard.d.ts +1 -0
  64. package/dist/routes/dashboard.js +184 -0
  65. package/dist/routes/dashboard.js.map +1 -0
  66. package/dist/routes/messages.d.ts +1 -0
  67. package/dist/routes/messages.js +309 -0
  68. package/dist/routes/messages.js.map +1 -0
  69. package/dist/routes/models.d.ts +1 -0
  70. package/dist/routes/models.js +39 -0
  71. package/dist/routes/models.js.map +1 -0
  72. package/dist/routes/payment.d.ts +1 -0
  73. package/dist/routes/payment.js +150 -0
  74. package/dist/routes/payment.js.map +1 -0
  75. package/dist/routes/sitemap.d.ts +1 -0
  76. package/dist/routes/sitemap.js +38 -0
  77. package/dist/routes/sitemap.js.map +1 -0
  78. package/dist/services/alipay.d.ts +27 -0
  79. package/dist/services/alipay.js +106 -0
  80. package/dist/services/alipay.js.map +1 -0
  81. package/dist/services/database.d.ts +4 -0
  82. package/dist/services/database.js +170 -0
  83. package/dist/services/database.js.map +1 -0
  84. package/dist/services/health-checker.d.ts +13 -0
  85. package/dist/services/health-checker.js +95 -0
  86. package/dist/services/health-checker.js.map +1 -0
  87. package/dist/services/mailer.d.ts +3 -0
  88. package/dist/services/mailer.js +91 -0
  89. package/dist/services/mailer.js.map +1 -0
  90. package/dist/services/metrics.d.ts +56 -0
  91. package/dist/services/metrics.js +94 -0
  92. package/dist/services/metrics.js.map +1 -0
  93. package/dist/services/remote-control.d.ts +20 -0
  94. package/dist/services/remote-control.js +209 -0
  95. package/dist/services/remote-control.js.map +1 -0
  96. package/dist/services/remote-ws.d.ts +5 -0
  97. package/dist/services/remote-ws.js +143 -0
  98. package/dist/services/remote-ws.js.map +1 -0
  99. package/dist/services/usage.d.ts +13 -0
  100. package/dist/services/usage.js +39 -0
  101. package/dist/services/usage.js.map +1 -0
  102. package/dist/utils/errors.d.ts +27 -0
  103. package/dist/utils/errors.js +48 -0
  104. package/dist/utils/errors.js.map +1 -0
  105. package/dist/utils/logger.d.ts +2 -0
  106. package/dist/utils/logger.js +14 -0
  107. package/dist/utils/logger.js.map +1 -0
  108. package/docker-compose.yml +19 -0
  109. package/package.json +39 -0
  110. package/public/robots.txt +8 -0
  111. package/src/config.ts +140 -0
  112. package/src/converter/request.ts +207 -0
  113. package/src/converter/response.ts +85 -0
  114. package/src/converter/stream.ts +373 -0
  115. package/src/converter/types.ts +257 -0
  116. package/src/data/posts.ts +474 -0
  117. package/src/index.ts +219 -0
  118. package/src/middleware/api-key-auth.ts +82 -0
  119. package/src/middleware/quota-guard.ts +28 -0
  120. package/src/middleware/rate-limiter.ts +61 -0
  121. package/src/middleware/request-logger.ts +36 -0
  122. package/src/middleware/session-auth.ts +91 -0
  123. package/src/providers/aliyun.ts +16 -0
  124. package/src/providers/base-provider.ts +148 -0
  125. package/src/providers/deepseek.ts +14 -0
  126. package/src/providers/registry.ts +111 -0
  127. package/src/providers/types.ts +26 -0
  128. package/src/routes/admin.ts +169 -0
  129. package/src/routes/auth.ts +369 -0
  130. package/src/routes/blog.ts +28 -0
  131. package/src/routes/dashboard.ts +208 -0
  132. package/src/routes/messages.ts +346 -0
  133. package/src/routes/models.ts +37 -0
  134. package/src/routes/payment.ts +189 -0
  135. package/src/routes/sitemap.ts +40 -0
  136. package/src/services/alipay.ts +116 -0
  137. package/src/services/database.ts +187 -0
  138. package/src/services/health-checker.ts +115 -0
  139. package/src/services/mailer.ts +90 -0
  140. package/src/services/metrics.ts +104 -0
  141. package/src/services/remote-control.ts +226 -0
  142. package/src/services/remote-ws.ts +145 -0
  143. package/src/services/usage.ts +57 -0
  144. package/src/types/express.d.ts +46 -0
  145. package/src/utils/errors.ts +44 -0
  146. package/src/utils/logger.ts +8 -0
  147. package/tsconfig.json +17 -0
  148. package/views/pages/404.ejs +14 -0
  149. package/views/pages/admin.ejs +307 -0
  150. package/views/pages/blog-post.ejs +378 -0
  151. package/views/pages/blog.ejs +148 -0
  152. package/views/pages/dashboard.ejs +441 -0
  153. package/views/pages/docs.ejs +807 -0
  154. package/views/pages/index.ejs +416 -0
  155. package/views/pages/login.ejs +170 -0
  156. package/views/pages/orders.ejs +111 -0
  157. package/views/pages/pricing.ejs +379 -0
  158. package/views/pages/register.ejs +397 -0
  159. package/views/pages/remote.ejs +334 -0
  160. package/views/pages/settings.ejs +373 -0
  161. package/views/partials/header.ejs +70 -0
  162. package/views/partials/nav.ejs +140 -0
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AliyunProvider = void 0;
4
+ const base_provider_1 = require("./base-provider");
5
+ /**
6
+ * Alibaba Cloud Bailian (DashScope) provider.
7
+ * Native Anthropic endpoint: https://dashscope-intl.aliyuncs.com/apps/anthropic
8
+ *
9
+ * Supports Qwen series models with full Anthropic API compatibility:
10
+ * - Streaming SSE
11
+ * - Tool calling (96.5% accuracy)
12
+ * - Context caching (automatic, reduces cost for repeated prefixes)
13
+ */
14
+ class AliyunProvider extends base_provider_1.BaseProvider {
15
+ constructor(anthropicBaseUrl, apiKey, timeout, openaiBaseUrl) {
16
+ super('aliyun', anthropicBaseUrl, apiKey, timeout, openaiBaseUrl);
17
+ }
18
+ }
19
+ exports.AliyunProvider = AliyunProvider;
20
+ //# sourceMappingURL=aliyun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliyun.js","sourceRoot":"","sources":["../../src/providers/aliyun.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAE/C;;;;;;;;GAQG;AACH,MAAa,cAAe,SAAQ,4BAAY;IAC9C,YAAY,gBAAwB,EAAE,MAAc,EAAE,OAAe,EAAE,aAAsB;QAC3F,KAAK,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IACpE,CAAC;CACF;AAJD,wCAIC"}
@@ -0,0 +1,36 @@
1
+ import type { IncomingMessage } from 'http';
2
+ import type { LLMProvider } from './types';
3
+ /**
4
+ * Transparent Anthropic proxy provider.
5
+ *
6
+ * Forwards requests directly to the provider's native Anthropic-compatible
7
+ * endpoint WITHOUT format conversion. The provider handles all Anthropic
8
+ * protocol details (streaming SSE, tool calling, etc.).
9
+ *
10
+ * This is the highest-quality approach: we rely on the provider's own
11
+ * Anthropic compatibility layer rather than building our own.
12
+ */
13
+ export declare class BaseProvider implements LLMProvider {
14
+ readonly name: string;
15
+ readonly anthropicBaseUrl: string;
16
+ readonly openaiBaseUrl: string | undefined;
17
+ private readonly apiKey;
18
+ private readonly timeout;
19
+ private healthy;
20
+ private consecutiveFailures;
21
+ private lastFailureTime;
22
+ private static readonly FAILURE_THRESHOLD;
23
+ private static readonly RECOVERY_TIME_MS;
24
+ constructor(name: string, anthropicBaseUrl: string, apiKey: string, timeout: number, openaiBaseUrl?: string);
25
+ isHealthy(): boolean;
26
+ markUnhealthy(reason: string): void;
27
+ markHealthy(): void;
28
+ /**
29
+ * Transparent proxy: forward an Anthropic-format request body directly
30
+ * to the provider's native Anthropic endpoint.
31
+ *
32
+ * Returns the raw IncomingMessage so the caller can pipe it directly
33
+ * back to the client (for streaming) or buffer it (for non-streaming).
34
+ */
35
+ proxy(path: string, body: Buffer | string, headers: Record<string, string>, isStream: boolean): Promise<IncomingMessage>;
36
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BaseProvider = void 0;
7
+ const http_1 = __importDefault(require("http"));
8
+ const https_1 = __importDefault(require("https"));
9
+ const url_1 = require("url");
10
+ const logger_1 = require("../utils/logger");
11
+ /**
12
+ * Transparent Anthropic proxy provider.
13
+ *
14
+ * Forwards requests directly to the provider's native Anthropic-compatible
15
+ * endpoint WITHOUT format conversion. The provider handles all Anthropic
16
+ * protocol details (streaming SSE, tool calling, etc.).
17
+ *
18
+ * This is the highest-quality approach: we rely on the provider's own
19
+ * Anthropic compatibility layer rather than building our own.
20
+ */
21
+ class BaseProvider {
22
+ name;
23
+ anthropicBaseUrl;
24
+ openaiBaseUrl;
25
+ apiKey;
26
+ timeout;
27
+ // Circuit breaker
28
+ healthy = true;
29
+ consecutiveFailures = 0;
30
+ lastFailureTime = 0;
31
+ static FAILURE_THRESHOLD = 3;
32
+ static RECOVERY_TIME_MS = 30_000;
33
+ constructor(name, anthropicBaseUrl, apiKey, timeout, openaiBaseUrl) {
34
+ this.name = name;
35
+ this.anthropicBaseUrl = anthropicBaseUrl;
36
+ this.openaiBaseUrl = openaiBaseUrl;
37
+ this.apiKey = apiKey;
38
+ this.timeout = timeout;
39
+ }
40
+ isHealthy() {
41
+ if (this.healthy)
42
+ return true;
43
+ if (Date.now() - this.lastFailureTime > BaseProvider.RECOVERY_TIME_MS) {
44
+ this.healthy = true;
45
+ this.consecutiveFailures = 0;
46
+ logger_1.logger.info({ provider: this.name }, 'Provider auto-recovered');
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ markUnhealthy(reason) {
52
+ this.consecutiveFailures++;
53
+ this.lastFailureTime = Date.now();
54
+ if (this.consecutiveFailures >= BaseProvider.FAILURE_THRESHOLD) {
55
+ this.healthy = false;
56
+ logger_1.logger.warn({ provider: this.name, reason, failures: this.consecutiveFailures }, 'Provider marked unhealthy');
57
+ }
58
+ }
59
+ markHealthy() {
60
+ if (!this.healthy) {
61
+ logger_1.logger.info({ provider: this.name }, 'Provider recovered');
62
+ }
63
+ this.healthy = true;
64
+ this.consecutiveFailures = 0;
65
+ }
66
+ /**
67
+ * Transparent proxy: forward an Anthropic-format request body directly
68
+ * to the provider's native Anthropic endpoint.
69
+ *
70
+ * Returns the raw IncomingMessage so the caller can pipe it directly
71
+ * back to the client (for streaming) or buffer it (for non-streaming).
72
+ */
73
+ proxy(path, body, headers, isStream) {
74
+ return new Promise((resolve, reject) => {
75
+ const parsed = new url_1.URL(this.anthropicBaseUrl);
76
+ const isHttps = parsed.protocol === 'https:';
77
+ const transport = isHttps ? https_1.default : http_1.default;
78
+ const payload = typeof body === 'string' ? Buffer.from(body) : body;
79
+ const fullPath = parsed.pathname.replace(/\/+$/, '') + path;
80
+ // Forward most headers, but override auth
81
+ const proxyHeaders = {
82
+ 'Content-Type': 'application/json',
83
+ 'Content-Length': payload.length,
84
+ 'x-api-key': this.apiKey,
85
+ 'Authorization': `Bearer ${this.apiKey}`,
86
+ };
87
+ // Pass through anthropic-specific headers
88
+ if (headers['anthropic-version'])
89
+ proxyHeaders['anthropic-version'] = headers['anthropic-version'];
90
+ if (headers['anthropic-beta'])
91
+ proxyHeaders['anthropic-beta'] = headers['anthropic-beta'];
92
+ const options = {
93
+ hostname: parsed.hostname,
94
+ port: parsed.port || (isHttps ? 443 : 80),
95
+ path: fullPath,
96
+ method: 'POST',
97
+ headers: proxyHeaders,
98
+ timeout: this.timeout,
99
+ };
100
+ const req = transport.request(options, (res) => {
101
+ if (isStream || (res.statusCode && res.statusCode < 400)) {
102
+ resolve(res);
103
+ return;
104
+ }
105
+ // Non-stream error: buffer the error response to log it
106
+ const chunks = [];
107
+ res.on('data', (chunk) => chunks.push(chunk));
108
+ res.on('end', () => {
109
+ const errBody = Buffer.concat(chunks).toString();
110
+ let message = `Provider ${this.name} returned ${res.statusCode}`;
111
+ try {
112
+ const parsed = JSON.parse(errBody);
113
+ message = parsed.error?.message || message;
114
+ }
115
+ catch { }
116
+ const err = new Error(message);
117
+ err.statusCode = res.statusCode || 500;
118
+ err.rawBody = errBody;
119
+ reject(err);
120
+ });
121
+ });
122
+ req.on('timeout', () => {
123
+ req.destroy();
124
+ reject(new Error(`Request to ${this.name} timed out after ${this.timeout}ms`));
125
+ });
126
+ req.on('error', reject);
127
+ req.write(payload);
128
+ req.end();
129
+ });
130
+ }
131
+ }
132
+ exports.BaseProvider = BaseProvider;
133
+ //# sourceMappingURL=base-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-provider.js","sourceRoot":"","sources":["../../src/providers/base-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { BaseProvider } from './base-provider';
2
+ /**
3
+ * DeepSeek provider.
4
+ * Native Anthropic endpoint: https://api.deepseek.com/anthropic
5
+ *
6
+ * Best for: simple conversations, light tasks, cost-sensitive routing.
7
+ * Note: tool calling accuracy (~81.5%) is lower than Qwen.
8
+ */
9
+ export declare class DeepSeekProvider extends BaseProvider {
10
+ constructor(anthropicBaseUrl: string, apiKey: string, timeout: number, openaiBaseUrl?: string);
11
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeepSeekProvider = void 0;
4
+ const base_provider_1 = require("./base-provider");
5
+ /**
6
+ * DeepSeek provider.
7
+ * Native Anthropic endpoint: https://api.deepseek.com/anthropic
8
+ *
9
+ * Best for: simple conversations, light tasks, cost-sensitive routing.
10
+ * Note: tool calling accuracy (~81.5%) is lower than Qwen.
11
+ */
12
+ class DeepSeekProvider extends base_provider_1.BaseProvider {
13
+ constructor(anthropicBaseUrl, apiKey, timeout, openaiBaseUrl) {
14
+ super('deepseek', anthropicBaseUrl, apiKey, timeout, openaiBaseUrl);
15
+ }
16
+ }
17
+ exports.DeepSeekProvider = DeepSeekProvider;
18
+ //# sourceMappingURL=deepseek.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/providers/deepseek.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAE/C;;;;;;GAMG;AACH,MAAa,gBAAiB,SAAQ,4BAAY;IAChD,YAAY,gBAAwB,EAAE,MAAc,EAAE,OAAe,EAAE,aAAsB;QAC3F,KAAK,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IACtE,CAAC;CACF;AAJD,4CAIC"}
@@ -0,0 +1,18 @@
1
+ import type { AppConfig } from '../config';
2
+ import type { LLMProvider, ResolvedRoute } from './types';
3
+ export declare function initRegistry(config: AppConfig): void;
4
+ /**
5
+ * Smart routing: decide which provider/model based on request characteristics.
6
+ *
7
+ * Rules:
8
+ * - If request has tools -> coding task -> use primary coding model (Qwen)
9
+ * - If request is simple (no tools, few messages) -> use light model (DeepSeek)
10
+ * - Model name mapping: claude-opus -> best available, claude-haiku -> cheapest
11
+ * - Fallback: try providers in priority order
12
+ */
13
+ export declare function smartResolve(claudeModel: string, hasTools: boolean, messageCount: number): ResolvedRoute | null;
14
+ /**
15
+ * Resolve with failover: yield providers to try in priority order.
16
+ */
17
+ export declare function resolveWithFailover(claudeModel: string): AsyncGenerator<ResolvedRoute>;
18
+ export declare function getProviders(): Map<string, LLMProvider>;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initRegistry = initRegistry;
4
+ exports.smartResolve = smartResolve;
5
+ exports.resolveWithFailover = resolveWithFailover;
6
+ exports.getProviders = getProviders;
7
+ const aliyun_1 = require("./aliyun");
8
+ const deepseek_1 = require("./deepseek");
9
+ const logger_1 = require("../utils/logger");
10
+ const providers = new Map();
11
+ let modelRouting = {};
12
+ let smartRouting;
13
+ function initRegistry(config) {
14
+ providers.clear();
15
+ for (const pc of config.providers) {
16
+ const provider = createProvider(pc);
17
+ if (provider) {
18
+ providers.set(pc.name, provider);
19
+ logger_1.logger.info({ provider: pc.name, endpoint: pc.anthropicBaseUrl }, 'Provider registered (native Anthropic endpoint)');
20
+ }
21
+ }
22
+ modelRouting = config.modelRouting;
23
+ smartRouting = config.smartRouting;
24
+ if (providers.size === 0) {
25
+ logger_1.logger.warn('No providers configured! API requests will fail.');
26
+ }
27
+ }
28
+ function createProvider(pc) {
29
+ if (!pc.enabled || !pc.apiKey)
30
+ return null;
31
+ switch (pc.name) {
32
+ case 'aliyun':
33
+ return new aliyun_1.AliyunProvider(pc.anthropicBaseUrl, pc.apiKey, pc.timeout, pc.openaiBaseUrl);
34
+ case 'deepseek':
35
+ return new deepseek_1.DeepSeekProvider(pc.anthropicBaseUrl, pc.apiKey, pc.timeout, pc.openaiBaseUrl);
36
+ default:
37
+ logger_1.logger.warn({ provider: pc.name }, 'Unknown provider type');
38
+ return null;
39
+ }
40
+ }
41
+ /**
42
+ * Smart routing: decide which provider/model based on request characteristics.
43
+ *
44
+ * Rules:
45
+ * - If request has tools -> coding task -> use primary coding model (Qwen)
46
+ * - If request is simple (no tools, few messages) -> use light model (DeepSeek)
47
+ * - Model name mapping: claude-opus -> best available, claude-haiku -> cheapest
48
+ * - Fallback: try providers in priority order
49
+ */
50
+ function smartResolve(claudeModel, hasTools, messageCount) {
51
+ // Claude-haiku always goes to the cheap model
52
+ if (claudeModel.includes('haiku')) {
53
+ const p = providers.get(smartRouting.lightModel.provider);
54
+ if (p?.isHealthy()) {
55
+ return { provider: p, backendModel: smartRouting.lightModel.model };
56
+ }
57
+ }
58
+ // No tools + short conversation = light task
59
+ if (!hasTools && messageCount <= smartRouting.lightTaskMaxMessages) {
60
+ const p = providers.get(smartRouting.lightModel.provider);
61
+ if (p?.isHealthy()) {
62
+ return { provider: p, backendModel: smartRouting.lightModel.model };
63
+ }
64
+ }
65
+ // Tools present or complex conversation = coding task
66
+ if (hasTools) {
67
+ const p = providers.get(smartRouting.codingModel.provider);
68
+ if (p?.isHealthy()) {
69
+ return { provider: p, backendModel: smartRouting.codingModel.model };
70
+ }
71
+ }
72
+ // Fallback to model-based routing
73
+ return null;
74
+ }
75
+ /**
76
+ * Resolve with failover: yield providers to try in priority order.
77
+ */
78
+ async function* resolveWithFailover(claudeModel) {
79
+ const routes = modelRouting[claudeModel];
80
+ if (!routes || routes.length === 0) {
81
+ logger_1.logger.error({ model: claudeModel }, 'No routes configured for model');
82
+ return;
83
+ }
84
+ for (const route of routes) {
85
+ const provider = providers.get(route.provider);
86
+ if (!provider)
87
+ continue;
88
+ if (!provider.isHealthy()) {
89
+ logger_1.logger.debug({ provider: route.provider }, 'Skipping unhealthy provider');
90
+ continue;
91
+ }
92
+ yield { provider, backendModel: route.model };
93
+ }
94
+ }
95
+ function getProviders() {
96
+ return providers;
97
+ }
98
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { IncomingMessage } from 'http';
2
+ export interface LLMProvider {
3
+ readonly name: string;
4
+ readonly anthropicBaseUrl: string;
5
+ isHealthy(): boolean;
6
+ markUnhealthy(reason: string): void;
7
+ markHealthy(): void;
8
+ /**
9
+ * Transparent proxy: forward raw Anthropic request to provider's native endpoint.
10
+ * Returns the raw HTTP response for direct piping back to the client.
11
+ */
12
+ proxy(path: string, body: Buffer | string, headers: Record<string, string>, isStream: boolean): Promise<IncomingMessage>;
13
+ }
14
+ export interface ResolvedRoute {
15
+ provider: LLMProvider;
16
+ backendModel: string;
17
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export declare const adminRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adminRouter = void 0;
4
+ const express_1 = require("express");
5
+ const database_1 = require("../services/database");
6
+ const session_auth_1 = require("../middleware/session-auth");
7
+ const health_checker_1 = require("../services/health-checker");
8
+ const logger_1 = require("../utils/logger");
9
+ exports.adminRouter = (0, express_1.Router)();
10
+ // All routes require admin auth
11
+ exports.adminRouter.use(session_auth_1.adminAuth);
12
+ /**
13
+ * GET /api/admin/stats
14
+ * Platform-wide statistics.
15
+ */
16
+ exports.adminRouter.get('/stats', async (_req, res) => {
17
+ try {
18
+ const pool = (0, database_1.getPool)();
19
+ const [users] = await pool.execute(`
20
+ SELECT
21
+ COUNT(*) as total,
22
+ SUM(CASE WHEN DATE(created_at) = CURRENT_DATE THEN 1 ELSE 0 END) as today_new
23
+ FROM users
24
+ `);
25
+ const [activeUsers] = await pool.execute(`
26
+ SELECT COUNT(DISTINCT user_id) as cnt FROM usage_logs
27
+ WHERE created_at >= NOW() - INTERVAL '7 days'
28
+ `);
29
+ const [requests] = await pool.execute(`
30
+ SELECT
31
+ COUNT(*) as total,
32
+ SUM(CASE WHEN DATE(created_at) = CURRENT_DATE THEN 1 ELSE 0 END) as today
33
+ FROM usage_logs
34
+ `);
35
+ const [tokens] = await pool.execute(`
36
+ SELECT
37
+ COALESCE(SUM(input_tokens + output_tokens), 0) as total_tokens,
38
+ COALESCE(SUM(provider_cost), 0) as total_cost
39
+ FROM usage_logs
40
+ `);
41
+ const [monthCost] = await pool.execute(`
42
+ SELECT COALESCE(SUM(provider_cost), 0) as cost
43
+ FROM usage_logs WHERE created_at >= date_trunc('month', NOW())
44
+ `);
45
+ const [revenue] = await pool.execute(`
46
+ SELECT COALESCE(SUM(amount), 0) as total FROM orders WHERE status = 'paid'
47
+ `);
48
+ const [monthRevenue] = await pool.execute(`
49
+ SELECT COALESCE(SUM(amount), 0) as total FROM orders
50
+ WHERE status = 'paid' AND paid_at >= date_trunc('month', NOW())
51
+ `);
52
+ // Provider health
53
+ const providerHealth = (0, health_checker_1.getHealthStatus)();
54
+ res.json({
55
+ success: true,
56
+ stats: {
57
+ users: users[0],
58
+ activeUsers7d: activeUsers[0].cnt,
59
+ requests: requests[0],
60
+ tokens: tokens[0],
61
+ monthCost: parseFloat(monthCost[0].cost),
62
+ revenue: parseFloat(revenue[0].total),
63
+ monthRevenue: parseFloat(monthRevenue[0].total),
64
+ providerHealth,
65
+ },
66
+ });
67
+ }
68
+ catch (err) {
69
+ logger_1.logger.error({ err }, 'admin stats error');
70
+ res.status(500).json({ success: false, error: 'Internal error' });
71
+ }
72
+ });
73
+ /**
74
+ * GET /api/admin/users
75
+ * Full user list with plan and usage info.
76
+ */
77
+ exports.adminRouter.get('/users', async (_req, res) => {
78
+ try {
79
+ const pool = (0, database_1.getPool)();
80
+ const [rows] = await pool.execute(`
81
+ SELECT
82
+ u.id, u.email, u.name, u.role, u.status, u.created_at,
83
+ p.name as plan_name, p.display_name as plan_display,
84
+ COALESCE(s.tokens_used, 0) as tokens_used,
85
+ p.token_limit_monthly,
86
+ (SELECT COUNT(*) FROM usage_logs WHERE user_id = u.id) as total_requests,
87
+ (SELECT COUNT(*) FROM api_keys WHERE user_id = u.id AND status = 'active') as api_key_count
88
+ FROM users u
89
+ LEFT JOIN subscriptions s ON s.user_id = u.id
90
+ LEFT JOIN plans p ON s.plan_id = p.id
91
+ ORDER BY u.created_at DESC
92
+ `);
93
+ res.json({ success: true, users: rows });
94
+ }
95
+ catch (err) {
96
+ logger_1.logger.error({ err }, 'admin users error');
97
+ res.status(500).json({ success: false, error: 'Internal error' });
98
+ }
99
+ });
100
+ /**
101
+ * PUT /api/admin/users/:id
102
+ * Update user status or plan.
103
+ */
104
+ exports.adminRouter.put('/users/:id', async (req, res) => {
105
+ try {
106
+ const pool = (0, database_1.getPool)();
107
+ const userId = req.params.id;
108
+ const { status, plan } = req.body;
109
+ if (status) {
110
+ await pool.execute('UPDATE users SET status = ? WHERE id = ?', [status, userId]);
111
+ }
112
+ if (plan) {
113
+ const [plans] = await pool.execute('SELECT id FROM plans WHERE name = ?', [plan]);
114
+ const planId = plans[0]?.id;
115
+ if (planId) {
116
+ await pool.execute('UPDATE subscriptions SET plan_id = ?, tokens_used = 0 WHERE id = (SELECT id FROM subscriptions WHERE user_id = ? ORDER BY period_start DESC LIMIT 1)', [planId, userId]);
117
+ }
118
+ }
119
+ res.json({ success: true });
120
+ }
121
+ catch (err) {
122
+ logger_1.logger.error({ err }, 'admin update user error');
123
+ res.status(500).json({ success: false, error: 'Internal error' });
124
+ }
125
+ });
126
+ /**
127
+ * GET /api/admin/cost-report
128
+ * Provider cost breakdown.
129
+ */
130
+ exports.adminRouter.get('/cost-report', async (_req, res) => {
131
+ try {
132
+ const pool = (0, database_1.getPool)();
133
+ const [daily] = await pool.execute(`
134
+ SELECT
135
+ DATE(created_at) as date,
136
+ provider_name,
137
+ COUNT(*) as requests,
138
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
139
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
140
+ COALESCE(SUM(provider_cost), 0) as cost
141
+ FROM usage_logs
142
+ WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
143
+ GROUP BY DATE(created_at), provider_name
144
+ ORDER BY date DESC, provider_name
145
+ `);
146
+ res.json({ success: true, daily });
147
+ }
148
+ catch (err) {
149
+ logger_1.logger.error({ err }, 'cost report error');
150
+ res.status(500).json({ success: false, error: 'Internal error' });
151
+ }
152
+ });
153
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/routes/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Router } from 'express';
2
+ export declare function createAuthRouter(jwtSecret: string): Router;