coding-tool-x 3.6.5 → 3.6.6
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/package.json
CHANGED
package/src/config/paths.js
CHANGED
|
@@ -431,6 +431,7 @@ const PATHS = {
|
|
|
431
431
|
sessionCache: path.join(CACHE_DIR, 'session-cache.json'),
|
|
432
432
|
sessionHasCache: path.join(CACHE_DIR, 'session-has-cache.json'),
|
|
433
433
|
channelModels: path.join(CACHE_DIR, 'channel-models.json'),
|
|
434
|
+
channelBalanceStrategies: path.join(CACHE_DIR, 'channel-balance-strategies.json'),
|
|
434
435
|
envBackups: path.join(BACKUPS_DIR, 'env'),
|
|
435
436
|
proxyRuntime: {
|
|
436
437
|
claude: path.join(RUNTIME_DIR, 'claude-proxy.json'),
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
2
|
const https = require('https');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
4
6
|
const { URL } = require('url');
|
|
7
|
+
const { PATHS } = require('../../config/paths');
|
|
5
8
|
const { loadUIConfig } = require('./ui-config');
|
|
6
9
|
|
|
7
10
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -16,6 +19,66 @@ const UNSUPPORTED_BALANCE_PLATFORMS = new Set(['dashscope', 'modelscope']);
|
|
|
16
19
|
|
|
17
20
|
const balanceCache = new Map();
|
|
18
21
|
const balanceStrategyCache = new Map();
|
|
22
|
+
let strategyCacheLoaded = false;
|
|
23
|
+
|
|
24
|
+
function getStrategyCacheFilePath() {
|
|
25
|
+
return PATHS.channelBalanceStrategies
|
|
26
|
+
|| path.join(PATHS.storage || path.dirname(PATHS.channelModels), 'cache', 'channel-balance-strategies.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function loadPersistedStrategyCache() {
|
|
30
|
+
if (strategyCacheLoaded) return;
|
|
31
|
+
strategyCacheLoaded = true;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const filePath = getStrategyCacheFilePath();
|
|
35
|
+
if (!fs.existsSync(filePath)) return;
|
|
36
|
+
const payload = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
37
|
+
const entries = payload && typeof payload === 'object' ? payload.entries : null;
|
|
38
|
+
if (!entries || typeof entries !== 'object') return;
|
|
39
|
+
Object.entries(entries).forEach(([key, value]) => {
|
|
40
|
+
const strategy = value?.strategy && typeof value.strategy === 'object' ? value.strategy : null;
|
|
41
|
+
if (strategy?.type) {
|
|
42
|
+
balanceStrategyCache.set(key, strategy);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
} catch {
|
|
46
|
+
// A corrupt strategy cache should only cost a fresh probe, not break balance display.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function writePersistedStrategyCache() {
|
|
51
|
+
try {
|
|
52
|
+
const filePath = getStrategyCacheFilePath();
|
|
53
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
54
|
+
const entries = {};
|
|
55
|
+
balanceStrategyCache.forEach((strategy, key) => {
|
|
56
|
+
if (strategy?.type) {
|
|
57
|
+
entries[key] = {
|
|
58
|
+
strategy,
|
|
59
|
+
updatedAt: nowIso()
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
fs.writeFileSync(filePath, JSON.stringify({ version: 1, entries }, null, 2), 'utf8');
|
|
64
|
+
} catch {
|
|
65
|
+
// Persisting the optimization is best-effort; in-memory cache still works.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function rememberBalanceStrategy(key, strategy) {
|
|
70
|
+
if (!key || !strategy?.type) return;
|
|
71
|
+
loadPersistedStrategyCache();
|
|
72
|
+
balanceStrategyCache.set(key, strategy);
|
|
73
|
+
writePersistedStrategyCache();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function forgetBalanceStrategy(key) {
|
|
77
|
+
loadPersistedStrategyCache();
|
|
78
|
+
if (balanceStrategyCache.delete(key)) {
|
|
79
|
+
writePersistedStrategyCache();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
19
82
|
|
|
20
83
|
function nowIso(now = Date.now()) {
|
|
21
84
|
return new Date(now).toISOString();
|
|
@@ -1275,24 +1338,25 @@ async function refreshChannelBalanceSnapshot(source, channel, options = {}) {
|
|
|
1275
1338
|
|
|
1276
1339
|
let lastSnapshot = null;
|
|
1277
1340
|
const token = resolveBalanceToken(channel);
|
|
1341
|
+
loadPersistedStrategyCache();
|
|
1278
1342
|
const cachedStrategy = balanceStrategyCache.get(key);
|
|
1279
1343
|
if (cachedStrategy) {
|
|
1280
1344
|
const strategyResult = await tryBalanceStrategy(cachedStrategy, token, channel);
|
|
1281
1345
|
if (strategyResult?.snapshot?.visible) {
|
|
1282
|
-
|
|
1346
|
+
rememberBalanceStrategy(key, strategyResult.strategy);
|
|
1283
1347
|
balanceCache.set(key, {
|
|
1284
1348
|
snapshot: strategyResult.snapshot,
|
|
1285
1349
|
expiresAt: currentTime + CACHE_TTL_MS
|
|
1286
1350
|
});
|
|
1287
1351
|
return strategyResult.snapshot;
|
|
1288
1352
|
}
|
|
1289
|
-
|
|
1353
|
+
forgetBalanceStrategy(key);
|
|
1290
1354
|
}
|
|
1291
1355
|
|
|
1292
1356
|
for (const baseUrl of bases) {
|
|
1293
1357
|
const result = await probeBalanceFromBase(baseUrl, channel, token);
|
|
1294
1358
|
if (result?.snapshot?.visible) {
|
|
1295
|
-
|
|
1359
|
+
rememberBalanceStrategy(key, result.strategy);
|
|
1296
1360
|
balanceCache.set(key, {
|
|
1297
1361
|
snapshot: result.snapshot,
|
|
1298
1362
|
expiresAt: currentTime + CACHE_TTL_MS
|
|
@@ -1333,6 +1397,10 @@ function getChannelsForSource(source) {
|
|
|
1333
1397
|
return [];
|
|
1334
1398
|
}
|
|
1335
1399
|
|
|
1400
|
+
function getEnabledBalanceChannels(source) {
|
|
1401
|
+
return getChannelsForSource(source).filter(channel => channel?.enabled !== false);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1336
1404
|
function isBalanceDisplayEnabled() {
|
|
1337
1405
|
try {
|
|
1338
1406
|
return loadUIConfig().channelBalance?.showRemaining === true;
|
|
@@ -1369,7 +1437,7 @@ async function getChannelBalances(source, options = {}) {
|
|
|
1369
1437
|
return { enabled: false, source: normalizedSource, balances: {} };
|
|
1370
1438
|
}
|
|
1371
1439
|
|
|
1372
|
-
const channels =
|
|
1440
|
+
const channels = getEnabledBalanceChannels(normalizedSource);
|
|
1373
1441
|
const entries = await mapWithConcurrency(channels, 4, async (channel) => {
|
|
1374
1442
|
const snapshot = await refreshChannelBalanceSnapshot(normalizedSource, channel, options);
|
|
1375
1443
|
return [channel.id, snapshot];
|
|
@@ -1388,7 +1456,7 @@ async function refreshChannelBalance(source, channelId) {
|
|
|
1388
1456
|
return { enabled: false, source: normalizedSource, channelId, balance: makeHiddenSnapshot() };
|
|
1389
1457
|
}
|
|
1390
1458
|
|
|
1391
|
-
const channels =
|
|
1459
|
+
const channels = getEnabledBalanceChannels(normalizedSource);
|
|
1392
1460
|
const channel = channels.find(item => String(item.id) === String(channelId));
|
|
1393
1461
|
if (!channel) {
|
|
1394
1462
|
const error = new Error('Channel not found');
|
|
@@ -1408,6 +1476,7 @@ async function refreshChannelBalance(source, channelId) {
|
|
|
1408
1476
|
function clearBalanceCache() {
|
|
1409
1477
|
balanceCache.clear();
|
|
1410
1478
|
balanceStrategyCache.clear();
|
|
1479
|
+
strategyCacheLoaded = false;
|
|
1411
1480
|
}
|
|
1412
1481
|
|
|
1413
1482
|
module.exports = {
|
|
@@ -1435,6 +1504,7 @@ module.exports = {
|
|
|
1435
1504
|
build88CodeUsageSnapshot,
|
|
1436
1505
|
build88CodeSubscriptionSnapshot,
|
|
1437
1506
|
buildBalanceProbeStrategies,
|
|
1507
|
+
getEnabledBalanceChannels,
|
|
1438
1508
|
runBalanceStrategy,
|
|
1439
1509
|
refreshChannelBalanceSnapshot,
|
|
1440
1510
|
makeVisibleSnapshot,
|