@liyang8246/dsh-accounts 0.1.3 → 0.1.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/lib/client.js +28 -4
- package/lib/index.js +41 -5
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
empty: "No accounts yet",
|
|
37
37
|
remove: "Remove",
|
|
38
38
|
confirmRemove: "Remove this saved login and its local credentials?",
|
|
39
|
+
resetCredit: "Reset",
|
|
40
|
+
confirmResetCredit: "Spend one reset credit to clear the used 5-hour window?",
|
|
39
41
|
cancel: "Cancel",
|
|
40
42
|
open: "Open authorization page",
|
|
41
43
|
code: "Authorization code",
|
|
@@ -64,6 +66,8 @@
|
|
|
64
66
|
empty: "暂无账号",
|
|
65
67
|
remove: "删除",
|
|
66
68
|
confirmRemove: "删除此登录账号及其本地凭据?",
|
|
69
|
+
resetCredit: "重置",
|
|
70
|
+
confirmResetCredit: "消耗 1 次重置额度,清空已用的 5 小时窗口?",
|
|
67
71
|
cancel: "取消",
|
|
68
72
|
open: "打开授权页面",
|
|
69
73
|
code: "授权码",
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
.zph-accounts button{cursor:pointer}.zph-accounts button:hover{background:var(--dsw-alias-bg-layer-2)}.zph-accounts button:disabled{opacity:.5;cursor:default}.zph-accounts button.zph-link{border:0;background:none;min-height:0;padding:0;color:var(--dsw-alias-brand-primary);text-decoration:underline}.zph-accounts input{max-width:100%;min-width:0}.zph-accounts input[type=radio]{accent-color:var(--dsw-alias-brand-primary);min-height:0;padding:0}
|
|
101
105
|
.zph-accounts button:focus-visible,.zph-accounts input:focus-visible,.zph-accounts a:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.zph-accounts a{color:var(--dsw-alias-brand-primary)}
|
|
102
106
|
.zph-accounts .zph-quota{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}.zph-accounts .zph-window{padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2);font-size:11px;overflow-wrap:anywhere}.zph-accounts .zph-window-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
|
|
103
|
-
.zph-accounts .zph-track{margin:6px 0 0;width:100%}.zph-accounts .zph-error{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}.zph-accounts .zph-quota-unavailable{padding-left:
|
|
107
|
+
.zph-accounts .zph-track{margin:6px 0 0;width:100%}.zph-accounts .zph-error{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}.zph-accounts .zph-quota-unavailable{padding-left:5px}.zph-accounts .zph-quota-unavailable > p{display:inline}.zph-accounts .zph-quota-unavailable > p + p{margin-left:8px}
|
|
104
108
|
.zph-accounts .zph-add{padding:12px 16px;border-top:1px solid var(--dsw-alias-border-l1)}.zph-accounts .zph-form{display:flex;flex-direction:column;gap:10px;margin-top:10px}.zph-accounts .zph-form label{display:flex;flex-direction:column;gap:4px}.zph-accounts .zph-grow{flex:1 1 180px;min-width:0}.zph-accounts .zph-align-end{align-items:flex-end}
|
|
105
109
|
.zph-accounts .zph-switch{display:flex;align-items:center;cursor:pointer}.zph-accounts .zph-switch input{appearance:none;-webkit-appearance:none;position:relative;width:36px;height:20px;min-height:0;padding:0;border:0;border-radius:10px;background:var(--dsw-alias-bg-layer-2);cursor:pointer;transition:background .15s}
|
|
106
110
|
.zph-accounts .zph-switch input::after{content:'';position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-secondary);transition:transform .15s,background .15s}
|
|
@@ -134,7 +138,7 @@
|
|
|
134
138
|
return isObject(value) && (value.period === "5h" || value.period === "week" || value.period === "month" || value.period === "other") && optionalString(value.label) && optionalString(value.unit) && optionalString(value.resetsAt) && optionalNumber(value.usedPercent) && optionalNumber(value.used) && optionalNumber(value.limit) && (value.exhausted === void 0 || typeof value.exhausted === "boolean");
|
|
135
139
|
}
|
|
136
140
|
function isQuota(value) {
|
|
137
|
-
return isObject(value) && (value.status === "ok" || value.status === "unavailable" || value.status === "error") && typeof value.fetchedAt === "string" && Array.isArray(value.windows) && value.windows.every(isQuotaWindow) && optionalString(value.reason) && optionalString(value.plan);
|
|
141
|
+
return isObject(value) && (value.status === "ok" || value.status === "unavailable" || value.status === "error") && typeof value.fetchedAt === "string" && Array.isArray(value.windows) && value.windows.every(isQuotaWindow) && optionalString(value.reason) && optionalString(value.plan) && optionalNumber(value.resetCredits);
|
|
138
142
|
}
|
|
139
143
|
function isAccount(value) {
|
|
140
144
|
return isObject(value) && typeof value.id === "string" && isChannelId(value.channel) && typeof value.label === "string" && typeof value.createdAt === "string" && (value.auth === "api-key" || value.auth === "oauth") && typeof value.active === "boolean" && optionalString(value.email) && optionalString(value.orgName) && (value.quota === void 0 || isQuota(value.quota));
|
|
@@ -235,6 +239,7 @@
|
|
|
235
239
|
const [answer, setAnswer] = React.useState("");
|
|
236
240
|
const [paste, setPaste] = React.useState(false);
|
|
237
241
|
const [removing, setRemoving] = React.useState(null);
|
|
242
|
+
const [resetting, setResetting] = React.useState(null);
|
|
238
243
|
const mounted = React.useRef(true);
|
|
239
244
|
React.useEffect(() => {
|
|
240
245
|
mounted.current = true;
|
|
@@ -307,6 +312,7 @@
|
|
|
307
312
|
const value = isLoginRequest(request) ? await call(request) : await store.update(() => call(request));
|
|
308
313
|
if ("accounts" in value) {
|
|
309
314
|
if (mounted.current && request.action === "remove") setRemoving(null);
|
|
315
|
+
if (mounted.current && request.action === "reset_credit") setResetting(null);
|
|
310
316
|
if (mounted.current && request.action === "add_key") {
|
|
311
317
|
setApiKey("");
|
|
312
318
|
setForm(null);
|
|
@@ -429,7 +435,13 @@
|
|
|
429
435
|
id: account.id
|
|
430
436
|
});
|
|
431
437
|
}
|
|
432
|
-
}), /* @__PURE__ */ React.createElement("strong", null, account.label)), account.quota?.plan && /* @__PURE__ */ React.createElement("span", { className: "zph-pill" }, account.quota.plan), /* @__PURE__ */ React.createElement("div", { className: "zph-actions" },
|
|
438
|
+
}), /* @__PURE__ */ React.createElement("strong", null, account.label)), account.quota?.plan && /* @__PURE__ */ React.createElement("span", { className: "zph-pill" }, account.quota.plan), /* @__PURE__ */ React.createElement("div", { className: "zph-actions" }, typeof account.quota?.resetCredits === "number" && /* @__PURE__ */ React.createElement("button", {
|
|
439
|
+
type: "button",
|
|
440
|
+
disabled: busy || !account.quota.resetCredits,
|
|
441
|
+
"aria-expanded": resetting === account.id,
|
|
442
|
+
"aria-controls": `zph-reset-${account.id}`,
|
|
443
|
+
onClick: () => setResetting(account.id)
|
|
444
|
+
}, `${t("resetCredit")} (${account.quota.resetCredits})`), button(t("refresh"), {
|
|
433
445
|
action: "refresh_quota",
|
|
434
446
|
id: account.id
|
|
435
447
|
}), /* @__PURE__ */ React.createElement("button", {
|
|
@@ -438,7 +450,19 @@
|
|
|
438
450
|
"aria-expanded": removing === account.id,
|
|
439
451
|
"aria-controls": `zph-remove-${account.id}`,
|
|
440
452
|
onClick: () => setRemoving(account.id)
|
|
441
|
-
}, t("remove")))),
|
|
453
|
+
}, t("remove")))), resetting === account.id && /* @__PURE__ */ React.createElement("div", {
|
|
454
|
+
id: `zph-reset-${account.id}`,
|
|
455
|
+
className: "zph-row",
|
|
456
|
+
role: "group",
|
|
457
|
+
"aria-label": t("confirmResetCredit")
|
|
458
|
+
}, /* @__PURE__ */ React.createElement("span", null, t("confirmResetCredit")), button(t("resetCredit"), {
|
|
459
|
+
action: "reset_credit",
|
|
460
|
+
id: account.id
|
|
461
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
462
|
+
type: "button",
|
|
463
|
+
disabled: busy,
|
|
464
|
+
onClick: () => setResetting(null)
|
|
465
|
+
}, t("cancel"))), removing === account.id && /* @__PURE__ */ React.createElement("div", {
|
|
442
466
|
id: `zph-remove-${account.id}`,
|
|
443
467
|
className: "zph-row",
|
|
444
468
|
role: "group",
|
package/lib/index.js
CHANGED
|
@@ -6708,6 +6708,16 @@ async function loginOpenCode(interaction) {
|
|
|
6708
6708
|
});
|
|
6709
6709
|
}
|
|
6710
6710
|
// src/providers/quota.ts
|
|
6711
|
+
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
6712
|
+
const CODEX_RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits/consume";
|
|
6713
|
+
function codexHeaders(account) {
|
|
6714
|
+
if (account.credential.kind !== "oauth") throw new Error("Codex requires OAuth");
|
|
6715
|
+
return {
|
|
6716
|
+
"Authorization": `Bearer ${account.credential.tokens.access}`,
|
|
6717
|
+
"User-Agent": "dsh-zephyr/0.2.0",
|
|
6718
|
+
...account.credential.tokens.accountId ? { "ChatGPT-Account-Id": account.credential.tokens.accountId } : {}
|
|
6719
|
+
};
|
|
6720
|
+
}
|
|
6711
6721
|
function percent(value) {
|
|
6712
6722
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
6713
6723
|
}
|
|
@@ -6718,6 +6728,8 @@ function reset(value) {
|
|
|
6718
6728
|
function parseOpenAIQuota(input) {
|
|
6719
6729
|
const body = record(input, "OpenAI usage");
|
|
6720
6730
|
const rate = record(body.rate_limit, "OpenAI rate limit");
|
|
6731
|
+
const credits = body.rate_limit_reset_credits;
|
|
6732
|
+
const resetCredits = percent(credits && typeof credits === "object" && !Array.isArray(credits) ? credits.available_count : void 0);
|
|
6721
6733
|
const windows = [];
|
|
6722
6734
|
for (const key of ["primary_window", "secondary_window"]) {
|
|
6723
6735
|
if (rate[key] == null) continue;
|
|
@@ -6741,6 +6753,7 @@ function parseOpenAIQuota(input) {
|
|
|
6741
6753
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6742
6754
|
windows,
|
|
6743
6755
|
...typeof body.plan_type === "string" ? { plan: body.plan_type.slice(0, 80) } : {},
|
|
6756
|
+
...resetCredits === void 0 ? {} : { resetCredits },
|
|
6744
6757
|
...!windows.length ? { reason: "OpenAI did not return supported quota windows" } : {}
|
|
6745
6758
|
};
|
|
6746
6759
|
}
|
|
@@ -6773,15 +6786,11 @@ function parseOpenCodeQuota(input) {
|
|
|
6773
6786
|
}
|
|
6774
6787
|
async function fetchQuota(account, signal) {
|
|
6775
6788
|
if (account.channel === "commandcode-go") throw new Error("Use the CommandCode quota adapter");
|
|
6789
|
+
if (account.channel === "openai-codex") return parseOpenAIQuota(await fetchJSON(CODEX_USAGE_URL, { headers: codexHeaders(account) }, signal));
|
|
6776
6790
|
const headers = {
|
|
6777
6791
|
"Authorization": `Bearer ${account.credential.kind === "api-key" ? account.credential.key : account.credential.tokens.access}`,
|
|
6778
6792
|
"User-Agent": "dsh-zephyr/0.2.0"
|
|
6779
6793
|
};
|
|
6780
|
-
if (account.channel === "openai-codex") {
|
|
6781
|
-
if (account.credential.kind !== "oauth") throw new Error("Codex requires OAuth");
|
|
6782
|
-
if (account.credential.tokens.accountId) headers["ChatGPT-Account-Id"] = account.credential.tokens.accountId;
|
|
6783
|
-
return parseOpenAIQuota(await fetchJSON("https://chatgpt.com/backend-api/wham/usage", { headers }, signal));
|
|
6784
|
-
}
|
|
6785
6794
|
if (account.credential.kind === "oauth" && account.credential.tokens.orgId) headers["x-opencode-org-id"] = account.credential.tokens.orgId;
|
|
6786
6795
|
try {
|
|
6787
6796
|
return parseOpenCodeQuota(await fetchJSON("https://opencode.ai/zen/go/v1/usage", { headers }, signal));
|
|
@@ -6795,6 +6804,21 @@ async function fetchQuota(account, signal) {
|
|
|
6795
6804
|
};
|
|
6796
6805
|
}
|
|
6797
6806
|
}
|
|
6807
|
+
async function consumeCodexResetCredit(account, signal) {
|
|
6808
|
+
const body = record(await fetchJSON(CODEX_RESET_CREDITS_URL, {
|
|
6809
|
+
method: "POST",
|
|
6810
|
+
headers: {
|
|
6811
|
+
...codexHeaders(account),
|
|
6812
|
+
"Content-Type": "application/json"
|
|
6813
|
+
},
|
|
6814
|
+
body: JSON.stringify({ redeem_request_id: randomUUID() })
|
|
6815
|
+
}, signal), "Codex reset credit");
|
|
6816
|
+
if (body.code === "reset") return;
|
|
6817
|
+
if (body.code === "no_credit") throw new Error("No reset credit is available for this account");
|
|
6818
|
+
if (body.code === "nothing_to_reset") throw new Error("No used quota window needs a reset");
|
|
6819
|
+
if (body.code === "already_redeemed") throw new Error("This reset request was already redeemed");
|
|
6820
|
+
throw new Error(`Codex reset returned an unknown outcome: ${String(body.code).slice(0, 40)}`);
|
|
6821
|
+
}
|
|
6798
6822
|
// src/shared/channels.ts
|
|
6799
6823
|
const channels = [
|
|
6800
6824
|
{
|
|
@@ -6893,6 +6917,7 @@ function readQuotas(value) {
|
|
|
6893
6917
|
windows: view.windows.flatMap(readWindow)
|
|
6894
6918
|
};
|
|
6895
6919
|
for (const key of ["reason", "plan"]) if (typeof view[key] === "string") quota[key] = view[key];
|
|
6920
|
+
if (typeof view.resetCredits === "number" && Number.isFinite(view.resetCredits) && view.resetCredits >= 0) quota.resetCredits = view.resetCredits;
|
|
6896
6921
|
return [[id, quota]];
|
|
6897
6922
|
}));
|
|
6898
6923
|
}
|
|
@@ -7290,6 +7315,13 @@ var ModelAccountsService = class {
|
|
|
7290
7315
|
this.fetching.delete(id);
|
|
7291
7316
|
}
|
|
7292
7317
|
}
|
|
7318
|
+
async resetCredit(id, signal) {
|
|
7319
|
+
const view = (await this.accounts.list()).find((account) => account.id === id);
|
|
7320
|
+
if (!view) throw new Error("Account not found");
|
|
7321
|
+
if (view.channel !== "openai-codex") throw new Error("Only OpenAI Codex accounts have reset credits");
|
|
7322
|
+
await consumeCodexResetCredit(await this.accounts.resolve(view.channel, signal, id), signal);
|
|
7323
|
+
await this.refreshQuota(id, signal);
|
|
7324
|
+
}
|
|
7293
7325
|
async dispatch(raw, origin, signal) {
|
|
7294
7326
|
if (this.controller.signal.aborted) throw new Error("Plugin is unloading");
|
|
7295
7327
|
const args = record(raw);
|
|
@@ -7321,6 +7353,10 @@ var ModelAccountsService = class {
|
|
|
7321
7353
|
this.onChange?.();
|
|
7322
7354
|
break;
|
|
7323
7355
|
}
|
|
7356
|
+
case "reset_credit":
|
|
7357
|
+
if (origin !== "ui") throw new Error("Resetting quota is user-only; open Settings → Model Accounts");
|
|
7358
|
+
await this.resetCredit(text(args.id, "account id"), signal);
|
|
7359
|
+
break;
|
|
7324
7360
|
case "refresh_quota": {
|
|
7325
7361
|
const accounts = await this.accounts.list();
|
|
7326
7362
|
const ids = args.id === void 0 ? accounts.map((account) => account.id) : [text(args.id, "account id")];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liyang8246/dsh-accounts",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"description": "Independent multi-account Codex, OpenCode Go and CommandCode Go plugin for DeepSeek Harness.",
|
|
6
6
|
"repository": { "type": "git", "url": "https://github.com/liyang8246/dsh-zolin.git", "directory": "packages/dsh-accounts" },
|
|
7
7
|
"exports": {
|