dsh-edge 0.1.0
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +183 -0
- package/README.zh.md +183 -0
- package/THIRD_PARTY_NOTICES.md +199 -0
- package/dist/_headers +3 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/index-C-1AiF3k.js +112 -0
- package/dist/assets/index-CSGf6Qzd.css +1 -0
- package/dist/assets/langs/c-BIGW1oBm.js +2 -0
- package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
- package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
- package/dist/assets/langs/css-CLj8gQPS.js +2 -0
- package/dist/assets/langs/go-C27-OAKa.js +2 -0
- package/dist/assets/langs/html-CfGypltT.js +2 -0
- package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
- package/dist/assets/langs/java-CylS5w8V.js +2 -0
- package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
- package/dist/assets/langs/less-B1dDrJ26.js +2 -0
- package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
- package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
- package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
- package/dist/assets/langs/php-D-uCvoST.js +2 -0
- package/dist/assets/langs/python-B6aJPvgy.js +2 -0
- package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
- package/dist/assets/langs/rust-B1yitclQ.js +2 -0
- package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
- package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
- package/dist/assets/langs/swift-D82vCrfD.js +2 -0
- package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
- package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
- package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
- package/dist/assets/vendor-Cjbwl5VI.js +411 -0
- package/dist/assets/vendor-CjyC-hUb.css +1 -0
- package/dist/favicon.svg +8 -0
- package/dist/index.html +35 -0
- package/dist/manifest.webmanifest +16 -0
- package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
- package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
- package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
- package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
- package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
- package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
- package/package.json +97 -0
- package/scripts/assemble-web.mjs +230 -0
- package/scripts/bundle-size.d.mts +2 -0
- package/scripts/bundle-size.mjs +26 -0
- package/scripts/bundle.mjs +53 -0
- package/scripts/cli.d.mts +39 -0
- package/scripts/cli.mjs +496 -0
- package/scripts/install.d.mts +149 -0
- package/scripts/install.mjs +989 -0
- package/scripts/legal-files.mjs +24 -0
- package/scripts/verify-packed.mjs +32 -0
- package/scripts/wrangler-config.d.mts +18 -0
- package/scripts/wrangler-config.mjs +84 -0
- package/src/agent.ts +119 -0
- package/src/auth.ts +352 -0
- package/src/deepseek.ts +231 -0
- package/src/deployment.ts +147 -0
- package/src/direct-shell-core-empty.ts +10 -0
- package/src/direct-shell-protocol.ts +5 -0
- package/src/direct-shell.ts +341 -0
- package/src/do-session-persistence.ts +1064 -0
- package/src/edge-api.ts +796 -0
- package/src/edge-credentials.ts +56 -0
- package/src/edge-remotes.ts +41 -0
- package/src/edge-workspace-store.ts +339 -0
- package/src/http.ts +212 -0
- package/src/index.ts +196 -0
- package/src/instance.ts +1007 -0
- package/src/isolated-direct-shell-unavailable.ts +11 -0
- package/src/protocol.ts +39 -0
- package/src/release.ts +9 -0
- package/src/session-store.ts +1110 -0
- package/src/sse.ts +86 -0
- package/src/web-search.ts +38 -0
- package/src/workspace.ts +270 -0
- package/wrangler.jsonc +56 -0
package/src/deepseek.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type { AnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
|
|
2
|
+
import type { CredentialRef } from '@deepseek-ai/dsh-credentials'
|
|
3
|
+
import {
|
|
4
|
+
LlmAdapter,
|
|
5
|
+
LlmError,
|
|
6
|
+
assertUsableApiKey,
|
|
7
|
+
resolveRetryPolicy,
|
|
8
|
+
type GenerateOptions,
|
|
9
|
+
type LlmModelInfo,
|
|
10
|
+
type LlmProviderInfo,
|
|
11
|
+
type LlmResolvedModelInfo,
|
|
12
|
+
type ResolvedRetryPolicy,
|
|
13
|
+
type StreamChunk,
|
|
14
|
+
} from '@deepseek-ai/dsh-llm'
|
|
15
|
+
import type { SessionId } from '@deepseek-ai/dsh-session'
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
18
|
+
DeepSeekAdapter,
|
|
19
|
+
type DeepSeekConnectionOptions,
|
|
20
|
+
} from '@deepseek-ai/dsh-llm-deepseek'
|
|
21
|
+
|
|
22
|
+
const API_KEY_REF = 'DEEPSEEK_API_KEY' as CredentialRef
|
|
23
|
+
const DEFAULT_BASE_URL = 'https://api.deepseek.com'
|
|
24
|
+
const DEFAULT_MAX_TOKENS = 8_192
|
|
25
|
+
const DEFAULT_MODEL = 'deepseek-v4-flash'
|
|
26
|
+
const DEFAULT_REASONING_EFFORT = 'off'
|
|
27
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 120_000
|
|
28
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647
|
|
29
|
+
const MODEL_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/
|
|
30
|
+
|
|
31
|
+
export type EdgeReasoningEffort = 'off' | 'low' | 'high' | 'max'
|
|
32
|
+
|
|
33
|
+
/** Resolve and validate the DeepSeek HTTP endpoint selected by deployment configuration. */
|
|
34
|
+
export function resolveEdgeBaseURL(raw?: string): string {
|
|
35
|
+
const value = raw ?? DEFAULT_BASE_URL
|
|
36
|
+
let parsed: URL
|
|
37
|
+
try {
|
|
38
|
+
parsed = new URL(value)
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error('dsh-edge: DEEPSEEK_BASE_URL must be a valid HTTP(S) URL')
|
|
41
|
+
}
|
|
42
|
+
if ((parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
43
|
+
|| parsed.username.length > 0
|
|
44
|
+
|| parsed.password.length > 0) {
|
|
45
|
+
throw new Error('dsh-edge: DEEPSEEK_BASE_URL must be a valid HTTP(S) URL without credentials')
|
|
46
|
+
}
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Resolve the DeepSeek model id selected by Worker deployment configuration. */
|
|
51
|
+
export function resolveEdgeModel(raw?: string): string {
|
|
52
|
+
if (raw === undefined) return DEFAULT_MODEL
|
|
53
|
+
if (!MODEL_PATTERN.test(raw)) {
|
|
54
|
+
throw new Error('dsh-edge: DEEPSEEK_MODEL must be a valid model id of at most 128 characters')
|
|
55
|
+
}
|
|
56
|
+
return raw
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Resolve the DeepSeek thinking policy selected by Worker deployment configuration. */
|
|
60
|
+
export function resolveEdgeReasoningEffort(raw?: string): EdgeReasoningEffort {
|
|
61
|
+
if (raw === undefined) return DEFAULT_REASONING_EFFORT
|
|
62
|
+
if (raw !== 'off' && raw !== 'low' && raw !== 'high' && raw !== 'max') {
|
|
63
|
+
throw new Error('dsh-edge: DEEPSEEK_REASONING_EFFORT must be one of off, low, high, or max')
|
|
64
|
+
}
|
|
65
|
+
return raw
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Resolve the optional Worker deployment override before an SSE turn opens. */
|
|
69
|
+
export function resolveEdgeStreamIdleTimeoutMs(raw?: string): number {
|
|
70
|
+
if (raw === undefined) return DEFAULT_STREAM_IDLE_TIMEOUT_MS
|
|
71
|
+
if (!/^[1-9]\d*$/.test(raw)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'dsh-edge: DEEPSEEK_STREAM_IDLE_TIMEOUT_MS must be a positive integer',
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
const value = Number(raw)
|
|
77
|
+
if (!Number.isSafeInteger(value) || value > MAX_TIMER_DELAY_MS) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`dsh-edge: DEEPSEEK_STREAM_IDLE_TIMEOUT_MS must be no greater than ${MAX_TIMER_DELAY_MS}`,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
return value
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Resolve the request output cap selected by Worker deployment configuration. */
|
|
86
|
+
export function resolveEdgeMaxOutputTokens(raw?: string): number {
|
|
87
|
+
if (raw === undefined) return DEFAULT_MAX_TOKENS
|
|
88
|
+
if (!/^[1-9]\d*$/.test(raw)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'dsh-edge: DEEPSEEK_MAX_OUTPUT_TOKENS must be a positive integer',
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
const value = Number(raw)
|
|
94
|
+
if (!Number.isSafeInteger(value)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`dsh-edge: DEEPSEEK_MAX_OUTPUT_TOKENS must be no greater than ${Number.MAX_SAFE_INTEGER}`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
return value
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Construct one turn-scoped DSH DeepSeek adapter that resolves its bearer token per operation. */
|
|
103
|
+
function createEdgeDeepSeekModel(options: {
|
|
104
|
+
resolveApiKey: () => Promise<string | undefined>
|
|
105
|
+
baseURL?: string
|
|
106
|
+
maxTokens?: number
|
|
107
|
+
reasoningEffort?: EdgeReasoningEffort
|
|
108
|
+
streamIdleTimeoutMs?: number
|
|
109
|
+
}): DeepSeekAdapter {
|
|
110
|
+
const connection: DeepSeekConnectionOptions = {
|
|
111
|
+
apiKeyEnv: API_KEY_REF,
|
|
112
|
+
baseURL: options.baseURL ?? DEFAULT_BASE_URL,
|
|
113
|
+
defaults: { reasoningEffort: options.reasoningEffort ?? DEFAULT_REASONING_EFFORT },
|
|
114
|
+
maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
115
|
+
defaultContextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
116
|
+
models: [],
|
|
117
|
+
streamIdleTimeoutMs: options.streamIdleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS,
|
|
118
|
+
retryPolicy: resolveRetryPolicy(undefined, 'dsh-edge.retryPolicy'),
|
|
119
|
+
}
|
|
120
|
+
const anonymousRequestId = crypto.randomUUID() as AnonymousUserId
|
|
121
|
+
return new DeepSeekAdapter({
|
|
122
|
+
options: () => connection,
|
|
123
|
+
resolveApiKey: async () => {
|
|
124
|
+
const apiKey = await options.resolveApiKey()
|
|
125
|
+
if (apiKey === undefined) {
|
|
126
|
+
throw new LlmError(
|
|
127
|
+
'dsh-edge: no API key for provider route "deepseek-official"; configure the DEEPSEEK_API_KEY Worker secret',
|
|
128
|
+
'MISSING_CREDENTIAL',
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
return assertUsableApiKey(apiKey, 'dsh-edge', API_KEY_REF)
|
|
132
|
+
},
|
|
133
|
+
// Provider telemetry must not derive identity from the owner or Durable Object id.
|
|
134
|
+
resolveUserId: () => anonymousRequestId,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* One upstream LLM route with per-turn deployment configuration selected by
|
|
140
|
+
* the loop-stamped session id. Credentials resolve independently through the
|
|
141
|
+
* upstream credential service for each model operation.
|
|
142
|
+
*/
|
|
143
|
+
export class EdgeDeepSeekAdapter extends LlmAdapter {
|
|
144
|
+
private readonly active = new Map<SessionId, LlmAdapter>()
|
|
145
|
+
private metadataMaxTokens = DEFAULT_MAX_TOKENS
|
|
146
|
+
private metadataReasoningEffort: EdgeReasoningEffort = DEFAULT_REASONING_EFFORT
|
|
147
|
+
private metadata: DeepSeekAdapter
|
|
148
|
+
|
|
149
|
+
constructor(private readonly resolveApiKey?: () => Promise<string | undefined>) {
|
|
150
|
+
super()
|
|
151
|
+
this.metadata = createEdgeDeepSeekModel({
|
|
152
|
+
resolveApiKey: () => Promise.resolve('dsh-edge-model-metadata'),
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
bind(sessionId: SessionId, options: {
|
|
157
|
+
baseURL?: string
|
|
158
|
+
maxTokens?: number
|
|
159
|
+
reasoningEffort?: EdgeReasoningEffort
|
|
160
|
+
streamIdleTimeoutMs?: number
|
|
161
|
+
}): () => void {
|
|
162
|
+
this.configureDefaults(
|
|
163
|
+
options.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
164
|
+
options.reasoningEffort ?? DEFAULT_REASONING_EFFORT,
|
|
165
|
+
)
|
|
166
|
+
return this.bindAdapter(sessionId, createEdgeDeepSeekModel({
|
|
167
|
+
...options,
|
|
168
|
+
resolveApiKey: this.resolveApiKey ?? (() => Promise.resolve(undefined)),
|
|
169
|
+
}))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private configureDefaults(
|
|
173
|
+
maxTokens: number,
|
|
174
|
+
reasoningEffort: EdgeReasoningEffort,
|
|
175
|
+
): void {
|
|
176
|
+
if (maxTokens === this.metadataMaxTokens
|
|
177
|
+
&& reasoningEffort === this.metadataReasoningEffort) return
|
|
178
|
+
if (this.active.size > 0) {
|
|
179
|
+
throw new LlmError(
|
|
180
|
+
'dsh-edge: deployment model defaults changed while another session is active',
|
|
181
|
+
'EDGE_MODEL_BUSY',
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
this.metadata = createEdgeDeepSeekModel({
|
|
185
|
+
resolveApiKey: () => Promise.resolve('dsh-edge-model-metadata'),
|
|
186
|
+
maxTokens,
|
|
187
|
+
reasoningEffort,
|
|
188
|
+
})
|
|
189
|
+
this.metadataMaxTokens = maxTokens
|
|
190
|
+
this.metadataReasoningEffort = reasoningEffort
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
bindAdapter(sessionId: SessionId, adapter: LlmAdapter): () => void {
|
|
194
|
+
if (this.active.has(sessionId)) {
|
|
195
|
+
throw new LlmError(`dsh-edge: model is already bound for session "${sessionId}"`, 'EDGE_MODEL_BUSY')
|
|
196
|
+
}
|
|
197
|
+
this.active.set(sessionId, adapter)
|
|
198
|
+
return () => {
|
|
199
|
+
if (this.active.get(sessionId) === adapter) this.active.delete(sessionId)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
override providerInfo(provider: string): LlmProviderInfo {
|
|
204
|
+
return this.metadata.providerInfo(provider)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
override providerRetryPolicy(provider: string): ResolvedRetryPolicy {
|
|
208
|
+
return this.metadata.providerRetryPolicy(provider)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
override listModels(provider: string): Promise<readonly LlmModelInfo[]> {
|
|
212
|
+
return this.metadata.listModels(provider)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
override resolveModel(
|
|
216
|
+
provider: string,
|
|
217
|
+
model: string,
|
|
218
|
+
signal?: AbortSignal,
|
|
219
|
+
): Promise<LlmResolvedModelInfo> {
|
|
220
|
+
return this.metadata.resolveModel(provider, model, signal)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
override stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
|
224
|
+
const sessionId = options.sessionId
|
|
225
|
+
const adapter = sessionId === undefined ? undefined : this.active.get(sessionId)
|
|
226
|
+
if (adapter === undefined) {
|
|
227
|
+
throw new LlmError('dsh-edge: no request-scoped DeepSeek configuration is bound', 'EDGE_MODEL_NOT_BOUND')
|
|
228
|
+
}
|
|
229
|
+
return adapter.stream(options)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** Deployment configuration shared by Edge health and turn admission. */
|
|
2
|
+
|
|
3
|
+
import type { EdgeReasoningEffort } from './deepseek.ts'
|
|
4
|
+
import {
|
|
5
|
+
resolveEdgeBaseURL,
|
|
6
|
+
resolveEdgeMaxOutputTokens,
|
|
7
|
+
resolveEdgeModel,
|
|
8
|
+
resolveEdgeReasoningEffort,
|
|
9
|
+
resolveEdgeStreamIdleTimeoutMs,
|
|
10
|
+
} from './deepseek.ts'
|
|
11
|
+
import { resolveDeepSeekApiKey } from './http.ts'
|
|
12
|
+
import { resolveEdgeSearchBaseURL } from './web-search.ts'
|
|
13
|
+
import {
|
|
14
|
+
resolveEdgeCommandTimeoutPolicy,
|
|
15
|
+
type EdgeCommandTimeoutPolicy,
|
|
16
|
+
} from './workspace.ts'
|
|
17
|
+
import { DSH_EDGE_UPSTREAM_VERSION, DSH_EDGE_VERSION } from './release.ts'
|
|
18
|
+
|
|
19
|
+
declare const __DSH_EDGE_DEPLOYMENT_ID__: string | undefined
|
|
20
|
+
|
|
21
|
+
const LOCAL_DEPLOYMENT_ID = 'local-development'
|
|
22
|
+
|
|
23
|
+
/** Worker variables that control one Edge model and shell turn. */
|
|
24
|
+
export interface EdgeDeploymentConfigSource {
|
|
25
|
+
LOADER?: unknown
|
|
26
|
+
DEEPSEEK_API_KEY?: string
|
|
27
|
+
DEEPSEEK_BASE_URL?: string
|
|
28
|
+
DEEPSEEK_MAX_OUTPUT_TOKENS?: string
|
|
29
|
+
DEEPSEEK_MODEL?: string
|
|
30
|
+
DEEPSEEK_REASONING_EFFORT?: string
|
|
31
|
+
DEEPSEEK_SEARCH_BASE_URL?: string
|
|
32
|
+
DEEPSEEK_STREAM_IDLE_TIMEOUT_MS?: string
|
|
33
|
+
DSH_EDGE_DEFAULT_COMMAND_TIMEOUT_MS?: string
|
|
34
|
+
DSH_EDGE_MAX_COMMAND_TIMEOUT_MS?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Validated deployment choices consumed by one Edge turn. */
|
|
38
|
+
export interface EdgeDeploymentConfig {
|
|
39
|
+
baseURL: string
|
|
40
|
+
maxTokens: number
|
|
41
|
+
model: string
|
|
42
|
+
reasoningEffort: EdgeReasoningEffort
|
|
43
|
+
searchBaseURL: string
|
|
44
|
+
streamIdleTimeoutMs: number
|
|
45
|
+
commandTimeoutPolicy: EdgeCommandTimeoutPolicy
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Secret-free deployment facts safe to project to the authenticated owner. */
|
|
49
|
+
export interface EdgeDeploymentProfile {
|
|
50
|
+
shell: 'just-bash-direct' | 'just-bash-isolated'
|
|
51
|
+
storage: 'durable-object-sqlite-vfs'
|
|
52
|
+
deploymentId: string
|
|
53
|
+
apiKeyConfigured: boolean
|
|
54
|
+
baseURL: string
|
|
55
|
+
maxTokens: number
|
|
56
|
+
model: string
|
|
57
|
+
reasoningEffort: EdgeReasoningEffort
|
|
58
|
+
searchBaseURL: string
|
|
59
|
+
streamIdleTimeoutMs: number
|
|
60
|
+
commandTimeoutPolicy: EdgeCommandTimeoutPolicy
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Identify the exact Worker artifact that answered a readiness request. */
|
|
64
|
+
export function resolveEdgeDeploymentId(): string {
|
|
65
|
+
return typeof __DSH_EDGE_DEPLOYMENT_ID__ === 'string'
|
|
66
|
+
? __DSH_EDGE_DEPLOYMENT_ID__
|
|
67
|
+
: LOCAL_DEPLOYMENT_ID
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Resolve every deployment choice before health succeeds or a turn is claimed. */
|
|
71
|
+
export function resolveEdgeDeploymentConfig(
|
|
72
|
+
source: EdgeDeploymentConfigSource,
|
|
73
|
+
): EdgeDeploymentConfig {
|
|
74
|
+
resolveDeepSeekApiKey(source.DEEPSEEK_API_KEY)
|
|
75
|
+
return {
|
|
76
|
+
baseURL: resolveEdgeBaseURL(source.DEEPSEEK_BASE_URL),
|
|
77
|
+
maxTokens: resolveEdgeMaxOutputTokens(source.DEEPSEEK_MAX_OUTPUT_TOKENS),
|
|
78
|
+
model: resolveEdgeModel(source.DEEPSEEK_MODEL),
|
|
79
|
+
reasoningEffort: resolveEdgeReasoningEffort(source.DEEPSEEK_REASONING_EFFORT),
|
|
80
|
+
searchBaseURL: resolveEdgeSearchBaseURL(source.DEEPSEEK_SEARCH_BASE_URL),
|
|
81
|
+
streamIdleTimeoutMs: resolveEdgeStreamIdleTimeoutMs(
|
|
82
|
+
source.DEEPSEEK_STREAM_IDLE_TIMEOUT_MS,
|
|
83
|
+
),
|
|
84
|
+
commandTimeoutPolicy: resolveEdgeCommandTimeoutPolicy(
|
|
85
|
+
source.DSH_EDGE_DEFAULT_COMMAND_TIMEOUT_MS,
|
|
86
|
+
source.DSH_EDGE_MAX_COMMAND_TIMEOUT_MS,
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Whether the deployment supplies a non-blank DeepSeek Worker binding. */
|
|
92
|
+
export function edgeDeploymentApiKeyConfigured(source: EdgeDeploymentConfigSource): boolean {
|
|
93
|
+
return typeof source.DEEPSEEK_API_KEY === 'string'
|
|
94
|
+
&& source.DEEPSEEK_API_KEY.trim().length > 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Omit URL components that commonly carry credentials from the browser projection. */
|
|
98
|
+
function projectedEdgeBaseURL(raw?: string): string {
|
|
99
|
+
const value = resolveEdgeBaseURL(raw)
|
|
100
|
+
const parsed = new URL(value)
|
|
101
|
+
if (parsed.search.length === 0 && parsed.hash.length === 0) return value
|
|
102
|
+
parsed.search = ''
|
|
103
|
+
parsed.hash = ''
|
|
104
|
+
return parsed.href
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Resolve the effective runtime profile without retaining or returning a credential value. */
|
|
108
|
+
export function resolveEdgeDeploymentProfile(
|
|
109
|
+
source: EdgeDeploymentConfigSource,
|
|
110
|
+
): EdgeDeploymentProfile {
|
|
111
|
+
return {
|
|
112
|
+
shell: source.LOADER === undefined ? 'just-bash-direct' : 'just-bash-isolated',
|
|
113
|
+
storage: 'durable-object-sqlite-vfs',
|
|
114
|
+
deploymentId: resolveEdgeDeploymentId(),
|
|
115
|
+
apiKeyConfigured: edgeDeploymentApiKeyConfigured(source),
|
|
116
|
+
baseURL: projectedEdgeBaseURL(source.DEEPSEEK_BASE_URL),
|
|
117
|
+
maxTokens: resolveEdgeMaxOutputTokens(source.DEEPSEEK_MAX_OUTPUT_TOKENS),
|
|
118
|
+
model: resolveEdgeModel(source.DEEPSEEK_MODEL),
|
|
119
|
+
reasoningEffort: resolveEdgeReasoningEffort(source.DEEPSEEK_REASONING_EFFORT),
|
|
120
|
+
searchBaseURL: resolveEdgeSearchBaseURL(source.DEEPSEEK_SEARCH_BASE_URL),
|
|
121
|
+
streamIdleTimeoutMs: resolveEdgeStreamIdleTimeoutMs(
|
|
122
|
+
source.DEEPSEEK_STREAM_IDLE_TIMEOUT_MS,
|
|
123
|
+
),
|
|
124
|
+
commandTimeoutPolicy: resolveEdgeCommandTimeoutPolicy(
|
|
125
|
+
source.DSH_EDGE_DEFAULT_COMMAND_TIMEOUT_MS,
|
|
126
|
+
source.DSH_EDGE_MAX_COMMAND_TIMEOUT_MS,
|
|
127
|
+
),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Validate the default turn configuration and return the public runtime identity. */
|
|
132
|
+
export function resolveEdgeDeploymentHealth(source: EdgeDeploymentConfigSource) {
|
|
133
|
+
resolveEdgeDeploymentConfig(source)
|
|
134
|
+
const profile = resolveEdgeDeploymentProfile(source)
|
|
135
|
+
return {
|
|
136
|
+
ok: true,
|
|
137
|
+
service: 'dsh-edge',
|
|
138
|
+
storage: profile.storage,
|
|
139
|
+
shell: profile.shell,
|
|
140
|
+
agent: 'upstream-react-loop-agent',
|
|
141
|
+
access: 'single-owner-cookie',
|
|
142
|
+
deploymentId: profile.deploymentId,
|
|
143
|
+
version: DSH_EDGE_VERSION,
|
|
144
|
+
upstreamVersion: DSH_EDGE_UPSTREAM_VERSION,
|
|
145
|
+
status: 'ready',
|
|
146
|
+
} as const
|
|
147
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct-build replacement for Computer's embedded Dynamic Worker modules.
|
|
3
|
+
*
|
|
4
|
+
* Direct deployments have no Loader binding, so they cannot select
|
|
5
|
+
* `WorkerShellBackend`. The alias keeps Computer's filesystem and command
|
|
6
|
+
* adapters available without uploading an unreachable shell runtime.
|
|
7
|
+
*/
|
|
8
|
+
const directShellCore = Object.freeze({})
|
|
9
|
+
|
|
10
|
+
export default directShellCore
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Byte ceiling shared by shell execution, retention, and truncation rendering. */
|
|
2
|
+
export const EDGE_SHELL_OUTPUT_LIMIT_BYTES = 65_536
|
|
3
|
+
|
|
4
|
+
/** Standard shell-event result used when direct just-bash stops at its output budget. */
|
|
5
|
+
export const DIRECT_SHELL_OUTPUT_TRUNCATED = 'dsh-edge:direct-output-truncated'
|