@magpie-community/opencode-workbuddy-auth 0.1.15 → 0.1.17

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/index.mjs +39 -8
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -77,9 +77,27 @@ const SITES = {
77
77
  platform: "workbuddy-ai",
78
78
  ai: true,
79
79
  models: AI_MODELS,
80
+ // models whose images WorkBuddy AI drops though its config says they
81
+ // take them (see textOnly)
82
+ noImages: /^glm-/,
80
83
  },
81
84
  }
82
85
 
86
+ // textOnly says whether site drops the images sent to model, whatever its
87
+ // config says. On 2026-10-13, with a real WorkBuddy AI account (Yirasumi on
88
+ // Discord), www.workbuddy.ai's /v3/config marked every model
89
+ // supportsImages: true, but a chat to glm-5.3-flash, glm-5.3 or glm-5.2
90
+ // came back saying "[System reminder: the current model does not support
91
+ // images. Content filtered. ...]": the server had taken the image out.
92
+ // Its other models (deepseek-v4.1-flash, kimi, gemini, grok, gpt) read
93
+ // them, and WorkBuddy (China)'s glm-5.3-flash does too, so this is WorkBuddy
94
+ // AI's GLM models alone. Said text-only, magpie's image-describing model
95
+ // reads the image for them; a user who said the model takes images keeps
96
+ // their own answer (magpie's settings, over what this list says).
97
+ function textOnly(site, model) {
98
+ return !!site.noImages?.test(model)
99
+ }
100
+
83
101
  const NPM = "@ai-sdk/openai-compatible"
84
102
  const hex = () => randomBytes(16).toString("hex")
85
103
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
@@ -1180,7 +1198,7 @@ async function liveModels(site, a) {
1180
1198
  if (d) {
1181
1199
  m.context = d.maxInputTokens
1182
1200
  m.output = d.maxOutputTokens
1183
- m.images = d.supportsImages === true
1201
+ m.images = d.supportsImages === true && !textOnly(site, id)
1184
1202
  const r = creditsOf(d.credits)
1185
1203
  if (r !== undefined) {
1186
1204
  m.free = r === 0
@@ -1332,6 +1350,18 @@ const REFUSED_HINT =
1332
1350
  "WorkBuddy refuses chats from Codex and Claude Code (their system prompt); use it from Hermes, OpenCode or Pi, or add another provider to this group"
1333
1351
  const REFUSED = /unapproved channel|illegal api invocation/i
1334
1352
 
1353
+ // RESTRICTED_HINT is what the user can do about WorkBuddy's 403 code 11140
1354
+ // "request illegal", whose displayMsg says "Your account has been restricted
1355
+ // due to unusual activity" (magpie #1849): the account is out until WorkBuddy
1356
+ // lifts the restriction, which an appeal asks for. It ends the error's
1357
+ // message as magpie's built-in puts it (provider.WBRestrictedHint), word for
1358
+ // word, so magpie rests the account for hours rather than minutes.
1359
+ const RESTRICTED_HINT =
1360
+ "WorkBuddy restricted this account for unusual activity; it stays out until WorkBuddy lifts that, which an appeal (Submit feedback in WorkBuddy) asks for"
1361
+ const RESTRICTED_CODE = 11140
1362
+ // The code is matched whole, as with 14018: 111400 is another error.
1363
+ const RESTRICTED_TEXT = /account has been restricted due to unusual activity|账号存在异常行为|帳號存在異常行為|["']code["']\s*:\s*["']?11140(?![\d.])/i
1364
+
1335
1365
  // EXHAUSTED_HINT explains WorkBuddy's requirement of an active package
1336
1366
  // even for 0-rate / free models. When an account has no packages or 0 credits,
1337
1367
  // WorkBuddy returns code 14018 ("Credits exhausted...").
@@ -1349,7 +1379,7 @@ function kept(res) {
1349
1379
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
1350
1380
  }
1351
1381
 
1352
- // explained is res with REFUSED_HINT or EXHAUSTED_HINT added to that refusal's message.
1382
+ // explained is res with REFUSED_HINT, RESTRICTED_HINT or EXHAUSTED_HINT added to that refusal's message.
1353
1383
  // Each refusal says the sign-in is kept: the built-in passed WorkBuddy's
1354
1384
  // 401 on and never marked a WorkBuddy account lapsed.
1355
1385
  async function explained(res) {
@@ -1368,8 +1398,9 @@ async function explained(res) {
1368
1398
  const at = v?.error && typeof v.error === "object" ? v.error : v && typeof v === "object" ? v : null
1369
1399
  const code = at?.data?.code ?? at?.code ?? v?.code ?? v?.data?.code
1370
1400
  const isRefused = REFUSED.test(text)
1371
- const isExhausted = code === 14018 || Number(code) === 14018 || EXHAUSTED_TEXT.test(text)
1372
- if (!isRefused && !isExhausted) return again(text)
1401
+ const isRestricted = !isRefused && (Number(code) === RESTRICTED_CODE || RESTRICTED_TEXT.test(text))
1402
+ const isExhausted = !isRestricted && (code === 14018 || Number(code) === 14018 || EXHAUSTED_TEXT.test(text))
1403
+ if (!isRefused && !isRestricted && !isExhausted) return again(text)
1373
1404
 
1374
1405
  let msg = text.trim()
1375
1406
  if (at && typeof at.message === "string") msg = at.message
@@ -1378,9 +1409,9 @@ async function explained(res) {
1378
1409
  else if (typeof v?.msg === "string" && v.msg) msg = v.msg // WorkBuddy's own {"code", "msg"}
1379
1410
  else if (typeof v?.data?.msg === "string") msg = v.data.msg
1380
1411
 
1381
- const hint = isRefused ? REFUSED_HINT : EXHAUSTED_HINT
1382
- const type = isRefused ? "permission_error" : "insufficient_quota"
1383
- const errCode = isRefused ? null : 14018
1412
+ const hint = isRefused ? REFUSED_HINT : isRestricted ? RESTRICTED_HINT : EXHAUSTED_HINT
1413
+ const type = isRefused || isRestricted ? "permission_error" : "insufficient_quota"
1414
+ const errCode = isRefused ? null : isRestricted ? RESTRICTED_CODE : 14018
1384
1415
  msg = `${msg} — ${hint}`
1385
1416
  const headers = new Headers(res.headers)
1386
1417
  headers.set("content-type", "application/json")
@@ -1394,4 +1425,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
1394
1425
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
1395
1426
 
1396
1427
  // for tests
1397
- export const _internal = { runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
1428
+ export const _internal = { textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-workbuddy-auth",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "OpenCode provider plugins for WorkBuddy's plans: WorkBuddy (CodeBuddy, copilot.tencent.com) and WorkBuddy AI (www.workbuddy.ai)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",