@yanglaofish/dsh-proxy-pro 1.0.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.md +153 -0
- package/cordis.patch.yml +14 -0
- package/docs/ACCEPTANCE.md +61 -0
- package/docs/LESSONS.md +551 -0
- package/docs/SPEC.md +381 -0
- package/lib/client.js +457 -0
- package/lib/index.js +693 -0
- package/lib/proxy-core.js +268 -0
- package/lib/tool-schemas.js +103 -0
- package/package.json +62 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-proxy-pro — pure proxy-resolution logic (host + agent tools + HTTP API).
|
|
3
|
+
*
|
|
4
|
+
* Dependency-free (no DSH/Cordis imports) so it can be tested in isolation.
|
|
5
|
+
* Covers:
|
|
6
|
+
* - parseProxyServer / parseRegValue — read Windows Internet Settings facts.
|
|
7
|
+
* - makeSystemProxyReader — reg.exe-backed reader (runner injectable).
|
|
8
|
+
* - composeNoProxy / parseOverrideEntries — NO_PROXY + Windows ProxyOverride merge.
|
|
9
|
+
* - resolveProxyState — effective proxy from config + (system mode) live facts.
|
|
10
|
+
* - keepaliveHint — when to suggest a curl NAT window (NTLM SWG proxies).
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-proxy-pro/proxy-core
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const PROXY_REG_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'
|
|
16
|
+
const LOCAL_ENTRIES = ['localhost', '127.0.0.1', '::1']
|
|
17
|
+
|
|
18
|
+
/** Normalize a bare `host:port` or already-schemed URL into an http(s) URL. */
|
|
19
|
+
export function normalizeProxyUrl(value) {
|
|
20
|
+
if (typeof value !== 'string') return null
|
|
21
|
+
const trimmed = value.trim()
|
|
22
|
+
if (trimmed.length === 0) return null
|
|
23
|
+
let url
|
|
24
|
+
try {
|
|
25
|
+
url = new URL(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`)
|
|
26
|
+
} catch {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') return null
|
|
30
|
+
if (!url.hostname) return null
|
|
31
|
+
return url.toString().replace(/\/+$/, '')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse a Windows `ProxyServer` string: single `host:port` (both protocols)
|
|
36
|
+
* or `http=...;https=...` form; `socks=` ignored (undici speaks HTTP CONNECT).
|
|
37
|
+
*/
|
|
38
|
+
export function parseProxyServer(value) {
|
|
39
|
+
if (typeof value !== 'string') return null
|
|
40
|
+
const trimmed = value.trim()
|
|
41
|
+
if (trimmed.length === 0) return null
|
|
42
|
+
const parts = trimmed.split(';').map((p) => p.trim()).filter(Boolean)
|
|
43
|
+
const out = {}
|
|
44
|
+
let usable = false
|
|
45
|
+
for (const part of parts) {
|
|
46
|
+
const eq = part.indexOf('=')
|
|
47
|
+
if (eq === -1) {
|
|
48
|
+
const url = normalizeProxyUrl(part)
|
|
49
|
+
if (url === null) return null
|
|
50
|
+
out.http = url
|
|
51
|
+
out.https = url
|
|
52
|
+
usable = true
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
const protocol = part.slice(0, eq).trim().toLowerCase()
|
|
56
|
+
const target = part.slice(eq + 1).trim()
|
|
57
|
+
if (protocol !== 'http' && protocol !== 'https') continue
|
|
58
|
+
const url = normalizeProxyUrl(target)
|
|
59
|
+
if (url === null) return null
|
|
60
|
+
out[protocol] = url
|
|
61
|
+
usable = true
|
|
62
|
+
}
|
|
63
|
+
return usable ? out : null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Parse a `REG_DWORD` line like ` ProxyEnable REG_DWORD 0x1`. */
|
|
67
|
+
export function parseRegDword(line) {
|
|
68
|
+
if (typeof line !== 'string') return undefined
|
|
69
|
+
const match = /REG_DWORD\s+0x([0-9a-fA-F]+)/.exec(line)
|
|
70
|
+
if (match === null) return undefined
|
|
71
|
+
return Number.parseInt(match[1], 16)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Parse a `REG_SZ` line like ` ProxyServer REG_SZ 127.0.0.1:10808`. */
|
|
75
|
+
export function parseRegString(line) {
|
|
76
|
+
if (typeof line !== 'string') return undefined
|
|
77
|
+
const match = /REG_(?:SZ|EXPAND_SZ)\s+(.*)$/.exec(line)
|
|
78
|
+
if (match === null) return undefined
|
|
79
|
+
return match[1].trim()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Decode one `reg query` invocation's raw stdout into a fact. */
|
|
83
|
+
export function queryRegValue(stdout) {
|
|
84
|
+
if (typeof stdout !== 'string') return null
|
|
85
|
+
const match = /REG_(?:DWORD|SZ|EXPAND_SZ)\s+(.*)$/.exec(stdout.trim())
|
|
86
|
+
if (match === null) return null
|
|
87
|
+
return match[1].trim()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Build a system-proxy reader for Windows: three `reg.exe` queries resolved
|
|
92
|
+
* into `{ enabled, http, https, url, override, serverLine }`.
|
|
93
|
+
* @param execFile - `(file, args, options) => Promise<{ stdout, stderr }>`.
|
|
94
|
+
*/
|
|
95
|
+
export function makeSystemProxyReader(execFile) {
|
|
96
|
+
const query = async (valueName) => {
|
|
97
|
+
try {
|
|
98
|
+
const result = await execFile('reg', ['query', PROXY_REG_KEY, '/v', valueName], {
|
|
99
|
+
windowsHide: true,
|
|
100
|
+
timeout: 3000,
|
|
101
|
+
})
|
|
102
|
+
const raw = queryRegValue(String(result.stdout ?? ''))
|
|
103
|
+
if (raw === null) return undefined
|
|
104
|
+
if (valueName === 'ProxyEnable') {
|
|
105
|
+
return /^0x[0-9a-fA-F]+$/.test(raw) ? Number.parseInt(raw.slice(2), 16) : Number.parseInt(raw, 10)
|
|
106
|
+
}
|
|
107
|
+
return raw
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return async () => {
|
|
113
|
+
const [enable, server, override] = await Promise.all([
|
|
114
|
+
query('ProxyEnable'),
|
|
115
|
+
query('ProxyServer'),
|
|
116
|
+
query('ProxyOverride'),
|
|
117
|
+
])
|
|
118
|
+
const enabled = typeof enable === 'number' && enable === 1
|
|
119
|
+
const parsed = parseProxyServer(typeof server === 'string' ? server : '')
|
|
120
|
+
const url = enabled ? (parsed?.http ?? parsed?.https ?? null) : null
|
|
121
|
+
return {
|
|
122
|
+
enabled,
|
|
123
|
+
http: enabled ? (parsed?.http ?? null) : null,
|
|
124
|
+
https: enabled ? (parsed?.https ?? null) : null,
|
|
125
|
+
url,
|
|
126
|
+
serverLine: typeof server === 'string' ? server : '',
|
|
127
|
+
override: typeof override === 'string' ? override : '',
|
|
128
|
+
raw: { enable, server, override },
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Merge configured NO_PROXY entries with always-local entries and (optionally)
|
|
135
|
+
* the Windows ProxyOverride list. `<local>` maps to loopbacks; dedup in order.
|
|
136
|
+
*/
|
|
137
|
+
export function composeNoProxy(configured, override) {
|
|
138
|
+
const seen = new Set()
|
|
139
|
+
const out = []
|
|
140
|
+
const push = (entry) => {
|
|
141
|
+
if (entry.length === 0 || seen.has(entry)) return
|
|
142
|
+
seen.add(entry)
|
|
143
|
+
out.push(entry)
|
|
144
|
+
}
|
|
145
|
+
for (const entry of LOCAL_ENTRIES) push(entry)
|
|
146
|
+
const raw = typeof configured === 'string' ? configured : ''
|
|
147
|
+
for (const part of raw.split(',')) {
|
|
148
|
+
const entry = part.trim()
|
|
149
|
+
if (entry.length === 0) continue
|
|
150
|
+
if (entry === '<local>') {
|
|
151
|
+
for (const local of LOCAL_ENTRIES) push(local)
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
push(entry)
|
|
155
|
+
}
|
|
156
|
+
if (typeof override === 'string' && override.trim().length > 0) {
|
|
157
|
+
for (const entry of parseOverrideEntries(override)) push(entry)
|
|
158
|
+
}
|
|
159
|
+
return out.join(',')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parse a Windows `ProxyOverride` string into NO_PROXY entries undici honors.
|
|
164
|
+
* `;`-separated; `<local>` maps to loopbacks; IP-prefix wildcards (`10.*`) are
|
|
165
|
+
* dropped because undici's matcher cannot represent them.
|
|
166
|
+
*/
|
|
167
|
+
export function parseOverrideEntries(override) {
|
|
168
|
+
const out = []
|
|
169
|
+
const seen = new Set()
|
|
170
|
+
const push = (entry) => {
|
|
171
|
+
if (entry.length === 0 || seen.has(entry)) return
|
|
172
|
+
seen.add(entry)
|
|
173
|
+
out.push(entry)
|
|
174
|
+
}
|
|
175
|
+
if (typeof override !== 'string') return out
|
|
176
|
+
const parts = override.split(/[;,]/).map((part) => part.trim()).filter(Boolean)
|
|
177
|
+
for (const part of parts) {
|
|
178
|
+
if (part === '<local>') {
|
|
179
|
+
for (const local of LOCAL_ENTRIES) push(local)
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
if (part.includes('*') && !/^\*?\.[^.*]/.test(part)) continue
|
|
183
|
+
if (/^\*\./.test(part)) {
|
|
184
|
+
push(part)
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
push(part)
|
|
188
|
+
}
|
|
189
|
+
return out
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Compare two system-proxy fact sets (poll dedup). */
|
|
193
|
+
export function systemFactsEqual(a, b) {
|
|
194
|
+
if (a === b) return true
|
|
195
|
+
if (a === null || b === null) return false
|
|
196
|
+
return a.enabled === b.enabled
|
|
197
|
+
&& (a.url ?? null) === (b.url ?? null)
|
|
198
|
+
&& (a.http ?? null) === (b.http ?? null)
|
|
199
|
+
&& (a.https ?? null) === (b.https ?? null)
|
|
200
|
+
&& (a.override ?? '') === (b.override ?? '')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Decide the effective proxy from config plus live system facts. The system
|
|
205
|
+
* ProxyOverride is merged into NO_PROXY only in mode 'system': in custom mode
|
|
206
|
+
* the configured whitelist stands alone (the user's own list, nothing from
|
|
207
|
+
* Windows) (§17.3f).
|
|
208
|
+
* @param {object} config - `{ enabled, mode, customUrl, noProxy }`.
|
|
209
|
+
* @param {object|null} systemProxy - reader output (url/override facts).
|
|
210
|
+
*/
|
|
211
|
+
export function resolveProxyState(config, systemProxy = null) {
|
|
212
|
+
const configOnlyNoProxy = composeNoProxy(config?.noProxy)
|
|
213
|
+
if (config?.enabled !== true || config?.mode === 'none') {
|
|
214
|
+
return { active: false, url: null, http: null, https: null, noProxy: configOnlyNoProxy, reason: config?.mode === 'none' ? 'mode-none' : 'disabled' }
|
|
215
|
+
}
|
|
216
|
+
if (config?.mode === 'custom') {
|
|
217
|
+
const url = normalizeProxyUrl(config?.customUrl)
|
|
218
|
+
if (url === null) {
|
|
219
|
+
return { active: false, url: null, http: null, https: null, noProxy: configOnlyNoProxy, reason: 'invalid-custom-url' }
|
|
220
|
+
}
|
|
221
|
+
return { active: true, url, http: url, https: url, noProxy: configOnlyNoProxy, reason: 'custom' }
|
|
222
|
+
}
|
|
223
|
+
// mode === 'system'
|
|
224
|
+
if (systemProxy?.enabled !== true || systemProxy.url === null) {
|
|
225
|
+
return { active: false, url: null, http: null, https: null, noProxy: configOnlyNoProxy, reason: 'system-proxy-off' }
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
active: true,
|
|
229
|
+
url: systemProxy.url,
|
|
230
|
+
http: systemProxy.http ?? systemProxy.url,
|
|
231
|
+
https: systemProxy.https ?? systemProxy.url,
|
|
232
|
+
noProxy: composeNoProxy(config?.noProxy, systemProxy.override),
|
|
233
|
+
reason: 'system',
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Render a short human-ready summary of the effective state (used by tools,
|
|
239
|
+
* the system prompt section, and the JSON status API).
|
|
240
|
+
*/
|
|
241
|
+
export function summarize(snapshot) {
|
|
242
|
+
const effective = snapshot?.effective
|
|
243
|
+
const config = snapshot?.config
|
|
244
|
+
return {
|
|
245
|
+
active: effective?.active === true,
|
|
246
|
+
enabled: config?.enabled === true,
|
|
247
|
+
mode: config?.mode ?? 'none',
|
|
248
|
+
url: effective?.url ?? '',
|
|
249
|
+
noProxy: effective?.noProxy ?? composeNoProxy(config?.noProxy),
|
|
250
|
+
reason: effective?.reason ?? 'unknown',
|
|
251
|
+
source: config?.mode === 'system' ? 'system' : config?.mode === 'custom' ? 'custom' : 'none',
|
|
252
|
+
at: snapshot?.at ?? '',
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* NTLM keepalive hint: SWG proxies (netentsec etc.) ask undici for NTLM auth
|
|
258
|
+
* which undici cannot negotiate; a curl/browser NAT'd connection keeps a
|
|
259
|
+
* window open. Only relevant when a 407 or auth-required response is seen.
|
|
260
|
+
* The example URL is caller-supplied (the effective proxy), never hardcoded —
|
|
261
|
+
* a fixed sample leaked this developer's own corporate proxy into every hint.
|
|
262
|
+
*/
|
|
263
|
+
export function keepaliveHint(message = '', proxyUrl = '') {
|
|
264
|
+
const text = typeof message === 'string' ? message : ''
|
|
265
|
+
if (!/407|NTLM|proxy authentication|authentication required/i.test(text)) return ''
|
|
266
|
+
const example = typeof proxyUrl === 'string' && proxyUrl !== '' ? proxyUrl : 'http://localhost:7890'
|
|
267
|
+
return `This proxy asks for NTLM authentication, which undici cannot negotiate by itself. A recent curl/browser connection to the same proxy keeps an authenticated window open — run a \`curl\` through the proxy (e.g. \`curl -x ${example} -I https://github.com\`) once, then retry.`
|
|
268
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-proxy-pro — tool schema specs (pure data, no imports).
|
|
3
|
+
*
|
|
4
|
+
* These are the author-facing dsh-tools value-schema specs for the three agent
|
|
5
|
+
* tools. They live in their own module for ONE reason: `test/tool-schema.test.mjs`
|
|
6
|
+
* compiles them with the real `@deepseek-ai/dsh-tools` compiler before the app
|
|
7
|
+
* ever boots, so a schema-DSL violation can no longer surface only as a
|
|
8
|
+
* boot-time `plugin tree failed to load` (see docs/LESSONS.md §15).
|
|
9
|
+
*
|
|
10
|
+
* DSL rules enforced by that compiler (dsh-tools lib/schema compiler):
|
|
11
|
+
* - every `type: 'object'` must declare `additionalProperties` as an explicit
|
|
12
|
+
* boolean — omitting it throws;
|
|
13
|
+
* - `required` is a per-PROPERTY marker and may only be present as `true`;
|
|
14
|
+
* omit it for optional properties (`required: false` is an error);
|
|
15
|
+
* - only these keys are allowed on a node: description/title/default/examples,
|
|
16
|
+
* `required` (property nodes only), plus `type`, and per type:
|
|
17
|
+
* object → properties/additionalProperties, array → items,
|
|
18
|
+
* scalar → enum/const, or the `oneOf` alternative (never with `type`).
|
|
19
|
+
*
|
|
20
|
+
* @module dsh-proxy-pro/tool-schemas
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** Output schema of `proxy_status` and `proxy_set` (the status snapshot). */
|
|
24
|
+
export const PROXY_STATUS_SCHEMA = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
properties: {
|
|
28
|
+
active: { type: 'boolean', required: true },
|
|
29
|
+
enabled: { type: 'boolean', required: true },
|
|
30
|
+
mode: { type: 'string', required: true },
|
|
31
|
+
url: { type: 'string', required: true },
|
|
32
|
+
noProxy: { type: 'string', required: true },
|
|
33
|
+
reason: { type: 'string', required: true },
|
|
34
|
+
source: { type: 'string', required: true },
|
|
35
|
+
at: { type: 'string', required: true },
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Parameters of `proxy_set`. */
|
|
40
|
+
export const PROXY_SET_PARAMETERS = {
|
|
41
|
+
enabled: {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
required: true,
|
|
44
|
+
description: 'true = route traffic through the configured proxy; false = go direct (no proxy).',
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Parameters of `proxy_test`. */
|
|
49
|
+
export const PROXY_TEST_PARAMETERS = {
|
|
50
|
+
url: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
required: true,
|
|
53
|
+
description: 'The URL to diagnose, e.g. https://github.com/foo/bar.',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Output schema of `proxy_test`. */
|
|
58
|
+
export const PROXY_TEST_SCHEMA = {
|
|
59
|
+
type: 'object',
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
properties: {
|
|
62
|
+
url: { type: 'string', required: true },
|
|
63
|
+
route: { type: 'string', required: true },
|
|
64
|
+
bypassed: { type: 'boolean', required: true },
|
|
65
|
+
probe: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
required: true,
|
|
69
|
+
properties: {
|
|
70
|
+
ok: { type: 'boolean', required: true },
|
|
71
|
+
status: { type: 'number' },
|
|
72
|
+
kind: { type: 'string' },
|
|
73
|
+
short: { type: 'string' },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
hint: { type: 'string' },
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parameters of `proxy_config` — every field optional: call with none to read
|
|
82
|
+
* the configuration, or with any subset to change it. Optional properties must
|
|
83
|
+
* OMIT `required` entirely (`required: false` is a DSL error, see §15).
|
|
84
|
+
*/
|
|
85
|
+
export const PROXY_CONFIG_PARAMETERS = {
|
|
86
|
+
enabled: {
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
description: 'true = route through the proxy; false = go direct. Omit to leave the switch unchanged.',
|
|
89
|
+
},
|
|
90
|
+
mode: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
enum: ['system', 'custom'],
|
|
93
|
+
description: 'system = follow the Windows system proxy; custom = use customUrl. Omit to leave the mode unchanged.',
|
|
94
|
+
},
|
|
95
|
+
customUrl: {
|
|
96
|
+
type: 'string',
|
|
97
|
+
description: 'Proxy address for mode "custom", e.g. http://127.0.0.1:7890 (bare host:port also accepted). Pass an empty string to clear it.',
|
|
98
|
+
},
|
|
99
|
+
noProxy: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'Comma-separated hosts/domains that always go DIRECT, e.g. "api.deepseek.com,huggingface.co". Pass an empty string to clear it.',
|
|
102
|
+
},
|
|
103
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yanglaofish/dsh-proxy-pro",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DSH proxy manager: route model requests, web fetches and spawned tools through the Windows system proxy or a custom address; per-URL route diagnosis, one-click toggle from the conversation header, a dedicated Settings page, and keepalive-aware 407 hints.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"lib",
|
|
15
|
+
"docs",
|
|
16
|
+
"cordis.patch.yml",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"deepseek-harness",
|
|
22
|
+
"dsh",
|
|
23
|
+
"dsh-plugin",
|
|
24
|
+
"cordis",
|
|
25
|
+
"proxy",
|
|
26
|
+
"network",
|
|
27
|
+
"undici"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/yanglaofish/dsh-proxy-pro.git"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/yanglaofish/dsh-proxy-pro",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/yanglaofish/dsh-proxy-pro/issues"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node --test test/*.mjs"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@deepseek-ai/cordis": "^4.0.0",
|
|
43
|
+
"@deepseek-ai/dsh-http-proxy": "^0.1.5-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
|
|
47
|
+
"@deepseek-ai/schemastery": "^3.18.0"
|
|
48
|
+
},
|
|
49
|
+
"dsh": {
|
|
50
|
+
"bundle": {
|
|
51
|
+
"patch": "./cordis.patch.yml"
|
|
52
|
+
},
|
|
53
|
+
"client": {
|
|
54
|
+
"platform": "web",
|
|
55
|
+
"inject": [
|
|
56
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-conversation"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|