opencode-codebuddy-external-auth 1.0.17 → 1.0.19

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/README.md CHANGED
@@ -18,8 +18,12 @@ npm install opencode-codebuddy-external-auth
18
18
  "plugin": ["opencode-codebuddy-external-auth"],
19
19
  "provider": {
20
20
  "codebuddy-external": {
21
- "npm": "@ai-sdk/anthropic",
21
+ "npm": "@ai-sdk/openai-compatible",
22
22
  "name": "CodeBuddy External (IOA)",
23
+ "options": {
24
+ "compatibility": "compatible",
25
+ "baseURL": "https://copilot.tencent.com"
26
+ },
23
27
  "models": {
24
28
  "claude-4.5": { "name": "Claude Sonnet 4.5 (x2.20)", "contextLength": 176000 },
25
29
  "claude-opus-4.5": { "name": "Claude Opus 4.5 (x3.33)", "contextLength": 176000 },
@@ -41,7 +45,7 @@ npm install opencode-codebuddy-external-auth
41
45
  }
42
46
  }
43
47
  },
44
- "model": "codebuddy-external/claude-4.5"
48
+ "model": "codebuddy-external/glm-4.7-ioa"
45
49
  }
46
50
  ```
47
51
 
@@ -53,6 +57,9 @@ npm install opencode-codebuddy-external-auth
53
57
  4. 在浏览器中完成 IOA 登录
54
58
  5. 返回终端开始使用
55
59
 
60
+ > 日志会输出实际发送的模型:`[codebuddy-external] 使用模型: <model>`
61
+ > 如模型不在可用列表,直接报错并提示更换(不再自动回退)
62
+
56
63
  ## 支持的模型
57
64
 
58
65
  根据 CodeBuddy IOA 版本配置(2026-01-24):
@@ -96,10 +103,13 @@ npm install opencode-codebuddy-external-auth
96
103
 
97
104
  ## 认证流程
98
105
 
99
- 1. 插件请求 `/plugin/auth/state` 获取认证状态和 URL
106
+ 1. 插件请求 `/v2/plugin/auth/state` 获取认证状态和 URL
100
107
  2. 用户在浏览器中打开 URL 完成 IOA 登录
101
- 3. 插件轮询 `/plugin/auth/token` 获取访问令牌
102
- 4. Token 到期前自动通过 `/plugin/auth/token/refresh` 刷新
108
+ 3. 插件轮询 `/v2/plugin/auth/token` 获取访问令牌
109
+ 4. Token 到期前自动通过 `/v2/plugin/auth/token/refresh` 刷新
110
+
111
+ > 插件会自动解析 access token 获取 tenant/user/enterprise;
112
+ > enterprise 缺失时会请求 `/console/enterprises/{tenantId}/config/models` 兜底
103
113
 
104
114
  ## License
105
115
 
package/dist/plugin.js CHANGED
@@ -59,6 +59,16 @@ function extractTenantIdFromIss(iss) {
59
59
  const match = iss.match(/realms\/sso-([^/]+)$/);
60
60
  return match?.[1] || "";
61
61
  }
62
+ function extractTenantIdFromRoles(roles) {
63
+ if (!roles || roles.length === 0)
64
+ return "";
65
+ for (const role of roles) {
66
+ const match = role.match(/ent-(?:member|plugin-enabled|group):([A-Za-z0-9_-]+)/);
67
+ if (match?.[1])
68
+ return match[1];
69
+ }
70
+ return "";
71
+ }
62
72
  let warnedTenantId = false;
63
73
  let warnedEnterpriseId = false;
64
74
  let warnedUserId = false;
@@ -66,8 +76,10 @@ function resolveTenantId(accessToken) {
66
76
  if (CONFIG.tenantId)
67
77
  return CONFIG.tenantId;
68
78
  const payload = decodeJwtPayload(accessToken);
79
+ const roles = payload?.realm_access?.roles || payload?.resource_access?.account?.roles;
69
80
  return (payload?.tenant_id ||
70
81
  payload?.tenantId ||
82
+ extractTenantIdFromRoles(roles) ||
71
83
  extractTenantIdFromIss(payload?.iss));
72
84
  }
73
85
  function resolveEnterpriseId(accessToken) {
@@ -94,9 +106,53 @@ function resolveModel(inputModel) {
94
106
  let cachedTenantId = "";
95
107
  let cachedEnterpriseId = "";
96
108
  let cachedUserId = "";
97
- async function fetchEnterpriseId(accessToken, tenantId) {
109
+ let cachedModelIds = [];
110
+ let warnedModelFallback = false;
111
+ let warnedIdSummary = false;
112
+ function extractModelIds(payload) {
113
+ const results = [];
114
+ const collect = (list) => {
115
+ if (!Array.isArray(list))
116
+ return;
117
+ for (const item of list) {
118
+ if (!item)
119
+ continue;
120
+ if (typeof item === "string") {
121
+ results.push(item);
122
+ continue;
123
+ }
124
+ if (typeof item === "object") {
125
+ const id = item.id ||
126
+ item.model ||
127
+ item.modelId ||
128
+ item.model_id ||
129
+ item.code ||
130
+ item.name;
131
+ if (typeof id === "string")
132
+ results.push(id);
133
+ if (Array.isArray(item.models))
134
+ collect(item.models);
135
+ }
136
+ }
137
+ };
138
+ const candidates = [
139
+ payload?.data?.models,
140
+ payload?.data?.modelList,
141
+ payload?.data?.availableModels,
142
+ payload?.models,
143
+ payload?.modelList,
144
+ payload?.availableModels,
145
+ payload?.data?.modelGroups,
146
+ payload?.modelGroups,
147
+ ];
148
+ for (const list of candidates) {
149
+ collect(list);
150
+ }
151
+ return Array.from(new Set(results)).filter(Boolean);
152
+ }
153
+ async function fetchConfigModels(accessToken, tenantId) {
98
154
  if (!tenantId)
99
- return "";
155
+ return { enterpriseId: "", models: [] };
100
156
  try {
101
157
  const url = `${CONFIG.serverUrl}/console/enterprises/${tenantId}/config/models`;
102
158
  const response = await fetch(url, {
@@ -108,21 +164,44 @@ async function fetchEnterpriseId(accessToken, tenantId) {
108
164
  },
109
165
  });
110
166
  if (!response.ok) {
111
- return "";
167
+ return { enterpriseId: "", models: [] };
112
168
  }
113
169
  const data = await response.json();
114
- return data?.data?.enterpriseId || data?.enterpriseId || "";
170
+ const enterpriseId = data?.data?.enterpriseId || data?.enterpriseId || "";
171
+ const models = extractModelIds(data);
172
+ return { enterpriseId, models };
115
173
  }
116
174
  catch {
117
- return "";
175
+ return { enterpriseId: "", models: [] };
176
+ }
177
+ }
178
+ async function getSupportedModels(accessToken, tenantId) {
179
+ if (cachedModelIds.length > 0)
180
+ return cachedModelIds;
181
+ const config = await fetchConfigModels(accessToken, tenantId);
182
+ if (config.enterpriseId && !cachedEnterpriseId) {
183
+ cachedEnterpriseId = config.enterpriseId;
184
+ }
185
+ if (config.models.length) {
186
+ cachedModelIds = config.models;
118
187
  }
188
+ return cachedModelIds;
189
+ }
190
+ function pickFallbackModel(models) {
191
+ if (models.includes("glm-4.7-ioa"))
192
+ return "glm-4.7-ioa";
193
+ return models[0] || "";
119
194
  }
120
195
  async function buildAuthHeaders(accessToken) {
121
196
  const tenantId = cachedTenantId || resolveTenantId(accessToken);
122
197
  let enterpriseId = cachedEnterpriseId || resolveEnterpriseId(accessToken);
123
198
  const userId = cachedUserId || resolveUserId(accessToken);
124
199
  if (!enterpriseId) {
125
- enterpriseId = await fetchEnterpriseId(accessToken, tenantId);
200
+ const config = await fetchConfigModels(accessToken, tenantId);
201
+ enterpriseId = config.enterpriseId || enterpriseId;
202
+ if (config.models.length) {
203
+ cachedModelIds = config.models;
204
+ }
126
205
  }
127
206
  if (tenantId)
128
207
  cachedTenantId = tenantId;
@@ -130,6 +209,10 @@ async function buildAuthHeaders(accessToken) {
130
209
  cachedEnterpriseId = enterpriseId;
131
210
  if (userId)
132
211
  cachedUserId = userId;
212
+ if (!warnedIdSummary) {
213
+ warnedIdSummary = true;
214
+ console.log(`[codebuddy-external] IDs: tenant=${tenantId ? "ok" : "empty"}, enterprise=${enterpriseId ? "ok" : "empty"}, user=${userId ? "ok" : "empty"}`);
215
+ }
133
216
  if (!tenantId && !warnedTenantId) {
134
217
  warnedTenantId = true;
135
218
  console.warn("[codebuddy-external] 未获取到 X-Tenant-Id,请设置 CODEBUDDY_TENANT_ID");
@@ -378,10 +461,16 @@ async function executeViaAuthApi(openaiRequest, auth) {
378
461
  throw new Error("缺少 access token,无法调用 CodeBuddy API");
379
462
  }
380
463
  let accessToken = auth.access;
464
+ const tenantId = resolveTenantId(accessToken);
381
465
  const resolvedModel = resolveModel(openaiRequest.model);
466
+ const supportedModels = await getSupportedModels(accessToken, tenantId);
382
467
  if (!resolvedModel) {
383
- throw new Error("未设置模型,请设置 CODEBUDDY_DEFAULT_MODEL 或在 OpenCode 选择模型");
468
+ throw new Error("未设置模型,请在 OpenCode 选择模型");
469
+ }
470
+ if (supportedModels.length && !supportedModels.includes(resolvedModel)) {
471
+ throw new Error(`[codebuddy-external] 模型 ${resolvedModel} 不在可用列表,请更换模型`);
384
472
  }
473
+ console.log(`[codebuddy-external] 使用模型: ${resolvedModel}`);
385
474
  const requestBody = {
386
475
  ...openaiRequest,
387
476
  model: resolvedModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codebuddy-external-auth",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "OpenCode plugin for CodeBuddy External (IOA) authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",