opencode-claude-auth 0.5.3 → 0.5.5
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 +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -97
- package/dist/index.js.map +1 -1
- package/installation.md +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# opencode-claude-auth
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/opencode-claude-auth)
|
|
4
|
+
[](https://github.com/griffinmartin/opencode-claude-auth/actions/workflows/ci.yml)
|
|
5
|
+
[](https://socket.dev/npm/package/opencode-claude-auth)
|
|
4
6
|
|
|
5
7
|
OpenCode plugin that uses your existing Claude Code credentials — no separate login needed.
|
|
6
8
|
|
|
7
9
|
## How it works
|
|
8
10
|
|
|
9
|
-
Claude Code stores OAuth tokens in the macOS Keychain (or `~/.claude/.credentials.json` on other platforms). This plugin reads those tokens and
|
|
11
|
+
Claude Code stores OAuth tokens in the macOS Keychain (or `~/.claude/.credentials.json` on other platforms). This plugin reads those tokens and writes them to OpenCode's `~/.local/share/opencode/auth.json`, so you don't need to log in twice. It re-syncs every 5 minutes to pick up token refreshes. If a token is near expiry, it runs the Claude CLI to trigger a refresh.
|
|
10
12
|
|
|
11
13
|
## Prerequisites
|
|
12
14
|
|
|
@@ -28,7 +30,7 @@ Fetch https://raw.githubusercontent.com/griffinmartin/opencode-claude-auth/main/
|
|
|
28
30
|
### Manual install
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
|
-
npm install opencode-claude-auth
|
|
33
|
+
npm install -g opencode-claude-auth
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
Then add to the `plugin` array in your `opencode.json`:
|
|
@@ -41,7 +43,7 @@ Then add to the `plugin` array in your `opencode.json`:
|
|
|
41
43
|
|
|
42
44
|
## Usage
|
|
43
45
|
|
|
44
|
-
Just run OpenCode. The plugin
|
|
46
|
+
Just run OpenCode. The plugin syncs your Claude Code credentials to OpenCode's auth.json and refreshes them in the background.
|
|
45
47
|
|
|
46
48
|
## Credential sources
|
|
47
49
|
|
|
@@ -63,12 +65,12 @@ The plugin checks these in order:
|
|
|
63
65
|
|
|
64
66
|
## How it works (technical)
|
|
65
67
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
68
|
+
- Reads OAuth tokens from macOS Keychain (`Claude Code-credentials` entry) or `~/.claude/.credentials.json` fallback
|
|
69
|
+
- Writes an `anthropic` entry to `~/.local/share/opencode/auth.json` in OpenCode's native OAuth format
|
|
70
|
+
- Preserves other provider entries already in auth.json
|
|
71
|
+
- Re-syncs credentials every 5 minutes in the background
|
|
72
|
+
- When a token is within 60 seconds of expiry, runs `claude` CLI to trigger a refresh
|
|
73
|
+
- If credentials are unavailable or unreadable, the plugin disables itself and OpenCode continues without Claude auth
|
|
72
74
|
|
|
73
75
|
## License
|
|
74
76
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAsEjD,QAAA,MAAM,MAAM,EAAE,MAmCb,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
3
|
import { dirname, join } from "node:path";
|
|
5
4
|
import { homedir } from "node:os";
|
|
6
5
|
import { readClaudeCredentials } from "./keychain.js";
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
join(homedir(), "
|
|
10
|
-
join(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
function getAuthJsonPath() {
|
|
7
|
+
if (process.platform === "win32") {
|
|
8
|
+
const appData = process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
|
|
9
|
+
return join(appData, "opencode", "auth.json");
|
|
10
|
+
}
|
|
11
|
+
return join(homedir(), ".local", "share", "opencode", "auth.json");
|
|
12
|
+
}
|
|
13
|
+
function syncAuthJson(creds) {
|
|
14
|
+
const authPath = getAuthJsonPath();
|
|
15
|
+
let auth = {};
|
|
16
|
+
if (existsSync(authPath)) {
|
|
17
|
+
const raw = readFileSync(authPath, "utf-8").trim();
|
|
18
|
+
if (raw) {
|
|
19
|
+
try {
|
|
20
|
+
auth = JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Malformed file, start fresh
|
|
20
24
|
}
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// Non-fatal: may not have write permissions
|
|
24
25
|
}
|
|
25
26
|
}
|
|
27
|
+
auth.anthropic = {
|
|
28
|
+
type: "oauth",
|
|
29
|
+
access: creds.accessToken,
|
|
30
|
+
refresh: creds.refreshToken,
|
|
31
|
+
expires: creds.expiresAt,
|
|
32
|
+
};
|
|
33
|
+
const dir = dirname(authPath);
|
|
34
|
+
if (!existsSync(dir)) {
|
|
35
|
+
mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
writeFileSync(authPath, JSON.stringify(auth, null, 2), "utf-8");
|
|
26
38
|
}
|
|
27
39
|
function refreshViaCli() {
|
|
28
40
|
try {
|
|
@@ -37,92 +49,49 @@ function refreshViaCli() {
|
|
|
37
49
|
// Non-fatal: Claude CLI may not be available
|
|
38
50
|
}
|
|
39
51
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return readFileSync(promptPath, "utf-8");
|
|
52
|
+
function refreshIfNeeded() {
|
|
53
|
+
let creds = readClaudeCredentials();
|
|
54
|
+
if (creds && creds.expiresAt > Date.now() + 60_000) {
|
|
55
|
+
return creds;
|
|
45
56
|
}
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
// Token is expired or near expiry, try CLI refresh
|
|
58
|
+
refreshViaCli();
|
|
59
|
+
creds = readClaudeCredentials();
|
|
60
|
+
if (creds && creds.expiresAt > Date.now() + 60_000) {
|
|
61
|
+
return creds;
|
|
48
62
|
}
|
|
63
|
+
return null;
|
|
49
64
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
refreshViaCli();
|
|
61
|
-
const afterRefresh = readClaudeCredentials();
|
|
62
|
-
if (afterRefresh && afterRefresh.expiresAt > Date.now() + 60_000) {
|
|
63
|
-
current = afterRefresh;
|
|
64
|
-
onRefresh(current);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
throw new Error("opencode-claude-auth: Token expired and refresh failed. " +
|
|
68
|
-
"Re-authenticate with Claude Code by running `claude` in your terminal.");
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const headers = new Headers(init?.headers);
|
|
73
|
-
headers.set("Authorization", `Bearer ${current.accessToken}`);
|
|
74
|
-
return fetch(fetchInput, { ...init, headers });
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
const plugin = async (input) => {
|
|
78
|
-
const creds = readClaudeCredentials();
|
|
65
|
+
const SYNC_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
|
66
|
+
const plugin = async () => {
|
|
67
|
+
let creds = null;
|
|
68
|
+
try {
|
|
69
|
+
creds = readClaudeCredentials();
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.warn("opencode-claude-auth: Failed to read Claude Code credentials:", err instanceof Error ? err.message : err);
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
79
75
|
if (!creds) {
|
|
76
|
+
console.warn("opencode-claude-auth: No Claude Code credentials found. " +
|
|
77
|
+
"Plugin disabled. Run `claude` to authenticate.");
|
|
80
78
|
return {};
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
// Sync credentials to auth.json on startup
|
|
81
|
+
syncAuthJson(creds);
|
|
82
|
+
// Keep auth.json synced, refreshing via CLI if token is near expiry
|
|
83
|
+
setInterval(() => {
|
|
84
|
+
try {
|
|
85
|
+
const fresh = refreshIfNeeded();
|
|
86
|
+
if (fresh) {
|
|
87
|
+
syncAuthJson(fresh);
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
expires: initialCreds.expiresAt,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
return {
|
|
101
|
-
apiKey: "oauth",
|
|
102
|
-
fetch: createAuthFetch(initialCreds, (updated) => {
|
|
103
|
-
void input.client.auth.set({
|
|
104
|
-
path: { id: "anthropic" },
|
|
105
|
-
body: {
|
|
106
|
-
type: "oauth",
|
|
107
|
-
access: updated.accessToken,
|
|
108
|
-
refresh: updated.refreshToken,
|
|
109
|
-
expires: updated.expiresAt,
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
}),
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
methods: [],
|
|
116
|
-
};
|
|
117
|
-
return {
|
|
118
|
-
auth,
|
|
119
|
-
async "experimental.chat.system.transform"(hookInput, output) {
|
|
120
|
-
if (hookInput.model.providerID !== "anthropic")
|
|
121
|
-
return;
|
|
122
|
-
const prompt = loadSessionPrompt();
|
|
123
|
-
output.system.unshift(prompt);
|
|
124
|
-
},
|
|
125
|
-
};
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Non-fatal
|
|
92
|
+
}
|
|
93
|
+
}, SYNC_INTERVAL);
|
|
94
|
+
return {};
|
|
126
95
|
};
|
|
127
96
|
export default plugin;
|
|
128
97
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/installation.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-claude-auth",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "OpenCode plugin that uses your Claude Code credentials — no separate login needed.",
|
|
5
|
+
"preferGlobal": true,
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"module": "dist/index.js",
|
|
7
8
|
"types": "dist/index.d.ts",
|