pi-anthropic-oauth 0.1.7 → 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 +27 -2
- package/package.json +1 -1
- package/src/auth.ts +0 -1
- package/src/convert.ts +0 -2
- package/src/index.ts +46 -34
- package/src/stream.ts +16 -4
package/README.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# Pi Anthropic OAuth
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/pi-anthropic-oauth) [](https://www.npmjs.com/package/pi-anthropic-oauth) [](https://nodejs.org)
|
|
4
4
|
|
|
5
5
|
Claude Pro/Max OAuth extension for Pi.
|
|
6
6
|
|
|
7
7
|
> [!WARNING]
|
|
8
8
|
> Use this at your own risk. This may go against Anthropic's terms.
|
|
9
|
-
> I do not recommend using this with gsd2 or any extensions that will cause heavy token usage.
|
|
10
9
|
|
|
11
10
|
## Install
|
|
12
11
|
|
|
@@ -28,10 +27,36 @@ Claude Pro/Max
|
|
|
28
27
|
|
|
29
28
|
## Notes
|
|
30
29
|
|
|
30
|
+
- uses Pi's built-in Anthropic model list
|
|
31
31
|
- sends Claude Code-like OAuth headers
|
|
32
32
|
- rewrites Pi system identity where needed
|
|
33
33
|
- auto-creates `~/.Claude Code` → `~/.pi` symlink
|
|
34
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
|
+
|
|
35
60
|
## License
|
|
36
61
|
|
|
37
62
|
MIT
|
package/package.json
CHANGED
package/src/auth.ts
CHANGED
package/src/convert.ts
CHANGED
|
@@ -98,8 +98,6 @@ export function convertPiMessagesToAnthropic(
|
|
|
98
98
|
for (const block of message.content) {
|
|
99
99
|
if (block.type === "text" && block.text.trim()) {
|
|
100
100
|
blocks.push({ type: "text", text: sanitizeSurrogates(block.text) });
|
|
101
|
-
} else if (block.type === "thinking" && block.thinking.trim()) {
|
|
102
|
-
blocks.push({ type: "text", text: sanitizeSurrogates(block.thinking) });
|
|
103
101
|
} else if (block.type === "toolCall") {
|
|
104
102
|
blocks.push({
|
|
105
103
|
type: "tool_use",
|
package/src/index.ts
CHANGED
|
@@ -1,40 +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
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
id: "claude-sonnet-4-6",
|
|
21
|
-
name: "Claude Sonnet 4.6",
|
|
22
|
-
reasoning: true,
|
|
23
|
-
input: ["text", "image"] as ("text" | "image")[],
|
|
24
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
25
|
-
contextWindow: 1000000,
|
|
26
|
-
maxTokens: 64000,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: "claude-haiku-4-5",
|
|
30
|
-
name: "Claude Haiku 4.5",
|
|
31
|
-
reasoning: true,
|
|
32
|
-
input: ["text", "image"] as ("text" | "image")[],
|
|
33
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
34
|
-
contextWindow: 200000,
|
|
35
|
-
maxTokens: 64000,
|
|
36
|
-
},
|
|
37
|
-
];
|
|
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
|
+
};
|
|
38
25
|
|
|
39
26
|
function ensureClaudeCodeSymlink() {
|
|
40
27
|
const target = join(homedir(), ".pi");
|
|
@@ -42,17 +29,42 @@ function ensureClaudeCodeSymlink() {
|
|
|
42
29
|
if (existsSync(target) && !existsSync(link)) {
|
|
43
30
|
try {
|
|
44
31
|
symlinkSync(target, link);
|
|
45
|
-
} catch {}
|
|
32
|
+
} catch { }
|
|
46
33
|
}
|
|
47
34
|
}
|
|
48
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
|
+
|
|
49
60
|
export default function (pi: ExtensionAPI) {
|
|
50
61
|
ensureClaudeCodeSymlink();
|
|
62
|
+
const models = getAnthropicModels();
|
|
63
|
+
|
|
51
64
|
pi.registerProvider("anthropic", {
|
|
52
65
|
baseUrl: "https://api.anthropic.com",
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
models: [...MODELS],
|
|
66
|
+
api: "anthropic-messages",
|
|
67
|
+
models,
|
|
56
68
|
oauth: {
|
|
57
69
|
name: "Claude Pro/Max",
|
|
58
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,
|
|
@@ -63,8 +63,16 @@ function makeDefaultHeaders(
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (options?.headers) {
|
|
66
|
-
for (const [key, value] of Object.entries(options.headers))
|
|
66
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
67
|
+
const normalizedKey = key.toLowerCase();
|
|
68
|
+
if (
|
|
69
|
+
isOAuth &&
|
|
70
|
+
(normalizedKey === "x-api-key" || normalizedKey === "authorization")
|
|
71
|
+
) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
67
74
|
headers[key] = value;
|
|
75
|
+
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
return headers;
|
|
@@ -97,8 +105,12 @@ export function streamAnthropicOAuth(
|
|
|
97
105
|
};
|
|
98
106
|
|
|
99
107
|
try {
|
|
100
|
-
const apiKey = options?.apiKey
|
|
101
|
-
|
|
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);
|
|
102
114
|
const defaultHeaders = makeDefaultHeaders(isOAuth, options);
|
|
103
115
|
|
|
104
116
|
if (isOAuth) defaultHeaders.authorization = `Bearer ${apiKey}`;
|