instar 1.3.431 → 1.3.433
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/dashboard/subscriptions.js +21 -0
- package/dist/core/MessagingToneGate.d.ts +8 -0
- package/dist/core/MessagingToneGate.d.ts.map +1 -1
- package/dist/core/MessagingToneGate.js.map +1 -1
- package/dist/core/OAuthRefresher.d.ts +112 -0
- package/dist/core/OAuthRefresher.d.ts.map +1 -0
- package/dist/core/OAuthRefresher.js +222 -0
- package/dist/core/OAuthRefresher.js.map +1 -0
- package/dist/core/QuotaPoller.d.ts +33 -6
- package/dist/core/QuotaPoller.d.ts.map +1 -1
- package/dist/core/QuotaPoller.js +73 -66
- package/dist/core/QuotaPoller.js.map +1 -1
- package/dist/core/SubscriptionPool.d.ts +8 -0
- package/dist/core/SubscriptionPool.d.ts.map +1 -1
- package/dist/core/SubscriptionPool.js +3 -0
- package/dist/core/SubscriptionPool.js.map +1 -1
- package/dist/core/types.d.ts +9 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/server/middleware.d.ts +20 -0
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +20 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/outboundGateBudget.d.ts +33 -0
- package/dist/server/outboundGateBudget.d.ts.map +1 -0
- package/dist/server/outboundGateBudget.js +57 -0
- package/dist/server/outboundGateBudget.js.map +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +23 -5
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.432.md +27 -0
- package/upgrades/1.3.433.md +52 -0
- package/upgrades/side-effects/outbound-gate-budget.md +109 -0
- package/upgrades/side-effects/subscription-token-autorefresh.md +29 -0
|
@@ -90,6 +90,20 @@ export function countdown(iso, now = Date.now(), { expiredWord = 'expired' } = {
|
|
|
90
90
|
return `${sec}s`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/** A coarse "N ago" for a PAST ISO timestamp (token-refresh recency). '' if invalid. */
|
|
94
|
+
export function relativeAge(iso, now = Date.now()) {
|
|
95
|
+
const t = typeof iso === 'string' ? Date.parse(iso) : NaN;
|
|
96
|
+
if (Number.isNaN(t)) return '';
|
|
97
|
+
const sec = Math.floor((now - t) / 1000);
|
|
98
|
+
if (sec < 0) return 'just now';
|
|
99
|
+
if (sec < 60) return 'just now';
|
|
100
|
+
const min = Math.floor(sec / 60);
|
|
101
|
+
if (min < 60) return `${min}m ago`;
|
|
102
|
+
const hr = Math.floor(min / 60);
|
|
103
|
+
if (hr < 24) return `${hr}h ago`;
|
|
104
|
+
return `${Math.floor(hr / 24)}d ago`;
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
// ── DOM helpers (textContent ONLY — never innerHTML) ────────────────────────
|
|
94
108
|
function el(doc, tag, cls, text) {
|
|
95
109
|
const node = doc.createElement(tag);
|
|
@@ -142,6 +156,13 @@ export function renderAccounts(doc, target, accounts, now = Date.now()) {
|
|
|
142
156
|
} else {
|
|
143
157
|
card.appendChild(el(doc, 'div', 'sub-account-noquota', 'No quota reading yet.'));
|
|
144
158
|
}
|
|
159
|
+
// Token health: when the poller silently refreshed the access token from the
|
|
160
|
+
// refresh token, show it — so a routine access-token expiry reads as healthy
|
|
161
|
+
// (auto-handled) rather than looking like a re-auth event.
|
|
162
|
+
const refAge = a && a.lastRefreshAt ? relativeAge(a.lastRefreshAt, now) : null;
|
|
163
|
+
if (refAge) {
|
|
164
|
+
card.appendChild(el(doc, 'div', 'sub-account-refresh', `Token auto-refreshed ${refAge}`));
|
|
165
|
+
}
|
|
145
166
|
target.appendChild(card);
|
|
146
167
|
}
|
|
147
168
|
}
|
|
@@ -37,6 +37,14 @@ export interface ToneReviewResult {
|
|
|
37
37
|
failedOpen?: boolean;
|
|
38
38
|
/** True if the LLM's rule citation was invalid (not in B1..B18) — gate failed open. */
|
|
39
39
|
invalidRule?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* True if the gate review exceeded the outbound route's hard budget and the
|
|
42
|
+
* route failed it open without waiting for the verdict. Distinguishes a
|
|
43
|
+
* budget-cap fail-open (gate too slow under load) from an error fail-open
|
|
44
|
+
* (`failedOpen`) in the latency/over-block audit. Set by the route seam, not
|
|
45
|
+
* by `review()` itself.
|
|
46
|
+
*/
|
|
47
|
+
budgetExceeded?: boolean;
|
|
40
48
|
}
|
|
41
49
|
export interface ToneReviewContextMessage {
|
|
42
50
|
role: 'user' | 'agent';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagingToneGate.d.ts","sourceRoot":"","sources":["../../src/core/MessagingToneGate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AASvD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uFAAuF;IACvF,WAAW,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MessagingToneGate.d.ts","sourceRoot":"","sources":["../../src/core/MessagingToneGate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagingToneGate.js","sourceRoot":"","sources":["../../src/core/MessagingToneGate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAGjC;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MessagingToneGate.js","sourceRoot":"","sources":["../../src/core/MessagingToneGate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuthRefresher — mint a fresh Claude Code OAuth access token from its stored
|
|
3
|
+
* refresh token, so the QuotaPoller never falsely flags a still-valid login as
|
|
4
|
+
* `needs-reauth` (P1.2 hardening of the Subscription & Auth Standard).
|
|
5
|
+
*
|
|
6
|
+
* ── Why this exists (the bug it fixes) ──
|
|
7
|
+
* A Claude Code login holds TWO tokens in its config home's credential store:
|
|
8
|
+
* - a short-lived ACCESS token (`sk-ant-oat…`, ~8–12h), and
|
|
9
|
+
* - a long-lived REFRESH token (`sk-ant-ort…`, weeks→months).
|
|
10
|
+
* The `claude` client silently exchanges the refresh token for a new access
|
|
11
|
+
* token on every real use. The QuotaPoller reads the access token out-of-band
|
|
12
|
+
* and calls the usage endpoint directly — so when the access token has expired
|
|
13
|
+
* (which is routine, daily) but the refresh token is still perfectly valid, the
|
|
14
|
+
* usage read returns 401 and the poller wrongly marks the account `needs-reauth`.
|
|
15
|
+
* That cried wolf: the login is intact, only the access token lapsed. This module
|
|
16
|
+
* performs the same refresh-token exchange the client does, so a routine expiry
|
|
17
|
+
* recovers silently and `needs-reauth` is reserved for a genuinely dead login
|
|
18
|
+
* (refresh token revoked / password change) — exactly what SubscriptionPool's
|
|
19
|
+
* status comment already promises.
|
|
20
|
+
*
|
|
21
|
+
* ── Corruption safety (the load-bearing invariant) ──
|
|
22
|
+
* The ONLY way this module could harm a working login is by writing a bad
|
|
23
|
+
* credential back. So the write is gated three ways:
|
|
24
|
+
* 1. it happens ONLY on a fully-validated 200 response (new access token shaped
|
|
25
|
+
* `sk-ant-oat…`, a positive numeric `expires_in`);
|
|
26
|
+
* 2. it is a READ-MERGE-WRITE — the existing credential JSON is re-read and only
|
|
27
|
+
* the access token / refresh token / expiry are overwritten, so scopes,
|
|
28
|
+
* subscriptionType, rateLimitTier and any unknown fields are preserved; and
|
|
29
|
+
* 3. if the server rotates the refresh token, the NEW one is persisted; if the
|
|
30
|
+
* response omits a refresh token (non-rotating server), the existing one is
|
|
31
|
+
* kept — never dropped.
|
|
32
|
+
* A wrong endpoint / client id / network failure can therefore only ever make the
|
|
33
|
+
* exchange FAIL (→ the caller's existing `needs-reauth` path), never corrupt.
|
|
34
|
+
*
|
|
35
|
+
* Token values are NEVER logged or returned to any persisted surface. The OAuth
|
|
36
|
+
* token endpoint + client id are the public Claude Code values, extracted from
|
|
37
|
+
* the official client binary (verified 2026-06-08).
|
|
38
|
+
*
|
|
39
|
+
* Testability: the credential store, the fetch surface, and the clock are all
|
|
40
|
+
* injectable, so the whole refresh runs hermetically with zero keychain, zero
|
|
41
|
+
* network, and a deterministic clock in tests.
|
|
42
|
+
*/
|
|
43
|
+
/** Public Claude Code OAuth token endpoint (from the official client binary). */
|
|
44
|
+
export declare const CLAUDE_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
45
|
+
/** Public Claude Code OAuth client id (from the official client binary). */
|
|
46
|
+
export declare const CLAUDE_CODE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
47
|
+
export type RefreshFailReason = 'unsupported-account' | 'read-failed' | 'no-refresh-token' | 'exchange-failed' | 'malformed-response' | 'write-failed';
|
|
48
|
+
export type RefreshResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
accessToken: string;
|
|
51
|
+
expiresAt: number;
|
|
52
|
+
rotated: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
reason: RefreshFailReason;
|
|
56
|
+
status?: number;
|
|
57
|
+
};
|
|
58
|
+
/** The OAuth access + refresh tokens parsed out of a credential store entry. */
|
|
59
|
+
export interface ClaudeOauth {
|
|
60
|
+
accessToken?: string;
|
|
61
|
+
refreshToken?: string;
|
|
62
|
+
expiresAt?: number;
|
|
63
|
+
[k: string]: unknown;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A credential store for one config home. `read` returns the RAW JSON string of
|
|
67
|
+
* the stored entry (so the refresher can merge-preserve every field); `write`
|
|
68
|
+
* persists a replacement raw JSON string. Injectable so tests use a fake.
|
|
69
|
+
*/
|
|
70
|
+
export interface CredentialStore {
|
|
71
|
+
read(configHome: string): string | null;
|
|
72
|
+
write(configHome: string, rawJson: string): boolean;
|
|
73
|
+
}
|
|
74
|
+
/** POST-capable fetch surface (distinct from QuotaPoller's GET-only FetchImpl). */
|
|
75
|
+
export type RefreshFetch = (url: string, init: {
|
|
76
|
+
method: string;
|
|
77
|
+
headers: Record<string, string>;
|
|
78
|
+
body: string;
|
|
79
|
+
}) => Promise<{
|
|
80
|
+
ok: boolean;
|
|
81
|
+
status: number;
|
|
82
|
+
json: () => Promise<unknown>;
|
|
83
|
+
}>;
|
|
84
|
+
export interface RefreshDeps {
|
|
85
|
+
store?: CredentialStore;
|
|
86
|
+
fetchImpl?: RefreshFetch;
|
|
87
|
+
now?: () => number;
|
|
88
|
+
tokenUrl?: string;
|
|
89
|
+
clientId?: string;
|
|
90
|
+
}
|
|
91
|
+
export declare function expandHome(p: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* macOS keychain service name for a config home's Claude Code credentials.
|
|
94
|
+
* The default home (`~/.claude`) has no hash suffix; every other config home is
|
|
95
|
+
* suffixed with the first 8 hex of sha256(configHome) — verified empirically and
|
|
96
|
+
* matched by the official client.
|
|
97
|
+
*/
|
|
98
|
+
export declare function claudeCredentialService(configHome: string): string;
|
|
99
|
+
/** Non-darwin credential file for a config home. */
|
|
100
|
+
export declare function claudeCredentialFilePath(configHome: string): string;
|
|
101
|
+
/** Default store: macOS keychain, else a per-config-home credentials file. */
|
|
102
|
+
export declare const defaultCredentialStore: CredentialStore;
|
|
103
|
+
/** Parse the `claudeAiOauth` block out of a config home's credential store. */
|
|
104
|
+
export declare function readClaudeOauth(configHome: string, store?: CredentialStore): ClaudeOauth | null;
|
|
105
|
+
/**
|
|
106
|
+
* Refresh a config home's Claude Code access token from its stored refresh token.
|
|
107
|
+
* Returns the new access token + expiry on success; otherwise a typed failure
|
|
108
|
+
* reason the caller maps to its `needs-reauth` decision. Writes NOTHING on any
|
|
109
|
+
* failure — a working login is never put at risk by an unsuccessful refresh.
|
|
110
|
+
*/
|
|
111
|
+
export declare function refreshClaudeToken(configHome: string, deps?: RefreshDeps): Promise<RefreshResult>;
|
|
112
|
+
//# sourceMappingURL=OAuthRefresher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthRefresher.d.ts","sourceRoot":"","sources":["../../src/core/OAuthRefresher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAQH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,+CAA+C,CAAC;AAC7E,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,yCAAyC,CAAC;AAK5E,MAAM,MAAM,iBAAiB,GACzB,qBAAqB,GACrB,aAAa,GACb,kBAAkB,GAClB,iBAAiB,GACjB,oBAAoB,GACpB,cAAc,CAAC;AAEnB,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACxC,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACrD;AAED,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACpE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED,oDAAoD;AACpD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,EAAE,eAoDpC,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,eAAwC,GAC9C,WAAW,GAAG,IAAI,CAUpB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,aAAa,CAAC,CA+ExB"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuthRefresher — mint a fresh Claude Code OAuth access token from its stored
|
|
3
|
+
* refresh token, so the QuotaPoller never falsely flags a still-valid login as
|
|
4
|
+
* `needs-reauth` (P1.2 hardening of the Subscription & Auth Standard).
|
|
5
|
+
*
|
|
6
|
+
* ── Why this exists (the bug it fixes) ──
|
|
7
|
+
* A Claude Code login holds TWO tokens in its config home's credential store:
|
|
8
|
+
* - a short-lived ACCESS token (`sk-ant-oat…`, ~8–12h), and
|
|
9
|
+
* - a long-lived REFRESH token (`sk-ant-ort…`, weeks→months).
|
|
10
|
+
* The `claude` client silently exchanges the refresh token for a new access
|
|
11
|
+
* token on every real use. The QuotaPoller reads the access token out-of-band
|
|
12
|
+
* and calls the usage endpoint directly — so when the access token has expired
|
|
13
|
+
* (which is routine, daily) but the refresh token is still perfectly valid, the
|
|
14
|
+
* usage read returns 401 and the poller wrongly marks the account `needs-reauth`.
|
|
15
|
+
* That cried wolf: the login is intact, only the access token lapsed. This module
|
|
16
|
+
* performs the same refresh-token exchange the client does, so a routine expiry
|
|
17
|
+
* recovers silently and `needs-reauth` is reserved for a genuinely dead login
|
|
18
|
+
* (refresh token revoked / password change) — exactly what SubscriptionPool's
|
|
19
|
+
* status comment already promises.
|
|
20
|
+
*
|
|
21
|
+
* ── Corruption safety (the load-bearing invariant) ──
|
|
22
|
+
* The ONLY way this module could harm a working login is by writing a bad
|
|
23
|
+
* credential back. So the write is gated three ways:
|
|
24
|
+
* 1. it happens ONLY on a fully-validated 200 response (new access token shaped
|
|
25
|
+
* `sk-ant-oat…`, a positive numeric `expires_in`);
|
|
26
|
+
* 2. it is a READ-MERGE-WRITE — the existing credential JSON is re-read and only
|
|
27
|
+
* the access token / refresh token / expiry are overwritten, so scopes,
|
|
28
|
+
* subscriptionType, rateLimitTier and any unknown fields are preserved; and
|
|
29
|
+
* 3. if the server rotates the refresh token, the NEW one is persisted; if the
|
|
30
|
+
* response omits a refresh token (non-rotating server), the existing one is
|
|
31
|
+
* kept — never dropped.
|
|
32
|
+
* A wrong endpoint / client id / network failure can therefore only ever make the
|
|
33
|
+
* exchange FAIL (→ the caller's existing `needs-reauth` path), never corrupt.
|
|
34
|
+
*
|
|
35
|
+
* Token values are NEVER logged or returned to any persisted surface. The OAuth
|
|
36
|
+
* token endpoint + client id are the public Claude Code values, extracted from
|
|
37
|
+
* the official client binary (verified 2026-06-08).
|
|
38
|
+
*
|
|
39
|
+
* Testability: the credential store, the fetch surface, and the clock are all
|
|
40
|
+
* injectable, so the whole refresh runs hermetically with zero keychain, zero
|
|
41
|
+
* network, and a deterministic clock in tests.
|
|
42
|
+
*/
|
|
43
|
+
import { execFileSync } from 'node:child_process';
|
|
44
|
+
import crypto from 'node:crypto';
|
|
45
|
+
import fs from 'node:fs';
|
|
46
|
+
import os from 'node:os';
|
|
47
|
+
import path from 'node:path';
|
|
48
|
+
/** Public Claude Code OAuth token endpoint (from the official client binary). */
|
|
49
|
+
export const CLAUDE_TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
|
|
50
|
+
/** Public Claude Code OAuth client id (from the official client binary). */
|
|
51
|
+
export const CLAUDE_CODE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
52
|
+
const ACCESS_PREFIX = 'sk-ant-oat';
|
|
53
|
+
const REFRESH_PREFIX = 'sk-ant-ort';
|
|
54
|
+
export function expandHome(p) {
|
|
55
|
+
if (p === '~' || p.startsWith('~/')) {
|
|
56
|
+
return path.join(process.env.HOME ?? '', p.slice(1));
|
|
57
|
+
}
|
|
58
|
+
return p;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* macOS keychain service name for a config home's Claude Code credentials.
|
|
62
|
+
* The default home (`~/.claude`) has no hash suffix; every other config home is
|
|
63
|
+
* suffixed with the first 8 hex of sha256(configHome) — verified empirically and
|
|
64
|
+
* matched by the official client.
|
|
65
|
+
*/
|
|
66
|
+
export function claudeCredentialService(configHome) {
|
|
67
|
+
const home = expandHome(configHome);
|
|
68
|
+
const defaultHome = expandHome('~/.claude');
|
|
69
|
+
return home === defaultHome
|
|
70
|
+
? 'Claude Code-credentials'
|
|
71
|
+
: `Claude Code-credentials-${crypto.createHash('sha256').update(home).digest('hex').slice(0, 8)}`;
|
|
72
|
+
}
|
|
73
|
+
/** Non-darwin credential file for a config home. */
|
|
74
|
+
export function claudeCredentialFilePath(configHome) {
|
|
75
|
+
return path.join(expandHome(configHome), '.credentials.json');
|
|
76
|
+
}
|
|
77
|
+
/** Default store: macOS keychain, else a per-config-home credentials file. */
|
|
78
|
+
export const defaultCredentialStore = {
|
|
79
|
+
read(configHome) {
|
|
80
|
+
if (process.platform === 'darwin') {
|
|
81
|
+
try {
|
|
82
|
+
const raw = execFileSync('security', ['find-generic-password', '-s', claudeCredentialService(configHome), '-w'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
83
|
+
return raw || null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null; // @silent-fallback-ok: no keychain entry → unreadable
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const p = claudeCredentialFilePath(configHome);
|
|
91
|
+
return fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : null;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null; // @silent-fallback-ok: missing/unreadable creds file
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
write(configHome, rawJson) {
|
|
98
|
+
if (process.platform === 'darwin') {
|
|
99
|
+
try {
|
|
100
|
+
execFileSync('security', [
|
|
101
|
+
'add-generic-password',
|
|
102
|
+
'-U', // update the existing entry in place
|
|
103
|
+
'-a',
|
|
104
|
+
os.userInfo().username,
|
|
105
|
+
'-s',
|
|
106
|
+
claudeCredentialService(configHome),
|
|
107
|
+
'-w',
|
|
108
|
+
rawJson,
|
|
109
|
+
], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false; // @silent-fallback-ok: keychain write failed → caller falls to needs-reauth
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const p = claudeCredentialFilePath(configHome);
|
|
118
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
119
|
+
fs.writeFileSync(p, rawJson, { mode: 0o600 });
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false; // @silent-fallback-ok: file write failed
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
/** Parse the `claudeAiOauth` block out of a config home's credential store. */
|
|
128
|
+
export function readClaudeOauth(configHome, store = defaultCredentialStore) {
|
|
129
|
+
const raw = store.read(configHome);
|
|
130
|
+
if (!raw)
|
|
131
|
+
return null;
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(raw);
|
|
134
|
+
const oauth = parsed?.claudeAiOauth;
|
|
135
|
+
return oauth && typeof oauth === 'object' ? oauth : null;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null; // @silent-fallback-ok: unparseable entry
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Refresh a config home's Claude Code access token from its stored refresh token.
|
|
143
|
+
* Returns the new access token + expiry on success; otherwise a typed failure
|
|
144
|
+
* reason the caller maps to its `needs-reauth` decision. Writes NOTHING on any
|
|
145
|
+
* failure — a working login is never put at risk by an unsuccessful refresh.
|
|
146
|
+
*/
|
|
147
|
+
export async function refreshClaudeToken(configHome, deps = {}) {
|
|
148
|
+
const store = deps.store ?? defaultCredentialStore;
|
|
149
|
+
const fetchImpl = deps.fetchImpl ??
|
|
150
|
+
((url, init) => fetch(url, init));
|
|
151
|
+
const now = deps.now ?? (() => Date.now());
|
|
152
|
+
const tokenUrl = deps.tokenUrl ?? CLAUDE_TOKEN_URL;
|
|
153
|
+
const clientId = deps.clientId ?? CLAUDE_CODE_CLIENT_ID;
|
|
154
|
+
const raw = store.read(configHome);
|
|
155
|
+
if (!raw)
|
|
156
|
+
return { ok: false, reason: 'read-failed' };
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = JSON.parse(raw);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { ok: false, reason: 'read-failed' };
|
|
163
|
+
}
|
|
164
|
+
const oauth = (parsed?.claudeAiOauth ?? null);
|
|
165
|
+
const refreshToken = oauth?.refreshToken;
|
|
166
|
+
if (typeof refreshToken !== 'string' || !refreshToken) {
|
|
167
|
+
return { ok: false, reason: 'no-refresh-token' };
|
|
168
|
+
}
|
|
169
|
+
let res;
|
|
170
|
+
try {
|
|
171
|
+
res = await fetchImpl(tokenUrl, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: { 'Content-Type': 'application/json' },
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
grant_type: 'refresh_token',
|
|
176
|
+
refresh_token: refreshToken,
|
|
177
|
+
client_id: clientId,
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return { ok: false, reason: 'exchange-failed' };
|
|
183
|
+
}
|
|
184
|
+
if (!res.ok)
|
|
185
|
+
return { ok: false, reason: 'exchange-failed', status: res.status };
|
|
186
|
+
let data;
|
|
187
|
+
try {
|
|
188
|
+
data = (await res.json());
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return { ok: false, reason: 'malformed-response' };
|
|
192
|
+
}
|
|
193
|
+
const newAccess = data?.access_token;
|
|
194
|
+
const expiresIn = data?.expires_in;
|
|
195
|
+
if (typeof newAccess !== 'string' || !newAccess.startsWith(ACCESS_PREFIX)) {
|
|
196
|
+
return { ok: false, reason: 'malformed-response' };
|
|
197
|
+
}
|
|
198
|
+
if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {
|
|
199
|
+
return { ok: false, reason: 'malformed-response' };
|
|
200
|
+
}
|
|
201
|
+
const newRefresh = data?.refresh_token;
|
|
202
|
+
const rotated = typeof newRefresh === 'string' &&
|
|
203
|
+
newRefresh.startsWith(REFRESH_PREFIX) &&
|
|
204
|
+
newRefresh !== refreshToken;
|
|
205
|
+
const expiresAt = now() + expiresIn * 1000;
|
|
206
|
+
// READ-MERGE-WRITE: preserve every existing field, overwrite only the tokens
|
|
207
|
+
// + expiry. Keep the old refresh token if the server didn't rotate.
|
|
208
|
+
const updatedOauth = {
|
|
209
|
+
...oauth,
|
|
210
|
+
accessToken: newAccess,
|
|
211
|
+
refreshToken: typeof newRefresh === 'string' && newRefresh.startsWith(REFRESH_PREFIX)
|
|
212
|
+
? newRefresh
|
|
213
|
+
: refreshToken,
|
|
214
|
+
expiresAt,
|
|
215
|
+
};
|
|
216
|
+
const updatedRaw = { ...parsed, claudeAiOauth: updatedOauth };
|
|
217
|
+
if (!store.write(configHome, JSON.stringify(updatedRaw))) {
|
|
218
|
+
return { ok: false, reason: 'write-failed' };
|
|
219
|
+
}
|
|
220
|
+
return { ok: true, accessToken: newAccess, expiresAt, rotated };
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=OAuthRefresher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthRefresher.js","sourceRoot":"","sources":["../../src/core/OAuthRefresher.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,8 +32,15 @@
|
|
|
32
32
|
* poller runs hermetically with zero credentials and zero network in tests.
|
|
33
33
|
*/
|
|
34
34
|
import type { SubscriptionPool, SubscriptionAccount, AccountQuotaSnapshot } from './SubscriptionPool.js';
|
|
35
|
+
import { type RefreshResult } from './OAuthRefresher.js';
|
|
35
36
|
/** Injectable token resolver — returns an account's OAuth access token or null. */
|
|
36
37
|
export type TokenResolver = (account: SubscriptionAccount) => string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Injectable account refresher — exchanges a config home's stored refresh token
|
|
40
|
+
* for a fresh access token (see OAuthRefresher). Defaults to the real keychain/
|
|
41
|
+
* file-backed refresh; tests inject a stub so the poller runs hermetically.
|
|
42
|
+
*/
|
|
43
|
+
export type AccountRefresher = (account: SubscriptionAccount) => Promise<RefreshResult>;
|
|
37
44
|
/** Minimal fetch surface so tests inject a stub (no global fetch dependency). */
|
|
38
45
|
export type FetchImpl = (url: string, init: {
|
|
39
46
|
headers: Record<string, string>;
|
|
@@ -50,6 +57,12 @@ export interface QuotaPollerConfig {
|
|
|
50
57
|
fetchImpl?: FetchImpl;
|
|
51
58
|
/** Injected for tests; defaults to the config-home credential resolver. */
|
|
52
59
|
tokenResolver?: TokenResolver;
|
|
60
|
+
/**
|
|
61
|
+
* Injected for tests; defaults to the real OAuth refresh-token exchange. On a
|
|
62
|
+
* usage-read auth failure the poller calls this BEFORE declaring needs-reauth,
|
|
63
|
+
* so a routine access-token expiry recovers silently instead of crying wolf.
|
|
64
|
+
*/
|
|
65
|
+
refresher?: AccountRefresher;
|
|
53
66
|
/** Logger (defaults to console). */
|
|
54
67
|
logger?: {
|
|
55
68
|
log: (m: string) => void;
|
|
@@ -66,10 +79,12 @@ export interface BurnRate {
|
|
|
66
79
|
}
|
|
67
80
|
/**
|
|
68
81
|
* Resolve a claude-code account's OAuth access token from its config home,
|
|
69
|
-
* TRANSIENTLY. Never persisted, never logged.
|
|
70
|
-
* keychain
|
|
71
|
-
*
|
|
72
|
-
*
|
|
82
|
+
* TRANSIENTLY. Never persisted, never logged. Reads via the shared OAuthRefresher
|
|
83
|
+
* locator (macOS keychain `Claude Code-credentials-<sha256(configHome)[0:8]>`,
|
|
84
|
+
* else `<configHome>/.credentials.json`) so the resolver and the refresher always
|
|
85
|
+
* agree on WHERE a config home's credentials live. NOTE: an EXPIRED access token
|
|
86
|
+
* is still returned here (it's a valid string) — expiry is detected by the usage
|
|
87
|
+
* read's 401 and recovered by the refresher, not by this resolver.
|
|
73
88
|
*/
|
|
74
89
|
export declare function defaultTokenResolver(account: SubscriptionAccount): string | null;
|
|
75
90
|
/**
|
|
@@ -92,6 +107,7 @@ export declare class QuotaPoller {
|
|
|
92
107
|
private readonly pollIntervalMs;
|
|
93
108
|
private readonly fetchImpl;
|
|
94
109
|
private readonly tokenResolver;
|
|
110
|
+
private readonly refresher;
|
|
95
111
|
private readonly logger;
|
|
96
112
|
private interval;
|
|
97
113
|
/** Most-recent snapshot per account id. */
|
|
@@ -101,10 +117,21 @@ export declare class QuotaPoller {
|
|
|
101
117
|
constructor(config: QuotaPollerConfig);
|
|
102
118
|
start(): void;
|
|
103
119
|
stop(): void;
|
|
120
|
+
/**
|
|
121
|
+
* One usage read. Returns the parsed body (or null body on a non-auth non-ok),
|
|
122
|
+
* an auth-failed marker (401/403), or null on a network failure. NEVER logs the
|
|
123
|
+
* token.
|
|
124
|
+
*/
|
|
125
|
+
private readUsage;
|
|
126
|
+
private markNeedsReauth;
|
|
104
127
|
/**
|
|
105
128
|
* Poll one account: resolve token transiently, read usage, map to snapshot.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
129
|
+
* On a usage-read auth failure (401/403) the access token may simply have
|
|
130
|
+
* EXPIRED while the refresh token is still valid — so the poller attempts a
|
|
131
|
+
* refresh-token exchange and ONE retry BEFORE declaring needs-reauth. Only a
|
|
132
|
+
* genuinely dead login (no refresh token / refresh rejected / still 401 after
|
|
133
|
+
* a fresh token) yields needs-reauth. Returns null when the token is
|
|
134
|
+
* unresolvable or the read fails. NEVER logs or returns the token.
|
|
108
135
|
*/
|
|
109
136
|
pollAccount(account: SubscriptionAccount): Promise<AccountQuotaSnapshot | null>;
|
|
110
137
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaPoller.d.ts","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;
|
|
1
|
+
{"version":3,"file":"QuotaPoller.d.ts","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"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"}
|