opencode-deepseek-auth 1.0.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { PluginContext, PluginResult } from "./types";
2
2
  /**
3
- * Registers the DeepSeek Auth provider for Opencode, handling auth, request rewriting,
4
- * and response normalization for DeepSeek requests.
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
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAGV,aAAa,EACb,YAAY,EAGb,MAAM,SAAS,CAAC;AAqGjB;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,aAAa,KACxB,OAAO,CAAC,YAAY,CAuJrB,CAAC"}
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,144 +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 Auth provider for Opencode, handling auth, request rewriting,
86
- * and response normalization for DeepSeek requests.
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
- // Handle both OAuth-based auth and API key formatted credentials
94
- if (!isOAuthAuth(auth) && !auth.apiKey) {
47
+ // This plugin only handles API key authentication
48
+ if (!auth?.apiKey) {
95
49
  return null;
96
50
  }
97
- // If models are defined in the provider, set cost to 0 to indicate free usage
98
- if (provider.models) {
99
- for (const model of Object.values(provider.models)) {
100
- if (model) {
101
- model.cost = { input: 0, output: 0 };
102
- }
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}`);
103
60
  }
104
- }
105
- // Handle the case where API key contains email:password
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
113
- const result = await (0, auth_1.loginDeepSeek)(identifier, password);
114
- if (result.type === "success") {
115
- resolvedAccessToken = result.access;
116
- // Cache this for potential future use
117
- tokenCache.set(identifier, {
118
- token: result.access,
119
- expires: result.expires,
120
- email: result.email || identifier
121
- });
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
+ }
122
69
  }
123
- else {
124
- throw new Error(`Failed to authenticate with provided credentials: ${result.error}`);
125
- }
126
- }
127
- else {
128
- // If it's not in email:password format, assume it's already a valid access token
129
- resolvedAccessToken = auth.apiKey;
130
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
+ };
131
86
  }
132
- else if (isOAuthAuth(auth)) {
133
- // Handle normal OAuth flow
134
- let authRecord = auth;
135
- // Check if token has expired and refresh if possible
136
- if (accessTokenExpired(authRecord)) {
137
- const refreshed = await refreshAccessToken(authRecord, client);
138
- if (!refreshed) {
139
- console.warn("Could not refresh DeepSeek access token");
140
- return null;
141
- }
142
- authRecord = refreshed;
143
- }
144
- resolvedAccessToken = authRecord.access;
145
- if (!resolvedAccessToken) {
146
- return null;
147
- }
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)) {
95
+ return fetch(input, init);
96
+ }
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
+ };
148
105
  }
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
106
  },
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
- ],
107
+ methods: [], // Empty methods array since we're using config-based auth
223
108
  },
224
109
  });
225
110
  exports.DeepSeekAuthPlugin = DeepSeekAuthPlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-deepseek-auth",
3
- "version": "1.0.3",
3
+ "version": "2.0.0",
4
4
  "description": "Authenticate the Opencode CLI with your DeepSeek account credentials",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
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,160 +59,96 @@ function isDeepSeekRequest(input: Parameters<typeof fetch>[0]): boolean {
117
59
  }
118
60
 
119
61
  /**
120
- * Registers the DeepSeek Auth provider for Opencode, handling auth, request rewriting,
121
- * and response normalization for DeepSeek requests.
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,
125
67
  ): Promise<PluginResult> => ({
126
68
  auth: {
127
69
  provider: DEEPSEEK_PROVIDER_ID,
128
- loader: async (getAuth: GetAuth, provider: Provider): Promise<LoaderResult | null> => {
129
- const auth = await getAuth();
130
-
131
- // Handle both OAuth-based auth and API key formatted credentials
132
- if (!isOAuthAuth(auth) && !auth.apiKey) {
133
- return null;
134
- }
135
-
136
- // If models are defined in the provider, set cost to 0 to indicate free usage
137
- if (provider.models) {
138
- for (const model of Object.values(provider.models)) {
139
- if (model) {
140
- model.cost = { input: 0, output: 0 };
141
- }
142
- }
143
- }
144
-
145
- // Handle the case where API key contains email:password
146
- let resolvedAccessToken = "";
147
- if (auth.apiKey) {
148
- // Check if API key contains email:password or phone:password format
149
- const credentialParts = auth.apiKey.split(':');
150
- if (credentialParts.length === 2) {
151
- const [identifier, password] = credentialParts;
152
- // Try to login with these credentials
153
- const result = await loginDeepSeek(identifier, password);
154
- if (result.type === "success") {
155
- resolvedAccessToken = result.access;
156
- // Cache this for potential future use
157
- tokenCache.set(identifier, {
158
- token: result.access,
159
- expires: result.expires,
160
- email: result.email || identifier
161
- });
162
- } else {
163
- throw new Error(`Failed to authenticate with provided credentials: ${result.error}`);
164
- }
165
- } else {
166
- // If it's not in email:password format, assume it's already a valid access token
167
- resolvedAccessToken = auth.apiKey;
168
- }
169
- } else if (isOAuthAuth(auth)) {
170
- // Handle normal OAuth flow
171
- let authRecord = auth;
172
-
173
- // Check if token has expired and refresh if possible
174
- if (accessTokenExpired(authRecord)) {
175
- const refreshed = await refreshAccessToken(authRecord, client);
176
- if (!refreshed) {
177
- console.warn("Could not refresh DeepSeek access token");
178
- return null;
179
- }
180
- authRecord = refreshed;
181
- }
182
-
183
- resolvedAccessToken = authRecord.access;
184
- if (!resolvedAccessToken) {
185
- return null;
186
- }
187
- }
188
-
189
- return {
190
- apiKey: resolvedAccessToken || "",
191
- async fetch(input: Parameters<typeof fetch>[0], init?: RequestInit) {
192
- // If this isn't a DeepSeek request, pass through normally
193
- if (!isDeepSeekRequest(input)) {
194
- return fetch(input as RequestInfo, init);
195
- }
196
-
197
- // Prepare the request with proper headers
198
- const { request, init: transformedInit } = prepareDeepSeekRequest(
199
- input,
200
- init,
201
- resolvedAccessToken
202
- );
203
-
204
- // Make the API call
205
- const response = await fetch(request, transformedInit);
206
-
207
- // Transform response if needed
208
- return transformDeepSeekResponse(response);
209
- },
210
- };
211
- },
212
- methods: [
213
- {
214
- label: "Login with DeepSeek Account",
215
- type: "oauth",
216
- authorize: async () => {
217
- // Direct credential form for DeepSeek as they don't use standard OAuth
218
- const form: FormConfig = {
219
- fields: [
220
- {
221
- name: "email",
222
- label: "Email",
223
- type: "email",
224
- required: true,
225
- },
226
- {
227
- name: "password",
228
- label: "Password",
229
- type: "password",
230
- required: true,
231
- }
232
- ]
233
- };
234
-
235
- return {
236
- url: "https://chat.deepseek.com",
237
- instructions: "Enter your DeepSeek account credentials below. Your credentials will be stored securely and used for authentication.",
238
- method: "form",
239
- form: form,
240
- callback: async (formData: Record<string, string>): Promise<DeepSeekTokenExchangeResult> => {
241
- const email = formData.email;
242
- const password = formData.password;
243
-
244
- // Validate inputs
245
- if (!email || !password) {
246
- return {
247
- type: "failed",
248
- error: "Email and password are required for DeepSeek authentication"
249
- };
250
- }
70
+ loader: async (getAuth: GetAuth, provider: Provider): Promise<LoaderResult | null> => {
71
+ const auth = await getAuth();
72
+
73
+ // This plugin only handles API key authentication
74
+ if (!auth?.apiKey) {
75
+ return null;
76
+ }
77
+
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}`);
88
+ }
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
+ }
99
+ }
100
+ }
101
+
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)) {
107
+ return fetch(input as RequestInfo, init);
108
+ }
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
+ );
251
142
 
252
- // Attempt to log in using the provided credentials
253
- const result = await loginDeepSeek(email, password);
254
-
255
- if (result.type === "success") {
256
- // Cache the token
257
- tokenCache.set(email, {
258
- token: result.access,
259
- expires: result.expires,
260
- email: result.email || email
261
- });
262
- }
263
-
264
- return result;
265
- },
266
- };
267
- },
268
- },
269
- {
270
- provider: DEEPSEEK_PROVIDER_ID,
271
- label: "Enter credentials (email:password or phone:password)",
272
- type: "api",
273
- },
274
- ],
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
+ }
151
+ },
152
+ methods: [], // Empty methods array since we're using config-based auth
275
153
  },
276
154
  });