opencode-deepseek-auth 1.0.3 → 2.0.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/README.md +65 -15
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +106 -165
- package/dist/plugin.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.js +43 -0
- package/src/deepseek/auth.js +93 -0
- package/src/plugin.js +166 -0
- package/src/plugin.ts +152 -207
- package/src/types.js +5 -0
- package/src/types.ts +5 -1
- package/test_auth_flow.js +122 -0
- package/test_auth_flow.ts +142 -0
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ Add the plugin to your OpenCode configuration file
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
### Method 1: Email/Password Authentication (Recommended)
|
|
29
|
+
|
|
28
30
|
1. **Login**: Run the authentication command in your terminal:
|
|
29
31
|
|
|
30
32
|
```bash
|
|
@@ -36,7 +38,28 @@ Add the plugin to your OpenCode configuration file
|
|
|
36
38
|
- Enter your email and password when prompted
|
|
37
39
|
- Your credentials will be stored securely for future use
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
### Method 2: Direct Access Token
|
|
42
|
+
|
|
43
|
+
If you have an existing DeepSeek access token, you can set it directly:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
opencode auth set --provider deepseek --api-key "your-deepseek-access-token"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Method 3: Config-based Authentication
|
|
50
|
+
|
|
51
|
+
You can also put DeepSeek credentials directly in your OpenCode config file:
|
|
52
|
+
|
|
53
|
+
**File:** `~/.config/opencode/opencode.json`
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"provider": {
|
|
57
|
+
"deepseek": {
|
|
58
|
+
"apiKey": "email:password" // Replace with your actual email and password
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
40
63
|
|
|
41
64
|
## Configuration
|
|
42
65
|
|
|
@@ -67,29 +90,51 @@ OpenCode config.
|
|
|
67
90
|
|
|
68
91
|
## Features
|
|
69
92
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
93
|
+
- **Dual Authentication Support**: Supports both email:password flow and direct access token authentication
|
|
94
|
+
- **Smart Format Recognition**: Automatically detects between email:password and regular API keys
|
|
95
|
+
- **Secure Storage**: Securely manages your DeepSeek credentials
|
|
96
|
+
- **Automatic Token Refresh**: Handles token lifecycle and refreshing when needed
|
|
97
|
+
- **OpenCode Integration**: Seamless integration with OpenCode's authentication system
|
|
98
|
+
- **Conflict Resolution**: Properly separates OpenCode's own API keys from DeepSeek authentication
|
|
99
|
+
- **Free Usage**: Leverage your existing DeepSeek account quota and features
|
|
75
100
|
|
|
76
101
|
## How It Works
|
|
77
102
|
|
|
78
|
-
The plugin
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
The plugin now supports two authentication modes:
|
|
104
|
+
|
|
105
|
+
**Email/Password Mode**:
|
|
106
|
+
1. Authenticate directly with DeepSeek's API using your email and password
|
|
107
|
+
2. Obtain an access token from DeepSeek
|
|
108
|
+
3. Use this token to make authenticated requests to DeepSeek's API
|
|
109
|
+
4. Manage token lifecycle and refresh when needed
|
|
110
|
+
|
|
111
|
+
**Direct Token Mode**:
|
|
112
|
+
1. Accept a direct access token as the API key
|
|
113
|
+
2. Use this token directly with DeepSeek's API
|
|
114
|
+
3. Handle request preparation and response transformation
|
|
115
|
+
|
|
116
|
+
## Authentication Format Detection
|
|
83
117
|
|
|
84
|
-
|
|
118
|
+
The plugin intelligently determines the authentication method:
|
|
119
|
+
|
|
120
|
+
- **Email:Password Detection**: If `apiKey` contains exactly one colon (`:`) and the first part contains `@` (email) or appears to be a phone number, it treats it as email:password
|
|
121
|
+
- **Explicit Provider Marking**: If the auth object has `provider: "deepseek"` or `type: "deepseek-email-password"`, it always treats the key as email:password format
|
|
122
|
+
- **Regular API Key**: Otherwise, treats the value as a direct access token
|
|
85
123
|
|
|
86
124
|
## Troubleshooting
|
|
87
125
|
|
|
88
|
-
###
|
|
126
|
+
### Authentication Issues
|
|
127
|
+
|
|
128
|
+
- **OpenCode API key conflict**: If OpenCode reports your API key is invalid, ensure you're using the correct format for your intended authentication method
|
|
129
|
+
- **Email:Password Format**: Must be in `email:password` or `phone:password` format. Special characters in passwords may require URL encoding
|
|
130
|
+
- **Direct Token**: If using a direct access token, ensure it's a valid DeepSeek access token
|
|
131
|
+
|
|
132
|
+
### Common Solutions
|
|
89
133
|
|
|
90
134
|
- Verify that your email and password are correct
|
|
91
|
-
- Check that your account is active and not suspended
|
|
92
|
-
-
|
|
135
|
+
- Check that your DeepSeek account is active and not suspended
|
|
136
|
+
- Ensure you're not hitting rate limits
|
|
137
|
+
- If experiencing conflicts, try explicit authentication method via the config file
|
|
93
138
|
|
|
94
139
|
### Token Expiration
|
|
95
140
|
|
|
@@ -98,6 +143,11 @@ The plugin handles token refresh automatically, but if you encounter authenticat
|
|
|
98
143
|
opencode auth login
|
|
99
144
|
```
|
|
100
145
|
|
|
146
|
+
Or clear the stored credentials:
|
|
147
|
+
```bash
|
|
148
|
+
opencode auth clear --provider deepseek
|
|
149
|
+
```
|
|
150
|
+
|
|
101
151
|
## Development
|
|
102
152
|
|
|
103
153
|
To develop on this plugin locally:
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginContext, PluginResult } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
* Registers the DeepSeek
|
|
4
|
-
*
|
|
3
|
+
* Registers the DeepSeek simple API key provider for Opencode.
|
|
4
|
+
* Users can configure their credentials using `opencode connect` with format: email:password or phone:password
|
|
5
5
|
*/
|
|
6
6
|
export declare const DeepSeekAuthPlugin: ({ client }: PluginContext) => Promise<PluginResult>;
|
|
7
7
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,aAAa,EACb,YAAY,EAEb,MAAM,SAAS,CAAC;AA8CjB;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,aAAa,KACxB,OAAO,CAAC,YAAY,CA0JrB,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -3,51 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DeepSeekAuthPlugin = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
5
|
const auth_1 = require("./deepseek/auth");
|
|
6
|
-
// Keep track of active tokens
|
|
7
|
-
const tokenCache = new Map();
|
|
8
|
-
/**
|
|
9
|
-
* Checks if an access token has expired
|
|
10
|
-
*/
|
|
11
|
-
function accessTokenExpired(authRecord) {
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
return !authRecord.expires || now >= authRecord.expires - 60000; // 1 minute before expiry
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Determines if the auth method is OAuth-based
|
|
17
|
-
*/
|
|
18
|
-
function isOAuthAuth(auth) {
|
|
19
|
-
return auth && auth.type === "oauth";
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Refresh access token if expired
|
|
23
|
-
*/
|
|
24
|
-
async function refreshAccessToken(authRecord, client) {
|
|
25
|
-
// DeepSeek doesn't have refresh tokens, so a full re-login is required
|
|
26
|
-
const email = authRecord.email;
|
|
27
|
-
const password = authRecord.password;
|
|
28
|
-
if (!email || !password) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
const result = await (0, auth_1.loginDeepSeek)(email, password);
|
|
32
|
-
if (result.type === "success") {
|
|
33
|
-
// Update the stored credentials
|
|
34
|
-
const newAuth = {
|
|
35
|
-
type: "oauth",
|
|
36
|
-
access: result.access,
|
|
37
|
-
expires: result.expires,
|
|
38
|
-
email: result.email || email,
|
|
39
|
-
password: password // Store password for refresh
|
|
40
|
-
};
|
|
41
|
-
// Update cache
|
|
42
|
-
tokenCache.set(email, {
|
|
43
|
-
token: result.access,
|
|
44
|
-
expires: result.expires,
|
|
45
|
-
email: result.email || email
|
|
46
|
-
});
|
|
47
|
-
return newAuth;
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
6
|
/**
|
|
52
7
|
* Prepares the request to DeepSeek API by setting appropriate headers and transforming the payload
|
|
53
8
|
*/
|
|
@@ -69,7 +24,6 @@ function prepareDeepSeekRequest(input, init, accessToken) {
|
|
|
69
24
|
*/
|
|
70
25
|
async function transformDeepSeekResponse(response) {
|
|
71
26
|
// For now, just pass through the response
|
|
72
|
-
// If needed, we could transform to match OpenAI format
|
|
73
27
|
return response;
|
|
74
28
|
}
|
|
75
29
|
/**
|
|
@@ -82,144 +36,131 @@ function isDeepSeekRequest(input) {
|
|
|
82
36
|
return urlString.includes('deepseek.com') || urlString.includes('/v1/chat/completions');
|
|
83
37
|
}
|
|
84
38
|
/**
|
|
85
|
-
* Registers the DeepSeek
|
|
86
|
-
*
|
|
39
|
+
* Registers the DeepSeek simple API key provider for Opencode.
|
|
40
|
+
* Users can configure their credentials using `opencode connect` with format: email:password or phone:password
|
|
87
41
|
*/
|
|
88
42
|
const DeepSeekAuthPlugin = async ({ client }) => ({
|
|
89
43
|
auth: {
|
|
90
44
|
provider: constants_1.DEEPSEEK_PROVIDER_ID,
|
|
91
45
|
loader: async (getAuth, provider) => {
|
|
92
46
|
const auth = await getAuth();
|
|
93
|
-
//
|
|
94
|
-
if (!
|
|
47
|
+
// This plugin only handles API key authentication
|
|
48
|
+
if (!auth?.apiKey) {
|
|
95
49
|
return null;
|
|
96
50
|
}
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
let resolvedAccessToken = "";
|
|
107
|
-
if (auth.apiKey) {
|
|
108
|
-
// Check if API key contains email:password or phone:password format
|
|
109
|
-
const credentialParts = auth.apiKey.split(':');
|
|
110
|
-
if (credentialParts.length === 2) {
|
|
111
|
-
const [identifier, password] = credentialParts;
|
|
112
|
-
// Try to login with these credentials
|
|
51
|
+
// Determine if this is a DeepSeek-specific authentication by checking the auth metadata
|
|
52
|
+
const isDeepSeekAuth = auth.provider === 'deepseek' || auth.type === 'deepseek-email-password';
|
|
53
|
+
const apiKeyValue = auth.apiKey;
|
|
54
|
+
// If explicitly marked as DeepSeek auth, process as email:password
|
|
55
|
+
if (isDeepSeekAuth) {
|
|
56
|
+
const parts = apiKeyValue.split(':');
|
|
57
|
+
if (parts.length === 2) {
|
|
58
|
+
const [identifier, password] = parts;
|
|
59
|
+
// Convert credentials to DeepSeek access token
|
|
113
60
|
const result = await (0, auth_1.loginDeepSeek)(identifier, password);
|
|
114
|
-
if (result.type
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
tokenCache.set(identifier, {
|
|
118
|
-
token: result.access,
|
|
119
|
-
expires: result.expires,
|
|
120
|
-
email: result.email || identifier
|
|
121
|
-
});
|
|
61
|
+
if (result.type !== "success") {
|
|
62
|
+
console.error(`Failed to authenticate with provided credentials: ${result.error}`);
|
|
63
|
+
throw new Error(`Authentication failed: ${result.error}`);
|
|
122
64
|
}
|
|
123
|
-
|
|
124
|
-
|
|
65
|
+
// Successfully obtained access token from credentials
|
|
66
|
+
const accessToken = result.access;
|
|
67
|
+
// If models are defined in the provider, set cost to 0 to indicate free usage
|
|
68
|
+
if (provider.models) {
|
|
69
|
+
for (const model of Object.values(provider.models)) {
|
|
70
|
+
if (model) {
|
|
71
|
+
model.cost = { input: 0, output: 0 };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
125
74
|
}
|
|
75
|
+
return {
|
|
76
|
+
apiKey: accessToken,
|
|
77
|
+
async fetch(input, init) {
|
|
78
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
79
|
+
if (!isDeepSeekRequest(input)) {
|
|
80
|
+
return fetch(input, init);
|
|
81
|
+
}
|
|
82
|
+
// Prepare the request with proper headers
|
|
83
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
|
|
84
|
+
// Make the API call
|
|
85
|
+
const response = await fetch(request, transformedInit);
|
|
86
|
+
// Transform response if needed
|
|
87
|
+
return transformDeepSeekResponse(response);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
126
90
|
}
|
|
127
91
|
else {
|
|
128
|
-
// If
|
|
129
|
-
|
|
92
|
+
// If explicitly marked as DeepSeek auth but not in correct format, throw error
|
|
93
|
+
throw new Error(`DeepSeek authentication requires email:password format`);
|
|
130
94
|
}
|
|
131
95
|
}
|
|
132
|
-
else
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
96
|
+
else {
|
|
97
|
+
// Otherwise, check if this looks like email:password format but isn't explicitly marked
|
|
98
|
+
const parts = apiKeyValue.split(':');
|
|
99
|
+
if (parts.length === 2) {
|
|
100
|
+
const [potentialIdentifier, potentialPassword] = parts;
|
|
101
|
+
// Heuristic: Check if it looks like an email or phone number format, and password seems reasonable
|
|
102
|
+
const isEmailFormat = potentialIdentifier.includes('@');
|
|
103
|
+
const isPhoneFormat = /^[0-9+\-\s()]+$/.test(potentialIdentifier.trim());
|
|
104
|
+
const isPasswordReasonableLength = potentialPassword.length >= 6;
|
|
105
|
+
// Only attempt email:password processing if both parts seem valid
|
|
106
|
+
if ((isEmailFormat || isPhoneFormat) && isPasswordReasonableLength) {
|
|
107
|
+
try {
|
|
108
|
+
// Try to use as email:password
|
|
109
|
+
const result = await (0, auth_1.loginDeepSeek)(potentialIdentifier, potentialPassword);
|
|
110
|
+
if (result.type === "success") {
|
|
111
|
+
// Successfully authenticated with email:password
|
|
112
|
+
const accessToken = result.access;
|
|
113
|
+
// If models are defined in the provider, set cost to 0 to indicate free usage
|
|
114
|
+
if (provider.models) {
|
|
115
|
+
for (const model of Object.values(provider.models)) {
|
|
116
|
+
if (model) {
|
|
117
|
+
model.cost = { input: 0, output: 0 };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
apiKey: accessToken,
|
|
123
|
+
async fetch(input, init) {
|
|
124
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
125
|
+
if (!isDeepSeekRequest(input)) {
|
|
126
|
+
return fetch(input, init);
|
|
127
|
+
}
|
|
128
|
+
// Prepare the request with proper headers
|
|
129
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
|
|
130
|
+
// Make the API call
|
|
131
|
+
const response = await fetch(request, transformedInit);
|
|
132
|
+
// Transform response if needed
|
|
133
|
+
return transformDeepSeekResponse(response);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.warn("Email:password authentication failed, treating as regular API key:", error);
|
|
140
|
+
// Fall through to treat as regular API key
|
|
141
|
+
}
|
|
141
142
|
}
|
|
142
|
-
authRecord = refreshed;
|
|
143
|
-
}
|
|
144
|
-
resolvedAccessToken = authRecord.access;
|
|
145
|
-
if (!resolvedAccessToken) {
|
|
146
|
-
return null;
|
|
147
143
|
}
|
|
144
|
+
// Default: treat as direct DeepSeek access token or other API key
|
|
145
|
+
const accessToken = apiKeyValue;
|
|
146
|
+
return {
|
|
147
|
+
apiKey: accessToken,
|
|
148
|
+
async fetch(input, init) {
|
|
149
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
150
|
+
if (!isDeepSeekRequest(input)) {
|
|
151
|
+
return fetch(input, init);
|
|
152
|
+
}
|
|
153
|
+
// Prepare the request with proper headers
|
|
154
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
|
|
155
|
+
// Make the API call
|
|
156
|
+
const response = await fetch(request, transformedInit);
|
|
157
|
+
// Transform response if needed
|
|
158
|
+
return transformDeepSeekResponse(response);
|
|
159
|
+
},
|
|
160
|
+
};
|
|
148
161
|
}
|
|
149
|
-
return {
|
|
150
|
-
apiKey: resolvedAccessToken || "",
|
|
151
|
-
async fetch(input, init) {
|
|
152
|
-
// If this isn't a DeepSeek request, pass through normally
|
|
153
|
-
if (!isDeepSeekRequest(input)) {
|
|
154
|
-
return fetch(input, init);
|
|
155
|
-
}
|
|
156
|
-
// Prepare the request with proper headers
|
|
157
|
-
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, resolvedAccessToken);
|
|
158
|
-
// Make the API call
|
|
159
|
-
const response = await fetch(request, transformedInit);
|
|
160
|
-
// Transform response if needed
|
|
161
|
-
return transformDeepSeekResponse(response);
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
162
|
},
|
|
165
|
-
methods: [
|
|
166
|
-
{
|
|
167
|
-
label: "Login with DeepSeek Account",
|
|
168
|
-
type: "oauth",
|
|
169
|
-
authorize: async () => {
|
|
170
|
-
// Direct credential form for DeepSeek as they don't use standard OAuth
|
|
171
|
-
const form = {
|
|
172
|
-
fields: [
|
|
173
|
-
{
|
|
174
|
-
name: "email",
|
|
175
|
-
label: "Email",
|
|
176
|
-
type: "email",
|
|
177
|
-
required: true,
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: "password",
|
|
181
|
-
label: "Password",
|
|
182
|
-
type: "password",
|
|
183
|
-
required: true,
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
};
|
|
187
|
-
return {
|
|
188
|
-
url: "https://chat.deepseek.com",
|
|
189
|
-
instructions: "Enter your DeepSeek account credentials below. Your credentials will be stored securely and used for authentication.",
|
|
190
|
-
method: "form",
|
|
191
|
-
form: form,
|
|
192
|
-
callback: async (formData) => {
|
|
193
|
-
const email = formData.email;
|
|
194
|
-
const password = formData.password;
|
|
195
|
-
// Validate inputs
|
|
196
|
-
if (!email || !password) {
|
|
197
|
-
return {
|
|
198
|
-
type: "failed",
|
|
199
|
-
error: "Email and password are required for DeepSeek authentication"
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
// Attempt to log in using the provided credentials
|
|
203
|
-
const result = await (0, auth_1.loginDeepSeek)(email, password);
|
|
204
|
-
if (result.type === "success") {
|
|
205
|
-
// Cache the token
|
|
206
|
-
tokenCache.set(email, {
|
|
207
|
-
token: result.access,
|
|
208
|
-
expires: result.expires,
|
|
209
|
-
email: result.email || email
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return result;
|
|
213
|
-
},
|
|
214
|
-
};
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
provider: constants_1.DEEPSEEK_PROVIDER_ID,
|
|
219
|
-
label: "Enter credentials (email:password or phone:password)",
|
|
220
|
-
type: "api",
|
|
221
|
-
},
|
|
222
|
-
],
|
|
163
|
+
methods: [], // Empty methods array since we're using config-based auth
|
|
223
164
|
},
|
|
224
165
|
});
|
|
225
166
|
exports.DeepSeekAuthPlugin = DeepSeekAuthPlugin;
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;AAEA,2CAAiG;AACjG,
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QAC/E,OAAO,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QAC/E,OAAO,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,OAAO,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;QACxB,GAAG,EAAE,MAAM,CAAC;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,UAAU,CAAC;QAClB,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;KAC1C,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,oBAAoB;IACnC,kBAAkB,EAAE,MAAM,CAAC;CAC5B"}
|
package/package.json
CHANGED
package/src/constants.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Constants used for DeepSeek authentication flows and API integration.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEEPSEEK_AUTH_SCOPES = exports.DEEPSEEK_PROVIDER_ID = exports.DEEPSEEK_BASE_HEADERS = exports.DEEPSEEK_LOGIN_URL = exports.DEEPSEEK_API_BASE_URL = exports.DEEPSEEK_REDIRECT_URI = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* OAuth redirect URI used by the local CLI callback server.
|
|
9
|
+
*/
|
|
10
|
+
exports.DEEPSEEK_REDIRECT_URI = "http://localhost:8085/oauth2callback";
|
|
11
|
+
/**
|
|
12
|
+
* Base endpoint for the DeepSeek API
|
|
13
|
+
*/
|
|
14
|
+
exports.DEEPSEEK_API_BASE_URL = "https://chat.deepseek.com/api/v0";
|
|
15
|
+
/**
|
|
16
|
+
* Login endpoint
|
|
17
|
+
*/
|
|
18
|
+
exports.DEEPSEEK_LOGIN_URL = `${exports.DEEPSEEK_API_BASE_URL}/users/login`;
|
|
19
|
+
/**
|
|
20
|
+
* Headers used for DeepSeek API requests
|
|
21
|
+
*/
|
|
22
|
+
exports.DEEPSEEK_BASE_HEADERS = {
|
|
23
|
+
"Host": "chat.deepseek.com",
|
|
24
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
25
|
+
"Accept": "application/json",
|
|
26
|
+
"Accept-Encoding": "gzip",
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
"x-client-platform": "web",
|
|
29
|
+
"x-client-version": "1.5.0",
|
|
30
|
+
"x-client-locale": "en_US",
|
|
31
|
+
"accept-charset": "UTF-8",
|
|
32
|
+
"origin": "https://chat.deepseek.com",
|
|
33
|
+
"referer": "https://chat.deepseek.com/"
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Provider identifier shared between the plugin loader and credential store.
|
|
37
|
+
*/
|
|
38
|
+
exports.DEEPSEEK_PROVIDER_ID = "deepseek";
|
|
39
|
+
exports.DEEPSEEK_AUTH_SCOPES = [
|
|
40
|
+
"user:profile",
|
|
41
|
+
"user:chat",
|
|
42
|
+
"user:tokens"
|
|
43
|
+
];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loginDeepSeek = loginDeepSeek;
|
|
4
|
+
exports.exchangeDeepSeek = exchangeDeepSeek;
|
|
5
|
+
exports.authorizeDeepSeek = authorizeDeepSeek;
|
|
6
|
+
/**
|
|
7
|
+
* Direct login to DeepSeek using email/password
|
|
8
|
+
*/
|
|
9
|
+
async function loginDeepSeek(email, password) {
|
|
10
|
+
try {
|
|
11
|
+
// Prepare login payload
|
|
12
|
+
const payload = {
|
|
13
|
+
email,
|
|
14
|
+
password,
|
|
15
|
+
device_id: "opencode-plugin",
|
|
16
|
+
os: "web"
|
|
17
|
+
};
|
|
18
|
+
// Make login request to DeepSeek API
|
|
19
|
+
const response = await fetch("https://chat.deepseek.com/api/v0/users/login", {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: {
|
|
22
|
+
"Host": "chat.deepseek.com",
|
|
23
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
24
|
+
"Accept": "application/json",
|
|
25
|
+
"Accept-Encoding": "gzip",
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
"x-client-platform": "web",
|
|
28
|
+
"x-client-version": "1.5.0",
|
|
29
|
+
"x-client-locale": "en_US",
|
|
30
|
+
"accept-charset": "UTF-8",
|
|
31
|
+
"origin": "https://chat.deepseek.com",
|
|
32
|
+
"referer": "https://chat.deepseek.com/"
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(payload)
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errorText = await response.text();
|
|
38
|
+
return {
|
|
39
|
+
type: "failed",
|
|
40
|
+
error: `Login failed with status ${response.status}: ${errorText}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
if (!data.data || !data.data.biz_data || !data.data.biz_data.user) {
|
|
45
|
+
return {
|
|
46
|
+
type: "failed",
|
|
47
|
+
error: "Invalid response format from DeepSeek login API"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const userData = data.data.biz_data.user;
|
|
51
|
+
const token = userData.token;
|
|
52
|
+
if (!token) {
|
|
53
|
+
return {
|
|
54
|
+
type: "failed",
|
|
55
|
+
error: "No token returned from DeepSeek login"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Calculate expiration (default to 1 hour if not provided)
|
|
59
|
+
// In the actual DeepSeek API response, look for expires_in field
|
|
60
|
+
const expiresIn = data.data.biz_data.expires_in || 3600;
|
|
61
|
+
const expiresAt = Date.now() + (expiresIn * 1000);
|
|
62
|
+
return {
|
|
63
|
+
type: "success",
|
|
64
|
+
access: token,
|
|
65
|
+
expires: expiresAt,
|
|
66
|
+
email: userData.email || email
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
type: "failed",
|
|
72
|
+
error: error instanceof Error ? error.message : "Unknown error occurred during login"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Exchange credentials for tokens (for direct login approach)
|
|
78
|
+
*/
|
|
79
|
+
async function exchangeDeepSeek(credentials) {
|
|
80
|
+
return loginDeepSeek(credentials.email, credentials.password);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Placeholder authorize function for compatibility
|
|
84
|
+
*/
|
|
85
|
+
async function authorizeDeepSeek() {
|
|
86
|
+
// Since DeepSeek doesn't use standard OAuth, we'll return info indicating
|
|
87
|
+
// that direct login should be used
|
|
88
|
+
return {
|
|
89
|
+
verifier: "", // Placeholder since we don't use PKCE
|
|
90
|
+
success: false,
|
|
91
|
+
url: "Direct login required - use email and password"
|
|
92
|
+
};
|
|
93
|
+
}
|