opencode-codebuddy-external-auth 1.0.18 → 1.0.20
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 +15 -5
- package/dist/plugin.js +29 -0
- package/package.json +1 -1
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/
|
|
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/
|
|
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,18 +76,32 @@ 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
|
}
|
|
85
|
+
function extractEnterpriseIdFromRoles(roles) {
|
|
86
|
+
if (!roles || roles.length === 0)
|
|
87
|
+
return "";
|
|
88
|
+
for (const role of roles) {
|
|
89
|
+
const match = role.match(/group-admin:([A-Za-z0-9-]+)/);
|
|
90
|
+
if (match?.[1])
|
|
91
|
+
return match[1];
|
|
92
|
+
}
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
73
95
|
function resolveEnterpriseId(accessToken) {
|
|
74
96
|
if (CONFIG.enterpriseId)
|
|
75
97
|
return CONFIG.enterpriseId;
|
|
76
98
|
const payload = decodeJwtPayload(accessToken);
|
|
99
|
+
const roles = payload?.realm_access?.roles || payload?.resource_access?.account?.roles;
|
|
77
100
|
return (payload?.enterprise_id ||
|
|
78
101
|
payload?.enterpriseId ||
|
|
79
102
|
payload?.ent_id ||
|
|
80
103
|
payload?.entId ||
|
|
104
|
+
extractEnterpriseIdFromRoles(roles) ||
|
|
81
105
|
"");
|
|
82
106
|
}
|
|
83
107
|
function resolveUserId(accessToken) {
|
|
@@ -96,6 +120,7 @@ let cachedEnterpriseId = "";
|
|
|
96
120
|
let cachedUserId = "";
|
|
97
121
|
let cachedModelIds = [];
|
|
98
122
|
let warnedModelFallback = false;
|
|
123
|
+
let warnedIdSummary = false;
|
|
99
124
|
function extractModelIds(payload) {
|
|
100
125
|
const results = [];
|
|
101
126
|
const collect = (list) => {
|
|
@@ -196,6 +221,10 @@ async function buildAuthHeaders(accessToken) {
|
|
|
196
221
|
cachedEnterpriseId = enterpriseId;
|
|
197
222
|
if (userId)
|
|
198
223
|
cachedUserId = userId;
|
|
224
|
+
if (!warnedIdSummary) {
|
|
225
|
+
warnedIdSummary = true;
|
|
226
|
+
console.log(`[codebuddy-external] IDs: tenant=${tenantId ? "ok" : "empty"}, enterprise=${enterpriseId ? "ok" : "empty"}, user=${userId ? "ok" : "empty"}`);
|
|
227
|
+
}
|
|
199
228
|
if (!tenantId && !warnedTenantId) {
|
|
200
229
|
warnedTenantId = true;
|
|
201
230
|
console.warn("[codebuddy-external] 未获取到 X-Tenant-Id,请设置 CODEBUDDY_TENANT_ID");
|