@modelprofile.com/authswitch 6.0.0 → 6.1.0
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/accounts.js +3 -2
- package/dist_ts/classes.cli.js +3 -3
- package/dist_ts/classes.credentialstore.d.ts +3 -1
- package/dist_ts/classes.credentialstore.js +2 -2
- package/dist_ts/classes.fileharness.d.ts +16 -5
- package/dist_ts/classes.fileharness.js +31 -9
- package/dist_ts/classes.opencodeanthropicrefresh.d.ts +29 -0
- package/dist_ts/classes.opencodeanthropicrefresh.js +59 -0
- package/dist_ts/classes.opencodeharness.d.ts +29 -3
- package/dist_ts/classes.opencodeharness.js +84 -15
- package/dist_ts/interfaces.harness.d.ts +7 -3
- package/package.json +1 -1
- package/readme.md +22 -12
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/accounts.ts +2 -1
- package/ts/classes.cli.ts +3 -3
- package/ts/classes.credentialstore.ts +2 -2
- package/ts/classes.fileharness.ts +28 -8
- package/ts/classes.opencodeanthropicrefresh.ts +64 -0
- package/ts/classes.opencodeharness.ts +79 -14
- package/ts/interfaces.harness.ts +7 -3
|
@@ -4,10 +4,16 @@ import { CredentialStore, credentialHash, credentialRecord, credentialText, read
|
|
|
4
4
|
import { HarnessProcesses } from './classes.harnessprocesses.js';
|
|
5
5
|
import { ClaudeAccountStatus } from './classes.claudestatus.js';
|
|
6
6
|
import { CLAUDE_REFRESH_MARGIN_MS } from './classes.claudetokenrefresh.js';
|
|
7
|
+
import { ClaudeLoginRejectedError, ClaudeRateLimitError } from './claudehttp.js';
|
|
7
8
|
import { CodexAccountStatus } from './classes.codexstatus.js';
|
|
9
|
+
import { OpenCodeAnthropicRefresh } from './classes.opencodeanthropicrefresh.js';
|
|
8
10
|
import { writeSecretFileAtomically } from './helpers.js';
|
|
9
11
|
import { beginSavedOpenAiLogin, openCodeCredentialFromLogin } from './classes.login.js';
|
|
10
12
|
import { plainText } from './formatting.js';
|
|
13
|
+
/** Anthropic refused the grant OpenCode's entry holds; no retry and no other account changes that answer. */
|
|
14
|
+
const ANTHROPIC_LOGIN_REJECTED = 'Login expired or was rejected. Log in again with opencode auth login.';
|
|
15
|
+
/** A saved copy is not the login OpenCode uses, so renewing it would rotate a token OpenCode's own entry still holds. */
|
|
16
|
+
const SAVED_ANTHROPIC_EXPIRED = 'This saved Anthropic login\'s access token has expired. authswitch renews the Anthropic login OpenCode has active; activate this one to have it renewed, or log in again with opencode auth login.';
|
|
11
17
|
/** OpenCode owns one login per provider. Replacing a slot never replaces the whole provider set. */
|
|
12
18
|
export class OpenCodeHarness extends FileHarness {
|
|
13
19
|
id = 'opencode';
|
|
@@ -17,12 +23,17 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
17
23
|
liveSwap = false;
|
|
18
24
|
/** Only its OpenAI login reports usage, and a supervisor (AGL) restarts it around a switch, so a watch leaves it alone. */
|
|
19
25
|
autoSwitch = false;
|
|
26
|
+
/** OpenCode re-reads its credential file on every request; only a request already in flight still holds the previous login. */
|
|
27
|
+
runningRetention() {
|
|
28
|
+
return 'They re-read the credential file on every request, but a request already in flight still holds the previous login, and its token refresh can ';
|
|
29
|
+
}
|
|
20
30
|
loginProviders = [{ providerId: 'openai', label: 'OpenAI', flows: ['device'] }];
|
|
21
31
|
store;
|
|
22
32
|
file;
|
|
23
33
|
env;
|
|
24
34
|
status;
|
|
25
35
|
claudeStatus;
|
|
36
|
+
anthropicRefresh;
|
|
26
37
|
processes;
|
|
27
38
|
fetcher;
|
|
28
39
|
now;
|
|
@@ -33,6 +44,7 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
33
44
|
this.store = new CredentialStore(this.id, options.stashRoot);
|
|
34
45
|
this.status = new CodexAccountStatus(options.fetch);
|
|
35
46
|
this.claudeStatus = new ClaudeAccountStatus(options.fetch, options.now);
|
|
47
|
+
this.anthropicRefresh = new OpenCodeAnthropicRefresh(options.fetch, options.now);
|
|
36
48
|
this.fetcher = options.fetch;
|
|
37
49
|
this.now = options.now ?? Date.now;
|
|
38
50
|
this.processes = options.processes ?? new HarnessProcesses('opencode');
|
|
@@ -119,28 +131,85 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
119
131
|
if (readCredentialDocument(this.file).raw !== raw)
|
|
120
132
|
throw new Error('Active login verification failed. The outgoing login remains saved.');
|
|
121
133
|
}
|
|
134
|
+
/** The login OpenCode has in a provider slot, read from its file; undefined where the file holds none it can read. */
|
|
135
|
+
activeSlot(slotIdArg) {
|
|
136
|
+
return this.snapshot().accounts.find(account => account.slotId === slotIdArg);
|
|
137
|
+
}
|
|
138
|
+
/** Whether this login's access token is inside Claude Code's five-minute margin, the margin Claude's own logins use. */
|
|
139
|
+
renewalDue(credentialArg) {
|
|
140
|
+
const expires = credentialArg.expires;
|
|
141
|
+
return typeof expires === 'number' && Number.isFinite(expires) && this.now() + CLAUDE_REFRESH_MARGIN_MS >= expires;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Renews OpenCode's active Anthropic login and writes the rotated pair back into OpenCode's own credential file.
|
|
145
|
+
*
|
|
146
|
+
* Nothing is stranded by that write. OpenCode's auth service reads `auth.json` on every `Auth.all()`, `Auth.get()`
|
|
147
|
+
* is that read (1.18.15 @99100961, 1.18.31 @103733929), and its Anthropic provider -- `opencode-anthropic-auth`
|
|
148
|
+
* 0.0.13, since the binary carries no Anthropic OAuth code -- calls the `getAuth()` it was initialised with
|
|
149
|
+
* (@98017331) inside the `fetch` wrapper of every single request. A running OpenCode therefore holds no refresh
|
|
150
|
+
* token in memory and picks up this rotation on its next request, exactly as it picks up its own.
|
|
151
|
+
*
|
|
152
|
+
* The renewal is the only authswitch operation on the store while it runs, and the rotated pair replaces the
|
|
153
|
+
* entry only while OpenCode still holds the refresh token that was sent: a login OpenCode renewed meanwhile is
|
|
154
|
+
* returned as it now stands. The write itself is `apply`'s verified compare-and-swap, which preserves every other
|
|
155
|
+
* provider and replaces the file atomically -- OpenCode writes the same file in place. The slot's switch record
|
|
156
|
+
* follows that write, because an `anthropic` login's identity is its refresh token and this rotation is
|
|
157
|
+
* authswitch's own: without that, the next read would report this write as a different account.
|
|
158
|
+
*/
|
|
159
|
+
renewAnthropic(accountArg, signalArg) {
|
|
160
|
+
return this.store.locked(async () => {
|
|
161
|
+
const current = this.activeSlot(accountArg.slotId) ?? accountArg;
|
|
162
|
+
if (!this.renewalDue(current.credential))
|
|
163
|
+
return current;
|
|
164
|
+
const refreshToken = credentialText(current.credential.refresh);
|
|
165
|
+
if (refreshToken === undefined)
|
|
166
|
+
throw new Error('This Anthropic login holds no refresh token; log in again with opencode auth login.');
|
|
167
|
+
signalArg?.throwIfAborted();
|
|
168
|
+
const tokens = await this.anthropicRefresh.refresh(refreshToken);
|
|
169
|
+
const latest = this.activeSlot(accountArg.slotId);
|
|
170
|
+
if (!latest || credentialText(latest.credential.refresh) !== refreshToken)
|
|
171
|
+
return latest ?? current;
|
|
172
|
+
const renewed = this.inspect({ ...latest.credential, access: tokens.access, refresh: tokens.refresh, expires: tokens.expires }, latest.slotId);
|
|
173
|
+
this.apply(latest.slotId, latest, renewed);
|
|
174
|
+
this.recordRenewal(latest.slotId);
|
|
175
|
+
return renewed;
|
|
176
|
+
}, { signal: signalArg });
|
|
177
|
+
}
|
|
122
178
|
/**
|
|
123
179
|
* The subscription and usage of an OpenCode `anthropic` OAuth login.
|
|
124
180
|
*
|
|
125
181
|
* That entry holds the same Claude subscriber grant Claude Code stores, so Claude's own account endpoints answer
|
|
126
182
|
* for it and the account reports real windows instead of no quota API at all. OpenCode keeps no scopes and no
|
|
127
|
-
* account identity with it, so the service decides both
|
|
128
|
-
*
|
|
129
|
-
*
|
|
183
|
+
* account identity with it, so the service decides both. A due access token is renewed first, with OpenCode's own
|
|
184
|
+
* request; a refusal is reported and nothing is written, so the entry OpenCode owns is left exactly as it was.
|
|
185
|
+
*
|
|
186
|
+
* A saved copy is never renewed: OpenCode's entry holds the same refresh token, and rotating it here would leave
|
|
187
|
+
* the login OpenCode actually uses holding a token the service has replaced.
|
|
130
188
|
*/
|
|
131
|
-
anthropicStatus(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
189
|
+
async anthropicStatus(accountArg, isActiveArg, optionsArg) {
|
|
190
|
+
let account = accountArg;
|
|
191
|
+
if (this.renewalDue(account.credential)) {
|
|
192
|
+
if (!isActiveArg)
|
|
193
|
+
return this.claudeStatus.unavailable({}, SAVED_ANTHROPIC_EXPIRED);
|
|
194
|
+
try {
|
|
195
|
+
account = await this.renewAnthropic(account, optionsArg.signal);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (optionsArg.signal?.aborted)
|
|
199
|
+
throw error;
|
|
200
|
+
const problem = error instanceof ClaudeLoginRejectedError ? ANTHROPIC_LOGIN_REJECTED
|
|
201
|
+
: error instanceof Error ? error.message : 'the login could not be renewed.';
|
|
202
|
+
return this.claudeStatus.unavailable({}, `Login refresh: ${problem}`, error instanceof ClaudeRateLimitError ? { retryAt: error.retryAt } : undefined);
|
|
203
|
+
}
|
|
139
204
|
}
|
|
205
|
+
const accessToken = credentialText(account.credential.access);
|
|
206
|
+
if (accessToken === undefined)
|
|
207
|
+
return this.claudeStatus.unavailable({}, 'This Anthropic login holds no usable access token; log in again with opencode auth login.');
|
|
140
208
|
return this.claudeStatus.read({ accessToken, scopes: [] }, optionsArg);
|
|
141
209
|
}
|
|
142
|
-
async readAccountStatus(id, optionsArg) {
|
|
143
|
-
const
|
|
210
|
+
async readAccountStatus(id, optionsArg = {}) {
|
|
211
|
+
const active = this.activeCredential(id);
|
|
212
|
+
const account = active ?? this.savedCredential(id);
|
|
144
213
|
if (account.slotId === 'openai' && account.credential.type === 'oauth') {
|
|
145
214
|
const info = this.openAiInfo(account.credential);
|
|
146
215
|
if (!info.accountId)
|
|
@@ -148,7 +217,7 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
148
217
|
return this.status.readCredential(credentialText(account.credential.access), info.accountId, info.plan, optionsArg);
|
|
149
218
|
}
|
|
150
219
|
if (account.slotId === 'anthropic' && account.credential.type === 'oauth')
|
|
151
|
-
return this.anthropicStatus(account
|
|
220
|
+
return this.anthropicStatus(account, active !== undefined, optionsArg);
|
|
152
221
|
return { facts: [
|
|
153
222
|
{ section: 'Authentication', label: 'Provider', value: account.slotId },
|
|
154
223
|
{ section: 'Authentication', label: 'Login type', value: String(account.credential.type) },
|
|
@@ -156,4 +225,4 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
156
225
|
], problems: [] };
|
|
157
226
|
}
|
|
158
227
|
}
|
|
159
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5vcGVuY29kZWhhcm5lc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm9wZW5jb2RlaGFybmVzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsV0FBVyxFQUFnRCxNQUFNLDBCQUEwQixDQUFDO0FBQ3JHLE9BQU8sRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLGNBQWMsRUFBRSxzQkFBc0IsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzVKLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzNFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzlELE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUV6RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFhNUMsb0dBQW9HO0FBQ3BHLE1BQU0sT0FBTyxlQUFnQixTQUFRLFdBQVc7SUFDOUIsRUFBRSxHQUFHLFVBQVUsQ0FBQztJQUNoQixLQUFLLEdBQUcsVUFBVSxDQUFDO0lBQ25CLFNBQVMsR0FBRyx3RkFBd0YsQ0FBQztJQUNySCx1SEFBdUg7SUFDdkcsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUNqQywySEFBMkg7SUFDM0csVUFBVSxHQUFHLEtBQUssQ0FBQztJQUNuQixjQUFjLEdBQTRCLENBQUMsRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxRQUFR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
|
|
228
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5vcGVuY29kZWhhcm5lc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm9wZW5jb2RlaGFybmVzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsV0FBVyxFQUFnRCxNQUFNLDBCQUEwQixDQUFDO0FBQ3JHLE9BQU8sRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLGNBQWMsRUFBRSxzQkFBc0IsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzVKLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzNFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzlELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUV6RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFhNUMsNkdBQTZHO0FBQzdHLE1BQU0sd0JBQXdCLEdBQUcsdUVBQXVFLENBQUM7QUFDekcseUhBQXlIO0FBQ3pILE1BQU0sdUJBQXVCLEdBQUcsb01BQW9NLENBQUM7QUFFck8sb0dBQW9HO0FBQ3BHLE1BQU0sT0FBTyxlQUFnQixTQUFRLFdBQVc7SUFDOUIsRUFBRSxHQUFHLFVBQVUs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
|
|
@@ -236,9 +236,13 @@ export interface IAuthHarness {
|
|
|
236
236
|
*/
|
|
237
237
|
readonly autoSwitch?: boolean;
|
|
238
238
|
/**
|
|
239
|
-
* The account's live status. It must never activate, switch or clear a login.
|
|
240
|
-
*
|
|
241
|
-
*
|
|
239
|
+
* The account's live status. It must never activate, switch or clear a login.
|
|
240
|
+
*
|
|
241
|
+
* It may renew the tokens of the credential it reads when the provider leaves it no other way to answer, under
|
|
242
|
+
* its store's lock and with a compare-and-swap on the refresh token it sent. A saved copy is always its own to
|
|
243
|
+
* renew -- the Claude Code adapter renews one, because Claude Code's active login is Claude Code's. The active
|
|
244
|
+
* login may only be renewed where the owning harness re-reads its credential file on every request, so that the
|
|
245
|
+
* rotation strands nothing: OpenCode's Anthropic provider does, and authswitch renews that entry.
|
|
242
246
|
*/
|
|
243
247
|
readAccountStatus(accountIdArg: string, optionsArg?: IHarnessStatusOptions): Promise<IHarnessAccountStatus>;
|
|
244
248
|
readonly loginProviders?: IHarnessLoginProvider[];
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -109,8 +109,11 @@ never blocked by a waiting switch; after that the operation fails with nothing
|
|
|
109
109
|
changed. A lock left behind by a Claude Code that died goes stale after its own
|
|
110
110
|
threshold and is taken over, as Claude Code itself would.
|
|
111
111
|
|
|
112
|
-
**
|
|
113
|
-
|
|
112
|
+
**A running OpenCode can still overwrite a switch.** Not by holding the previous login:
|
|
113
|
+
its auth service caches nothing and re-reads `auth.json` on every lookup. The window is a
|
|
114
|
+
request already in flight, which read the previous login before the switch and can write
|
|
115
|
+
that login's own rotation back afterwards -- and OpenCode replaces the whole provider map
|
|
116
|
+
in place rather than atomically. So before `use` and
|
|
114
117
|
before `stash` without `--keep`, authswitch lists OpenCode's own running instances --
|
|
115
118
|
pid, start time and command line -- and offers to stop them:
|
|
116
119
|
|
|
@@ -217,11 +220,16 @@ ChatGPT OAuth accounts display their email and use the same direct status APIs a
|
|
|
217
220
|
Codex. An `anthropic` OAuth login holds the same Claude subscriber grant Claude Code
|
|
218
221
|
stores, so it reports Claude's own plan, organization and usage windows through the
|
|
219
222
|
account endpoints described under **Claude Code**; OpenCode keeps no scopes and no
|
|
220
|
-
account identity with that entry, so both are left to the service.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
account identity with that entry, so both are left to the service. An access token inside
|
|
224
|
+
Claude Code's five-minute margin is renewed first, with the request OpenCode's own
|
|
225
|
+
Anthropic provider sends (`opencode-anthropic-auth`: `console.anthropic.com`, Claude
|
|
226
|
+
Code's public client, no scope), and the rotated pair is written back into `auth.json`
|
|
227
|
+
atomically, preserving every other provider. Nothing is stranded by that write: OpenCode's
|
|
228
|
+
auth service re-reads `auth.json` on every lookup and its Anthropic provider asks for the
|
|
229
|
+
credential again inside every request, so a running instance picks up the rotation exactly
|
|
230
|
+
as it picks up its own. A refusal is reported and nothing is written, and a saved copy is
|
|
231
|
+
never renewed, because OpenCode's own entry holds the same refresh token.
|
|
232
|
+
API keys and opaque provider tokens have no reliable email, so their
|
|
225
233
|
labels contain the provider and a SHA-256 fingerprint. Their quota/billing information is
|
|
226
234
|
explicitly unavailable. `OPENCODE_AUTH_CONTENT` overrides the file and blocks file
|
|
227
235
|
management. Provider configuration and environment credentials remain managed by OpenCode.
|
|
@@ -339,10 +347,12 @@ reason names what to do:
|
|
|
339
347
|
the service answered in a shape authswitch does not read. Nothing is guessed from a
|
|
340
348
|
response it does not understand.
|
|
341
349
|
|
|
342
|
-
An OpenCode `anthropic` row reads the same endpoints and so shows the same reasons
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
350
|
+
An OpenCode `anthropic` row reads the same endpoints and so shows the same reasons. Its
|
|
351
|
+
renewal adds `Login refresh: Login expired or was rejected. Log in again with opencode
|
|
352
|
+
auth login.`, which is Anthropic refusing the refresh token OpenCode's entry holds -- no
|
|
353
|
+
retry changes that, and the refusal is never remembered, because OpenCode owns that entry.
|
|
354
|
+
`This saved Anthropic login's access token has expired.` is a stashed copy, which is left
|
|
355
|
+
alone so that the login OpenCode uses keeps a refresh token the service still accepts.
|
|
346
356
|
|
|
347
357
|
OpenCode and Claude saved records are separate owner-only files under
|
|
348
358
|
`~/.authswitch/opencode` and `~/.authswitch/claude`. Emails are accepted as account
|
|
@@ -1038,7 +1048,7 @@ Account identity comes from the `id_token` inside the credential — the email c
|
|
|
1038
1048
|
|
|
1039
1049
|
**Other Codex clients keep running.** Stopping the managed app-server does not stop an editor extension that spawned its own. Close it, or expect it to keep using the credential it already loaded.
|
|
1040
1050
|
|
|
1041
|
-
**A running OpenCode can undo a switch.**
|
|
1051
|
+
**A running OpenCode can undo a switch.** Its auth service keeps no login in memory -- every lookup re-reads `auth.json` -- but a request already in flight, which read the previous login before the switch, can write that login's own rotation back afterwards, over a provider map OpenCode replaces in place. Authswitch lists those instances, offers to stop them, warns when any keep running, and detects the overwrite afterwards — but it cannot prevent it while they run. Restart them after a switch. A running Claude Code picks the switch up instead; its guarantee rests on the behaviour and lock protocol verified against Claude Code 2.1.273, and `authswitch claude active` still reports a login that changed outside authswitch.
|
|
1042
1052
|
|
|
1043
1053
|
**The Codex stash is keyed by email.** Two Codex workspaces with the same email
|
|
1044
1054
|
cannot both be saved under that key. Their account IDs are distinguished during
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/accounts.ts
CHANGED
|
@@ -239,7 +239,8 @@ const STATUS_LOOKUP_CONCURRENCY = 4;
|
|
|
239
239
|
* Bounded status lookups: at most four accounts at a time, started in order, each handed the caller's signal.
|
|
240
240
|
* A lookup that fails reports undefined; once the signal aborts, no further lookup starts. A callback that throws
|
|
241
241
|
* rejects the call, and no other lookup starts after it. No lookup activates, switches or clears a login, though an
|
|
242
|
-
* adapter may renew the tokens of the
|
|
242
|
+
* adapter may renew the tokens of the credential it reads, saved or active, on that method's terms
|
|
243
|
+
* (`IAuthHarness.readAccountStatus`).
|
|
243
244
|
*/
|
|
244
245
|
const lookupStatuses = async (
|
|
245
246
|
harnessArg: IAuthHarness,
|
package/ts/classes.cli.ts
CHANGED
|
@@ -108,9 +108,9 @@ ${bold('Options')}
|
|
|
108
108
|
|
|
109
109
|
A switch works while OpenCode or Claude Code is running. Claude Code picks the new login
|
|
110
110
|
up on its next request, so its sessions are stopped only on --stop or --force-stop.
|
|
111
|
-
OpenCode
|
|
112
|
-
|
|
113
|
-
it running and says so.
|
|
111
|
+
OpenCode caches no login either, but a request already in flight can write the previous
|
|
112
|
+
login's own rotation back, so an OpenCode switch offers to stop it first; without a
|
|
113
|
+
terminal it leaves it running and says so.
|
|
114
114
|
|
|
115
115
|
Watch covers the harnesses that support automatic switching (${[...this.harnesses.values()].filter(harness => harness.autoSwitch === true).map(harness => harness.id).join(', ') || 'none registered'}).
|
|
116
116
|
--interval takes seconds or a unit (120, 90s, 2m; from 1m to 1d); --threshold takes 50 to 100.
|
|
@@ -164,8 +164,8 @@ export class CredentialStore {
|
|
|
164
164
|
&& typeof record.switchedAt === 'string' && Number.isFinite(Date.parse(record.switchedAt));
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
|
-
public recordSwitch(entry: Omit<ISwitchRecord, 'switchedAt'>): void {
|
|
168
|
-
this.writeSwitchRecords([...this.switchRecords().filter(item => item.slotId !== entry.slotId), { ...entry, switchedAt: new Date().toISOString() }]);
|
|
167
|
+
public recordSwitch(entry: Omit<ISwitchRecord, 'switchedAt'> & { switchedAt?: string }): void {
|
|
168
|
+
this.writeSwitchRecords([...this.switchRecords().filter(item => item.slotId !== entry.slotId), { ...entry, switchedAt: entry.switchedAt ?? new Date().toISOString() }]);
|
|
169
169
|
}
|
|
170
170
|
public clearSwitch(slotId: string): void {
|
|
171
171
|
const remaining = this.switchRecords().filter(item => item.slotId !== slotId);
|
|
@@ -99,9 +99,10 @@ export abstract class FileHarness implements IAuthHarness {
|
|
|
99
99
|
* Credential slots that no longer hold the account the last switch wrote.
|
|
100
100
|
*
|
|
101
101
|
* The login can change outside authswitch -- a new native login, or, for a harness that does not pick
|
|
102
|
-
* up swaps live, a
|
|
102
|
+
* up swaps live, a request already in flight writing the previous login's own rotation back. The recorded
|
|
103
103
|
* hash detects that the file changed at all; comparing the account distinguishes the benign case -- the
|
|
104
|
-
* switched-in account rotating its own token -- from a different account.
|
|
104
|
+
* switched-in account rotating its own token -- from a different account. A rotation authswitch performed
|
|
105
|
+
* itself is not drift at all: `recordRenewal` moves the record onto it. Detection is advisory and
|
|
105
106
|
* never turns a read into a failure.
|
|
106
107
|
*/
|
|
107
108
|
private credentialDrift(snapshotArg: IFileHarnessSnapshot): IHarnessCredentialDrift[] {
|
|
@@ -127,14 +128,18 @@ export abstract class FileHarness implements IAuthHarness {
|
|
|
127
128
|
private savedLabel(accountIdArg: string): string | null {
|
|
128
129
|
try { return this.store.read(accountIdArg).label; } catch { return null; }
|
|
129
130
|
}
|
|
130
|
-
/**
|
|
131
|
+
/** How a running instance still holds the previous login and can write it back; stated per harness. */
|
|
132
|
+
protected runningRetention(): string {
|
|
133
|
+
return 'They keep the previous login in memory until they are restarted, and a token refresh by one of them can ';
|
|
134
|
+
}
|
|
135
|
+
/** Instances that can still write a replaced or cleared login back; none for a live-swap harness. */
|
|
131
136
|
private runningCaveat(kindArg: 'replaced' | 'cleared'): string[] {
|
|
132
137
|
if (this.liveSwap) return [];
|
|
133
138
|
let count: number;
|
|
134
139
|
try { count = this.processes.list().length; }
|
|
135
140
|
catch { return [`Could not check for running ${this.label} processes. If any are running, restart them: an unrestarted instance can write its previous login back.`]; }
|
|
136
141
|
if (!count) return [];
|
|
137
|
-
return [`${count} ${this.label} process(es) are still running.
|
|
142
|
+
return [`${count} ${this.label} process(es) are still running. ${this.runningRetention()}`
|
|
138
143
|
+ `${kindArg === 'replaced' ? 'overwrite the credential file with the previous account' : 'write the cleared login back into the credential file'}. `
|
|
139
144
|
+ `Restart them, then check authswitch ${this.id} active.`];
|
|
140
145
|
}
|
|
@@ -154,6 +159,25 @@ export abstract class FileHarness implements IAuthHarness {
|
|
|
154
159
|
return [];
|
|
155
160
|
} catch { return [`The login was written, but this switch could not be recorded for later credential checks.`]; }
|
|
156
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Let a slot's switch record follow a credential authswitch itself renewed in place.
|
|
164
|
+
*
|
|
165
|
+
* The record must name what authswitch last wrote, or the next read reports authswitch's own rotation as a
|
|
166
|
+
* login someone else put there -- an account whose identity is its refresh token changes id on every rotation.
|
|
167
|
+
* A slot no switch armed keeps no record: a renewal never starts watching one. The record is advisory, so an
|
|
168
|
+
* update that fails costs at most the drift note the next read would have shown anyway; a status read has
|
|
169
|
+
* nowhere to report it.
|
|
170
|
+
*/
|
|
171
|
+
protected recordRenewal(slotIdArg: string): void {
|
|
172
|
+
try {
|
|
173
|
+
const record = this.store.switchRecords().find(item => item.slotId === slotIdArg);
|
|
174
|
+
const active = this.snapshot().accounts.find(account => account.slotId === slotIdArg);
|
|
175
|
+
if (!record || !active) return;
|
|
176
|
+
// The switch time stays the switch time: a renewal changes what the record names, not when the login was put there.
|
|
177
|
+
this.store.recordSwitch({ slotId: slotIdArg, accountId: this.store.id(active.slotId, active.identity),
|
|
178
|
+
credentialHash: credentialHash(JSON.stringify(active.credential)), switchedAt: record.switchedAt });
|
|
179
|
+
} catch { /* Advisory state only. */ }
|
|
180
|
+
}
|
|
157
181
|
/** The active login with this account id, read from the native files; undefined when the account is not active. */
|
|
158
182
|
protected activeCredential(id: string): IFileAccount | undefined {
|
|
159
183
|
return this.snapshot().accounts.find(account => this.store.id(account.slotId, account.identity) === id);
|
|
@@ -162,10 +186,6 @@ export abstract class FileHarness implements IAuthHarness {
|
|
|
162
186
|
protected savedCredential(id: string): IFileAccount {
|
|
163
187
|
return this.saved(this.store.read(id));
|
|
164
188
|
}
|
|
165
|
-
/** The account's current credential: the active login when it is active, which may be newer than its saved copy. */
|
|
166
|
-
protected readCredential(id: string): IFileAccount {
|
|
167
|
-
return this.activeCredential(id) ?? this.savedCredential(id);
|
|
168
|
-
}
|
|
169
189
|
/** Save a newly authenticated credential without changing a native harness's login. */
|
|
170
190
|
public importCredential(slotId: string, credential: Record<string, unknown>): Promise<{ accountId: string }> {
|
|
171
191
|
return this.store.locked(() => ({ accountId: this.store.save(this.inspect(credential, slotId)).id }));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { credentialText } from './classes.credentialstore.js';
|
|
2
|
+
import { claudeRequest, ClaudeLoginRejectedError, ClaudeRequestError, oauthErrorCode } from './claudehttp.js';
|
|
3
|
+
|
|
4
|
+
/** The token endpoint `opencode-anthropic-auth` 0.0.13 renews an OpenCode `anthropic` login at (`index.mjs`). */
|
|
5
|
+
const TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';
|
|
6
|
+
/** The OAuth client that plugin sends, Claude Code's public one (`CLIENT_ID`, `index.mjs`). */
|
|
7
|
+
const CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
8
|
+
/**
|
|
9
|
+
* How long one renewal may take. A renewal is never cut shorter than that, nor cancelled once sent: the service may
|
|
10
|
+
* already have replaced the refresh token the request carries.
|
|
11
|
+
*/
|
|
12
|
+
const REFRESH_TIMEOUT_MS = 30_000;
|
|
13
|
+
|
|
14
|
+
/** The token fields OpenCode keeps for an OAuth login, as one renewal replaces them. */
|
|
15
|
+
export interface IOpenCodeAnthropicTokens {
|
|
16
|
+
access: string;
|
|
17
|
+
refresh: string;
|
|
18
|
+
/** Milliseconds since the epoch, as OpenCode stores them. */
|
|
19
|
+
expires: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const seconds = (valueArg: unknown): number | undefined =>
|
|
23
|
+
typeof valueArg === 'number' && Number.isFinite(valueArg) && valueArg >= 0 ? valueArg : undefined;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Renews an OpenCode `anthropic` OAuth login with the request OpenCode's own Anthropic provider sends for it.
|
|
27
|
+
*
|
|
28
|
+
* `opencode-anthropic-auth` 0.0.13 -- the plugin that gives OpenCode 1.18.x its `anthropic` OAuth provider; the
|
|
29
|
+
* binary carries no Anthropic OAuth code of its own -- posts a JSON body of `grant_type`, `refresh_token` and
|
|
30
|
+
* Claude Code's public client to `console.anthropic.com`, with no scope, and stores `{access, refresh, expires}`
|
|
31
|
+
* from the answer. This sends exactly that request, so a login renewed here is renewed the way OpenCode renews it.
|
|
32
|
+
*
|
|
33
|
+
* It only talks to the token endpoint; which login is renewed, and where the rotated pair is written, belongs to
|
|
34
|
+
* the harness.
|
|
35
|
+
*/
|
|
36
|
+
export class OpenCodeAnthropicRefresh {
|
|
37
|
+
constructor(private readonly fetcher: typeof fetch = globalThis.fetch, private readonly now: () => number = Date.now) {}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* New tokens for the login. A response without a refresh token keeps the one that was sent, as the service's own
|
|
41
|
+
* answer to a grant it did not rotate. Throws `ClaudeRequestError` with a fixed diagnostic; a grant the service
|
|
42
|
+
* rejects is `ClaudeLoginRejectedError`, and a rate limit is `ClaudeRateLimitError`.
|
|
43
|
+
*/
|
|
44
|
+
public async refresh(refreshTokenArg: string): Promise<IOpenCodeAnthropicTokens> {
|
|
45
|
+
const { status, body } = await claudeRequest(this.fetcher, {
|
|
46
|
+
url: TOKEN_URL, method: 'POST', headers: { 'Content-Type': 'application/json' }, subject: 'Claude sign-in',
|
|
47
|
+
body: JSON.stringify({ grant_type: 'refresh_token', refresh_token: refreshTokenArg, client_id: CLIENT_ID }),
|
|
48
|
+
timeoutMs: REFRESH_TIMEOUT_MS, readErrorBody: true, now: this.now,
|
|
49
|
+
});
|
|
50
|
+
if ((status === 400 || status === 401) && oauthErrorCode(body) === 'invalid_grant') throw new ClaudeLoginRejectedError();
|
|
51
|
+
if (status < 200 || status > 299) throw new ClaudeRequestError(`The Claude sign-in service returned HTTP ${status}.`);
|
|
52
|
+
const receivedAt = this.now();
|
|
53
|
+
const access = credentialText(body?.access_token);
|
|
54
|
+
const tokenType = body?.token_type;
|
|
55
|
+
const expiresIn = seconds(body?.expires_in);
|
|
56
|
+
const rotated = body?.refresh_token == null ? null : credentialText(body.refresh_token);
|
|
57
|
+
// Only a response every field of the stored entry can be taken from is written back into a file OpenCode owns.
|
|
58
|
+
if (!access || (tokenType != null && (typeof tokenType !== 'string' || tokenType.toLowerCase() !== 'bearer'))
|
|
59
|
+
|| expiresIn === undefined || rotated === undefined) {
|
|
60
|
+
throw new ClaudeRequestError('The Claude sign-in service returned an unsupported token response; the stored login was not changed.');
|
|
61
|
+
}
|
|
62
|
+
return { access, refresh: rotated ?? refreshTokenArg, expires: receivedAt + expiresIn * 1000 };
|
|
63
|
+
}
|
|
64
|
+
}
|