manifest 5.38.1 → 5.38.4
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/dist/backend/common/utils/hash.util.js +5 -2
- package/dist/backend/common/utils/url-validation.js +1 -1
- package/dist/backend/database/database-seeder.service.js +1 -0
- package/dist/backend/database/models-dev-sync.service.js +27 -0
- package/dist/backend/model-discovery/known-model-prices.js +18 -0
- package/dist/backend/model-discovery/model-discovery.service.js +13 -1
- package/dist/backend/model-discovery/model-fallback.js +24 -0
- package/dist/backend/model-discovery/provider-model-fetcher.service.js +5 -5
- package/dist/backend/notifications/dto/set-notification-email.dto.js +24 -0
- package/dist/backend/notifications/notifications.controller.js +2 -1
- package/dist/backend/public-stats/public-stats.controller.js +34 -34
- package/dist/backend/public-stats/public-stats.service.js +33 -9
- package/dist/backend/routing/oauth/openai-oauth.controller.js +4 -2
- package/dist/backend/routing/oauth/openai-oauth.types.js +3 -2
- package/dist/backend/routing/proxy/proxy-error-sanitizer.js +8 -1
- package/dist/backend/routing/routing-core/tier-auto-assign.service.js +10 -5
- package/dist/backend/scoring/index.js +3 -11
- package/dist/openclaw.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,11 @@ function verifyKey(input, storedHash) {
|
|
|
20
20
|
const actual = (0, crypto_1.scryptSync)(input, salt, KEY_LENGTH);
|
|
21
21
|
return expected.length === KEY_LENGTH && (0, crypto_1.timingSafeEqual)(actual, expected);
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (!/^[0-9a-fA-F]{64}$/.test(storedHash))
|
|
24
|
+
return false;
|
|
25
|
+
const legacyHashBuf = (0, crypto_1.scryptSync)(input, LEGACY_SALT, KEY_LENGTH);
|
|
26
|
+
const storedBuf = Buffer.from(storedHash, 'hex');
|
|
27
|
+
return (0, crypto_1.timingSafeEqual)(legacyHashBuf, storedBuf);
|
|
25
28
|
}
|
|
26
29
|
function keyPrefix(key) {
|
|
27
30
|
return key.substring(0, 12);
|
|
@@ -54,7 +54,7 @@ function isCloudMetadataIp(ip) {
|
|
|
54
54
|
return CLOUD_METADATA_RANGES.some((range) => (addr & range.mask) === range.addr);
|
|
55
55
|
}
|
|
56
56
|
async function validatePublicUrl(url) {
|
|
57
|
-
if (process.env['NODE_ENV'] === 'test')
|
|
57
|
+
if (process.env['NODE_ENV'] === 'test' && process.env['SKIP_SSRF_VALIDATION'] !== 'false')
|
|
58
58
|
return;
|
|
59
59
|
let parsed;
|
|
60
60
|
try {
|
|
@@ -61,6 +61,7 @@ let DatabaseSeederService = DatabaseSeederService_1 = class DatabaseSeederServic
|
|
|
61
61
|
await this.seedTenantAndAgent();
|
|
62
62
|
await this.seedAgentMessages();
|
|
63
63
|
this.logger.log('Seeded demo data (dev/test only, SEED_DATA=true)');
|
|
64
|
+
this.logger.warn('SECURITY: Default seed credentials are active (admin@manifest.build). Do NOT use in production.');
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
async runBetterAuthMigrations() {
|
|
@@ -43,6 +43,10 @@ const SHORT_DATE_SUFFIX_RE = /-\d{4}$/;
|
|
|
43
43
|
const LATEST_SUFFIX = '-latest';
|
|
44
44
|
const GOOGLE_VARIANT_RE = /-(?:preview(?:-\d{2,4}){1,3}|exp-\d{4}|latest)$/;
|
|
45
45
|
const REASONING_SUFFIX_RE = /-(reasoning|non-reasoning)$/;
|
|
46
|
+
const LEGACY_NAME_ALIASES = new Map([
|
|
47
|
+
['open-mistral-nemo', 'mistral-nemo'],
|
|
48
|
+
['mistral-tiny', 'open-mistral-7b'],
|
|
49
|
+
]);
|
|
46
50
|
let ModelsDevSyncService = ModelsDevSyncService_1 = class ModelsDevSyncService {
|
|
47
51
|
logger = new common_1.Logger(ModelsDevSyncService_1.name);
|
|
48
52
|
cache = new Map();
|
|
@@ -90,6 +94,18 @@ let ModelsDevSyncService = ModelsDevSyncService_1 = class ModelsDevSyncService {
|
|
|
90
94
|
const exact = providerModels.get(modelId);
|
|
91
95
|
if (exact)
|
|
92
96
|
return exact;
|
|
97
|
+
const aliasBase = modelId
|
|
98
|
+
.replace(DATE_SUFFIX_RE, '')
|
|
99
|
+
.replace(SHORT_DATE_SUFFIX_RE, '')
|
|
100
|
+
.replace(/-latest$/, '');
|
|
101
|
+
const aliasTarget = LEGACY_NAME_ALIASES.get(aliasBase) ??
|
|
102
|
+
LEGACY_NAME_ALIASES.get(modelId) ??
|
|
103
|
+
LEGACY_NAME_ALIASES.get(modelId.replace(/-latest$/, ''));
|
|
104
|
+
if (aliasTarget) {
|
|
105
|
+
const found = providerModels.get(aliasTarget);
|
|
106
|
+
if (found)
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
93
109
|
const noVersion = modelId.replace(VERSION_SUFFIX_RE, '');
|
|
94
110
|
if (noVersion !== modelId) {
|
|
95
111
|
const found = providerModels.get(noVersion);
|
|
@@ -135,6 +151,17 @@ let ModelsDevSyncService = ModelsDevSyncService_1 = class ModelsDevSyncService {
|
|
|
135
151
|
return withLatest;
|
|
136
152
|
}
|
|
137
153
|
}
|
|
154
|
+
if (modelId.endsWith(LATEST_SUFFIX)) {
|
|
155
|
+
const base = modelId.slice(0, -LATEST_SUFFIX.length);
|
|
156
|
+
const prefix = `${base}-`;
|
|
157
|
+
for (const [key, entry] of providerModels) {
|
|
158
|
+
if (key.startsWith(prefix) && key !== modelId)
|
|
159
|
+
return entry;
|
|
160
|
+
}
|
|
161
|
+
const baseMatch = providerModels.get(base);
|
|
162
|
+
if (baseMatch)
|
|
163
|
+
return baseMatch;
|
|
164
|
+
}
|
|
138
165
|
return null;
|
|
139
166
|
}
|
|
140
167
|
getModelsForProvider(providerId) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lookupKnownPrice = lookupKnownPrice;
|
|
4
|
+
const PER_MILLION = 1_000_000;
|
|
5
|
+
const KNOWN_PRICES = [
|
|
6
|
+
{ prefix: 'moonshot-v1-', price: { input: 1.66 / PER_MILLION, output: 1.66 / PER_MILLION } },
|
|
7
|
+
{ prefix: 'gemma-3-1b-it', price: { input: 0, output: 0 } },
|
|
8
|
+
{ prefix: 'gemini-pro-latest', price: { input: 1.25 / PER_MILLION, output: 10.0 / PER_MILLION } },
|
|
9
|
+
];
|
|
10
|
+
function lookupKnownPrice(modelId) {
|
|
11
|
+
for (const entry of KNOWN_PRICES) {
|
|
12
|
+
if (modelId.startsWith(entry.prefix) || modelId === entry.prefix) {
|
|
13
|
+
return entry.price;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=known-model-prices.js.map
|
|
@@ -30,6 +30,7 @@ const qwen_region_1 = require("../routing/qwen-region");
|
|
|
30
30
|
const copilot_token_service_1 = require("../routing/proxy/copilot-token.service");
|
|
31
31
|
const anthropic_subscription_probe_1 = require("./anthropic-subscription-probe");
|
|
32
32
|
const model_fallback_1 = require("./model-fallback");
|
|
33
|
+
const known_model_prices_1 = require("./known-model-prices");
|
|
33
34
|
const customProviderKey = (id) => `custom:${id}`;
|
|
34
35
|
const customModelKey = (id, modelName) => `custom:${id}/${modelName}`;
|
|
35
36
|
function isQwenProvider(providerId) {
|
|
@@ -209,7 +210,10 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
|
|
|
209
210
|
return all.find((m) => m.id === modelName);
|
|
210
211
|
}
|
|
211
212
|
enrichModel(model, providerId) {
|
|
212
|
-
if (model.inputPricePerToken !== null &&
|
|
213
|
+
if (model.inputPricePerToken !== null &&
|
|
214
|
+
model.inputPricePerToken >= 0 &&
|
|
215
|
+
model.outputPricePerToken !== null &&
|
|
216
|
+
model.outputPricePerToken >= 0) {
|
|
213
217
|
return this.computeScore(this.applyCapabilities(model, providerId));
|
|
214
218
|
}
|
|
215
219
|
if (this.modelsDevSync) {
|
|
@@ -251,6 +255,14 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
|
|
|
251
255
|
});
|
|
252
256
|
}
|
|
253
257
|
}
|
|
258
|
+
const known = (0, known_model_prices_1.lookupKnownPrice)(model.id);
|
|
259
|
+
if (known) {
|
|
260
|
+
return this.computeScore({
|
|
261
|
+
...model,
|
|
262
|
+
inputPricePerToken: known.input,
|
|
263
|
+
outputPricePerToken: known.output,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
254
266
|
return this.computeScore(model);
|
|
255
267
|
}
|
|
256
268
|
applyCapabilities(model, providerId) {
|
|
@@ -32,10 +32,23 @@ function findOpenRouterPrefix(providerId) {
|
|
|
32
32
|
}
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
|
+
const OPENROUTER_NAME_ALIASES = new Map([
|
|
36
|
+
['voxtral-small', 'voxtral-small-24b'],
|
|
37
|
+
['open-mistral-nemo', 'mistral-nemo'],
|
|
38
|
+
['mistral-tiny', 'open-mistral-7b'],
|
|
39
|
+
]);
|
|
35
40
|
function lookupWithVariants(pricingSync, prefix, modelId) {
|
|
36
41
|
const exact = pricingSync.lookupPricing(`${prefix}/${modelId}`);
|
|
37
42
|
if (exact)
|
|
38
43
|
return exact;
|
|
44
|
+
for (const [from, to] of OPENROUTER_NAME_ALIASES) {
|
|
45
|
+
if (modelId.includes(from)) {
|
|
46
|
+
const aliased = modelId.replace(from, to);
|
|
47
|
+
const aliasResult = pricingSync.lookupPricing(`${prefix}/${aliased}`);
|
|
48
|
+
if (aliasResult)
|
|
49
|
+
return aliasResult;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
39
52
|
const dotVariant = modelId.replace(/-(\d+)-(\d)/g, '-$1.$2');
|
|
40
53
|
if (dotVariant !== modelId) {
|
|
41
54
|
const dotResult = pricingSync.lookupPricing(`${prefix}/${dotVariant}`);
|
|
@@ -69,6 +82,17 @@ function lookupWithVariants(pricingSync, prefix, modelId) {
|
|
|
69
82
|
if (result)
|
|
70
83
|
return result;
|
|
71
84
|
}
|
|
85
|
+
if (modelId.endsWith('-latest')) {
|
|
86
|
+
const base = modelId.slice(0, -'-latest'.length);
|
|
87
|
+
const scanPrefix = `${prefix}/${base}-`;
|
|
88
|
+
for (const [key] of pricingSync.getAll()) {
|
|
89
|
+
if (key.startsWith(scanPrefix)) {
|
|
90
|
+
const found = pricingSync.lookupPricing(key);
|
|
91
|
+
if (found)
|
|
92
|
+
return found;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
72
96
|
return null;
|
|
73
97
|
}
|
|
74
98
|
function buildModelsDevFallback(modelsDevSync, providerId) {
|
|
@@ -63,10 +63,10 @@ function parseOpenAIDeduped(body, provider) {
|
|
|
63
63
|
}
|
|
64
64
|
exports.UNIVERSAL_NON_CHAT_RE = /(?:embed|tts|whisper|dall-e|imagen|cogview|wanx|sambert|paraformer|text-embedding|speech-to|voice-|audio-turbo)/i;
|
|
65
65
|
exports.PROVIDER_NON_CHAT = {
|
|
66
|
-
openai: /(?:moderation|davinci|babbage|^text-|realtime|-transcribe|^sora|^gpt-3\.5-turbo-instruct|audio|^chatgpt-image)/i,
|
|
66
|
+
openai: /(?:moderation|davinci|babbage|^text-|realtime|-transcribe|^sora|^gpt-3\.5-turbo-instruct|audio|^chatgpt-image|search-api)/i,
|
|
67
67
|
'openai-subscription': /(?:moderation|davinci|babbage|^text-|realtime|-transcribe|^sora|audio|^chatgpt-image)/i,
|
|
68
|
-
gemini: /(?:^aqs-|nano-banana|^deep-research|computer-use|^lyria|^gemini-2\.0-flash-lite$|flash-lite-preview)/i,
|
|
69
|
-
mistral: /(?:^mistral-ocr|moderation|voxtral-.*-(?:transcribe|realtime)|^labs-)/i,
|
|
68
|
+
gemini: /(?:^aqs-|nano-banana|^deep-research|computer-use|^lyria|^gemini-2\.0-flash-lite$|flash-lite-preview|robotics)/i,
|
|
69
|
+
mistral: /(?:^mistral-ocr|moderation|voxtral-.*-(?:transcribe|realtime)|^labs-|^mistral-vibe-cli)/i,
|
|
70
70
|
xai: /(?:imagine|multi-agent)/i,
|
|
71
71
|
};
|
|
72
72
|
exports.PROVIDER_BLOCKLIST = {
|
|
@@ -174,8 +174,8 @@ function parseOpenRouter(body, provider) {
|
|
|
174
174
|
displayName: entry.name || entry.id,
|
|
175
175
|
provider,
|
|
176
176
|
contextWindow: entry.context_length ?? 128000,
|
|
177
|
-
inputPricePerToken: prompt !== null && Number.isFinite(prompt) ? prompt : null,
|
|
178
|
-
outputPricePerToken: completion !== null && Number.isFinite(completion) ? completion : null,
|
|
177
|
+
inputPricePerToken: prompt !== null && Number.isFinite(prompt) && prompt >= 0 ? prompt : null,
|
|
178
|
+
outputPricePerToken: completion !== null && Number.isFinite(completion) && completion >= 0 ? completion : null,
|
|
179
179
|
capabilityReasoning: false,
|
|
180
180
|
capabilityCode: false,
|
|
181
181
|
qualityScore: 3,
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SetNotificationEmailDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
const class_transformer_1 = require("class-transformer");
|
|
15
|
+
class SetNotificationEmailDto {
|
|
16
|
+
email;
|
|
17
|
+
}
|
|
18
|
+
exports.SetNotificationEmailDto = SetNotificationEmailDto;
|
|
19
|
+
__decorate([
|
|
20
|
+
(0, class_validator_1.IsEmail)(),
|
|
21
|
+
(0, class_transformer_1.Transform)(({ value }) => (typeof value === 'string' ? value.trim().toLowerCase() : value)),
|
|
22
|
+
__metadata("design:type", String)
|
|
23
|
+
], SetNotificationEmailDto.prototype, "email", void 0);
|
|
24
|
+
//# sourceMappingURL=set-notification-email.dto.js.map
|
|
@@ -23,6 +23,7 @@ const notification_cron_service_1 = require("./services/notification-cron.servic
|
|
|
23
23
|
const limit_check_service_1 = require("./services/limit-check.service");
|
|
24
24
|
const notification_rule_dto_1 = require("./dto/notification-rule.dto");
|
|
25
25
|
const set_email_provider_dto_1 = require("./dto/set-email-provider.dto");
|
|
26
|
+
const set_notification_email_dto_1 = require("./dto/set-notification-email.dto");
|
|
26
27
|
let NotificationsController = NotificationsController_1 = class NotificationsController {
|
|
27
28
|
rulesService;
|
|
28
29
|
notificationLog;
|
|
@@ -145,7 +146,7 @@ __decorate([
|
|
|
145
146
|
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
|
146
147
|
__param(1, (0, common_1.Body)()),
|
|
147
148
|
__metadata("design:type", Function),
|
|
148
|
-
__metadata("design:paramtypes", [Object,
|
|
149
|
+
__metadata("design:paramtypes", [Object, set_notification_email_dto_1.SetNotificationEmailDto]),
|
|
149
150
|
__metadata("design:returntype", Promise)
|
|
150
151
|
], NotificationsController.prototype, "setNotificationEmail", null);
|
|
151
152
|
__decorate([
|
|
@@ -15,91 +15,91 @@ const common_1 = require("@nestjs/common");
|
|
|
15
15
|
const public_decorator_1 = require("../common/decorators/public.decorator");
|
|
16
16
|
const cache_constants_1 = require("../common/constants/cache.constants");
|
|
17
17
|
const public_stats_service_1 = require("./public-stats.service");
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
let
|
|
21
|
-
let
|
|
22
|
-
let
|
|
23
|
-
let
|
|
18
|
+
let cachedUsage = null;
|
|
19
|
+
let usageTimestamp = 0;
|
|
20
|
+
let usageInflight = null;
|
|
21
|
+
let cachedFree = null;
|
|
22
|
+
let freeTimestamp = 0;
|
|
23
|
+
let freeInflight = null;
|
|
24
24
|
let PublicStatsController = PublicStatsController_1 = class PublicStatsController {
|
|
25
25
|
service;
|
|
26
26
|
logger = new common_1.Logger(PublicStatsController_1.name);
|
|
27
27
|
constructor(service) {
|
|
28
28
|
this.service = service;
|
|
29
29
|
}
|
|
30
|
-
async
|
|
31
|
-
if (
|
|
32
|
-
return
|
|
30
|
+
async getUsage() {
|
|
31
|
+
if (cachedUsage && Date.now() - usageTimestamp < cache_constants_1.PUBLIC_STATS_CACHE_TTL_MS) {
|
|
32
|
+
return cachedUsage;
|
|
33
33
|
}
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (!usageInflight) {
|
|
35
|
+
usageInflight = this.refreshUsage().finally(() => {
|
|
36
|
+
usageInflight = null;
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
return
|
|
39
|
+
return usageInflight;
|
|
40
40
|
}
|
|
41
|
-
async
|
|
42
|
-
if (
|
|
43
|
-
return
|
|
41
|
+
async getFreeModels() {
|
|
42
|
+
if (cachedFree && Date.now() - freeTimestamp < cache_constants_1.PUBLIC_STATS_CACHE_TTL_MS) {
|
|
43
|
+
return cachedFree;
|
|
44
44
|
}
|
|
45
|
-
if (!
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (!freeInflight) {
|
|
46
|
+
freeInflight = this.refreshFreeModels().finally(() => {
|
|
47
|
+
freeInflight = null;
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
return
|
|
50
|
+
return freeInflight;
|
|
51
51
|
}
|
|
52
|
-
async
|
|
52
|
+
async refreshUsage() {
|
|
53
53
|
try {
|
|
54
54
|
const stats = await this.service.getUsageStats();
|
|
55
|
-
|
|
55
|
+
cachedUsage = {
|
|
56
56
|
total_messages: stats.total_messages,
|
|
57
57
|
top_models: stats.top_models,
|
|
58
58
|
cached_at: new Date().toISOString(),
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
usageTimestamp = Date.now();
|
|
61
61
|
}
|
|
62
62
|
catch (err) {
|
|
63
63
|
const msg = err instanceof Error ? err.message : String(err);
|
|
64
64
|
this.logger.error(`Failed to fetch usage stats: ${msg}`);
|
|
65
65
|
}
|
|
66
|
-
return (
|
|
66
|
+
return (cachedUsage ?? { total_messages: 0, top_models: [], cached_at: new Date().toISOString() });
|
|
67
67
|
}
|
|
68
|
-
async
|
|
68
|
+
async refreshFreeModels() {
|
|
69
69
|
try {
|
|
70
70
|
const stats = await this.service.getUsageStats();
|
|
71
71
|
const models = this.service.getFreeModels(stats.token_map);
|
|
72
|
-
|
|
72
|
+
cachedFree = {
|
|
73
73
|
models,
|
|
74
74
|
total_models: models.length,
|
|
75
75
|
cached_at: new Date().toISOString(),
|
|
76
76
|
};
|
|
77
|
-
|
|
77
|
+
freeTimestamp = Date.now();
|
|
78
78
|
}
|
|
79
79
|
catch (err) {
|
|
80
80
|
const msg = err instanceof Error ? err.message : String(err);
|
|
81
|
-
this.logger.error(`Failed to fetch
|
|
81
|
+
this.logger.error(`Failed to fetch free models: ${msg}`);
|
|
82
82
|
}
|
|
83
|
-
return
|
|
83
|
+
return cachedFree ?? { models: [], total_models: 0, cached_at: new Date().toISOString() };
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
86
|
exports.PublicStatsController = PublicStatsController;
|
|
87
87
|
__decorate([
|
|
88
88
|
(0, public_decorator_1.Public)(),
|
|
89
|
-
(0, common_1.Get)('
|
|
89
|
+
(0, common_1.Get)('usage'),
|
|
90
90
|
__metadata("design:type", Function),
|
|
91
91
|
__metadata("design:paramtypes", []),
|
|
92
92
|
__metadata("design:returntype", Promise)
|
|
93
|
-
], PublicStatsController.prototype, "
|
|
93
|
+
], PublicStatsController.prototype, "getUsage", null);
|
|
94
94
|
__decorate([
|
|
95
95
|
(0, public_decorator_1.Public)(),
|
|
96
|
-
(0, common_1.Get)('
|
|
96
|
+
(0, common_1.Get)('free-models'),
|
|
97
97
|
__metadata("design:type", Function),
|
|
98
98
|
__metadata("design:paramtypes", []),
|
|
99
99
|
__metadata("design:returntype", Promise)
|
|
100
|
-
], PublicStatsController.prototype, "
|
|
100
|
+
], PublicStatsController.prototype, "getFreeModels", null);
|
|
101
101
|
exports.PublicStatsController = PublicStatsController = PublicStatsController_1 = __decorate([
|
|
102
|
-
(0, common_1.Controller)('api/v1'),
|
|
102
|
+
(0, common_1.Controller)('api/v1/public'),
|
|
103
103
|
__metadata("design:paramtypes", [public_stats_service_1.PublicStatsService])
|
|
104
104
|
], PublicStatsController);
|
|
105
105
|
//# sourceMappingURL=public-stats.controller.js.map
|
|
@@ -19,6 +19,11 @@ const typeorm_2 = require("typeorm");
|
|
|
19
19
|
const agent_message_entity_1 = require("../entities/agent-message.entity");
|
|
20
20
|
const model_pricing_cache_service_1 = require("../model-prices/model-pricing-cache.service");
|
|
21
21
|
const sql_dialect_1 = require("../common/utils/sql-dialect");
|
|
22
|
+
const MAX_RESULTS = 10;
|
|
23
|
+
const EXCLUDED_PROVIDERS = new Set(['Unknown']);
|
|
24
|
+
function isCustomModel(model) {
|
|
25
|
+
return model.startsWith('custom:');
|
|
26
|
+
}
|
|
22
27
|
let PublicStatsService = class PublicStatsService {
|
|
23
28
|
messageRepo;
|
|
24
29
|
pricingCache;
|
|
@@ -37,7 +42,6 @@ let PublicStatsService = class PublicStatsService {
|
|
|
37
42
|
.where('at.model IS NOT NULL')
|
|
38
43
|
.groupBy('at.model')
|
|
39
44
|
.orderBy('usage_count', 'DESC')
|
|
40
|
-
.limit(20)
|
|
41
45
|
.getRawMany(),
|
|
42
46
|
this.messageRepo
|
|
43
47
|
.createQueryBuilder('at')
|
|
@@ -52,12 +56,18 @@ let PublicStatsService = class PublicStatsService {
|
|
|
52
56
|
for (const r of tokenRows) {
|
|
53
57
|
tokenMap.set(r.model, Number(r.tokens ?? 0));
|
|
54
58
|
}
|
|
55
|
-
const
|
|
59
|
+
const eligible = [];
|
|
60
|
+
for (const r of topRows) {
|
|
56
61
|
const modelName = r.model;
|
|
62
|
+
if (isCustomModel(modelName))
|
|
63
|
+
continue;
|
|
57
64
|
const pricing = this.pricingCache.getByModel(modelName);
|
|
58
|
-
|
|
65
|
+
const provider = pricing?.provider || 'Unknown';
|
|
66
|
+
if (EXCLUDED_PROVIDERS.has(provider))
|
|
67
|
+
continue;
|
|
68
|
+
eligible.push({
|
|
59
69
|
model: modelName,
|
|
60
|
-
provider
|
|
70
|
+
provider,
|
|
61
71
|
tokens_7d: tokenMap.get(modelName) ?? 0,
|
|
62
72
|
input_price_per_million: pricing?.input_price_per_token != null
|
|
63
73
|
? Number(pricing.input_price_per_token) * 1_000_000
|
|
@@ -65,9 +75,12 @@ let PublicStatsService = class PublicStatsService {
|
|
|
65
75
|
output_price_per_million: pricing?.output_price_per_token != null
|
|
66
76
|
? Number(pricing.output_price_per_token) * 1_000_000
|
|
67
77
|
: null,
|
|
68
|
-
usage_rank:
|
|
69
|
-
};
|
|
70
|
-
}
|
|
78
|
+
usage_rank: 0,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
eligible.sort((a, b) => b.tokens_7d - a.tokens_7d);
|
|
82
|
+
const topModels = eligible.slice(0, MAX_RESULTS);
|
|
83
|
+
topModels.forEach((m, i) => (m.usage_rank = i + 1));
|
|
71
84
|
return {
|
|
72
85
|
total_messages: Number(countRow?.total ?? 0),
|
|
73
86
|
top_models: topModels,
|
|
@@ -77,12 +90,23 @@ let PublicStatsService = class PublicStatsService {
|
|
|
77
90
|
getFreeModels(tokenMap) {
|
|
78
91
|
return this.pricingCache
|
|
79
92
|
.getAll()
|
|
80
|
-
.filter((e) =>
|
|
93
|
+
.filter((e) => {
|
|
94
|
+
if ((e.input_price_per_token ?? 0) !== 0 || (e.output_price_per_token ?? 0) !== 0)
|
|
95
|
+
return false;
|
|
96
|
+
if (isCustomModel(e.model_name))
|
|
97
|
+
return false;
|
|
98
|
+
const provider = e.provider || 'Unknown';
|
|
99
|
+
if (EXCLUDED_PROVIDERS.has(provider))
|
|
100
|
+
return false;
|
|
101
|
+
return (tokenMap.get(e.model_name) ?? 0) > 0;
|
|
102
|
+
})
|
|
81
103
|
.map((e) => ({
|
|
82
104
|
model_name: e.model_name,
|
|
83
105
|
provider: e.provider || 'Unknown',
|
|
84
106
|
tokens_7d: tokenMap.get(e.model_name) ?? 0,
|
|
85
|
-
}))
|
|
107
|
+
}))
|
|
108
|
+
.sort((a, b) => b.tokens_7d - a.tokens_7d)
|
|
109
|
+
.slice(0, MAX_RESULTS);
|
|
86
110
|
}
|
|
87
111
|
};
|
|
88
112
|
exports.PublicStatsService = PublicStatsService;
|
|
@@ -15,6 +15,7 @@ var OpenaiOauthController_1;
|
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.OpenaiOauthController = void 0;
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
|
+
const crypto_1 = require("crypto");
|
|
18
19
|
const public_decorator_1 = require("../../common/decorators/public.decorator");
|
|
19
20
|
const current_user_decorator_1 = require("../../auth/current-user.decorator");
|
|
20
21
|
const openai_oauth_service_1 = require("./openai-oauth.service");
|
|
@@ -85,9 +86,10 @@ let OpenaiOauthController = OpenaiOauthController_1 = class OpenaiOauthControlle
|
|
|
85
86
|
}
|
|
86
87
|
done(ok, res) {
|
|
87
88
|
const success = ok === '1';
|
|
89
|
+
const nonce = (0, crypto_1.randomBytes)(16).toString('base64');
|
|
88
90
|
res.setHeader('Content-Type', 'text/html');
|
|
89
|
-
res.setHeader('Content-Security-Policy',
|
|
90
|
-
res.send((0, openai_oauth_service_1.oauthDoneHtml)(success));
|
|
91
|
+
res.setHeader('Content-Security-Policy', `default-src 'none'; script-src 'nonce-${nonce}'`);
|
|
92
|
+
res.send((0, openai_oauth_service_1.oauthDoneHtml)(success, nonce));
|
|
91
93
|
}
|
|
92
94
|
};
|
|
93
95
|
exports.OpenaiOauthController = OpenaiOauthController;
|
|
@@ -22,18 +22,19 @@ function parseOAuthTokenBlob(rawValue) {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
function oauthDoneHtml(success) {
|
|
25
|
+
function oauthDoneHtml(success, nonce) {
|
|
26
26
|
const message = success ? 'manifest-oauth-success' : 'manifest-oauth-error';
|
|
27
27
|
const text = success
|
|
28
28
|
? 'Login successful!'
|
|
29
29
|
: 'Login failed. Please close this window and try again.';
|
|
30
|
+
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
|
|
30
31
|
return `<!DOCTYPE html>
|
|
31
32
|
<html>
|
|
32
33
|
<head><title>Manifest — OpenAI Login</title></head>
|
|
33
34
|
<body style="font-family:system-ui;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0;background:#111;color:#eee;">
|
|
34
35
|
<p>${text}</p>
|
|
35
36
|
<p id="hint" style="font-size:13px;color:#888;display:none;">You can close this window.</p>
|
|
36
|
-
<script>
|
|
37
|
+
<script${nonceAttr}>
|
|
37
38
|
try{var bc=new BroadcastChannel('manifest-oauth');bc.postMessage({type:'${message}'});bc.close();}catch(e){}
|
|
38
39
|
if(window.opener){window.opener.postMessage({type:'${message}'},window.location.origin);}
|
|
39
40
|
setTimeout(function(){window.close();document.getElementById('hint').style.display='block';},1500);
|
|
@@ -14,6 +14,13 @@ const KNOWN_ERROR_MESSAGES = {
|
|
|
14
14
|
503: 'Upstream provider temporarily unavailable',
|
|
15
15
|
504: 'Upstream provider gateway timeout',
|
|
16
16
|
};
|
|
17
|
+
function sanitizeSensitivePatterns(msg) {
|
|
18
|
+
return msg
|
|
19
|
+
.replace(/sk-ant-[a-zA-Z0-9_-]{20,}/g, 'sk-ant-***')
|
|
20
|
+
.replace(/sk-[a-zA-Z0-9_-]{20,}/g, 'sk-***')
|
|
21
|
+
.replace(/key=[^&\s"]+/g, 'key=***')
|
|
22
|
+
.replace(/Bearer\s+[^\s"]+/gi, 'Bearer ***');
|
|
23
|
+
}
|
|
17
24
|
function sanitizeProviderError(status, rawBody, nodeEnv) {
|
|
18
25
|
const generic = KNOWN_ERROR_MESSAGES[status] ?? `Upstream provider returned HTTP ${status}`;
|
|
19
26
|
if ((nodeEnv ?? 'production') === 'production')
|
|
@@ -23,7 +30,7 @@ function sanitizeProviderError(status, rawBody, nodeEnv) {
|
|
|
23
30
|
const error = parsed.error;
|
|
24
31
|
const message = error?.message ?? parsed.message;
|
|
25
32
|
if (typeof message === 'string' && message.length > 0) {
|
|
26
|
-
return message.slice(0, 500);
|
|
33
|
+
return sanitizeSensitivePatterns(message).slice(0, 500);
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
catch {
|
|
@@ -31,8 +31,10 @@ function filterSubModels(models) {
|
|
|
31
31
|
}
|
|
32
32
|
const result = [];
|
|
33
33
|
for (const providerModels of byProvider.values()) {
|
|
34
|
-
const zeroCost = providerModels.filter((m) =>
|
|
35
|
-
|
|
34
|
+
const zeroCost = providerModels.filter((m) => m.inputPricePerToken != null &&
|
|
35
|
+
m.outputPricePerToken != null &&
|
|
36
|
+
m.inputPricePerToken === 0 &&
|
|
37
|
+
m.outputPricePerToken === 0);
|
|
36
38
|
result.push(...(zeroCost.length > 0 ? zeroCost : providerModels));
|
|
37
39
|
}
|
|
38
40
|
return result;
|
|
@@ -82,11 +84,14 @@ let TierAutoAssignService = TierAutoAssignService_1 = class TierAutoAssignServic
|
|
|
82
84
|
pickBest(models, tier) {
|
|
83
85
|
if (models.length === 0)
|
|
84
86
|
return null;
|
|
85
|
-
const totalPrice = (m) =>
|
|
86
|
-
(m.
|
|
87
|
+
const totalPrice = (m) => {
|
|
88
|
+
if (m.inputPricePerToken == null || m.outputPricePerToken == null)
|
|
89
|
+
return Infinity;
|
|
90
|
+
return Number(m.inputPricePerToken) + Number(m.outputPricePerToken);
|
|
91
|
+
};
|
|
87
92
|
const quality = (m) => m.qualityScore ?? 3;
|
|
88
93
|
const byPrice = [...models].sort((a, b) => totalPrice(a) - totalPrice(b));
|
|
89
|
-
let picked;
|
|
94
|
+
let picked = byPrice[0];
|
|
90
95
|
switch (tier) {
|
|
91
96
|
case 'simple': {
|
|
92
97
|
picked = byPrice[0];
|
|
@@ -119,18 +119,10 @@ function scoreRequest(input, configOverride, momentum) {
|
|
|
119
119
|
const hasTools = tools && tools.length > 0;
|
|
120
120
|
const hasMomentum = momentum?.recentTiers && momentum.recentTiers.length > 0;
|
|
121
121
|
if (lastUserText.length > 0 && lastUserText.length < 50 && !hasTools) {
|
|
122
|
-
if (!hasMomentum) {
|
|
123
|
-
return {
|
|
124
|
-
tier: 'simple',
|
|
125
|
-
score: -0.3,
|
|
126
|
-
confidence: 0.9,
|
|
127
|
-
reason: 'short_message',
|
|
128
|
-
dimensions: emptyDimensions(config),
|
|
129
|
-
momentum: null,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
122
|
const lastMatches = trie.scan(lastUserText);
|
|
133
|
-
|
|
123
|
+
const hasSimpleIndicator = lastMatches.some((m) => m.dimension === 'simpleIndicators');
|
|
124
|
+
const hasComplexSignal = lastMatches.some((m) => m.dimension !== 'simpleIndicators' && m.dimension !== 'relay');
|
|
125
|
+
if (!hasComplexSignal && (!hasMomentum || hasSimpleIndicator)) {
|
|
134
126
|
return {
|
|
135
127
|
tier: 'simple',
|
|
136
128
|
score: -0.3,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "manifest",
|
|
3
3
|
"name": "Manifest Self-Hosted LLM Router",
|
|
4
|
-
"version": "5.38.
|
|
4
|
+
"version": "5.38.4",
|
|
5
5
|
"description": "Run the Manifest LLM router locally with SQLite. Zero-config dashboard included.",
|
|
6
6
|
"author": "MNFST Inc.",
|
|
7
7
|
"homepage": "https://manifest.build",
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "manifest",
|
|
3
3
|
"name": "Manifest Self-Hosted LLM Router",
|
|
4
|
-
"version": "5.38.
|
|
4
|
+
"version": "5.38.4",
|
|
5
5
|
"description": "Run the Manifest LLM router locally with SQLite. Zero-config dashboard included.",
|
|
6
6
|
"author": "MNFST Inc.",
|
|
7
7
|
"homepage": "https://manifest.build",
|