dsh-oauth-login 0.2.4 → 0.2.7
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/INSTALL.md +17 -4
- package/INSTALL.zh.md +16 -4
- package/README.en.md +20 -6
- package/README.md +20 -6
- package/lib/bin.js +15 -6
- package/lib/client.js +14 -15
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +15 -6
- package/package.json +34 -30
- package/src/client/SearchableModelSelect.tsx +13 -9
- package/src/client/index.tsx +2 -2
- package/src/hosted-images.ts +2 -2
- package/src/native-tools.ts +9 -3
- package/src/provider.ts +3 -2
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LlmFailure,
|
|
1
|
+
import { LlmFailure, RequestMessage, RetryPolicyConfig, StreamChunk } from "@deepseek-ai/dsh-llm";
|
|
2
2
|
import { Api, AuthInteraction, AuthOperationOptions, Context, Credential, CredentialInfo, CredentialStore, Model, MutableModels, Provider, StreamOptions } from "@earendil-works/pi-ai";
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -395,7 +395,7 @@ declare function decodeHostedImage(base64: string): {
|
|
|
395
395
|
* pi-ai replay cannot represent assistant ImageBlocks. Drop them before the
|
|
396
396
|
* next request so replay metadata still lines up with remaining content.
|
|
397
397
|
*/
|
|
398
|
-
declare function stripAssistantImages(messages: readonly
|
|
398
|
+
declare function stripAssistantImages(messages: readonly RequestMessage[]): RequestMessage[];
|
|
399
399
|
declare function injectHostedImages(source: AsyncIterable<StreamChunk>, capture: HostedCapture, save: (input: SaveImageAttachment) => Promise<ImageAttachmentRef>): AsyncGenerator<StreamChunk>;
|
|
400
400
|
//#endregion
|
|
401
401
|
//#region src/oauth-refresh.d.ts
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LlmError, ReasoningEffortId, RetryPolicySchema, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
2
|
-
import { createModels, defaultProviderAuthContext } from "@earendil-works/pi-ai";
|
|
2
|
+
import { createModels, defaultProviderAuthContext, getCurrentTools, normalizeContext } from "@earendil-works/pi-ai";
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -762,6 +762,10 @@ const OPENROUTER_REFRESH_MS = 9e5;
|
|
|
762
762
|
const OPENROUTER_COOLDOWN_MS = 6e4;
|
|
763
763
|
//#endregion
|
|
764
764
|
//#region src/native-tools.ts
|
|
765
|
+
/**
|
|
766
|
+
* OAuth routes that already have provider-hosted search / image tools.
|
|
767
|
+
* DSH's web_search function tool would hide those and bill a second key.
|
|
768
|
+
*/
|
|
765
769
|
/** DSH model-facing tools that steal traffic from a subscribed provider. */
|
|
766
770
|
const DSH_WEB_TOOL_NAMES = ["web_search", "web_fetch"];
|
|
767
771
|
/** Matching tool-web prompt sections. */
|
|
@@ -1041,10 +1045,15 @@ function wrapOnPayload(existing, providerId, api, policy = DEFAULT_NATIVE_TOOL_P
|
|
|
1041
1045
|
};
|
|
1042
1046
|
}
|
|
1043
1047
|
function filterPiContext(context, providerId, api, policy = DEFAULT_NATIVE_TOOL_POLICY) {
|
|
1044
|
-
if (nativePlan(providerId, api, policy) === void 0
|
|
1048
|
+
if (nativePlan(providerId, api, policy) === void 0) return context;
|
|
1045
1049
|
return {
|
|
1046
1050
|
...context,
|
|
1047
|
-
tools: context.tools.filter((tool) => !isDshWebToolName(tool.name))
|
|
1051
|
+
...context.tools === void 0 ? {} : { tools: context.tools.filter((tool) => !isDshWebToolName(tool.name)) },
|
|
1052
|
+
messages: context.messages.map((message) => message.role !== "system" ? message : {
|
|
1053
|
+
...message,
|
|
1054
|
+
...message.toolsAdded === void 0 ? {} : { toolsAdded: message.toolsAdded.filter((tool) => !isDshWebToolName(tool.name)) },
|
|
1055
|
+
...message.toolsRemoved === void 0 ? {} : { toolsRemoved: message.toolsRemoved.filter((tool) => !isDshWebToolName(tool.name)) }
|
|
1056
|
+
})
|
|
1048
1057
|
};
|
|
1049
1058
|
}
|
|
1050
1059
|
/**
|
|
@@ -1056,7 +1065,7 @@ function filterPiContext(context, providerId, api, policy = DEFAULT_NATIVE_TOOL_
|
|
|
1056
1065
|
* when the provider serializer later emits `tools: []`.
|
|
1057
1066
|
*/
|
|
1058
1067
|
function prepareNativeToolRequest(context, options, providerId, api, policy = DEFAULT_NATIVE_TOOL_POLICY) {
|
|
1059
|
-
if (context.tools === void 0 || nativePlan(providerId, api, policy) === void 0) return {
|
|
1068
|
+
if (context.tools === void 0 && getCurrentTools(context.messages).length === 0 || nativePlan(providerId, api, policy) === void 0) return {
|
|
1060
1069
|
context,
|
|
1061
1070
|
options
|
|
1062
1071
|
};
|
|
@@ -1903,12 +1912,12 @@ function harnessProvider(spec, native = DEFAULT_NATIVE_TOOL_POLICY, catalog) {
|
|
|
1903
1912
|
stream: (model, context, options) => {
|
|
1904
1913
|
const request = prepareNativeToolRequest(context, options ?? {}, spec.id, model.api, native);
|
|
1905
1914
|
const guarded = spec.id === "openrouter" ? prepareOpenRouterOptions(model, request.options, catalog) : request.options;
|
|
1906
|
-
return base.stream(model, request.context, guarded);
|
|
1915
|
+
return base.stream(model, normalizeContext(request.context), guarded);
|
|
1907
1916
|
},
|
|
1908
1917
|
streamSimple: (model, context, options) => {
|
|
1909
1918
|
const request = prepareNativeToolRequest(context, options ?? {}, spec.id, model.api, native);
|
|
1910
1919
|
const guarded = spec.id === "openrouter" ? prepareOpenRouterOptions(model, request.options, catalog) : request.options;
|
|
1911
|
-
return base.streamSimple(model, request.context, guarded);
|
|
1920
|
+
return base.streamSimple(model, normalizeContext(request.context), guarded);
|
|
1912
1921
|
}
|
|
1913
1922
|
};
|
|
1914
1923
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-oauth-login",
|
|
3
3
|
"description": "DSH-owned subscription login powered by Pi AI adapters: OAuth plus official Plan API keys, with an independent credential store.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.7",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -90,16 +90,17 @@
|
|
|
90
90
|
},
|
|
91
91
|
"peerDependencies": {
|
|
92
92
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
93
|
+
"@deepseek-ai/dsh": ">=0.2.0-rc.2 <0.2.1",
|
|
93
94
|
"@deepseek-ai/dsh-atomic-write": "*",
|
|
94
95
|
"@deepseek-ai/dsh-attachment": "*",
|
|
95
|
-
"@deepseek-ai/dsh-client-connection": ">=0.
|
|
96
|
+
"@deepseek-ai/dsh-client-connection": ">=0.2.0-rc.2 <0.2.1",
|
|
96
97
|
"@deepseek-ai/dsh-home-paths": "*",
|
|
97
98
|
"@deepseek-ai/dsh-host-webserver": "*",
|
|
98
99
|
"@deepseek-ai/dsh-invariants": "*",
|
|
99
100
|
"@deepseek-ai/dsh-llm": "*",
|
|
100
101
|
"@deepseek-ai/dsh-llm-pi-ai": "*",
|
|
101
102
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
102
|
-
"@earendil-works/pi-ai": "^0.
|
|
103
|
+
"@earendil-works/pi-ai": "^0.87.1",
|
|
103
104
|
"react": "^18.2.0"
|
|
104
105
|
},
|
|
105
106
|
"peerDependenciesMeta": {
|
|
@@ -109,6 +110,9 @@
|
|
|
109
110
|
"@deepseek-ai/cordis": {
|
|
110
111
|
"optional": true
|
|
111
112
|
},
|
|
113
|
+
"@deepseek-ai/dsh": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
112
116
|
"@deepseek-ai/dsh-atomic-write": {
|
|
113
117
|
"optional": true
|
|
114
118
|
},
|
|
@@ -141,33 +145,33 @@
|
|
|
141
145
|
}
|
|
142
146
|
},
|
|
143
147
|
"devDependencies": {
|
|
144
|
-
"@deepseek-ai/cordis": "
|
|
145
|
-
"@deepseek-ai/dsh-api-session-controller": "0.
|
|
146
|
-
"@deepseek-ai/dsh-atomic-write": "0.
|
|
147
|
-
"@deepseek-ai/dsh-attachment": "0.
|
|
148
|
-
"@deepseek-ai/dsh-authorization": "0.
|
|
149
|
-
"@deepseek-ai/dsh-brand": "0.
|
|
150
|
-
"@deepseek-ai/dsh-client-connection": "0.
|
|
151
|
-
"@deepseek-ai/dsh-client-locale": "0.
|
|
152
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.
|
|
153
|
-
"@deepseek-ai/dsh-client-ui-model-selection": "0.
|
|
154
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.
|
|
155
|
-
"@deepseek-ai/dsh-client-ui-session": "0.
|
|
156
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.
|
|
157
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.
|
|
158
|
-
"@deepseek-ai/dsh-credentials": "0.
|
|
159
|
-
"@deepseek-ai/dsh-fs": "0.
|
|
160
|
-
"@deepseek-ai/dsh-home-paths": "0.
|
|
161
|
-
"@deepseek-ai/dsh-host-webserver": "0.
|
|
162
|
-
"@deepseek-ai/dsh-invariants": "0.
|
|
163
|
-
"@deepseek-ai/dsh-launch-environment": "0.
|
|
164
|
-
"@deepseek-ai/dsh-llm": "0.
|
|
165
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.
|
|
166
|
-
"@deepseek-ai/dsh-settings": "0.
|
|
167
|
-
"@deepseek-ai/dsh-timeout": "0.
|
|
168
|
-
"@deepseek-ai/dsh-util-values": "0.
|
|
169
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
170
|
-
"@earendil-works/pi-ai": "0.
|
|
148
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
149
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
|
|
150
|
+
"@deepseek-ai/dsh-atomic-write": "0.2.0-rc.2",
|
|
151
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
|
|
152
|
+
"@deepseek-ai/dsh-authorization": "0.2.0-rc.2",
|
|
153
|
+
"@deepseek-ai/dsh-brand": "0.2.0-rc.2",
|
|
154
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
|
|
155
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
156
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
|
|
157
|
+
"@deepseek-ai/dsh-client-ui-model-selection": "0.2.0-rc.2",
|
|
158
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
159
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
|
|
160
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
|
|
161
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
162
|
+
"@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
|
|
163
|
+
"@deepseek-ai/dsh-fs": "0.2.0-rc.2",
|
|
164
|
+
"@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
|
|
165
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
|
|
166
|
+
"@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
|
|
167
|
+
"@deepseek-ai/dsh-launch-environment": "0.2.0-rc.2",
|
|
168
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
169
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.2",
|
|
170
|
+
"@deepseek-ai/dsh-settings": "0.2.0-rc.2",
|
|
171
|
+
"@deepseek-ai/dsh-timeout": "0.2.0-rc.2",
|
|
172
|
+
"@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
|
|
173
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
174
|
+
"@earendil-works/pi-ai": "0.87.1",
|
|
171
175
|
"@types/node": "^22.20.0",
|
|
172
176
|
"@types/react": "~18.3.1",
|
|
173
177
|
"@types/semver": "7.7.1",
|
|
@@ -322,18 +322,22 @@ export function SearchableModelSelect(
|
|
|
322
322
|
close()
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
const settleSelection = (
|
|
326
|
-
if (
|
|
325
|
+
const settleSelection = (result: Awaited<ReturnType<ModelSelectInjected['select']>>): void => {
|
|
326
|
+
if (result === undefined) return
|
|
327
|
+
if (result.ok) {
|
|
327
328
|
if (rootRef.current !== null) close(true)
|
|
328
329
|
return
|
|
329
330
|
}
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
331
|
+
const { error } = result
|
|
332
|
+
noticeSeq.current += 1
|
|
333
|
+
window.clearTimeout(noticeTimer.current)
|
|
334
|
+
setNotice({
|
|
335
|
+
seq: noticeSeq.current,
|
|
336
|
+
text: error.code === 'session/writer-held'
|
|
337
|
+
? t('error.sessionInUse')
|
|
338
|
+
: t('error.action', { message: `${error.code}: ${error.message}` }),
|
|
339
|
+
})
|
|
340
|
+
noticeTimer.current = window.setTimeout(() => { setNotice(null) }, 4000)
|
|
337
341
|
}
|
|
338
342
|
|
|
339
343
|
const choose = (selection: ModelSelection): void => {
|
package/src/client/index.tsx
CHANGED
|
@@ -62,8 +62,8 @@ export function apply(ctx: ClientContext): void {
|
|
|
62
62
|
if (available) directory.load().catch(() => { /* surfaced on the store */ })
|
|
63
63
|
},
|
|
64
64
|
select: (selection: ModelSelection) => available
|
|
65
|
-
? directory.select(selection)
|
|
66
|
-
: Promise.resolve(
|
|
65
|
+
? directory.select(selection)
|
|
66
|
+
: Promise.resolve(undefined),
|
|
67
67
|
ts,
|
|
68
68
|
}
|
|
69
69
|
},
|
package/src/hosted-images.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { ImageAttachmentRef, ImageMediaType, SaveImageAttachment } from '@deepseek-ai/dsh-attachment'
|
|
7
|
-
import type {
|
|
7
|
+
import type { RequestMessage, StreamChunk } from '@deepseek-ai/dsh-llm'
|
|
8
8
|
import type { HostedCapture, HostedImage } from './hosted-capture.ts'
|
|
9
9
|
|
|
10
10
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -107,7 +107,7 @@ function extensionFor(mediaType: ImageMediaType): string {
|
|
|
107
107
|
* pi-ai replay cannot represent assistant ImageBlocks. Drop them before the
|
|
108
108
|
* next request so replay metadata still lines up with remaining content.
|
|
109
109
|
*/
|
|
110
|
-
export function stripAssistantImages(messages: readonly
|
|
110
|
+
export function stripAssistantImages(messages: readonly RequestMessage[]): RequestMessage[] {
|
|
111
111
|
return messages.map(message => {
|
|
112
112
|
if (message.role !== 'assistant') return message
|
|
113
113
|
const content = message.content.filter(block => block.type !== 'image')
|
package/src/native-tools.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* DSH's web_search function tool would hide those and bill a second key.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { getCurrentTools } from '@earendil-works/pi-ai'
|
|
6
7
|
import type { Context as PiContext, StreamOptions } from '@earendil-works/pi-ai'
|
|
7
8
|
import type { ReplayEnvelope, StreamChunk } from '@deepseek-ai/dsh-llm'
|
|
8
9
|
import { piLoginProviderByRoute } from './catalog.ts'
|
|
@@ -391,10 +392,15 @@ export function filterPiContext(
|
|
|
391
392
|
api: string | undefined,
|
|
392
393
|
policy: NativeToolPolicy = DEFAULT_NATIVE_TOOL_POLICY,
|
|
393
394
|
): PiContext {
|
|
394
|
-
if (nativePlan(providerId, api, policy) === undefined
|
|
395
|
+
if (nativePlan(providerId, api, policy) === undefined) return context
|
|
395
396
|
return {
|
|
396
397
|
...context,
|
|
397
|
-
tools: context.tools.filter(tool => !isDshWebToolName(tool.name)),
|
|
398
|
+
...(context.tools === undefined ? {} : { tools: context.tools.filter(tool => !isDshWebToolName(tool.name)) }),
|
|
399
|
+
messages: context.messages.map(message => message.role !== 'system' ? message : {
|
|
400
|
+
...message,
|
|
401
|
+
...(message.toolsAdded === undefined ? {} : { toolsAdded: message.toolsAdded.filter(tool => !isDshWebToolName(tool.name)) }),
|
|
402
|
+
...(message.toolsRemoved === undefined ? {} : { toolsRemoved: message.toolsRemoved.filter(tool => !isDshWebToolName(tool.name)) }),
|
|
403
|
+
}),
|
|
398
404
|
}
|
|
399
405
|
}
|
|
400
406
|
|
|
@@ -413,7 +419,7 @@ export function prepareNativeToolRequest<TOptions extends StreamOptions>(
|
|
|
413
419
|
api: string | undefined,
|
|
414
420
|
policy: NativeToolPolicy = DEFAULT_NATIVE_TOOL_POLICY,
|
|
415
421
|
): { context: PiContext; options: TOptions & StreamOptions } {
|
|
416
|
-
if (context.tools === undefined || nativePlan(providerId, api, policy) === undefined) {
|
|
422
|
+
if ((context.tools === undefined && getCurrentTools(context.messages).length === 0) || nativePlan(providerId, api, policy) === undefined) {
|
|
417
423
|
return { context, options }
|
|
418
424
|
}
|
|
419
425
|
const onPayload = wrapOnPayload(options?.onPayload, providerId, api, policy)
|
package/src/provider.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Installed pi-ai providers remapped onto independent harness routes. */
|
|
2
2
|
|
|
3
|
+
import { normalizeContext } from '@earendil-works/pi-ai'
|
|
3
4
|
import { builtinProviders } from '@earendil-works/pi-ai/providers/all'
|
|
4
5
|
import type { Api, ApiKeyAuth, Model, Provider } from '@earendil-works/pi-ai'
|
|
5
6
|
import { PI_LOGIN_PROVIDERS } from './catalog.ts'
|
|
@@ -108,7 +109,7 @@ export function harnessProvider(
|
|
|
108
109
|
? prepareOpenRouterOptions(model, request.options, catalog) : request.options
|
|
109
110
|
return base.stream(
|
|
110
111
|
model,
|
|
111
|
-
request.context,
|
|
112
|
+
normalizeContext(request.context),
|
|
112
113
|
// pi-ai's generic ApiStreamOptions<T> is a conditional type. The
|
|
113
114
|
// preparation step preserves every provider-specific field and only
|
|
114
115
|
// adds StreamOptions.onPayload, but TypeScript cannot prove that for T.
|
|
@@ -121,7 +122,7 @@ export function harnessProvider(
|
|
|
121
122
|
? prepareOpenRouterOptions(model, request.options, catalog) : request.options
|
|
122
123
|
return base.streamSimple(
|
|
123
124
|
model,
|
|
124
|
-
request.context,
|
|
125
|
+
normalizeContext(request.context),
|
|
125
126
|
guarded,
|
|
126
127
|
)
|
|
127
128
|
},
|