@magpie-community/opencode-workbuddy-auth 0.1.2 → 0.1.3

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 +53 -6
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -243,7 +243,7 @@ async function browserSignIn(site) {
243
243
  const deadline = Date.now() + SIGN_IN_MS
244
244
  const q = encodeURIComponent(state.state)
245
245
  const token = await poll(site, `/v2/plugin/auth/token?state=${q}`, {}, [11217], deadline)
246
- if (!token.accessToken) return { type: "failed" }
246
+ if (!token.accessToken) return { type: "failed", error: `${site.name} sent back no token` }
247
247
  let a = merge({}, token)
248
248
  const who = await poll(site, `/v2/plugin/login/account?state=${q}`, {
249
249
  Authorization: "Bearer " + a.access,
@@ -253,8 +253,8 @@ async function browserSignIn(site) {
253
253
  }, [12151], deadline)
254
254
  a.uid = who.uid ?? ""
255
255
  return success(site, a, who.nickname || who.phoneNumber || who.uid)
256
- } catch {
257
- return { type: "failed" }
256
+ } catch (e) {
257
+ return { type: "failed", error: e.message }
258
258
  }
259
259
  },
260
260
  }
@@ -319,7 +319,7 @@ function desktopSignIn(site) {
319
319
  method: "auto",
320
320
  async callback() {
321
321
  const d = readDesktop(site)
322
- if (!d) return { type: "failed" }
322
+ if (!d) return { type: "failed", error: `${site.name} desktop isn't signed in` }
323
323
  return { type: "success", refresh: "", access: "", expires: 0, source: "desktop", accountId: d.name || site.name, uid: d.uid }
324
324
  },
325
325
  }
@@ -439,12 +439,22 @@ function modelOf(site, provider, m) {
439
439
  },
440
440
  release_date: "",
441
441
  variants: variants(m.efforts),
442
+ // magpie's own: served at no cost to the plan's credits
443
+ free: !!m.free,
442
444
  }
443
445
  }
444
446
 
445
447
  // liveModels asks WorkBuddy's product config for the plan's models: the
446
448
  // "cli" agent's list, each with its details. The CLI User-Agent picks
447
449
  // WorkBuddy's config (a bare WorkBuddy/<v> gets CodeBuddy IDE's, no cli).
450
+ // freeCredits says whether a model's credits are none: "x0.00", "0" or 0.
451
+ // A rate it can't read is not free.
452
+ function freeCredits(c) {
453
+ if (c == null || c === "") return false
454
+ const s = String(c).trim().toLowerCase().replace(/^x/, "")
455
+ return s !== "" && Number.isFinite(Number(s)) && Number(s) === 0
456
+ }
457
+
448
458
  async function liveModels(site, a) {
449
459
  const headers = new Headers()
450
460
  sign(site, a, headers)
@@ -466,6 +476,7 @@ async function liveModels(site, a) {
466
476
  m.context = d.maxInputTokens
467
477
  m.output = d.maxOutputTokens
468
478
  m.images = d.supportsImages === true
479
+ m.free = freeCredits(d.credits)
469
480
  const es = d.reasoning?.supportedEfforts ?? []
470
481
  if (es.length) {
471
482
  const canOff = d.reasoning?.canDisableThinking
@@ -539,7 +550,8 @@ function makePlugin(site) {
539
550
  sign(site, a, headers)
540
551
  let body = init?.body
541
552
  if (body === undefined && req) body = await req.clone().text()
542
- return fetch(req ? req.url : input, { ...init, method: init?.method ?? req?.method, headers, body: withSystem(body) })
553
+ const res = await fetch(req ? req.url : input, { ...init, method: init?.method ?? req?.method, headers, body: withSystem(body) })
554
+ return res.status >= 400 ? explained(res) : res
543
555
  },
544
556
  }
545
557
  },
@@ -571,8 +583,43 @@ function makePlugin(site) {
571
583
  })
572
584
  }
573
585
 
586
+ // REFUSED_HINT is what the user can do about WorkBuddy's "Illegal API
587
+ // invocation from an unapproved channel": both builds answer it to a chat
588
+ // whose system prompt is Codex's or Claude Code's own (magpie #182),
589
+ // whatever the headers. It ends the error's message, as magpie's built-in
590
+ // put it (provider.WBRefusedHint), so magpie's routing page says it apart.
591
+ const REFUSED_HINT =
592
+ "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"
593
+ const REFUSED = /unapproved channel|illegal api invocation/i
594
+
595
+ // explained is res with REFUSED_HINT added to that refusal's message.
596
+ async function explained(res) {
597
+ const text = await res.text()
598
+ const again = (b) => {
599
+ const headers = new Headers(res.headers)
600
+ headers.delete("content-length")
601
+ headers.delete("content-encoding")
602
+ return new Response(b, { status: res.status, statusText: res.statusText, headers })
603
+ }
604
+ if (!REFUSED.test(text)) return again(text)
605
+ let msg = text.trim()
606
+ let v
607
+ try {
608
+ v = JSON.parse(text)
609
+ } catch {}
610
+ const at = v?.error && typeof v.error === "object" ? v.error : v && typeof v === "object" ? v : null
611
+ if (at && typeof at.message === "string") msg = at.message
612
+ else if (typeof v?.error === "string") msg = v.error
613
+ msg = `${msg} — ${REFUSED_HINT}`
614
+ const headers = new Headers(res.headers)
615
+ headers.set("content-type", "application/json")
616
+ headers.delete("content-length")
617
+ headers.delete("content-encoding")
618
+ return new Response(JSON.stringify({ error: { message: msg, type: "permission_error", code: null } }), { status: res.status, statusText: res.statusText, headers })
619
+ }
620
+
574
621
  export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
575
622
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
576
623
 
577
624
  // for tests
578
- export const _internal = { usageOf, desktopHeld }
625
+ export const _internal = { usageOf, desktopHeld, explained, REFUSED_HINT, freeCredits }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-workbuddy-auth",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",