dsh-plugin-subscription-oauth 0.17.1 → 0.17.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.
- package/lib/client/FastModeButton.js +11 -2
- package/lib/client/index.js +28 -16
- package/lib/client.js +32 -13
- package/lib/client.js.map +1 -1
- package/lib/providers/workbuddy.d.ts +10 -8
- package/lib/providers/workbuddy.js +46 -33
- package/package.json +1 -1
|
@@ -181,18 +181,20 @@ export declare class WorkBuddyAdapter extends LlmAdapter {
|
|
|
181
181
|
/**
|
|
182
182
|
* The stored session one access token belongs to.
|
|
183
183
|
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* is still read with the token the caller passed.
|
|
189
|
-
*
|
|
190
|
-
* @param accessToken - the token discovery was called with.
|
|
191
|
-
* @returns the session's fields as a signable token, or undefined when none was readable.
|
|
184
|
+
* The chat signs with the resolved token, but the request still needs the session's
|
|
185
|
+
* attribution fields (`uid`, `enterpriseId`, `domain`). They are read back by matching
|
|
186
|
+
* the resolved token against each stored account; a token that matches none (already
|
|
187
|
+
* rotated away) leaves the caller to sign with the bare token.
|
|
192
188
|
*/
|
|
193
189
|
private sessionFor;
|
|
194
190
|
providerRetryPolicy(): undefined;
|
|
195
191
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
192
|
+
/**
|
|
193
|
+
* One chat-completions POST, named the way WorkBuddy's own client names a chat: the
|
|
194
|
+
* message, the request, the turn, and the conversation. Without these the gateway
|
|
195
|
+
* counts the request under no client and can cut the stream.
|
|
196
|
+
*/
|
|
197
|
+
private post;
|
|
196
198
|
/** The stored session's fields as the token the request signs with. */
|
|
197
199
|
private tokenOf;
|
|
198
200
|
private requestBody;
|
|
@@ -614,14 +614,10 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
614
614
|
/**
|
|
615
615
|
* The stored session one access token belongs to.
|
|
616
616
|
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
* is still read with the token the caller passed.
|
|
622
|
-
*
|
|
623
|
-
* @param accessToken - the token discovery was called with.
|
|
624
|
-
* @returns the session's fields as a signable token, or undefined when none was readable.
|
|
617
|
+
* The chat signs with the resolved token, but the request still needs the session's
|
|
618
|
+
* attribution fields (`uid`, `enterpriseId`, `domain`). They are read back by matching
|
|
619
|
+
* the resolved token against each stored account; a token that matches none (already
|
|
620
|
+
* rotated away) leaves the caller to sign with the bare token.
|
|
625
621
|
*/
|
|
626
622
|
async sessionFor(accessToken) {
|
|
627
623
|
try {
|
|
@@ -630,8 +626,7 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
630
626
|
if (held?.session.accessToken === accessToken)
|
|
631
627
|
return this.tokenOf(held.session);
|
|
632
628
|
}
|
|
633
|
-
|
|
634
|
-
return current === undefined ? undefined : this.tokenOf(current.session);
|
|
629
|
+
return undefined;
|
|
635
630
|
}
|
|
636
631
|
catch {
|
|
637
632
|
return undefined;
|
|
@@ -642,10 +637,11 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
642
637
|
}
|
|
643
638
|
async *stream(options) {
|
|
644
639
|
diag(`stream entered: model=${options.model} provider=${options.provider}`);
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const
|
|
640
|
+
// Resolve (not just read) the session so a token close to its end is refreshed
|
|
641
|
+
// eagerly, the way WorkBuddy's own app does — a bare session can carry an expired
|
|
642
|
+
// access token into the request, which the gateway refuses with a 403.
|
|
643
|
+
const held = await this.options.tokens.resolve();
|
|
644
|
+
const session = await this.sessionFor(held.accessToken);
|
|
649
645
|
const watchdog = idleWatchdog(options.signal, this.options.streamIdleTimeoutMs);
|
|
650
646
|
// Capture the tool-call history so unsafe tool names in past turns can be renamed to
|
|
651
647
|
// the safe ones the model now sees, keeping the conversation consistent.
|
|
@@ -653,28 +649,24 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
653
649
|
// The last usage frame carries the turn's real billed credit; record it so the spend
|
|
654
650
|
// total is a sum of real figures, not an estimate.
|
|
655
651
|
const turn = { credit: 0 };
|
|
656
|
-
diag(`stream start: model=${options.model} account=${held.account} expiresAt=${held.
|
|
652
|
+
diag(`stream start: model=${options.model} account=${held.account} expiresAt=${held.expiresAt} now=${Date.now()}`);
|
|
657
653
|
try {
|
|
658
654
|
const { body, names } = this.requestBody(options, history);
|
|
659
655
|
diag(`body: keys=${Object.keys(body).join(',')} messages=${Array.isArray(body.messages) ? body.messages.length : 0} model=${String(body.model)}`);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
body: JSON.stringify(body),
|
|
675
|
-
signal: watchdog.signal,
|
|
676
|
-
});
|
|
677
|
-
diag(`response: status=${response.status} ok=${response.ok} ct=${response.headers.get('content-type') ?? ''}`);
|
|
656
|
+
// Post once with the resolved token. A 401/403 can mean the access token lapsed
|
|
657
|
+
// between resolve() and now: force one refresh and post again, as WorkBuddy's own
|
|
658
|
+
// client does — a 403 with a genuinely dead credential keeps its meaning below.
|
|
659
|
+
let response = await this.post(body, session ?? held, watchdog.signal);
|
|
660
|
+
if (response.status === 401 || response.status === 403) {
|
|
661
|
+
diag(`response: ${response.status} on resolved token; forcing one refresh and retrying`);
|
|
662
|
+
const refreshed = await this.options.tokens.resolve(held.account, true);
|
|
663
|
+
const fresh = await this.sessionFor(refreshed.accessToken);
|
|
664
|
+
response = await this.post(body, fresh ?? refreshed, watchdog.signal);
|
|
665
|
+
diag(`response after refresh: status=${response.status} ok=${response.ok} ct=${response.headers.get('content-type') ?? ''}`);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
diag(`response: status=${response.status} ok=${response.ok} ct=${response.headers.get('content-type') ?? ''}`);
|
|
669
|
+
}
|
|
678
670
|
// WorkBuddy refuses a chat mid-stream when the request carries words it filters; a
|
|
679
671
|
// 403/400 here names why, so surface it instead of dying on an empty stream.
|
|
680
672
|
if (!response.ok || response.body === null) {
|
|
@@ -692,6 +684,27 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
692
684
|
}
|
|
693
685
|
}
|
|
694
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* One chat-completions POST, named the way WorkBuddy's own client names a chat: the
|
|
689
|
+
* message, the request, the turn, and the conversation. Without these the gateway
|
|
690
|
+
* counts the request under no client and can cut the stream.
|
|
691
|
+
*/
|
|
692
|
+
post(body, token, signal) {
|
|
693
|
+
return this.fetchFn(WORKBUDDY_API, {
|
|
694
|
+
method: 'POST',
|
|
695
|
+
headers: {
|
|
696
|
+
'Content-Type': 'application/json',
|
|
697
|
+
'Accept': 'text/event-stream',
|
|
698
|
+
...workbuddyHeaders(token),
|
|
699
|
+
'X-Conversation-Message-ID': randomHex(),
|
|
700
|
+
'X-Request-ID': randomHex(),
|
|
701
|
+
'X-Conversation-Request-ID': randomHex(),
|
|
702
|
+
'X-Conversation-ID': randomHex(),
|
|
703
|
+
},
|
|
704
|
+
body: JSON.stringify(body),
|
|
705
|
+
signal,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
695
708
|
/** The stored session's fields as the token the request signs with. */
|
|
696
709
|
tokenOf(session) {
|
|
697
710
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-subscription-oauth",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "Use subscription accounts as DeepSeek Harness LLM providers — Kimi Code (OAuth device flow), Grok / X Premium (OIDC + PKCE), Google Antigravity (Google OAuth + Cloud Code Assist) and GLM / BigModel (ZCode's browser sign-in) — with per-model visibility, drag-to-reorder Auto fallback and plan quotas from the web Settings page",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "EricFL1998",
|