creditdaddy 1.3.2 → 1.4.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.
- package/README.md +65 -13
- package/bin/creditdaddy.js +33 -12
- package/package.json +5 -2
- package/src/accounts.js +10 -0
- package/src/authDevice.js +5 -4
- package/src/bgdaemon.js +14 -5
- package/src/checkin.js +69 -39
- package/src/daemon.js +1266 -1059
- package/src/i18n/en.json +1490 -0
- package/src/i18n/ja.json +1490 -0
- package/src/i18n/ko.json +1490 -0
- package/src/i18n/zh-TW.json +1490 -0
- package/src/i18n.js +47 -0
- package/src/logger.js +114 -17
- package/src/minimaxClient.js +18 -21
- package/src/minimaxGateway.js +63 -45
- package/src/minimaxLocal.js +61 -16
- package/src/mirasimClient.js +6 -1
- package/src/panel.html +1139 -626
- package/src/qoderClient.js +24 -4
- package/src/qoderUmid.js +4 -3
- package/src/refreshGuard.js +69 -0
- package/src/store.js +11 -0
- package/src/tenrouter.js +14 -7
- package/src/traeGateway.js +43 -24
- package/src/transfer.js +44 -6
- package/src/workbuddyClient.js +14 -7
- package/src/zcodeAutoClaim.js +47 -7
- package/src/zcodeClient.js +8 -3
- package/src/zcodeGateway.js +22 -15
package/src/minimaxLocal.js
CHANGED
|
@@ -49,6 +49,44 @@ function writeJson(file, data) {
|
|
|
49
49
|
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8');
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* recordKey → uid 的小缓存。
|
|
54
|
+
*
|
|
55
|
+
* 为什么需要:auth.json 的 record 只有 accessToken / refreshToken / clientId 这些,
|
|
56
|
+
* 稳定 uid 只能靠 profile 接口拿;profile 偶发失败时(离线 / 接口抖动),
|
|
57
|
+
* 「客户端当前是哪个账号」就没法判断了,缓存一份兜底。
|
|
58
|
+
*
|
|
59
|
+
* 注意:缓存**只对 auth.json 里仍然存在的 record 生效**(见 currentMiniMaxUid)。
|
|
60
|
+
* 客户端登出后 records 为空,此时若还回落到缓存,面板会把某个账号一直标成
|
|
61
|
+
* 「客户端当前」——这正是「客户端明明没登录却显示已登录」的来源,所以读的时候必须
|
|
62
|
+
* 与当前 record 集合求交集,不能无条件回落。
|
|
63
|
+
*/
|
|
64
|
+
function readUidCache() {
|
|
65
|
+
return readJson(minimaxUidCachePath()) || {};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 写入 uid 缓存,并顺手丢掉 auth.json 里已经不存在的 record(防止缓存无限膨胀 / 陈旧) */
|
|
69
|
+
function writeUidCache(recordKey, uid) {
|
|
70
|
+
if (!recordKey || !uid) return;
|
|
71
|
+
try {
|
|
72
|
+
const cache = readUidCache();
|
|
73
|
+
if (cache[recordKey] === String(uid)) return;
|
|
74
|
+
cache[recordKey] = String(uid);
|
|
75
|
+
const live = liveRecordKeys();
|
|
76
|
+
if (live) for (const k of Object.keys(cache)) if (!live.has(k)) delete cache[k];
|
|
77
|
+
writeJson(minimaxUidCachePath(), cache);
|
|
78
|
+
} catch {
|
|
79
|
+
// 缓存写失败不影响调用方
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** auth.json 里当前仍有 accessToken 的 recordKey 集合;读不到(文件缺失/损坏)返回 null */
|
|
84
|
+
function liveRecordKeys() {
|
|
85
|
+
const authData = readJson(minimaxAuthPath());
|
|
86
|
+
if (!authData?.records) return null;
|
|
87
|
+
return new Set(Object.keys(authData.records).filter((k) => authData.records[k]?.accessToken));
|
|
88
|
+
}
|
|
89
|
+
|
|
52
90
|
/**
|
|
53
91
|
* 寻找 MiniMax Code.exe 安装路径
|
|
54
92
|
*/
|
|
@@ -115,18 +153,10 @@ export async function liveToAccount() {
|
|
|
115
153
|
// profile 失败不影响返回基础账号信息
|
|
116
154
|
}
|
|
117
155
|
|
|
118
|
-
// auth.json
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
if (profile?.userId && cached[recordKey] !== profile.userId) {
|
|
123
|
-
cached[recordKey] = profile.userId;
|
|
124
|
-
try {
|
|
125
|
-
writeJson(minimaxUidCachePath(), cached);
|
|
126
|
-
} catch {
|
|
127
|
-
// 缓存写失败不影响本次返回
|
|
128
|
-
}
|
|
129
|
-
}
|
|
156
|
+
// auth.json 的记录没有稳定 uid 字段,token 又高频轮换,profile 偶发失败时无处可查 ——
|
|
157
|
+
// 用 recordKey 把 uid 记住,供 currentMiniMaxUid 离线比对(见 minimaxUidCachePath 注释)。
|
|
158
|
+
const uid = profile?.userId || record.subject || record.accountId || readUidCache()[recordKey] || null;
|
|
159
|
+
if (uid) writeUidCache(recordKey, uid);
|
|
130
160
|
const name = profile?.name || (uid ? `MiniMax_${String(uid).slice(-6)}` : 'MiniMax Code');
|
|
131
161
|
|
|
132
162
|
return {
|
|
@@ -139,7 +169,10 @@ export async function liveToAccount() {
|
|
|
139
169
|
email: profile?.email || null,
|
|
140
170
|
source: 'local-app',
|
|
141
171
|
meta: {
|
|
142
|
-
authRecordKey
|
|
172
|
+
// 不写 authRecordKey:它是「这条账号绑定本机客户端凭据链」的开关,一旦挂上,
|
|
173
|
+
// 该账号刷新前就会去 auth.json 重读 refreshToken(可能会被别的账号的链顶掉)、
|
|
174
|
+
// 刷新后还会回写 auth.json。本机导入/浏览器登录混用时曾因此让两个账号共用同一条链
|
|
175
|
+
// 互相作废,故这里只留来源标记,链的绑定交给 alignMiniMaxFromLocal 按 uid 判断。
|
|
143
176
|
clientId: record.clientId,
|
|
144
177
|
scopes: record.scopes,
|
|
145
178
|
capturedAt: new Date().toISOString(),
|
|
@@ -149,7 +182,11 @@ export async function liveToAccount() {
|
|
|
149
182
|
|
|
150
183
|
/**
|
|
151
184
|
* 获取当前登录的 UID。
|
|
152
|
-
* 优先用 record 自带字段;没有则按
|
|
185
|
+
* 优先用 record 自带字段;没有则按 recordKey 命中 uid 缓存(token 会轮换,uid 不会)。
|
|
186
|
+
*
|
|
187
|
+
* records 为空(客户端从未登录 / 已登出)时一律返回 null——绝不回落到缓存。
|
|
188
|
+
* 回落的代价是:客户端登出后 uid 缓存还留着,面板会继续把那个旧账号标成
|
|
189
|
+
* 「客户端当前」,看起来像登出没生效。
|
|
153
190
|
*/
|
|
154
191
|
export function currentMiniMaxUid() {
|
|
155
192
|
const authFile = minimaxAuthPath();
|
|
@@ -160,8 +197,7 @@ export function currentMiniMaxUid() {
|
|
|
160
197
|
const record = authData.records[recordKey];
|
|
161
198
|
const own = record?.subject || record?.accountId;
|
|
162
199
|
if (own) return String(own);
|
|
163
|
-
|
|
164
|
-
return cached?.[recordKey] || null;
|
|
200
|
+
return readUidCache()[recordKey] || null;
|
|
165
201
|
}
|
|
166
202
|
|
|
167
203
|
/**
|
|
@@ -209,8 +245,17 @@ export function alignMiniMaxFromLocal(account, log) {
|
|
|
209
245
|
const key = account?.meta?.authRecordKey;
|
|
210
246
|
if (!key) return false;
|
|
211
247
|
try {
|
|
248
|
+
// 归属校验:本机客户端同一时刻只登录一个账号,auth.json 里那条 record 属于**它**,
|
|
249
|
+
// 不一定属于调用方这个账号。若两个账号(如「本机导入」与「浏览器登录」)因为历史原因
|
|
250
|
+
// 挂了同一个 authRecordKey,不校验就会拿别人的链去刷新——两个账号共用一次性 refreshToken,
|
|
251
|
+
// 必然互相作废(invalid_grant)。uid 对不上就认定不是本账号的链,直接跳过对齐。
|
|
212
252
|
const rec = readMiniMaxRecord(key);
|
|
213
253
|
if (!rec?.refreshToken || rec.refreshToken === account.refreshToken) return false;
|
|
254
|
+
const recUid = rec.subject || rec.accountId || readUidCache()[key] || null;
|
|
255
|
+
if (recUid && account.uid && String(recUid) !== String(account.uid)) {
|
|
256
|
+
log?.('本机客户端当前登录的是另一个 MiniMax 账号,跳过凭据对齐(避免两条链互相作废)');
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
214
259
|
account.refreshToken = rec.refreshToken;
|
|
215
260
|
if (rec.accessToken) account.token = rec.accessToken;
|
|
216
261
|
if (rec.expiresAtMs) account.expiresAt = new Date(rec.expiresAtMs).toISOString();
|
package/src/mirasimClient.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { fetchJsonRace } from './zcodeClient.js';
|
|
10
10
|
import { FETCH_TIMEOUT_MS } from './constants.js';
|
|
11
|
+
import { withRefreshDedupe } from './refreshGuard.js';
|
|
11
12
|
|
|
12
13
|
const AUTH_ME_URL = 'https://auth.mirasim.ai/auth/me';
|
|
13
14
|
const RELAY_LIMITS_URL = 'https://relay.mirasim.ai/v1/limits';
|
|
@@ -64,7 +65,11 @@ async function withToken(account, fn, ctx) {
|
|
|
64
65
|
let token = account.token;
|
|
65
66
|
let refreshed = false;
|
|
66
67
|
const doRefresh = async () => {
|
|
67
|
-
|
|
68
|
+
// 并发互斥 + 失效链熔断:与 minimax / workbuddy 共用 refreshGuard
|
|
69
|
+
const creds = await withRefreshDedupe(account, refreshMirasimToken, {
|
|
70
|
+
deadMessage: 'mirasim 凭据链已被服务端作废,等待重新登录',
|
|
71
|
+
deadNotice: 'mirasim 凭据链已失效,将不再自动重试(重新登录或重新导入该账号后自动恢复)',
|
|
72
|
+
});
|
|
68
73
|
account.token = creds.token;
|
|
69
74
|
account.refreshToken = creds.refreshToken;
|
|
70
75
|
if (creds.expiresAt) account.expiresAt = creds.expiresAt;
|