opencode-codebuddy-external-auth 1.0.21 → 1.0.23
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.js +85 -17
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -8,8 +8,8 @@ const PROVIDER_ID = "codebuddy-external";
|
|
|
8
8
|
const CONFIG = {
|
|
9
9
|
// IOA 版本使用 copilot.tencent.com 进行认证
|
|
10
10
|
serverUrl: "https://copilot.tencent.com",
|
|
11
|
-
// 真实对话 API
|
|
12
|
-
|
|
11
|
+
// 真实对话 API 路径(优先尝试 /v2/chat/completions)
|
|
12
|
+
chatCompletionsPaths: ["/v2/chat/completions", "/v2/plugin/chat/completions"],
|
|
13
13
|
// 平台标识
|
|
14
14
|
platform: "CLI",
|
|
15
15
|
appVersion: "2.37.20",
|
|
@@ -39,6 +39,35 @@ function generateUuid() {
|
|
|
39
39
|
}
|
|
40
40
|
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
41
41
|
}
|
|
42
|
+
function generateHexId(length) {
|
|
43
|
+
const bytes = Math.ceil(length / 2);
|
|
44
|
+
const buffer = new Uint8Array(bytes);
|
|
45
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
46
|
+
globalThis.crypto.getRandomValues(buffer);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (let i = 0; i < buffer.length; i += 1) {
|
|
50
|
+
buffer[i] = Math.floor(Math.random() * 256);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return Array.from(buffer)
|
|
54
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
55
|
+
.join("")
|
|
56
|
+
.slice(0, length);
|
|
57
|
+
}
|
|
58
|
+
function buildTraceHeaders() {
|
|
59
|
+
const traceId = generateHexId(32);
|
|
60
|
+
const spanId = generateHexId(16);
|
|
61
|
+
const parentSpanId = generateHexId(16);
|
|
62
|
+
const sampled = "1";
|
|
63
|
+
return {
|
|
64
|
+
"X-B3-TraceId": traceId,
|
|
65
|
+
"X-B3-ParentSpanId": parentSpanId,
|
|
66
|
+
"X-B3-SpanId": spanId,
|
|
67
|
+
"X-B3-Sampled": sampled,
|
|
68
|
+
b3: `${traceId}-${spanId}-${sampled}-${parentSpanId}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
42
71
|
function decodeJwtPayload(token) {
|
|
43
72
|
try {
|
|
44
73
|
const parts = token.split(".");
|
|
@@ -204,10 +233,25 @@ function pickFallbackModel(models) {
|
|
|
204
233
|
return "glm-4.7-ioa";
|
|
205
234
|
return models[0] || "";
|
|
206
235
|
}
|
|
236
|
+
function buildEnterpriseHeaders(accessToken) {
|
|
237
|
+
const tenantId = cachedTenantId || resolveTenantId(accessToken);
|
|
238
|
+
const enterpriseId = cachedEnterpriseId || resolveEnterpriseId(accessToken);
|
|
239
|
+
const resolvedTenantId = tenantId || enterpriseId;
|
|
240
|
+
if (tenantId)
|
|
241
|
+
cachedTenantId = tenantId;
|
|
242
|
+
if (enterpriseId)
|
|
243
|
+
cachedEnterpriseId = enterpriseId;
|
|
244
|
+
const headers = {};
|
|
245
|
+
if (resolvedTenantId)
|
|
246
|
+
headers["X-Tenant-Id"] = resolvedTenantId;
|
|
247
|
+
if (enterpriseId)
|
|
248
|
+
headers["X-Enterprise-Id"] = enterpriseId;
|
|
249
|
+
return headers;
|
|
250
|
+
}
|
|
207
251
|
async function buildAuthHeaders(accessToken) {
|
|
208
252
|
const payload = decodeJwtPayload(accessToken);
|
|
209
253
|
const roles = payload?.realm_access?.roles || payload?.resource_access?.account?.roles;
|
|
210
|
-
|
|
254
|
+
let tenantId = cachedTenantId || resolveTenantId(accessToken);
|
|
211
255
|
let enterpriseId = cachedEnterpriseId || resolveEnterpriseId(accessToken);
|
|
212
256
|
const userId = cachedUserId || resolveUserId(accessToken);
|
|
213
257
|
if (!enterpriseId) {
|
|
@@ -217,6 +261,9 @@ async function buildAuthHeaders(accessToken) {
|
|
|
217
261
|
cachedModelIds = config.models;
|
|
218
262
|
}
|
|
219
263
|
}
|
|
264
|
+
if (!tenantId && enterpriseId) {
|
|
265
|
+
tenantId = enterpriseId;
|
|
266
|
+
}
|
|
220
267
|
if (tenantId)
|
|
221
268
|
cachedTenantId = tenantId;
|
|
222
269
|
if (enterpriseId)
|
|
@@ -243,6 +290,8 @@ async function buildAuthHeaders(accessToken) {
|
|
|
243
290
|
const conversationId = generateUuid();
|
|
244
291
|
const messageId = generateUuid();
|
|
245
292
|
const requestId = messageId;
|
|
293
|
+
const traceHeaders = buildTraceHeaders();
|
|
294
|
+
const encodedUserId = userId ? encodeURIComponent(userId) : "";
|
|
246
295
|
const headers = {
|
|
247
296
|
"Accept": "application/json",
|
|
248
297
|
"Content-Type": "application/json",
|
|
@@ -259,13 +308,14 @@ async function buildAuthHeaders(accessToken) {
|
|
|
259
308
|
"X-Domain": CONFIG.domain,
|
|
260
309
|
"X-Product": CONFIG.product,
|
|
261
310
|
"User-Agent": `CLI/${CONFIG.appVersion} CodeBuddy/${CONFIG.appVersion}`,
|
|
311
|
+
...traceHeaders,
|
|
262
312
|
};
|
|
263
313
|
if (tenantId)
|
|
264
314
|
headers["X-Tenant-Id"] = tenantId;
|
|
265
315
|
if (enterpriseId)
|
|
266
316
|
headers["X-Enterprise-Id"] = enterpriseId;
|
|
267
|
-
if (
|
|
268
|
-
headers["X-User-Id"] =
|
|
317
|
+
if (encodedUserId)
|
|
318
|
+
headers["X-User-Id"] = encodedUserId;
|
|
269
319
|
return headers;
|
|
270
320
|
}
|
|
271
321
|
/**
|
|
@@ -492,21 +542,32 @@ async function executeViaAuthApi(openaiRequest, auth) {
|
|
|
492
542
|
response_format: openaiRequest.response_format || { type: "text" },
|
|
493
543
|
stream: openaiRequest.stream ?? true,
|
|
494
544
|
};
|
|
495
|
-
const doRequest = async (token) => {
|
|
545
|
+
const doRequest = async (token, path) => {
|
|
496
546
|
const headers = await buildAuthHeaders(token);
|
|
497
|
-
const response = await fetch(`${CONFIG.serverUrl}${
|
|
547
|
+
const response = await fetch(`${CONFIG.serverUrl}${path}`, {
|
|
498
548
|
method: "POST",
|
|
499
549
|
headers,
|
|
500
550
|
body: JSON.stringify(requestBody),
|
|
501
551
|
});
|
|
502
552
|
return response;
|
|
503
553
|
};
|
|
504
|
-
|
|
554
|
+
const requestWithFallback = async (token) => {
|
|
555
|
+
let lastResponse = null;
|
|
556
|
+
for (const path of CONFIG.chatCompletionsPaths) {
|
|
557
|
+
const response = await doRequest(token, path);
|
|
558
|
+
if (response.status !== 404) {
|
|
559
|
+
return response;
|
|
560
|
+
}
|
|
561
|
+
lastResponse = response;
|
|
562
|
+
}
|
|
563
|
+
return lastResponse || doRequest(token, CONFIG.chatCompletionsPaths[0]);
|
|
564
|
+
};
|
|
565
|
+
let response = await requestWithFallback(accessToken);
|
|
505
566
|
if ((response.status === 401 || response.status === 403) && auth.refresh) {
|
|
506
|
-
const refreshed = await refreshAccessToken(auth.refresh);
|
|
567
|
+
const refreshed = await refreshAccessToken(auth.refresh, accessToken);
|
|
507
568
|
if (refreshed?.accessToken) {
|
|
508
569
|
accessToken = refreshed.accessToken;
|
|
509
|
-
response = await
|
|
570
|
+
response = await requestWithFallback(accessToken);
|
|
510
571
|
}
|
|
511
572
|
}
|
|
512
573
|
if (!response.ok) {
|
|
@@ -716,15 +777,22 @@ async function pollForToken(state, expiresAt, signal) {
|
|
|
716
777
|
/**
|
|
717
778
|
* Refresh the access token
|
|
718
779
|
*/
|
|
719
|
-
async function refreshAccessToken(refreshToken) {
|
|
780
|
+
async function refreshAccessToken(refreshToken, accessToken) {
|
|
720
781
|
try {
|
|
782
|
+
const traceHeaders = buildTraceHeaders();
|
|
783
|
+
const headers = {
|
|
784
|
+
"Content-Type": "application/json",
|
|
785
|
+
"Accept": "application/json",
|
|
786
|
+
"X-Refresh-Token": refreshToken,
|
|
787
|
+
...traceHeaders,
|
|
788
|
+
};
|
|
789
|
+
if (accessToken) {
|
|
790
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
791
|
+
Object.assign(headers, buildEnterpriseHeaders(accessToken));
|
|
792
|
+
}
|
|
721
793
|
const response = await fetch(`${CONFIG.serverUrl}/v2/plugin/auth/token/refresh`, {
|
|
722
794
|
method: "POST",
|
|
723
|
-
headers
|
|
724
|
-
"Content-Type": "application/json",
|
|
725
|
-
"Accept": "application/json",
|
|
726
|
-
"Authorization": `Bearer ${refreshToken}`,
|
|
727
|
-
},
|
|
795
|
+
headers,
|
|
728
796
|
});
|
|
729
797
|
if (!response.ok) {
|
|
730
798
|
console.warn(`[codebuddy-external] Token refresh failed: ${response.status}`);
|
|
@@ -766,7 +834,7 @@ const CodeBuddyExternalAuthPlugin = async (_input) => {
|
|
|
766
834
|
if (auth.type !== "oauth" || !auth.refresh) {
|
|
767
835
|
return null;
|
|
768
836
|
}
|
|
769
|
-
const tokenData = await refreshAccessToken(auth.refresh);
|
|
837
|
+
const tokenData = await refreshAccessToken(auth.refresh, auth.access);
|
|
770
838
|
if (!tokenData) {
|
|
771
839
|
return null;
|
|
772
840
|
}
|