@magpie-community/opencode-workbuddy-auth 0.1.42 → 0.1.43

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 (3) hide show
  1. package/README.md +10 -1
  2. package/index.mjs +83 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -235,7 +235,16 @@ is used.
235
235
  - Kimi-K3, Kimi-K2.6
236
236
  - MiniMax-M3
237
237
 
238
- A model's reasoning levels are its variants (`reasoningEffort`).
238
+ A model's reasoning levels are its variants (`reasoningEffort`). A chat
239
+ that names no level goes at the model's own default from the config
240
+ (`reasoning.defaultEffort`, "high" for DeepSeek-V4.1-Flash), as WorkBuddy's
241
+ client sends it: WorkBuddy doesn't think when a chat names none, so
242
+ OpenCode's default variant got answers without thinking. An effort the
243
+ client names, `none` too, goes as it came.
244
+
245
+ A chat goes gzipped (`Content-Encoding: gzip`), as WorkBuddy's own client
246
+ sends its model requests (`RequestBodyGzip` in both builds' product.json).
247
+ Claude's Messages route goes as it came.
239
248
 
240
249
  A model's price is its `credits` ("x0.29") and the config's
241
250
  `modelPromotions`. A discount for whole days (Step-5-Preview's 独家限免) is
package/index.mjs CHANGED
@@ -13,6 +13,7 @@ import { createHash, randomBytes } from "node:crypto"
13
13
  import { readFileSync } from "node:fs"
14
14
  import { homedir } from "node:os"
15
15
  import { join } from "node:path"
16
+ import { gzipSync } from "node:zlib"
16
17
 
17
18
  const APP_VERSION = "2.0.0" // the version WorkBuddy's sign-in page is opened with
18
19
  const POLL_MS = 1000
@@ -1289,6 +1290,24 @@ function fitted(body) {
1289
1290
  }
1290
1291
  }
1291
1292
 
1293
+ // gzipped is a chat's body compressed, as WorkBuddy's own client sends its
1294
+ // model requests: its CLI's GzipRequestProcessor (WorkBuddy 5.9.3, CLI
1295
+ // 2.165.0; WorkBuddy AI 5.8.4 the same) gzips each POST's body and sets
1296
+ // Content-Encoding: gzip when the product's RequestBodyGzip feature is on,
1297
+ // which both builds' product.json turn on and /v3/config (merged over it)
1298
+ // doesn't turn off. Sent as plain JSON, a long agent conversation went up
1299
+ // whole on every step: runmiNeverstop on X saw 2.89 GB up to
1300
+ // copilot.tencent.com against 258 MB down. Measured 2026-10-28 on
1301
+ // www.workbuddy.ai: a gzipped chat answers as the plain one does. A
1302
+ // Messages request (Claude on the China build) goes as it came: that route
1303
+ // wasn't tried gzipped. null when the body isn't sent gzipped.
1304
+ function gzipped(native, b) {
1305
+ if (native) return null
1306
+ if (typeof b === "string") return gzipSync(Buffer.from(b))
1307
+ if (b instanceof ArrayBuffer || ArrayBuffer.isView(b)) return gzipSync(b)
1308
+ return null
1309
+ }
1310
+
1292
1311
  // prepared is a request's body read once for every change the loader makes
1293
1312
  // to it and everything it reads off it (plugins#461): a long chat, 45 MiB
1294
1313
  // of history, was parsed six times on its way, once by each step, and
@@ -2119,6 +2138,37 @@ function effortsOf(d, site, id) {
2119
2138
  return EFFORT_ORDER.filter((e) => set.has(e))
2120
2139
  }
2121
2140
 
2141
+ // defaultEffortOf is the level WorkBuddy's own client asks a model for when
2142
+ // the user picked none: its CLI's configureThinkingSettings (WorkBuddy
2143
+ // 5.9.3, CLI 2.165.0) takes the session's effort, else the settings', else
2144
+ // the config's reasoning.defaultEffort, else reasoning.effort, and sends it
2145
+ // as reasoning_effort unless thinking was turned off. A request with no
2146
+ // reasoning_effort is not thinking on WorkBuddy's side: measured 2026-10-28
2147
+ // on www.workbuddy.ai, deepseek-v4.1-flash (defaultEffort "high") answered
2148
+ // with 0 reasoning tokens without one, and thought at low and high
2149
+ // (troilus1011 on X, OpenCode's default variant). A default the model's
2150
+ // levels don't hold is left out, as the CLI clamps it to one they do.
2151
+ function defaultEffortOf(d, efforts) {
2152
+ const own = d.reasoning?.defaultEffort ?? d.reasoning?.effort
2153
+ return typeof own === "string" && efforts.includes(own) ? own : undefined
2154
+ }
2155
+
2156
+ // withDefaultEffort gives a chat that names no effort the model's default
2157
+ // (defaultEffortOf), as WorkBuddy's client sends it; true when it did. An
2158
+ // effort the client named, "none" too, goes as it came.
2159
+ function withDefaultEffort(chat, list) {
2160
+ if (!chat || typeof chat !== "object" || namesEffort(chat)) return false
2161
+ const m = list?.find((x) => x.id === chat.model)
2162
+ if (!m?.effort) return false
2163
+ chat.reasoning_effort = m.effort
2164
+ return true
2165
+ }
2166
+
2167
+ // namesEffort is whether a chat names its effort itself, in either spelling
2168
+ function namesEffort(chat) {
2169
+ return chat.reasoning_effort !== undefined || (!!chat.reasoning && typeof chat.reasoning === "object" && chat.reasoning.effort !== undefined)
2170
+ }
2171
+
2122
2172
  async function liveModels(site, a) {
2123
2173
  const headers = new Headers()
2124
2174
  sign(site, a, headers)
@@ -2145,6 +2195,8 @@ async function liveModels(site, a) {
2145
2195
  if (es.length) {
2146
2196
  const canOff = d.reasoning?.canDisableThinking
2147
2197
  m.efforts = [...(!d.onlyReasoning && (canOff == null || canOff) ? ["none"] : []), ...es]
2198
+ const def = defaultEffortOf(d, es)
2199
+ if (def) m.effort = def
2148
2200
  }
2149
2201
  }
2150
2202
  out.push(m)
@@ -2238,6 +2290,24 @@ function makePlugin(site) {
2238
2290
  if (lists.size > 64) lists.delete(lists.keys().next().value)
2239
2291
  return list
2240
2292
  }
2293
+ // effortList is the list a chat's default effort is read from
2294
+ // (withDefaultEffort): listed's, or null when there is none to read — a
2295
+ // key WorkBuddy's CLI reads no config with (as models does), or a read
2296
+ // that failed within PRICE_TTL, so a config that can't be read costs a
2297
+ // chat one try a minute, not one each
2298
+ const unlisted = new Map()
2299
+ const effortList = async (a) => {
2300
+ if (a.source === "key" && !remoteConfig(a.key)) return null
2301
+ const k = listKey(a)
2302
+ if (Date.now() - (unlisted.get(k) ?? 0) < PRICE_TTL) return null
2303
+ try {
2304
+ return await listed(a)
2305
+ } catch {
2306
+ unlisted.set(k, Date.now())
2307
+ if (unlisted.size > 64) unlisted.delete(unlisted.keys().next().value)
2308
+ return null
2309
+ }
2310
+ }
2241
2311
  const freeOnly = () => magpie?.option?.(site.id, FREE_ONLY) === true
2242
2312
  // onlyFree is a list with the switch on: its free models, or a failure
2243
2313
  // when there are none to tell (magpie then keeps the list it had)
@@ -2369,10 +2439,20 @@ function makePlugin(site) {
2369
2439
  let changed = p.changed
2370
2440
  body = p.body
2371
2441
  if (!native) names = p.names
2442
+ // a chat that names no effort is asked at the model's default,
2443
+ // as WorkBuddy's client asks it (withDefaultEffort); with no
2444
+ // list to read (effortList) the chat goes as it came
2445
+ if (!native && chat && !namesEffort(chat) && withDefaultEffort(chat, await effortList(a))) changed = true
2372
2446
  chatHeaders(site, headers)
2373
2447
  attendChat(headers, chat, session)
2374
- const post = (b) =>
2375
- fetch(req ? req.url : input, { ...init, method: init?.method ?? req?.method, headers, body: b }).catch((e) => unanswered(site, a, b, e, init?.signal))
2448
+ const post = (b) => {
2449
+ // a chat goes gzipped, as WorkBuddy's client sends it
2450
+ // (gzipped); its size on the wire is what unanswered weighs
2451
+ const z = gzipped(native, b)
2452
+ if (z) headers.set("Content-Encoding", "gzip")
2453
+ else headers.delete("Content-Encoding")
2454
+ return fetch(req ? req.url : input, { ...init, method: init?.method ?? req?.method, headers, body: z ?? b }).catch((e) => unanswered(site, a, z ?? b, e, init?.signal))
2455
+ }
2376
2456
  // a 401 is answered once, on whichever try meets it: by a
2377
2457
  // sign-in with another access token (reissued), the request
2378
2458
  // sent once more with it, never again after that; else, or
@@ -2653,4 +2733,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
2653
2733
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
2654
2734
 
2655
2735
  // for tests
2656
- export const _internal = { FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
2736
+ export const _internal = { withDefaultEffort, defaultEffortOf, gzipped, FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-workbuddy-auth",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
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",