@moxxy/plugin-provider-claude-code 0.21.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Moxxy (moxxy.ai)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Constants for the Claude subscription (Pro/Max) provider.
3
+ *
4
+ * These are the public Claude Code OAuth client parameters — the same values
5
+ * the `claude` CLI uses — so a token minted here interoperates with one from
6
+ * `claude setup-token`, and vice-versa. We never embed a client secret; the
7
+ * flow is PKCE + an out-of-band (manual paste) authorization code.
8
+ */
9
+ /** Provider name in the registry / `/model` picker / `provider.name` config. */
10
+ export declare const CLAUDE_CODE_PROVIDER_ID = "claude-code";
11
+ /** Human-readable upstream service, shown in `moxxy login` and the init wizard. */
12
+ export declare const CLAUDE_CODE_SERVICE_NAME = "Claude (Pro/Max subscription)";
13
+ export declare const CLAUDE_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
14
+ export declare const CLAUDE_TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
15
+ export declare const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
16
+ /**
17
+ * Out-of-band redirect: after consent, Anthropic shows the user a
18
+ * `code#state` string to paste back into the terminal (no loopback server).
19
+ */
20
+ export declare const CLAUDE_REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
21
+ export declare const CLAUDE_SCOPES: readonly ["org:create_api_key", "user:profile", "user:inference"];
22
+ /**
23
+ * `anthropic-beta` values sent with every request. `oauth-2025-04-20` is the
24
+ * flag that makes the Messages API accept a subscription bearer token at all;
25
+ * `claude-code-20250219` matches what the real CLI sends. Sent comma-joined.
26
+ */
27
+ export declare const CLAUDE_OAUTH_BETA: readonly ["oauth-2025-04-20", "claude-code-20250219"];
28
+ /**
29
+ * Required first system block. Claude rejects a subscription token unless the
30
+ * system prompt leads with this exact identity line; moxxy's real system
31
+ * prompt is appended as the following block. (Verified against the working
32
+ * opencode / claude-code-router behaviour.)
33
+ */
34
+ export declare const CLAUDE_CODE_SYSTEM = "You are Claude Code, Anthropic's official CLI for Claude.";
35
+ /**
36
+ * Env vars a pasted/CI token can arrive through. `CLAUDE_CODE_OAUTH_TOKEN` is
37
+ * what `claude setup-token` documents; `ANTHROPIC_AUTH_TOKEN` is the
38
+ * SDK-native bearer var. Checked in this order.
39
+ */
40
+ export declare const CLAUDE_TOKEN_ENV_VARS: readonly ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_AUTH_TOKEN"];
41
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gFAAgF;AAChF,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,kCAAkC,CAAC;AAExE,eAAO,MAAM,oBAAoB,sCAAsC,CAAC;AACxE,eAAO,MAAM,gBAAgB,iDAAiD,CAAC;AAC/E,eAAO,MAAM,gBAAgB,yCAAyC,CAAC;AACvE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,sDAAsD,CAAC;AACvF,eAAO,MAAM,aAAa,mEAAoE,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,uDAAwD,CAAC;AAEvF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,8DAA8D,CAAC;AAE9F;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,8DAA+D,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Constants for the Claude subscription (Pro/Max) provider.
3
+ *
4
+ * These are the public Claude Code OAuth client parameters — the same values
5
+ * the `claude` CLI uses — so a token minted here interoperates with one from
6
+ * `claude setup-token`, and vice-versa. We never embed a client secret; the
7
+ * flow is PKCE + an out-of-band (manual paste) authorization code.
8
+ */
9
+ /** Provider name in the registry / `/model` picker / `provider.name` config. */
10
+ export const CLAUDE_CODE_PROVIDER_ID = 'claude-code';
11
+ /** Human-readable upstream service, shown in `moxxy login` and the init wizard. */
12
+ export const CLAUDE_CODE_SERVICE_NAME = 'Claude (Pro/Max subscription)';
13
+ export const CLAUDE_AUTHORIZE_URL = 'https://claude.ai/oauth/authorize';
14
+ export const CLAUDE_TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';
15
+ export const CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
16
+ /**
17
+ * Out-of-band redirect: after consent, Anthropic shows the user a
18
+ * `code#state` string to paste back into the terminal (no loopback server).
19
+ */
20
+ export const CLAUDE_REDIRECT_URI = 'https://console.anthropic.com/oauth/code/callback';
21
+ export const CLAUDE_SCOPES = ['org:create_api_key', 'user:profile', 'user:inference'];
22
+ /**
23
+ * `anthropic-beta` values sent with every request. `oauth-2025-04-20` is the
24
+ * flag that makes the Messages API accept a subscription bearer token at all;
25
+ * `claude-code-20250219` matches what the real CLI sends. Sent comma-joined.
26
+ */
27
+ export const CLAUDE_OAUTH_BETA = ['oauth-2025-04-20', 'claude-code-20250219'];
28
+ /**
29
+ * Required first system block. Claude rejects a subscription token unless the
30
+ * system prompt leads with this exact identity line; moxxy's real system
31
+ * prompt is appended as the following block. (Verified against the working
32
+ * opencode / claude-code-router behaviour.)
33
+ */
34
+ export const CLAUDE_CODE_SYSTEM = "You are Claude Code, Anthropic's official CLI for Claude.";
35
+ /**
36
+ * Env vars a pasted/CI token can arrive through. `CLAUDE_CODE_OAUTH_TOKEN` is
37
+ * what `claude setup-token` documents; `ANTHROPIC_AUTH_TOKEN` is the
38
+ * SDK-native bearer var. Checked in this order.
39
+ */
40
+ export const CLAUDE_TOKEN_ENV_VARS = ['CLAUDE_CODE_OAUTH_TOKEN', 'ANTHROPIC_AUTH_TOKEN'];
41
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC;AAErD,mFAAmF;AACnF,MAAM,CAAC,MAAM,wBAAwB,GAAG,+BAA+B,CAAC;AAExE,MAAM,CAAC,MAAM,oBAAoB,GAAG,mCAAmC,CAAC;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAG,8CAA8C,CAAC;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,sCAAsC,CAAC;AACvE;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mDAAmD,CAAC;AACvF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,oBAAoB,EAAE,cAAc,EAAE,gBAAgB,CAAU,CAAC;AAE/F;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,kBAAkB,EAAE,sBAAsB,CAAU,CAAC;AAEvF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,2DAA2D,CAAC;AAE9F;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,yBAAyB,EAAE,sBAAsB,CAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const claudeCodeProviderDef: import("@moxxy/sdk").ProviderDef;
2
+ export declare const claudeCodePlugin: import("@moxxy/sdk").Plugin;
3
+ export default claudeCodePlugin;
4
+ export { CLAUDE_CODE_PROVIDER_ID, CLAUDE_CODE_SERVICE_NAME, CLAUDE_CODE_SYSTEM, CLAUDE_OAUTH_BETA, CLAUDE_TOKEN_ENV_VARS, } from './constants.js';
5
+ export { claudeLogin, claudeLogout, claudeStatus, ensureFreshClaudeTokens, refreshClaudeAccessToken, type FreshClaudeTokens, } from './login.js';
6
+ export { createClaudeCodeClient, type ClaudeCodeProviderConfig } from './provider.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,qBAAqB,kCAehC,CAAC;AAEH,eAAO,MAAM,gBAAgB,6BAI3B,CAAC;AAEH,eAAe,gBAAgB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import { defineProvider, definePlugin } from '@moxxy/sdk';
2
+ import { anthropicModels } from '@moxxy/plugin-provider-anthropic';
3
+ import { CLAUDE_CODE_PROVIDER_ID, CLAUDE_CODE_SERVICE_NAME } from './constants.js';
4
+ import { createClaudeCodeClient } from './provider.js';
5
+ import { claudeLogin, claudeLogout, claudeStatus } from './login.js';
6
+ export const claudeCodeProviderDef = defineProvider({
7
+ name: CLAUDE_CODE_PROVIDER_ID,
8
+ // Same Claude models as the API-key `anthropic` provider — it's the same
9
+ // Messages API, only the credential differs.
10
+ models: [...anthropicModels],
11
+ createClient: (config) => createClaudeCodeClient(config),
12
+ // No validateKey: an OAuth bearer is validated by the request itself, and
13
+ // an interactive paste/sign-in already proves the token round-trips.
14
+ auth: {
15
+ kind: 'oauth',
16
+ serviceName: CLAUDE_CODE_SERVICE_NAME,
17
+ login: claudeLogin,
18
+ logout: claudeLogout,
19
+ status: claudeStatus,
20
+ },
21
+ });
22
+ export const claudeCodePlugin = definePlugin({
23
+ name: '@moxxy/plugin-provider-claude-code',
24
+ version: '0.0.0',
25
+ providers: [claudeCodeProviderDef],
26
+ });
27
+ export default claudeCodePlugin;
28
+ export { CLAUDE_CODE_PROVIDER_ID, CLAUDE_CODE_SERVICE_NAME, CLAUDE_CODE_SYSTEM, CLAUDE_OAUTH_BETA, CLAUDE_TOKEN_ENV_VARS, } from './constants.js';
29
+ export { claudeLogin, claudeLogout, claudeStatus, ensureFreshClaudeTokens, refreshClaudeAccessToken, } from './login.js';
30
+ export { createClaudeCodeClient } from './provider.js';
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAiC,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC;IAClD,IAAI,EAAE,uBAAuB;IAC7B,yEAAyE;IACzE,6CAA6C;IAC7C,MAAM,EAAE,CAAC,GAAG,eAAe,CAAC;IAC5B,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAkC,CAAC;IACpF,0EAA0E;IAC1E,qEAAqE;IACrE,IAAI,EAAE;QACJ,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,wBAAwB;QACrC,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,MAAM,EAAE,YAAY;KACrB;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC;IAC3C,IAAI,EAAE,oCAAoC;IAC1C,OAAO,EAAE,OAAO;IAChB,SAAS,EAAE,CAAC,qBAAqB,CAAC;CACnC,CAAC,CAAC;AAEH,eAAe,gBAAgB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,uBAAuB,EACvB,wBAAwB,GAEzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAiC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Claude subscription login + token lifecycle.
3
+ *
4
+ * Claude's OAuth is an out-of-band (manual code-paste) authorization-code +
5
+ * PKCE flow whose token endpoint speaks JSON (not the form-encoded dialect
6
+ * the framework's `exchangeCodeForToken` / `refreshAccessToken` assume), so
7
+ * the HTTP exchange + refresh are implemented here. Everything else is reused
8
+ * from `@moxxy/plugin-oauth`: PKCE, the auth-URL builder, the browser opener,
9
+ * vault storage layout, and `parseTokenResponse`.
10
+ */
11
+ import { type OAuthVault } from '@moxxy/plugin-oauth';
12
+ import type { ProviderAuthContext, ProviderOAuthResult, ProviderOAuthStatus } from '@moxxy/sdk';
13
+ /**
14
+ * Drive an interactive Claude sign-in. Two ways in, both through one command:
15
+ * 1. paste an existing `claude setup-token` token (stored verbatim), or
16
+ * 2. press Enter to run the browser out-of-band authorization-code flow.
17
+ * Needs `ctx.prompt` (a TTY); headless callers use `CLAUDE_CODE_OAUTH_TOKEN`.
18
+ */
19
+ export declare function claudeLogin(ctx: ProviderAuthContext): Promise<ProviderOAuthResult>;
20
+ export declare function claudeLogout(ctx: ProviderAuthContext): Promise<boolean>;
21
+ export declare function claudeStatus(ctx: ProviderAuthContext): Promise<ProviderOAuthStatus | null>;
22
+ export interface FreshClaudeTokens {
23
+ readonly accessToken: string;
24
+ readonly expiresAt?: number;
25
+ /** True when a refresh_token is stored, so a 401 can be recovered. */
26
+ readonly canRefresh: boolean;
27
+ }
28
+ /**
29
+ * Read the stored Claude creds, refreshing first if the access token is near
30
+ * expiry and a refresh_token is available. Returns null when nothing is
31
+ * stored. Persists rotated tokens BEFORE returning so a crash can't strand a
32
+ * single-use refresh_token. The refresh itself is serialized per credential
33
+ * (in-process + cross-process) and coalesces with concurrent refreshers.
34
+ */
35
+ export declare function ensureFreshClaudeTokens(vault: OAuthVault): Promise<FreshClaudeTokens | null>;
36
+ /**
37
+ * Force a refresh of the stored Claude tokens and persist the rotated bundle.
38
+ * Used as the provider's reactive 401 recovery. Throws if no refresh_token is
39
+ * stored (e.g. a pasted `setup-token`).
40
+ */
41
+ export declare function refreshClaudeAccessToken(vault: OAuthVault): Promise<{
42
+ token: string;
43
+ expiresAt?: number;
44
+ }>;
45
+ export declare function __setClaudeFetch(f: typeof fetch): void;
46
+ export declare function __setClaudeOpenBrowser(f: (url: string) => Promise<void>): void;
47
+ export declare function __setClaudeSleep(f: (ms: number) => Promise<void>): void;
48
+ export declare function __setClaudeTimeoutMs(ms: number): void;
49
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAaL,KAAK,UAAU,EAGhB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAepB;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuFxF;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAM7E;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAUhG;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAalG;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAYhD;AA4ED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,KAAK,GAAG,IAAI,CAEtD;AAID,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAE9E;AAID,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAEvE;AAID,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAErD"}
package/dist/login.js ADDED
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Claude subscription login + token lifecycle.
3
+ *
4
+ * Claude's OAuth is an out-of-band (manual code-paste) authorization-code +
5
+ * PKCE flow whose token endpoint speaks JSON (not the form-encoded dialect
6
+ * the framework's `exchangeCodeForToken` / `refreshAccessToken` assume), so
7
+ * the HTTP exchange + refresh are implemented here. Everything else is reused
8
+ * from `@moxxy/plugin-oauth`: PKCE, the auth-URL builder, the browser opener,
9
+ * vault storage layout, and `parseTokenResponse`.
10
+ */
11
+ import { buildAuthUrl, clearStoredCreds, computeCodeChallenge, generateCodeVerifier, generateState, isAuthRejection, isExpired, openInBrowser, parseTokenResponse, readStoredCreds, storeTokenSet, withCredentialLock, } from '@moxxy/plugin-oauth';
12
+ import { MoxxyError } from '@moxxy/sdk';
13
+ import { CLAUDE_AUTHORIZE_URL, CLAUDE_CLIENT_ID, CLAUDE_CODE_PROVIDER_ID, CLAUDE_CODE_SERVICE_NAME, CLAUDE_REDIRECT_URI, CLAUDE_SCOPES, CLAUDE_TOKEN_URL, } from './constants.js';
14
+ const PASTE_PROMPT = 'Paste a token from `claude setup-token` (or press Enter to sign in via browser): ';
15
+ const CODE_PROMPT = 'Paste the authorization code shown after you approve: ';
16
+ /**
17
+ * Drive an interactive Claude sign-in. Two ways in, both through one command:
18
+ * 1. paste an existing `claude setup-token` token (stored verbatim), or
19
+ * 2. press Enter to run the browser out-of-band authorization-code flow.
20
+ * Needs `ctx.prompt` (a TTY); headless callers use `CLAUDE_CODE_OAUTH_TOKEN`.
21
+ */
22
+ export async function claudeLogin(ctx) {
23
+ if (!ctx.prompt) {
24
+ throw new MoxxyError({
25
+ code: 'OAUTH_FLOW_NOT_SUPPORTED',
26
+ message: 'Claude sign-in needs an interactive terminal.',
27
+ hint: 'Run `claude setup-token` and set CLAUDE_CODE_OAUTH_TOKEN, or run ' +
28
+ '`moxxy login claude-code` in a terminal.',
29
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
30
+ });
31
+ }
32
+ const pasted = (await ctx.prompt(PASTE_PROMPT, { mask: true })).trim();
33
+ if (pasted) {
34
+ // A `setup-token` is a long-lived bearer with no refresh token; store it
35
+ // as-is. `isExpired` treats a missing expiry as non-expiring.
36
+ await storeTokenSet(ctx.vault, CLAUDE_CODE_PROVIDER_ID, { accessToken: pasted, tokenType: 'Bearer' }, { clientId: CLAUDE_CLIENT_ID, tokenUrl: CLAUDE_TOKEN_URL });
37
+ ctx.write('\nStored your Claude token.\n');
38
+ return {};
39
+ }
40
+ const verifier = generateCodeVerifier();
41
+ const challenge = computeCodeChallenge(verifier);
42
+ const state = generateState();
43
+ const authUrl = buildAuthUrl({
44
+ authUrl: CLAUDE_AUTHORIZE_URL,
45
+ clientId: CLAUDE_CLIENT_ID,
46
+ redirectUri: CLAUDE_REDIRECT_URI,
47
+ scopes: [...CLAUDE_SCOPES],
48
+ codeChallenge: challenge,
49
+ state,
50
+ extraAuthParams: { code: 'true' },
51
+ });
52
+ ctx.write(`\nSign in to ${CLAUDE_CODE_SERVICE_NAME} to authorize moxxy.\n\n` +
53
+ `If your browser doesn't open automatically, paste this URL:\n\n ${authUrl}\n\n` +
54
+ `After approving, copy the authorization code shown and paste it back here.\n` +
55
+ `(Anthropic's sign-in page sometimes shows "Internal server error" on the\n` +
56
+ ` first attempt — just click "Try again" and it goes through.)\n\n`);
57
+ try {
58
+ await openBrowserImpl(authUrl);
59
+ }
60
+ catch {
61
+ // Non-fatal — the user can open the URL surfaced above by hand.
62
+ }
63
+ // The authorization code is a single-use, exchangeable credential — mask it
64
+ // so it doesn't echo into scrollback / screen-share, matching the token paste.
65
+ const entered = (await ctx.prompt(CODE_PROMPT, { mask: true })).trim();
66
+ if (!entered) {
67
+ throw new MoxxyError({
68
+ code: 'AUTH_DENIED',
69
+ message: 'No authorization code entered — sign-in cancelled.',
70
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
71
+ });
72
+ }
73
+ // Anthropic returns `code#state`, but often shows just the bare code, so the
74
+ // state check below is best-effort: it only fires when a state was pasted.
75
+ // The real CSRF/code-injection defense is PKCE — the `code_verifier` binds
76
+ // the code to this client session, so a foreign code fails the exchange.
77
+ const hash = entered.indexOf('#');
78
+ const code = hash >= 0 ? entered.slice(0, hash) : entered;
79
+ const returnedState = hash >= 0 ? entered.slice(hash + 1) : '';
80
+ if (returnedState && returnedState !== state) {
81
+ throw new MoxxyError({
82
+ code: 'AUTH_INVALID',
83
+ message: 'Authorization state mismatch — please run sign-in again.',
84
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
85
+ });
86
+ }
87
+ const { tokenSet, accountEmail } = await exchangeClaudeCode(code, returnedState || state, verifier);
88
+ await persistClaudeTokens(ctx.vault, tokenSet, accountEmail);
89
+ ctx.write(`\nSigned in to Claude${accountEmail ? ` as ${accountEmail}` : ''}.\n`);
90
+ // Omit `expiresAt` when the credential never expires (setup-token paste, or a
91
+ // token response without `expires_in`). Surfacing `0` would read as "epoch =
92
+ // already expired" to the CLI status renderer, which only checks `!== undefined`.
93
+ return {
94
+ ...(accountEmail ? { accountId: accountEmail } : {}),
95
+ ...(tokenSet.expiresAt !== undefined ? { expiresAt: tokenSet.expiresAt } : {}),
96
+ };
97
+ }
98
+ export async function claudeLogout(ctx) {
99
+ try {
100
+ return (await clearStoredCreds(ctx.vault, CLAUDE_CODE_PROVIDER_ID)) > 0;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ export async function claudeStatus(ctx) {
107
+ const stored = await readStoredCreds(ctx.vault, CLAUDE_CODE_PROVIDER_ID);
108
+ if (!stored)
109
+ return null;
110
+ return {
111
+ accountId: stored.extras.account_email ?? null,
112
+ // Omit when absent — a stored setup-token has no expiry, and `0` would be
113
+ // mis-rendered as "expired" (the CLI only treats `!== undefined` as set).
114
+ ...(stored.tokenSet.expiresAt !== undefined ? { expiresAt: stored.tokenSet.expiresAt } : {}),
115
+ vaultKey: `oauth/${CLAUDE_CODE_PROVIDER_ID}/*`,
116
+ };
117
+ }
118
+ /**
119
+ * Read the stored Claude creds, refreshing first if the access token is near
120
+ * expiry and a refresh_token is available. Returns null when nothing is
121
+ * stored. Persists rotated tokens BEFORE returning so a crash can't strand a
122
+ * single-use refresh_token. The refresh itself is serialized per credential
123
+ * (in-process + cross-process) and coalesces with concurrent refreshers.
124
+ */
125
+ export async function ensureFreshClaudeTokens(vault) {
126
+ const stored = await readStoredCreds(vault, CLAUDE_CODE_PROVIDER_ID);
127
+ if (!stored)
128
+ return null;
129
+ const { tokenSet } = stored;
130
+ if (tokenSet.refreshToken && isExpired(tokenSet)) {
131
+ const refreshed = await refreshClaudeUnderLock(vault, stored);
132
+ return { accessToken: refreshed.accessToken, ...(refreshed.expiresAt !== undefined ? { expiresAt: refreshed.expiresAt } : {}), canRefresh: true };
133
+ }
134
+ return {
135
+ accessToken: tokenSet.accessToken,
136
+ ...(tokenSet.expiresAt !== undefined ? { expiresAt: tokenSet.expiresAt } : {}),
137
+ canRefresh: tokenSet.refreshToken !== undefined,
138
+ };
139
+ }
140
+ /**
141
+ * Force a refresh of the stored Claude tokens and persist the rotated bundle.
142
+ * Used as the provider's reactive 401 recovery. Throws if no refresh_token is
143
+ * stored (e.g. a pasted `setup-token`).
144
+ */
145
+ export async function refreshClaudeAccessToken(vault) {
146
+ const stored = await readStoredCreds(vault, CLAUDE_CODE_PROVIDER_ID);
147
+ if (!stored?.tokenSet.refreshToken) {
148
+ throw new MoxxyError({
149
+ code: 'AUTH_EXPIRED',
150
+ message: 'Claude token expired and no refresh_token is stored.',
151
+ hint: 'Run `moxxy login claude-code` again, or refresh `CLAUDE_CODE_OAUTH_TOKEN` via `claude setup-token`.',
152
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
153
+ });
154
+ }
155
+ const refreshed = await refreshClaudeUnderLock(vault, stored);
156
+ return { token: refreshed.accessToken, ...(refreshed.expiresAt !== undefined ? { expiresAt: refreshed.expiresAt } : {}) };
157
+ }
158
+ /**
159
+ * Refresh + persist under the per-credential lock. Anthropic ROTATES the
160
+ * refresh_token on every refresh and invalidates the previous one, so two
161
+ * concurrent refreshes (a second consumer in this process, or another moxxy
162
+ * process — TUI alongside a desktop runner) must not both burn the same
163
+ * stored token. Under the lock we:
164
+ * 1. re-read the vault — if someone else already rotated and the new access
165
+ * token is still fresh, reuse it (coalesce; no IdP call at all);
166
+ * 2. otherwise refresh with the freshest stored refresh_token;
167
+ * 3. on an invalid_grant-style rejection, re-read once more — if the
168
+ * on-disk refresh_token changed under us (another process won a race we
169
+ * couldn't see), retry ONCE with the fresher token before declaring
170
+ * re-auth necessary.
171
+ */
172
+ async function refreshClaudeUnderLock(vault, baseline) {
173
+ return withCredentialLock(`oauth-${CLAUDE_CODE_PROVIDER_ID}`, async () => {
174
+ const current = (await readStoredCreds(vault, CLAUDE_CODE_PROVIDER_ID)) ?? baseline;
175
+ if (current.tokenSet.accessToken !== baseline.tokenSet.accessToken && !isExpired(current.tokenSet)) {
176
+ return current.tokenSet;
177
+ }
178
+ const refreshToken = current.tokenSet.refreshToken ?? baseline.tokenSet.refreshToken;
179
+ if (!refreshToken) {
180
+ throw new MoxxyError({
181
+ code: 'AUTH_EXPIRED',
182
+ message: 'Claude token expired and no refresh_token is stored.',
183
+ hint: 'Run `moxxy login claude-code` again, or refresh `CLAUDE_CODE_OAUTH_TOKEN` via `claude setup-token`.',
184
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
185
+ });
186
+ }
187
+ const email = current.extras.account_email ?? baseline.extras.account_email;
188
+ try {
189
+ return await refreshAndPersist(vault, refreshToken, email);
190
+ }
191
+ catch (err) {
192
+ if (isAuthRejection(err)) {
193
+ const latest = await readStoredCreds(vault, CLAUDE_CODE_PROVIDER_ID);
194
+ const latestRefresh = latest?.tokenSet.refreshToken;
195
+ if (latestRefresh && latestRefresh !== refreshToken) {
196
+ return refreshAndPersist(vault, latestRefresh, latest.extras.account_email ?? email);
197
+ }
198
+ }
199
+ throw err;
200
+ }
201
+ });
202
+ }
203
+ /**
204
+ * Anthropic's OAuth endpoints (both `claude.ai/oauth/authorize` and the token
205
+ * endpoint) intermittently return a transient HTTP 500 — the *same* request
206
+ * then succeeds on retry. So retry 5xx/429/network failures with a short
207
+ * backoff before giving up. 4xx (bad/expired/already-used code, invalid_grant)
208
+ * is deterministic and surfaced immediately, never retried.
209
+ */
210
+ const TOKEN_POST_MAX_ATTEMPTS = 3;
211
+ const TOKEN_POST_BACKOFF_MS = [600, 1800];
212
+ /**
213
+ * Per-attempt deadline on the token POST. Node's `fetch` has NO default
214
+ * timeout, so a half-open TCP socket (server accepts but never responds — a
215
+ * stalled edge node, a black-holing proxy, a captive portal that keeps the
216
+ * connection alive) would hang the whole login/refresh indefinitely. Bound
217
+ * each attempt; a timeout aborts the request and is classed as a transient
218
+ * network error, so the retry loop tries again and ultimately surfaces the
219
+ * "endpoint kept failing" hint instead of wedging forever.
220
+ */
221
+ const TOKEN_POST_TIMEOUT_MS = 30_000;
222
+ /** Test seam so the exchange/refresh can run against a fake fetch. */
223
+ let fetchImpl = fetch;
224
+ export function __setClaudeFetch(f) {
225
+ fetchImpl = f;
226
+ }
227
+ /** Test seam so the OOB login can run without launching a real browser. */
228
+ let openBrowserImpl = openInBrowser;
229
+ export function __setClaudeOpenBrowser(f) {
230
+ openBrowserImpl = f;
231
+ }
232
+ /** Test seam so the retry backoff doesn't actually sleep under test. */
233
+ let sleepImpl = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
234
+ export function __setClaudeSleep(f) {
235
+ sleepImpl = f;
236
+ }
237
+ /** Test seam to shrink the per-attempt network deadline (default {@link TOKEN_POST_TIMEOUT_MS}). */
238
+ let timeoutMs = TOKEN_POST_TIMEOUT_MS;
239
+ export function __setClaudeTimeoutMs(ms) {
240
+ timeoutMs = ms;
241
+ }
242
+ async function exchangeClaudeCode(code, state, verifier) {
243
+ return postClaudeToken({
244
+ grant_type: 'authorization_code',
245
+ code,
246
+ state,
247
+ client_id: CLAUDE_CLIENT_ID,
248
+ redirect_uri: CLAUDE_REDIRECT_URI,
249
+ code_verifier: verifier,
250
+ });
251
+ }
252
+ async function refreshAndPersist(vault, refreshToken, priorEmail) {
253
+ const { tokenSet, accountEmail } = await postClaudeToken({
254
+ grant_type: 'refresh_token',
255
+ refresh_token: refreshToken,
256
+ client_id: CLAUDE_CLIENT_ID,
257
+ });
258
+ // Claude rotates the refresh_token on every refresh; if a response ever
259
+ // omits one, keep the prior so we don't lock ourselves out.
260
+ const merged = tokenSet.refreshToken
261
+ ? tokenSet
262
+ : { ...tokenSet, refreshToken };
263
+ await persistClaudeTokens(vault, merged, accountEmail ?? priorEmail);
264
+ return merged;
265
+ }
266
+ async function persistClaudeTokens(vault, tokenSet, accountEmail) {
267
+ await storeTokenSet(vault, CLAUDE_CODE_PROVIDER_ID, tokenSet, {
268
+ clientId: CLAUDE_CLIENT_ID,
269
+ tokenUrl: CLAUDE_TOKEN_URL,
270
+ ...(accountEmail ? { extras: { account_email: accountEmail } } : {}),
271
+ });
272
+ }
273
+ async function postClaudeToken(body) {
274
+ let transient = '';
275
+ for (let attempt = 1; attempt <= TOKEN_POST_MAX_ATTEMPTS; attempt++) {
276
+ if (attempt > 1)
277
+ await sleepImpl(TOKEN_POST_BACKOFF_MS[attempt - 2] ?? 1800);
278
+ let res;
279
+ try {
280
+ res = await fetchImpl(CLAUDE_TOKEN_URL, {
281
+ method: 'POST',
282
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
283
+ body: JSON.stringify(body),
284
+ // Bound a hung connection so it surfaces as a retryable network error
285
+ // (TimeoutError) rather than blocking the login/refresh forever.
286
+ signal: AbortSignal.timeout(timeoutMs),
287
+ });
288
+ }
289
+ catch (err) {
290
+ transient = `network error (${err instanceof Error ? err.message : String(err)})`;
291
+ continue;
292
+ }
293
+ if (res.ok) {
294
+ // A captive portal / proxy / misbehaving edge can return 200 with an HTML
295
+ // or empty body, or a JSON primitive/array. Treat any of these as a
296
+ // transient and retry rather than letting a raw SyntaxError escape the
297
+ // retry/classify path.
298
+ let parsed;
299
+ try {
300
+ parsed = await res.json();
301
+ }
302
+ catch {
303
+ transient = 'HTTP 200 with non-JSON body';
304
+ continue;
305
+ }
306
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
307
+ transient = 'HTTP 200 with malformed token response';
308
+ continue;
309
+ }
310
+ const json = parsed;
311
+ return { tokenSet: parseTokenResponse(json), ...extractAccountEmail(json) };
312
+ }
313
+ const text = await res.text().catch(() => '');
314
+ // Deterministic client errors: surface immediately, don't burn retries.
315
+ if (res.status < 500 && res.status !== 429) {
316
+ throw new MoxxyError({
317
+ code: res.status === 401 || res.status === 403 ? 'AUTH_DENIED' : 'AUTH_INVALID',
318
+ message: `Claude token endpoint returned HTTP ${res.status}: ${text.slice(0, 300)}`,
319
+ context: { provider: CLAUDE_CODE_PROVIDER_ID, status: res.status },
320
+ });
321
+ }
322
+ // 5xx / 429 — Anthropic-side flake; loop and try the same request again.
323
+ transient = `HTTP ${res.status}: ${text.slice(0, 200)}`;
324
+ }
325
+ throw new MoxxyError({
326
+ code: 'AUTH_INVALID',
327
+ message: `Claude token endpoint kept failing after ${TOKEN_POST_MAX_ATTEMPTS} attempts (last: ${transient}).`,
328
+ hint: "Anthropic's OAuth endpoint is returning transient errors right now — " +
329
+ 'wait a few seconds and run `moxxy login claude-code` again.',
330
+ context: { provider: CLAUDE_CODE_PROVIDER_ID },
331
+ });
332
+ }
333
+ function extractAccountEmail(json) {
334
+ const account = json.account;
335
+ if (account && typeof account === 'object') {
336
+ const email = account.email_address;
337
+ if (typeof email === 'string' && email)
338
+ return { accountEmail: email };
339
+ }
340
+ return {};
341
+ }
342
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The Claude subscription provider is the standard Anthropic Messages API
3
+ * with a bearer (OAuth) credential instead of an API key, so it reuses
4
+ * `AnthropicProvider` in OAuth mode rather than duplicating the streaming /
5
+ * tool-call event handling. This module just stamps in the Claude-specific
6
+ * constants (beta headers + the required identity preamble) and forwards the
7
+ * resolved token + refresh callback.
8
+ */
9
+ import { AnthropicProvider } from '@moxxy/plugin-provider-anthropic';
10
+ export interface ClaudeCodeProviderConfig {
11
+ /** Claude Code OAuth access token (bearer). Resolved by the host. */
12
+ readonly oauthToken?: string;
13
+ /** Epoch-ms expiry of `oauthToken`, when known (drives proactive refresh). */
14
+ readonly oauthExpiresAt?: number;
15
+ /** Refresh callback wired by the host; omitted for non-refreshable tokens. */
16
+ readonly oauthRefresh?: () => Promise<{
17
+ readonly token: string;
18
+ readonly expiresAt?: number;
19
+ }>;
20
+ /** Optional default model override. */
21
+ readonly defaultModel?: string;
22
+ }
23
+ export declare function createClaudeCodeClient(config?: ClaudeCodeProviderConfig): AnthropicProvider;
24
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGrE,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/F,uCAAuC;IACvC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,wBAA6B,GAAG,iBAAiB,CAU/F"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The Claude subscription provider is the standard Anthropic Messages API
3
+ * with a bearer (OAuth) credential instead of an API key, so it reuses
4
+ * `AnthropicProvider` in OAuth mode rather than duplicating the streaming /
5
+ * tool-call event handling. This module just stamps in the Claude-specific
6
+ * constants (beta headers + the required identity preamble) and forwards the
7
+ * resolved token + refresh callback.
8
+ */
9
+ import { AnthropicProvider } from '@moxxy/plugin-provider-anthropic';
10
+ import { CLAUDE_CODE_PROVIDER_ID, CLAUDE_CODE_SYSTEM, CLAUDE_OAUTH_BETA } from './constants.js';
11
+ export function createClaudeCodeClient(config = {}) {
12
+ return new AnthropicProvider({
13
+ name: CLAUDE_CODE_PROVIDER_ID,
14
+ oauthBeta: [...CLAUDE_OAUTH_BETA],
15
+ systemPreamble: CLAUDE_CODE_SYSTEM,
16
+ ...(config.oauthToken ? { oauthToken: config.oauthToken } : {}),
17
+ ...(config.oauthExpiresAt !== undefined ? { oauthExpiresAt: config.oauthExpiresAt } : {}),
18
+ ...(config.oauthRefresh ? { oauthRefresh: config.oauthRefresh } : {}),
19
+ ...(config.defaultModel ? { defaultModel: config.defaultModel } : {}),
20
+ });
21
+ }
22
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAahG,MAAM,UAAU,sBAAsB,CAAC,SAAmC,EAAE;IAC1E,OAAO,IAAI,iBAAiB,CAAC;QAC3B,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC;QACjC,cAAc,EAAE,kBAAkB;QAClC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,MAAM,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzF,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtE,CAAC,CAAC;AACL,CAAC"}