@dcrays/dcgchat-test 0.6.12 → 0.6.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.
Files changed (60) hide show
  1. package/index.js +305 -0
  2. package/openclaw.plugin.json +41 -12
  3. package/package.json +8 -35
  4. package/README.md +0 -167
  5. package/index.ts +0 -35
  6. package/src/agent.ts +0 -568
  7. package/src/bot.ts +0 -646
  8. package/src/channel/index.ts +0 -329
  9. package/src/channel/outboundMedia.ts +0 -144
  10. package/src/channel/outboundTarget.ts +0 -62
  11. package/src/channel/uploadMediaUrl.ts +0 -76
  12. package/src/cron/message.ts +0 -114
  13. package/src/cron/params.ts +0 -164
  14. package/src/cron/toolGuard.ts +0 -466
  15. package/src/cron/types.ts +0 -15
  16. package/src/gateway/index.ts +0 -430
  17. package/src/gateway/security.ts +0 -95
  18. package/src/gateway/socket.ts +0 -256
  19. package/src/libs/ali-oss-6.23.0.tgz +0 -0
  20. package/src/libs/axios-1.13.6.tgz +0 -0
  21. package/src/libs/md5-2.3.0.tgz +0 -0
  22. package/src/libs/mime-types-3.0.2.tgz +0 -0
  23. package/src/libs/unzipper-0.12.3.tgz +0 -0
  24. package/src/libs/ws-8.19.0.tgz +0 -0
  25. package/src/monitor.ts +0 -269
  26. package/src/request/api.ts +0 -80
  27. package/src/request/oss.ts +0 -200
  28. package/src/request/request.ts +0 -191
  29. package/src/request/userInfo.ts +0 -44
  30. package/src/session.ts +0 -28
  31. package/src/skill.ts +0 -114
  32. package/src/tool.ts +0 -603
  33. package/src/tools/messageTool.ts +0 -334
  34. package/src/tools/toolCallGuard.ts +0 -327
  35. package/src/transport.ts +0 -217
  36. package/src/types.ts +0 -139
  37. package/src/utils/agentErrors.ts +0 -116
  38. package/src/utils/constant.ts +0 -60
  39. package/src/utils/env-config.ts +0 -19
  40. package/src/utils/formatLlmInputEvent.ts +0 -48
  41. package/src/utils/gatewayMsgHandler.ts +0 -147
  42. package/src/utils/global.ts +0 -309
  43. package/src/utils/inboundTurnState.ts +0 -66
  44. package/src/utils/log.ts +0 -77
  45. package/src/utils/mediaAttached.ts +0 -244
  46. package/src/utils/mediaEmitter.ts +0 -54
  47. package/src/utils/outboundAssistantText.ts +0 -117
  48. package/src/utils/params.ts +0 -104
  49. package/src/utils/passTxt.ts +0 -4
  50. package/src/utils/resolveRegisterConfig.ts +0 -33
  51. package/src/utils/searchFile.ts +0 -228
  52. package/src/utils/sessionState.ts +0 -137
  53. package/src/utils/sessionTermination.ts +0 -228
  54. package/src/utils/streamMerge.ts +0 -150
  55. package/src/utils/subagentRunMap.ts +0 -71
  56. package/src/utils/undiciFetchInterceptor.ts +0 -346
  57. package/src/utils/workspaceFilePaths.ts +0 -18
  58. package/src/utils/wsMessageHandler.ts +0 -124
  59. package/src/utils/zipExtract.ts +0 -97
  60. package/src/utils/zipPath.ts +0 -24
@@ -1,150 +0,0 @@
1
- import type { IMsgParams } from '../types.js'
2
- import { sendChunk } from '../transport.js'
3
- import { getWsConnection } from './global.js'
4
- import { isSessionStreamSuppressed } from './sessionTermination.js'
5
- import { getInboundGeneration, takeStreamChunkIdxAndAdvance } from './inboundTurnState.js'
6
-
7
- export const STREAM_MERGE_PROFILES = [
8
- { maxAvgDeltaGapMs: 20, flushIntervalMs: 50, deltaCount: 20, charCount: 100 },
9
- { maxAvgDeltaGapMs: 60, flushIntervalMs: 30, deltaCount: 15, charCount: 60 },
10
- { maxAvgDeltaGapMs: Number.POSITIVE_INFINITY, flushIntervalMs: 20, deltaCount: 6, charCount: 30 }
11
- ] as const
12
-
13
- export const STREAM_MERGE_ENABLE_WINDOW_MS = 1000
14
- export const STREAM_MERGE_ENABLE_DELTA_COUNT = 50
15
- export const STREAM_MERGE_IDLE_RESET_MS = 300
16
- export const STREAM_BACKPRESSURE_HIGH_WATERMARK = 512 * 1024
17
- export const STREAM_BACKPRESSURE_RETRY_MS = 50
18
-
19
- export interface StreamMergeController {
20
- queueDelta(delta: string): boolean
21
- flush(force?: boolean): boolean
22
- dispose(): void
23
- }
24
-
25
- export interface CreateStreamMergeControllerOptions {
26
- sessionKey: string
27
- inboundGenAtStart: number
28
- outboundCtx: IMsgParams
29
- }
30
-
31
- export function createStreamMergeController(
32
- options: CreateStreamMergeControllerOptions
33
- ): StreamMergeController {
34
- const { sessionKey, inboundGenAtStart, outboundCtx } = options
35
-
36
- let pendingText = ''
37
- let pendingDeltaCount = 0
38
- let timer: ReturnType<typeof setTimeout> | undefined
39
- let mergeMode = false
40
- let burstWindowStart = 0
41
- let burstDeltaCount = 0
42
- let lastDeltaAt = 0
43
- let deltaGapAvgMs = 0
44
-
45
- const clearTimer = () => {
46
- if (timer) {
47
- clearTimeout(timer)
48
- timer = undefined
49
- }
50
- }
51
-
52
- const isBackpressured = () => (getWsConnection()?.bufferedAmount ?? 0) > STREAM_BACKPRESSURE_HIGH_WATERMARK
53
-
54
- const currentProfile = () =>
55
- STREAM_MERGE_PROFILES.find((profile) => deltaGapAvgMs <= profile.maxAvgDeltaGapMs) ?? STREAM_MERGE_PROFILES[1]
56
-
57
- const isStale = () =>
58
- getInboundGeneration(sessionKey) !== inboundGenAtStart || isSessionStreamSuppressed(sessionKey)
59
-
60
- const emit = (text: string) => sendChunk(text, outboundCtx, takeStreamChunkIdxAndAdvance(sessionKey))
61
-
62
- const resetMerge = () => {
63
- mergeMode = false
64
- burstWindowStart = 0
65
- burstDeltaCount = 0
66
- deltaGapAvgMs = 0
67
- }
68
-
69
- const flush = (force = false): boolean => {
70
- clearTimer()
71
- if (!pendingText.trim()) {
72
- pendingText = ''
73
- pendingDeltaCount = 0
74
- return false
75
- }
76
- if (isStale()) {
77
- pendingText = ''
78
- pendingDeltaCount = 0
79
- return false
80
- }
81
- if (!force && isBackpressured()) {
82
- scheduleFlush(STREAM_BACKPRESSURE_RETRY_MS)
83
- return false
84
- }
85
- const textToSend = pendingText
86
- pendingText = ''
87
- pendingDeltaCount = 0
88
- return emit(textToSend)
89
- }
90
-
91
- const scheduleFlush = (delayMs = currentProfile().flushIntervalMs) => {
92
- if (timer) return
93
- timer = setTimeout(() => flush(), delayMs)
94
- }
95
-
96
- const queueDelta = (delta: string): boolean => {
97
- const now = Date.now()
98
- const deltaGapMs = lastDeltaAt > 0 ? now - lastDeltaAt : 0
99
-
100
- if (lastDeltaAt > 0 && deltaGapMs >= STREAM_MERGE_IDLE_RESET_MS) {
101
- flush(true)
102
- resetMerge()
103
- }
104
-
105
- if (deltaGapMs > 0) {
106
- deltaGapAvgMs = deltaGapAvgMs === 0 ? deltaGapMs : deltaGapAvgMs * 0.7 + deltaGapMs * 0.3
107
- }
108
- lastDeltaAt = now
109
-
110
- if (!mergeMode) {
111
- if (burstWindowStart === 0 || now - burstWindowStart > STREAM_MERGE_ENABLE_WINDOW_MS) {
112
- burstWindowStart = now
113
- burstDeltaCount = 1
114
- } else {
115
- burstDeltaCount += 1
116
- }
117
-
118
- pendingText += delta
119
- pendingDeltaCount += 1
120
- flush()
121
-
122
- if (burstDeltaCount >= STREAM_MERGE_ENABLE_DELTA_COUNT) {
123
- mergeMode = true
124
- }
125
- return true
126
- }
127
-
128
- pendingText += delta
129
- pendingDeltaCount += 1
130
- const profile = currentProfile()
131
- if (pendingText.length >= profile.charCount || pendingDeltaCount >= profile.deltaCount) {
132
- flush()
133
- return true
134
- }
135
- scheduleFlush()
136
- return true
137
- }
138
-
139
- const dispose = () => {
140
- clearTimer()
141
- pendingText = ''
142
- pendingDeltaCount = 0
143
- }
144
-
145
- return {
146
- queueDelta,
147
- flush,
148
- dispose
149
- }
150
- }
@@ -1,71 +0,0 @@
1
- const requesterSessionKeyByRunId = new Map<string, string>()
2
- const childSessionKeyByRunId = new Map<string, string>()
3
-
4
- export type SubagentRunMapSnapshot = {
5
- requesterSessionKeyByRunId?: Array<[string, string]>
6
- childSessionKeyByRunId?: Array<[string, string]>
7
- }
8
-
9
- export function setSubagentRunRequester(runId: string | undefined, requesterSessionKey: string | undefined): void {
10
- const rid = runId?.trim()
11
- const req = requesterSessionKey?.trim()
12
- if (!rid || !req) return
13
- requesterSessionKeyByRunId.set(rid, req)
14
- }
15
-
16
- export function setSubagentRunChild(runId: string | undefined, childSessionKey: string | undefined): void {
17
- const rid = runId?.trim()
18
- const child = childSessionKey?.trim()
19
- if (!rid || !child) return
20
- childSessionKeyByRunId.set(rid, child)
21
- }
22
-
23
- export function deleteSubagentRunMappings(runId: string | undefined): void {
24
- const rid = runId?.trim()
25
- if (!rid) return
26
- requesterSessionKeyByRunId.delete(rid)
27
- childSessionKeyByRunId.delete(rid)
28
- }
29
-
30
- export function clearSubagentRunMappings(): void {
31
- requesterSessionKeyByRunId.clear()
32
- childSessionKeyByRunId.clear()
33
- }
34
-
35
- export function resolveRequesterSessionKeyBySubagentRunId(runId: string | undefined): string | undefined {
36
- const rid = runId?.trim()
37
- if (!rid) return undefined
38
- return requesterSessionKeyByRunId.get(rid)
39
- }
40
-
41
- export function resolveChildSessionKeyBySubagentRunId(runId: string | undefined): string | undefined {
42
- const rid = runId?.trim()
43
- if (!rid) return undefined
44
- return childSessionKeyByRunId.get(rid)
45
- }
46
-
47
- export function cleanupSubagentRunMappingsForSessionKeys(sessionKeys: Set<string>): void {
48
- for (const [rid, child] of childSessionKeyByRunId.entries()) {
49
- if (sessionKeys.has(child)) deleteSubagentRunMappings(rid)
50
- }
51
- for (const [rid, requester] of requesterSessionKeyByRunId.entries()) {
52
- if (sessionKeys.has(requester)) deleteSubagentRunMappings(rid)
53
- }
54
- }
55
-
56
- export function snapshotSubagentRunMappings(): SubagentRunMapSnapshot {
57
- return {
58
- requesterSessionKeyByRunId: [...requesterSessionKeyByRunId.entries()],
59
- childSessionKeyByRunId: [...childSessionKeyByRunId.entries()]
60
- }
61
- }
62
-
63
- export function restoreSubagentRunMappings(snap: SubagentRunMapSnapshot | null | undefined): void {
64
- clearSubagentRunMappings()
65
- for (const [k, v] of snap?.requesterSessionKeyByRunId ?? []) {
66
- if (typeof k === 'string' && typeof v === 'string' && k && v) requesterSessionKeyByRunId.set(k, v)
67
- }
68
- for (const [k, v] of snap?.childSessionKeyByRunId ?? []) {
69
- if (typeof k === 'string' && typeof v === 'string' && k && v) childSessionKeyByRunId.set(k, v)
70
- }
71
- }
@@ -1,346 +0,0 @@
1
- import { realpathSync } from 'node:fs'
2
- import { createRequire } from 'node:module'
3
- import path from 'node:path'
4
- import { dcgLogger } from './log.js'
5
-
6
- const UNDICI_WRAPPED_KEY = Symbol.for('dcgchat.undici-fetch-wrapped')
7
- const GLOBAL_FETCH_WRAPPED_KEY = Symbol.for('dcgchat.global-fetch-wrapped')
8
- const ORIGINAL_UNDICI_FETCH_KEY = Symbol.for('dcgchat.original-undici-fetch')
9
- const ORIGINAL_GLOBAL_FETCH_KEY = Symbol.for('dcgchat.original-global-fetch')
10
- const UNDICI_RUNTIME_DEPS_KEY = '__OPENCLAW_TEST_UNDICI_RUNTIME_DEPS__'
11
- const PENDING_TTL_MS = 60_000
12
- const MAX_PENDING_QUEUE = 64
13
-
14
- type FetchLike = (input: unknown, init?: unknown) => Promise<Response>
15
- export type PendingLlmBodyExt = { sessionKey: string }
16
-
17
- type PendingEntry = PendingLlmBodyExt & { enqueuedAt: number }
18
-
19
- type UndiciLike = {
20
- fetch?: FetchLike
21
- Agent?: unknown
22
- EnvHttpProxyAgent?: unknown
23
- ProxyAgent?: unknown
24
- FormData?: unknown
25
- Headers?: unknown
26
- Request?: unknown
27
- Response?: unknown
28
- }
29
-
30
- type InterceptorGlobal = typeof globalThis & {
31
- [UNDICI_WRAPPED_KEY]?: boolean
32
- [GLOBAL_FETCH_WRAPPED_KEY]?: boolean
33
- [ORIGINAL_UNDICI_FETCH_KEY]?: FetchLike
34
- [ORIGINAL_GLOBAL_FETCH_KEY]?: typeof globalThis.fetch
35
- [UNDICI_RUNTIME_DEPS_KEY]?: Record<string, unknown>
36
- }
37
-
38
- const pendingLlmBodyExtQueue: PendingEntry[] = []
39
-
40
- function prunePendingQueue(now = Date.now()): void {
41
- while (pendingLlmBodyExtQueue.length > 0) {
42
- const head = pendingLlmBodyExtQueue[0]
43
- if (!head || now - head.enqueuedAt <= PENDING_TTL_MS) break
44
- pendingLlmBodyExtQueue.shift()
45
- }
46
- while (pendingLlmBodyExtQueue.length > MAX_PENDING_QUEUE) {
47
- pendingLlmBodyExtQueue.shift()
48
- }
49
- }
50
-
51
- function logDroppedPending(dropped: PendingEntry | undefined, reason: string): void {
52
- if (dropped) {
53
- dcgLogger(`llm body ext pending dropped (${reason}): sessionKey=${dropped.sessionKey}`)
54
- }
55
- }
56
-
57
- export function setPendingLlmBodyExt(pending: PendingLlmBodyExt | undefined): void {
58
- if (!pending) {
59
- pendingLlmBodyExtQueue.length = 0
60
- return
61
- }
62
- const sessionKey = pending.sessionKey.trim()
63
- if (!sessionKey) return
64
- prunePendingQueue()
65
- pendingLlmBodyExtQueue.push({ sessionKey, enqueuedAt: Date.now() })
66
- }
67
-
68
- function getFetchUrl(input: unknown): string {
69
- if (typeof input === 'string') return input
70
- if (input instanceof URL) return input.toString()
71
- if (input && typeof input === 'object' && 'url' in input) {
72
- const url = (input as { url?: unknown }).url
73
- if (typeof url === 'string') return url
74
- }
75
- return String(input)
76
- }
77
-
78
- function isLikelyLlmFetch(url: string): boolean {
79
- return (
80
- url.includes('/chat/completions') ||
81
- url.includes('/v1/chat') ||
82
- url.includes('/v1/completions') ||
83
- (url.includes('/completions') && !url.includes('/embeddings'))
84
- )
85
- }
86
-
87
- function looksLikeChatCompletionJsonBody(parsed: unknown): parsed is Record<string, unknown> {
88
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return false
89
- const record = parsed as Record<string, unknown>
90
- return Array.isArray(record.messages) || typeof record.prompt === 'string'
91
- }
92
-
93
- function isReadableStreamLike(body: unknown): boolean {
94
- return Boolean(body && typeof body === 'object' && typeof (body as { getReader?: unknown }).getReader === 'function')
95
- }
96
-
97
- function isNodeStreamLike(body: unknown): boolean {
98
- return Boolean(body && typeof body === 'object' && typeof (body as { pipe?: unknown }).pipe === 'function')
99
- }
100
-
101
- function isSafeBodyForTextRead(body: unknown): boolean {
102
- if (typeof body === 'string') return true
103
- if (typeof Blob !== 'undefined' && body instanceof Blob) return true
104
- if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true
105
- return false
106
- }
107
-
108
- async function readBodyAsText(body: unknown): Promise<string | null> {
109
- if (body == null) return null
110
- if (typeof body === 'string') return body
111
- if (isReadableStreamLike(body) || isNodeStreamLike(body) || !isSafeBodyForTextRead(body)) return null
112
- try {
113
- return await new Response(body as BodyInit).text()
114
- } catch {
115
- return null
116
- }
117
- }
118
-
119
- function isRequestLike(input: unknown): input is Request {
120
- return Boolean(input && typeof input === 'object' && typeof (input as Request).clone === 'function')
121
- }
122
-
123
- async function resolveRequestBodyText(input: unknown, init?: unknown): Promise<string | null> {
124
- const initObj = init && typeof init === 'object' ? (init as RequestInit) : undefined
125
- if (initObj?.body != null) {
126
- return readBodyAsText(initObj.body)
127
- }
128
- if (isRequestLike(input)) {
129
- try {
130
- return await input.clone().text()
131
- } catch {
132
- return null
133
- }
134
- }
135
- return null
136
- }
137
-
138
- function injectSessionKeyIntoBody(bodyText: string, sessionKey: string): string | null {
139
- try {
140
- const parsed: unknown = JSON.parse(bodyText)
141
- if (!looksLikeChatCompletionJsonBody(parsed)) return null
142
-
143
- const extBase =
144
- 'ext' in parsed && typeof parsed.ext === 'object' && parsed.ext !== null && !Array.isArray(parsed.ext)
145
- ? { ...(parsed.ext as Record<string, unknown>) }
146
- : {}
147
- parsed.ext = { ...extBase, sessionKey }
148
- return JSON.stringify(parsed)
149
- } catch {
150
- return null
151
- }
152
- }
153
-
154
- function headersWithoutContentLength(headers: HeadersInit | undefined): HeadersInit | undefined {
155
- if (!headers) return undefined
156
- const nextHeaders = new Headers(headers)
157
- nextHeaders.delete('content-length')
158
- return nextHeaders
159
- }
160
-
161
- function rebuildFetchArgs(input: unknown, init: unknown, body: string): { input: unknown; init?: unknown } {
162
- const initObj = init && typeof init === 'object' ? (init as RequestInit) : undefined
163
- if (initObj?.body != null) {
164
- return { input, init: { ...initObj, headers: headersWithoutContentLength(initObj.headers), body } }
165
- }
166
- if (isRequestLike(input)) {
167
- const nextInit: RequestInit = {
168
- method: input.method,
169
- headers: headersWithoutContentLength(input.headers),
170
- body,
171
- redirect: input.redirect,
172
- signal: input.signal,
173
- credentials: input.credentials,
174
- integrity: input.integrity,
175
- keepalive: input.keepalive,
176
- referrer: input.referrer,
177
- referrerPolicy: input.referrerPolicy,
178
- mode: input.mode,
179
- cache: input.cache
180
- }
181
- return { input: new Request(input.url, nextInit), init: initObj }
182
- }
183
- return { input, init: initObj ? { ...initObj, body } : { body } }
184
- }
185
-
186
- async function prepareFetchWithPendingLlmBodyExt(input: unknown, init?: unknown): Promise<{ input: unknown; init?: unknown }> {
187
- prunePendingQueue()
188
- if (pendingLlmBodyExtQueue.length === 0) return { input, init }
189
-
190
- const url = getFetchUrl(input)
191
- if (!isLikelyLlmFetch(url)) return { input, init }
192
-
193
- const pending = pendingLlmBodyExtQueue.shift()
194
- if (!pending) return { input, init }
195
-
196
- const bodyText = await resolveRequestBodyText(input, init)
197
- if (bodyText == null) {
198
- logDroppedPending(pending, 'unreadable body on LLM fetch')
199
- return { input, init }
200
- }
201
-
202
- const injectedBody = injectSessionKeyIntoBody(bodyText, pending.sessionKey)
203
- if (!injectedBody) {
204
- logDroppedPending(pending, 'body shape not injectable')
205
- return { input, init }
206
- }
207
-
208
- dcgLogger(`llm body ext injected: sessionKey=${pending.sessionKey}`)
209
- return rebuildFetchArgs(input, init, injectedBody)
210
- }
211
-
212
- function resolveUndici(): UndiciLike | null {
213
- const pluginRequire = createRequire(import.meta.url)
214
- const candidates: Array<{ label: string; load: () => UndiciLike }> = []
215
-
216
- if (process.argv[1]) {
217
- candidates.push({
218
- label: 'host-entry',
219
- load: () => {
220
- const hostEntry = realpathSync(path.resolve(process.argv[1] as string))
221
- return createRequire(hostEntry)('undici') as UndiciLike
222
- }
223
- })
224
- }
225
-
226
- candidates.push({
227
- label: 'openclaw-package',
228
- load: () => {
229
- const openclawPackageJson = pluginRequire.resolve('openclaw/package.json')
230
- return createRequire(openclawPackageJson)('undici') as UndiciLike
231
- }
232
- })
233
-
234
- candidates.push({
235
- label: 'plugin-local',
236
- load: () => pluginRequire('undici') as UndiciLike
237
- })
238
-
239
- for (const candidate of candidates) {
240
- try {
241
- const undici = candidate.load()
242
- if (typeof undici?.fetch === 'function') {
243
- dcgLogger(`undici resolved via ${candidate.label}`)
244
- return undici
245
- }
246
- } catch {
247
- // Try the next resolution root.
248
- }
249
- }
250
- return null
251
- }
252
-
253
- function copyFunctionShape(target: FetchLike, source: FetchLike): void {
254
- try {
255
- Object.assign(target, source)
256
- } catch {
257
- // Some runtimes expose non-writable function properties; the wrapper still works without copying them.
258
- }
259
- }
260
-
261
- function installGlobalFetchPatch(): boolean {
262
- const g = globalThis as InterceptorGlobal
263
- if (g[GLOBAL_FETCH_WRAPPED_KEY]) return true
264
- if (typeof globalThis.fetch !== 'function') {
265
- dcgLogger('global fetch body ext patch skipped: globalThis.fetch unavailable', 'error')
266
- return false
267
- }
268
-
269
- const originalFetch = globalThis.fetch.bind(globalThis)
270
- g[ORIGINAL_GLOBAL_FETCH_KEY] = globalThis.fetch
271
-
272
- const patchedFetch = async function patchedGlobalFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
273
- const prepared = await prepareFetchWithPendingLlmBodyExt(input, init)
274
- return originalFetch(prepared.input as RequestInfo | URL, prepared.init as RequestInit | undefined)
275
- }
276
- try {
277
- Object.assign(patchedFetch, globalThis.fetch)
278
- } catch {
279
- // The patch only needs call compatibility.
280
- }
281
-
282
- globalThis.fetch = patchedFetch as typeof globalThis.fetch
283
- g[GLOBAL_FETCH_WRAPPED_KEY] = true
284
- dcgLogger('global fetch body ext patch installed')
285
- return true
286
- }
287
-
288
- export function installUndiciFetchInterceptor(): boolean {
289
- const g = globalThis as InterceptorGlobal
290
- const globalFetchPatched = installGlobalFetchPatch()
291
- if (g[UNDICI_WRAPPED_KEY]) return true
292
-
293
- const undici = resolveUndici()
294
- if (!undici) {
295
- dcgLogger('undici.fetch intercept skipped: undici module unavailable', 'error')
296
- return globalFetchPatched
297
- }
298
-
299
- const originalFetch = undici.fetch
300
- if (typeof originalFetch !== 'function') {
301
- return globalFetchPatched
302
- }
303
-
304
- g[ORIGINAL_UNDICI_FETCH_KEY] = originalFetch
305
-
306
- let callId = 0
307
- const wrappedFetch: FetchLike = async function wrappedUndiciFetch(input: unknown, init?: unknown) {
308
- const url = getFetchUrl(input)
309
- const prepared = await prepareFetchWithPendingLlmBodyExt(input, init)
310
- if (!isLikelyLlmFetch(url)) {
311
- return originalFetch(prepared.input, prepared.init)
312
- }
313
-
314
- const id = ++callId
315
- const startedAt = Date.now()
316
- try {
317
- const response = await originalFetch(prepared.input, prepared.init)
318
- return response
319
- } catch (e) {
320
- dcgLogger(`[undici.fetch #${id}] LLM request failed duration=${Date.now() - startedAt}ms error=${String(e)}`, 'error')
321
- throw e
322
- }
323
- }
324
- copyFunctionShape(wrappedFetch, originalFetch)
325
-
326
- try {
327
- undici.fetch = wrappedFetch
328
- } catch (e) {
329
- dcgLogger(`undici.fetch direct patch failed, runtime deps injection will still be attempted: ${String(e)}`, 'error')
330
- }
331
-
332
- g[UNDICI_RUNTIME_DEPS_KEY] = {
333
- ...g[UNDICI_RUNTIME_DEPS_KEY],
334
- Agent: undici.Agent,
335
- EnvHttpProxyAgent: undici.EnvHttpProxyAgent,
336
- ProxyAgent: undici.ProxyAgent,
337
- FormData: undici.FormData,
338
- Headers: undici.Headers,
339
- Request: undici.Request,
340
- Response: undici.Response,
341
- fetch: wrappedFetch
342
- }
343
- g[UNDICI_WRAPPED_KEY] = true
344
- dcgLogger(`undici.fetch intercept installed via ${UNDICI_RUNTIME_DEPS_KEY}`)
345
- return true
346
- }
@@ -1,18 +0,0 @@
1
- import os from 'node:os'
2
- import path from 'node:path'
3
-
4
- /**
5
- * 将正文里的 `~/...` / `~\...` 展开为绝对路径(`path.resolve` 不会处理 `~`)。
6
- */
7
- export function expandTildePath(input: string): string {
8
- const s = input.trim()
9
- if (!s) return s
10
- if (s === '~') return os.homedir()
11
- if (s.startsWith('~/')) {
12
- return path.resolve(os.homedir(), s.slice(2))
13
- }
14
- if (s.startsWith('~\\')) {
15
- return path.resolve(os.homedir(), s.slice(2))
16
- }
17
- return s
18
- }