@magpie-community/opencode-workbuddy-auth 0.1.12 → 0.1.13
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 -0
- package/index.mjs +44 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,14 @@ included, doesn't mark the account, as the built-in never did; magpie
|
|
|
43
43
|
tries again later, and the old token is used meanwhile.
|
|
44
44
|
OpenCode doesn't call `auth.refresh`; the check before each request stays.
|
|
45
45
|
|
|
46
|
+
An enterprise (team) account is asked for as the enterprise. A browser
|
|
47
|
+
sign-in keeps the enterprise id WorkBuddy names for the account (from
|
|
48
|
+
`/login/account`, else its account list), and desktop's is read from the
|
|
49
|
+
app's file. Every request then carries `X-Enterprise-Id` and `X-Tenant-Id`,
|
|
50
|
+
as WorkBuddy's own client sends them; without them WorkBuddy answers as the
|
|
51
|
+
personal account, with the personal plan's models. A browser sign-in made
|
|
52
|
+
before 0.1.13 has no enterprise id kept: sign in again.
|
|
53
|
+
|
|
46
54
|
Desktop's sign-in is refreshed only when the app hasn't refreshed it
|
|
47
55
|
itself, and the new token is kept in memory, never written back, as magpie's
|
|
48
56
|
built-in WorkBuddy does.
|
package/index.mjs
CHANGED
|
@@ -172,6 +172,7 @@ function renew(site, a, save) {
|
|
|
172
172
|
"X-Refresh-Token": a.refresh,
|
|
173
173
|
"X-Auth-Refresh-Source": "plugin",
|
|
174
174
|
"X-Domain": domainOf(site, a),
|
|
175
|
+
...enterpriseHeaders(a),
|
|
175
176
|
}, {})
|
|
176
177
|
if (!got?.accessToken) throw Object.assign(new Error("WorkBuddy gave no refreshed token"), { bare: true })
|
|
177
178
|
const next = merge(a, got)
|
|
@@ -259,10 +260,48 @@ function sign(site, a, headers) {
|
|
|
259
260
|
headers.set("X-Agent-Purpose", "conversation")
|
|
260
261
|
headers.set("X-IDE-Name", "WorkBuddy")
|
|
261
262
|
headers.set("X-IDE-Version", UA_VERSION)
|
|
263
|
+
for (const [k, v] of Object.entries(enterpriseHeaders(a))) headers.set(k, v)
|
|
262
264
|
if (!site.ai) return
|
|
263
265
|
headers.set("X-Agent-Type", "main")
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
// enterpriseHeaders names the enterprise (team) an account signed in as, as
|
|
269
|
+
// WorkBuddy's own client does on every request it makes for that account
|
|
270
|
+
// (its auth interceptor: X-Enterprise-Id and X-Tenant-Id, both the
|
|
271
|
+
// account's enterpriseId). Without them WorkBuddy answers as the personal
|
|
272
|
+
// account: /v3/config lists the personal plan's models and not the
|
|
273
|
+
// enterprise's (#75). A personal account has none and sends none.
|
|
274
|
+
function enterpriseHeaders(a) {
|
|
275
|
+
const id = typeof a?.enterpriseId === "string" ? a.enterpriseId.trim() : ""
|
|
276
|
+
return id ? { "X-Enterprise-Id": id, "X-Tenant-Id": id } : {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// enterpriseOf is the enterprise the account a sign-in gave belongs to:
|
|
280
|
+
// the one /login/account names, else, for an account that isn't personal,
|
|
281
|
+
// the one WorkBuddy's account list has for it — the same choice WorkBuddy's
|
|
282
|
+
// own client makes (mergeCurrentAccountDetails): the uid's only entry of that
|
|
283
|
+
// type, or the only one it last signed in to. None found is personal.
|
|
284
|
+
async function enterpriseOf(site, a, who) {
|
|
285
|
+
const own = typeof who?.enterpriseId === "string" ? who.enterpriseId.trim() : ""
|
|
286
|
+
if (own) return own
|
|
287
|
+
const type = String(who?.type ?? "").trim().toLowerCase()
|
|
288
|
+
if (!type || type === "personal") return ""
|
|
289
|
+
let list
|
|
290
|
+
try {
|
|
291
|
+
list = await call(site, "GET", "/v2/plugin/accounts", {
|
|
292
|
+
Authorization: "Bearer " + a.access,
|
|
293
|
+
"X-Domain": domainOf(site, a),
|
|
294
|
+
})
|
|
295
|
+
} catch {
|
|
296
|
+
return ""
|
|
297
|
+
}
|
|
298
|
+
const mine = (list?.accounts ?? []).filter((x) => x?.uid === who.uid && typeof x.enterpriseId === "string" && x.enterpriseId.trim())
|
|
299
|
+
const same = mine.filter((x) => String(x.type ?? "").trim().toLowerCase() === type)
|
|
300
|
+
const last = mine.filter((x) => x.lastLogin)
|
|
301
|
+
const pick = same.length === 1 ? same[0] : last.length === 1 ? last[0] : mine.length === 1 ? mine[0] : undefined
|
|
302
|
+
return pick ? pick.enterpriseId.trim() : ""
|
|
303
|
+
}
|
|
304
|
+
|
|
266
305
|
// WorkBuddy's own client names each chat to its gateway four ways: the
|
|
267
306
|
// conversation it belongs to, the user send (turn) it answers, the message
|
|
268
307
|
// itself, and the request that carried it. Its backend counts a user send as
|
|
@@ -759,6 +798,7 @@ async function browserSignIn(site) {
|
|
|
759
798
|
"X-No-Enterprise-Id": "true",
|
|
760
799
|
}, [12151], deadline)
|
|
761
800
|
a.uid = who.uid ?? ""
|
|
801
|
+
a.enterpriseId = await enterpriseOf(site, a, who)
|
|
762
802
|
return success(site, a, who.nickname || who.phoneNumber || who.uid)
|
|
763
803
|
} catch (e) {
|
|
764
804
|
return { type: "failed", error: e.message }
|
|
@@ -776,6 +816,7 @@ function success(site, a, name) {
|
|
|
776
816
|
accountId: name || site.name,
|
|
777
817
|
uid: a.uid ?? "",
|
|
778
818
|
domain: a.domain ?? "",
|
|
819
|
+
...(a.enterpriseId ? { enterpriseId: a.enterpriseId } : {}),
|
|
779
820
|
...(a.refreshExpiresAt ? { refreshExpiresAt: a.refreshExpiresAt } : {}),
|
|
780
821
|
...(a.tokenType ? { tokenType: a.tokenType } : {}),
|
|
781
822
|
}
|
|
@@ -808,6 +849,7 @@ function readDesktop(site) {
|
|
|
808
849
|
domain: typeof t.domain === "string" ? t.domain : "",
|
|
809
850
|
tokenType: typeof t.tokenType === "string" ? t.tokenType : "",
|
|
810
851
|
uid: acct.uid,
|
|
852
|
+
...(typeof acct.enterpriseId === "string" && acct.enterpriseId.trim() ? { enterpriseId: acct.enterpriseId.trim() } : {}),
|
|
811
853
|
name: acct.nickname || acct.phoneNumber || acct.uid,
|
|
812
854
|
}
|
|
813
855
|
} catch {
|
|
@@ -880,7 +922,7 @@ async function meter(site, a, path) {
|
|
|
880
922
|
method: "POST",
|
|
881
923
|
headers: { "Content-Type": "application/json", Accept: "application/json", "User-Agent": "WorkBuddy/" + UA_VERSION,
|
|
882
924
|
Authorization: "Bearer " + a.access, "X-User-Id": a.uid ?? "", "X-Domain": domainOf(site, a), "X-Product": "SaaS",
|
|
883
|
-
"X-IDE-Type": "WorkBuddy" },
|
|
925
|
+
"X-IDE-Type": "WorkBuddy", ...enterpriseHeaders(a) },
|
|
884
926
|
body: "{}",
|
|
885
927
|
signal: AbortSignal.timeout(20000),
|
|
886
928
|
})
|
|
@@ -1216,4 +1258,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
|
|
|
1216
1258
|
export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
|
|
1217
1259
|
|
|
1218
1260
|
// for tests
|
|
1219
|
-
export const _internal = { withSystem, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
|
|
1261
|
+
export const _internal = { enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_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.
|
|
3
|
+
"version": "0.1.13",
|
|
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",
|