dsh-agy-link 0.4.14 → 0.4.15
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/CHANGELOG.md +7 -0
- package/dist/index.js +29 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.15 (2026-08-24)
|
|
4
|
+
|
|
5
|
+
- **Quota Polling Risk-Exposure Minimization (root-cause follow-up).**
|
|
6
|
+
- **Poll Interval 5min → 15min (configurable)**: New `quotaPollIntervalMs` config (env `DSH_AGY_QUOTA_POLL_INTERVAL_MS`, clamped to >= 60s) cuts background `v1internal` polling volume by 3x — the poller was the last remaining high-frequency network surface after the CLI-level hardening.
|
|
7
|
+
- **Restricted-Account Poll Gating**: Automatic polling now skips disabled, auth-quarantined (`invalid_grant`) and 429-cooldown accounts (`shouldPollAccount`), so the poller never keeps probing Google endpoints for accounts already known to be limited. Manual UI force-refresh still refreshes everything.
|
|
8
|
+
- **Userinfo Endpoint Called Only When Email Unknown**: The primary account previously hit the OAuth userinfo endpoint on every poll cycle just to detect account switching; that detection now rides the local log scan (`detectEmailFromAgyLogs`, zero network), eliminating one network call per cycle.
|
|
9
|
+
|
|
3
10
|
## 0.4.14 (2026-08-24)
|
|
4
11
|
|
|
5
12
|
- **Hardened 429 Rate Limit Cooldown & Circuit Breaker (Anti-Risk Control).**
|
package/dist/index.js
CHANGED
|
@@ -90,7 +90,8 @@ function defaultConfig() {
|
|
|
90
90
|
rateLimitPerMinute: 0,
|
|
91
91
|
autoFallbackModel: false,
|
|
92
92
|
logRetentionDays: 7,
|
|
93
|
-
disableTelemetry: true
|
|
93
|
+
disableTelemetry: true,
|
|
94
|
+
quotaPollIntervalMs: 9e5
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
97
|
const Err = {
|
|
@@ -225,7 +226,8 @@ function resolveConfig(entry, env = process.env, overrides = readOverrides()) {
|
|
|
225
226
|
rateLimitPerMinute: asNum(get("rateLimitPerMinute")) ?? base.rateLimitPerMinute,
|
|
226
227
|
autoFallbackModel: asBool(get("autoFallbackModel")) ?? base.autoFallbackModel,
|
|
227
228
|
logRetentionDays: asNum(get("logRetentionDays")) ?? base.logRetentionDays,
|
|
228
|
-
disableTelemetry: asBool(get("disableTelemetry")) ?? base.disableTelemetry
|
|
229
|
+
disableTelemetry: asBool(get("disableTelemetry")) ?? base.disableTelemetry,
|
|
230
|
+
quotaPollIntervalMs: asNum(get("quotaPollIntervalMs")) ?? base.quotaPollIntervalMs
|
|
229
231
|
};
|
|
230
232
|
if (env.DSH_AGY_ENABLED !== void 0) cfg.enabled = asBool(env.DSH_AGY_ENABLED) ?? cfg.enabled;
|
|
231
233
|
if (env.DSH_AGY_BIN) cfg.agyBin = env.DSH_AGY_BIN;
|
|
@@ -271,6 +273,10 @@ function resolveConfig(entry, env = process.env, overrides = readOverrides()) {
|
|
|
271
273
|
const dt = asBool(env.DSH_AGY_DISABLE_TELEMETRY);
|
|
272
274
|
if (dt !== void 0) cfg.disableTelemetry = dt;
|
|
273
275
|
}
|
|
276
|
+
if (env.DSH_AGY_QUOTA_POLL_INTERVAL_MS) {
|
|
277
|
+
const q = asNum(env.DSH_AGY_QUOTA_POLL_INTERVAL_MS);
|
|
278
|
+
if (q && q >= 6e4) cfg.quotaPollIntervalMs = q;
|
|
279
|
+
}
|
|
274
280
|
return cfg;
|
|
275
281
|
}
|
|
276
282
|
//#endregion
|
|
@@ -284,6 +290,20 @@ function modelFamilyOf(modelId) {
|
|
|
284
290
|
if (id.startsWith("gpt-") || id.startsWith("openai/") || id.includes("gpt-oss")) return "openai";
|
|
285
291
|
return "unknown";
|
|
286
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Whether the background quota poller should touch this account at all.
|
|
295
|
+
* Disabled, auth-quarantined and cooldown accounts are skipped so automatic
|
|
296
|
+
* polling never hammers Google endpoints for accounts already known to be
|
|
297
|
+
* restricted (risk-control exposure minimization). Manual force refresh
|
|
298
|
+
* from the UI bypasses this gate.
|
|
299
|
+
*/
|
|
300
|
+
function shouldPollAccount(account) {
|
|
301
|
+
if (!account.enabled) return false;
|
|
302
|
+
if (account.authRequired) return false;
|
|
303
|
+
const now = Date.now();
|
|
304
|
+
for (const cd of Object.values(account.cooldowns)) if (cd && cd.cooldownUntil > now) return false;
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
287
307
|
function defaultPoolData() {
|
|
288
308
|
return {
|
|
289
309
|
version: 1,
|
|
@@ -27888,7 +27908,7 @@ var QuotaService = class {
|
|
|
27888
27908
|
return null;
|
|
27889
27909
|
}
|
|
27890
27910
|
const [summary, discovered] = await Promise.all([this.fetchQuotaSummary(accessToken, account.proxyUrl), this.fetchAvailableModels(accessToken, account.proxyUrl)]);
|
|
27891
|
-
if (
|
|
27911
|
+
if (!email) {
|
|
27892
27912
|
const info = await this.fetchUserInfo(accessToken, account.proxyUrl);
|
|
27893
27913
|
if (info?.email) email = info.email;
|
|
27894
27914
|
}
|
|
@@ -27964,9 +27984,13 @@ var QuotaService = class {
|
|
|
27964
27984
|
}
|
|
27965
27985
|
/**
|
|
27966
27986
|
* Refresh quota statistics for all accounts in the pool.
|
|
27987
|
+
* Automatic polling (force=false) skips restricted accounts (disabled /
|
|
27988
|
+
* auth-quarantined / in cooldown) so the poller never keeps knocking on
|
|
27989
|
+
* Google endpoints for accounts already known to be limited. Manual force
|
|
27990
|
+
* refresh from the UI refreshes everything.
|
|
27967
27991
|
*/
|
|
27968
27992
|
async refreshAllQuotas(force = false) {
|
|
27969
|
-
const accounts = this.pool.getAccounts();
|
|
27993
|
+
const accounts = force ? this.pool.getAccounts() : this.pool.getAccounts().filter(shouldPollAccount);
|
|
27970
27994
|
await Promise.allSettled(accounts.map((acc) => this.refreshAccountQuota(acc, force)));
|
|
27971
27995
|
}
|
|
27972
27996
|
};
|
|
@@ -28830,7 +28854,7 @@ function apply(ctx, entryConfig = {}) {
|
|
|
28830
28854
|
quota.refreshAllQuotas().catch(() => void 0);
|
|
28831
28855
|
};
|
|
28832
28856
|
const boot = setTimeout(refresh, 5e3);
|
|
28833
|
-
const timer = setInterval(refresh,
|
|
28857
|
+
const timer = setInterval(refresh, Math.max(6e4, getConfig().quotaPollIntervalMs));
|
|
28834
28858
|
return () => {
|
|
28835
28859
|
clearTimeout(boot);
|
|
28836
28860
|
clearInterval(timer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agy-link",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.15",
|
|
4
4
|
"description": "Google Antigravity (agy CLI) models for DeepSeek Harness — stream Gemini/Claude/GPT-OSS subscriptions into DSH with thinking, tool activity, token usage and in-GUI Google OAuth login.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|