dsh-connect-minimaxcode 0.6.4 → 0.6.5
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.en.md +6 -0
- package/README.md +6 -0
- package/lib/client.js +23 -2
- package/lib/index.d.ts +60 -6
- package/lib/index.js +93 -15
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -384,6 +384,12 @@ The OAuth token is valid for about an hour and is refreshed automatically by the
|
|
|
384
384
|
|
|
385
385
|
## Changelog
|
|
386
386
|
|
|
387
|
+
### 0.6.5 (2026-11-12)
|
|
388
|
+
|
|
389
|
+
- **Fixed "the limit windows never showed up"**: once a plan is held, the upstream changed the quota endpoint's **field names and its meaning** — from the share *used* (the string `"19%"`) to the share *remaining* (the number `90`). The plugin still read the old names, so the windows either could not be read at all or read as the opposite. Both shapes are accepted now, and what leaves the plugin is always the share *used*: 92% remaining is 8% used. The window length is still derived from the answer's own edges (measured: five hours and seven days) rather than assumed.
|
|
390
|
+
- **Added the plan to the card**: the membership answer now names the tier and the day it ends (`token_plan_tier`, `token_plan_expires_at`), and the plugin shows one more line in the pill detail and on the plugin page — "Go · until 2026-12-11". It comes from the same answer as the balance, so it costs no extra request.
|
|
391
|
+
- **An account without a plan sees exactly what it saw before**: the plan fields are simply absent there, so the plugin shows nothing about a plan; the windows are still absent, and the card still says "No M Plan subscription (credits only), so there are no usage windows". The new `tests/free-account.spec.ts` pins that whole path for a free account (balance, plan, windows), and `tests/plan.spec.ts` covers the new field shape, the old one and the free account.
|
|
392
|
+
|
|
387
393
|
### 0.6.4 (2026-11-11)
|
|
388
394
|
|
|
389
395
|
- **Fixed "the self-check report never appeared"**: the previous version put the self-check in the client half (the card reports what the host offered), but **the server half was never landed** — the status document had no field to carry the report and no route to receive it, so the card reported into nowhere. It is complete now: the report is received by the plugin's own route and appears in the status document as `capability`.
|
package/README.md
CHANGED
|
@@ -395,6 +395,12 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
|
|
|
395
395
|
|
|
396
396
|
## 更新记录
|
|
397
397
|
|
|
398
|
+
### 0.6.5(2026-11-12)
|
|
399
|
+
|
|
400
|
+
- **修复「限额窗口一直显示不出来」**:上游在有了套餐之后改了额度接口的**字段名和语义** —— 从「已用百分比」(字符串 `"19%"`)改成「**剩余**百分比」(数字 `90`)。插件仍按旧字段名读,于是窗口读不到,或者读成相反的意思。现在两种形态都认,且对外一律是「已用百分比」:读到剩余 92% 就是已用 8%。窗口长度仍从响应自身的起止时刻推导(实测 5 小时与 7 天),不写死。
|
|
401
|
+
- **新增套餐显示**:会员接口现在会报出档位与到期日(`token_plan_tier`、`token_plan_expires_at`),插件在胶囊详情与插件页多显示一行,例如「Go · 2026-12-11 到期」。这与余额读的是同一个响应,不额外发请求。
|
|
402
|
+
- **免费用户看到的完全不变**:没有套餐的账户,套餐字段根本不存在,插件也就不显示任何套餐信息;窗口依然读不到,卡片上那句「未订阅 M Plan(仅积分包),因此没有限额窗口」照旧。新增 `tests/free-account.spec.ts` 把免费账户的整条路径钉住(余额、套餐、窗口三样),另加 `tests/plan.spec.ts` 覆盖新字段形态、旧字段形态与免费账户三种情况。
|
|
403
|
+
|
|
398
404
|
### 0.6.4(2026-11-11)
|
|
399
405
|
|
|
400
406
|
- **修复「自检报告从未出现」**:上一版把自检能力加在了客户端(卡片上报宿主提供了什么),但**服务端那一半没有落地** —— 状态文档里没有承载报告的字段,也没有接收报告的路由,于是卡片上报到哪里都无人接收。现已补齐:报告由插件自己的路由接收,并出现在状态文档的 `capability` 里。
|
package/lib/client.js
CHANGED
|
@@ -77,6 +77,8 @@
|
|
|
77
77
|
usageRanking: '用量排名前 {percent}%',
|
|
78
78
|
usageBestDay: '最活跃的一天 {date}',
|
|
79
79
|
planNoSubscription: '未订阅 M Plan(仅积分包),因此没有限额窗口',
|
|
80
|
+
planGeneric: 'M Plan 订阅',
|
|
81
|
+
planExpires: '{date} 到期',
|
|
80
82
|
generateTitle: '生成',
|
|
81
83
|
generateImage: '图片',
|
|
82
84
|
generateVideo: '视频',
|
|
@@ -155,6 +157,8 @@
|
|
|
155
157
|
usageRanking: 'Top {percent}% by usage',
|
|
156
158
|
usageBestDay: 'Busiest day {date}',
|
|
157
159
|
planNoSubscription: 'No M Plan subscription (credits only), so there are no usage windows',
|
|
160
|
+
planGeneric: 'M Plan subscription',
|
|
161
|
+
planExpires: 'until {date}',
|
|
158
162
|
generateTitle: 'Generate',
|
|
159
163
|
generateImage: 'Image',
|
|
160
164
|
generateVideo: 'Video',
|
|
@@ -383,6 +387,23 @@
|
|
|
383
387
|
return null;
|
|
384
388
|
return h('div', { style: mutedStyle, 'data-minimax-plan-notice': true }, t('planNoSubscription'));
|
|
385
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* The plan the account holds, when it holds one.
|
|
392
|
+
*
|
|
393
|
+
* An account without a plan has nothing to say here, and the card shows
|
|
394
|
+
* it nothing: the rolling windows it does not have are already
|
|
395
|
+
* explained by the notice beside them. What this adds is for the
|
|
396
|
+
* accounts that do hold one — the tier's name and the day it ends.
|
|
397
|
+
*/
|
|
398
|
+
function planLine(t, plan) {
|
|
399
|
+
if (plan === undefined || !plan.active)
|
|
400
|
+
return null;
|
|
401
|
+
const tier = plan.tier ?? t('planGeneric');
|
|
402
|
+
const expiry = plan.expiresAtMs === undefined
|
|
403
|
+
? null
|
|
404
|
+
: t('planExpires', { date: day(plan.expiresAtMs) });
|
|
405
|
+
return h('div', { style: rowStyle, 'data-minimax-plan': true }, h('span', null, tier), h('span', null, expiry ?? ''));
|
|
406
|
+
}
|
|
386
407
|
/** The pill itself; the gate decides whether it is drawn at all. */
|
|
387
408
|
function CreditPill(props) {
|
|
388
409
|
const [status] = useStatus();
|
|
@@ -415,7 +436,7 @@
|
|
|
415
436
|
const detail = available
|
|
416
437
|
? h('div', { style: detailStyle, 'data-minimax-credits-detail': true }, row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), credits_.consumed !== undefined && credits_.consumed > 0
|
|
417
438
|
? row(t('consumed'), credits(credits_.consumed, 3))
|
|
418
|
-
: null, expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), usageProfileBlock(t, status?.usageProfile), usage !== undefined
|
|
439
|
+
: null, expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), planLine(t, status?.plan), usageProfileBlock(t, status?.usageProfile), usage !== undefined
|
|
419
440
|
? h('div', { style: rowStyle }, h('span', null, t('tokens', { count: tokens(usage.inputTokens + usage.outputTokens + usage.cacheReadTokens) })), h('span', null, t('cost', { amount: money(usage.estimatedCny) })))
|
|
420
441
|
: null, credits_.updatedAtMs !== undefined
|
|
421
442
|
? row(t('updated', { time: new Date(credits_.updatedAtMs).toLocaleTimeString() }), '')
|
|
@@ -913,7 +934,7 @@
|
|
|
913
934
|
// usable even when the balance cannot be read.
|
|
914
935
|
h(GenerationPanel, props));
|
|
915
936
|
}
|
|
916
|
-
return h('div', { style: pageStyle, 'data-minimax-page': true }, h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), signinBlock(t, signin, refresh), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
|
|
937
|
+
return h('div', { style: pageStyle, 'data-minimax-page': true }, h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), planLine(t, status?.plan), signinBlock(t, signin, refresh), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
|
|
917
938
|
? h('div', { style: mutedStyle }, t('noLots'))
|
|
918
939
|
: h('ul', { style: listStyle }, ...(credits_.upcoming ?? []).map((lot, index) => h('li', { key: `${lot.expireAtMs}-${index}`, style: rowStyle }, h('span', null, day(lot.expireAtMs)), h('span', null, credits(lot.amount, 2))))), usage !== undefined
|
|
919
940
|
? h('div', { style: usageBoxStyle }, row(t('tokens', { count: tokens(usage.inputTokens + usage.outputTokens + usage.cacheReadTokens) }), ''), row(t('cost', { amount: money(usage.estimatedCny) }), ''), usage.requests > 0 ? row(t('requests', { count: usage.requests }), usage.lastModel ?? '') : null)
|
package/lib/index.d.ts
CHANGED
|
@@ -320,6 +320,32 @@ interface CreditSummary {
|
|
|
320
320
|
/** Remaining free (granted) credits. */
|
|
321
321
|
free: number;
|
|
322
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* The plan the account subscribes to.
|
|
325
|
+
*
|
|
326
|
+
* An account without one still reads a balance — it simply has no rolling
|
|
327
|
+
* windows, which is what the limit readout used to report as missing. With a
|
|
328
|
+
* plan the upstream names the tier and the day it ends, and both are worth
|
|
329
|
+
* showing: the tier says what the account has, the day says how long it has.
|
|
330
|
+
*/
|
|
331
|
+
interface PlanSummary {
|
|
332
|
+
/** The tier's name, as the upstream spells it ("Go"). */
|
|
333
|
+
tier?: string;
|
|
334
|
+
/** When the plan ends, in milliseconds since the epoch. */
|
|
335
|
+
expiresAtMs?: number;
|
|
336
|
+
/** Whether the upstream reports an active plan at all. */
|
|
337
|
+
active: boolean;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Read the plan out of a membership response.
|
|
341
|
+
*
|
|
342
|
+
* The upstream only fills these in for an account that subscribes: without a
|
|
343
|
+
* plan `has_token_plan` is false and the tier is absent. `subscription_type`
|
|
344
|
+
* says so too — "mplan" once a plan is held, "none" before.
|
|
345
|
+
*
|
|
346
|
+
* @returns The plan, or `undefined` when the response carries no plan fields.
|
|
347
|
+
*/
|
|
348
|
+
declare function parsePlanSummary(document: unknown): PlanSummary | undefined;
|
|
323
349
|
/**
|
|
324
350
|
* Read the credit summary out of a membership response.
|
|
325
351
|
*
|
|
@@ -327,6 +353,14 @@ interface CreditSummary {
|
|
|
327
353
|
* balance fields, so the same fallback chain is used here.
|
|
328
354
|
*/
|
|
329
355
|
declare function parseCreditSummary(document: unknown): CreditSummary | undefined;
|
|
356
|
+
/**
|
|
357
|
+
* Fetch the membership document.
|
|
358
|
+
*
|
|
359
|
+
* One answer carries several things: the credit balance, the plan the
|
|
360
|
+
* account subscribes to, and when that plan ends. They are read together so
|
|
361
|
+
* that the account is asked once.
|
|
362
|
+
*/
|
|
363
|
+
declare function fetchMembershipDocument(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch): Promise<Record<string, unknown> | undefined>;
|
|
330
364
|
/**
|
|
331
365
|
* Fetch the current credit balance.
|
|
332
366
|
*
|
|
@@ -387,19 +421,24 @@ interface RateLimitWindows {
|
|
|
387
421
|
weekly: UsageWindow;
|
|
388
422
|
}
|
|
389
423
|
/**
|
|
390
|
-
* Read the two rate-limit windows out of a remains
|
|
424
|
+
* Read the two rate-limit windows out of a remains response.
|
|
391
425
|
*
|
|
392
426
|
* The upstream answers with one entry per metered group; the general one is
|
|
393
427
|
* the only one the plan meters today, and the first entry is used when it is
|
|
394
|
-
* absent. The
|
|
395
|
-
*
|
|
396
|
-
* edges are read. The window length is derived from the entry's own endpoints
|
|
397
|
-
* rather than assumed to be five hours or seven days — the plan's terms have
|
|
428
|
+
* absent. The window length is derived from the entry's own endpoints rather
|
|
429
|
+
* than assumed to be five hours or seven days — the plan's terms have
|
|
398
430
|
* changed before, and the display should follow whatever the server says.
|
|
399
431
|
*
|
|
400
432
|
* A window the server marks as unlimited (`current_*_status` of 3) carries no
|
|
401
433
|
* allowance to be a percentage of, so it produces no window at all: reporting
|
|
402
434
|
* "0%" of an unlimited quota would be a number nobody can act on.
|
|
435
|
+
*
|
|
436
|
+
* **Which share the upstream reports changed.** It used to answer with the
|
|
437
|
+
* share already used, as a string ("19%"); with a plan subscribed it answers
|
|
438
|
+
* with the share still *remaining*, as a plain number (90). Both are
|
|
439
|
+
* accepted, and the two are opposites, so what leaves this function is
|
|
440
|
+
* always the share used — otherwise the same window would read 90% used on
|
|
441
|
+
* a plan that has barely spent anything.
|
|
403
442
|
*/
|
|
404
443
|
declare function parseRateLimitWindows(document: unknown): RateLimitWindows | undefined;
|
|
405
444
|
/**
|
|
@@ -493,6 +532,8 @@ declare class MinimaxCredits {
|
|
|
493
532
|
get rateLimit(): RateLimitWindows | undefined;
|
|
494
533
|
/** Why the windows could not be read, when they could not. */
|
|
495
534
|
get rateLimitError(): string | undefined;
|
|
535
|
+
/** The plan the account subscribes to, as the last read found it. */
|
|
536
|
+
get plan(): PlanSummary | undefined;
|
|
496
537
|
/** Why the plan has no windows, when the upstream said so plainly. */
|
|
497
538
|
get rateLimitAbsence(): 'no-subscription' | undefined;
|
|
498
539
|
/** When the windows were last read. */
|
|
@@ -985,6 +1026,15 @@ interface UsageStatus {
|
|
|
985
1026
|
/** How many requests were measured. */
|
|
986
1027
|
requests: number;
|
|
987
1028
|
}
|
|
1029
|
+
/** The plan the account subscribes to, as the card reads it. */
|
|
1030
|
+
interface PlanStatus {
|
|
1031
|
+
/** Whether the account holds a plan. */
|
|
1032
|
+
active: boolean;
|
|
1033
|
+
/** The tier's name, as the upstream spells it ("Go"). */
|
|
1034
|
+
tier?: string;
|
|
1035
|
+
/** When the plan ends, in milliseconds since the epoch. */
|
|
1036
|
+
expiresAtMs?: number;
|
|
1037
|
+
}
|
|
988
1038
|
/** The plan's two rolling usage windows, as the card reads them. */
|
|
989
1039
|
interface RateLimitStatus {
|
|
990
1040
|
/** Whether the windows could be read. */
|
|
@@ -1085,6 +1135,8 @@ interface StatusDocument {
|
|
|
1085
1135
|
login?: LoginStatus;
|
|
1086
1136
|
/** The account's usage profile, when it could be read. */
|
|
1087
1137
|
usageProfile?: UsageProfileStatus;
|
|
1138
|
+
/** The plan the account subscribes to, when there is one. */
|
|
1139
|
+
plan?: PlanStatus;
|
|
1088
1140
|
/** The choices the generation endpoints offer. */
|
|
1089
1141
|
generation?: GenerationOptions;
|
|
1090
1142
|
/** What the host offered the card the last time it looked. */
|
|
@@ -1124,6 +1176,8 @@ interface StatusDeps {
|
|
|
1124
1176
|
login?: LoginStatus;
|
|
1125
1177
|
/** The account's usage profile, when the account API could be reached. */
|
|
1126
1178
|
usageProfile?: UsageProfileStatus;
|
|
1179
|
+
/** The plan the account subscribes to, when there is one. */
|
|
1180
|
+
plan?: PlanStatus;
|
|
1127
1181
|
/** The choices the generation endpoints offer. */
|
|
1128
1182
|
generation?: GenerationOptions;
|
|
1129
1183
|
/** What the host offered the card the last time it looked. */
|
|
@@ -1215,4 +1269,4 @@ declare const inject: string[];
|
|
|
1215
1269
|
*/
|
|
1216
1270
|
declare function apply(ctx: Context): void;
|
|
1217
1271
|
//#endregion
|
|
1218
|
-
export { type AuthState, type CatalogModel, type CatalogSource, type Credential, type CreditSummary, DEVICE_CODE_GRANT, DEVICE_CODE_PATH, type DeviceLoginState, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_ACTION_PATH, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, type MinimaxRegion, type ModelUsage, type OAuthCredential, type OAuthState, PLUGIN_CREDENTIAL_FILENAME, PLUGIN_STATE_FILENAME, type PluginCredential, type PluginState, REMAINS_PERCENT_PATH, type RateLimitWindows, SIGNIN_CLAIM_PATH, SIGNIN_CLAIM_RESULT, SIGNIN_CYCLE_DAYS, SIGNIN_DAY_STATUS, SIGNIN_PANEL_PATH, type SigninClaim, type SigninDay, type SigninPanel, type StatusDocument, type TokenClaims, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, claimSignin, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, deletePluginCredential, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchRateLimitWindows, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parseRateLimitWindows, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };
|
|
1272
|
+
export { type AuthState, type CatalogModel, type CatalogSource, type Credential, type CreditSummary, DEVICE_CODE_GRANT, DEVICE_CODE_PATH, type DeviceLoginState, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_ACTION_PATH, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, type MinimaxRegion, type ModelUsage, type OAuthCredential, type OAuthState, PLUGIN_CREDENTIAL_FILENAME, PLUGIN_STATE_FILENAME, type PlanSummary, type PluginCredential, type PluginState, REMAINS_PERCENT_PATH, type RateLimitWindows, SIGNIN_CLAIM_PATH, SIGNIN_CLAIM_RESULT, SIGNIN_CYCLE_DAYS, SIGNIN_DAY_STATUS, SIGNIN_PANEL_PATH, type SigninClaim, type SigninDay, type SigninPanel, type StatusDocument, type TokenClaims, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, claimSignin, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, deletePluginCredential, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchMembershipDocument, fetchRateLimitWindows, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parsePlanSummary, parseRateLimitWindows, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };
|
package/lib/index.js
CHANGED
|
@@ -786,6 +786,7 @@ function statusDocument(deps) {
|
|
|
786
786
|
...deps.signin === void 0 ? {} : { signin: deps.signin },
|
|
787
787
|
...deps.login === void 0 ? {} : { login: deps.login },
|
|
788
788
|
...deps.usageProfile === void 0 ? {} : { usageProfile: deps.usageProfile },
|
|
789
|
+
...deps.plan === void 0 ? {} : { plan: deps.plan },
|
|
789
790
|
...deps.capability === void 0 ? {} : { capability: deps.capability },
|
|
790
791
|
...deps.generation === void 0 ? {} : { generation: deps.generation }
|
|
791
792
|
};
|
|
@@ -1374,6 +1375,29 @@ async function ensureOAuthCredential(region, env = process.env, now = Date.now()
|
|
|
1374
1375
|
}
|
|
1375
1376
|
}
|
|
1376
1377
|
/**
|
|
1378
|
+
* Read the plan out of a membership response.
|
|
1379
|
+
*
|
|
1380
|
+
* The upstream only fills these in for an account that subscribes: without a
|
|
1381
|
+
* plan `has_token_plan` is false and the tier is absent. `subscription_type`
|
|
1382
|
+
* says so too — "mplan" once a plan is held, "none" before.
|
|
1383
|
+
*
|
|
1384
|
+
* @returns The plan, or `undefined` when the response carries no plan fields.
|
|
1385
|
+
*/
|
|
1386
|
+
function parsePlanSummary(document) {
|
|
1387
|
+
if (typeof document !== "object" || document === null) return void 0;
|
|
1388
|
+
const record = document;
|
|
1389
|
+
const hasTokenPlan = record.has_token_plan;
|
|
1390
|
+
const tier = typeof record.token_plan_tier === "string" && record.token_plan_tier.length > 0 ? record.token_plan_tier : void 0;
|
|
1391
|
+
const expires = toNumber$1(record.token_plan_expires_at);
|
|
1392
|
+
const expiresAtMs = expires === void 0 || expires <= 0 ? void 0 : expires;
|
|
1393
|
+
if (hasTokenPlan !== true && tier === void 0 && expiresAtMs === void 0) return void 0;
|
|
1394
|
+
return {
|
|
1395
|
+
tier,
|
|
1396
|
+
expiresAtMs,
|
|
1397
|
+
active: hasTokenPlan === true || tier !== void 0
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1377
1401
|
* Read the credit summary out of a membership response.
|
|
1378
1402
|
*
|
|
1379
1403
|
* The app's settings page prefers the summary and falls back to the flat
|
|
@@ -1411,11 +1435,13 @@ function toNumber$1(value) {
|
|
|
1411
1435
|
}
|
|
1412
1436
|
}
|
|
1413
1437
|
/**
|
|
1414
|
-
* Fetch the
|
|
1438
|
+
* Fetch the membership document.
|
|
1415
1439
|
*
|
|
1416
|
-
*
|
|
1440
|
+
* One answer carries several things: the credit balance, the plan the
|
|
1441
|
+
* account subscribes to, and when that plan ends. They are read together so
|
|
1442
|
+
* that the account is asked once.
|
|
1417
1443
|
*/
|
|
1418
|
-
async function
|
|
1444
|
+
async function fetchMembershipDocument(credential, region, fetchImpl = fetch) {
|
|
1419
1445
|
const response = await fetchImpl(`${MINIMAX_ACCOUNT_HOSTS[region]}${MEMBERSHIP_PATH}`, {
|
|
1420
1446
|
method: "POST",
|
|
1421
1447
|
headers: {
|
|
@@ -1425,7 +1451,17 @@ async function fetchCreditSummary(credential, region, fetchImpl = fetch) {
|
|
|
1425
1451
|
body: "{}"
|
|
1426
1452
|
});
|
|
1427
1453
|
if (!response.ok) throw new Error(`membership request failed: HTTP ${response.status}`);
|
|
1428
|
-
|
|
1454
|
+
const document = await response.json();
|
|
1455
|
+
return typeof document === "object" && document !== null ? document : void 0;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Fetch the current credit balance.
|
|
1459
|
+
*
|
|
1460
|
+
* @returns The balance, or `undefined` when the account has no credit summary.
|
|
1461
|
+
*/
|
|
1462
|
+
async function fetchCreditSummary(credential, region, fetchImpl = fetch) {
|
|
1463
|
+
const document = await fetchMembershipDocument(credential, region, fetchImpl);
|
|
1464
|
+
return document === void 0 ? void 0 : parseCreditSummary(document);
|
|
1429
1465
|
}
|
|
1430
1466
|
/**
|
|
1431
1467
|
* Read the credit lots out of a ledger response.
|
|
@@ -1487,19 +1523,24 @@ async function fetchCreditLots(credential, region, fetchImpl = fetch) {
|
|
|
1487
1523
|
return lots;
|
|
1488
1524
|
}
|
|
1489
1525
|
/**
|
|
1490
|
-
* Read the two rate-limit windows out of a remains
|
|
1526
|
+
* Read the two rate-limit windows out of a remains response.
|
|
1491
1527
|
*
|
|
1492
1528
|
* The upstream answers with one entry per metered group; the general one is
|
|
1493
1529
|
* the only one the plan meters today, and the first entry is used when it is
|
|
1494
|
-
* absent. The
|
|
1495
|
-
*
|
|
1496
|
-
* edges are read. The window length is derived from the entry's own endpoints
|
|
1497
|
-
* rather than assumed to be five hours or seven days — the plan's terms have
|
|
1530
|
+
* absent. The window length is derived from the entry's own endpoints rather
|
|
1531
|
+
* than assumed to be five hours or seven days — the plan's terms have
|
|
1498
1532
|
* changed before, and the display should follow whatever the server says.
|
|
1499
1533
|
*
|
|
1500
1534
|
* A window the server marks as unlimited (`current_*_status` of 3) carries no
|
|
1501
1535
|
* allowance to be a percentage of, so it produces no window at all: reporting
|
|
1502
1536
|
* "0%" of an unlimited quota would be a number nobody can act on.
|
|
1537
|
+
*
|
|
1538
|
+
* **Which share the upstream reports changed.** It used to answer with the
|
|
1539
|
+
* share already used, as a string ("19%"); with a plan subscribed it answers
|
|
1540
|
+
* with the share still *remaining*, as a plain number (90). Both are
|
|
1541
|
+
* accepted, and the two are opposites, so what leaves this function is
|
|
1542
|
+
* always the share used — otherwise the same window would read 90% used on
|
|
1543
|
+
* a plan that has barely spent anything.
|
|
1503
1544
|
*/
|
|
1504
1545
|
function parseRateLimitWindows(document) {
|
|
1505
1546
|
if (typeof document !== "object" || document === null) return void 0;
|
|
@@ -1508,8 +1549,8 @@ function parseRateLimitWindows(document) {
|
|
|
1508
1549
|
const entry = remains.find((item) => typeof item === "object" && item !== null && item.model_name === "general") ?? remains[0];
|
|
1509
1550
|
if (typeof entry !== "object" || entry === null) return void 0;
|
|
1510
1551
|
const record = entry;
|
|
1511
|
-
const interval = readWindow(record.current_interval_used_percent, record.start_time, record.end_time, record.current_interval_status);
|
|
1512
|
-
const weekly = readWindow(record.current_weekly_used_percent, record.weekly_start_time, record.weekly_end_time, record.current_weekly_status);
|
|
1552
|
+
const interval = readWindow(record.current_interval_used_percent, record.current_interval_remaining_percent, record.start_time, record.end_time, record.current_interval_status);
|
|
1553
|
+
const weekly = readWindow(record.current_weekly_used_percent, record.current_weekly_remaining_percent, record.weekly_start_time, record.weekly_end_time, record.current_weekly_status);
|
|
1513
1554
|
if (interval === void 0 || weekly === void 0) return void 0;
|
|
1514
1555
|
return {
|
|
1515
1556
|
interval,
|
|
@@ -1522,10 +1563,25 @@ function parseRateLimitWindows(document) {
|
|
|
1522
1563
|
* The desktop app reads it the same way: `isUnlimited: 3 === status`.
|
|
1523
1564
|
*/
|
|
1524
1565
|
const UNLIMITED_STATUS = 3;
|
|
1566
|
+
/**
|
|
1567
|
+
* The share of a window's allowance already used, whichever way it is reported.
|
|
1568
|
+
*
|
|
1569
|
+
* The endpoint named the used share `current_*_used_percent` and then
|
|
1570
|
+
* renamed it to `current_*_remaining_percent` while inverting its meaning.
|
|
1571
|
+
* The used share is what a person needs, so it is what this returns; the
|
|
1572
|
+
* remaining share is subtracted from a hundred to get there.
|
|
1573
|
+
*/
|
|
1574
|
+
function readUsedPercent(used, remaining) {
|
|
1575
|
+
const usedPercent = parsePercent(used);
|
|
1576
|
+
if (usedPercent !== void 0) return usedPercent;
|
|
1577
|
+
const remainingPercent = parsePercent(remaining);
|
|
1578
|
+
if (remainingPercent === void 0) return void 0;
|
|
1579
|
+
return 100 - remainingPercent;
|
|
1580
|
+
}
|
|
1525
1581
|
/** Assemble one window from its percent and edge fields; all must be present. */
|
|
1526
|
-
function readWindow(percent, startAtMs, resetAtMs, status) {
|
|
1582
|
+
function readWindow(percent, remainingPercent, startAtMs, resetAtMs, status) {
|
|
1527
1583
|
if (status === UNLIMITED_STATUS) return void 0;
|
|
1528
|
-
const usedPercent =
|
|
1584
|
+
const usedPercent = readUsedPercent(percent, remainingPercent);
|
|
1529
1585
|
const start = toNumber$1(startAtMs);
|
|
1530
1586
|
const reset = toNumber$1(resetAtMs);
|
|
1531
1587
|
if (usedPercent === void 0 || start === void 0 || reset === void 0) return void 0;
|
|
@@ -1718,6 +1774,7 @@ var MinimaxCredits = class {
|
|
|
1718
1774
|
#ledgerError;
|
|
1719
1775
|
#rateLimit;
|
|
1720
1776
|
#rateLimitError;
|
|
1777
|
+
#plan;
|
|
1721
1778
|
#rateLimitAbsence;
|
|
1722
1779
|
#rateLimitAtMs;
|
|
1723
1780
|
#usageProfile;
|
|
@@ -1755,6 +1812,10 @@ var MinimaxCredits = class {
|
|
|
1755
1812
|
get rateLimitError() {
|
|
1756
1813
|
return this.#rateLimitError;
|
|
1757
1814
|
}
|
|
1815
|
+
/** The plan the account subscribes to, as the last read found it. */
|
|
1816
|
+
get plan() {
|
|
1817
|
+
return this.#plan;
|
|
1818
|
+
}
|
|
1758
1819
|
/** Why the plan has no windows, when the upstream said so plainly. */
|
|
1759
1820
|
get rateLimitAbsence() {
|
|
1760
1821
|
return this.#rateLimitAbsence;
|
|
@@ -1785,7 +1846,9 @@ var MinimaxCredits = class {
|
|
|
1785
1846
|
async refresh(credential, region, fetchImpl = fetch, now = Date.now()) {
|
|
1786
1847
|
let changed = false;
|
|
1787
1848
|
try {
|
|
1788
|
-
const
|
|
1849
|
+
const document = await fetchMembershipDocument(credential, region, fetchImpl);
|
|
1850
|
+
if (document !== void 0) this.#plan = parsePlanSummary(document);
|
|
1851
|
+
const summary = document === void 0 ? void 0 : parseCreditSummary(document);
|
|
1789
1852
|
if (summary === void 0) this.#error = "membership response carried no credit summary";
|
|
1790
1853
|
else {
|
|
1791
1854
|
const previous = this.#snapshot;
|
|
@@ -2956,6 +3019,20 @@ function apply(ctx) {
|
|
|
2956
3019
|
updatedAt: profile.lastUpdateTime
|
|
2957
3020
|
};
|
|
2958
3021
|
};
|
|
3022
|
+
/**
|
|
3023
|
+
* The plan the account holds, as the card reads it.
|
|
3024
|
+
*
|
|
3025
|
+
* It comes from the same membership answer as the balance, so it is free.
|
|
3026
|
+
*/
|
|
3027
|
+
const buildPlanStatus = () => {
|
|
3028
|
+
const held = credits.plan;
|
|
3029
|
+
if (held === void 0) return void 0;
|
|
3030
|
+
return {
|
|
3031
|
+
active: held.active,
|
|
3032
|
+
tier: held.tier,
|
|
3033
|
+
expiresAtMs: held.expiresAtMs
|
|
3034
|
+
};
|
|
3035
|
+
};
|
|
2959
3036
|
/** The plan's rolling usage windows as the card reads them. */
|
|
2960
3037
|
const buildRateLimitStatus = () => {
|
|
2961
3038
|
if (creditState === "signed-out" || creditState === "expired") return { state: creditState };
|
|
@@ -3105,6 +3182,7 @@ function apply(ctx) {
|
|
|
3105
3182
|
signin: buildSigninStatus(),
|
|
3106
3183
|
login: buildLoginStatus(),
|
|
3107
3184
|
usageProfile: buildUsageProfileStatus(),
|
|
3185
|
+
plan: buildPlanStatus(),
|
|
3108
3186
|
capability,
|
|
3109
3187
|
generation: {
|
|
3110
3188
|
imageModels: [...IMAGE_MODELS],
|
|
@@ -3514,4 +3592,4 @@ async function readRequestBody(req) {
|
|
|
3514
3592
|
return text;
|
|
3515
3593
|
}
|
|
3516
3594
|
//#endregion
|
|
3517
|
-
export { DEVICE_CODE_GRANT, DEVICE_CODE_PATH, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_ACTION_PATH, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, PLUGIN_CREDENTIAL_FILENAME, PLUGIN_STATE_FILENAME, REMAINS_PERCENT_PATH, SIGNIN_CLAIM_PATH, SIGNIN_CLAIM_RESULT, SIGNIN_CYCLE_DAYS, SIGNIN_DAY_STATUS, SIGNIN_PANEL_PATH, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, claimSignin, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, deletePluginCredential, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchRateLimitWindows, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parseRateLimitWindows, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };
|
|
3595
|
+
export { DEVICE_CODE_GRANT, DEVICE_CODE_PATH, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_ACTION_PATH, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, PLUGIN_CREDENTIAL_FILENAME, PLUGIN_STATE_FILENAME, REMAINS_PERCENT_PATH, SIGNIN_CLAIM_PATH, SIGNIN_CLAIM_RESULT, SIGNIN_CYCLE_DAYS, SIGNIN_DAY_STATUS, SIGNIN_PANEL_PATH, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, claimSignin, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, deletePluginCredential, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchMembershipDocument, fetchRateLimitWindows, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parsePlanSummary, parseRateLimitWindows, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-minimaxcode",
|
|
3
3
|
"displayName": "DSH Connect MiniMax Code",
|
|
4
4
|
"description": "把本机已登录的 MiniMax Code 桌面 App 模型接入 DeepSeek Harness:零配置、Anthropic Messages 兼容、动态模型目录,并显示积分余额与到期 — bring MiniMax Code desktop app models into DSH with a dynamic model catalog and a credit balance with expiry details.",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.5",
|
|
6
6
|
"author": "JeffTsuiCUG",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|