dsh-connect-minimaxcode 0.5.4 → 0.5.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/README.en.md +497 -478
- package/README.md +47 -0
- package/lib/client.js +69 -3
- package/lib/index.d.ts +88 -3
- package/lib/index.js +288 -19
- package/package.json +7 -3
package/lib/index.js
CHANGED
|
@@ -693,7 +693,9 @@ const HINTS = {
|
|
|
693
693
|
"app-missing": "未找到 MiniMax Code 的登录信息。请先安装并登录 MiniMax Code 桌面 App,然后重启 DSH。",
|
|
694
694
|
"not-signed-in": "MiniMax Code 尚未登录。请在 MiniMax Code 桌面 App 中登录后重启 DSH。",
|
|
695
695
|
unreadable: "登录信息文件无法读取。请确认 MiniMax Code 仍处于登录状态。",
|
|
696
|
-
malformed: "登录令牌格式无法解析。请在 MiniMax Code 中重新登录后重启 DSH。"
|
|
696
|
+
malformed: "登录令牌格式无法解析。请在 MiniMax Code 中重新登录后重启 DSH。",
|
|
697
|
+
"credential-revoked": "登录凭据已被服务器撤销,模型请求无法通过。请点击下方登录按钮重新登录 MiniMax Code。",
|
|
698
|
+
"credential-expired": "登录凭据已过期。请点击下方登录按钮重新登录 MiniMax Code。"
|
|
697
699
|
};
|
|
698
700
|
/**
|
|
699
701
|
* Build the card's status document.
|
|
@@ -712,6 +714,7 @@ function statusDocument(deps) {
|
|
|
712
714
|
models: deps.models,
|
|
713
715
|
refreshing: deps.refreshing,
|
|
714
716
|
...deps.credits === void 0 ? {} : { credits: deps.credits },
|
|
717
|
+
...deps.rateLimit === void 0 ? {} : { rateLimit: deps.rateLimit },
|
|
715
718
|
...deps.usage === void 0 ? {} : { usage: deps.usage },
|
|
716
719
|
...deps.signin === void 0 ? {} : { signin: deps.signin },
|
|
717
720
|
...deps.login === void 0 ? {} : { login: deps.login }
|
|
@@ -720,7 +723,7 @@ function statusDocument(deps) {
|
|
|
720
723
|
...base,
|
|
721
724
|
expiry: "expired",
|
|
722
725
|
daysRemaining: 0,
|
|
723
|
-
hint: "登录令牌已过期。请打开并登录 MiniMax Code 桌面 App 以获取新令牌,然后重启 DSH。"
|
|
726
|
+
hint: HINTS[auth.reason ?? ""] ?? "登录令牌已过期。请打开并登录 MiniMax Code 桌面 App 以获取新令牌,然后重启 DSH。"
|
|
724
727
|
};
|
|
725
728
|
if (auth.state === "signed-out") return {
|
|
726
729
|
...base,
|
|
@@ -780,10 +783,18 @@ const MINIMAX_ACCOUNT_HOSTS = {
|
|
|
780
783
|
io: "https://agent.minimax.io",
|
|
781
784
|
com: "https://agent.minimaxi.com"
|
|
782
785
|
};
|
|
786
|
+
/** Open-platform account API, which serves the per-model usage summary. */
|
|
787
|
+
const MINIMAX_OPEN_HOSTS = {
|
|
788
|
+
cn: "https://www.minimaxi.com",
|
|
789
|
+
io: "https://www.minimaxi.com",
|
|
790
|
+
com: "https://www.minimaxi.com"
|
|
791
|
+
};
|
|
783
792
|
/** Membership endpoint: carries the credit summary. */
|
|
784
793
|
const MEMBERSHIP_PATH = "/matrix/api/v1/commerce/get_membership_info";
|
|
785
794
|
/** Credit ledger endpoint: per-grant amounts and expiry. */
|
|
786
795
|
const CREDIT_DETAILS_PATH = "/minimax-cloud/api/v1/credit/details";
|
|
796
|
+
/** Rate-limit window endpoint: the 5-hour and weekly usage the plan meters. */
|
|
797
|
+
const REMAINS_PERCENT_PATH = "/backend/account/token_plan/remains_percent";
|
|
787
798
|
/** OAuth token endpoint. */
|
|
788
799
|
const TOKEN_PATH$1 = "/oauth2/token";
|
|
789
800
|
/** Absolute path of the app's credential file for a region. */
|
|
@@ -1088,6 +1099,84 @@ async function fetchCreditLots(credential, region, fetchImpl = fetch) {
|
|
|
1088
1099
|
return lots;
|
|
1089
1100
|
}
|
|
1090
1101
|
/**
|
|
1102
|
+
* Read the two rate-limit windows out of a remains-percent response.
|
|
1103
|
+
*
|
|
1104
|
+
* The upstream answers with one entry per metered group; the general one is
|
|
1105
|
+
* the only one the plan meters today, and the first entry is used when it is
|
|
1106
|
+
* absent. The percents arrive as strings ("19%"), and the counts arrive as -1
|
|
1107
|
+
* when the plan does not meter by count, so only the percents and the window
|
|
1108
|
+
* edges are read. The window length is derived from the entry's own endpoints
|
|
1109
|
+
* rather than assumed to be five hours or seven days — the plan's terms have
|
|
1110
|
+
* changed before, and the display should follow whatever the server says.
|
|
1111
|
+
*
|
|
1112
|
+
* A window the server marks as unlimited (`current_*_status` of 3) carries no
|
|
1113
|
+
* allowance to be a percentage of, so it produces no window at all: reporting
|
|
1114
|
+
* "0%" of an unlimited quota would be a number nobody can act on.
|
|
1115
|
+
*/
|
|
1116
|
+
function parseRateLimitWindows(document) {
|
|
1117
|
+
if (typeof document !== "object" || document === null) return void 0;
|
|
1118
|
+
const remains = document.model_remains;
|
|
1119
|
+
if (!Array.isArray(remains) || remains.length === 0) return void 0;
|
|
1120
|
+
const entry = remains.find((item) => typeof item === "object" && item !== null && item.model_name === "general") ?? remains[0];
|
|
1121
|
+
if (typeof entry !== "object" || entry === null) return void 0;
|
|
1122
|
+
const record = entry;
|
|
1123
|
+
const interval = readWindow(record.current_interval_used_percent, record.start_time, record.end_time, record.current_interval_status);
|
|
1124
|
+
const weekly = readWindow(record.current_weekly_used_percent, record.weekly_start_time, record.weekly_end_time, record.current_weekly_status);
|
|
1125
|
+
if (interval === void 0 || weekly === void 0) return void 0;
|
|
1126
|
+
return {
|
|
1127
|
+
interval,
|
|
1128
|
+
weekly
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* The status the upstream gives a window that is not limited at all.
|
|
1133
|
+
*
|
|
1134
|
+
* The desktop app reads it the same way: `isUnlimited: 3 === status`.
|
|
1135
|
+
*/
|
|
1136
|
+
const UNLIMITED_STATUS = 3;
|
|
1137
|
+
/** Assemble one window from its percent and edge fields; all must be present. */
|
|
1138
|
+
function readWindow(percent, startAtMs, resetAtMs, status) {
|
|
1139
|
+
if (status === UNLIMITED_STATUS) return void 0;
|
|
1140
|
+
const usedPercent = parsePercent(percent);
|
|
1141
|
+
const start = toNumber$1(startAtMs);
|
|
1142
|
+
const reset = toNumber$1(resetAtMs);
|
|
1143
|
+
if (usedPercent === void 0 || start === void 0 || reset === void 0) return void 0;
|
|
1144
|
+
return {
|
|
1145
|
+
usedPercent,
|
|
1146
|
+
startAtMs: start,
|
|
1147
|
+
resetAtMs: reset
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Read "19%" as 19, and a bare 19 as 19 as well.
|
|
1152
|
+
*
|
|
1153
|
+
* The desktop app parses both shapes, so both are accepted here: anything
|
|
1154
|
+
* unreadable stays unreadable rather than becoming a zero.
|
|
1155
|
+
*/
|
|
1156
|
+
function parsePercent(value) {
|
|
1157
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
1158
|
+
if (typeof value !== "string") return void 0;
|
|
1159
|
+
const parsed = Number(value.replace(/%\s*$/, ""));
|
|
1160
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Fetch the plan's two rate-limit windows.
|
|
1164
|
+
*
|
|
1165
|
+
* The endpoint lives on the open-platform host, like the usage summary, and
|
|
1166
|
+
* answers the same OAuth credential the membership endpoint takes. The JWT the
|
|
1167
|
+
* model gateway accepts does not work here: this is an account endpoint.
|
|
1168
|
+
*
|
|
1169
|
+
* @returns The windows, or `undefined` when the response carries none.
|
|
1170
|
+
*/
|
|
1171
|
+
async function fetchRateLimitWindows(credential, region, fetchImpl = fetch) {
|
|
1172
|
+
const response = await fetchImpl(`${MINIMAX_OPEN_HOSTS[region]}${REMAINS_PERCENT_PATH}?business=Agent&device_platform=desktop`, { headers: {
|
|
1173
|
+
accept: "application/json",
|
|
1174
|
+
authorization: `Bearer ${credential.token}`
|
|
1175
|
+
} });
|
|
1176
|
+
if (!response.ok) throw new Error(`rate-limit window request failed: HTTP ${response.status}`);
|
|
1177
|
+
return parseRateLimitWindows(await response.json());
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1091
1180
|
* Summarize what is about to expire.
|
|
1092
1181
|
*
|
|
1093
1182
|
* Only lots that still hold credits count: a lot that was granted and used up
|
|
@@ -1124,6 +1213,9 @@ var MinimaxCredits = class {
|
|
|
1124
1213
|
#expiry;
|
|
1125
1214
|
#ledgerAtMs;
|
|
1126
1215
|
#ledgerError;
|
|
1216
|
+
#rateLimit;
|
|
1217
|
+
#rateLimitError;
|
|
1218
|
+
#rateLimitAtMs;
|
|
1127
1219
|
/** The balance as last read. */
|
|
1128
1220
|
get snapshot() {
|
|
1129
1221
|
return this.#snapshot;
|
|
@@ -1148,12 +1240,26 @@ var MinimaxCredits = class {
|
|
|
1148
1240
|
get ledgerError() {
|
|
1149
1241
|
return this.#ledgerError;
|
|
1150
1242
|
}
|
|
1243
|
+
/** The plan's rate-limit windows as last read. */
|
|
1244
|
+
get rateLimit() {
|
|
1245
|
+
return this.#rateLimit;
|
|
1246
|
+
}
|
|
1247
|
+
/** Why the windows could not be read, when they could not. */
|
|
1248
|
+
get rateLimitError() {
|
|
1249
|
+
return this.#rateLimitError;
|
|
1250
|
+
}
|
|
1251
|
+
/** When the windows were last read. */
|
|
1252
|
+
get rateLimitAtMs() {
|
|
1253
|
+
return this.#rateLimitAtMs;
|
|
1254
|
+
}
|
|
1151
1255
|
/**
|
|
1152
1256
|
* Read the balance and record how much was consumed since the last read.
|
|
1153
1257
|
*
|
|
1154
1258
|
* The credit ledger is read alongside it, but a ledger that cannot be read
|
|
1155
1259
|
* never hides the balance: the two come from different endpoints and only
|
|
1156
|
-
* the balance is essential.
|
|
1260
|
+
* the balance is essential. The rate-limit windows ride along under the
|
|
1261
|
+
* same rule — they come from yet another endpoint, and none of the three
|
|
1262
|
+
* takes the others down with it.
|
|
1157
1263
|
*
|
|
1158
1264
|
* @returns Whether the balance changed.
|
|
1159
1265
|
*/
|
|
@@ -1176,6 +1282,7 @@ var MinimaxCredits = class {
|
|
|
1176
1282
|
this.#error = error instanceof Error ? error.message : String(error);
|
|
1177
1283
|
}
|
|
1178
1284
|
await this.refreshLedger(credential, region, fetchImpl, now);
|
|
1285
|
+
await this.refreshRateLimit(credential, region, fetchImpl, now);
|
|
1179
1286
|
return changed;
|
|
1180
1287
|
}
|
|
1181
1288
|
/**
|
|
@@ -1196,6 +1303,22 @@ var MinimaxCredits = class {
|
|
|
1196
1303
|
this.#ledgerError = error instanceof Error ? error.message : String(error);
|
|
1197
1304
|
}
|
|
1198
1305
|
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Read the plan's rate-limit windows.
|
|
1308
|
+
*
|
|
1309
|
+
* Same rule as the ledger: a failure is remembered but never thrown, and a
|
|
1310
|
+
* window that cannot be read leaves the last one on display.
|
|
1311
|
+
*/
|
|
1312
|
+
async refreshRateLimit(credential, region, fetchImpl = fetch, now = Date.now()) {
|
|
1313
|
+
try {
|
|
1314
|
+
const windows = await fetchRateLimitWindows(credential, region, fetchImpl);
|
|
1315
|
+
this.#rateLimit = windows;
|
|
1316
|
+
this.#rateLimitError = windows === void 0 ? "response carried no rate-limit windows" : void 0;
|
|
1317
|
+
this.#rateLimitAtMs = now;
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
this.#rateLimitError = error instanceof Error ? error.message : String(error);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1199
1322
|
};
|
|
1200
1323
|
//#endregion
|
|
1201
1324
|
//#region src/credentials.ts
|
|
@@ -1284,6 +1407,11 @@ async function readPluginCredential(env = process.env) {
|
|
|
1284
1407
|
async function writePluginCredential(credential, env = process.env) {
|
|
1285
1408
|
await writePrivate(pluginCredentialPath(env), credential);
|
|
1286
1409
|
}
|
|
1410
|
+
/** Remove the plugin's own credential, which is what signing out means. */
|
|
1411
|
+
async function deletePluginCredential(env = process.env) {
|
|
1412
|
+
const { rm } = await import("node:fs/promises");
|
|
1413
|
+
await rm(pluginCredentialPath(env), { force: true });
|
|
1414
|
+
}
|
|
1287
1415
|
/** Read the plugin's own state. */
|
|
1288
1416
|
async function readPluginState(env = process.env) {
|
|
1289
1417
|
let text;
|
|
@@ -1891,6 +2019,25 @@ var UsageMeter = class {
|
|
|
1891
2019
|
//#endregion
|
|
1892
2020
|
//#region src/index.ts
|
|
1893
2021
|
/** Loader entry name; also the id the bundle patch inserts. */
|
|
2022
|
+
/**
|
|
2023
|
+
* Whether a message says the server refused the credential itself.
|
|
2024
|
+
*
|
|
2025
|
+
* The account and catalog endpoints answer a revoked or unknown bearer with
|
|
2026
|
+
* `401`, and the only place that surfaces is the message the request layer
|
|
2027
|
+
* built. Matching the status code rather than the wording keeps this working
|
|
2028
|
+
* whatever language the message is written in.
|
|
2029
|
+
*/
|
|
2030
|
+
const isUnauthorized = (message) => message !== void 0 && /\bHTTP 401\b/.test(message);
|
|
2031
|
+
/**
|
|
2032
|
+
* Whether a failed refresh means the credential can never be used again.
|
|
2033
|
+
*
|
|
2034
|
+
* The authorization server answers a revoked or already-spent refresh token
|
|
2035
|
+
* with `400`/`401`, and a credential that carries no refresh token can never
|
|
2036
|
+
* be renewed at all. Both are final. A `5xx` or a dropped connection is not:
|
|
2037
|
+
* throwing the credential away over a bad minute upstream would sign the
|
|
2038
|
+
* person out for nothing.
|
|
2039
|
+
*/
|
|
2040
|
+
const isRefreshRefused = (message) => message.includes("没有可用于续期的 refresh token") || /\bHTTP (400|401)\b/.test(message);
|
|
1894
2041
|
const name = "dsh-connect-minimaxcode";
|
|
1895
2042
|
/** Services this plugin needs before it can serve anything. */
|
|
1896
2043
|
const inject = ["llm"];
|
|
@@ -1951,14 +2098,50 @@ function apply(ctx) {
|
|
|
1951
2098
|
let loginSource;
|
|
1952
2099
|
/** Whether a device login is waiting to be authorized. */
|
|
1953
2100
|
let loginPending = false;
|
|
2101
|
+
/** Whether the server refused the plugin's own credential. */
|
|
2102
|
+
let revokedByServer = false;
|
|
2103
|
+
/**
|
|
2104
|
+
* Give up on the plugin's own credential after the server refused it.
|
|
2105
|
+
*
|
|
2106
|
+
* A revoked token keeps its expiry date, so every later call would send the
|
|
2107
|
+
* same dead token and fail the same way. The only way back is a new sign-in,
|
|
2108
|
+
* so the credential is deleted, the cached views of the account it belonged
|
|
2109
|
+
* to are dropped, and the card is left asking for one.
|
|
2110
|
+
*/
|
|
2111
|
+
const revokePluginCredential = async () => {
|
|
2112
|
+
await deletePluginCredential();
|
|
2113
|
+
if (pluginState.deviceLogin !== void 0) {
|
|
2114
|
+
pluginState = {
|
|
2115
|
+
...pluginState,
|
|
2116
|
+
deviceLogin: void 0
|
|
2117
|
+
};
|
|
2118
|
+
await writePluginState(pluginState);
|
|
2119
|
+
}
|
|
2120
|
+
revokedByServer = true;
|
|
2121
|
+
modelToken = void 0;
|
|
2122
|
+
loginSource = void 0;
|
|
2123
|
+
loginPending = false;
|
|
2124
|
+
creditState = "signed-out";
|
|
2125
|
+
signinPanel = void 0;
|
|
2126
|
+
signinClaim = void 0;
|
|
2127
|
+
signinError = void 0;
|
|
2128
|
+
catalog.invalidate();
|
|
2129
|
+
lastCreditAtMs = Date.now();
|
|
2130
|
+
lastSigninAtMs = Date.now();
|
|
2131
|
+
lastCatalogAtMs = 0;
|
|
2132
|
+
};
|
|
1954
2133
|
/**
|
|
1955
2134
|
* The credential the model gateway is called with.
|
|
1956
2135
|
*
|
|
1957
2136
|
* The plugin's own sign-in comes first: it is the credential the person
|
|
1958
2137
|
* chose, and on a machine where the desktop app was never installed it is
|
|
1959
|
-
* the only one there is.
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
2138
|
+
* the only one there is.
|
|
2139
|
+
*
|
|
2140
|
+
* The desktop app's OAuth grant is next, and it is not a mere fallback: it
|
|
2141
|
+
* is the credential a current install actually holds. Its `mmoat_` bearer is
|
|
2142
|
+
* what the gateway accepts, while the legacy JWT older installs left behind
|
|
2143
|
+
* is refused with `401 auth failed` — so the OAuth file has to be consulted
|
|
2144
|
+
* before that JWT, or a working machine looks broken.
|
|
1962
2145
|
*/
|
|
1963
2146
|
const resolveGatewayCredential = async () => {
|
|
1964
2147
|
const own = await readPluginCredential();
|
|
@@ -1968,11 +2151,18 @@ function apply(ctx) {
|
|
|
1968
2151
|
await writePluginCredential(next);
|
|
1969
2152
|
loginSource = "plugin";
|
|
1970
2153
|
modelToken = next.accessToken;
|
|
2154
|
+
revokedByServer = false;
|
|
1971
2155
|
return {
|
|
1972
2156
|
token: next.accessToken,
|
|
1973
2157
|
claims: { exp: Math.floor((next.expiresAtMs ?? Date.now()) / 1e3) }
|
|
1974
2158
|
};
|
|
1975
|
-
} catch {
|
|
2159
|
+
} catch (error) {
|
|
2160
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2161
|
+
if (isRefreshRefused(message)) {
|
|
2162
|
+
await revokePluginCredential();
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
1976
2166
|
else {
|
|
1977
2167
|
loginSource = "plugin";
|
|
1978
2168
|
modelToken = own.accessToken;
|
|
@@ -1982,6 +2172,15 @@ function apply(ctx) {
|
|
|
1982
2172
|
};
|
|
1983
2173
|
}
|
|
1984
2174
|
}
|
|
2175
|
+
const oauth = await ensureOAuthCredential(region, process.env);
|
|
2176
|
+
if (oauth.state === "signed-in") {
|
|
2177
|
+
loginSource = "desktop-app";
|
|
2178
|
+
modelToken = oauth.credential.token;
|
|
2179
|
+
return {
|
|
2180
|
+
token: oauth.credential.token,
|
|
2181
|
+
claims: { exp: Math.floor((oauth.credential.expiresAtMs ?? Date.now()) / 1e3) }
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
1985
2184
|
const state = await readCredential();
|
|
1986
2185
|
if (state.state !== "signed-in") {
|
|
1987
2186
|
loginSource = void 0;
|
|
@@ -2009,7 +2208,15 @@ function apply(ctx) {
|
|
|
2009
2208
|
state: "signed-in",
|
|
2010
2209
|
credential: toOAuthCredential(next)
|
|
2011
2210
|
};
|
|
2012
|
-
} catch {
|
|
2211
|
+
} catch (error) {
|
|
2212
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2213
|
+
if (isRefreshRefused(message)) {
|
|
2214
|
+
await revokePluginCredential();
|
|
2215
|
+
return {
|
|
2216
|
+
state: "signed-out",
|
|
2217
|
+
reason: "not-signed-in"
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2013
2220
|
return {
|
|
2014
2221
|
state: "expired",
|
|
2015
2222
|
credential: toOAuthCredential(own)
|
|
@@ -2064,6 +2271,15 @@ function apply(ctx) {
|
|
|
2064
2271
|
}
|
|
2065
2272
|
await credits.refresh(state.credential, region, fetch, now);
|
|
2066
2273
|
creditState = credits.snapshot === void 0 ? "unavailable" : "ok";
|
|
2274
|
+
if ([
|
|
2275
|
+
credits.error,
|
|
2276
|
+
credits.ledgerError,
|
|
2277
|
+
credits.rateLimitError
|
|
2278
|
+
].find((message) => isUnauthorized(message)) !== void 0) {
|
|
2279
|
+
await revokePluginCredential();
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (credits.snapshot !== void 0) revokedByServer = false;
|
|
2067
2283
|
};
|
|
2068
2284
|
/**
|
|
2069
2285
|
* Read the check-in cycle, and claim the day's grant when asked to.
|
|
@@ -2089,7 +2305,12 @@ function apply(ctx) {
|
|
|
2089
2305
|
signinPanel = panel;
|
|
2090
2306
|
signinError = void 0;
|
|
2091
2307
|
} catch (error) {
|
|
2092
|
-
|
|
2308
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2309
|
+
if (isUnauthorized(message)) {
|
|
2310
|
+
await revokePluginCredential();
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
signinError = message;
|
|
2093
2314
|
return;
|
|
2094
2315
|
}
|
|
2095
2316
|
if (!pluginState.autoCheckin || panel === void 0) return;
|
|
@@ -2120,7 +2341,12 @@ function apply(ctx) {
|
|
|
2120
2341
|
refreshCredits();
|
|
2121
2342
|
return claim;
|
|
2122
2343
|
} catch (error) {
|
|
2123
|
-
|
|
2344
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2345
|
+
if (isUnauthorized(message)) {
|
|
2346
|
+
await revokePluginCredential();
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
signinError = message;
|
|
2124
2350
|
return;
|
|
2125
2351
|
}
|
|
2126
2352
|
};
|
|
@@ -2150,6 +2376,21 @@ function apply(ctx) {
|
|
|
2150
2376
|
...credits.ledgerError === void 0 ? {} : { ledgerError: credits.ledgerError }
|
|
2151
2377
|
};
|
|
2152
2378
|
};
|
|
2379
|
+
/** The plan's rolling usage windows as the card reads them. */
|
|
2380
|
+
const buildRateLimitStatus = () => {
|
|
2381
|
+
if (creditState === "signed-out" || creditState === "expired") return { state: creditState };
|
|
2382
|
+
const windows = credits.rateLimit;
|
|
2383
|
+
if (windows === void 0) return {
|
|
2384
|
+
state: "unavailable",
|
|
2385
|
+
...credits.rateLimitError === void 0 ? {} : { error: credits.rateLimitError }
|
|
2386
|
+
};
|
|
2387
|
+
return {
|
|
2388
|
+
state: "ok",
|
|
2389
|
+
interval: windows.interval,
|
|
2390
|
+
weekly: windows.weekly,
|
|
2391
|
+
...credits.rateLimitAtMs === void 0 ? {} : { updatedAtMs: credits.rateLimitAtMs }
|
|
2392
|
+
};
|
|
2393
|
+
};
|
|
2153
2394
|
/** What this host's own requests have used. */
|
|
2154
2395
|
const buildUsageStatus = () => {
|
|
2155
2396
|
const totals = meter.totals();
|
|
@@ -2227,21 +2468,47 @@ function apply(ctx) {
|
|
|
2227
2468
|
}
|
|
2228
2469
|
});
|
|
2229
2470
|
ctx.llm.registerAdapter(["minimaxcode"], adapter);
|
|
2230
|
-
/**
|
|
2231
|
-
|
|
2471
|
+
/**
|
|
2472
|
+
* The sign-in state the card reports.
|
|
2473
|
+
*
|
|
2474
|
+
* It describes the credential the gateway will actually be called with, not
|
|
2475
|
+
* a particular file on disk: the card exists to answer "will a request
|
|
2476
|
+
* work", and a token the server refused is not a sign-in however unexpired
|
|
2477
|
+
* it looks. Reading the legacy JWT alone is what made a machine whose model
|
|
2478
|
+
* gateway was working report a signed-out, expired card.
|
|
2479
|
+
*/
|
|
2480
|
+
const buildAuthStatus = async () => {
|
|
2481
|
+
if (revokedByServer) return {
|
|
2482
|
+
state: "signed-out",
|
|
2483
|
+
reason: "credential-revoked"
|
|
2484
|
+
};
|
|
2485
|
+
const current = await readNow();
|
|
2486
|
+
if (current !== void 0) {
|
|
2487
|
+
const remaining = expiresInMs(current.claims);
|
|
2488
|
+
if (remaining <= 0) return {
|
|
2489
|
+
state: "expired",
|
|
2490
|
+
reason: "credential-expired"
|
|
2491
|
+
};
|
|
2492
|
+
return {
|
|
2493
|
+
state: "signed-in",
|
|
2494
|
+
daysRemaining: Math.ceil(remaining / 864e5),
|
|
2495
|
+
expiry: expiryLevel(current.claims)
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2232
2498
|
const state = await readCredential();
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
daysRemaining: Math.ceil(expiresInMs(state.credential.claims) / 864e5),
|
|
2236
|
-
expiry: expiryLevel(state.credential.claims)
|
|
2237
|
-
} : state.state === "expired" ? { state: "expired" } : {
|
|
2499
|
+
if (state.state === "expired") return { state: "expired" };
|
|
2500
|
+
if (state.state === "signed-out") return {
|
|
2238
2501
|
state: "signed-out",
|
|
2239
2502
|
reason: state.reason
|
|
2240
2503
|
};
|
|
2504
|
+
return { state: "signed-in" };
|
|
2505
|
+
};
|
|
2506
|
+
/** Build the document the card renders, without ever exposing the token. */
|
|
2507
|
+
const buildStatus = async () => {
|
|
2241
2508
|
await refreshCredits();
|
|
2242
2509
|
await refreshSignin();
|
|
2243
2510
|
return statusDocument({
|
|
2244
|
-
auth,
|
|
2511
|
+
auth: await buildAuthStatus(),
|
|
2245
2512
|
region,
|
|
2246
2513
|
catalogSource: catalog.source,
|
|
2247
2514
|
catalogError: catalog.error,
|
|
@@ -2252,6 +2519,7 @@ function apply(ctx) {
|
|
|
2252
2519
|
})),
|
|
2253
2520
|
refreshing,
|
|
2254
2521
|
credits: buildCreditStatus(),
|
|
2522
|
+
rateLimit: buildRateLimitStatus(),
|
|
2255
2523
|
usage: buildUsageStatus(),
|
|
2256
2524
|
signin: buildSigninStatus(),
|
|
2257
2525
|
login: buildLoginStatus()
|
|
@@ -2328,6 +2596,7 @@ function apply(ctx) {
|
|
|
2328
2596
|
};
|
|
2329
2597
|
await writePluginState(pluginState);
|
|
2330
2598
|
loginPending = false;
|
|
2599
|
+
revokedByServer = false;
|
|
2331
2600
|
loginSource = "plugin";
|
|
2332
2601
|
modelToken = result.credential.accessToken;
|
|
2333
2602
|
catalog.invalidate();
|
|
@@ -2445,4 +2714,4 @@ async function readRequestBody(req) {
|
|
|
2445
2714
|
return text;
|
|
2446
2715
|
}
|
|
2447
2716
|
//#endregion
|
|
2448
|
-
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, 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, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };
|
|
2717
|
+
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 };
|
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.5.
|
|
5
|
+
"version": "0.5.6",
|
|
6
6
|
"author": "JeffTsuiCUG",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|
|
@@ -13,9 +13,13 @@
|
|
|
13
13
|
"name": "eghrhegpe",
|
|
14
14
|
"url": "https://github.com/eghrhegpe"
|
|
15
15
|
},
|
|
16
|
+
{
|
|
17
|
+
"name": "qzz19970504",
|
|
18
|
+
"url": "https://github.com/qzz19970504"
|
|
19
|
+
},
|
|
16
20
|
{
|
|
17
|
-
"name": "
|
|
18
|
-
"url": "https://github.com/
|
|
21
|
+
"name": "azsora",
|
|
22
|
+
"url": "https://github.com/azsora"
|
|
19
23
|
}
|
|
20
24
|
],
|
|
21
25
|
"license": "MIT",
|