pi-provider-qoder-cn 0.2.3 → 0.2.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.md +7 -12
- package/dist/index.js +76 -69
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ Qoder CN Plan (personal_standard)
|
|
|
74
74
|
Overall [███░░░░░░░░░░░░░░░░░░] 12.5% used
|
|
75
75
|
Plan quota [███░░░░░░░░░░░░░░░░░░] 250 / 2000 credits 12.5% 1750 left
|
|
76
76
|
Add-on [░░░░░░░░░░░░░░░░░░░░░] 0 / 700 credits 0% 700 left
|
|
77
|
-
|
|
77
|
+
Expires never
|
|
78
78
|
Manage https://qoder.com.cn/account/usage
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -100,8 +100,8 @@ Notes:
|
|
|
100
100
|
not reserve space for one it does not have.
|
|
101
101
|
- Colour is suppressed automatically for a `NO_COLOR` environment, a dumb
|
|
102
102
|
terminal, and any non-TTY output that is not the interactive UI.
|
|
103
|
-
- `
|
|
104
|
-
allowance
|
|
103
|
+
- `Expires never` means the API reported the year-9999 sentinel, i.e. the
|
|
104
|
+
allowance has no expiry date.
|
|
105
105
|
- `Note plan quota is prorated` appears after a mid-cycle plan change.
|
|
106
106
|
- The command uses the same stored token as chat requests, refreshing it first,
|
|
107
107
|
so it reports the same quota the model is actually billed against.
|
|
@@ -157,21 +157,16 @@ the currently saved endpoint and defaults to the personal account:
|
|
|
157
157
|
Choose the Qoder CN endpoint (default: personal account)
|
|
158
158
|
→ Personal account — public gateway (qoder.com.cn)
|
|
159
159
|
Enterprise account — use a VPC endpoint
|
|
160
|
-
Enterprise account — <detected value> (detected: stored credential)
|
|
161
160
|
```
|
|
162
161
|
|
|
163
|
-
- **Personal account** uses the public gateway and pins that choice
|
|
164
|
-
|
|
165
|
-
`
|
|
166
|
-
|
|
162
|
+
- **Personal account** uses the public gateway and pins that choice. If
|
|
163
|
+
`QODER_VPC_ENDPOINT` / `QODERCN_VPC_ENDPOINT` is set, it still wins —
|
|
164
|
+
`/login` says so and warns that a personal pick reverts on restart until the
|
|
165
|
+
variable is removed.
|
|
167
166
|
- **Enterprise account** then prompts for the instance name or domain.
|
|
168
167
|
- Switching back and forth is expected; re-running `/login` presents the saved
|
|
169
168
|
endpoint so you can change it.
|
|
170
169
|
|
|
171
|
-
A `vpc_endpoint` found in `~/.pi/agent/auth.json` is offered as the third option
|
|
172
|
-
but never applied on its own. `/qoder-endpoint` with no argument also reports it
|
|
173
|
-
under “Detected (not applied)”.
|
|
174
|
-
|
|
175
170
|
## Models
|
|
176
171
|
|
|
177
172
|
| Model | id | Context | Thinking effort |
|
package/dist/index.js
CHANGED
|
@@ -256,22 +256,6 @@ function readVpcEndpointFromSettings() {
|
|
|
256
256
|
}
|
|
257
257
|
return void 0;
|
|
258
258
|
}
|
|
259
|
-
function readVpcEndpointFromAuth() {
|
|
260
|
-
try {
|
|
261
|
-
const path = join2(piAgentDir(), "auth.json");
|
|
262
|
-
if (!existsSync2(path)) return void 0;
|
|
263
|
-
const auth = JSON.parse(readFileSync2(path, "utf8"));
|
|
264
|
-
const entry = auth?.["qoder-cn"];
|
|
265
|
-
const value = entry?.vpc_endpoint ?? entry?.vpcInstance;
|
|
266
|
-
if (value !== void 0 && value !== "") return String(value);
|
|
267
|
-
} catch {
|
|
268
|
-
}
|
|
269
|
-
return void 0;
|
|
270
|
-
}
|
|
271
|
-
function readInheritedVpcEndpoint() {
|
|
272
|
-
const value = readVpcEndpointFromAuth();
|
|
273
|
-
return value ? { value, source: "stored credential" } : void 0;
|
|
274
|
-
}
|
|
275
259
|
function readQoderVpcEndpoint() {
|
|
276
260
|
const fromEnv = process.env.QODER_VPC_ENDPOINT || process.env.QODERCN_VPC_ENDPOINT;
|
|
277
261
|
if (fromEnv) return fromEnv;
|
|
@@ -323,7 +307,20 @@ function saveQoderVpcEndpoint(raw) {
|
|
|
323
307
|
try {
|
|
324
308
|
const dir = piAgentDir();
|
|
325
309
|
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
326
|
-
|
|
310
|
+
const path = settingsFilePath();
|
|
311
|
+
let payload = {};
|
|
312
|
+
if (existsSync2(path)) {
|
|
313
|
+
try {
|
|
314
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
315
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
316
|
+
payload = parsed;
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
payload = {};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
payload.vpc_endpoint = raw || "";
|
|
323
|
+
writeFileSync2(path, JSON.stringify(payload, null, 2), "utf8");
|
|
327
324
|
} catch (error) {
|
|
328
325
|
console.error("[pi-provider-qoder-cn] Failed to save qoder-cn-settings.json:", error);
|
|
329
326
|
}
|
|
@@ -364,9 +361,6 @@ function getQoderBaseUrl(mode) {
|
|
|
364
361
|
function getQoderOpenApiUrl(mode) {
|
|
365
362
|
return getRegionValue(mode, "openApiUrl");
|
|
366
363
|
}
|
|
367
|
-
function getQoderCenterUrl(mode) {
|
|
368
|
-
return getRegionValue(mode, "centerUrl");
|
|
369
|
-
}
|
|
370
364
|
function getQoderModelListURL(mode) {
|
|
371
365
|
return `${getQoderBaseUrl(mode)}algo/api/v2/model/list?Encode=1`;
|
|
372
366
|
}
|
|
@@ -383,7 +377,7 @@ function getQoderUsageURL(mode) {
|
|
|
383
377
|
return `${getQoderOpenApiUrl(mode)}/api/v2/quota/usage`;
|
|
384
378
|
}
|
|
385
379
|
function getQoderRefreshURL(mode) {
|
|
386
|
-
return `${
|
|
380
|
+
return `${getQoderOpenApiUrl(mode)}/api/v1/jobToken/refresh`;
|
|
387
381
|
}
|
|
388
382
|
function getQoderDeviceLoginURL(codeChallenge, machineID, nonce) {
|
|
389
383
|
const baseUrl = getQoderRegionConfig("global").deviceLoginUrl;
|
|
@@ -1146,7 +1140,7 @@ async function exchangeJobToken(pat, mode) {
|
|
|
1146
1140
|
const parsed = Date.parse(data.expires_at);
|
|
1147
1141
|
if (!Number.isNaN(parsed)) expiresAt = parsed;
|
|
1148
1142
|
} else if (data.expires_in) {
|
|
1149
|
-
expiresAt = Date.now() + data.expires_in;
|
|
1143
|
+
expiresAt = Date.now() + (data.expires_in > 86400 ? data.expires_in : data.expires_in * 1e3);
|
|
1150
1144
|
}
|
|
1151
1145
|
return {
|
|
1152
1146
|
jobToken: data.token,
|
|
@@ -1200,7 +1194,7 @@ var init_pat = __esm({
|
|
|
1200
1194
|
"use strict";
|
|
1201
1195
|
init_cosy();
|
|
1202
1196
|
init_region();
|
|
1203
|
-
UA = "pi-provider-qoder";
|
|
1197
|
+
UA = "pi-provider-qoder-cn";
|
|
1204
1198
|
PAT_REFRESH_PREFIX = "pat";
|
|
1205
1199
|
}
|
|
1206
1200
|
});
|
|
@@ -1245,21 +1239,13 @@ function parseExpiresAt(s, expiresInSeconds) {
|
|
|
1245
1239
|
async function confirmCnEndpoint(callbacks, mode) {
|
|
1246
1240
|
if (mode !== "cn") return;
|
|
1247
1241
|
const current = getQoderCNEndpoints();
|
|
1248
|
-
const inherited = readInheritedVpcEndpoint();
|
|
1249
1242
|
const envNames = ["QODER_VPC_ENDPOINT", "QODERCN_VPC_ENDPOINT"].filter((name) => process.env[name]);
|
|
1250
1243
|
const personalId = "personal";
|
|
1251
1244
|
const enterpriseId = "enterprise";
|
|
1252
|
-
const inheritedId = "enterprise-inherited";
|
|
1253
1245
|
const options = [
|
|
1254
1246
|
{ id: personalId, label: "Personal account \u2014 public gateway (qoder.com.cn)" },
|
|
1255
1247
|
{ id: enterpriseId, label: "Enterprise account \u2014 use a VPC endpoint" }
|
|
1256
1248
|
];
|
|
1257
|
-
if (inherited) {
|
|
1258
|
-
options.push({
|
|
1259
|
-
id: inheritedId,
|
|
1260
|
-
label: `Enterprise account \u2014 ${inherited.value} (detected: ${inherited.source})`
|
|
1261
|
-
});
|
|
1262
|
-
}
|
|
1263
1249
|
let message = current.isDefault ? "Choose the Qoder CN endpoint to log in against (default: personal account)" : `Choose the Qoder CN endpoint to log in against (current: ${current.raw})`;
|
|
1264
1250
|
if (envNames.length > 0) {
|
|
1265
1251
|
message += `
|
|
@@ -1275,10 +1261,6 @@ async function confirmCnEndpoint(callbacks, mode) {
|
|
|
1275
1261
|
}
|
|
1276
1262
|
return;
|
|
1277
1263
|
}
|
|
1278
|
-
if (choice === inheritedId && inherited) {
|
|
1279
|
-
setQoderCNEndpoint(inherited.value);
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
1264
|
if (choice === enterpriseId) {
|
|
1283
1265
|
const prompt = getPrompt(callbacks);
|
|
1284
1266
|
const raw = await prompt({
|
|
@@ -1361,7 +1343,15 @@ async function patLogin(callbacks, providedPat, mode) {
|
|
|
1361
1343
|
throw new Error("No Personal Access Token provided");
|
|
1362
1344
|
}
|
|
1363
1345
|
getProgress(callbacks)?.("Exchanging access token...");
|
|
1364
|
-
|
|
1346
|
+
let creds;
|
|
1347
|
+
try {
|
|
1348
|
+
creds = await credentialsFromPat(pat, mode);
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1351
|
+
throw new Error(
|
|
1352
|
+
`${detail}. Check that the PAT is copied in full (64 characters for pt-... tokens) and was issued for the selected endpoint.`
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1365
1355
|
getProgress(callbacks)?.("Login successful!");
|
|
1366
1356
|
return creds;
|
|
1367
1357
|
}
|
|
@@ -1400,7 +1390,7 @@ async function runDeviceFlow(callbacks) {
|
|
|
1400
1390
|
method: "GET",
|
|
1401
1391
|
headers: {
|
|
1402
1392
|
Accept: "application/json",
|
|
1403
|
-
"User-Agent": "pi-provider-qoder"
|
|
1393
|
+
"User-Agent": "pi-provider-qoder-cn"
|
|
1404
1394
|
},
|
|
1405
1395
|
signal: getSignal(callbacks)
|
|
1406
1396
|
});
|
|
@@ -1425,7 +1415,7 @@ async function runDeviceFlow(callbacks) {
|
|
|
1425
1415
|
headers: {
|
|
1426
1416
|
Authorization: `Bearer ${tokenData.token}`,
|
|
1427
1417
|
Accept: "application/json",
|
|
1428
|
-
"User-Agent": "pi-provider-qoder"
|
|
1418
|
+
"User-Agent": "pi-provider-qoder-cn"
|
|
1429
1419
|
}
|
|
1430
1420
|
});
|
|
1431
1421
|
if (userinfoRes.ok) {
|
|
@@ -1524,8 +1514,13 @@ function saveCredentialsToAuthFile(providerID, credentials) {
|
|
|
1524
1514
|
if (!existsSync4(dir)) {
|
|
1525
1515
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
1526
1516
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1517
|
+
let auth;
|
|
1518
|
+
try {
|
|
1519
|
+
auth = JSON.parse(readFileSync4(authPath, "utf-8")) ?? {};
|
|
1520
|
+
} catch {
|
|
1521
|
+
const cached = readAuthFileCached();
|
|
1522
|
+
auth = cached ? { ...cached } : {};
|
|
1523
|
+
}
|
|
1529
1524
|
auth[providerID] = { type: "oauth", ...credentials };
|
|
1530
1525
|
writeFileSync4(authPath, JSON.stringify(auth, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1531
1526
|
authFileMem = { path: authPath, data: auth };
|
|
@@ -1572,20 +1567,21 @@ async function resolveQoderIdentity(accessToken, providerID, mode) {
|
|
|
1572
1567
|
const mem = identityCache.get(cacheKey);
|
|
1573
1568
|
if (mem?.userID) return mem;
|
|
1574
1569
|
const cached = getCachedCredentials(accessToken, providerID);
|
|
1575
|
-
if (cached?.userID) {
|
|
1570
|
+
if (cached?.userID && cached.access === accessToken) {
|
|
1576
1571
|
identityCache.set(cacheKey, cached);
|
|
1577
1572
|
return cached;
|
|
1578
1573
|
}
|
|
1579
1574
|
const info = await fetchUserInfo(accessToken, mode);
|
|
1580
1575
|
const machineID = getMachineId();
|
|
1576
|
+
const stored = readAuthFileCached()?.[providerID];
|
|
1581
1577
|
const creds = {
|
|
1582
1578
|
access: accessToken,
|
|
1583
1579
|
userID: info.userID || "qoder-user",
|
|
1584
1580
|
email: info.email || region.userEmailFallback,
|
|
1585
1581
|
name: info.name || region.userNameFallback,
|
|
1586
1582
|
machineID,
|
|
1587
|
-
refresh: "",
|
|
1588
|
-
expires: 0
|
|
1583
|
+
refresh: stored?.refresh || "",
|
|
1584
|
+
expires: stored?.expires || 0
|
|
1589
1585
|
};
|
|
1590
1586
|
identityCache.set(cacheKey, creds);
|
|
1591
1587
|
saveCredentialsToAuthFile(providerID, creds);
|
|
@@ -1649,9 +1645,9 @@ async function refreshQoderTokenForMode(credentials, mode) {
|
|
|
1649
1645
|
"Content-Type": "application/json",
|
|
1650
1646
|
Authorization: `Bearer ${credentials.access}`,
|
|
1651
1647
|
Accept: "application/json",
|
|
1652
|
-
"User-Agent": "pi-provider-qoder"
|
|
1648
|
+
"User-Agent": "pi-provider-qoder-cn"
|
|
1653
1649
|
},
|
|
1654
|
-
body: JSON.stringify({ refreshToken })
|
|
1650
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
1655
1651
|
});
|
|
1656
1652
|
if (response.ok) {
|
|
1657
1653
|
const data = await response.json();
|
|
@@ -1662,7 +1658,7 @@ async function refreshQoderTokenForMode(credentials, mode) {
|
|
|
1662
1658
|
const parsed = Date.parse(data.expires_at);
|
|
1663
1659
|
if (!Number.isNaN(parsed)) expireMs = parsed;
|
|
1664
1660
|
} else if (data.expires_in) {
|
|
1665
|
-
expireMs = Date.now() + data.expires_in * 1e3;
|
|
1661
|
+
expireMs = Date.now() + (data.expires_in > 86400 ? data.expires_in : data.expires_in * 1e3);
|
|
1666
1662
|
}
|
|
1667
1663
|
const refreshed = {
|
|
1668
1664
|
...credentials,
|
|
@@ -1713,7 +1709,7 @@ async function fetchQoderUsageForMode(credentials, mode) {
|
|
|
1713
1709
|
headers: {
|
|
1714
1710
|
Authorization: `Bearer ${credentials.access}`,
|
|
1715
1711
|
Accept: "application/json",
|
|
1716
|
-
"User-Agent": "pi-provider-qoder"
|
|
1712
|
+
"User-Agent": "pi-provider-qoder-cn"
|
|
1717
1713
|
}
|
|
1718
1714
|
});
|
|
1719
1715
|
if (!response.ok) {
|
|
@@ -1721,27 +1717,30 @@ async function fetchQoderUsageForMode(credentials, mode) {
|
|
|
1721
1717
|
}
|
|
1722
1718
|
const raw = await response.json();
|
|
1723
1719
|
const usageBuckets = [];
|
|
1724
|
-
|
|
1720
|
+
const userQuota = raw.user_quota ?? raw.userQuota;
|
|
1721
|
+
const org = raw.org_resource_package ?? raw.orgResourcePackage ?? raw.shared_quota ?? raw.sharedQuota;
|
|
1722
|
+
if (userQuota) {
|
|
1725
1723
|
usageBuckets.push({
|
|
1726
1724
|
id: "user-quota",
|
|
1727
1725
|
label: "User Quota",
|
|
1728
|
-
usedDisplay:
|
|
1729
|
-
limitDisplay:
|
|
1730
|
-
unit:
|
|
1726
|
+
usedDisplay: userQuota.used.toFixed(2),
|
|
1727
|
+
limitDisplay: (userQuota.total ?? userQuota.cap ?? 0).toFixed(2),
|
|
1728
|
+
unit: userQuota.unit,
|
|
1731
1729
|
resetAt: raw.expiresAt ? new Date(raw.expiresAt).toISOString() : void 0
|
|
1732
1730
|
});
|
|
1733
1731
|
}
|
|
1734
|
-
|
|
1732
|
+
const orgLimit = org?.cap ?? org?.total ?? 0;
|
|
1733
|
+
if (org && orgLimit > 0) {
|
|
1735
1734
|
usageBuckets.push({
|
|
1736
1735
|
id: "org-resource-package",
|
|
1737
1736
|
label: "Org Resource Package",
|
|
1738
|
-
usedDisplay:
|
|
1739
|
-
limitDisplay:
|
|
1740
|
-
unit:
|
|
1737
|
+
usedDisplay: org.used.toFixed(2),
|
|
1738
|
+
limitDisplay: orgLimit.toFixed(2),
|
|
1739
|
+
unit: org.unit,
|
|
1741
1740
|
resetAt: raw.expiresAt ? new Date(raw.expiresAt).toISOString() : void 0
|
|
1742
1741
|
});
|
|
1743
1742
|
}
|
|
1744
|
-
const remainingText =
|
|
1743
|
+
const remainingText = userQuota ? `${userQuota.remaining.toFixed(2)} ${userQuota.unit} remaining` : org ? `${org.remaining.toFixed(2)} ${org.unit} remaining` : "";
|
|
1745
1744
|
return {
|
|
1746
1745
|
summary: remainingText,
|
|
1747
1746
|
subscriptionTitle: region.usageTitle,
|
|
@@ -1770,13 +1769,10 @@ function describeEndpoint() {
|
|
|
1770
1769
|
async function runEndpointCommand(args, ctx) {
|
|
1771
1770
|
const input = (args || "").trim();
|
|
1772
1771
|
if (!input) {
|
|
1773
|
-
const inherited = readInheritedVpcEndpoint();
|
|
1774
|
-
const inheritedHint = inherited ? `
|
|
1775
|
-
Detected (not applied): ${inherited.value} from ${inherited.source} \u2014 run /qoder-endpoint ${inherited.value} to use it.` : "";
|
|
1776
1772
|
ctx?.ui?.notify(
|
|
1777
1773
|
`Current Qoder CN endpoint: ${describeEndpoint()}
|
|
1778
1774
|
To set: /qoder-endpoint <domain>
|
|
1779
|
-
To reset: /qoder-endpoint default
|
|
1775
|
+
To reset: /qoder-endpoint default`,
|
|
1780
1776
|
"info"
|
|
1781
1777
|
);
|
|
1782
1778
|
return;
|
|
@@ -1860,7 +1856,7 @@ function formatResetTime(expiresAt, now = Date.now()) {
|
|
|
1860
1856
|
const hours = Math.floor(totalMinutes % 1440 / 60);
|
|
1861
1857
|
const minutes = totalMinutes % 60;
|
|
1862
1858
|
const relative = days > 0 ? `${days}d ${hours}h` : hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
|
1863
|
-
return `${new Date(timestamp).
|
|
1859
|
+
return `${new Date(timestamp).toLocaleDateString("sv")} (in ${relative})`;
|
|
1864
1860
|
}
|
|
1865
1861
|
function isBucketRow(row) {
|
|
1866
1862
|
return typeof row.used === "number" || typeof row.total === "number";
|
|
@@ -1915,22 +1911,32 @@ function hasBucket(bucket) {
|
|
|
1915
1911
|
if (!bucket) return false;
|
|
1916
1912
|
return (bucket.total ?? 0) > 0 || (bucket.used ?? 0) > 0;
|
|
1917
1913
|
}
|
|
1914
|
+
function pickBucket(raw, camel) {
|
|
1915
|
+
if (!raw) return void 0;
|
|
1916
|
+
const snake = camel.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
1917
|
+
return raw[snake] ?? raw[camel];
|
|
1918
|
+
}
|
|
1918
1919
|
function formatQoderUsage(raw, mode, now = Date.now(), options = {}) {
|
|
1919
1920
|
const color = options.color ?? false;
|
|
1920
1921
|
const region = getQoderRegionConfig(mode);
|
|
1921
1922
|
const danger = Boolean(raw?.isQuotaExceeded);
|
|
1922
1923
|
const userType = raw?.userType ? ` (${raw.userType})` : "";
|
|
1923
|
-
const
|
|
1924
|
+
const showUserQuota = hasBucket(pickBucket(raw, "userQuota"));
|
|
1925
|
+
const showAddOnQuota = hasBucket(pickBucket(raw, "addOnQuota"));
|
|
1926
|
+
const org = pickBucket(raw, "orgResourcePackage") ?? pickBucket(raw, "sharedQuota");
|
|
1927
|
+
const showOrg = hasBucket(org);
|
|
1928
|
+
const orgPercent = typeof org?.used === "number" && typeof org?.cap === "number" && org.cap > 0 ? formatPercent(org.used / org.cap * 100) : void 0;
|
|
1929
|
+
const overallPercent = formatPercent(showOrg ? raw?.totalUsagePercentage || orgPercent : raw?.totalUsagePercentage);
|
|
1924
1930
|
const rows = [{ label: "Overall", percent: overallPercent, tail: "used", danger }];
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
if (
|
|
1928
|
-
|
|
1931
|
+
if (showUserQuota) rows.push(bucketRow("Plan quota", pickBucket(raw, "userQuota"), danger));
|
|
1932
|
+
if (showAddOnQuota) rows.push(bucketRow("Add-on", pickBucket(raw, "addOnQuota"), danger));
|
|
1933
|
+
if (showOrg && org)
|
|
1934
|
+
rows.push(bucketRow("Enterprise", { ...org, percentage: void 0, total: org.cap ?? org.total }, danger));
|
|
1929
1935
|
const notes = [];
|
|
1930
1936
|
if (danger) notes.push({ label: "Status", value: "quota exceeded", color: ANSI.red });
|
|
1931
|
-
if (!showUserQuota && !showAddOnQuota) notes.push({ label: "Buckets", value: "none returned" });
|
|
1937
|
+
if (!showUserQuota && !showAddOnQuota && !showOrg) notes.push({ label: "Buckets", value: "none returned" });
|
|
1932
1938
|
if (raw?.isPlanQuotaProrated) notes.push({ label: "Note", value: "plan quota is prorated" });
|
|
1933
|
-
notes.push({ label: "
|
|
1939
|
+
notes.push({ label: "Expires", value: formatResetTime(raw?.expiresAt, now) });
|
|
1934
1940
|
const manageUrl = raw?.addOnQuota?.detailUrl || raw?.upgradeUrl || region.manageUrl;
|
|
1935
1941
|
if (manageUrl) notes.push({ label: "Manage", value: manageUrl });
|
|
1936
1942
|
const title = `${region.usageTitle}${userType}`;
|
|
@@ -2081,8 +2087,9 @@ function positiveNumber(value, fallback) {
|
|
|
2081
2087
|
function parseQueueNotice(envelope) {
|
|
2082
2088
|
const payload = digForQueue(envelope, 0);
|
|
2083
2089
|
if (!payload) return null;
|
|
2090
|
+
const retryMs = positiveNumber(payload.retry_after_ms, 0) || positiveNumber(payload.retryAfterSeconds, DEFAULT_RETRY_SECONDS) * 1e3;
|
|
2084
2091
|
return {
|
|
2085
|
-
retryAfterMs: Math.min(
|
|
2092
|
+
retryAfterMs: Math.min(retryMs, MAX_WAIT_MS),
|
|
2086
2093
|
waitTimeSeconds: positiveNumber(payload.waitTime, 0),
|
|
2087
2094
|
queueCount: positiveNumber(payload.queueCount, 0)
|
|
2088
2095
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-qoder-cn",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Pi extension for Qoder CN — PAT/OAuth auth, /qoder-cn.usage quota command, and enterprise VPC endpoint support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
7
10
|
"author": "divingyu",
|
|
8
11
|
"repository": {
|
|
9
12
|
"type": "git",
|