opencode-deepseek-auth 1.0.2 → 2.0.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/dist/plugin.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +55 -139
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/plugin.ts +75 -169
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,CAuFrB,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,113 +36,75 @@ 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
|
-
|
|
47
|
+
// This plugin only handles API key authentication
|
|
48
|
+
if (!auth?.apiKey) {
|
|
94
49
|
return null;
|
|
95
50
|
}
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
51
|
+
// Check if API key is in email:password or phone:password format
|
|
52
|
+
const credentialParts = auth.apiKey.split(':');
|
|
53
|
+
if (credentialParts.length === 2) {
|
|
54
|
+
const [identifier, password] = credentialParts;
|
|
55
|
+
// Convert credentials to DeepSeek access token
|
|
56
|
+
const result = await (0, auth_1.loginDeepSeek)(identifier, password);
|
|
57
|
+
if (result.type !== "success") {
|
|
58
|
+
console.error(`Failed to authenticate with provided credentials: ${result.error}`);
|
|
59
|
+
throw new Error(`Authentication failed: ${result.error}`);
|
|
60
|
+
}
|
|
61
|
+
// Successfully obtained access token from credentials
|
|
62
|
+
const accessToken = result.access;
|
|
63
|
+
// If models are defined in the provider, set cost to 0 to indicate free usage
|
|
64
|
+
if (provider.models) {
|
|
65
|
+
for (const model of Object.values(provider.models)) {
|
|
66
|
+
if (model) {
|
|
67
|
+
model.cost = { input: 0, output: 0 };
|
|
68
|
+
}
|
|
101
69
|
}
|
|
102
70
|
}
|
|
71
|
+
return {
|
|
72
|
+
apiKey: accessToken,
|
|
73
|
+
async fetch(input, init) {
|
|
74
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
75
|
+
if (!isDeepSeekRequest(input)) {
|
|
76
|
+
return fetch(input, init);
|
|
77
|
+
}
|
|
78
|
+
// Prepare the request with proper headers
|
|
79
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
|
|
80
|
+
// Make the API call
|
|
81
|
+
const response = await fetch(request, transformedInit);
|
|
82
|
+
// Transform response if needed
|
|
83
|
+
return transformDeepSeekResponse(response);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
103
86
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const latestAuth = await getAuth();
|
|
113
|
-
if (!isOAuthAuth(latestAuth)) {
|
|
114
|
-
return fetch(input, init);
|
|
115
|
-
}
|
|
116
|
-
let authRecord = latestAuth;
|
|
117
|
-
// Check if token has expired and refresh if possible
|
|
118
|
-
if (accessTokenExpired(authRecord)) {
|
|
119
|
-
const refreshed = await refreshAccessToken(authRecord, client);
|
|
120
|
-
if (!refreshed) {
|
|
121
|
-
console.warn("Could not refresh DeepSeek access token");
|
|
87
|
+
else {
|
|
88
|
+
// Assuming it's already a valid access token
|
|
89
|
+
const accessToken = auth.apiKey;
|
|
90
|
+
return {
|
|
91
|
+
apiKey: accessToken,
|
|
92
|
+
async fetch(input, init) {
|
|
93
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
94
|
+
if (!isDeepSeekRequest(input)) {
|
|
122
95
|
return fetch(input, init);
|
|
123
96
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const response = await fetch(request, transformedInit);
|
|
134
|
-
// Transform response if needed
|
|
135
|
-
return transformDeepSeekResponse(response);
|
|
136
|
-
},
|
|
137
|
-
};
|
|
97
|
+
// Prepare the request with proper headers
|
|
98
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
|
|
99
|
+
// Make the API call
|
|
100
|
+
const response = await fetch(request, transformedInit);
|
|
101
|
+
// Transform response if needed
|
|
102
|
+
return transformDeepSeekResponse(response);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
138
106
|
},
|
|
139
|
-
methods: [
|
|
140
|
-
{
|
|
141
|
-
label: "Login with DeepSeek Account",
|
|
142
|
-
type: "oauth",
|
|
143
|
-
authorize: async () => {
|
|
144
|
-
// Direct credential form for DeepSeek as they don't use standard OAuth
|
|
145
|
-
const form = {
|
|
146
|
-
fields: [
|
|
147
|
-
{
|
|
148
|
-
name: "email",
|
|
149
|
-
label: "Email",
|
|
150
|
-
type: "email",
|
|
151
|
-
required: true,
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
name: "password",
|
|
155
|
-
label: "Password",
|
|
156
|
-
type: "password",
|
|
157
|
-
required: true,
|
|
158
|
-
}
|
|
159
|
-
]
|
|
160
|
-
};
|
|
161
|
-
return {
|
|
162
|
-
url: "https://chat.deepseek.com",
|
|
163
|
-
instructions: "Enter your DeepSeek account credentials below. Your credentials will be stored securely and used for authentication.",
|
|
164
|
-
method: "form",
|
|
165
|
-
form: form,
|
|
166
|
-
callback: async (formData) => {
|
|
167
|
-
const email = formData.email;
|
|
168
|
-
const password = formData.password;
|
|
169
|
-
// Validate inputs
|
|
170
|
-
if (!email || !password) {
|
|
171
|
-
return {
|
|
172
|
-
type: "failed",
|
|
173
|
-
error: "Email and password are required for DeepSeek authentication"
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
// Attempt to log in using the provided credentials
|
|
177
|
-
const result = await (0, auth_1.loginDeepSeek)(email, password);
|
|
178
|
-
if (result.type === "success") {
|
|
179
|
-
// Cache the token
|
|
180
|
-
tokenCache.set(email, {
|
|
181
|
-
token: result.access,
|
|
182
|
-
expires: result.expires,
|
|
183
|
-
email: result.email || email
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
return result;
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
],
|
|
107
|
+
methods: [], // Empty methods array since we're using config-based auth
|
|
192
108
|
},
|
|
193
109
|
});
|
|
194
110
|
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/package.json
CHANGED
package/src/plugin.ts
CHANGED
|
@@ -2,8 +2,6 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
|
|
3
3
|
import { DEEPSEEK_PROVIDER_ID, DEEPSEEK_REDIRECT_URI, DEEPSEEK_BASE_HEADERS } from "./constants";
|
|
4
4
|
import {
|
|
5
|
-
authorizeDeepSeek,
|
|
6
|
-
exchangeDeepSeek,
|
|
7
5
|
loginDeepSeek
|
|
8
6
|
} from "./deepseek/auth";
|
|
9
7
|
import type { DeepSeekTokenExchangeResult } from "./deepseek/auth";
|
|
@@ -13,64 +11,9 @@ import type {
|
|
|
13
11
|
LoaderResult,
|
|
14
12
|
PluginContext,
|
|
15
13
|
PluginResult,
|
|
16
|
-
Provider
|
|
17
|
-
FormConfig
|
|
14
|
+
Provider
|
|
18
15
|
} from "./types";
|
|
19
16
|
|
|
20
|
-
// Keep track of active tokens
|
|
21
|
-
const tokenCache = new Map<string, { token: string, expires: number, email: string }>();
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Checks if an access token has expired
|
|
25
|
-
*/
|
|
26
|
-
function accessTokenExpired(authRecord: any): boolean {
|
|
27
|
-
const now = Date.now();
|
|
28
|
-
return !authRecord.expires || now >= authRecord.expires - 60000; // 1 minute before expiry
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Determines if the auth method is OAuth-based
|
|
33
|
-
*/
|
|
34
|
-
function isOAuthAuth(auth: any): boolean {
|
|
35
|
-
return auth && auth.type === "oauth";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Refresh access token if expired
|
|
40
|
-
*/
|
|
41
|
-
async function refreshAccessToken(authRecord: any, client: any): Promise<any | null> {
|
|
42
|
-
// DeepSeek doesn't have refresh tokens, so a full re-login is required
|
|
43
|
-
const email = authRecord.email;
|
|
44
|
-
const password = authRecord.password;
|
|
45
|
-
|
|
46
|
-
if (!email || !password) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const result = await loginDeepSeek(email, password);
|
|
51
|
-
if (result.type === "success") {
|
|
52
|
-
// Update the stored credentials
|
|
53
|
-
const newAuth = {
|
|
54
|
-
type: "oauth",
|
|
55
|
-
access: result.access,
|
|
56
|
-
expires: result.expires,
|
|
57
|
-
email: result.email || email,
|
|
58
|
-
password: password // Store password for refresh
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Update cache
|
|
62
|
-
tokenCache.set(email, {
|
|
63
|
-
token: result.access,
|
|
64
|
-
expires: result.expires,
|
|
65
|
-
email: result.email || email
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return newAuth;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
17
|
/**
|
|
75
18
|
* Prepares the request to DeepSeek API by setting appropriate headers and transforming the payload
|
|
76
19
|
*/
|
|
@@ -102,7 +45,6 @@ async function transformDeepSeekResponse(
|
|
|
102
45
|
response: Response
|
|
103
46
|
): Promise<Response> {
|
|
104
47
|
// For now, just pass through the response
|
|
105
|
-
// If needed, we could transform to match OpenAI format
|
|
106
48
|
return response;
|
|
107
49
|
}
|
|
108
50
|
|
|
@@ -117,8 +59,8 @@ function isDeepSeekRequest(input: Parameters<typeof fetch>[0]): boolean {
|
|
|
117
59
|
}
|
|
118
60
|
|
|
119
61
|
/**
|
|
120
|
-
* Registers the DeepSeek
|
|
121
|
-
*
|
|
62
|
+
* Registers the DeepSeek simple API key provider for Opencode.
|
|
63
|
+
* Users can configure their credentials using `opencode connect` with format: email:password or phone:password
|
|
122
64
|
*/
|
|
123
65
|
export const DeepSeekAuthPlugin = async (
|
|
124
66
|
{ client }: PluginContext,
|
|
@@ -127,122 +69,86 @@ export const DeepSeekAuthPlugin = async (
|
|
|
127
69
|
provider: DEEPSEEK_PROVIDER_ID,
|
|
128
70
|
loader: async (getAuth: GetAuth, provider: Provider): Promise<LoaderResult | null> => {
|
|
129
71
|
const auth = await getAuth();
|
|
130
|
-
|
|
72
|
+
|
|
73
|
+
// This plugin only handles API key authentication
|
|
74
|
+
if (!auth?.apiKey) {
|
|
131
75
|
return null;
|
|
132
76
|
}
|
|
133
77
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
78
|
+
// Check if API key is in email:password or phone:password format
|
|
79
|
+
const credentialParts = auth.apiKey.split(':');
|
|
80
|
+
if (credentialParts.length === 2) {
|
|
81
|
+
const [identifier, password] = credentialParts;
|
|
82
|
+
|
|
83
|
+
// Convert credentials to DeepSeek access token
|
|
84
|
+
const result = await loginDeepSeek(identifier, password);
|
|
85
|
+
if (result.type !== "success") {
|
|
86
|
+
console.error(`Failed to authenticate with provided credentials: ${result.error}`);
|
|
87
|
+
throw new Error(`Authentication failed: ${result.error}`);
|
|
140
88
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Get current auth state
|
|
152
|
-
const latestAuth = await getAuth();
|
|
153
|
-
if (!isOAuthAuth(latestAuth)) {
|
|
154
|
-
return fetch(input as RequestInfo, init);
|
|
89
|
+
|
|
90
|
+
// Successfully obtained access token from credentials
|
|
91
|
+
const accessToken = result.access;
|
|
92
|
+
|
|
93
|
+
// If models are defined in the provider, set cost to 0 to indicate free usage
|
|
94
|
+
if (provider.models) {
|
|
95
|
+
for (const model of Object.values(provider.models)) {
|
|
96
|
+
if (model) {
|
|
97
|
+
model.cost = { input: 0, output: 0 };
|
|
98
|
+
}
|
|
155
99
|
}
|
|
100
|
+
}
|
|
156
101
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!refreshed) {
|
|
163
|
-
console.warn("Could not refresh DeepSeek access token");
|
|
102
|
+
return {
|
|
103
|
+
apiKey: accessToken,
|
|
104
|
+
async fetch(input: Parameters<typeof fetch>[0], init?: RequestInit) {
|
|
105
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
106
|
+
if (!isDeepSeekRequest(input)) {
|
|
164
107
|
return fetch(input as RequestInfo, init);
|
|
165
108
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
109
|
+
|
|
110
|
+
// Prepare the request with proper headers
|
|
111
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(
|
|
112
|
+
input,
|
|
113
|
+
init,
|
|
114
|
+
accessToken
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Make the API call
|
|
118
|
+
const response = await fetch(request, transformedInit);
|
|
119
|
+
|
|
120
|
+
// Transform response if needed
|
|
121
|
+
return transformDeepSeekResponse(response);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
} else {
|
|
125
|
+
// Assuming it's already a valid access token
|
|
126
|
+
const accessToken = auth.apiKey;
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
apiKey: accessToken,
|
|
130
|
+
async fetch(input: Parameters<typeof fetch>[0], init?: RequestInit) {
|
|
131
|
+
// If this isn't a DeepSeek request, pass through normally
|
|
132
|
+
if (!isDeepSeekRequest(input)) {
|
|
133
|
+
return fetch(input as RequestInfo, init);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Prepare the request with proper headers
|
|
137
|
+
const { request, init: transformedInit } = prepareDeepSeekRequest(
|
|
138
|
+
input,
|
|
139
|
+
init,
|
|
140
|
+
accessToken
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Make the API call
|
|
144
|
+
const response = await fetch(request, transformedInit);
|
|
145
|
+
|
|
146
|
+
// Transform response if needed
|
|
147
|
+
return transformDeepSeekResponse(response);
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
188
151
|
},
|
|
189
|
-
|
|
190
|
-
{
|
|
191
|
-
label: "Login with DeepSeek Account",
|
|
192
|
-
type: "oauth",
|
|
193
|
-
authorize: async () => {
|
|
194
|
-
// Direct credential form for DeepSeek as they don't use standard OAuth
|
|
195
|
-
const form: FormConfig = {
|
|
196
|
-
fields: [
|
|
197
|
-
{
|
|
198
|
-
name: "email",
|
|
199
|
-
label: "Email",
|
|
200
|
-
type: "email",
|
|
201
|
-
required: true,
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: "password",
|
|
205
|
-
label: "Password",
|
|
206
|
-
type: "password",
|
|
207
|
-
required: true,
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
url: "https://chat.deepseek.com",
|
|
214
|
-
instructions: "Enter your DeepSeek account credentials below. Your credentials will be stored securely and used for authentication.",
|
|
215
|
-
method: "form",
|
|
216
|
-
form: form,
|
|
217
|
-
callback: async (formData: Record<string, string>): Promise<DeepSeekTokenExchangeResult> => {
|
|
218
|
-
const email = formData.email;
|
|
219
|
-
const password = formData.password;
|
|
220
|
-
|
|
221
|
-
// Validate inputs
|
|
222
|
-
if (!email || !password) {
|
|
223
|
-
return {
|
|
224
|
-
type: "failed",
|
|
225
|
-
error: "Email and password are required for DeepSeek authentication"
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Attempt to log in using the provided credentials
|
|
230
|
-
const result = await loginDeepSeek(email, password);
|
|
231
|
-
|
|
232
|
-
if (result.type === "success") {
|
|
233
|
-
// Cache the token
|
|
234
|
-
tokenCache.set(email, {
|
|
235
|
-
token: result.access,
|
|
236
|
-
expires: result.expires,
|
|
237
|
-
email: result.email || email
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return result;
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
],
|
|
152
|
+
methods: [], // Empty methods array since we're using config-based auth
|
|
247
153
|
},
|
|
248
154
|
});
|