feat: account management overhaul + 7 issue resolutions (#147)

- Add smart JSON export/import with schema validation and deduplication
- Add per-account proxy configurations (Issue #79)
- Add advanced account sorting engine (Issues #126 & #99)
- Add compact horizontal layout, AI credits, inline model visibility (Issues #145 & #134)
- Add desktop push notifications for low quota (Issue #53)
- Fix Arch Linux AUR process detection bug (Issue #117)
- Default theme set to Dark Mode (it's better)
- UI Polishing: Improved sort dropdown and symmetrical dialog layouts
- Dev Mode: Resolved port conflicts and single-instance locks
- Updated README and README.zh-CN with new features
This commit is contained in:
vd authored and GitHub committed 2026-04-06 20:32:42 +08:00
1 parent 50a325be11
commit ed0dd811a8
37 files changed
+1989 -455

No files matched your search

@@ -435,6 +435,10 @@ export class TokenManagerService implements OnModuleInit {
return candidates.find(([accountId]) => accountId === stickyBinding.accountId) ?? null;
}
public resetSelectionState(): void {
this.currentIndex = 0;
}
private pickRoundRobinEntry(candidates: TokenEntry[]): TokenEntry | null {
if (candidates.length === 0) {
return null;
@@ -507,10 +511,7 @@ export class TokenManagerService implements OnModuleInit {
const stickyBinding = this.getValidSessionBinding(sessionKey, now);
if (stickyBinding && mode === 'cache-first') {
const waitSec = this.rateLimitTracker.getRemainingWaitSeconds(
stickyBinding.accountId,
model,
);
const waitSec = this.rateLimitTracker.getRemainingWaitSeconds(stickyBinding.accountId, model);
const waitMs = waitSec * 1000;
const maxWaitMs = this.getMaxWaitDurationMs();
if (waitMs > 0 && waitMs <= maxWaitMs) {
@@ -586,18 +587,18 @@ export class TokenManagerService implements OnModuleInit {
): void {
const now = Date.now();
const legacyAccountId = this.predictLegacyAccountCandidateId(allTokens, sessionKey, now);
const parityAccountId = this.predictParityAccountCandidateId(
allTokens,
sessionKey,
model,
now,
);
const parityAccountId = this.predictParityAccountCandidateId(allTokens, sessionKey, model, now);
this.updateShadowStats(legacyAccountId, parityAccountId);
}
private updateShadowStats(legacyId: string | null, parityId: string | null): void {
this.shadowComparisonCount++;
if (legacyAccountId !== parityAccountId) {
if (legacyId !== parityId) {
this.shadowMismatchCount++;
this.logger.warn(
`Parity shadow mismatch detected: legacy=${legacyAccountId ?? 'n/a'}, parity=${parityAccountId ?? 'n/a'}`,
`Parity shadow mismatch detected: legacy=${legacyId ?? 'n/a'}, parity=${parityId ?? 'n/a'}`,
);
}