fix: respect auth cooldown with auth.order
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
- Commands: accept /models as an alias for /model.
|
- Commands: accept /models as an alias for /model.
|
||||||
- Debugging: add raw model stream logging flags and document gateway watch mode.
|
- Debugging: add raw model stream logging flags and document gateway watch mode.
|
||||||
- Agent: add claude-cli/opus-4.5 runner via Claude CLI with resume support (tools disabled).
|
- Agent: add claude-cli/opus-4.5 runner via Claude CLI with resume support (tools disabled).
|
||||||
|
- Auth: respect cooldown tracking even with explicit `auth.order` (avoid repeatedly trying known-bad keys). — thanks @steipete
|
||||||
- CLI: move `clawdbot message` to subcommands (`message send|poll|…`), fold Discord/Slack/Telegram/WhatsApp tools into `message`, and require `--provider` unless only one provider is configured.
|
- CLI: move `clawdbot message` to subcommands (`message send|poll|…`), fold Discord/Slack/Telegram/WhatsApp tools into `message`, and require `--provider` unless only one provider is configured.
|
||||||
- CLI: improve `logs` output (pretty/plain/JSONL), add gateway unreachable hint, and document logging.
|
- CLI: improve `logs` output (pretty/plain/JSONL), add gateway unreachable hint, and document logging.
|
||||||
- WhatsApp: route queued replies to the original sender instead of the bot's own number. (#534) — thanks @mcinteerj
|
- WhatsApp: route queued replies to the original sender instead of the bot's own number. (#534) — thanks @mcinteerj
|
||||||
|
|||||||
@@ -130,6 +130,27 @@ describe("resolveAuthProfileOrder", () => {
|
|||||||
expect(order).toEqual(["anthropic:work", "anthropic:default"]);
|
expect(order).toEqual(["anthropic:work", "anthropic:default"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("pushes cooldown profiles to the end even with configured order", () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const order = resolveAuthProfileOrder({
|
||||||
|
cfg: {
|
||||||
|
auth: {
|
||||||
|
order: { anthropic: ["anthropic:default", "anthropic:work"] },
|
||||||
|
profiles: cfg.auth.profiles,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
store: {
|
||||||
|
...store,
|
||||||
|
usageStats: {
|
||||||
|
"anthropic:default": { cooldownUntil: now + 60_000 },
|
||||||
|
"anthropic:work": { lastUsed: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provider: "anthropic",
|
||||||
|
});
|
||||||
|
expect(order).toEqual(["anthropic:work", "anthropic:default"]);
|
||||||
|
});
|
||||||
|
|
||||||
it("normalizes z.ai aliases in auth.order", () => {
|
it("normalizes z.ai aliases in auth.order", () => {
|
||||||
const order = resolveAuthProfileOrder({
|
const order = resolveAuthProfileOrder({
|
||||||
cfg: {
|
cfg: {
|
||||||
|
|||||||
@@ -897,14 +897,37 @@ export function resolveAuthProfileOrder(params: {
|
|||||||
|
|
||||||
// If user specified explicit order in config, respect it exactly
|
// If user specified explicit order in config, respect it exactly
|
||||||
if (configuredOrder && configuredOrder.length > 0) {
|
if (configuredOrder && configuredOrder.length > 0) {
|
||||||
// Still put preferredProfile first if specified
|
// ...but still respect cooldown tracking to avoid repeatedly selecting a
|
||||||
if (preferredProfile && deduped.includes(preferredProfile)) {
|
// known-bad/rate-limited key as the first candidate.
|
||||||
return [
|
const now = Date.now();
|
||||||
preferredProfile,
|
const available: string[] = [];
|
||||||
...deduped.filter((e) => e !== preferredProfile),
|
const inCooldown: Array<{ profileId: string; cooldownUntil: number }> = [];
|
||||||
];
|
|
||||||
|
for (const profileId of deduped) {
|
||||||
|
const cooldownUntil = store.usageStats?.[profileId]?.cooldownUntil;
|
||||||
|
if (
|
||||||
|
typeof cooldownUntil === "number" &&
|
||||||
|
Number.isFinite(cooldownUntil) &&
|
||||||
|
cooldownUntil > 0 &&
|
||||||
|
now < cooldownUntil
|
||||||
|
) {
|
||||||
|
inCooldown.push({ profileId, cooldownUntil });
|
||||||
|
} else {
|
||||||
|
available.push(profileId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return deduped;
|
|
||||||
|
const cooldownSorted = inCooldown
|
||||||
|
.sort((a, b) => a.cooldownUntil - b.cooldownUntil)
|
||||||
|
.map((entry) => entry.profileId);
|
||||||
|
|
||||||
|
const ordered = [...available, ...cooldownSorted];
|
||||||
|
|
||||||
|
// Still put preferredProfile first if specified
|
||||||
|
if (preferredProfile && ordered.includes(preferredProfile)) {
|
||||||
|
return [preferredProfile, ...ordered.filter((e) => e !== preferredProfile)];
|
||||||
|
}
|
||||||
|
return ordered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, use round-robin: sort by lastUsed (oldest first)
|
// Otherwise, use round-robin: sort by lastUsed (oldest first)
|
||||||
|
|||||||
Reference in New Issue
Block a user