manifest 5.27.0 → 5.28.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 (42) hide show
  1. package/dist/backend/routing/minimax-oauth.controller.js +80 -0
  2. package/dist/backend/routing/minimax-oauth.service.js +306 -0
  3. package/dist/backend/routing/model-discovery/model-discovery.service.js +11 -9
  4. package/dist/backend/routing/model-discovery/model-fallback.js +13 -3
  5. package/dist/backend/routing/model-discovery/provider-model-fetcher.service.js +24 -2
  6. package/dist/backend/routing/openai-oauth.types.js +21 -0
  7. package/dist/backend/routing/provider-base-url.js +25 -0
  8. package/dist/backend/routing/proxy/chatgpt-adapter.js +25 -5
  9. package/dist/backend/routing/proxy/provider-client.js +23 -0
  10. package/dist/backend/routing/proxy/provider-endpoints.js +25 -1
  11. package/dist/backend/routing/proxy/proxy.service.js +37 -12
  12. package/dist/backend/routing/routing.module.js +5 -0
  13. package/dist/backend/routing/routing.service.js +8 -7
  14. package/dist/index.js +2 -2
  15. package/dist/local-mode.js +1 -1
  16. package/dist/openclaw.plugin.json +1 -1
  17. package/dist/subscription.js +1 -1
  18. package/openclaw.plugin.json +1 -1
  19. package/package.json +1 -1
  20. package/public/assets/Account-CgHKYcxX.js +1 -0
  21. package/public/assets/{Limits-DMhjJmfn.js → Limits-DtI10mW2.js} +1 -1
  22. package/public/assets/Login-DhEMJAnc.js +1 -0
  23. package/public/assets/{MessageLog-ouDtsrVk.js → MessageLog-D-PrXoJr.js} +1 -1
  24. package/public/assets/{ModelPrices-CLb817O4.js → ModelPrices-xY-lQ6DR.js} +1 -1
  25. package/public/assets/{Overview-BkBN-yAJ.js → Overview-B6kaNMxS.js} +1 -1
  26. package/public/assets/ProviderIcon-C1sEdkWq.js +1 -0
  27. package/public/assets/{Register-BTimdbbI.js → Register-BbHU04Jz.js} +1 -1
  28. package/public/assets/{ResetPassword-VA6EMO3z.js → ResetPassword-USsGc2kx.js} +1 -1
  29. package/public/assets/Routing-BpgdSvv8.js +3 -0
  30. package/public/assets/{Settings-BO_Zw9re.js → Settings-CtTGuerh.js} +1 -1
  31. package/public/assets/{SocialButtons-BYMeXs5O.js → SocialButtons-s6oYDylB.js} +1 -1
  32. package/public/assets/{index-iVuNO4tR.css → index-BZMbFWG-.css} +1 -1
  33. package/public/assets/{index-B4etj0id.js → index-CpEcUZbW.js} +2 -2
  34. package/public/assets/{model-display-BFHsHhiv.js → model-display-CcUHbbaH.js} +1 -1
  35. package/public/assets/{overview-K8ENzzN2.js → overview-DqBss4Dv.js} +1 -1
  36. package/public/index.html +2 -2
  37. package/subscription-capabilities/index.d.ts +1 -0
  38. package/subscription-capabilities/index.js +19 -0
  39. package/public/assets/Account-VXfD0W30.js +0 -1
  40. package/public/assets/Login-GEA0cpss.js +0 -1
  41. package/public/assets/ProviderIcon-DuNcnqvr.js +0 -1
  42. package/public/assets/Routing-DfCzJ6V-.js +0 -3
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MinimaxOauthController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const current_user_decorator_1 = require("../auth/current-user.decorator");
18
+ const minimax_oauth_service_1 = require("./minimax-oauth.service");
19
+ const resolve_agent_service_1 = require("./resolve-agent.service");
20
+ let MinimaxOauthController = class MinimaxOauthController {
21
+ oauthService;
22
+ resolveAgent;
23
+ constructor(oauthService, resolveAgent) {
24
+ this.oauthService = oauthService;
25
+ this.resolveAgent = resolveAgent;
26
+ }
27
+ async start(agentName, region, user) {
28
+ if (!agentName) {
29
+ throw new common_1.HttpException('agentName query parameter is required', common_1.HttpStatus.BAD_REQUEST);
30
+ }
31
+ if (region && !(0, minimax_oauth_service_1.isMinimaxRegion)(region)) {
32
+ throw new common_1.HttpException('region query parameter must be one of: global, cn', common_1.HttpStatus.BAD_REQUEST);
33
+ }
34
+ const agent = await this.resolveAgent.resolve(user.id, agentName);
35
+ const selectedRegion = region && (0, minimax_oauth_service_1.isMinimaxRegion)(region) ? region : 'global';
36
+ try {
37
+ return await this.oauthService.startAuthorization(agent.id, user.id, selectedRegion);
38
+ }
39
+ catch (err) {
40
+ const message = err instanceof Error ? err.message : 'Failed to start MiniMax OAuth';
41
+ throw new common_1.HttpException(message, common_1.HttpStatus.SERVICE_UNAVAILABLE);
42
+ }
43
+ }
44
+ async poll(flowId, user) {
45
+ if (!flowId) {
46
+ throw new common_1.HttpException('flowId query parameter is required', common_1.HttpStatus.BAD_REQUEST);
47
+ }
48
+ try {
49
+ return await this.oauthService.pollAuthorization(flowId, user.id);
50
+ }
51
+ catch (err) {
52
+ const message = err instanceof Error ? err.message : 'Failed to poll MiniMax OAuth';
53
+ throw new common_1.HttpException(message, common_1.HttpStatus.SERVICE_UNAVAILABLE);
54
+ }
55
+ }
56
+ };
57
+ exports.MinimaxOauthController = MinimaxOauthController;
58
+ __decorate([
59
+ (0, common_1.Post)('start'),
60
+ __param(0, (0, common_1.Query)('agentName')),
61
+ __param(1, (0, common_1.Query)('region')),
62
+ __param(2, (0, current_user_decorator_1.CurrentUser)()),
63
+ __metadata("design:type", Function),
64
+ __metadata("design:paramtypes", [String, Object, Object]),
65
+ __metadata("design:returntype", Promise)
66
+ ], MinimaxOauthController.prototype, "start", null);
67
+ __decorate([
68
+ (0, common_1.Get)('poll'),
69
+ __param(0, (0, common_1.Query)('flowId')),
70
+ __param(1, (0, current_user_decorator_1.CurrentUser)()),
71
+ __metadata("design:type", Function),
72
+ __metadata("design:paramtypes", [String, Object]),
73
+ __metadata("design:returntype", Promise)
74
+ ], MinimaxOauthController.prototype, "poll", null);
75
+ exports.MinimaxOauthController = MinimaxOauthController = __decorate([
76
+ (0, common_1.Controller)('api/v1/oauth/minimax'),
77
+ __metadata("design:paramtypes", [minimax_oauth_service_1.MinimaxOauthService,
78
+ resolve_agent_service_1.ResolveAgentService])
79
+ ], MinimaxOauthController);
80
+ //# sourceMappingURL=minimax-oauth.controller.js.map
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var MinimaxOauthService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.MINIMAX_DEFAULT_RESOURCE_URL = exports.MinimaxOauthService = void 0;
14
+ exports.isMinimaxRegion = isMinimaxRegion;
15
+ const common_1 = require("@nestjs/common");
16
+ const config_1 = require("@nestjs/config");
17
+ const crypto_1 = require("crypto");
18
+ const routing_service_1 = require("./routing.service");
19
+ const model_discovery_service_1 = require("./model-discovery/model-discovery.service");
20
+ const provider_base_url_1 = require("./provider-base-url");
21
+ const MINIMAX_BASE_URLS = {
22
+ global: 'https://api.minimax.io',
23
+ cn: 'https://api.minimaxi.com',
24
+ };
25
+ const DEFAULT_REGION = 'global';
26
+ const DEFAULT_BASE_URL = MINIMAX_BASE_URLS[DEFAULT_REGION];
27
+ const DEFAULT_CLIENT_ID = '78257093-7e40-4613-99e0-527b14b39113';
28
+ const DEFAULT_RESOURCE_URL = `${DEFAULT_BASE_URL}/anthropic`;
29
+ exports.MINIMAX_DEFAULT_RESOURCE_URL = DEFAULT_RESOURCE_URL;
30
+ const SCOPE = 'group_id profile model.completion';
31
+ const USER_CODE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:user_code';
32
+ const DEFAULT_POLL_INTERVAL_MS = 2000;
33
+ const ABSOLUTE_TIME_THRESHOLD_MS = 10_000_000_000;
34
+ function isMinimaxRegion(value) {
35
+ return value === 'global' || value === 'cn';
36
+ }
37
+ function getMinimaxBaseUrl(region = DEFAULT_REGION) {
38
+ return MINIMAX_BASE_URLS[region];
39
+ }
40
+ function buildMinimaxCodeUrl(baseUrl) {
41
+ return `${baseUrl}/oauth/code`;
42
+ }
43
+ function buildMinimaxTokenUrl(baseUrl) {
44
+ return `${baseUrl}/oauth/token`;
45
+ }
46
+ function buildMinimaxResourceUrl(baseUrl) {
47
+ return `${baseUrl}/anthropic`;
48
+ }
49
+ function getMinimaxResourceUrl(resourceUrl) {
50
+ if (!resourceUrl)
51
+ return null;
52
+ return (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(resourceUrl);
53
+ }
54
+ function getMinimaxOauthBaseUrl(resourceUrl) {
55
+ const normalized = getMinimaxResourceUrl(resourceUrl);
56
+ if (!normalized)
57
+ return DEFAULT_BASE_URL;
58
+ return new URL(normalized).origin;
59
+ }
60
+ function toAbsoluteExpiryTimestamp(expiredIn) {
61
+ if (expiredIn > ABSOLUTE_TIME_THRESHOLD_MS)
62
+ return expiredIn;
63
+ return Date.now() + expiredIn * 1000;
64
+ }
65
+ function toPollIntervalMs(interval) {
66
+ if (!interval || interval <= 0)
67
+ return DEFAULT_POLL_INTERVAL_MS;
68
+ return interval >= 1000 ? interval : interval * 1000;
69
+ }
70
+ function isOAuthTokenBlob(value) {
71
+ if (!value || typeof value !== 'object')
72
+ return false;
73
+ const blob = value;
74
+ return (typeof blob.t === 'string' &&
75
+ typeof blob.r === 'string' &&
76
+ typeof blob.e === 'number' &&
77
+ Number.isFinite(blob.e) &&
78
+ (blob.u === undefined || typeof blob.u === 'string'));
79
+ }
80
+ let MinimaxOauthService = MinimaxOauthService_1 = class MinimaxOauthService {
81
+ routingService;
82
+ configService;
83
+ discoveryService;
84
+ logger = new common_1.Logger(MinimaxOauthService_1.name);
85
+ pending = new Map();
86
+ clientId;
87
+ constructor(routingService, configService, discoveryService) {
88
+ this.routingService = routingService;
89
+ this.configService = configService;
90
+ this.discoveryService = discoveryService;
91
+ this.clientId = this.configService.get('MINIMAX_OAUTH_CLIENT_ID') ?? DEFAULT_CLIENT_ID;
92
+ }
93
+ async startAuthorization(agentId, userId, region = DEFAULT_REGION) {
94
+ this.cleanupExpired();
95
+ const verifier = (0, crypto_1.randomBytes)(32).toString('base64url');
96
+ const challenge = (0, crypto_1.createHash)('sha256').update(verifier).digest('base64url');
97
+ const flowId = (0, crypto_1.randomBytes)(16).toString('base64url');
98
+ const baseUrl = getMinimaxBaseUrl(region);
99
+ const resourceUrl = buildMinimaxResourceUrl(baseUrl);
100
+ const response = await fetch(buildMinimaxCodeUrl(baseUrl), {
101
+ method: 'POST',
102
+ headers: {
103
+ 'Content-Type': 'application/x-www-form-urlencoded',
104
+ Accept: 'application/json',
105
+ 'x-request-id': (0, crypto_1.randomUUID)(),
106
+ },
107
+ body: new URLSearchParams({
108
+ response_type: 'code',
109
+ client_id: this.clientId,
110
+ scope: SCOPE,
111
+ code_challenge: challenge,
112
+ code_challenge_method: 'S256',
113
+ state: flowId,
114
+ }),
115
+ });
116
+ if (!response.ok) {
117
+ const text = await response.text();
118
+ this.logger.error(`MiniMax OAuth code request failed: ${text}`);
119
+ throw new Error('Failed to start MiniMax OAuth');
120
+ }
121
+ const payload = (await response.json());
122
+ if (!payload.user_code || !payload.verification_uri || !payload.expired_in) {
123
+ throw new Error(payload.error ?? 'MiniMax OAuth returned an incomplete authorization payload.');
124
+ }
125
+ if (payload.state !== flowId) {
126
+ throw new Error('MiniMax OAuth state mismatch');
127
+ }
128
+ const pollIntervalMs = toPollIntervalMs(payload.interval);
129
+ const expiresAt = toAbsoluteExpiryTimestamp(payload.expired_in);
130
+ this.pending.set(flowId, {
131
+ verifier,
132
+ userCode: payload.user_code,
133
+ agentId,
134
+ userId,
135
+ baseUrl,
136
+ resourceUrl,
137
+ expiresAt,
138
+ pollIntervalMs,
139
+ });
140
+ return {
141
+ flowId,
142
+ userCode: payload.user_code,
143
+ verificationUri: payload.verification_uri,
144
+ expiresAt,
145
+ pollIntervalMs,
146
+ };
147
+ }
148
+ async pollAuthorization(flowId, userId) {
149
+ this.cleanupExpired();
150
+ const pending = this.pending.get(flowId);
151
+ if (!pending) {
152
+ return { status: 'error', message: 'MiniMax login expired. Start again.' };
153
+ }
154
+ if (pending.userId !== userId) {
155
+ return { status: 'error', message: 'MiniMax login session does not match the current user.' };
156
+ }
157
+ if (Date.now() >= pending.expiresAt) {
158
+ this.pending.delete(flowId);
159
+ return { status: 'error', message: 'MiniMax login expired. Start again.' };
160
+ }
161
+ const response = await fetch(buildMinimaxTokenUrl(pending.baseUrl), {
162
+ method: 'POST',
163
+ headers: {
164
+ 'Content-Type': 'application/x-www-form-urlencoded',
165
+ Accept: 'application/json',
166
+ },
167
+ body: new URLSearchParams({
168
+ grant_type: USER_CODE_GRANT_TYPE,
169
+ client_id: this.clientId,
170
+ user_code: pending.userCode,
171
+ code_verifier: pending.verifier,
172
+ }),
173
+ });
174
+ const text = await response.text();
175
+ const payload = this.parseTokenResponse(text);
176
+ if (!response.ok) {
177
+ const message = payload?.base_resp?.status_msg ?? text ?? 'MiniMax OAuth failed';
178
+ this.pending.delete(flowId);
179
+ return { status: 'error', message };
180
+ }
181
+ if (!payload) {
182
+ this.pending.delete(flowId);
183
+ return { status: 'error', message: 'MiniMax OAuth returned an invalid token response.' };
184
+ }
185
+ if (payload.status === 'error') {
186
+ this.pending.delete(flowId);
187
+ return { status: 'error', message: 'MiniMax OAuth failed. Please try again later.' };
188
+ }
189
+ if (payload.status !== 'success') {
190
+ return {
191
+ status: 'pending',
192
+ message: 'Waiting for MiniMax approval…',
193
+ pollIntervalMs: pending.pollIntervalMs,
194
+ };
195
+ }
196
+ if (!payload.access_token || !payload.refresh_token || !payload.expired_in) {
197
+ this.pending.delete(flowId);
198
+ return { status: 'error', message: 'MiniMax OAuth returned an incomplete token payload.' };
199
+ }
200
+ this.pending.delete(flowId);
201
+ const resourceUrl = getMinimaxResourceUrl(payload.resource_url) ?? pending.resourceUrl;
202
+ const blob = {
203
+ t: payload.access_token,
204
+ r: payload.refresh_token,
205
+ e: toAbsoluteExpiryTimestamp(payload.expired_in),
206
+ u: resourceUrl,
207
+ };
208
+ const { provider: savedProvider } = await this.routingService.upsertProvider(pending.agentId, pending.userId, 'minimax', JSON.stringify(blob), 'subscription');
209
+ try {
210
+ await this.discoveryService.discoverModels(savedProvider);
211
+ await this.routingService.recalculateTiers(pending.agentId);
212
+ }
213
+ catch (err) {
214
+ this.logger.warn(`Model discovery after MiniMax OAuth failed: ${err}`);
215
+ }
216
+ this.logger.log(`MiniMax OAuth token stored for agent=${pending.agentId}`);
217
+ return { status: 'success' };
218
+ }
219
+ async refreshAccessToken(refreshToken, resourceUrl) {
220
+ const baseUrl = getMinimaxOauthBaseUrl(resourceUrl);
221
+ const response = await fetch(buildMinimaxTokenUrl(baseUrl), {
222
+ method: 'POST',
223
+ headers: {
224
+ 'Content-Type': 'application/x-www-form-urlencoded',
225
+ Accept: 'application/json',
226
+ },
227
+ body: new URLSearchParams({
228
+ grant_type: 'refresh_token',
229
+ refresh_token: refreshToken,
230
+ client_id: this.clientId,
231
+ }),
232
+ });
233
+ if (!response.ok) {
234
+ const text = await response.text();
235
+ this.logger.error(`MiniMax token refresh failed: ${text}`);
236
+ throw new Error('Token refresh failed');
237
+ }
238
+ const payload = (await response.json());
239
+ if (!payload.access_token || !payload.expired_in) {
240
+ throw new Error('MiniMax token refresh returned an incomplete payload');
241
+ }
242
+ const nextResourceUrl = getMinimaxResourceUrl(payload.resource_url) ??
243
+ getMinimaxResourceUrl(resourceUrl) ??
244
+ buildMinimaxResourceUrl(baseUrl);
245
+ return {
246
+ t: payload.access_token,
247
+ r: payload.refresh_token || refreshToken,
248
+ e: toAbsoluteExpiryTimestamp(payload.expired_in),
249
+ ...(nextResourceUrl ? { u: nextResourceUrl } : {}),
250
+ };
251
+ }
252
+ async unwrapToken(rawValue, agentId, userId) {
253
+ let blob;
254
+ try {
255
+ const parsed = JSON.parse(rawValue);
256
+ if (!isOAuthTokenBlob(parsed))
257
+ return null;
258
+ blob = parsed;
259
+ }
260
+ catch {
261
+ return null;
262
+ }
263
+ if (!blob.t || !blob.r || !blob.e)
264
+ return null;
265
+ if (Date.now() < blob.e - 60_000)
266
+ return blob;
267
+ try {
268
+ const refreshed = await this.refreshAccessToken(blob.r, blob.u);
269
+ await this.routingService.upsertProvider(agentId, userId, 'minimax', JSON.stringify(refreshed), 'subscription');
270
+ this.logger.log(`MiniMax OAuth token refreshed for agent=${agentId}`);
271
+ return refreshed;
272
+ }
273
+ catch (err) {
274
+ this.logger.error(`Failed to refresh MiniMax token: ${err}`);
275
+ return blob;
276
+ }
277
+ }
278
+ getPendingCount() {
279
+ return this.pending.size;
280
+ }
281
+ cleanupExpired() {
282
+ const now = Date.now();
283
+ for (const [flowId, pending] of this.pending.entries()) {
284
+ if (pending.expiresAt <= now)
285
+ this.pending.delete(flowId);
286
+ }
287
+ }
288
+ parseTokenResponse(text) {
289
+ if (!text)
290
+ return null;
291
+ try {
292
+ return JSON.parse(text);
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ }
298
+ };
299
+ exports.MinimaxOauthService = MinimaxOauthService;
300
+ exports.MinimaxOauthService = MinimaxOauthService = MinimaxOauthService_1 = __decorate([
301
+ (0, common_1.Injectable)(),
302
+ __metadata("design:paramtypes", [routing_service_1.RoutingService,
303
+ config_1.ConfigService,
304
+ model_discovery_service_1.ModelDiscoveryService])
305
+ ], MinimaxOauthService);
306
+ //# sourceMappingURL=minimax-oauth.service.js.map
@@ -23,6 +23,7 @@ const provider_model_fetcher_service_1 = require("./provider-model-fetcher.servi
23
23
  const crypto_util_1 = require("../../common/utils/crypto.util");
24
24
  const quality_score_util_1 = require("../../database/quality-score.util");
25
25
  const pricing_sync_service_1 = require("../../database/pricing-sync.service");
26
+ const openai_oauth_types_1 = require("../openai-oauth.types");
26
27
  const model_fallback_1 = require("./model-fallback");
27
28
  const customProviderKey = (id) => `custom:${id}`;
28
29
  const customModelKey = (id, modelName) => `custom:${id}/${modelName}`;
@@ -40,6 +41,7 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
40
41
  }
41
42
  async discoverModels(provider) {
42
43
  let apiKey = '';
44
+ let endpointOverride;
43
45
  if (provider.api_key_encrypted) {
44
46
  try {
45
47
  apiKey = (0, crypto_util_1.decrypt)(provider.api_key_encrypted, (0, crypto_util_1.getEncryptionSecret)());
@@ -49,17 +51,17 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
49
51
  return [];
50
52
  }
51
53
  }
52
- if (provider.auth_type === 'subscription' &&
53
- provider.provider.toLowerCase() === 'openai' &&
54
- apiKey) {
55
- try {
56
- const blob = JSON.parse(apiKey);
57
- if (blob.t) {
54
+ if (provider.auth_type === 'subscription' && apiKey) {
55
+ const lowerProvider = provider.provider.toLowerCase();
56
+ if (lowerProvider === 'openai' || lowerProvider === 'minimax') {
57
+ const blob = (0, openai_oauth_types_1.parseOAuthTokenBlob)(apiKey);
58
+ if (blob?.t) {
58
59
  apiKey = blob.t;
60
+ if (lowerProvider === 'minimax' && blob.u) {
61
+ endpointOverride = blob.u;
62
+ }
59
63
  }
60
64
  }
61
- catch {
62
- }
63
65
  }
64
66
  let raw;
65
67
  if (provider.auth_type === 'subscription' && !apiKey) {
@@ -69,7 +71,7 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
69
71
  }
70
72
  }
71
73
  else {
72
- raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type);
74
+ raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type, endpointOverride);
73
75
  if (raw.length === 0) {
74
76
  raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider);
75
77
  if (raw.length > 0) {
@@ -87,6 +87,7 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
87
87
  const knownPrefixes = (0, subscription_capabilities_1.getSubscriptionKnownModels)(providerId);
88
88
  if (!knownPrefixes)
89
89
  return [];
90
+ const normalizedKnownPrefixes = knownPrefixes.map((modelId) => modelId.toLowerCase());
90
91
  const capabilities = (0, subscription_capabilities_1.getSubscriptionCapabilities)(providerId);
91
92
  const models = [];
92
93
  const seen = new Set();
@@ -96,8 +97,9 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
96
97
  if (!fullId.startsWith(`${orPrefix}/`))
97
98
  continue;
98
99
  const modelId = fullId.substring(orPrefix.length + 1);
99
- if (!knownPrefixes.some((p) => modelId.startsWith(p)))
100
+ if (!normalizedKnownPrefixes.some((p) => modelId.toLowerCase().startsWith(p))) {
100
101
  continue;
102
+ }
101
103
  if (seen.has(modelId))
102
104
  continue;
103
105
  seen.add(modelId);
@@ -120,7 +122,11 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
120
122
  }
121
123
  const defaultCtx = capabilities?.maxContextWindow ?? 200000;
122
124
  for (const modelId of knownPrefixes) {
123
- const covered = models.some((m) => m.id === modelId || m.id.startsWith(`${modelId}-`));
125
+ const lowerModelId = modelId.toLowerCase();
126
+ const covered = models.some((m) => {
127
+ const lowerDiscovered = m.id.toLowerCase();
128
+ return lowerDiscovered === lowerModelId || lowerDiscovered.startsWith(`${lowerModelId}-`);
129
+ });
124
130
  if (covered)
125
131
  continue;
126
132
  models.push({
@@ -144,7 +150,11 @@ function supplementWithKnownModels(raw, providerId) {
144
150
  const capabilities = (0, subscription_capabilities_1.getSubscriptionCapabilities)(providerId);
145
151
  const defaultCtx = capabilities?.maxContextWindow ?? 200000;
146
152
  for (const modelId of knownModels) {
147
- const covered = raw.some((m) => m.id === modelId || m.id.startsWith(`${modelId}-`));
153
+ const lowerModelId = modelId.toLowerCase();
154
+ const covered = raw.some((m) => {
155
+ const lowerDiscovered = m.id.toLowerCase();
156
+ return lowerDiscovered === lowerModelId || lowerDiscovered.startsWith(`${lowerModelId}-`);
157
+ });
148
158
  if (covered)
149
159
  continue;
150
160
  raw.push({
@@ -10,10 +10,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.ProviderModelFetcherService = exports.PROVIDER_CONFIGS = void 0;
11
11
  const common_1 = require("@nestjs/common");
12
12
  const ollama_1 = require("../../common/constants/ollama");
13
+ const provider_base_url_1 = require("../provider-base-url");
13
14
  const FETCH_TIMEOUT_MS = 5000;
14
15
  const DEFAULT_CONTEXT_WINDOW = 128000;
15
16
  const ANTHROPIC_DEFAULT_CONTEXT = 200000;
16
17
  const GEMINI_DEFAULT_CONTEXT = 1000000;
18
+ const MINIMAX_SUBSCRIPTION_MODELS_URL = 'https://api.minimax.io/anthropic/v1/models?limit=100';
17
19
  function parseOpenAI(body, provider) {
18
20
  const data = body?.data;
19
21
  if (!Array.isArray(data))
@@ -212,6 +214,14 @@ exports.PROVIDER_CONFIGS = {
212
214
  buildHeaders: bearerHeaders,
213
215
  parse: parseOpenAI,
214
216
  },
217
+ 'minimax-subscription': {
218
+ endpoint: MINIMAX_SUBSCRIPTION_MODELS_URL,
219
+ buildHeaders: (key) => ({
220
+ Authorization: `Bearer ${key}`,
221
+ 'anthropic-version': '2023-06-01',
222
+ }),
223
+ parse: parseAnthropic,
224
+ },
215
225
  qwen: {
216
226
  endpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1/models',
217
227
  buildHeaders: bearerHeaders,
@@ -257,17 +267,29 @@ exports.PROVIDER_CONFIGS = {
257
267
  };
258
268
  let ProviderModelFetcherService = ProviderModelFetcherService_1 = class ProviderModelFetcherService {
259
269
  logger = new common_1.Logger(ProviderModelFetcherService_1.name);
260
- async fetch(providerId, apiKey, authType) {
270
+ async fetch(providerId, apiKey, authType, endpointOverride) {
261
271
  let configKey = providerId.toLowerCase();
262
272
  if (configKey === 'openai' && authType === 'subscription') {
263
273
  configKey = 'openai-subscription';
264
274
  }
275
+ else if (configKey === 'minimax' && authType === 'subscription') {
276
+ configKey = 'minimax-subscription';
277
+ }
265
278
  const config = exports.PROVIDER_CONFIGS[configKey];
266
279
  if (!config) {
267
280
  this.logger.warn(`No fetcher config for provider: ${providerId}`);
268
281
  return [];
269
282
  }
270
- const url = typeof config.endpoint === 'function' ? config.endpoint(apiKey) : config.endpoint;
283
+ let url = typeof config.endpoint === 'function' ? config.endpoint(apiKey) : config.endpoint;
284
+ if (endpointOverride && configKey === 'minimax-subscription') {
285
+ const minimaxBaseUrl = (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(endpointOverride);
286
+ if (minimaxBaseUrl) {
287
+ url = `${minimaxBaseUrl}/v1/models?limit=100`;
288
+ }
289
+ else {
290
+ this.logger.warn('Ignoring invalid MiniMax subscription endpoint override');
291
+ }
292
+ }
271
293
  const headers = config.buildHeaders(apiKey, authType);
272
294
  try {
273
295
  const controller = new AbortController();
@@ -1,6 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseOAuthTokenBlob = parseOAuthTokenBlob;
3
4
  exports.oauthDoneHtml = oauthDoneHtml;
5
+ function parseOAuthTokenBlob(rawValue) {
6
+ try {
7
+ const parsed = JSON.parse(rawValue);
8
+ if (typeof parsed?.t !== 'string' ||
9
+ typeof parsed?.r !== 'string' ||
10
+ typeof parsed?.e !== 'number' ||
11
+ (parsed.u !== undefined && typeof parsed.u !== 'string')) {
12
+ return null;
13
+ }
14
+ return {
15
+ t: parsed.t,
16
+ r: parsed.r,
17
+ e: parsed.e,
18
+ ...(parsed.u !== undefined ? { u: parsed.u } : {}),
19
+ };
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
4
25
  function oauthDoneHtml(success) {
5
26
  const message = success ? 'manifest-oauth-success' : 'manifest-oauth-error';
6
27
  const text = success
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeProviderBaseUrl = normalizeProviderBaseUrl;
4
+ exports.normalizeMinimaxSubscriptionBaseUrl = normalizeMinimaxSubscriptionBaseUrl;
5
+ function normalizeProviderBaseUrl(baseUrl) {
6
+ return baseUrl.replace(/\/+$/, '').replace(/\/v1$/, '');
7
+ }
8
+ const MINIMAX_SUBSCRIPTION_BASE_URLS = new Set([
9
+ 'https://api.minimax.io/anthropic',
10
+ 'https://api.minimaxi.com/anthropic',
11
+ ]);
12
+ function normalizeMinimaxSubscriptionBaseUrl(baseUrl) {
13
+ try {
14
+ const url = new URL(baseUrl);
15
+ if (url.protocol !== 'https:' || url.username || url.password) {
16
+ return null;
17
+ }
18
+ const normalized = normalizeProviderBaseUrl(`${url.origin}${url.pathname}`);
19
+ return MINIMAX_SUBSCRIPTION_BASE_URLS.has(normalized) ? normalized : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ //# sourceMappingURL=provider-base-url.js.map
@@ -4,21 +4,19 @@ exports.toResponsesRequest = toResponsesRequest;
4
4
  exports.fromResponsesResponse = fromResponsesResponse;
5
5
  exports.transformResponsesStreamChunk = transformResponsesStreamChunk;
6
6
  const crypto_1 = require("crypto");
7
+ const DEFAULT_INSTRUCTIONS = 'You are a helpful assistant.';
7
8
  function toResponsesRequest(body, model) {
8
9
  const messages = (body.messages ?? []);
9
- const systemMsg = messages.find((m) => m.role === 'system');
10
10
  const input = messages
11
- .filter((m) => m.role !== 'system')
11
+ .filter((m) => m.role !== 'system' && m.role !== 'developer')
12
12
  .map((m) => ({ role: m.role, content: convertContent(m.content, m.role) }));
13
13
  const request = {
14
14
  model,
15
15
  input,
16
16
  stream: body.stream !== false,
17
17
  store: false,
18
+ instructions: extractInstructions(messages),
18
19
  };
19
- if (systemMsg && typeof systemMsg.content === 'string') {
20
- request.instructions = systemMsg.content;
21
- }
22
20
  return request;
23
21
  }
24
22
  function fromResponsesResponse(data, model) {
@@ -114,6 +112,28 @@ function convertContent(content, role) {
114
112
  return part;
115
113
  });
116
114
  }
115
+ function extractInstructions(messages) {
116
+ const parts = [];
117
+ for (const message of messages) {
118
+ if (message.role !== 'system' && message.role !== 'developer')
119
+ continue;
120
+ parts.push(...extractTextParts(message.content));
121
+ }
122
+ const instructions = parts
123
+ .map((part) => part.trim())
124
+ .filter(Boolean)
125
+ .join('\n\n');
126
+ return instructions || DEFAULT_INSTRUCTIONS;
127
+ }
128
+ function extractTextParts(content) {
129
+ if (typeof content === 'string')
130
+ return [content];
131
+ if (!Array.isArray(content))
132
+ return [];
133
+ return content
134
+ .filter((part) => part.type === 'text' && typeof part.text === 'string')
135
+ .map((part) => part.text);
136
+ }
117
137
  function safeParse(str) {
118
138
  try {
119
139
  return JSON.parse(str);