creditdaddy 1.2.0 → 1.3.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/LICENSE +21 -21
- package/README.md +16 -8
- package/package.json +2 -2
- package/src/accounts.js +18 -7
- package/src/authDevice.js +3 -0
- package/src/catpawClient.js +103 -103
- package/src/catpawLocal.js +229 -229
- package/src/checkin.js +7 -2
- package/src/constants.js +4 -2
- package/src/daemon.js +73 -11
- package/src/localDetect.js +147 -147
- package/src/logger.js +60 -7
- package/src/minimaxAuth.js +127 -0
- package/src/minimaxClient.js +330 -0
- package/src/minimaxGateway.js +404 -0
- package/src/minimaxLocal.js +259 -0
- package/src/mirasimClient.js +189 -189
- package/src/panel.html +214 -29
- package/src/providers.js +26 -0
- package/src/qoderApp.js +3 -9
- package/src/qoderClient.js +237 -237
- package/src/qoderUmid.js +198 -198
- package/src/store.js +49 -6
- package/src/tenrouter.js +8 -0
- package/src/tenrouterAccounts.js +7 -0
- package/src/transfer.js +24 -1
- package/src/usageSync.js +505 -505
- package/src/winProc.js +36 -0
- package/src/workbuddyLocal.js +133 -133
- package/src/zcodeAuth.js +131 -131
- package/src/zcodeAutoClaim.js +97 -97
- package/src/zcodeClient.js +30 -13
- package/src/zcodeGateway.js +79 -33
- package/src/zcrypto.js +160 -160
package/src/panel.html
CHANGED
|
@@ -126,6 +126,8 @@
|
|
|
126
126
|
html[data-theme="dark"] .badge.prod.catpaw { color: #fdba74; }
|
|
127
127
|
.badge.prod.trae { color: #047857; background: rgba(16,185,129,.14); }
|
|
128
128
|
html[data-theme="dark"] .badge.prod.trae { color: #6ee7b7; }
|
|
129
|
+
.badge.prod.minimax { color: #e11d48; background: rgba(244,63,94,.14); }
|
|
130
|
+
html[data-theme="dark"] .badge.prod.minimax { color: #fda4af; }
|
|
129
131
|
.seg-ctl { display: inline-flex; gap: 2px; padding: 2px; border-radius: 9px; background: var(--hover); }
|
|
130
132
|
.seg-ctl button { border: none; background: transparent; color: var(--text-2); font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 7px; cursor: pointer; }
|
|
131
133
|
.seg-ctl button.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
|
|
@@ -247,7 +249,7 @@
|
|
|
247
249
|
.prod-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
248
250
|
.prod-name { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 700; }
|
|
249
251
|
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
250
|
-
.dot.qoder { background: #7c3aed; } .dot.workbuddy { background: #14b8a6; } .dot.zcode { background: #2563eb; } .dot.mirasim { background: #64748b; } .dot.catpaw { background: #f97316; } .dot.trae { background: #10b981; }
|
|
252
|
+
.dot.qoder { background: #7c3aed; } .dot.workbuddy { background: #14b8a6; } .dot.zcode { background: #2563eb; } .dot.mirasim { background: #64748b; } .dot.catpaw { background: #f97316; } .dot.trae { background: #10b981; } .dot.minimax { background: #f43f5e; }
|
|
251
253
|
.prod-go { color: var(--text-3); font-size: 12px; white-space: nowrap; display: inline-flex; align-items: center; gap: 2px; }
|
|
252
254
|
.prod-go svg { width: 12px; height: 12px; transition: transform .15s; }
|
|
253
255
|
.prodcard:hover .prod-go { color: var(--text); }
|
|
@@ -363,6 +365,7 @@
|
|
|
363
365
|
<button data-view="mirasim">mirasim<span class="cnt" id="cnt-mirasim">0</span></button>
|
|
364
366
|
<button data-view="catpaw">妙手<span class="cnt" id="cnt-catpaw">0</span></button>
|
|
365
367
|
<button data-view="trae">Trae<span class="cnt" id="cnt-trae">0</span></button>
|
|
368
|
+
<button data-view="minimax">MiniMax<span class="cnt" id="cnt-minimax">0</span></button>
|
|
366
369
|
<button data-view="tenrouter">10Router<span class="cnt" id="cnt-tenrouter">–</span></button>
|
|
367
370
|
</nav>
|
|
368
371
|
|
|
@@ -415,6 +418,10 @@
|
|
|
415
418
|
<span id="t-actions" style="display:none">
|
|
416
419
|
<button class="btn" id="tt-open" style="display:none"></button>
|
|
417
420
|
</span>
|
|
421
|
+
<span id="m-actions" style="display:none">
|
|
422
|
+
<button class="btn" id="mt-gw"></button>
|
|
423
|
+
<button class="btn" id="mt-open" style="display:none"></button>
|
|
424
|
+
</span>
|
|
418
425
|
<button class="btn" id="btn-add"></button>
|
|
419
426
|
<button class="btn" id="btn-import"></button>
|
|
420
427
|
<button class="btn" id="btn-export"></button>
|
|
@@ -430,9 +437,14 @@
|
|
|
430
437
|
<div class="panel" id="tr-panel"><div class="empty">加载中…</div></div>
|
|
431
438
|
</section>
|
|
432
439
|
|
|
440
|
+
<!-- 日志卡:运行日志 + 各网关调用日志,卡内标签切换(默认跟随当前页,可手动切) -->
|
|
433
441
|
<section class="pcard collapsed" id="log-card">
|
|
434
442
|
<div class="pcard-title">
|
|
435
|
-
<
|
|
443
|
+
<div class="seg-ctl" id="log-tabs">
|
|
444
|
+
<button data-log-tab="run" class="active">运行日志</button>
|
|
445
|
+
<button data-log-tab="zcode">ZCode 网关</button>
|
|
446
|
+
<button data-log-tab="minimax">MiniMax 网关</button>
|
|
447
|
+
</div>
|
|
436
448
|
<button class="btn" id="btn-logs">展开</button>
|
|
437
449
|
</div>
|
|
438
450
|
<div class="logs" id="logs">(暂无)</div>
|
|
@@ -463,6 +475,7 @@
|
|
|
463
475
|
<option value="qoder">Qoder 国际版</option><option value="qoder-cn">Qoder 国内版</option>
|
|
464
476
|
<option value="workbuddy">WorkBuddy / CodeBuddy 国内版</option><option value="workbuddy-intl">WorkBuddy / CodeBuddy 国际版</option>
|
|
465
477
|
<option value="zcode-bigmodel">ZCode · BigModel(智谱)</option><option value="zcode-zai">ZCode · Z.ai(国际)</option>
|
|
478
|
+
<option value="minimax">MiniMax Code</option>
|
|
466
479
|
</select>
|
|
467
480
|
</div>
|
|
468
481
|
<div class="row"><button class="btn primary" id="btn-device">生成授权链接</button><button class="btn" id="dev-cancel" style="display:none">取消</button></div>
|
|
@@ -475,7 +488,7 @@
|
|
|
475
488
|
<div class="pane" data-pane="token">
|
|
476
489
|
<div class="field">
|
|
477
490
|
<label>版本</label>
|
|
478
|
-
<select class="input" id="f-provider"><option value="qoder">Qoder 国际版</option><option value="qoder-cn">Qoder 国内版</option><option value="workbuddy">WorkBuddy 国内版</option><option value="workbuddy-intl">WorkBuddy 国际版</option></select>
|
|
491
|
+
<select class="input" id="f-provider"><option value="qoder">Qoder 国际版</option><option value="qoder-cn">Qoder 国内版</option><option value="workbuddy">WorkBuddy 国内版</option><option value="workbuddy-intl">WorkBuddy 国际版</option><option value="minimax">MiniMax</option></select>
|
|
479
492
|
</div>
|
|
480
493
|
<div class="field"><label>备注名(可选)</label><input class="input" id="f-name" placeholder="留空则自动读取昵称"></div>
|
|
481
494
|
<div class="field"><label>Token</label><input class="input mono" id="f-token" placeholder="dt-… / jt-… 或 PAT(pt-…,自动兑换)"></div>
|
|
@@ -490,7 +503,7 @@
|
|
|
490
503
|
<div class="modal-title"><span>导出账号</span><button class="close" data-close>✕</button></div>
|
|
491
504
|
<div class="field">
|
|
492
505
|
<label>范围</label>
|
|
493
|
-
<select class="input" id="ex-provider"><option value="">全部账号</option><option value="p:qoder">Qoder(全部)</option><option value="qoder">仅 Qoder 国际版</option><option value="qoder-cn">仅 Qoder 国内版</option><option value="p:workbuddy">WorkBuddy(全部)</option><option value="workbuddy">仅 WorkBuddy 国内版</option><option value="workbuddy-intl">仅 WorkBuddy 国际版</option><option value="zcode">ZCode</option><option value="p:mirasim">mirasim</option><option value="p:catpaw">妙手</option><option value="p:trae">Trae</option></select>
|
|
506
|
+
<select class="input" id="ex-provider"><option value="">全部账号</option><option value="p:qoder">Qoder(全部)</option><option value="qoder">仅 Qoder 国际版</option><option value="qoder-cn">仅 Qoder 国内版</option><option value="p:workbuddy">WorkBuddy(全部)</option><option value="workbuddy">仅 WorkBuddy 国内版</option><option value="workbuddy-intl">仅 WorkBuddy 国际版</option><option value="zcode">ZCode</option><option value="p:mirasim">mirasim</option><option value="p:catpaw">妙手</option><option value="p:trae">Trae</option><option value="p:minimax">MiniMax</option></select>
|
|
494
507
|
</div>
|
|
495
508
|
<div class="field">
|
|
496
509
|
<label>加密口令(必填)</label>
|
|
@@ -640,6 +653,24 @@
|
|
|
640
653
|
</div>
|
|
641
654
|
</div>
|
|
642
655
|
|
|
656
|
+
<!-- MiniMax 本地接口 -->
|
|
657
|
+
<div class="mask" id="m-mgw">
|
|
658
|
+
<div class="modal sm">
|
|
659
|
+
<div class="modal-title"><span>MiniMax 本地接口</span><button class="close" data-close>✕</button></div>
|
|
660
|
+
<div class="hint" id="mgw-url" style="margin-bottom:10px"></div>
|
|
661
|
+
<div class="field">
|
|
662
|
+
<label class="check"><input type="checkbox" id="mgw-on"> 启用本地接口(/gateway/minimax/v1/messages)</label>
|
|
663
|
+
</div>
|
|
664
|
+
<div class="field" id="mgw-lan-fields">
|
|
665
|
+
<label class="check"><input type="checkbox" id="mgw-lan"> 对局域网开放(需全局局域网已开启)</label>
|
|
666
|
+
<label>IP 白名单(每行一个,支持 192.168.31.* 通配)</label>
|
|
667
|
+
<textarea class="input" id="mgw-allow" rows="4" placeholder="192.168.31.101"></textarea>
|
|
668
|
+
</div>
|
|
669
|
+
<div class="hint">开启后,MiniMax 积分与模型能力变成本机的一个原生 Anthropic Messages 补全接口——在 10Router 或 Claude Code 等工具中建一个 anthropic-compatible 节点指向本机 <code>/gateway/minimax/v1/messages</code> 即可调用。支持多账号自动轮转、429 限流自动冷却 5 分钟、凭据失效自动静默刷新。白名单内的机器<b>免密直连</b>;本机回环(127.0.0.1)始终放行。</div>
|
|
670
|
+
<div class="modal-actions"><button class="btn" data-close>取消</button><button class="btn primary" id="mgw-ok">保存</button></div>
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
|
|
643
674
|
<input type="file" id="file-input" accept=".json,application/json" style="display:none">
|
|
644
675
|
<div id="toast"></div>
|
|
645
676
|
|
|
@@ -669,8 +700,8 @@ const I = {
|
|
|
669
700
|
};
|
|
670
701
|
|
|
671
702
|
const $ = (id) => document.getElementById(id);
|
|
672
|
-
const state = { accounts: [], quotas: {}, status: null, busy: {}, view: 'dash', region: '', addProduct: '' };
|
|
673
|
-
const VIEWS = ['dash', 'qoder', 'workbuddy', 'zcode', 'mirasim', 'catpaw', 'trae', 'tenrouter'];
|
|
703
|
+
const state = { accounts: [], quotas: {}, status: null, busy: {}, view: 'dash', region: '', addProduct: '', logTab: 'run' };
|
|
704
|
+
const VIEWS = ['dash', 'qoder', 'workbuddy', 'zcode', 'mirasim', 'catpaw', 'trae', 'minimax', 'tenrouter'];
|
|
674
705
|
// 10Router 集成:config 来自 /api/tenrouter,quotas 来自 /api/tenrouter/quotas
|
|
675
706
|
const tr = { config: null, quotas: null, error: null, loading: false, syncing: false };
|
|
676
707
|
const PRODUCTS = [
|
|
@@ -680,6 +711,7 @@ const PRODUCTS = [
|
|
|
680
711
|
{ id: 'mirasim', label: 'mirasim' },
|
|
681
712
|
{ id: 'catpaw', label: '妙手' },
|
|
682
713
|
{ id: 'trae', label: 'Trae' },
|
|
714
|
+
{ id: 'minimax', label: 'MiniMax' },
|
|
683
715
|
];
|
|
684
716
|
const prodLabel = (id) => (PRODUCTS.find((x) => x.id === id) || { label: id }).label;
|
|
685
717
|
|
|
@@ -691,6 +723,7 @@ const COMPONENTS = [
|
|
|
691
723
|
{ id: 'mirasim', label: 'mirasim' },
|
|
692
724
|
{ id: 'catpaw', label: '妙手' },
|
|
693
725
|
{ id: 'trae', label: 'Trae' },
|
|
726
|
+
{ id: 'minimax', label: 'MiniMax' },
|
|
694
727
|
{ id: 'tenrouter', label: '10Router' },
|
|
695
728
|
];
|
|
696
729
|
const HIDDEN_COMP_KEY = 'qd_hidden_components';
|
|
@@ -812,12 +845,15 @@ const PROV = {
|
|
|
812
845
|
mirasim: { prod: 'mirasim', prodLabel: 'mirasim', region: '', cls: '' },
|
|
813
846
|
catpaw: { prod: 'catpaw', prodLabel: '妙手', region: '', cls: '' },
|
|
814
847
|
trae: { prod: 'trae', prodLabel: 'Trae', region: '', cls: '' },
|
|
848
|
+
minimax: { prod: 'minimax', prodLabel: 'MiniMax', region: '', cls: '' },
|
|
815
849
|
};
|
|
816
850
|
// 支持每日签到的产品(ZCode / mirasim / 妙手 额度靠套餐或滚动窗口发放,不做签到)
|
|
817
|
-
const CHECKIN_PRODUCTS = new Set(['qoder', 'workbuddy', 'trae']);
|
|
818
|
-
const CLIENT_LABEL = { qoder: 'Qoder', workbuddy: 'WorkBuddy', zcode: 'ZCode', mirasim: 'mirasim', catpaw: '妙手', trae: 'Trae' };
|
|
851
|
+
const CHECKIN_PRODUCTS = new Set(['qoder', 'workbuddy', 'trae', 'minimax']);
|
|
852
|
+
const CLIENT_LABEL = { qoder: 'Qoder', workbuddy: 'WorkBuddy', zcode: 'ZCode', mirasim: 'mirasim', catpaw: '妙手', trae: 'Trae', minimax: 'MiniMax' };
|
|
819
853
|
// 额度单位为 Credits/时长而非「积分」口径的产品(不计入剩余积分合计)
|
|
820
854
|
const CREDIT_VIEWS = new Set(['zcode', 'mirasim', 'catpaw']);
|
|
855
|
+
// 网关调用日志:tag → 所属产品页(无网关的产品不展示日志卡)
|
|
856
|
+
const GW_LOG_TAG = { zcode: 'ZCODE-GW', minimax: 'MINIMAX-GW' };
|
|
821
857
|
const provOf = (p) => PROV[p] || { prod: 'qoder', prodLabel: p, region: '', cls: '' };
|
|
822
858
|
const provBadges = (p, regionOnly, flavor) => {
|
|
823
859
|
const v = provOf(p);
|
|
@@ -942,7 +978,8 @@ function initTheme() {
|
|
|
942
978
|
function statusOf(a) {
|
|
943
979
|
if (!CHECKIN_PRODUCTS.has(a.product)) return { cls: '', text: '', msg: '' }; // ZCode 无每日领取,不显示徽标
|
|
944
980
|
const intl = a.provider === 'workbuddy-intl';
|
|
945
|
-
const
|
|
981
|
+
const byProd = (state.status && state.status.todayByProduct) || {};
|
|
982
|
+
const today = byProd[a.product] || (state.status && state.status.today);
|
|
946
983
|
const done = state.status && state.status.todayDone && state.status.todayDone[a.id] === today;
|
|
947
984
|
const r = a.lastResult;
|
|
948
985
|
const fresh = r && r.at && state.status && (Date.now() - new Date(r.at).getTime() < 26 * 3600e3);
|
|
@@ -1104,6 +1141,7 @@ function cardHtml(a) {
|
|
|
1104
1141
|
+ provBadges(a.provider, true, a.flavor)
|
|
1105
1142
|
+ (isCurrent ? '<span class="pill cur" title="' + esc(CLIENT_LABEL[a.product] || '') + ' 客户端当前登录的账号">客户端当前</span>' : '')
|
|
1106
1143
|
+ (a.product === 'zcode' && isGatewayCurrent(a) ? '<span class="pill gw" title="体验包接口最近一次补全用的账号(多账号时按轮换自动选择,与客户端登录无关)">接口使用中</span>' : '')
|
|
1144
|
+
+ (a.product === 'minimax' && isMiniMaxGatewayCurrent(a) ? '<span class="pill gw" title="本地接口最近一次补全用的账号(多账号时按轮换自动选择,与客户端登录无关)">接口使用中</span>' : '')
|
|
1107
1145
|
+ (a.isPat ? '<span class="badge">PAT</span>' : '')
|
|
1108
1146
|
+ (a.verified === false ? '<span class="pill fail">token 无效</span>' : '')
|
|
1109
1147
|
+ (st.text ? '<span class="pill ' + st.cls + '" title="' + esc(a.lastResult ? fmtTime(a.lastResult.at) + ' ' + (a.lastResult.message || a.lastResult.status) : '') + '">' + esc(st.text) + '</span>' : '')
|
|
@@ -1125,7 +1163,14 @@ function cardHtml(a) {
|
|
|
1125
1163
|
+ '</div>';
|
|
1126
1164
|
}
|
|
1127
1165
|
|
|
1128
|
-
const isDone = (a) =>
|
|
1166
|
+
const isDone = (a) => {
|
|
1167
|
+
if (!state.status || !state.status.todayDone) return false;
|
|
1168
|
+
const doneDay = state.status.todayDone[a.id];
|
|
1169
|
+
if (!doneDay) return false;
|
|
1170
|
+
const byProd = state.status.todayByProduct || {};
|
|
1171
|
+
const expectedDay = byProd[a.product] || state.status.today;
|
|
1172
|
+
return doneDay === expectedDay;
|
|
1173
|
+
};
|
|
1129
1174
|
const ofProduct = (prod) => state.accounts.filter((a) => a.product === prod);
|
|
1130
1175
|
const currentUidOf = (prod) => state.status && (prod === 'workbuddy' ? state.status.workbuddyCurrentUid : prod === 'zcode' ? state.status.zcodeCurrentUid : prod === 'catpaw' ? state.status.catpawCurrentUid : prod === 'trae' ? state.status.traeCurrentUid : null);
|
|
1131
1176
|
/** 接口使用中判定:体验包接口最近一次补全用的账号(lastAccount 记的是 name || uid || id) */
|
|
@@ -1133,8 +1178,13 @@ function isGatewayCurrent(a) {
|
|
|
1133
1178
|
const last = state.zGatewayStats && state.zGatewayStats.lastAccount;
|
|
1134
1179
|
return Boolean(last) && (a.name === last || a.uid === last || a.id === last);
|
|
1135
1180
|
}
|
|
1181
|
+
/** MiniMax 本地接口使用中判定 */
|
|
1182
|
+
function isMiniMaxGatewayCurrent(a) {
|
|
1183
|
+
const last = state.mGatewayStats && state.mGatewayStats.lastAccount;
|
|
1184
|
+
return Boolean(last) && (a.name === last || a.uid === last || a.id === last);
|
|
1185
|
+
}
|
|
1136
1186
|
|
|
1137
|
-
// 客户端当前登录判定:WorkBuddy / mirasim / 妙手 按 uid;ZCode 按 uid 或 email(早期导入的账号可能只存了其中一个)
|
|
1187
|
+
// 客户端当前登录判定:WorkBuddy / mirasim / 妙手 / MiniMax 按 uid;ZCode 按 uid 或 email(早期导入的账号可能只存了其中一个)
|
|
1138
1188
|
function isClientCurrent(a) {
|
|
1139
1189
|
const s = state.status;
|
|
1140
1190
|
if (!s) return false;
|
|
@@ -1146,6 +1196,7 @@ function isClientCurrent(a) {
|
|
|
1146
1196
|
if (a.product === 'mirasim') return Boolean(a.uid && s.mirasimCurrentUid && s.mirasimCurrentUid === a.uid);
|
|
1147
1197
|
if (a.product === 'catpaw') return Boolean(a.uid && s.catpawCurrentUid && s.catpawCurrentUid === a.uid);
|
|
1148
1198
|
if (a.product === 'trae') return Boolean(a.uid && s.traeCurrentUid && s.traeCurrentUid === a.uid);
|
|
1199
|
+
if (a.product === 'minimax') return Boolean(a.uid && s.minimaxCurrentUid && s.minimaxCurrentUid === a.uid);
|
|
1149
1200
|
if (a.product === 'zcode') {
|
|
1150
1201
|
const id = s.zcodeCurrentIdentity;
|
|
1151
1202
|
return Boolean(id && ((a.uid && id.uid != null && String(id.uid) === String(a.uid)) || (a.email && id.email && id.email === a.email)));
|
|
@@ -1169,6 +1220,7 @@ function creditSum(list) {
|
|
|
1169
1220
|
function render() {
|
|
1170
1221
|
$('z-actions').style.display = 'none'; // 兜底:切到仪表盘 / 10Router 时 renderProd 不跑,先藏起来
|
|
1171
1222
|
$('t-actions').style.display = 'none';
|
|
1223
|
+
$('m-actions').style.display = 'none';
|
|
1172
1224
|
for (const p of PRODUCTS) $('cnt-' + p.id).textContent = ofProduct(p.id).length;
|
|
1173
1225
|
$('cnt-tenrouter').textContent = tr.quotas ? tr.quotas.connections.length : tr.config && tr.config.configured ? '…' : '–';
|
|
1174
1226
|
if (state.view === 'dash') renderDash();
|
|
@@ -1183,6 +1235,7 @@ const EMPTY_TIP = {
|
|
|
1183
1235
|
mirasim: '还没有 mirasim 账号。<br>点「添加账号」可读取本机 mirasim 客户端当前登录的账号(本地解密,token 不出本机)。',
|
|
1184
1236
|
catpaw: '还没有妙手账号。<br>点「添加账号」可读取本机妙手(美团 CatPaw)客户端当前登录的账号(本地解密凭据,token 不出本机)。',
|
|
1185
1237
|
trae: '还没有 Trae 账号。<br>点「添加账号 → 本机导入」可读取本机 Trae 客户端当前登录的账号(本地解密 storage.json 凭据,token 不出本机)。多个账号需先在 Trae 里分别登录并逐个导入。',
|
|
1238
|
+
minimax: '还没有 MiniMax 账号。<br>点「添加账号 → 本机导入」可读取本机 MiniMax Code 客户端当前登录的账号,或粘贴 Bearer Token 添加。',
|
|
1186
1239
|
};
|
|
1187
1240
|
|
|
1188
1241
|
function renderProd() {
|
|
@@ -1211,7 +1264,7 @@ function renderProd() {
|
|
|
1211
1264
|
$('s-credits').textContent = CREDIT_VIEWS.has(prod)
|
|
1212
1265
|
? acc.filter((a) => { const q = state.quotas[a.id]; return q && !q.error && !q.empty; }).length + ' / ' + acc.length
|
|
1213
1266
|
: c.loading ? '…' : (c.known ? fmtNum(c.sum) : '-');
|
|
1214
|
-
const hasRegions = !CREDIT_VIEWS.has(prod) && prod !== 'trae'; // Trae 不分国际 / 国内版
|
|
1267
|
+
const hasRegions = !CREDIT_VIEWS.has(prod) && prod !== 'trae' && prod !== 'minimax'; // Trae / MiniMax 不分国际 / 国内版
|
|
1215
1268
|
$('region').style.display = hasRegions ? '' : 'none';
|
|
1216
1269
|
$('region-div').style.display = hasRegions ? '' : 'none';
|
|
1217
1270
|
document.querySelectorAll('#region button').forEach((x) => x.classList.toggle('active', x.dataset.r === state.region));
|
|
@@ -1250,6 +1303,19 @@ function renderProd() {
|
|
|
1250
1303
|
$('tt-open').title = tc.running ? 'Trae 客户端运行中,点我唤到前台' : 'Trae 客户端未运行,点我启动';
|
|
1251
1304
|
}
|
|
1252
1305
|
}
|
|
1306
|
+
$('m-actions').style.display = prod === 'minimax' ? 'contents' : 'none';
|
|
1307
|
+
if (prod === 'minimax') {
|
|
1308
|
+
const gwOn = state.mGateway === true;
|
|
1309
|
+
$('mt-gw').innerHTML = dot(gwOn) + '接口' + (gwOn && state.mGatewayLan === true ? '·局域网' : '');
|
|
1310
|
+
$('mt-gw').title = 'MiniMax 本地接口:开关、局域网开放与 IP 白名单';
|
|
1311
|
+
const mc = (state.status && state.status.minimaxClient) || {};
|
|
1312
|
+
const canOpen = mc.installed && window.creditdaddy && typeof window.creditdaddy.openMiniMaxClient === 'function';
|
|
1313
|
+
$('mt-open').style.display = canOpen ? '' : 'none';
|
|
1314
|
+
if (canOpen) {
|
|
1315
|
+
$('mt-open').innerHTML = dot(mc.running === true, true) + '打开客户端';
|
|
1316
|
+
$('mt-open').title = mc.running ? 'MiniMax Code 客户端运行中,点我唤到前台' : 'MiniMax Code 客户端未运行,点我启动';
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1253
1319
|
}
|
|
1254
1320
|
|
|
1255
1321
|
function kpi(label, value, sub, labelTitle) {
|
|
@@ -1266,7 +1332,7 @@ function renderDash() {
|
|
|
1266
1332
|
const failed = signable.filter((a) => statusOf(a).cls === 'fail').length;
|
|
1267
1333
|
const credit = creditSum(state.accounts.filter((a) => a.product !== 'zcode'));
|
|
1268
1334
|
const byProd = orderedVisibleComps().filter((id) => id !== 'tenrouter').map((id) => prodLabel(id) + ' ' + ofProduct(id).length).join(' · ');
|
|
1269
|
-
const creditByProd = ['qoder', 'workbuddy'].filter(compVisible).map((id) => { const c = creditSum(ofProduct(id)); return prodLabel(id) + ' ' + (c.known ? fmtNum(c.sum) : '-'); }).join(' · ');
|
|
1335
|
+
const creditByProd = ['qoder', 'workbuddy', 'minimax'].filter(compVisible).map((id) => { const c = creditSum(ofProduct(id)); return prodLabel(id) + ' ' + (c.known ? fmtNum(c.sum) : '-'); }).join(' · ');
|
|
1270
1336
|
// 今日进度拆「国内领取 / 国际活跃」:国际版 WorkBuddy 是保持活跃(活跃不等于必有奖励)
|
|
1271
1337
|
const intlAcc = signable.filter((a) => a.provider === 'workbuddy-intl');
|
|
1272
1338
|
const domAcc = signable.filter((a) => a.provider !== 'workbuddy-intl');
|
|
@@ -1306,11 +1372,18 @@ function renderDash() {
|
|
|
1306
1372
|
+ (cnList.length && intlList.length ? ' · ' : '')
|
|
1307
1373
|
+ (intlList.length ? intlList.filter(isDone).length + ' / ' + intlList.length + ' 国际' : '')
|
|
1308
1374
|
+ '</span>';
|
|
1375
|
+
} else if (p.id === 'minimax') {
|
|
1376
|
+
const planned = list.filter((a) => { const q = state.quotas[a.id]; return q && !q.error && !q.empty; }).length;
|
|
1377
|
+
rows = '<span class="lbl">今日领取</span><span class="val">' + d + ' / ' + list.length + '</span>'
|
|
1378
|
+
+ '<span style="grid-column:1 / -1">' + meter(d, list.length) + '</span>'
|
|
1379
|
+
+ '<span class="lbl">剩余积分</span><span class="val">' + (c.known ? fmtNum(c.sum) : '-') + '</span>';
|
|
1309
1380
|
} else {
|
|
1310
1381
|
rows = '<span class="lbl">今日领取</span><span class="val">' + d + ' / ' + list.length + '</span>';
|
|
1311
1382
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1383
|
+
if (p.id !== 'minimax') {
|
|
1384
|
+
rows += '<span style="grid-column:1 / -1">' + meter(d, list.length) + '</span>'
|
|
1385
|
+
+ '<span class="lbl">剩余积分</span><span class="val">' + (c.known ? fmtNum(c.sum) : '-') + '</span>';
|
|
1386
|
+
}
|
|
1314
1387
|
} else if (p.id === 'mirasim') {
|
|
1315
1388
|
const planned = list.filter((a) => { const q = state.quotas[a.id]; return q && !q.error && !q.empty; }).length;
|
|
1316
1389
|
rows = '<span class="lbl">额度体系</span><span class="val dim">5h / 7d 滚动窗口</span>'
|
|
@@ -1337,6 +1410,7 @@ function renderDash() {
|
|
|
1337
1410
|
: p.id === 'mirasim' && s.mirasimCurrentUid ? '未导入的账号'
|
|
1338
1411
|
: p.id === 'catpaw' && s.catpawClient && s.catpawClient.signedIn && !list.some((a) => isClientCurrent(a)) ? '未导入的账号'
|
|
1339
1412
|
: p.id === 'trae' && s.traeClient && s.traeClient.signedIn && !list.some((a) => isClientCurrent(a)) ? '未导入的账号'
|
|
1413
|
+
: p.id === 'minimax' && s.minimaxClient && s.minimaxClient.signedIn && !list.some((a) => isClientCurrent(a)) ? '未导入的账号'
|
|
1340
1414
|
: (p.id === 'workbuddy' && s.workbuddyCurrentUid ? '未导入的账号' : '—');
|
|
1341
1415
|
rows += '<span class="lbl">客户端当前</span><span class="val">' + live + '</span>';
|
|
1342
1416
|
}
|
|
@@ -1409,8 +1483,11 @@ function renderStatus() {
|
|
|
1409
1483
|
// 产品相关提示只在对应标签页出现
|
|
1410
1484
|
const hasIntl = state.view === 'qoder' && state.region !== 'cn' && state.accounts.some((a) => a.provider === 'qoder');
|
|
1411
1485
|
const n = $('notice');
|
|
1412
|
-
n.classList.toggle('info', state.view === 'zcode' || state.view === 'mirasim' || state.view === 'catpaw' || state.view === 'trae' || (hasIntl && Boolean(s.riskIdentity)));
|
|
1413
|
-
if (state.view === '
|
|
1486
|
+
n.classList.toggle('info', state.view === 'zcode' || state.view === 'mirasim' || state.view === 'catpaw' || state.view === 'trae' || state.view === 'minimax' || (hasIntl && Boolean(s.riskIdentity)));
|
|
1487
|
+
if (state.view === 'minimax') {
|
|
1488
|
+
n.style.display = '';
|
|
1489
|
+
n.innerHTML = '<div class="z-row"><span class="z-label">每日签到</span><span class="z-text">每日签到领 800~2000 积分 + 400 连签加成。支持自动检测本机 MiniMax Code 凭据与 Bearer Token 导入,自带标准 Anthropic 本地网关。</span></div>';
|
|
1490
|
+
} else if (state.view === 'trae') {
|
|
1414
1491
|
n.style.display = '';
|
|
1415
1492
|
n.innerHTML = '<div class="z-row"><span class="z-label">每日签到</span><span class="z-text">每日签到领约 150 通用积分(7 天有效,多资源包自动归并)。凭据由本机 Trae 客户端解密导入,支持多账号快照一键切换。</span></div>';
|
|
1416
1493
|
} else if (state.view === 'catpaw') {
|
|
@@ -1515,6 +1592,29 @@ document.addEventListener('click', async (e) => {
|
|
|
1515
1592
|
} catch (err) { toast(String(err && err.message || err), false); }
|
|
1516
1593
|
return;
|
|
1517
1594
|
}
|
|
1595
|
+
if (e.target.closest('#mt-open')) {
|
|
1596
|
+
try {
|
|
1597
|
+
const r = await window.creditdaddy.openMiniMaxClient();
|
|
1598
|
+
if (r && r.ok) toast('已启动 MiniMax Code 客户端');
|
|
1599
|
+
else toast((r && r.error) || '无法打开 MiniMax Code 客户端', false);
|
|
1600
|
+
} catch (err) { toast(String(err && err.message || err), false); }
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (e.target.closest('#mt-gw')) {
|
|
1604
|
+
// MiniMax 本地接口弹窗:开关 + 局域网 + 白名单设置
|
|
1605
|
+
$('mgw-on').checked = state.mGateway === true;
|
|
1606
|
+
$('mgw-lan').checked = state.mGatewayLan === true;
|
|
1607
|
+
const allow = Array.isArray(state.mGatewayAllow) ? state.mGatewayAllow : [];
|
|
1608
|
+
$('mgw-allow').value = (allow.length ? allow : (state.mGatewaySuggest ? [state.mGatewaySuggest] : [])).join('\n');
|
|
1609
|
+
const st = state.mGatewayStats;
|
|
1610
|
+
$('mgw-url').innerHTML = '接口地址 <code>POST http://127.0.0.1:' + location.port + '/gateway/minimax/v1/messages</code>(局域网开放时把 127.0.0.1 换成本机 IP)'
|
|
1611
|
+
+ (st && st.lastCallAt
|
|
1612
|
+
? '<br>最近调用 ' + esc(String(st.lastCallAt).slice(11, 19)) + ' · 共 ' + st.calls + ' 次' + (st.lastAccount ? ' · 最后用号 ' + esc(st.lastAccount) : '')
|
|
1613
|
+
: '<br>尚未有调用');
|
|
1614
|
+
syncMgwLanFields();
|
|
1615
|
+
openModal('m-mgw');
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1518
1618
|
if (e.target.closest('#zt-ac')) {
|
|
1519
1619
|
// 自动领取计划弹窗:周期 / 运行时长由用户决定
|
|
1520
1620
|
$('zac-interval').value = state.zClaimIntervalMin || 2;
|
|
@@ -1570,6 +1670,19 @@ async function loadZGateway() {
|
|
|
1570
1670
|
state.zGatewayHasCaptcha = d.hasCaptcha === true;
|
|
1571
1671
|
state.zGatewayStats = d.stats || null;
|
|
1572
1672
|
if (state.view === 'zcode') { renderStatus(); render(); }
|
|
1673
|
+
applyLogTabs(); // 网关开关变化影响日志标签显隐
|
|
1674
|
+
} catch (e) {}
|
|
1675
|
+
}
|
|
1676
|
+
async function loadMGateway() {
|
|
1677
|
+
try {
|
|
1678
|
+
const d = await api('/api/minimax-gateway');
|
|
1679
|
+
state.mGateway = d.enabled === true;
|
|
1680
|
+
state.mGatewayLan = d.lan === true;
|
|
1681
|
+
state.mGatewayAllow = d.allow || [];
|
|
1682
|
+
state.mGatewaySuggest = d.suggestAllow || '';
|
|
1683
|
+
state.mGatewayStats = d.stats || null;
|
|
1684
|
+
if (state.view === 'minimax') { renderStatus(); render(); }
|
|
1685
|
+
applyLogTabs(); // 网关开关变化影响日志标签显隐
|
|
1573
1686
|
} catch (e) {}
|
|
1574
1687
|
}
|
|
1575
1688
|
|
|
@@ -2080,12 +2193,46 @@ async function loadQuotas(ids) {
|
|
|
2080
2193
|
};
|
|
2081
2194
|
await Promise.all([worker(), worker(), worker()]);
|
|
2082
2195
|
}
|
|
2196
|
+
let logTabView = null; // 当前日志标签所属的视图:切到别的视图时才重置跟随,同一视图内保留手动切换
|
|
2197
|
+
function followLogTab() {
|
|
2198
|
+
if (logTabView !== state.view) {
|
|
2199
|
+
state.logTab = (state.view !== 'dash' && GW_LOG_TAG[state.view]) ? state.view : 'run';
|
|
2200
|
+
logTabView = state.view;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2083
2203
|
async function loadLogs() {
|
|
2204
|
+
followLogTab();
|
|
2205
|
+
applyLogTabs();
|
|
2206
|
+
await loadCurrentLogs();
|
|
2207
|
+
}
|
|
2208
|
+
function applyLogTabs() {
|
|
2209
|
+
// 网关开关关闭时,隐藏对应日志标签(无该网关时也看不到)
|
|
2210
|
+
document.querySelectorAll('#log-tabs button').forEach((b) => {
|
|
2211
|
+
const t = b.dataset.logTab;
|
|
2212
|
+
let show = true;
|
|
2213
|
+
if (t === 'zcode') show = Boolean(state.zGateway);
|
|
2214
|
+
if (t === 'minimax') show = Boolean(state.mGateway);
|
|
2215
|
+
b.style.display = show ? '' : 'none';
|
|
2216
|
+
b.classList.toggle('active', t === state.logTab);
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
async function loadCurrentLogs() {
|
|
2220
|
+
const el = $('logs');
|
|
2221
|
+
if (!el) return;
|
|
2222
|
+
let logs = [];
|
|
2084
2223
|
try {
|
|
2085
|
-
|
|
2086
|
-
|
|
2224
|
+
if (state.logTab === 'run') {
|
|
2225
|
+
const data = await api('/api/logs');
|
|
2226
|
+
logs = data.logs || [];
|
|
2227
|
+
} else {
|
|
2228
|
+
const data = await api('/api/logs?tag=' + encodeURIComponent(GW_LOG_TAG[state.logTab]));
|
|
2229
|
+
logs = data.logs || [];
|
|
2230
|
+
}
|
|
2087
2231
|
const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 8;
|
|
2088
|
-
|
|
2232
|
+
const withTag = state.logTab === 'run';
|
|
2233
|
+
el.innerHTML = logs.length
|
|
2234
|
+
? logs.map((l) => '<div class="' + esc(l.level) + '">[' + esc(l.at) + '] ' + (withTag ? esc(l.tag) + ' · ' : '') + esc(l.msg) + '</div>').join('')
|
|
2235
|
+
: '(暂无)';
|
|
2089
2236
|
if (atBottom) el.scrollTop = el.scrollHeight;
|
|
2090
2237
|
} catch (e) {}
|
|
2091
2238
|
}
|
|
@@ -2498,6 +2645,27 @@ $('zgw-ok').addEventListener('click', async () => {
|
|
|
2498
2645
|
} catch (err) { toast(err.message, false); }
|
|
2499
2646
|
});
|
|
2500
2647
|
|
|
2648
|
+
// MiniMax 本地接口:开关 + 局域网开放 + 免密 IP 白名单一次保存
|
|
2649
|
+
function syncMgwLanFields() {
|
|
2650
|
+
const off = !$('mgw-on').checked;
|
|
2651
|
+
$('mgw-lan-fields').querySelectorAll('input, textarea').forEach((el) => { el.disabled = off; });
|
|
2652
|
+
$('mgw-lan-fields').style.opacity = off ? '.45' : '';
|
|
2653
|
+
}
|
|
2654
|
+
$('mgw-on').addEventListener('change', syncMgwLanFields);
|
|
2655
|
+
$('mgw-ok').addEventListener('click', async () => {
|
|
2656
|
+
const allow = $('mgw-allow').value.split('\n').map((x) => x.trim()).filter(Boolean);
|
|
2657
|
+
try {
|
|
2658
|
+
const r = await api('/api/minimax-gateway', { method: 'PUT', json: { enabled: $('mgw-on').checked, lan: $('mgw-lan').checked, allow } });
|
|
2659
|
+
state.mGateway = r.enabled === true;
|
|
2660
|
+
state.mGatewayLan = r.lan === true;
|
|
2661
|
+
state.mGatewayAllow = r.allow || [];
|
|
2662
|
+
renderStatus(); render();
|
|
2663
|
+
closeModal('m-mgw');
|
|
2664
|
+
toast('MiniMax 接口:' + (r.enabled ? '已开启' : '已关闭')
|
|
2665
|
+
+ (r.enabled && r.lan ? ',局域网开放' + (r.allow && r.allow.length ? ',白名单 ' + r.allow.join('、') : '') : ''));
|
|
2666
|
+
} catch (err) { toast(err.message, false); }
|
|
2667
|
+
});
|
|
2668
|
+
|
|
2501
2669
|
// ZCode 网络出口:顺序 + 代理地址保存
|
|
2502
2670
|
$('znet-ok').addEventListener('click', async () => {
|
|
2503
2671
|
try {
|
|
@@ -2529,10 +2697,11 @@ const LOCAL_HINT = {
|
|
|
2529
2697
|
mirasim: '读取本机 mirasim 客户端当前登录的账号(本地解密 ~/.mirasim/setting.json),token 不出本机。',
|
|
2530
2698
|
catpaw: '读取本机妙手(美团 CatPaw)客户端当前登录的账号(本地解密 %APPDATA%\\catpaw-moon\\catx-credential.json),token 不出本机。',
|
|
2531
2699
|
trae: '读取本机 Trae 客户端当前登录的账号(本地解密 <客户端数据目录>\\User\\globalStorage\\storage.json 的 tc 信封),token 不出本机。',
|
|
2700
|
+
minimax: '读取本机 MiniMax Code 客户端当前登录的账号(~/.minimax/auth/.../auth.json),token 不出本机。',
|
|
2532
2701
|
};
|
|
2533
2702
|
function openAdd(product) {
|
|
2534
2703
|
state.addProduct = product || '';
|
|
2535
|
-
//
|
|
2704
|
+
// 各产品支持的添加方式:MiniMax 三种都支持(浏览器登录 = 官方设备码授权,独立凭据链不与本机客户端互斥)
|
|
2536
2705
|
const tabShown = { local: true, device: product !== 'mirasim' && product !== 'catpaw' && product !== 'trae', token: product !== 'zcode' && product !== 'mirasim' && product !== 'catpaw' && product !== 'trae' };
|
|
2537
2706
|
document.querySelectorAll('#m-add .tab').forEach((t) => { t.style.display = tabShown[t.dataset.tab] ? '' : 'none'; });
|
|
2538
2707
|
$('local-hint').textContent = LOCAL_HINT[state.addProduct];
|
|
@@ -2562,7 +2731,7 @@ function openAdd(product) {
|
|
|
2562
2731
|
async function localScan() {
|
|
2563
2732
|
const box = $('scan-result');
|
|
2564
2733
|
$('scan-status').textContent = '扫描中(最多约 10 秒)…';
|
|
2565
|
-
box.innerHTML = '<div class="hint">正在读取本机客户端凭据(Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae)…</div>';
|
|
2734
|
+
box.innerHTML = '<div class="hint">正在读取本机客户端凭据(Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae / MiniMax)…</div>';
|
|
2566
2735
|
try {
|
|
2567
2736
|
const r = await api('/api/local/scan', { method: 'POST' });
|
|
2568
2737
|
$('scan-status').textContent = '';
|
|
@@ -2583,9 +2752,9 @@ async function localScan() {
|
|
|
2583
2752
|
html += '<div class="hint" style="color:var(--err)">读取失败:' + esc(e.file) + ':' + esc(e.error) + '</div>';
|
|
2584
2753
|
}
|
|
2585
2754
|
if (!cands.length) {
|
|
2586
|
-
const where = state.addProduct ? prodLabel(state.addProduct) : 'Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae';
|
|
2587
|
-
const alt = state.addProduct === 'zcode' || state.addProduct === 'mirasim' || state.addProduct === 'catpaw' ? '' : ',或改用「浏览器登录」「粘贴 Token」';
|
|
2588
|
-
html += '<div class="hint">未发现已登录的 ' + where + ' 账号。请先在客户端登录' + alt + '。<br>已检查:Qoder / Qoder CN 客户端数据目录、WorkBuddy 凭据目录、ZCode 数据目录、mirasim 数据目录、妙手凭据目录、Trae 数据目录'
|
|
2755
|
+
const where = state.addProduct ? prodLabel(state.addProduct) : 'Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae / MiniMax';
|
|
2756
|
+
const alt = state.addProduct === 'zcode' || state.addProduct === 'mirasim' || state.addProduct === 'catpaw' || state.addProduct === 'trae' ? '' : ',或改用「浏览器登录」「粘贴 Token」';
|
|
2757
|
+
html += '<div class="hint">未发现已登录的 ' + where + ' 账号。请先在客户端登录' + alt + '。<br>已检查:Qoder / Qoder CN 客户端数据目录、WorkBuddy 凭据目录、ZCode 数据目录、mirasim 数据目录、妙手凭据目录、Trae 数据目录、MiniMax 数据目录'
|
|
2589
2758
|
+ (r.scannedDirs && r.scannedDirs.length ? '、' + r.scannedDirs.map(esc).join('、') : '') + '</div>';
|
|
2590
2759
|
}
|
|
2591
2760
|
box.innerHTML = html;
|
|
@@ -3022,7 +3191,7 @@ $('btn-export-dash').onclick = () => { $('ex-provider').value = ''; openModal('m
|
|
|
3022
3191
|
$('btn-export').onclick = () => {
|
|
3023
3192
|
const v = state.view;
|
|
3024
3193
|
// ZCode 是单一 provider,直接按 provider 选;其余产品页按产品线(p:);仪表盘 / 10Router 导出全部
|
|
3025
|
-
$('ex-provider').value = v === 'zcode' ? 'zcode' : (v === 'qoder' || v === 'workbuddy' || v === 'mirasim' || v === 'catpaw' || v === 'trae') ? 'p:' + v : '';
|
|
3194
|
+
$('ex-provider').value = v === 'zcode' ? 'zcode' : (v === 'qoder' || v === 'workbuddy' || v === 'mirasim' || v === 'catpaw' || v === 'trae' || v === 'minimax') ? 'p:' + v : '';
|
|
3026
3195
|
openModal('m-export');
|
|
3027
3196
|
};
|
|
3028
3197
|
$('btn-do-export').onclick = doExport;
|
|
@@ -3050,7 +3219,11 @@ function applyView() {
|
|
|
3050
3219
|
$('view-prod').classList.toggle('active', state.view !== 'dash' && state.view !== 'tenrouter');
|
|
3051
3220
|
$('view-10r').classList.toggle('active', state.view === 'tenrouter');
|
|
3052
3221
|
if (state.view === 'tenrouter') loadZGateway();
|
|
3222
|
+
if (state.view === 'minimax') loadMGateway();
|
|
3053
3223
|
if (state.view === 'tenrouter' && tr.config && tr.config.configured && !tr.quotas && !tr.loading) loadTrQuotas();
|
|
3224
|
+
followLogTab();
|
|
3225
|
+
applyLogTabs();
|
|
3226
|
+
loadCurrentLogs();
|
|
3054
3227
|
render();
|
|
3055
3228
|
renderStatus();
|
|
3056
3229
|
}
|
|
@@ -3074,15 +3247,26 @@ $('region').addEventListener('click', (e) => {
|
|
|
3074
3247
|
renderStatus();
|
|
3075
3248
|
});
|
|
3076
3249
|
$('f-provider').addEventListener('change', () => {
|
|
3077
|
-
$('f-token').placeholder = $('f-provider').value
|
|
3250
|
+
$('f-token').placeholder = $('f-provider').value === 'minimax'
|
|
3251
|
+
? 'MiniMax OAuth Bearer Token(mmoat_…)'
|
|
3252
|
+
: $('f-provider').value.startsWith('workbuddy')
|
|
3078
3253
|
? 'WorkBuddy / CodeBuddy 登录 token(eyJ…,可在 CodeBuddyExtension 的 auth/*.info 中找到)'
|
|
3079
3254
|
: 'dt-… / jt-… 或 PAT(pt-…,自动兑换)';
|
|
3080
3255
|
});
|
|
3081
3256
|
$('btn-logs').onclick = () => {
|
|
3082
3257
|
const c = $('log-card').classList.toggle('collapsed');
|
|
3083
3258
|
$('btn-logs').textContent = c ? '展开' : '收起';
|
|
3084
|
-
if (!c) {
|
|
3259
|
+
if (!c) { loadCurrentLogs(); $('logs').scrollTop = $('logs').scrollHeight; }
|
|
3085
3260
|
};
|
|
3261
|
+
$('log-tabs').addEventListener('click', (e) => {
|
|
3262
|
+
const b = e.target.closest('button[data-log-tab]');
|
|
3263
|
+
if (!b) return;
|
|
3264
|
+
state.logTab = b.dataset.logTab;
|
|
3265
|
+
logTabView = state.view; // 手动切换后,本视图内不再被 10s 轮询重置
|
|
3266
|
+
applyLogTabs();
|
|
3267
|
+
loadCurrentLogs();
|
|
3268
|
+
$('logs').scrollTop = $('logs').scrollHeight;
|
|
3269
|
+
});
|
|
3086
3270
|
|
|
3087
3271
|
// ── 仪表盘底部两卡:默认收起,点标题展开,展开状态存本地 ──
|
|
3088
3272
|
const DASH_FOLD_KEY = 'qd_dash_fold';
|
|
@@ -3115,6 +3299,7 @@ applyView();
|
|
|
3115
3299
|
loadSettings().then(renderCompMenu);
|
|
3116
3300
|
loadZNet();
|
|
3117
3301
|
loadZGateway();
|
|
3302
|
+
loadMGateway();
|
|
3118
3303
|
loadTrConfig().then(() => loadTrQuotas());
|
|
3119
3304
|
setInterval(() => loadTrQuotas(), 5 * 60 * 1000);
|
|
3120
3305
|
// 账号余额(ZCode 剩余 token 等)自动刷新:可见时每 2 分钟一轮;从托盘/后台切回时若超过 1 分钟没刷过立即补一轮
|
|
@@ -3124,7 +3309,7 @@ applyView();
|
|
|
3124
3309
|
});
|
|
3125
3310
|
loadLogs();
|
|
3126
3311
|
setInterval(loadLogs, 10000);
|
|
3127
|
-
setInterval(async () => { await loadStatus(); await loadZNet(); await loadZGateway(); render(); }, 30000);
|
|
3312
|
+
setInterval(async () => { await loadStatus(); await loadZNet(); await loadZGateway(); await loadMGateway(); render(); }, 30000);
|
|
3128
3313
|
}
|
|
3129
3314
|
// 先直接请求(本机模式无需密码);需要密码时 api() 会弹框并重试
|
|
3130
3315
|
api('/api/status').then(start).catch((e) => toast(e.code === 'PANEL_KEY'
|
package/src/providers.js
CHANGED
|
@@ -15,6 +15,7 @@ import { fetchZcodeQuota } from './zcodeClient.js';
|
|
|
15
15
|
import { fetchMirasimQuota, fetchMirasimProfile } from './mirasimClient.js';
|
|
16
16
|
import { fetchCatpawQuota, fetchCatpawProfile } from './catpawClient.js';
|
|
17
17
|
import { fetchTraeQuota, checkinTrae, verifyTraeAccount } from './traeClient.js';
|
|
18
|
+
import { fetchMiniMaxQuota, checkinMiniMax, verifyMiniMaxAccount } from './minimaxClient.js';
|
|
18
19
|
/** 从 userinfo 响应中挑一个可读的显示名 */
|
|
19
20
|
export function displayNameFrom(ui) {
|
|
20
21
|
const pick = [ui?.nickname, ui?.name, ui?.username, ui?.email]
|
|
@@ -183,8 +184,33 @@ const PRODUCTS = {
|
|
|
183
184
|
quota: (account) => fetchTraeQuota(account),
|
|
184
185
|
verify: (account) => verifyTraeAccount(account),
|
|
185
186
|
},
|
|
187
|
+
minimax: {
|
|
188
|
+
label: 'MiniMax',
|
|
189
|
+
// token 约 1 小时过期:checkin / quota 内部 401 时用 refreshToken 自动续期(withMiniMaxAuth)。
|
|
190
|
+
// 本机导入账号刷新前会重读 ~/.minimax auth.json 对齐客户端最新 refreshToken;
|
|
191
|
+
// ctx.onRefresh 回写账号库后,这里再安全回写 auth.json(仅客户端未运行时,见 minimaxLocal.writeMiniMaxAuth)。
|
|
192
|
+
// device 登录账号自持独立凭据链(无 meta.authRecordKey),writeMiniMaxAuth 对其 no-op,不与本机客户端互斥。
|
|
193
|
+
checkin: (account, ctx = {}) => checkinMiniMax(account, wrapMiniMaxRefresh(account, ctx)),
|
|
194
|
+
quota: (account, ctx = {}) => fetchMiniMaxQuota(account, wrapMiniMaxRefresh(account, ctx)),
|
|
195
|
+
verify: (account) => verifyMiniMaxAccount(account),
|
|
196
|
+
},
|
|
186
197
|
};
|
|
187
198
|
|
|
199
|
+
/** 包裹 MiniMax 的 ctx.onRefresh:回写账号库后,安全同步新凭据回本机 auth.json(仅本机导入账号且客户端未运行时) */
|
|
200
|
+
function wrapMiniMaxRefresh(account, ctx = {}) {
|
|
201
|
+
return {
|
|
202
|
+
...ctx,
|
|
203
|
+
onRefresh: async (creds) => {
|
|
204
|
+
await ctx.onRefresh?.(creds);
|
|
205
|
+
try {
|
|
206
|
+
const { writeMiniMaxAuth } = await import('./minimaxLocal.js');
|
|
207
|
+
const synced = writeMiniMaxAuth({ ...account, ...creds });
|
|
208
|
+
if (synced) ctx.log?.('已同步新 token 到 MiniMax 客户端');
|
|
209
|
+
} catch {}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
188
214
|
export function productImpl(provider) {
|
|
189
215
|
return PRODUCTS[productOf(provider)];
|
|
190
216
|
}
|
package/src/qoderApp.js
CHANGED
|
@@ -19,6 +19,7 @@ import path from 'node:path';
|
|
|
19
19
|
import crypto from 'node:crypto';
|
|
20
20
|
import { spawn, execFile, execFileSync } from 'node:child_process';
|
|
21
21
|
import { dataDir } from './store.js';
|
|
22
|
+
import { anyProcRunning } from './winProc.js';
|
|
22
23
|
import { installedUmid, CLI_RISK_ENV } from './qoderUmid.js';
|
|
23
24
|
|
|
24
25
|
const RISK_TTL_MS = 50 * 60 * 1000; // 客户端每 60±5 分钟刷新一次,这里保守取 50 分钟
|
|
@@ -460,15 +461,8 @@ const QODER_EXES = ['Qoder.exe', 'Qoder CN.exe'];
|
|
|
460
461
|
|
|
461
462
|
/** Qoder IDE(国际版 / 国内版)是否在运行 */
|
|
462
463
|
export function qoderRunning() {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
const out = execFileSync('tasklist.exe', ['/FI', `IMAGENAME eq ${exe}`, '/NH'],
|
|
467
|
-
{ windowsHide: true, encoding: 'buffer', timeout: 8000 }).toString('latin1');
|
|
468
|
-
if (!/No Tasks|没有/i.test(out)) return true;
|
|
469
|
-
} catch { /* 查不到就换下一名 */ }
|
|
470
|
-
}
|
|
471
|
-
return false;
|
|
464
|
+
// 走 winProc:这里曾经用 latin1 解 tasklist,中文 Windows 上恒为 true
|
|
465
|
+
return anyProcRunning(QODER_EXES);
|
|
472
466
|
}
|
|
473
467
|
|
|
474
468
|
const nap = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|