@magpie-community/opencode-workbuddy-auth 0.1.41 → 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.
- package/README.md +10 -1
- package/index.mjs +212 -71
- 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
|
|
@@ -608,8 +609,13 @@ function isMessages(url, body) {
|
|
|
608
609
|
// unsigned thinking is body itself, byte for byte.
|
|
609
610
|
function withoutUnsigned(body) {
|
|
610
611
|
const b = chatOf(body)
|
|
612
|
+
return unsignedOn(b) ? JSON.stringify(b) : body
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// unsignedOn makes withoutUnsigned's change to a body's object; true when it made one
|
|
616
|
+
function unsignedOn(b) {
|
|
611
617
|
const msgs = b?.messages
|
|
612
|
-
if (!Array.isArray(msgs)) return
|
|
618
|
+
if (!Array.isArray(msgs)) return false
|
|
613
619
|
let changed = false
|
|
614
620
|
for (const m of msgs) {
|
|
615
621
|
if (m?.role !== "assistant" || !Array.isArray(m.content)) continue
|
|
@@ -618,7 +624,7 @@ function withoutUnsigned(body) {
|
|
|
618
624
|
changed = true
|
|
619
625
|
m.content = kept.length ? kept : [{ type: "text", text: "" }]
|
|
620
626
|
}
|
|
621
|
-
return changed
|
|
627
|
+
return changed
|
|
622
628
|
}
|
|
623
629
|
|
|
624
630
|
// withoutSignedThinking is a Messages body without any thinking block at
|
|
@@ -674,7 +680,10 @@ function sessionOf(headers) {
|
|
|
674
680
|
// already there — the caller's own, or another plugin's — is kept, and the
|
|
675
681
|
// message and request ids are the same one, as WorkBuddy's client has them.
|
|
676
682
|
function attend(headers, body, session) {
|
|
677
|
-
|
|
683
|
+
attendChat(headers, chatOf(body), session)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function attendChat(headers, chat, session) {
|
|
678
687
|
const conv = conversationKey(chat, session)
|
|
679
688
|
const msg = hex()
|
|
680
689
|
headers.set("X-Conversation-Message-ID", msg)
|
|
@@ -792,29 +801,34 @@ function withSystem(body) {
|
|
|
792
801
|
if (typeof body !== "string") return body
|
|
793
802
|
try {
|
|
794
803
|
const b = JSON.parse(body)
|
|
795
|
-
|
|
796
|
-
let changed = unflag(b.messages)
|
|
797
|
-
if (b.max_completion_tokens !== undefined) {
|
|
798
|
-
if (b.max_tokens === undefined) b.max_tokens = b.max_completion_tokens
|
|
799
|
-
delete b.max_completion_tokens
|
|
800
|
-
changed = true
|
|
801
|
-
}
|
|
802
|
-
for (const m of b.messages) {
|
|
803
|
-
if (m?.role !== "developer") continue
|
|
804
|
-
m.role = "system"
|
|
805
|
-
changed = true
|
|
806
|
-
}
|
|
807
|
-
if (asReasoning(b.messages)) changed = true
|
|
808
|
-
if (b.messages[0]?.role !== "system") {
|
|
809
|
-
b.messages.unshift({ role: "system", content: "You are a helpful assistant." })
|
|
810
|
-
changed = true
|
|
811
|
-
}
|
|
812
|
-
return changed ? JSON.stringify(b) : body
|
|
804
|
+
return systemOn(b) ? JSON.stringify(b) : body
|
|
813
805
|
} catch {
|
|
814
806
|
return body
|
|
815
807
|
}
|
|
816
808
|
}
|
|
817
809
|
|
|
810
|
+
// systemOn makes withSystem's changes to a chat's object; true when it made any
|
|
811
|
+
function systemOn(b) {
|
|
812
|
+
if (!Array.isArray(b?.messages) || !b.messages.length) return false
|
|
813
|
+
let changed = unflag(b.messages)
|
|
814
|
+
if (b.max_completion_tokens !== undefined) {
|
|
815
|
+
if (b.max_tokens === undefined) b.max_tokens = b.max_completion_tokens
|
|
816
|
+
delete b.max_completion_tokens
|
|
817
|
+
changed = true
|
|
818
|
+
}
|
|
819
|
+
for (const m of b.messages) {
|
|
820
|
+
if (m?.role !== "developer") continue
|
|
821
|
+
m.role = "system"
|
|
822
|
+
changed = true
|
|
823
|
+
}
|
|
824
|
+
if (asReasoning(b.messages)) changed = true
|
|
825
|
+
if (b.messages[0]?.role !== "system") {
|
|
826
|
+
b.messages.unshift({ role: "system", content: "You are a helpful assistant." })
|
|
827
|
+
changed = true
|
|
828
|
+
}
|
|
829
|
+
return changed
|
|
830
|
+
}
|
|
831
|
+
|
|
818
832
|
// WorkBuddy's channel check reads the system and assistant messages only:
|
|
819
833
|
// tried on 2026-10-21 with real accounts on both builds (glm-5.3,
|
|
820
834
|
// deepseek-v4.1-flash, gpt-5.5), each refused line was served in the user's
|
|
@@ -841,7 +855,11 @@ function textOf(content) {
|
|
|
841
855
|
// systemKey is the text of a chat's system messages, which a chat refused
|
|
842
856
|
// once is known by: the same agent's next chat goes with systemAsUser at once
|
|
843
857
|
function systemKey(body) {
|
|
844
|
-
|
|
858
|
+
return systemKeyOf(chatOf(body))
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function systemKeyOf(chat) {
|
|
862
|
+
const msgs = chat?.messages
|
|
845
863
|
if (!Array.isArray(msgs)) return ""
|
|
846
864
|
const key = msgs.filter((m) => m?.role === "system").map((m) => textOf(m.content)).join("\n")
|
|
847
865
|
return key.trim() === DEFAULT_SYSTEM ? "" : key
|
|
@@ -849,15 +867,21 @@ function systemKey(body) {
|
|
|
849
867
|
|
|
850
868
|
function systemAsUser(body) {
|
|
851
869
|
const b = chatOf(body)
|
|
870
|
+
return systemAsUserOn(b) ? JSON.stringify(b) : null
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// systemAsUserOn makes systemAsUser's move on a chat's object; false when
|
|
874
|
+
// there's nothing to move
|
|
875
|
+
function systemAsUserOn(b) {
|
|
852
876
|
const msgs = b?.messages
|
|
853
|
-
if (!Array.isArray(msgs)) return
|
|
877
|
+
if (!Array.isArray(msgs)) return false
|
|
854
878
|
let i = 0
|
|
855
879
|
const lead = []
|
|
856
880
|
for (; i < msgs.length && msgs[i]?.role === "system"; i++) lead.push(textOf(msgs[i].content))
|
|
857
881
|
const said = lead.join("\n\n").trim()
|
|
858
882
|
const rest = msgs.slice(i).map((m) => (m?.role === "system" ? { ...m, role: "user" } : m))
|
|
859
883
|
const moved = said && said !== DEFAULT_SYSTEM
|
|
860
|
-
if (!moved && rest.every((m, k) => m === msgs[i + k])) return
|
|
884
|
+
if (!moved && rest.every((m, k) => m === msgs[i + k])) return false
|
|
861
885
|
if (moved) {
|
|
862
886
|
const text = INSTRUCTIONS[0] + said + INSTRUCTIONS[1]
|
|
863
887
|
const u = rest.findIndex((m) => m?.role === "user")
|
|
@@ -867,7 +891,7 @@ function systemAsUser(body) {
|
|
|
867
891
|
else rest[u] = { ...rest[u], content: text }
|
|
868
892
|
}
|
|
869
893
|
b.messages = [{ role: "system", content: DEFAULT_SYSTEM }, ...rest]
|
|
870
|
-
return
|
|
894
|
+
return true
|
|
871
895
|
}
|
|
872
896
|
|
|
873
897
|
// normalizeToolChoice converts the object form emitted by OpenAI clients to
|
|
@@ -877,42 +901,47 @@ function normalizeToolChoice(body) {
|
|
|
877
901
|
if (typeof body !== "string") return body
|
|
878
902
|
try {
|
|
879
903
|
const b = JSON.parse(body)
|
|
880
|
-
|
|
881
|
-
const choice = b.tool_choice
|
|
882
|
-
if (typeof choice === "string") {
|
|
883
|
-
if (choice.trim().toLowerCase() === "none") {
|
|
884
|
-
delete b.tool_choice
|
|
885
|
-
delete b.tools
|
|
886
|
-
delete b.functions
|
|
887
|
-
}
|
|
888
|
-
return JSON.stringify(b)
|
|
889
|
-
}
|
|
890
|
-
if (!choice || typeof choice !== "object" || Array.isArray(choice)) {
|
|
891
|
-
delete b.tool_choice
|
|
892
|
-
return JSON.stringify(b)
|
|
893
|
-
}
|
|
894
|
-
const type = typeof choice.type === "string" ? choice.type.trim().toLowerCase() : ""
|
|
895
|
-
if (type === "none") {
|
|
896
|
-
delete b.tool_choice
|
|
897
|
-
delete b.tools
|
|
898
|
-
delete b.functions
|
|
899
|
-
} else if (type === "auto" || type === "required") {
|
|
900
|
-
b.tool_choice = type
|
|
901
|
-
} else if (type === "function") {
|
|
902
|
-
const fn = choice.function && typeof choice.function === "object" && !Array.isArray(choice.function)
|
|
903
|
-
? choice.function
|
|
904
|
-
: undefined
|
|
905
|
-
const name = typeof fn?.name === "string" ? fn.name.trim() : ""
|
|
906
|
-
b.tool_choice = name || "auto"
|
|
907
|
-
} else {
|
|
908
|
-
delete b.tool_choice
|
|
909
|
-
}
|
|
910
|
-
return JSON.stringify(b)
|
|
904
|
+
return toolChoiceOn(b) ? JSON.stringify(b) : body
|
|
911
905
|
} catch {
|
|
912
906
|
return body
|
|
913
907
|
}
|
|
914
908
|
}
|
|
915
909
|
|
|
910
|
+
// toolChoiceOn makes normalizeToolChoice's change to a chat's object; true
|
|
911
|
+
// when it made one (a string choice other than none is WorkBuddy's already)
|
|
912
|
+
function toolChoiceOn(b) {
|
|
913
|
+
if (!b || typeof b !== "object" || Array.isArray(b) || !("tool_choice" in b)) return false
|
|
914
|
+
const choice = b.tool_choice
|
|
915
|
+
if (typeof choice === "string") {
|
|
916
|
+
if (choice.trim().toLowerCase() !== "none") return false
|
|
917
|
+
delete b.tool_choice
|
|
918
|
+
delete b.tools
|
|
919
|
+
delete b.functions
|
|
920
|
+
return true
|
|
921
|
+
}
|
|
922
|
+
if (!choice || typeof choice !== "object" || Array.isArray(choice)) {
|
|
923
|
+
delete b.tool_choice
|
|
924
|
+
return true
|
|
925
|
+
}
|
|
926
|
+
const type = typeof choice.type === "string" ? choice.type.trim().toLowerCase() : ""
|
|
927
|
+
if (type === "none") {
|
|
928
|
+
delete b.tool_choice
|
|
929
|
+
delete b.tools
|
|
930
|
+
delete b.functions
|
|
931
|
+
} else if (type === "auto" || type === "required") {
|
|
932
|
+
b.tool_choice = type
|
|
933
|
+
} else if (type === "function") {
|
|
934
|
+
const fn = choice.function && typeof choice.function === "object" && !Array.isArray(choice.function)
|
|
935
|
+
? choice.function
|
|
936
|
+
: undefined
|
|
937
|
+
const name = typeof fn?.name === "string" ? fn.name.trim() : ""
|
|
938
|
+
b.tool_choice = name || "auto"
|
|
939
|
+
} else {
|
|
940
|
+
delete b.tool_choice
|
|
941
|
+
}
|
|
942
|
+
return true
|
|
943
|
+
}
|
|
944
|
+
|
|
916
945
|
// ---- tools WorkBuddy takes ----------------------------------------------------
|
|
917
946
|
|
|
918
947
|
// WorkBuddy AI answers 400 code 11133 "Invalid request parameters" (the
|
|
@@ -1261,6 +1290,58 @@ function fitted(body) {
|
|
|
1261
1290
|
}
|
|
1262
1291
|
}
|
|
1263
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
|
+
|
|
1311
|
+
// prepared is a request's body read once for every change the loader makes
|
|
1312
|
+
// to it and everything it reads off it (plugins#461): a long chat, 45 MiB
|
|
1313
|
+
// of history, was parsed six times on its way, once by each step, and
|
|
1314
|
+
// written out again whole by normalizeToolChoice even when nothing changed.
|
|
1315
|
+
// chat is the parsed body, changed whether any step changed it; the caller
|
|
1316
|
+
// writes it out once, when it did. Each step is the one its own function
|
|
1317
|
+
// above makes, in the same order.
|
|
1318
|
+
function prepared(body, native) {
|
|
1319
|
+
if (!native && (body instanceof ArrayBuffer || ArrayBuffer.isView(body))) body = new TextDecoder().decode(body)
|
|
1320
|
+
const none = { body, chat: null, names: new Map(), changed: false }
|
|
1321
|
+
if (typeof body !== "string") return none
|
|
1322
|
+
let chat
|
|
1323
|
+
try {
|
|
1324
|
+
chat = JSON.parse(body)
|
|
1325
|
+
} catch {
|
|
1326
|
+
return none
|
|
1327
|
+
}
|
|
1328
|
+
if (chat === null || typeof chat !== "object") return none
|
|
1329
|
+
if (native) return { body, chat, names: new Map(), changed: unsignedOn(chat) }
|
|
1330
|
+
try {
|
|
1331
|
+
let changed = systemOn(chat)
|
|
1332
|
+
const fit = fitTools(chat)
|
|
1333
|
+
if (fit.changed) changed = true
|
|
1334
|
+
if (toolChoiceOn(chat)) changed = true
|
|
1335
|
+
return { body, chat, names: fit.names, changed }
|
|
1336
|
+
} catch {
|
|
1337
|
+
// a shape a step can't take: the steps one by one, each keeping the
|
|
1338
|
+
// body as it was when it can't, as they always have
|
|
1339
|
+
const fit = fitted(withSystem(body))
|
|
1340
|
+
const out = normalizeToolChoice(fit.body)
|
|
1341
|
+
return { body: out, chat: chatOf(out), names: fit.names, changed: false }
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1264
1345
|
// namedBack gives the model's calls in a chat answer (one JSON body, or an
|
|
1265
1346
|
// SSE stream's chunks) the names the client gave its tools.
|
|
1266
1347
|
function namedBack(v, names) {
|
|
@@ -2057,6 +2138,37 @@ function effortsOf(d, site, id) {
|
|
|
2057
2138
|
return EFFORT_ORDER.filter((e) => set.has(e))
|
|
2058
2139
|
}
|
|
2059
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
|
+
|
|
2060
2172
|
async function liveModels(site, a) {
|
|
2061
2173
|
const headers = new Headers()
|
|
2062
2174
|
sign(site, a, headers)
|
|
@@ -2083,6 +2195,8 @@ async function liveModels(site, a) {
|
|
|
2083
2195
|
if (es.length) {
|
|
2084
2196
|
const canOff = d.reasoning?.canDisableThinking
|
|
2085
2197
|
m.efforts = [...(!d.onlyReasoning && (canOff == null || canOff) ? ["none"] : []), ...es]
|
|
2198
|
+
const def = defaultEffortOf(d, es)
|
|
2199
|
+
if (def) m.effort = def
|
|
2086
2200
|
}
|
|
2087
2201
|
}
|
|
2088
2202
|
out.push(m)
|
|
@@ -2176,6 +2290,24 @@ function makePlugin(site) {
|
|
|
2176
2290
|
if (lists.size > 64) lists.delete(lists.keys().next().value)
|
|
2177
2291
|
return list
|
|
2178
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
|
+
}
|
|
2179
2311
|
const freeOnly = () => magpie?.option?.(site.id, FREE_ONLY) === true
|
|
2180
2312
|
// onlyFree is a list with the switch on: its free models, or a failure
|
|
2181
2313
|
// when there are none to tell (magpie then keeps the list it had)
|
|
@@ -2302,18 +2434,25 @@ function makePlugin(site) {
|
|
|
2302
2434
|
// /v1/messages, where a chat is refused 11128.
|
|
2303
2435
|
const native = isMessages(req ? req.url : String(input), body)
|
|
2304
2436
|
let names = {}
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2437
|
+
const p = prepared(body, native)
|
|
2438
|
+
let chat = p.chat
|
|
2439
|
+
let changed = p.changed
|
|
2440
|
+
body = p.body
|
|
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
|
|
2313
2446
|
chatHeaders(site, headers)
|
|
2314
|
-
|
|
2315
|
-
const post = (b) =>
|
|
2316
|
-
|
|
2447
|
+
attendChat(headers, chat, session)
|
|
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
|
+
}
|
|
2317
2456
|
// a 401 is answered once, on whichever try meets it: by a
|
|
2318
2457
|
// sign-in with another access token (reissued), the request
|
|
2319
2458
|
// sent once more with it, never again after that; else, or
|
|
@@ -2358,11 +2497,13 @@ function makePlugin(site) {
|
|
|
2358
2497
|
}
|
|
2359
2498
|
// a model known to turn tool results then the user's message away
|
|
2360
2499
|
// gets the empty reply between them up front (see endsAfterTools)
|
|
2361
|
-
const model =
|
|
2500
|
+
const model = chat?.model
|
|
2362
2501
|
// a system prompt WorkBuddy refused before goes as the user's
|
|
2363
2502
|
// words at once (see systemAsUser)
|
|
2364
|
-
const sysKey = native ? "" :
|
|
2365
|
-
if (sysKey && refusedSystems.has(sysKey)
|
|
2503
|
+
const sysKey = native ? "" : systemKeyOf(chat)
|
|
2504
|
+
if (sysKey && refusedSystems.has(sysKey) && systemAsUserOn(chat)) changed = true
|
|
2505
|
+
if (changed) body = JSON.stringify(chat)
|
|
2506
|
+
chat = null
|
|
2366
2507
|
const shaped = (b) => (!native && endsAfterTools.has(model) ? replyAfterTools(b) ?? b : b)
|
|
2367
2508
|
let res = await send(shaped(body))
|
|
2368
2509
|
// a signature WorkBuddy's own signing didn't make — a session
|
|
@@ -2592,4 +2733,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
|
|
|
2592
2733
|
export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
|
|
2593
2734
|
|
|
2594
2735
|
// for tests
|
|
2595
|
-
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, 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.
|
|
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",
|