pi-anthropic-oauth 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -27,10 +27,36 @@ Claude Pro/Max
27
27
 
28
28
  ## Notes
29
29
 
30
+ - uses Pi's built-in Anthropic model list
30
31
  - sends Claude Code-like OAuth headers
31
32
  - rewrites Pi system identity where needed
32
33
  - auto-creates `~/.Claude Code` → `~/.pi` symlink
33
34
 
35
+ ## Custom models
36
+
37
+ To add another Anthropic model, create `~/.pi/agent/models.json`:
38
+
39
+ ```json
40
+ {
41
+ "providers": {
42
+ "anthropic": {
43
+ "baseUrl": "https://api.anthropic.com",
44
+ "apiKey": "REQUIRED_BY_PI_FOR_CUSTOM_MODELS",
45
+ "api": "anthropic-messages",
46
+ "models": [
47
+ {
48
+ "id": "your-model-id",
49
+ "name": "Your Model Name"
50
+ }
51
+ ]
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ > [!NOTE]
58
+ > Pi requires `baseUrl`, `apiKey`, and `api` when defining custom models in `models.json`. With this extension, requests normally authenticate through Claude Pro/Max OAuth after `/login anthropic`, so `apiKey` is present to satisfy Pi's config requirements and does not need to be a valid Anthropic API key.
59
+
34
60
  ## License
35
61
 
36
62
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-anthropic-oauth",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Claude Pro/Max OAuth extension for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/auth.ts CHANGED
@@ -152,7 +152,6 @@ export async function refreshAnthropicToken(
152
152
  grant_type: "refresh_token",
153
153
  client_id: CLIENT_ID,
154
154
  refresh_token: credentials.refresh,
155
- scope: SCOPES,
156
155
  }),
157
156
  },
158
157
  "Token refresh",
package/src/index.ts CHANGED
@@ -1,58 +1,27 @@
1
1
  import { existsSync, symlinkSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
- import type { ExtensionAPI, ProviderConfig } from "@mariozechner/pi-coding-agent";
4
+ import {
5
+ AuthStorage,
6
+ ModelRegistry,
7
+ type ExtensionAPI,
8
+ type ProviderConfig,
9
+ } from "@mariozechner/pi-coding-agent";
5
10
  import type { OAuthCredentials } from "@mariozechner/pi-ai";
6
11
  import { loginAnthropic, refreshAnthropicToken } from "./auth.js";
7
12
  import { streamAnthropicOAuth } from "./stream.js";
8
13
 
9
- const MODELS = [
10
- {
11
- id: "claude-opus-4-7",
12
- name: "Claude Opus 4.7",
13
- reasoning: true,
14
- input: ["text", "image"] as ("text" | "image")[],
15
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
16
- contextWindow: 1000000,
17
- maxTokens: 128000,
18
- },
19
- {
20
- id: "claude-opus-4-6",
21
- name: "Claude Opus 4.6",
22
- reasoning: true,
23
- input: ["text", "image"] as ("text" | "image")[],
24
- cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
25
- contextWindow: 1000000,
26
- maxTokens: 128000,
27
- },
28
- {
29
- id: "claude-sonnet-4-6",
30
- name: "Claude Sonnet 4.6",
31
- reasoning: true,
32
- input: ["text", "image"] as ("text" | "image")[],
33
- cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
34
- contextWindow: 1000000,
35
- maxTokens: 64000,
36
- },
37
- {
38
- id: "claude-haiku-4-5",
39
- name: "Claude Haiku 4.5",
40
- reasoning: true,
41
- input: ["text", "image"] as ("text" | "image")[],
42
- cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
43
- contextWindow: 200000,
44
- maxTokens: 64000,
45
- },
46
- {
47
- id: "claude-opus-4-5-20251101",
48
- name: "Claude Opus 4.5",
49
- reasoning: true,
50
- input: ["text", "image"] as ("text" | "image")[],
51
- cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
52
- contextWindow: 200000,
53
- maxTokens: 64000,
54
- }
55
- ];
14
+ const DEFAULT_OPUS_4_7: NonNullable<ProviderConfig["models"]>[number] = {
15
+ id: "claude-opus-4-7",
16
+ name: "Claude Opus 4.7",
17
+ api: "anthropic-messages",
18
+ reasoning: true,
19
+ input: ["text", "image"],
20
+ cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
21
+ contextWindow: 1000000,
22
+ maxTokens: 128000,
23
+ compat: undefined,
24
+ };
56
25
 
57
26
  function ensureClaudeCodeSymlink() {
58
27
  const target = join(homedir(), ".pi");
@@ -64,13 +33,38 @@ function ensureClaudeCodeSymlink() {
64
33
  }
65
34
  }
66
35
 
36
+ function getAnthropicModels(): NonNullable<ProviderConfig["models"]> {
37
+ const modelRegistry = ModelRegistry.create(AuthStorage.inMemory());
38
+ const models: NonNullable<ProviderConfig["models"]> = modelRegistry
39
+ .getAll()
40
+ .filter((model) => model.provider === "anthropic")
41
+ .map((model) => ({
42
+ id: model.id,
43
+ name: model.name,
44
+ api: model.api ?? "anthropic-messages",
45
+ reasoning: model.reasoning,
46
+ input: model.input,
47
+ cost: model.cost,
48
+ contextWindow: model.contextWindow,
49
+ maxTokens: model.maxTokens,
50
+ compat: model.compat,
51
+ }));
52
+
53
+ if (!models.some((model) => model.id === DEFAULT_OPUS_4_7.id)) {
54
+ models.push(DEFAULT_OPUS_4_7);
55
+ }
56
+
57
+ return models;
58
+ }
59
+
67
60
  export default function (pi: ExtensionAPI) {
68
61
  ensureClaudeCodeSymlink();
62
+ const models = getAnthropicModels();
63
+
69
64
  pi.registerProvider("anthropic", {
70
65
  baseUrl: "https://api.anthropic.com",
71
- apiKey: "ANTHROPIC_MAX_API_KEY",
72
- api: "anthropic-max-api",
73
- models: [...MODELS],
66
+ api: "anthropic-messages",
67
+ models,
74
68
  oauth: {
75
69
  name: "Claude Pro/Max",
76
70
  usesCallbackServer: true,
package/src/stream.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  type SimpleStreamOptions,
12
12
  type StopReason,
13
13
  } from "@mariozechner/pi-ai";
14
- import { USER_AGENT } from "./auth.js";
14
+ import { isClaudeOAuthAccessToken, USER_AGENT } from "./auth.js";
15
15
  import {
16
16
  convertPiMessagesToAnthropic,
17
17
  convertPiToolsToAnthropic,
@@ -105,8 +105,12 @@ export function streamAnthropicOAuth(
105
105
  };
106
106
 
107
107
  try {
108
- const apiKey = options?.apiKey ?? "";
109
- const isOAuth = true;
108
+ const apiKey = options?.apiKey;
109
+ if (!apiKey) {
110
+ throw new Error("No Anthropic auth available. Run /login anthropic.");
111
+ }
112
+
113
+ const isOAuth = isClaudeOAuthAccessToken(apiKey);
110
114
  const defaultHeaders = makeDefaultHeaders(isOAuth, options);
111
115
 
112
116
  if (isOAuth) defaultHeaders.authorization = `Bearer ${apiKey}`;