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.
Files changed (2) hide show
  1. package/dist/plugin.js +37 -18
  2. 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 端点 - Anthropic SDK 会自动追加 /messages
12
- apiBaseUrl: "https://copilot.tencent.com/plugin/v1",
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
- * This matches the behavior of `codebuddy /login` command
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: "GET",
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
- // 构造登录 URL(与 codebuddy CLI 输出的格式一致)
71
- const loginUrl = data.data.url ||
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
- Authorization: `Bearer ${refreshToken}`,
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
- // 关键修改:从服务端获取 state,而不是客户端生成
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codebuddy-external-auth",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "OpenCode plugin for CodeBuddy External (IOA) authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",