@jeffusion/bungee-core 5.5.0 → 5.6.1
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/dist/main.js +184 -92
- package/dist/plugins/chatgpt-oauth/control.js +300 -7
- package/dist/plugins/chatgpt-oauth/control.js.map +7 -6
- package/dist/plugins/chatgpt-oauth/manifest.json +23 -0
- package/dist/plugins/chatgpt-oauth/ui/AccountsPage.svelte +33 -10
- package/dist/plugins/chatgpt-oauth/ui/account-model.js +12 -0
- package/dist/plugins/chatgpt-oauth/ui/account-model.test.js +14 -1
- package/package.json +2 -2
|
@@ -535,6 +535,7 @@ class AccountControlError extends Error {
|
|
|
535
535
|
var MAX_ACCOUNTS = 128;
|
|
536
536
|
var MAX_TEXT = 512;
|
|
537
537
|
var MAX_TOKEN = 32768;
|
|
538
|
+
var MAX_AUTO_RESET_ATTEMPTS = 256;
|
|
538
539
|
function text(value, max = MAX_TEXT) {
|
|
539
540
|
if (typeof value !== "string" || value.length === 0 || value.length > max || value.trim() !== value) {
|
|
540
541
|
throw new AccountControlError("invalid_input");
|
|
@@ -550,13 +551,21 @@ function copyIdentity(value) {
|
|
|
550
551
|
return value === undefined ? undefined : { ...value };
|
|
551
552
|
}
|
|
552
553
|
function copyAccount(value) {
|
|
553
|
-
return {
|
|
554
|
+
return {
|
|
555
|
+
...value,
|
|
556
|
+
identity: copyIdentity(value.identity),
|
|
557
|
+
refreshLock: value.refreshLock && { ...value.refreshLock },
|
|
558
|
+
autoResetAttempts: value.autoResetAttempts?.map((item) => ({ ...item }))
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function validAutoResetAttempts(value) {
|
|
562
|
+
return value === undefined || Array.isArray(value) && value.length <= MAX_AUTO_RESET_ATTEMPTS && value.every((item) => item && typeof item.creditId === "string" && item.creditId.length > 0 && item.creditId.length <= MAX_TEXT && !/[\u0000-\u001f\u007f]/.test(item.creditId) && typeof item.redeemRequestId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(item.redeemRequestId) && (item.expiresAt === undefined || Number.isFinite(item.expiresAt) && item.expiresAt >= 0) && typeof item.completed === "boolean");
|
|
554
563
|
}
|
|
555
564
|
function validStoredAccount(value) {
|
|
556
565
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
557
566
|
return false;
|
|
558
567
|
const account = value;
|
|
559
|
-
return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
|
|
568
|
+
return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && (account.autoResetCredits === undefined || typeof account.autoResetCredits === "boolean") && validAutoResetAttempts(account.autoResetAttempts) && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
|
|
560
569
|
}
|
|
561
570
|
function decode(value) {
|
|
562
571
|
if (value === undefined)
|
|
@@ -590,6 +599,7 @@ function statusReason(account) {
|
|
|
590
599
|
return;
|
|
591
600
|
}
|
|
592
601
|
function accountListItem(account) {
|
|
602
|
+
const pending = account.autoResetAttempts?.find((item) => !item.completed);
|
|
593
603
|
return {
|
|
594
604
|
id: account.id,
|
|
595
605
|
label: account.label,
|
|
@@ -597,7 +607,9 @@ function accountListItem(account) {
|
|
|
597
607
|
available: account.status === "active" && account.credentialValid && Boolean(account.accessToken && account.refreshToken && Number.isFinite(account.expiresAt)),
|
|
598
608
|
reason: statusReason(account),
|
|
599
609
|
expiresAt: account.expiresAt,
|
|
600
|
-
identity: copyIdentity(account.identity)
|
|
610
|
+
identity: copyIdentity(account.identity),
|
|
611
|
+
autoResetCredits: account.autoResetCredits === true,
|
|
612
|
+
pendingAutoReset: pending && { ...pending }
|
|
601
613
|
};
|
|
602
614
|
}
|
|
603
615
|
|
|
@@ -789,6 +801,71 @@ class AccountStore {
|
|
|
789
801
|
return copyAccount(account);
|
|
790
802
|
});
|
|
791
803
|
}
|
|
804
|
+
async setAutoResetCredits(id, enabled) {
|
|
805
|
+
if (typeof enabled !== "boolean")
|
|
806
|
+
throw new AccountControlError("invalid_input");
|
|
807
|
+
return this.mutate((aggregate) => {
|
|
808
|
+
const account = aggregate.accounts.find((item) => item.id === id);
|
|
809
|
+
if (!account)
|
|
810
|
+
throw new AccountControlError("not_found");
|
|
811
|
+
if (account.status === "revoked")
|
|
812
|
+
throw new AccountControlError("revoked");
|
|
813
|
+
account.autoResetCredits = enabled;
|
|
814
|
+
account.updatedAt = Date.now();
|
|
815
|
+
return copyAccount(account);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
async claimAutoReset(id, credit, now) {
|
|
819
|
+
return this.mutate((aggregate) => {
|
|
820
|
+
const account = aggregate.accounts.find((item) => item.id === id);
|
|
821
|
+
if (!account || !accountListItem(account).available || account.autoResetCredits !== true)
|
|
822
|
+
return;
|
|
823
|
+
const attempts = account.autoResetAttempts ?? [];
|
|
824
|
+
if (attempts.some((item) => !item.completed || item.creditId === credit.id))
|
|
825
|
+
return;
|
|
826
|
+
account.autoResetAttempts = attempts.filter((item) => !item.completed || item.expiresAt === undefined || item.expiresAt > now);
|
|
827
|
+
if (account.autoResetAttempts.length >= MAX_AUTO_RESET_ATTEMPTS || credit.expiresAt === undefined || credit.expiresAt <= now)
|
|
828
|
+
return;
|
|
829
|
+
const attempt = { creditId: credit.id, redeemRequestId: randomUUID(), expiresAt: credit.expiresAt, completed: false };
|
|
830
|
+
account.autoResetAttempts.push(attempt);
|
|
831
|
+
account.updatedAt = Date.now();
|
|
832
|
+
return { ...attempt };
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
async recordResetAttempt(id, redeemRequestId, credit, now) {
|
|
836
|
+
await this.mutate((aggregate) => {
|
|
837
|
+
const account = aggregate.accounts.find((item) => item.id === id);
|
|
838
|
+
if (!account)
|
|
839
|
+
throw new AccountControlError("not_found");
|
|
840
|
+
const attempts = account.autoResetAttempts ?? [];
|
|
841
|
+
const pending = attempts.find((item) => !item.completed);
|
|
842
|
+
if (pending) {
|
|
843
|
+
if (pending.redeemRequestId !== redeemRequestId || pending.creditId !== credit.id)
|
|
844
|
+
throw new AccountControlError("version_conflict");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
account.autoResetAttempts = attempts.filter((item) => item.creditId !== credit.id && (item.expiresAt === undefined || item.expiresAt > now));
|
|
848
|
+
if (account.autoResetAttempts.length >= MAX_AUTO_RESET_ATTEMPTS)
|
|
849
|
+
throw new AccountControlError("invalid_input");
|
|
850
|
+
account.autoResetAttempts.push({ creditId: credit.id, redeemRequestId, expiresAt: credit.expiresAt, completed: false });
|
|
851
|
+
account.updatedAt = Date.now();
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
async finishAutoReset(id, redeemRequestId, cancel = false) {
|
|
855
|
+
await this.mutate((aggregate) => {
|
|
856
|
+
const account = aggregate.accounts.find((item) => item.id === id);
|
|
857
|
+
if (!account)
|
|
858
|
+
throw new AccountControlError("not_found");
|
|
859
|
+
if (cancel)
|
|
860
|
+
account.autoResetAttempts = account.autoResetAttempts?.filter((item) => item.redeemRequestId !== redeemRequestId);
|
|
861
|
+
else {
|
|
862
|
+
const attempt = account.autoResetAttempts?.find((item) => item.redeemRequestId === redeemRequestId);
|
|
863
|
+
if (attempt)
|
|
864
|
+
attempt.completed = true;
|
|
865
|
+
}
|
|
866
|
+
account.updatedAt = Date.now();
|
|
867
|
+
});
|
|
868
|
+
}
|
|
792
869
|
async enable(id) {
|
|
793
870
|
return this.mutate((aggregate) => {
|
|
794
871
|
const account = aggregate.accounts.find((item) => item.id === id);
|
|
@@ -1602,6 +1679,9 @@ class UsageService {
|
|
|
1602
1679
|
resetKey(redeemRequestId, creditId) {
|
|
1603
1680
|
return `${redeemRequestId}\x00${creditId}`;
|
|
1604
1681
|
}
|
|
1682
|
+
restorePendingReset(accountRef, redeemRequestId, creditId) {
|
|
1683
|
+
this.unknownReset.set(accountRef, this.resetKey(redeemRequestId, creditId));
|
|
1684
|
+
}
|
|
1605
1685
|
async runReset(accountRef, lease, key, body) {
|
|
1606
1686
|
let outcome;
|
|
1607
1687
|
let windowsReset = 0;
|
|
@@ -1628,7 +1708,7 @@ class UsageService {
|
|
|
1628
1708
|
} catch {}
|
|
1629
1709
|
return { outcome, windowsReset, ...usage === undefined ? {} : { usage } };
|
|
1630
1710
|
}
|
|
1631
|
-
async consume(accountRef, redeemRequestId, creditId, signal) {
|
|
1711
|
+
async consume(accountRef, redeemRequestId, creditId, signal, options = {}) {
|
|
1632
1712
|
const key = this.resetKey(redeemRequestId, creditId);
|
|
1633
1713
|
const active = this.resets.get(accountRef);
|
|
1634
1714
|
if (active !== undefined) {
|
|
@@ -1660,6 +1740,9 @@ class UsageService {
|
|
|
1660
1740
|
if (current.resetCredits.availableCount <= 0 || selected?.status !== "available")
|
|
1661
1741
|
throw new UsageError("credit_unavailable");
|
|
1662
1742
|
lease = await this.deps.credential(accountRef, this.deps.hostSignal);
|
|
1743
|
+
if (options.canConsume && !await options.canConsume(selected))
|
|
1744
|
+
throw new UsageError("credit_unavailable");
|
|
1745
|
+
await options.beforeConsume?.(selected);
|
|
1663
1746
|
}
|
|
1664
1747
|
this.invalidate(accountRef);
|
|
1665
1748
|
resolveOperation(await this.runReset(accountRef, lease, key, { redeem_request_id: redeemRequestId, credit_id: creditId }));
|
|
@@ -1684,6 +1767,193 @@ class UsageService {
|
|
|
1684
1767
|
}
|
|
1685
1768
|
}
|
|
1686
1769
|
|
|
1770
|
+
// plugins/chatgpt-oauth/server/auto-reset.ts
|
|
1771
|
+
var AUTO_RESET_LEAD_MS = 30 * 60 * 1000;
|
|
1772
|
+
var AUTO_RESET_INTERVAL_MS = 5 * 60 * 1000;
|
|
1773
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
1774
|
+
var defaultTimer = {
|
|
1775
|
+
schedule: (callback, delay) => {
|
|
1776
|
+
const timer = setTimeout(() => {
|
|
1777
|
+
callback();
|
|
1778
|
+
}, delay);
|
|
1779
|
+
timer.unref?.();
|
|
1780
|
+
return timer;
|
|
1781
|
+
},
|
|
1782
|
+
cancel: (handle) => clearTimeout(handle)
|
|
1783
|
+
};
|
|
1784
|
+
function isAutoResetDue(credit, now) {
|
|
1785
|
+
return credit.status === "available" && typeof credit.expiresAt === "number" && Number.isFinite(credit.expiresAt) && credit.expiresAt > now && credit.expiresAt - now <= AUTO_RESET_LEAD_MS;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
class AutoResetScheduler {
|
|
1789
|
+
accounts;
|
|
1790
|
+
usage;
|
|
1791
|
+
signal;
|
|
1792
|
+
now;
|
|
1793
|
+
timer;
|
|
1794
|
+
handle;
|
|
1795
|
+
started = false;
|
|
1796
|
+
disposed = false;
|
|
1797
|
+
running;
|
|
1798
|
+
plans = new Map;
|
|
1799
|
+
accountRuns = new Map;
|
|
1800
|
+
constructor(accounts, usage, signal, now = Date.now, timer = defaultTimer) {
|
|
1801
|
+
this.accounts = accounts;
|
|
1802
|
+
this.usage = usage;
|
|
1803
|
+
this.signal = signal;
|
|
1804
|
+
this.now = now;
|
|
1805
|
+
this.timer = timer;
|
|
1806
|
+
}
|
|
1807
|
+
start() {
|
|
1808
|
+
if (this.started || this.disposed || this.signal.aborted)
|
|
1809
|
+
return;
|
|
1810
|
+
this.started = true;
|
|
1811
|
+
this.schedule(0);
|
|
1812
|
+
}
|
|
1813
|
+
schedule(delay) {
|
|
1814
|
+
if (!this.started || this.disposed || this.signal.aborted)
|
|
1815
|
+
return;
|
|
1816
|
+
this.handle = this.timer.schedule(async () => {
|
|
1817
|
+
try {
|
|
1818
|
+
await this.runOnce();
|
|
1819
|
+
} catch {} finally {
|
|
1820
|
+
this.schedule(AUTO_RESET_INTERVAL_MS);
|
|
1821
|
+
}
|
|
1822
|
+
}, delay);
|
|
1823
|
+
}
|
|
1824
|
+
runOnce() {
|
|
1825
|
+
if (this.disposed || this.signal.aborted)
|
|
1826
|
+
return Promise.resolve();
|
|
1827
|
+
if (!this.running)
|
|
1828
|
+
this.running = this.scan().finally(() => {
|
|
1829
|
+
this.running = undefined;
|
|
1830
|
+
});
|
|
1831
|
+
return this.running;
|
|
1832
|
+
}
|
|
1833
|
+
async scan() {
|
|
1834
|
+
const accounts = await this.accounts.list();
|
|
1835
|
+
const eligible = new Set(accounts.filter((account) => account.available && account.autoResetCredits && (!account.pendingAutoReset || this.accountRuns.has(account.id))).map((account) => account.id));
|
|
1836
|
+
for (const id of this.plans.keys())
|
|
1837
|
+
if (!eligible.has(id))
|
|
1838
|
+
this.cancelPlan(id);
|
|
1839
|
+
for (const account of accounts) {
|
|
1840
|
+
if (this.disposed || this.signal.aborted)
|
|
1841
|
+
return;
|
|
1842
|
+
if (!eligible.has(account.id))
|
|
1843
|
+
continue;
|
|
1844
|
+
if (this.accountRuns.has(account.id))
|
|
1845
|
+
continue;
|
|
1846
|
+
try {
|
|
1847
|
+
const snapshot = await this.usage.get(account.id, this.signal, { forceFresh: true });
|
|
1848
|
+
if (this.disposed || this.signal.aborted)
|
|
1849
|
+
return;
|
|
1850
|
+
if (snapshot.resetCredits.state !== "fresh")
|
|
1851
|
+
continue;
|
|
1852
|
+
this.cancelPlan(account.id);
|
|
1853
|
+
if ((snapshot.resetCredits.availableCount ?? 0) <= 0)
|
|
1854
|
+
continue;
|
|
1855
|
+
const credits = [...snapshot.resetCredits.credits ?? []].filter((credit) => credit.status === "available" && typeof credit.expiresAt === "number" && Number.isFinite(credit.expiresAt) && credit.expiresAt > this.now()).sort((a, b) => a.expiresAt - b.expiresAt);
|
|
1856
|
+
const plan = { credits };
|
|
1857
|
+
this.plans.set(account.id, plan);
|
|
1858
|
+
await this.executePlan(account.id, plan);
|
|
1859
|
+
} catch {}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
cancelPlan(accountRef, expected) {
|
|
1863
|
+
const plan = this.plans.get(accountRef);
|
|
1864
|
+
if (expected && plan !== expected)
|
|
1865
|
+
return;
|
|
1866
|
+
if (plan?.handle !== undefined)
|
|
1867
|
+
this.timer.cancel(plan.handle);
|
|
1868
|
+
this.plans.delete(accountRef);
|
|
1869
|
+
}
|
|
1870
|
+
armPlan(accountRef, plan) {
|
|
1871
|
+
if (this.disposed || this.signal.aborted || this.plans.get(accountRef) !== plan)
|
|
1872
|
+
return;
|
|
1873
|
+
if (plan.credits.length === 0) {
|
|
1874
|
+
this.cancelPlan(accountRef);
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
const dueAt = plan.credits[0].expiresAt - AUTO_RESET_LEAD_MS;
|
|
1878
|
+
plan.handle = this.timer.schedule(() => this.executePlan(accountRef, plan), Math.min(MAX_TIMER_DELAY_MS, Math.max(0, dueAt - this.now())));
|
|
1879
|
+
}
|
|
1880
|
+
executePlan(accountRef, plan) {
|
|
1881
|
+
const previous = this.accountRuns.get(accountRef) ?? Promise.resolve();
|
|
1882
|
+
const operation = previous.catch(() => {
|
|
1883
|
+
return;
|
|
1884
|
+
}).then(async () => {
|
|
1885
|
+
if (this.disposed || this.signal.aborted || this.plans.get(accountRef) !== plan)
|
|
1886
|
+
return;
|
|
1887
|
+
try {
|
|
1888
|
+
const account = accountListItem(await this.accounts.get(accountRef));
|
|
1889
|
+
if (!account.available || !account.autoResetCredits || account.pendingAutoReset) {
|
|
1890
|
+
this.cancelPlan(accountRef, plan);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
while (this.plans.get(accountRef) === plan && plan.credits.length > 0) {
|
|
1894
|
+
if (this.disposed || this.signal.aborted)
|
|
1895
|
+
return;
|
|
1896
|
+
const credit = plan.credits[0];
|
|
1897
|
+
if (credit.expiresAt - AUTO_RESET_LEAD_MS > this.now())
|
|
1898
|
+
break;
|
|
1899
|
+
plan.credits.shift();
|
|
1900
|
+
if (!isAutoResetDue(credit, this.now()))
|
|
1901
|
+
continue;
|
|
1902
|
+
const attempt = await this.accounts.claimAutoReset(accountRef, credit, this.now());
|
|
1903
|
+
if (!attempt)
|
|
1904
|
+
continue;
|
|
1905
|
+
let deferred;
|
|
1906
|
+
try {
|
|
1907
|
+
const result = await this.usage.consume(accountRef, attempt.redeemRequestId, credit.id, this.signal, {
|
|
1908
|
+
canConsume: async (selected) => {
|
|
1909
|
+
const latest = await this.accounts.get(accountRef);
|
|
1910
|
+
if (this.disposed || this.signal.aborted || latest.autoResetCredits !== true || latest.status !== "active")
|
|
1911
|
+
return false;
|
|
1912
|
+
if (selected.status === "available" && typeof selected.expiresAt === "number" && Number.isFinite(selected.expiresAt) && selected.expiresAt - AUTO_RESET_LEAD_MS > this.now())
|
|
1913
|
+
deferred = selected;
|
|
1914
|
+
return isAutoResetDue(selected, this.now());
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
if (result.outcome === "reset_outcome_unknown") {
|
|
1918
|
+
this.cancelPlan(accountRef, plan);
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
await this.accounts.finishAutoReset(accountRef, attempt.redeemRequestId);
|
|
1922
|
+
} catch (error) {
|
|
1923
|
+
if (error instanceof UsageError && ["credits_unavailable", "credit_unavailable", "reset_in_progress"].includes(error.code)) {
|
|
1924
|
+
await this.accounts.finishAutoReset(accountRef, attempt.redeemRequestId, true);
|
|
1925
|
+
if (error.code === "credit_unavailable") {
|
|
1926
|
+
if (deferred) {
|
|
1927
|
+
plan.credits.push(deferred);
|
|
1928
|
+
plan.credits.sort((a, b) => a.expiresAt - b.expiresAt);
|
|
1929
|
+
}
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
this.cancelPlan(accountRef, plan);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
this.armPlan(accountRef, plan);
|
|
1938
|
+
} catch {
|
|
1939
|
+
this.cancelPlan(accountRef, plan);
|
|
1940
|
+
}
|
|
1941
|
+
}).finally(() => {
|
|
1942
|
+
if (this.accountRuns.get(accountRef) === operation)
|
|
1943
|
+
this.accountRuns.delete(accountRef);
|
|
1944
|
+
});
|
|
1945
|
+
this.accountRuns.set(accountRef, operation);
|
|
1946
|
+
return operation;
|
|
1947
|
+
}
|
|
1948
|
+
dispose() {
|
|
1949
|
+
this.started = false;
|
|
1950
|
+
this.disposed = true;
|
|
1951
|
+
this.timer.cancel(this.handle);
|
|
1952
|
+
for (const id of this.plans.keys())
|
|
1953
|
+
this.cancelPlan(id);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1687
1957
|
// plugins/chatgpt-oauth/server/control.ts
|
|
1688
1958
|
var CHATGPT_TARGET = "https://chatgpt.com";
|
|
1689
1959
|
var CONTROL_HOST_CONTRACT_GAP = "ControlRpcContext.binding.bindingOptions.accountRef is required and host-resolved; RPC payload is never used as a fallback.";
|
|
@@ -1900,6 +2170,7 @@ class ChatgptControl {
|
|
|
1900
2170
|
refreshControllers = new Map;
|
|
1901
2171
|
apiLifetime = new AbortController;
|
|
1902
2172
|
usage;
|
|
2173
|
+
autoReset;
|
|
1903
2174
|
disposed = false;
|
|
1904
2175
|
abortListener;
|
|
1905
2176
|
constructor(host, deps = {}) {
|
|
@@ -1912,10 +2183,11 @@ class ChatgptControl {
|
|
|
1912
2183
|
fetchImpl: deps.fetchImpl,
|
|
1913
2184
|
now: this.now,
|
|
1914
2185
|
timeoutMs: deps.timeoutMs,
|
|
1915
|
-
hostSignal:
|
|
2186
|
+
hostSignal: this.apiLifetime.signal,
|
|
1916
2187
|
credential: (id, signal) => this.credential(id, signal),
|
|
1917
2188
|
rejectAccess: (id, version) => this.accounts.rejectAccess(id, version)
|
|
1918
2189
|
});
|
|
2190
|
+
this.autoReset = new AutoResetScheduler(this.accounts, this.usage, this.apiLifetime.signal, this.now, deps.autoResetTimer);
|
|
1919
2191
|
this.abortListener = () => {
|
|
1920
2192
|
this.dispose();
|
|
1921
2193
|
};
|
|
@@ -1936,6 +2208,7 @@ class ChatgptControl {
|
|
|
1936
2208
|
this.assertAlive();
|
|
1937
2209
|
await this.accounts.read();
|
|
1938
2210
|
this.assertAlive();
|
|
2211
|
+
this.autoReset.start();
|
|
1939
2212
|
}
|
|
1940
2213
|
async refresh(id, signal) {
|
|
1941
2214
|
for (let attempt = 0;attempt < 120; attempt++) {
|
|
@@ -2080,7 +2353,25 @@ class ChatgptControl {
|
|
|
2080
2353
|
const creditId = requiredText(body.creditId, 512);
|
|
2081
2354
|
if (/[\0\r\n]/.test(creditId))
|
|
2082
2355
|
throw new ControlError("invalid_input");
|
|
2083
|
-
|
|
2356
|
+
const pending = accountListItem(await this.accounts.get(accountRef)).pendingAutoReset;
|
|
2357
|
+
if (pending) {
|
|
2358
|
+
if (pending.redeemRequestId !== redeemRequestId || pending.creditId !== creditId)
|
|
2359
|
+
throw new ControlError("reset_in_progress");
|
|
2360
|
+
this.usage.restorePendingReset(accountRef, redeemRequestId, creditId);
|
|
2361
|
+
}
|
|
2362
|
+
const result = await this.usage.consume(accountRef, redeemRequestId, creditId, context.requestSignal, {
|
|
2363
|
+
beforeConsume: (selected) => this.accounts.recordResetAttempt(accountRef, redeemRequestId, selected, this.now())
|
|
2364
|
+
});
|
|
2365
|
+
if (result.outcome !== "reset_outcome_unknown")
|
|
2366
|
+
await this.accounts.finishAutoReset(accountRef, redeemRequestId);
|
|
2367
|
+
return jsonResponse(result);
|
|
2368
|
+
}) },
|
|
2369
|
+
{ path: "/accounts/auto-reset", methods: ["POST"], handler: "setAutoResetCredits", invoke: invoke(async (context) => {
|
|
2370
|
+
const body = await this.readJson(context);
|
|
2371
|
+
exactKeys(body, ["accountRef", "enabled"]);
|
|
2372
|
+
if (typeof body.enabled !== "boolean")
|
|
2373
|
+
throw new ControlError("invalid_input");
|
|
2374
|
+
return jsonResponse({ account: accountListItem(await this.accounts.setAutoResetCredits(requiredText(body.accountRef, 128), body.enabled)) });
|
|
2084
2375
|
}) },
|
|
2085
2376
|
{ path: "/accounts/draft", methods: ["POST"], handler: "createDraft", invoke: invoke(async (context) => {
|
|
2086
2377
|
const body = await this.readJson(context);
|
|
@@ -2203,6 +2494,7 @@ class ChatgptControl {
|
|
|
2203
2494
|
for (const controller of this.refreshControllers.values())
|
|
2204
2495
|
controller.abort("disposed");
|
|
2205
2496
|
this.sessions.dispose();
|
|
2497
|
+
this.autoReset.dispose();
|
|
2206
2498
|
this.usage.dispose();
|
|
2207
2499
|
this.inFlight.clear();
|
|
2208
2500
|
}
|
|
@@ -2214,6 +2506,7 @@ var api = Object.freeze([
|
|
|
2214
2506
|
{ path: "/accounts", methods: ["GET"], handler: "listAccounts" },
|
|
2215
2507
|
{ path: "/accounts/usage", methods: ["GET"], handler: "getAccountUsage" },
|
|
2216
2508
|
{ path: "/accounts/usage/reset", methods: ["POST"], handler: "resetAccountUsage" },
|
|
2509
|
+
{ path: "/accounts/auto-reset", methods: ["POST"], handler: "setAutoResetCredits" },
|
|
2217
2510
|
{ path: "/accounts/draft", methods: ["POST"], handler: "createDraft" },
|
|
2218
2511
|
{ path: "/login/device", methods: ["POST"], handler: "startDeviceLogin" },
|
|
2219
2512
|
{ path: "/login/pkce", methods: ["POST"], handler: "startPkceLogin" },
|
|
@@ -2242,4 +2535,4 @@ export {
|
|
|
2242
2535
|
rpc
|
|
2243
2536
|
};
|
|
2244
2537
|
|
|
2245
|
-
//# debugId=
|
|
2538
|
+
//# debugId=8E7B0629542A407F64756E2164756E21
|