@cjhyy/code-shell-capability-coding 0.9.5 → 0.9.6
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/quota/index.d.ts +20 -4
- package/dist/quota/index.js +72 -14
- package/dist/quota/types.d.ts +19 -3
- package/dist/tools/check-quota.js +4 -2
- package/package.json +2 -2
package/dist/quota/index.d.ts
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
* rate_limit.{primary_window,secondary_window}.{used_percent,reset_at}.
|
|
7
7
|
* Zero cost (no message sent).
|
|
8
8
|
* - Claude: POST /v1/messages (max_tokens:1) → response headers
|
|
9
|
-
* anthropic-ratelimit-unified
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* anthropic-ratelimit-unified-<window>-{utilization,reset}, where
|
|
10
|
+
* <window> is discovered from the headers (5h / 7d / overage / …)
|
|
11
|
+
* rather than assumed — see parseClaudeWindows (re-verified
|
|
12
|
+
* 2026-09-06). Costs ~1 output token (Claude exposes quota only via
|
|
13
|
+
* response headers — there is no standalone usage endpoint).
|
|
12
14
|
*
|
|
13
15
|
* The `fetch` and credentials are injected so this is unit-testable offline and
|
|
14
16
|
* so the host owns secret resolution (see types.ts boundary note).
|
|
15
17
|
*/
|
|
16
|
-
import type { ProviderQuota, QuotaCredentials, QuotaResult } from "./types.js";
|
|
18
|
+
import type { ProviderQuota, QuotaCredentials, QuotaResult, QuotaWindow } from "./types.js";
|
|
17
19
|
type FetchLike = typeof fetch;
|
|
18
20
|
export interface CheckQuotaOptions {
|
|
19
21
|
creds: QuotaCredentials;
|
|
@@ -29,6 +31,20 @@ export interface CheckQuotaOptions {
|
|
|
29
31
|
export declare function queryCodexQuota(creds: QuotaCredentials, fetchImpl: FetchLike, signal: AbortSignal): Promise<ProviderQuota>;
|
|
30
32
|
/** Claude: POST a 1-token probe and read the unified rate-limit headers. */
|
|
31
33
|
export declare function queryClaudeQuota(creds: QuotaCredentials, fetchImpl: FetchLike, signal: AbortSignal): Promise<ProviderQuota>;
|
|
34
|
+
/**
|
|
35
|
+
* Discover every rate-limit window from the unified headers.
|
|
36
|
+
*
|
|
37
|
+
* Windows are found by PREFIX, not from a hardcoded list, because which ones
|
|
38
|
+
* the API sends depends on the account. A normal subscription reports 5h + 7d;
|
|
39
|
+
* an account on overage reports `overage` and omits 5h/7d entirely. Matching a
|
|
40
|
+
* fixed list is what silently broke this lookup before (see types.ts).
|
|
41
|
+
*
|
|
42
|
+
* Each window contributes `<prefix><name>-utilization` (0–1) and an optional
|
|
43
|
+
* `<prefix><name>-reset` (epoch seconds). Bare `<prefix>reset` / `<prefix>status`
|
|
44
|
+
* are envelope fields, not windows, so anything without a `-utilization` suffix
|
|
45
|
+
* is skipped.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseClaudeWindows(h: Headers): QuotaWindow[];
|
|
32
48
|
/** Query both providers (or the subset requested), concurrently. */
|
|
33
49
|
export declare function checkQuota(opts: CheckQuotaOptions): Promise<QuotaResult>;
|
|
34
50
|
/** Render a QuotaResult as a compact human/agent-readable summary. */
|
package/dist/quota/index.js
CHANGED
|
@@ -101,21 +101,68 @@ export async function queryClaudeQuota(creds, fetchImpl, signal) {
|
|
|
101
101
|
error: `HTTP ${resp.status}${resp.status === 401 ? " (token 可能已过期)" : ""}`,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
-
const
|
|
104
|
+
const windows = parseClaudeWindows(resp.headers);
|
|
105
|
+
if (windows.length === 0)
|
|
106
|
+
return { provider: "claude", error: "响应头无 rate-limit 字段" };
|
|
107
|
+
return { provider: "claude", windows };
|
|
108
|
+
}
|
|
109
|
+
const UNIFIED_PREFIX = "anthropic-ratelimit-unified-";
|
|
110
|
+
/**
|
|
111
|
+
* Map `representative-claim` values onto the window names used in the headers.
|
|
112
|
+
* The claim spells a window out ("five_hour"); the window headers abbreviate it
|
|
113
|
+
* ("5h"). An unlisted claim value falls through to an exact `kind` match, which
|
|
114
|
+
* is how "overage" already lines up.
|
|
115
|
+
*/
|
|
116
|
+
const CLAIM_TO_KIND = {
|
|
117
|
+
five_hour: "5h",
|
|
118
|
+
seven_day: "7d",
|
|
119
|
+
seven_day_sonnet: "7d_sonnet",
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Discover every rate-limit window from the unified headers.
|
|
123
|
+
*
|
|
124
|
+
* Windows are found by PREFIX, not from a hardcoded list, because which ones
|
|
125
|
+
* the API sends depends on the account. A normal subscription reports 5h + 7d;
|
|
126
|
+
* an account on overage reports `overage` and omits 5h/7d entirely. Matching a
|
|
127
|
+
* fixed list is what silently broke this lookup before (see types.ts).
|
|
128
|
+
*
|
|
129
|
+
* Each window contributes `<prefix><name>-utilization` (0–1) and an optional
|
|
130
|
+
* `<prefix><name>-reset` (epoch seconds). Bare `<prefix>reset` / `<prefix>status`
|
|
131
|
+
* are envelope fields, not windows, so anything without a `-utilization` suffix
|
|
132
|
+
* is skipped.
|
|
133
|
+
*/
|
|
134
|
+
export function parseClaudeWindows(h) {
|
|
105
135
|
const windows = [];
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
for (const [rawKey, rawVal] of h.entries()) {
|
|
137
|
+
const key = rawKey.toLowerCase();
|
|
138
|
+
if (!key.startsWith(UNIFIED_PREFIX) || !key.endsWith("-utilization"))
|
|
139
|
+
continue;
|
|
140
|
+
const kind = key.slice(UNIFIED_PREFIX.length, -"-utilization".length);
|
|
141
|
+
if (!kind)
|
|
142
|
+
continue; // guard a bare `<prefix>utilization`
|
|
143
|
+
const util = num(rawVal); // 0–1
|
|
112
144
|
if (util == null)
|
|
113
145
|
continue;
|
|
114
|
-
windows.push({
|
|
146
|
+
windows.push({
|
|
147
|
+
// Round to 4dp: `0.07 * 100` is 7.000000000000001 in binary float, which
|
|
148
|
+
// leaks into equality checks and any raw (unformatted) display.
|
|
149
|
+
kind,
|
|
150
|
+
usedPercent: Math.round(util * 100 * 1e4) / 1e4,
|
|
151
|
+
resetsAt: num(h.get(`${UNIFIED_PREFIX}${kind}-reset`)),
|
|
152
|
+
});
|
|
115
153
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
154
|
+
// Stable order so output does not shuffle between identical probes.
|
|
155
|
+
windows.sort((a, b) => a.kind.localeCompare(b.kind));
|
|
156
|
+
// Flag the binding window. A request is throttled on this one, so it is what
|
|
157
|
+
// an orchestrator should plan against when windows disagree.
|
|
158
|
+
const claim = h.get(`${UNIFIED_PREFIX}representative-claim`)?.trim().toLowerCase();
|
|
159
|
+
if (claim) {
|
|
160
|
+
const want = CLAIM_TO_KIND[claim] ?? claim;
|
|
161
|
+
const hit = windows.find((w) => w.kind === want);
|
|
162
|
+
if (hit)
|
|
163
|
+
hit.representative = true;
|
|
164
|
+
}
|
|
165
|
+
return windows;
|
|
119
166
|
}
|
|
120
167
|
/** Query both providers (or the subset requested), concurrently. */
|
|
121
168
|
export async function checkQuota(opts) {
|
|
@@ -145,17 +192,28 @@ export function formatQuota(result, nowSec) {
|
|
|
145
192
|
const plan = pq.planType ? ` [${pq.planType}]` : "";
|
|
146
193
|
const parts = pq.windows.map((w) => {
|
|
147
194
|
const reset = w.resetsAt != null ? ` (重置 ${formatReset(w.resetsAt - nowSec)})` : "";
|
|
148
|
-
|
|
195
|
+
// Star the binding window so a reader/agent knows which one throttles.
|
|
196
|
+
const star = w.representative ? "*" : "";
|
|
197
|
+
return `${w.kind}${star} 用了 ${w.usedPercent.toFixed(0)}%${reset}`;
|
|
149
198
|
});
|
|
150
199
|
lines.push(`${name}${plan}: ${parts.join(",")}`);
|
|
151
200
|
}
|
|
152
201
|
return lines.length ? lines.join("\n") : "(无可用额度信息)";
|
|
153
202
|
}
|
|
154
|
-
/**
|
|
203
|
+
/**
|
|
204
|
+
* "3d2h" / "2h13m" / "45m" / "已重置" from a seconds delta.
|
|
205
|
+
*
|
|
206
|
+
* The day unit matters: this only ever had to render 5h/7d windows, but an
|
|
207
|
+
* overage window can reset weeks out, and "606h0m 后" is not a readable way to
|
|
208
|
+
* say 25 days.
|
|
209
|
+
*/
|
|
155
210
|
function formatReset(deltaSec) {
|
|
156
211
|
if (deltaSec <= 0)
|
|
157
212
|
return "已重置";
|
|
158
|
-
const
|
|
213
|
+
const d = Math.floor(deltaSec / 86400);
|
|
214
|
+
const h = Math.floor((deltaSec % 86400) / 3600);
|
|
159
215
|
const m = Math.floor((deltaSec % 3600) / 60);
|
|
216
|
+
if (d > 0)
|
|
217
|
+
return `${d}d${h}h 后`;
|
|
160
218
|
return h > 0 ? `${h}h${m}m 后` : `${m}m 后`;
|
|
161
219
|
}
|
package/dist/quota/types.d.ts
CHANGED
|
@@ -10,14 +10,30 @@
|
|
|
10
10
|
* it from the rest of core. Nothing outside this module should know about
|
|
11
11
|
* Keychain / wham endpoints / `anthropic-ratelimit-*` header names.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* A single rolling limit window.
|
|
15
|
+
*
|
|
16
|
+
* `kind` is NOT a closed set. Claude reports whichever windows apply to the
|
|
17
|
+
* account: usually "5h" and "7d", but an account running on overage reports an
|
|
18
|
+
* "overage" window INSTEAD of those (verified 2026-09-06 against a team /
|
|
19
|
+
* default_claude_max_5x account). New window names appear without notice, so
|
|
20
|
+
* the parser discovers them from the header prefix rather than matching a
|
|
21
|
+
* hardcoded list. Renderers must treat `kind` as an opaque label.
|
|
22
|
+
*/
|
|
14
23
|
export interface QuotaWindow {
|
|
15
|
-
/** Which window this is. */
|
|
16
|
-
kind:
|
|
24
|
+
/** Which window this is: "5h" | "7d" | "overage" | any future name. */
|
|
25
|
+
kind: string;
|
|
17
26
|
/** Percent of the window's limit already used, 0–100. */
|
|
18
27
|
usedPercent: number;
|
|
19
28
|
/** Unix epoch seconds when this window resets, or null if unknown. */
|
|
20
29
|
resetsAt: number | null;
|
|
30
|
+
/**
|
|
31
|
+
* True for the window the API named as the binding constraint via
|
|
32
|
+
* `anthropic-ratelimit-unified-representative-claim`. Claude only; a request
|
|
33
|
+
* is throttled on THIS window, so it is the one to act on when several
|
|
34
|
+
* windows disagree.
|
|
35
|
+
*/
|
|
36
|
+
representative?: boolean;
|
|
21
37
|
}
|
|
22
38
|
/** Quota for one provider (claude | codex). */
|
|
23
39
|
export interface ProviderQuota {
|
|
@@ -3,9 +3,11 @@ import { resolveQuotaCredentials } from "../quota/credentials.js";
|
|
|
3
3
|
export const checkQuotaToolDef = {
|
|
4
4
|
name: "CheckQuota",
|
|
5
5
|
description: "Check remaining usage/rate-limit quota for the external coding-agent CLIs (Claude Code and/or " +
|
|
6
|
-
"Codex) — the same
|
|
6
|
+
"Codex) — the same subscription windows their status lines show. Use before or during " +
|
|
7
7
|
"orchestration (DriveAgent) to plan how much work to hand off, whether to wait for a reset, or " +
|
|
8
|
-
"which provider to use. Returns each
|
|
8
|
+
"which provider to use. Returns each window's used-% and reset time. Codex reports 5h/7d; " +
|
|
9
|
+
"Claude reports whichever windows apply to the account (5h/7d normally, 'overage' when the " +
|
|
10
|
+
"account is running on overage), and marks the currently binding window with '*'. " +
|
|
9
11
|
"COST: 'codex' is free (reads a usage endpoint). 'claude' costs ~1 token (Anthropic exposes " +
|
|
10
12
|
"quota only via a response header, so this sends a 1-token probe). Pass `provider` to query " +
|
|
11
13
|
"just one and avoid the other's cost/latency.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjhyy/code-shell-capability-coding",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Coding capability pack for the generic code-shell agent core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@cjhyy/code-shell-core": "0.9.
|
|
42
|
+
"@cjhyy/code-shell-core": "0.9.6"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=20.10"
|