@magpie-community/opencode-workbuddy-auth 0.1.45 → 0.1.47
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 +8 -5
- package/index.mjs +65 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,8 +126,8 @@ Each chat carries WorkBuddy's headers:
|
|
|
126
126
|
- `Authorization`, `X-User-Id`, `X-Domain`, `X-Product: SaaS`, `X-IDE-Type: WorkBuddy`;
|
|
127
127
|
- the desktop CLI's `User-Agent`, `WorkBuddy/<app> <appName>/<app> CLI/<cli>`:
|
|
128
128
|
`WorkBuddy/5.9.3 WorkBuddy/5.9.3 CLI/2.165.0` on the China build and
|
|
129
|
-
`WorkBuddy/5.
|
|
130
|
-
build's current app on 2026-10-
|
|
129
|
+
`WorkBuddy/5.9.3 workbuddy-ai/5.9.3 CLI/2.165.0` on WorkBuddy AI, each
|
|
130
|
+
build's current app on 2026-10-29; `X-IDE-Version` is the app's version.
|
|
131
131
|
A User-Agent not starting `WorkBuddy/<version>` is answered error 10085;
|
|
132
132
|
- the client's, which WorkBuddy's usage list shows as the client that asked
|
|
133
133
|
(使用端): `X-Agent-Purpose: conversation`, `X-IDE-Name: WorkBuddy`,
|
|
@@ -137,14 +137,17 @@ Each chat carries WorkBuddy's headers:
|
|
|
137
137
|
- the conversation's ids: `X-Conversation-ID`, `X-Conversation-Request-ID`,
|
|
138
138
|
`X-Conversation-Message-ID`, `X-Request-ID`. WorkBuddy's backend counts a
|
|
139
139
|
user send as one request by `X-Conversation-Request-ID`, so every step of
|
|
140
|
-
one send — a tool result sent back, a retry
|
|
141
|
-
|
|
140
|
+
one send — a tool result sent back, a retry — carries the same one, and
|
|
141
|
+
the user's next message carries another. Both ids are one account's own,
|
|
142
|
+
as WorkBuddy's client is signed in as one: a conversation sent as another
|
|
143
|
+
account (another of a group, or a sign-in saved since) carries that
|
|
144
|
+
account's ids, never the same ones under two `X-User-Id`s. The
|
|
142
145
|
conversation's is the session magpie (or OpenCode) names the request by,
|
|
143
146
|
else the chat's first user message; nothing is made up when neither names
|
|
144
147
|
one, a made-up conversation of the request's own being what splits one
|
|
145
148
|
conversation into many in the usage detail;
|
|
146
149
|
- what the CLI adds to a model request alone: `X-Agent-Type: main` on both
|
|
147
|
-
builds, `X-Agent-Runtime: local-agent`
|
|
150
|
+
builds, `X-Agent-Runtime: local-agent` (new in 5.9),
|
|
148
151
|
`Accept: application/json`, and the `X-Stainless-*` headers of the OpenAI
|
|
149
152
|
SDK it sends through (openai 6.25.0 on Electron 37's Node v22.21.1, a
|
|
150
153
|
1200s timeout, no retries).
|
package/index.mjs
CHANGED
|
@@ -92,11 +92,15 @@ const SITES = {
|
|
|
92
92
|
authID: "workbuddy-desktop-ai",
|
|
93
93
|
platform: "workbuddy-ai",
|
|
94
94
|
ai: true,
|
|
95
|
-
// WorkBuddy AI 5.
|
|
96
|
-
// 2026-10-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
// WorkBuddy AI 5.9.3.42398134, its update feed's current build on
|
|
96
|
+
// 2026-10-29 (see the China build's): the same CLI as the China
|
|
97
|
+
// build's 5.9.3, byte for byte but for product.json, and the same
|
|
98
|
+
// Electron (37.10.3); its desktop puts agentRuntime local-agent in the
|
|
99
|
+
// prompt meta as the China build's does
|
|
100
|
+
app: "5.9.3",
|
|
101
|
+
cli: "2.165.0",
|
|
99
102
|
appName: "workbuddy-ai",
|
|
103
|
+
runtime: "local-agent",
|
|
100
104
|
keys: "https://www.workbuddy.ai/profile/keys",
|
|
101
105
|
models: AI_MODELS,
|
|
102
106
|
// models whose images WorkBuddy AI drops though its config says they
|
|
@@ -442,7 +446,7 @@ async function enterpriseOf(site, a, who) {
|
|
|
442
446
|
// itself, and the request that carried it. Its backend counts a user send as
|
|
443
447
|
// one request by X-Conversation-Request-ID: a chat carrying a new one every
|
|
444
448
|
// time is counted on its own, so one send that takes several steps — a tool
|
|
445
|
-
// call answered and sent again, a retry
|
|
449
|
+
// call answered and sent again, a retry — shows up
|
|
446
450
|
// in the usage detail as many requests. The ids below are made from what
|
|
447
451
|
// names the conversation and the turn, so every step of one send carries the
|
|
448
452
|
// same turn id and the user's next message carries another, as WorkBuddy's
|
|
@@ -715,22 +719,64 @@ function sessionOf(headers) {
|
|
|
715
719
|
// turn's (one user send, whatever it takes), and the conversation's. A header
|
|
716
720
|
// already there — the caller's own, or another plugin's — is kept, and the
|
|
717
721
|
// message and request ids are the same one, as WorkBuddy's client has them.
|
|
718
|
-
|
|
719
|
-
|
|
722
|
+
//
|
|
723
|
+
// who is the account the chat goes as (scopeOf). The ids are that account's
|
|
724
|
+
// own: WorkBuddy's client runs as one account, so one conversation or turn
|
|
725
|
+
// never shows up under two. Without it, one magpie session spread over a
|
|
726
|
+
// group of accounts sent the same X-Conversation-ID and
|
|
727
|
+
// X-Conversation-Request-ID under every account's X-User-Id.
|
|
728
|
+
function attend(headers, body, session, who = "") {
|
|
729
|
+
return attendChat(headers, chatOf(body), session, who)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// attendChat is attend with the chat read already. It returns what reattend
|
|
733
|
+
// needs to set the ids again for another account once the chat itself is
|
|
734
|
+
// let go: the ids it set itself (not the caller's), and the digests of what
|
|
735
|
+
// names the turn and the conversation, which are never sent as they are.
|
|
736
|
+
function attendChat(headers, chat, session, who = "") {
|
|
737
|
+
const conv = conversationParts(chat, session)
|
|
738
|
+
const turn = turnParts(chat)
|
|
739
|
+
const ids = {
|
|
740
|
+
set: [],
|
|
741
|
+
// a turn is a conversation's own: two conversations whose user message
|
|
742
|
+
// reads the same are still two sends, not one
|
|
743
|
+
turn: turn.length ? idFor("turn", [...conv, "|", ...turn]) : "",
|
|
744
|
+
conv: conv.length ? idFor("conversation", conv) : "",
|
|
745
|
+
}
|
|
746
|
+
return setIds(headers, ids, who)
|
|
720
747
|
}
|
|
721
748
|
|
|
722
|
-
|
|
723
|
-
|
|
749
|
+
// setIds sets the message's id, and the turn's and conversation's for the
|
|
750
|
+
// account who, where the caller set none.
|
|
751
|
+
function setIds(headers, ids, who) {
|
|
724
752
|
const msg = hex()
|
|
725
753
|
headers.set("X-Conversation-Message-ID", msg)
|
|
726
754
|
headers.set("X-Request-ID", msg)
|
|
755
|
+
const set = []
|
|
756
|
+
const own = (kind, base) => (who ? idFor(kind, [`account:${who}`, "|", base]) : base)
|
|
727
757
|
if (!headers.has("X-Conversation-Request-ID")) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
758
|
+
headers.set("X-Conversation-Request-ID", ids.turn ? own("turn", ids.turn) : hex())
|
|
759
|
+
set.push("X-Conversation-Request-ID")
|
|
760
|
+
}
|
|
761
|
+
if (ids.conv && !headers.has("X-Conversation-ID")) {
|
|
762
|
+
headers.set("X-Conversation-ID", own("conversation", ids.conv))
|
|
763
|
+
set.push("X-Conversation-ID")
|
|
732
764
|
}
|
|
733
|
-
|
|
765
|
+
return { ...ids, set }
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// scopeOf names the account a chat goes as for attend: its uid, its
|
|
769
|
+
// enterprise, and for a key account the key (only ever hashed).
|
|
770
|
+
function scopeOf(a) {
|
|
771
|
+
return `${a?.uid ?? ""}|${a?.enterpriseId ?? ""}|${a?.source === "key" ? a.key ?? "" : ""}`
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// reattend sets a chat's ids again for the account it is now sent as, when
|
|
775
|
+
// that is another account than the one they were set for.
|
|
776
|
+
function reattend(headers, ids, was, now) {
|
|
777
|
+
if (was === now) return ids
|
|
778
|
+
for (const h of ids.set) headers.delete(h)
|
|
779
|
+
return setIds(headers, ids, now)
|
|
734
780
|
}
|
|
735
781
|
|
|
736
782
|
// FLAGGED are the words WorkBuddy refuses a chat for, "Illegal API
|
|
@@ -1328,7 +1374,7 @@ function fitted(body) {
|
|
|
1328
1374
|
|
|
1329
1375
|
// gzipped is a chat's body compressed, as WorkBuddy's own client sends its
|
|
1330
1376
|
// model requests: its CLI's GzipRequestProcessor (WorkBuddy 5.9.3, CLI
|
|
1331
|
-
// 2.165.0
|
|
1377
|
+
// 2.165.0, on both builds) gzips each POST's body and sets
|
|
1332
1378
|
// Content-Encoding: gzip when the product's RequestBodyGzip feature is on,
|
|
1333
1379
|
// which both builds' product.json turn on and /v3/config (merged over it)
|
|
1334
1380
|
// doesn't turn off. Sent as plain JSON, a long agent conversation went up
|
|
@@ -2506,7 +2552,8 @@ function makePlugin(site) {
|
|
|
2506
2552
|
const completion = !native && isCompletion(req ? req.url : input, chat)
|
|
2507
2553
|
if (completion && chat && !namesEffort(chat) && withDefaultEffort(chat, await effortList(a))) changed = true
|
|
2508
2554
|
chatHeaders(site, headers)
|
|
2509
|
-
|
|
2555
|
+
let who = scopeOf(a)
|
|
2556
|
+
let ids = attendChat(headers, chat, session, who)
|
|
2510
2557
|
const post = (b) => {
|
|
2511
2558
|
// a chat goes gzipped, as WorkBuddy's client sends it
|
|
2512
2559
|
// (gzipped); its size on the wire is what unanswered weighs
|
|
@@ -2553,6 +2600,7 @@ function makePlugin(site) {
|
|
|
2553
2600
|
renewed = true
|
|
2554
2601
|
a = r.a
|
|
2555
2602
|
sign(site, a, headers)
|
|
2603
|
+
ids = reattend(headers, ids, who, (who = scopeOf(a)))
|
|
2556
2604
|
const again = await post(b)
|
|
2557
2605
|
if (again.status !== 401) return again
|
|
2558
2606
|
return out("it refused the token renewed for it too", invalidToken(again), again)
|
|
@@ -2801,4 +2849,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
|
|
|
2801
2849
|
export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
|
|
2802
2850
|
|
|
2803
2851
|
// for tests
|
|
2804
|
-
export const _internal = { idFor, replyAfterToolsOn, 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 }
|
|
2852
|
+
export const _internal = { idFor, replyAfterToolsOn, 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, scopeOf, 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.47",
|
|
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",
|