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 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,CA2HrB,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,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 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
- if (!isOAuthAuth(auth)) {
47
+ // This plugin only handles API key authentication
48
+ if (!auth?.apiKey) {
94
49
  return null;
95
50
  }
96
- // If models are defined in the provider, set cost to 0 to indicate free usage
97
- if (provider.models) {
98
- for (const model of Object.values(provider.models)) {
99
- if (model) {
100
- model.cost = { input: 0, output: 0 };
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
- return {
105
- apiKey: "",
106
- async fetch(input, init) {
107
- // If this isn't a DeepSeek request, pass through normally
108
- if (!isDeepSeekRequest(input)) {
109
- return fetch(input, init);
110
- }
111
- // Get current auth state
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
- authRecord = refreshed;
125
- }
126
- const accessToken = authRecord.access;
127
- if (!accessToken) {
128
- return fetch(input, init);
129
- }
130
- // Prepare the request with proper headers
131
- const { request, init: transformedInit } = prepareDeepSeekRequest(input, init, accessToken);
132
- // Make the API call
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;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;AAEA,2CAAiG;AACjG,0CAIyB;AAYzB,8BAA8B;AAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAA6D,CAAC;AAExF;;GAEG;AACH,SAAS,kBAAkB,CAAC,UAAe;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,GAAG,IAAI,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,yBAAyB;AAC5F,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,IAAS;IAC5B,OAAO,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAAC,UAAe,EAAE,MAAW;IAC5D,uEAAuE;IACvE,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;IAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC;IAErC,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAa,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,gCAAgC;QAChC,MAAM,OAAO,GAAG;YACd,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;YAC5B,QAAQ,EAAE,QAAQ,CAAE,6BAA6B;SAClD,CAAC;QAEF,eAAe;QACf,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE;YACpB,KAAK,EAAE,MAAM,CAAC,MAAM;YACpB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;SAC7B,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAC7B,KAAkC,EAClC,IAA6B,EAC7B,WAAmB;IAEnB,+CAA+C;IAC/C,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAEpC,2BAA2B;IAC3B,eAAe,CAAC,OAAO,GAAG;QACxB,GAAG,eAAe,CAAC,OAAO;QAC1B,GAAG,iCAAqB;QACxB,eAAe,EAAE,UAAU,WAAW,EAAE;KACzC,CAAC;IAEF,wBAAwB;IACxB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAoB,EAAE,eAAe,CAAC,CAAC;IAEnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACtC,QAAkB;IAElB,0CAA0C;IAC1C,uDAAuD;IACvD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,KAAkC;IAC3D,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YACxC,KAAiB,CAAC,GAAG,IAAI,EAAE,CAAC;IAC9C,OAAO,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;AAC1F,CAAC;AAED;;;GAGG;AACI,MAAM,kBAAkB,GAAG,KAAK,EACrC,EAAE,MAAM,EAAiB,EACF,EAAE,CAAC,CAAC;IAC3B,IAAI,EAAE;QACJ,QAAQ,EAAE,gCAAoB;QAC9B,MAAM,EAAE,KAAK,EAAE,OAAgB,EAAE,QAAkB,EAAgC,EAAE;YACnF,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,8EAA8E;YAC9E,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACpB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnD,IAAI,KAAK,EAAE,CAAC;wBACV,KAAK,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;oBACvC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,KAAK,CAAC,KAAK,CAAC,KAAkC,EAAE,IAAkB;oBAChE,0DAA0D;oBAC1D,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC9B,OAAO,KAAK,CAAC,KAAoB,EAAE,IAAI,CAAC,CAAC;oBAC3C,CAAC;oBAED,yBAAyB;oBACzB,MAAM,UAAU,GAAG,MAAM,OAAO,EAAE,CAAC;oBACnC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC7B,OAAO,KAAK,CAAC,KAAoB,EAAE,IAAI,CAAC,CAAC;oBAC3C,CAAC;oBAED,IAAI,UAAU,GAAG,UAAU,CAAC;oBAE5B,qDAAqD;oBACrD,IAAI,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;wBACnC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;wBAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;4BACxD,OAAO,KAAK,CAAC,KAAoB,EAAE,IAAI,CAAC,CAAC;wBAC3C,CAAC;wBACD,UAAU,GAAG,SAAS,CAAC;oBACzB,CAAC;oBAED,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC;oBACtC,IAAI,CAAC,WAAW,EAAE,CAAC;wBACjB,OAAO,KAAK,CAAC,KAAoB,EAAE,IAAI,CAAC,CAAC;oBAC3C,CAAC;oBAED,0CAA0C;oBAC1C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,sBAAsB,CAC/D,KAAK,EACL,IAAI,EACJ,WAAW,CACZ,CAAC;oBAEF,oBAAoB;oBACpB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;oBAEvD,+BAA+B;oBAC/B,OAAO,yBAAyB,CAAC,QAAQ,CAAC,CAAC;gBAC7C,CAAC;aACF,CAAC;QACJ,CAAC;QACA,OAAO,EAAE;YACP;gBACE,KAAK,EAAE,6BAA6B;gBACpC,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,KAAK,IAAI,EAAE;oBACpB,uEAAuE;oBACvE,MAAM,IAAI,GAAe;wBACvB,MAAM,EAAE;4BACN;gCACE,IAAI,EAAE,OAAO;gCACb,KAAK,EAAE,OAAO;gCACd,IAAI,EAAE,OAAO;gCACb,QAAQ,EAAE,IAAI;6BACf;4BACD;gCACE,IAAI,EAAE,UAAU;gCAChB,KAAK,EAAE,UAAU;gCACjB,IAAI,EAAE,UAAU;gCAChB,QAAQ,EAAE,IAAI;6BACf;yBACF;qBACF,CAAC;oBAEF,OAAO;wBACL,GAAG,EAAE,2BAA2B;wBAChC,YAAY,EAAE,sHAAsH;wBACpI,MAAM,EAAE,MAAM;wBACd,IAAI,EAAE,IAAI;wBACV,QAAQ,EAAE,KAAK,EAAE,QAAgC,EAAwC,EAAE;4BACzF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;4BAC7B,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;4BAEnC,kBAAkB;4BAClB,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;gCACxB,OAAO;oCACL,IAAI,EAAE,QAAQ;oCACd,KAAK,EAAE,6DAA6D;iCACrE,CAAC;4BACJ,CAAC;4BAED,mDAAmD;4BACnD,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAa,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;4BAEpD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gCAC9B,kBAAkB;gCAClB,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE;oCACpB,KAAK,EAAE,MAAM,CAAC,MAAM;oCACpB,OAAO,EAAE,MAAM,CAAC,OAAO;oCACvB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;iCAC7B,CAAC,CAAC;4BACL,CAAC;4BAED,OAAO,MAAM,CAAC;wBAChB,CAAC;qBACF,CAAC;gBACJ,CAAC;aACF;SACF;KACH;CACF,CAAC,CAAC;AA7HU,QAAA,kBAAkB,sBA6H5B"}
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.2",
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,8 +59,8 @@ 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,
@@ -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
- if (!isOAuthAuth(auth)) {
72
+
73
+ // This plugin only handles API key authentication
74
+ if (!auth?.apiKey) {
131
75
  return null;
132
76
  }
133
77
 
134
- // If models are defined in the provider, set cost to 0 to indicate free usage
135
- if (provider.models) {
136
- for (const model of Object.values(provider.models)) {
137
- if (model) {
138
- model.cost = { input: 0, output: 0 };
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
- return {
144
- apiKey: "",
145
- async fetch(input: Parameters<typeof fetch>[0], init?: RequestInit) {
146
- // If this isn't a DeepSeek request, pass through normally
147
- if (!isDeepSeekRequest(input)) {
148
- return fetch(input as RequestInfo, init);
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
- let authRecord = latestAuth;
158
-
159
- // Check if token has expired and refresh if possible
160
- if (accessTokenExpired(authRecord)) {
161
- const refreshed = await refreshAccessToken(authRecord, client);
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
- authRecord = refreshed;
167
- }
168
-
169
- const accessToken = authRecord.access;
170
- if (!accessToken) {
171
- return fetch(input as RequestInfo, init);
172
- }
173
-
174
- // Prepare the request with proper headers
175
- const { request, init: transformedInit } = prepareDeepSeekRequest(
176
- input,
177
- init,
178
- accessToken
179
- );
180
-
181
- // Make the API call
182
- const response = await fetch(request, transformedInit);
183
-
184
- // Transform response if needed
185
- return transformDeepSeekResponse(response);
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
- methods: [
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
  });