opencode-codebuddy-external-auth 1.0.3 → 1.0.5
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 +37 -18
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -8,8 +8,9 @@ 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 端点 - IOA 版本不需要 /plugin 前缀
|
|
12
|
+
// OpenAI SDK 会自动追加 /chat/completions
|
|
13
|
+
apiBaseUrl: "https://copilot.tencent.com/v1",
|
|
13
14
|
// 平台标识
|
|
14
15
|
platform: "CLI",
|
|
15
16
|
appVersion: "2.37.20",
|
|
@@ -44,19 +45,27 @@ function createAuthenticatedFetch(accessToken, userId) {
|
|
|
44
45
|
// ============================================================================
|
|
45
46
|
/**
|
|
46
47
|
* Request auth state from server - the state is generated by server, not client!
|
|
47
|
-
*
|
|
48
|
+
*
|
|
49
|
+
* 关键发现:
|
|
50
|
+
* 1. 使用 POST 方法,不是 GET
|
|
51
|
+
* 2. 路径是 /v2/plugin/auth/state,不是 /plugin/auth/state
|
|
52
|
+
* 3. 需要设置 X-No-Authorization 等 Headers
|
|
48
53
|
*/
|
|
49
54
|
async function requestAuthState() {
|
|
50
|
-
// 构造与 codebuddy CLI 相同的请求
|
|
51
|
-
// codebuddy 输出的 URL: https://copilot.tencent.com/login?platform=CLI&state=xxx&ioa=1
|
|
52
55
|
const params = new URLSearchParams({
|
|
53
56
|
platform: CONFIG.platform,
|
|
54
57
|
ioa: "1",
|
|
55
58
|
});
|
|
56
|
-
const response = await fetch(`${CONFIG.serverUrl}/plugin/auth/state?${params.toString()}`, {
|
|
57
|
-
method: "
|
|
59
|
+
const response = await fetch(`${CONFIG.serverUrl}/v2/plugin/auth/state?${params.toString()}`, {
|
|
60
|
+
method: "POST", // 关键:使用 POST 方法
|
|
58
61
|
headers: {
|
|
59
|
-
Accept: "application/json",
|
|
62
|
+
"Accept": "application/json",
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
// 这些 Headers 告诉服务端跳过认证检查
|
|
65
|
+
"X-No-Authorization": "true",
|
|
66
|
+
"X-No-User-Id": "true",
|
|
67
|
+
"X-No-Enterprise-Id": "true",
|
|
68
|
+
"X-No-Department-Info": "true",
|
|
60
69
|
},
|
|
61
70
|
});
|
|
62
71
|
if (!response.ok) {
|
|
@@ -64,11 +73,11 @@ async function requestAuthState() {
|
|
|
64
73
|
throw new Error(`Auth state request failed: ${response.status} - ${text}`);
|
|
65
74
|
}
|
|
66
75
|
const data = await response.json();
|
|
67
|
-
if (!data.data?.state) {
|
|
76
|
+
if (data.code !== 0 || !data.data?.state) {
|
|
68
77
|
throw new Error(`Invalid auth state response: ${JSON.stringify(data)}`);
|
|
69
78
|
}
|
|
70
|
-
//
|
|
71
|
-
const loginUrl = data.data.
|
|
79
|
+
// 使用服务端返回的 authUrl,或构造默认 URL
|
|
80
|
+
const loginUrl = data.data.authUrl ||
|
|
72
81
|
`${CONFIG.serverUrl}/login?platform=${CONFIG.platform}&state=${data.data.state}&ioa=1`;
|
|
73
82
|
return {
|
|
74
83
|
state: data.data.state,
|
|
@@ -77,6 +86,8 @@ async function requestAuthState() {
|
|
|
77
86
|
}
|
|
78
87
|
/**
|
|
79
88
|
* Poll for token after user completes browser authentication
|
|
89
|
+
*
|
|
90
|
+
* 注意:token 端点也需要使用 /v2 前缀
|
|
80
91
|
*/
|
|
81
92
|
async function pollForToken(state, expiresAt, signal) {
|
|
82
93
|
const pollInterval = 3000; // 3 seconds
|
|
@@ -86,16 +97,20 @@ async function pollForToken(state, expiresAt, signal) {
|
|
|
86
97
|
}
|
|
87
98
|
await sleep(pollInterval);
|
|
88
99
|
try {
|
|
89
|
-
const response = await fetch(`${CONFIG.serverUrl}/plugin/auth/token?state=${state}`, {
|
|
100
|
+
const response = await fetch(`${CONFIG.serverUrl}/v2/plugin/auth/token?state=${state}`, {
|
|
90
101
|
method: "GET",
|
|
91
102
|
headers: {
|
|
92
|
-
Accept: "application/json",
|
|
103
|
+
"Accept": "application/json",
|
|
104
|
+
"X-No-Authorization": "true",
|
|
105
|
+
"X-No-User-Id": "true",
|
|
106
|
+
"X-No-Enterprise-Id": "true",
|
|
107
|
+
"X-No-Department-Info": "true",
|
|
93
108
|
},
|
|
94
109
|
signal,
|
|
95
110
|
});
|
|
96
111
|
if (response.ok) {
|
|
97
112
|
const data = await response.json();
|
|
98
|
-
if (data.data?.accessToken) {
|
|
113
|
+
if (data.code === 0 && data.data?.accessToken) {
|
|
99
114
|
return data.data;
|
|
100
115
|
}
|
|
101
116
|
}
|
|
@@ -115,11 +130,12 @@ async function pollForToken(state, expiresAt, signal) {
|
|
|
115
130
|
*/
|
|
116
131
|
async function refreshAccessToken(refreshToken) {
|
|
117
132
|
try {
|
|
118
|
-
const response = await fetch(`${CONFIG.serverUrl}/plugin/auth/token/refresh`, {
|
|
133
|
+
const response = await fetch(`${CONFIG.serverUrl}/v2/plugin/auth/token/refresh`, {
|
|
119
134
|
method: "POST",
|
|
120
135
|
headers: {
|
|
121
136
|
"Content-Type": "application/json",
|
|
122
|
-
|
|
137
|
+
"Accept": "application/json",
|
|
138
|
+
"Authorization": `Bearer ${refreshToken}`,
|
|
123
139
|
},
|
|
124
140
|
});
|
|
125
141
|
if (!response.ok) {
|
|
@@ -127,6 +143,10 @@ async function refreshAccessToken(refreshToken) {
|
|
|
127
143
|
return null;
|
|
128
144
|
}
|
|
129
145
|
const data = await response.json();
|
|
146
|
+
if (data.code !== 0) {
|
|
147
|
+
console.warn(`[codebuddy-external] Token refresh error: ${data.msg}`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
130
150
|
return data.data || null;
|
|
131
151
|
}
|
|
132
152
|
catch (error) {
|
|
@@ -173,8 +193,7 @@ const CodeBuddyExternalAuthPlugin = async (_input) => {
|
|
|
173
193
|
label: "IOA 登录 (浏览器)",
|
|
174
194
|
type: "oauth",
|
|
175
195
|
async authorize() {
|
|
176
|
-
//
|
|
177
|
-
// 这与 codebuddy CLI 的 /login 命令行为一致
|
|
196
|
+
// 从服务端获取 state(服务端生成,非客户端)
|
|
178
197
|
const authState = await requestAuthState();
|
|
179
198
|
const expiresAt = Date.now() + 10 * 60 * 1000; // 10 minutes
|
|
180
199
|
return {
|