pi-anthropic-oauth 0.1.16 → 0.2.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/README.md CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  Use Claude Pro/Max in Pi with browser OAuth.
6
6
 
7
+ > [!IMPORTANT]
8
+ > This release requires Pi 0.80.8 or later in the 0.80.x release line.
9
+
7
10
  ## Features
8
11
 
9
12
  - Claude Pro/Max login from `/login`
@@ -11,7 +14,7 @@ Use Claude Pro/Max in Pi with browser OAuth.
11
14
  - Claude Code-compatible OAuth headers and prompt shaping
12
15
  - No Anthropic API key needed
13
16
  - Uses Pi's Anthropic model registry
14
- - Adds Claude Opus 4.8 by default
17
+ - Adds Claude Opus 4.8 and Claude Fable 5 by default
15
18
  - Auto-creates `~/.Claude Code` → `~/.pi` symlink when missing
16
19
 
17
20
  ## Quick start
@@ -34,6 +37,8 @@ Claude Pro/Max
34
37
 
35
38
  Complete the browser login. After login, select an Anthropic model in Pi.
36
39
 
40
+ When the extension is active, `/login` includes `Claude Pro/Max` and Anthropic models show `(sub)` after OAuth login.
41
+
37
42
  Update with:
38
43
 
39
44
  ```bash
@@ -96,7 +101,7 @@ To add another Anthropic model, create `~/.pi/agent/models.json`:
96
101
  ```
97
102
 
98
103
  > [!NOTE]
99
- > Custom models are not needed for Claude Opus 4.8; this extension registers `claude-opus-4-8` automatically.
104
+ > Opus 4.8 and Fable 5 are included in Pi's built-in Anthropic model catalog.
100
105
  >
101
106
  > 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`, so `apiKey` is only a placeholder to satisfy Pi's config requirements and does not need to be a valid Anthropic API key.
102
107
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-anthropic-oauth",
3
- "version": "0.1.16",
3
+ "version": "0.2.0",
4
4
  "description": "Claude Pro/Max OAuth extension for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,12 +38,12 @@
38
38
  "@anthropic-ai/sdk": "^0.52.0"
39
39
  },
40
40
  "peerDependencies": {
41
- "@earendil-works/pi-ai": "*",
42
- "@earendil-works/pi-coding-agent": "*"
41
+ "@earendil-works/pi-ai": "^0.80.8",
42
+ "@earendil-works/pi-coding-agent": "^0.80.8"
43
43
  },
44
44
  "devDependencies": {
45
- "@earendil-works/pi-ai": "^0.74.0",
46
- "@earendil-works/pi-coding-agent": "^0.74.0",
45
+ "@earendil-works/pi-ai": "^0.80.8",
46
+ "@earendil-works/pi-coding-agent": "^0.80.8",
47
47
  "typescript": "^5.9.3"
48
48
  }
49
49
  }
package/src/index.ts CHANGED
@@ -2,8 +2,6 @@ import { existsSync, symlinkSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import {
5
- AuthStorage,
6
- ModelRegistry,
7
5
  type ExtensionAPI,
8
6
  type ProviderConfig,
9
7
  } from "@earendil-works/pi-coding-agent";
@@ -11,19 +9,6 @@ import type { OAuthCredentials } from "@earendil-works/pi-ai";
11
9
  import { loginAnthropic, refreshAnthropicToken } from "./auth.js";
12
10
  import { streamAnthropicOAuth } from "./stream.js";
13
11
 
14
- const DEFAULT_OPUS_4_8: NonNullable<ProviderConfig["models"]>[number] = {
15
- id: "claude-opus-4-8",
16
- name: "Claude Opus 4.8",
17
- api: "anthropic-messages",
18
- reasoning: true,
19
- thinkingLevelMap: { xhigh: "xhigh" },
20
- input: ["text", "image"],
21
- cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
22
- contextWindow: 1000000,
23
- maxTokens: 128000,
24
- compat: undefined,
25
- };
26
-
27
12
  function ensureClaudeCodeSymlink() {
28
13
  const target = join(homedir(), ".pi");
29
14
  const link = join(homedir(), ".Claude Code");
@@ -34,34 +19,12 @@ function ensureClaudeCodeSymlink() {
34
19
  }
35
20
  }
36
21
 
37
- function getAnthropicModels(): NonNullable<ProviderConfig["models"]> {
38
- const modelRegistry = ModelRegistry.create(AuthStorage.inMemory());
39
- const models: NonNullable<ProviderConfig["models"]> = modelRegistry
40
- .getAll()
41
- .filter((model) => model.provider === "anthropic")
42
- // Spread the full registry model so every field (thinkingLevelMap,
43
- // baseUrl, headers, compat, and anything added in future) propagates
44
- // automatically instead of being silently dropped by a hand-picked list.
45
- .map((model) => ({
46
- ...model,
47
- api: model.api ?? "anthropic-messages",
48
- }));
49
-
50
- if (!models.some((model) => model.id === DEFAULT_OPUS_4_8.id)) {
51
- models.push(DEFAULT_OPUS_4_8);
52
- }
53
-
54
- return models;
55
- }
56
-
57
22
  export default function (pi: ExtensionAPI) {
58
23
  ensureClaudeCodeSymlink();
59
- const models = getAnthropicModels();
60
24
 
61
25
  pi.registerProvider("anthropic", {
62
26
  baseUrl: "https://api.anthropic.com",
63
27
  api: "anthropic-messages",
64
- models,
65
28
  oauth: {
66
29
  name: "Claude Pro/Max",
67
30
  usesCallbackServer: true,
package/src/stream.ts CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  type Model,
11
11
  type SimpleStreamOptions,
12
12
  type StopReason,
13
- streamSimpleAnthropic,
14
13
  } from "@earendil-works/pi-ai";
15
14
  import { isClaudeOAuthAccessToken, USER_AGENT } from "./auth.js";
16
15
  import {
@@ -82,7 +81,11 @@ function makeDefaultHeaders(
82
81
  ) {
83
82
  continue;
84
83
  }
85
- headers[key] = value;
84
+ const existingKey = Object.keys(headers).find(
85
+ (header) => header.toLowerCase() === normalizedKey,
86
+ );
87
+ if (existingKey) delete headers[existingKey];
88
+ if (value !== null) headers[key] = value;
86
89
  }
87
90
  }
88
91
 
@@ -94,16 +97,6 @@ export function streamAnthropicOAuth(
94
97
  context: Context,
95
98
  options?: SimpleStreamOptions,
96
99
  ): AssistantMessageEventStream {
97
- // pi-ai keys streamSimple by `api`, so this runs for every anthropic-messages
98
- // model (copilot, openrouter, …). Delegate non-anthropic providers.
99
- if (model.provider !== "anthropic") {
100
- return streamSimpleAnthropic(
101
- model as Model<"anthropic-messages">,
102
- context,
103
- options,
104
- );
105
- }
106
-
107
100
  const stream = createAssistantMessageEventStream();
108
101
 
109
102
  void (async () => {