@klarkxy/dsh-plugin-kit 0.7.0 → 0.8.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/README.md +1 -1
- package/docs/README.zh-CN.md +1 -1
- package/lib/auxiliary-model.js +26 -2
- package/lib/auxiliary-model.js.map +1 -1
- package/lib/cli-media.js +1 -1
- package/lib/cli-process.d.ts +2 -0
- package/lib/cli-process.js +36 -2
- package/lib/cli-process.js.map +1 -1
- package/lib/client-logger.d.ts +19 -0
- package/lib/client-logger.js +153 -0
- package/lib/client-logger.js.map +1 -0
- package/lib/client-utils.d.ts +2 -1
- package/lib/client-utils.js +9 -3
- package/lib/client-utils.js.map +1 -1
- package/lib/domain.d.ts +6 -1
- package/lib/domain.js +26 -3
- package/lib/domain.js.map +1 -1
- package/lib/host-rpc.d.ts +2 -0
- package/lib/host-rpc.js +71 -2
- package/lib/host-rpc.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -1
- package/lib/llm-call.d.ts +3 -0
- package/lib/llm-call.js +79 -47
- package/lib/llm-call.js.map +1 -1
- package/lib/log-shared.d.ts +2 -0
- package/lib/log-shared.js +169 -0
- package/lib/log-shared.js.map +1 -0
- package/lib/log-shared2.d.ts +72 -0
- package/lib/logger.d.ts +87 -0
- package/lib/logger.js +572 -0
- package/lib/logger.js.map +1 -0
- package/package.json +17 -2
package/lib/llm-call.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-call.js","names":[],"sources":["../src/llm-call.ts"],"sourcesContent":["import {\n BlockAssembler, ReasoningEffortId, createUserMessage,\n type GenerateOptions, type LlmRuntime, type StreamChunk,\n} from '@deepseek-ai/dsh-llm'\nimport { getAuxiliaryModel, producerMessageSource, type ModelRoute } from './contracts.ts'\nimport { buildAuxiliaryUserText } from './auxiliary-context.ts'\n\n/** A feature's own saved model. Empty means the host default chat model. */\nexport interface FeatureModelRoute {\n readonly provider: string\n readonly model: string\n readonly reasoningEffort?: string\n}\n\nexport type LlmTextCaller = Pick<LlmRuntime, 'prepareCall' | 'resolveCallConfig'>\n\nexport interface LlmTextRequest {\n plugin: string\n /** Optional feature id, stamped on the context envelope for attribution. */\n feature?: string\n /**\n * Stable material rebuilt from current facts (session events, durable\n * records). Packed before the live text so provider-side caching reuses\n * the prefix; never model outputs or approval history. Strings or\n * pre-ordered segments.\n */\n context?: string | readonly string[]\n route: FeatureModelRoute\n system: string\n text: string\n /** Omit to use the selected model's normal output settings. */\n maxTokens?: number\n signal: AbortSignal\n /** The real session id for host attribution; never a feature-scoped alias. */\n sessionId?: string\n /** Auxiliary-call marker adapters may special-case (e.g. disable thinking for titles). */\n purpose?: GenerateOptions['purpose']\n /** Pure control selection using the exact model captured for this call. */\n configure?: Parameters<LlmRuntime['prepareCall']>[2]\n /** Rechecked before the call returns text. A false result discards the output. */\n isCurrent?: () => boolean\n}\n\nexport interface LlmTextResult {\n text: string\n provider: string\n model: string\n inputTokens?: number\n outputTokens?: number\n totalTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n reasoningTokens?: number\n}\n\ntype SessionModelsHost = {\n agents?: { get(sessionId: string): { session?: { requestHeader?: () => { config?: ModelRoute; adapterDefaults?: { reasoningEffort?: unknown } } | undefined } } | undefined }\n sessionProjections?: { stateOf(session: unknown, key: string): { pending?: ModelRoute | null } | undefined }\n agentDefaultModel?: { currentSelection(): ModelRoute | undefined }\n}\n\nfunction routeOf(value: { provider?: unknown; model?: unknown; reasoningEffort?: unknown } | undefined): FeatureModelRoute | undefined {\n if (typeof value?.provider !== 'string' || !value.provider.trim()) return undefined\n if (typeof value.model !== 'string' || !value.model.trim()) return undefined\n const effort = typeof value.reasoningEffort === 'string' ? value.reasoningEffort.trim() : ''\n return effort\n ? { provider: value.provider.trim(), model: value.model.trim(), reasoningEffort: effort }\n : { provider: value.provider.trim(), model: value.model.trim() }\n}\n\n/**\n * Plugin selection first; then the shared auxiliary default (when any\n * participant retains it); otherwise the live session model, then the host\n * default. Cordis callers must declare agents, sessionProjections and\n * agentDefaultModel in their inject list so the fallback reads use the\n * caller's live services.\n */\nexport function resolveFeatureModel(host: unknown, saved: FeatureModelRoute | undefined, sessionId?: string): FeatureModelRoute | undefined {\n const explicit = routeOf(saved)\n if (explicit) return explicit\n const auxiliary = getAuxiliaryModel(host)\n if (auxiliary) return auxiliary\n const face = host as SessionModelsHost | undefined\n if (sessionId && face?.agents && face.sessionProjections) {\n const session = face.agents.get(sessionId)?.session\n if (session) {\n try {\n const pending = face.sessionProjections.stateOf(session, 'modelSelection')?.pending\n const selected = routeOf(pending ?? undefined)\n if (selected) return selected\n } catch { /* fall through to the recorded route */ }\n const header = session.requestHeader?.()\n const recorded = header?.config\n if (recorded) {\n const selected = header?.adapterDefaults?.reasoningEffort === true\n ? { provider: recorded.provider, model: recorded.model }\n : recorded\n const route = routeOf(selected)\n if (route) return route\n }\n }\n }\n return routeOf(face?.agentDefaultModel?.currentSelection())\n}\n\nfunction joinText(assembler: BlockAssembler): string {\n return assembler.blocks()\n .filter((block): block is { type: 'text'; text: string } => block.type === 'text')\n .map(block => block.text)\n .join('')\n}\n\nasync function readStream(stream: AsyncIterable<StreamChunk>, signal: AbortSignal, live: () => boolean): Promise<BlockAssembler> {\n const assembler = new BlockAssembler()\n const iterator = stream[Symbol.asyncIterator]()\n try {\n while (live()) {\n if (signal.aborted) break\n const item = await iterator.next()\n if (item.done) break\n assembler.push(item.value)\n }\n } finally {\n // Settlement includes producer cleanup; never leave a detached return()\n // running after the caller has released its lifecycle ownership.\n await iterator.return?.()\n }\n return assembler\n}\n\n/** One native model call. No retry, timeout policy, or provider queue. */\nexport async function callLlmText(llm: LlmTextCaller, request: LlmTextRequest): Promise<LlmTextResult> {\n request.signal.throwIfAborted()\n const live = () => request.isCurrent?.() !== false && !request.signal.aborted\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n const effort = request.route.reasoningEffort?.trim()\n const prepared = await llm.prepareCall({\n provider: request.route.provider,\n model: request.route.model,\n ...(effort ? { reasoningEffort: ReasoningEffortId(effort) } : {}),\n ...(request.maxTokens !== undefined ? { maxTokens: request.maxTokens } : {}),\n }, request.signal, request.configure)\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n const maxTokens = request.maxTokens === undefined\n ? prepared.config.maxTokens\n : Math.min(request.maxTokens, prepared.config.maxTokens ?? request.maxTokens)\n const options: GenerateOptions = {\n provider: prepared.config.provider,\n model: prepared.config.model,\n ...(prepared.config.reasoningEffort ? { reasoningEffort: prepared.config.reasoningEffort } : {}),\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(prepared.config.temperature !== undefined ? { temperature: prepared.config.temperature } : {}),\n ...(prepared.config.stop ? { stop: [...prepared.config.stop] } : {}),\n system: request.system,\n messages: [createUserMessage({\n source: producerMessageSource(request.plugin),\n content: [{ type: 'text', text: buildAuxiliaryUserText(request) }],\n })],\n signal: request.signal,\n ...(request.purpose ? { purpose: request.purpose } : {}),\n ...(request.sessionId ? { sessionId: request.sessionId as GenerateOptions['sessionId'] } : {}),\n }\n const assembler = await readStream(prepared.stream(options), request.signal, live)\n if (request.signal.aborted || assembler.finish.kind === 'aborted') {\n throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n }\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n if (assembler.finish.kind === 'error') {\n const code = assembler.finish.failure.code\n throw Object.assign(new Error(assembler.finish.failure.message || '模型调用失败。'), { code })\n }\n if (assembler.blocks().some(block => block.type === 'tool-call') || assembler.finish.kind === 'tool-calls') {\n throw new Error('这次调用不使用工具。')\n }\n if (assembler.finish.kind !== 'stop') throw new Error('模型调用未完成。')\n // Every usable host usage field passes through; absent fields stay absent\n // (the caller's accounting must not see invented zeros).\n const usage = assembler.usage\n return {\n text: joinText(assembler),\n provider: prepared.config.provider,\n model: prepared.config.model,\n ...(usage ? {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n ...(usage.totalTokens !== undefined ? { totalTokens: usage.totalTokens } : {}),\n ...(usage.cacheReadTokens !== undefined ? { cacheReadTokens: usage.cacheReadTokens } : {}),\n ...(usage.cacheWriteTokens !== undefined ? { cacheWriteTokens: usage.cacheWriteTokens } : {}),\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n } : {}),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llm-call.js","names":[],"sources":["../src/llm-call.ts"],"sourcesContent":["import {\n BlockAssembler, ReasoningEffortId, createUserMessage,\n type GenerateOptions, type LlmRuntime, type StreamChunk,\n} from '@deepseek-ai/dsh-llm'\nimport { getAuxiliaryModel, producerMessageSource, type ModelRoute } from './contracts.ts'\nimport { buildAuxiliaryUserText } from './auxiliary-context.ts'\nimport type { PluginLogger } from './log-shared.ts'\n\n/** A feature's own saved model. Empty means the host default chat model. */\nexport interface FeatureModelRoute {\n readonly provider: string\n readonly model: string\n readonly reasoningEffort?: string\n}\n\nexport type LlmTextCaller = Pick<LlmRuntime, 'prepareCall' | 'resolveCallConfig'>\n\nexport interface LlmTextRequest {\n plugin: string\n /** Optional feature id, stamped on the context envelope for attribution. */\n feature?: string\n /**\n * Stable material rebuilt from current facts (session events, durable\n * records). Packed before the live text so provider-side caching reuses\n * the prefix; never model outputs or approval history. Strings or\n * pre-ordered segments.\n */\n context?: string | readonly string[]\n route: FeatureModelRoute\n system: string\n text: string\n /** Omit to use the selected model's normal output settings. */\n maxTokens?: number\n signal: AbortSignal\n /** The real session id for host attribution; never a feature-scoped alias. */\n sessionId?: string\n /** Auxiliary-call marker adapters may special-case (e.g. disable thinking for titles). */\n purpose?: GenerateOptions['purpose']\n /** Pure control selection using the exact model captured for this call. */\n configure?: Parameters<LlmRuntime['prepareCall']>[2]\n /** Rechecked before the call returns text. A false result discards the output. */\n isCurrent?: () => boolean\n /** Optional diagnostic logger for timing, token usage, and failure telemetry. */\n logger?: PluginLogger\n}\n\nexport interface LlmTextResult {\n text: string\n provider: string\n model: string\n inputTokens?: number\n outputTokens?: number\n totalTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n reasoningTokens?: number\n}\n\ntype SessionModelsHost = {\n agents?: { get(sessionId: string): { session?: { requestHeader?: () => { config?: ModelRoute; adapterDefaults?: { reasoningEffort?: unknown } } | undefined } } | undefined }\n sessionProjections?: { stateOf(session: unknown, key: string): { pending?: ModelRoute | null } | undefined }\n agentDefaultModel?: { currentSelection(): ModelRoute | undefined }\n}\n\nfunction routeOf(value: { provider?: unknown; model?: unknown; reasoningEffort?: unknown } | undefined): FeatureModelRoute | undefined {\n if (typeof value?.provider !== 'string' || !value.provider.trim()) return undefined\n if (typeof value.model !== 'string' || !value.model.trim()) return undefined\n const effort = typeof value.reasoningEffort === 'string' ? value.reasoningEffort.trim() : ''\n return effort\n ? { provider: value.provider.trim(), model: value.model.trim(), reasoningEffort: effort }\n : { provider: value.provider.trim(), model: value.model.trim() }\n}\n\n/**\n * Plugin selection first; then the shared auxiliary default (when any\n * participant retains it); otherwise the live session model, then the host\n * default. Cordis callers must declare agents, sessionProjections and\n * agentDefaultModel in their inject list so the fallback reads use the\n * caller's live services.\n */\nexport function resolveFeatureModel(host: unknown, saved: FeatureModelRoute | undefined, sessionId?: string): FeatureModelRoute | undefined {\n const explicit = routeOf(saved)\n if (explicit) return explicit\n const auxiliary = getAuxiliaryModel(host)\n if (auxiliary) return auxiliary\n const face = host as SessionModelsHost | undefined\n if (sessionId && face?.agents && face.sessionProjections) {\n const session = face.agents.get(sessionId)?.session\n if (session) {\n try {\n const pending = face.sessionProjections.stateOf(session, 'modelSelection')?.pending\n const selected = routeOf(pending ?? undefined)\n if (selected) return selected\n } catch { /* fall through to the recorded route */ }\n const header = session.requestHeader?.()\n const recorded = header?.config\n if (recorded) {\n const selected = header?.adapterDefaults?.reasoningEffort === true\n ? { provider: recorded.provider, model: recorded.model }\n : recorded\n const route = routeOf(selected)\n if (route) return route\n }\n }\n }\n return routeOf(face?.agentDefaultModel?.currentSelection())\n}\n\nfunction joinText(assembler: BlockAssembler): string {\n return assembler.blocks()\n .filter((block): block is { type: 'text'; text: string } => block.type === 'text')\n .map(block => block.text)\n .join('')\n}\n\nasync function readStream(stream: AsyncIterable<StreamChunk>, signal: AbortSignal, live: () => boolean): Promise<BlockAssembler> {\n const assembler = new BlockAssembler()\n const iterator = stream[Symbol.asyncIterator]()\n try {\n while (live()) {\n if (signal.aborted) break\n const item = await iterator.next()\n if (item.done) break\n assembler.push(item.value)\n }\n } finally {\n // Settlement includes producer cleanup; never leave a detached return()\n // running after the caller has released its lifecycle ownership.\n await iterator.return?.()\n }\n return assembler\n}\n\n/** One native model call. No retry, timeout policy, or provider queue. */\nexport async function callLlmText(llm: LlmTextCaller, request: LlmTextRequest): Promise<LlmTextResult> {\n request.signal.throwIfAborted()\n const live = () => request.isCurrent?.() !== false && !request.signal.aborted\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n const started = Date.now()\n const logBase = {\n plugin: request.plugin,\n feature: request.feature ?? null,\n provider: request.route.provider,\n model: request.route.model,\n purpose: request.purpose ?? null,\n sessionId: request.sessionId ?? null,\n }\n request.logger?.debug('llm.call.started', 'Auxiliary model call started.', logBase)\n try {\n const effort = request.route.reasoningEffort?.trim()\n const prepared = await llm.prepareCall({\n provider: request.route.provider,\n model: request.route.model,\n ...(effort ? { reasoningEffort: ReasoningEffortId(effort) } : {}),\n ...(request.maxTokens !== undefined ? { maxTokens: request.maxTokens } : {}),\n }, request.signal, request.configure)\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n const maxTokens = request.maxTokens === undefined\n ? prepared.config.maxTokens\n : Math.min(request.maxTokens, prepared.config.maxTokens ?? request.maxTokens)\n const options: GenerateOptions = {\n provider: prepared.config.provider,\n model: prepared.config.model,\n ...(prepared.config.reasoningEffort ? { reasoningEffort: prepared.config.reasoningEffort } : {}),\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(prepared.config.temperature !== undefined ? { temperature: prepared.config.temperature } : {}),\n ...(prepared.config.stop ? { stop: [...prepared.config.stop] } : {}),\n system: request.system,\n messages: [createUserMessage({\n source: producerMessageSource(request.plugin),\n content: [{ type: 'text', text: buildAuxiliaryUserText(request) }],\n })],\n signal: request.signal,\n ...(request.purpose ? { purpose: request.purpose } : {}),\n ...(request.sessionId ? { sessionId: request.sessionId as GenerateOptions['sessionId'] } : {}),\n }\n const assembler = await readStream(prepared.stream(options), request.signal, live)\n if (request.signal.aborted || assembler.finish.kind === 'aborted') {\n throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n }\n if (!live()) throw Object.assign(new Error('调用已取消。'), { name: 'AbortError' })\n if (assembler.finish.kind === 'error') {\n const code = assembler.finish.failure.code\n throw Object.assign(new Error(assembler.finish.failure.message || '模型调用失败。'), { code })\n }\n if (assembler.blocks().some(block => block.type === 'tool-call') || assembler.finish.kind === 'tool-calls') {\n throw new Error('这次调用不使用工具。')\n }\n if (assembler.finish.kind !== 'stop') throw new Error('模型调用未完成。')\n // Every usable host usage field passes through; absent fields stay absent\n // (the caller's accounting must not see invented zeros).\n const usage = assembler.usage\n request.logger?.debug('llm.call.completed', 'Auxiliary model call completed.', {\n ...logBase,\n provider: prepared.config.provider,\n model: prepared.config.model,\n durationMs: Date.now() - started,\n ...(usage?.inputTokens !== undefined ? { inputTokens: usage.inputTokens } : {}),\n ...(usage?.outputTokens !== undefined ? { outputTokens: usage.outputTokens } : {}),\n ...(usage?.totalTokens !== undefined ? { totalTokens: usage.totalTokens } : {}),\n })\n return {\n text: joinText(assembler),\n provider: prepared.config.provider,\n model: prepared.config.model,\n ...(usage ? {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n ...(usage.totalTokens !== undefined ? { totalTokens: usage.totalTokens } : {}),\n ...(usage.cacheReadTokens !== undefined ? { cacheReadTokens: usage.cacheReadTokens } : {}),\n ...(usage.cacheWriteTokens !== undefined ? { cacheWriteTokens: usage.cacheWriteTokens } : {}),\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n } : {}),\n }\n } catch (error) {\n const durationMs = Date.now() - started\n const aborted = request.signal.aborted || (error instanceof Error && error.name === 'AbortError')\n if (aborted) {\n request.logger?.debug('llm.call.cancelled', 'Auxiliary model call cancelled.', { ...logBase, durationMs })\n } else {\n request.logger?.warn('llm.call.failed', 'Auxiliary model call failed.', error, { ...logBase, durationMs })\n }\n throw error\n }\n}\n"],"mappings":";;;;AAgEA,SAAS,QAAQ,OAAsH;CACrI,IAAI,OAAO,OAAO,aAAa,YAAY,CAAC,MAAM,SAAS,KAAK,GAAG,OAAO,KAAA;CAC1E,IAAI,OAAO,MAAM,UAAU,YAAY,CAAC,MAAM,MAAM,KAAK,GAAG,OAAO,KAAA;CACnE,MAAM,SAAS,OAAO,MAAM,oBAAoB,WAAW,MAAM,gBAAgB,KAAK,IAAI;CAC1F,OAAO,SACH;EAAE,UAAU,MAAM,SAAS,KAAK;EAAG,OAAO,MAAM,MAAM,KAAK;EAAG,iBAAiB;CAAO,IACtF;EAAE,UAAU,MAAM,SAAS,KAAK;EAAG,OAAO,MAAM,MAAM,KAAK;CAAE;AACnE;;;;;;;;AASA,SAAgB,oBAAoB,MAAe,OAAsC,WAAmD;CAC1I,MAAM,WAAW,QAAQ,KAAK;CAC9B,IAAI,UAAU,OAAO;CACrB,MAAM,YAAY,kBAAkB,IAAI;CACxC,IAAI,WAAW,OAAO;CACtB,MAAM,OAAO;CACb,IAAI,aAAa,MAAM,UAAU,KAAK,oBAAoB;EACxD,MAAM,UAAU,KAAK,OAAO,IAAI,SAAS,CAAC,EAAE;EAC5C,IAAI,SAAS;GACX,IAAI;IACF,MAAM,UAAU,KAAK,mBAAmB,QAAQ,SAAS,gBAAgB,CAAC,EAAE;IAC5E,MAAM,WAAW,QAAQ,WAAW,KAAA,CAAS;IAC7C,IAAI,UAAU,OAAO;GACvB,QAAQ,CAA2C;GACnD,MAAM,SAAS,QAAQ,gBAAgB;GACvC,MAAM,WAAW,QAAQ;GACzB,IAAI,UAAU;IAIZ,MAAM,QAAQ,QAHG,QAAQ,iBAAiB,oBAAoB,OAC1D;KAAE,UAAU,SAAS;KAAU,OAAO,SAAS;IAAM,IACrD,QAC0B;IAC9B,IAAI,OAAO,OAAO;GACpB;EACF;CACF;CACA,OAAO,QAAQ,MAAM,mBAAmB,iBAAiB,CAAC;AAC5D;AAEA,SAAS,SAAS,WAAmC;CACnD,OAAO,UAAU,OAAO,CAAC,CACtB,QAAQ,UAAmD,MAAM,SAAS,MAAM,CAAC,CACjF,KAAI,UAAS,MAAM,IAAI,CAAC,CACxB,KAAK,EAAE;AACZ;AAEA,eAAe,WAAW,QAAoC,QAAqB,MAA8C;CAC/H,MAAM,YAAY,IAAI,eAAe;CACrC,MAAM,WAAW,OAAO,OAAO,cAAc,CAAC;CAC9C,IAAI;EACF,OAAO,KAAK,GAAG;GACb,IAAI,OAAO,SAAS;GACpB,MAAM,OAAO,MAAM,SAAS,KAAK;GACjC,IAAI,KAAK,MAAM;GACf,UAAU,KAAK,KAAK,KAAK;EAC3B;CACF,UAAU;EAGR,MAAM,SAAS,SAAS;CAC1B;CACA,OAAO;AACT;;AAGA,eAAsB,YAAY,KAAoB,SAAiD;CACrG,QAAQ,OAAO,eAAe;CAC9B,MAAM,aAAa,QAAQ,YAAY,MAAM,SAAS,CAAC,QAAQ,OAAO;CACtE,IAAI,CAAC,KAAK,GAAG,MAAM,OAAO,uBAAO,IAAI,MAAM,QAAQ,GAAG,EAAE,MAAM,aAAa,CAAC;CAC5E,MAAM,UAAU,KAAK,IAAI;CACzB,MAAM,UAAU;EACd,QAAQ,QAAQ;EAChB,SAAS,QAAQ,WAAW;EAC5B,UAAU,QAAQ,MAAM;EACxB,OAAO,QAAQ,MAAM;EACrB,SAAS,QAAQ,WAAW;EAC5B,WAAW,QAAQ,aAAa;CAClC;CACA,QAAQ,QAAQ,MAAM,oBAAoB,iCAAiC,OAAO;CAClF,IAAI;EACJ,MAAM,SAAS,QAAQ,MAAM,iBAAiB,KAAK;EACnD,MAAM,WAAW,MAAM,IAAI,YAAY;GACrC,UAAU,QAAQ,MAAM;GACxB,OAAO,QAAQ,MAAM;GACrB,GAAI,SAAS,EAAE,iBAAiB,kBAAkB,MAAM,EAAE,IAAI,CAAC;GAC/D,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;EAC5E,GAAG,QAAQ,QAAQ,QAAQ,SAAS;EACpC,IAAI,CAAC,KAAK,GAAG,MAAM,OAAO,uBAAO,IAAI,MAAM,QAAQ,GAAG,EAAE,MAAM,aAAa,CAAC;EAC5E,MAAM,YAAY,QAAQ,cAAc,KAAA,IACpC,SAAS,OAAO,YAChB,KAAK,IAAI,QAAQ,WAAW,SAAS,OAAO,aAAa,QAAQ,SAAS;EAC9E,MAAM,UAA2B;GAC/B,UAAU,SAAS,OAAO;GAC1B,OAAO,SAAS,OAAO;GACvB,GAAI,SAAS,OAAO,kBAAkB,EAAE,iBAAiB,SAAS,OAAO,gBAAgB,IAAI,CAAC;GAC9F,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;GAC/C,GAAI,SAAS,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,SAAS,OAAO,YAAY,IAAI,CAAC;GAChG,GAAI,SAAS,OAAO,OAAO,EAAE,MAAM,CAAC,GAAG,SAAS,OAAO,IAAI,EAAE,IAAI,CAAC;GAClE,QAAQ,QAAQ;GAChB,UAAU,CAAC,kBAAkB;IAC3B,QAAQ,sBAAsB,QAAQ,MAAM;IAC5C,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,uBAAuB,OAAO;IAAE,CAAC;GACnE,CAAC,CAAC;GACF,QAAQ,QAAQ;GAChB,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;GACtD,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAA0C,IAAI,CAAC;EAC9F;EACA,MAAM,YAAY,MAAM,WAAW,SAAS,OAAO,OAAO,GAAG,QAAQ,QAAQ,IAAI;EACjF,IAAI,QAAQ,OAAO,WAAW,UAAU,OAAO,SAAS,WACtD,MAAM,OAAO,uBAAO,IAAI,MAAM,QAAQ,GAAG,EAAE,MAAM,aAAa,CAAC;EAEjE,IAAI,CAAC,KAAK,GAAG,MAAM,OAAO,uBAAO,IAAI,MAAM,QAAQ,GAAG,EAAE,MAAM,aAAa,CAAC;EAC5E,IAAI,UAAU,OAAO,SAAS,SAAS;GACrC,MAAM,OAAO,UAAU,OAAO,QAAQ;GACtC,MAAM,OAAO,OAAO,IAAI,MAAM,UAAU,OAAO,QAAQ,WAAW,SAAS,GAAG,EAAE,KAAK,CAAC;EACxF;EACA,IAAI,UAAU,OAAO,CAAC,CAAC,MAAK,UAAS,MAAM,SAAS,WAAW,KAAK,UAAU,OAAO,SAAS,cAC5F,MAAM,IAAI,MAAM,YAAY;EAE9B,IAAI,UAAU,OAAO,SAAS,QAAQ,MAAM,IAAI,MAAM,UAAU;EAGhE,MAAM,QAAQ,UAAU;EACxB,QAAQ,QAAQ,MAAM,sBAAsB,mCAAmC;GAC7E,GAAG;GACH,UAAU,SAAS,OAAO;GAC1B,OAAO,SAAS,OAAO;GACvB,YAAY,KAAK,IAAI,IAAI;GACzB,GAAI,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;GAC7E,GAAI,OAAO,iBAAiB,KAAA,IAAY,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;GAChF,GAAI,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC/E,CAAC;EACD,OAAO;GACL,MAAM,SAAS,SAAS;GACxB,UAAU,SAAS,OAAO;GAC1B,OAAO,SAAS,OAAO;GACvB,GAAI,QAAQ;IACV,aAAa,MAAM;IACnB,cAAc,MAAM;IACpB,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;IAC5E,GAAI,MAAM,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;IACxF,GAAI,MAAM,qBAAqB,KAAA,IAAY,EAAE,kBAAkB,MAAM,iBAAiB,IAAI,CAAC;IAC3F,GAAI,MAAM,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;GAC1F,IAAI,CAAC;EACP;CACA,SAAS,OAAO;EACd,MAAM,aAAa,KAAK,IAAI,IAAI;EAEhC,IADgB,QAAQ,OAAO,WAAY,iBAAiB,SAAS,MAAM,SAAS,cAElF,QAAQ,QAAQ,MAAM,sBAAsB,mCAAmC;GAAE,GAAG;GAAS;EAAW,CAAC;OAEzG,QAAQ,QAAQ,KAAK,mBAAmB,gCAAgC,OAAO;GAAE,GAAG;GAAS;EAAW,CAAC;EAE3G,MAAM;CACR;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as truncateLogText, S as sanitizePluginLogText, _ as normalizePluginLogSource, a as PluginLogContext, b as sanitizePluginLogContext, c as PluginLogInput, d as PluginLogger, f as buildPluginLogEntry, g as normalizePluginLogEvent, h as mergePluginLogContext, i as PLUGIN_LOG_MAX_MESSAGE_CHARS, l as PluginLogLevel, m as isPluginLogLevel, n as PLUGIN_LOG_LEVELS, o as PluginLogEntry, p as createChildPluginLogger, r as PLUGIN_LOG_MAX_CONTEXT_CHARS, s as PluginLogError, t as DEFAULT_PLUGIN_LOG_LEVEL, u as PluginLogScalar, v as parsePluginLogLevel, x as sanitizePluginLogError, y as pluginLogLevelEnabled } from "./log-shared2.js";
|
|
2
|
+
export { DEFAULT_PLUGIN_LOG_LEVEL, PLUGIN_LOG_LEVELS, PLUGIN_LOG_MAX_CONTEXT_CHARS, PLUGIN_LOG_MAX_MESSAGE_CHARS, PluginLogContext, PluginLogEntry, PluginLogError, PluginLogInput, PluginLogLevel, PluginLogScalar, PluginLogger, buildPluginLogEntry, createChildPluginLogger, isPluginLogLevel, mergePluginLogContext, normalizePluginLogEvent, normalizePluginLogSource, parsePluginLogLevel, pluginLogLevelEnabled, sanitizePluginLogContext, sanitizePluginLogError, sanitizePluginLogText, truncateLogText };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
//#region src/log-shared.ts
|
|
2
|
+
const PLUGIN_LOG_LEVELS = [
|
|
3
|
+
"debug",
|
|
4
|
+
"info",
|
|
5
|
+
"warn",
|
|
6
|
+
"error",
|
|
7
|
+
"fault"
|
|
8
|
+
];
|
|
9
|
+
const DEFAULT_PLUGIN_LOG_LEVEL = "info";
|
|
10
|
+
const PLUGIN_LOG_MAX_MESSAGE_CHARS = 16e3;
|
|
11
|
+
const PLUGIN_LOG_MAX_CONTEXT_CHARS = 8e3;
|
|
12
|
+
const SOURCE_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
13
|
+
const EVENT_RE = /^[a-z][a-z0-9._-]{0,127}$/;
|
|
14
|
+
const CONTEXT_KEY_RE = /^[a-zA-Z][a-zA-Z0-9._-]{0,63}$/;
|
|
15
|
+
const SECRET_KEY_RE = /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|bearer|authorization|cookie|set[_-]?cookie|nonce|credential|private[_-]?key|password|secret|app[_-]?secret|client[_-]?secret|z_c0|d_c0)$/i;
|
|
16
|
+
const CHAT_PAYLOAD_RE = /"role"\s*:\s*"(?:system|user|assistant|tool)"|"(?:messages|content|prompt|input_text|input_image|image_url)"\s*:\s*[\[{"']/i;
|
|
17
|
+
const CREDENTIAL_BODY_RE = /-----BEGIN (?:RSA |EC |OPENSSH |PRIVATE )?PRIVATE KEY-----|"type"\s*:\s*"service_account"|credentials\.json|\.dsh[/\\](?:credentials|auth)/i;
|
|
18
|
+
const AUTH_HEADER_RE = /\b(Authorization|Proxy-Authorization|Cookie|Set-Cookie)\s*:\s*[^\r\n]*/gi;
|
|
19
|
+
const BEARER_RE = /\bBearer\s+[A-Za-z0-9._\-+=/]{8,}/g;
|
|
20
|
+
const SECRET_FIELD_RE = /(["']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|bearer|authorization|cookie|set-cookie|nonce|credential|private[_-]?key|password|secret|app[_-]?secret|client[_-]?secret|password|z_c0|d_c0)["']?\s*[:=]\s*)(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,&;}]+)/gi;
|
|
21
|
+
const SECRET_QUERY_RE = /([?&](?:token|api[_-]?key|key|secret|password|bootstrap|code|sig|signature|credential|password)=)[^\s&#"']+/gi;
|
|
22
|
+
const URL_CREDENTIAL_RE = /\b(https?:\/\/)[^\s/:@]+:[^\s/@]+@/gi;
|
|
23
|
+
const BOOTSTRAP_RE = /\/bootstrap\/[^\s"'?&#]+/gi;
|
|
24
|
+
const KNOWN_TOKEN_RE = /\b(?:sk-[A-Za-z0-9_-]{10,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{20,}|tvly-[A-Za-z0-9_-]{10,}|AKIA[0-9A-Z]{16})\b/g;
|
|
25
|
+
const PATH_RE = /(?:[A-Za-z]:\\|\\\\)[^\s"'`<>|]+|(?:\/(?:home|Users|tmp|var|root)\/|~\/)[^\s"'`<>|]+/g;
|
|
26
|
+
function isPluginLogLevel(value) {
|
|
27
|
+
return typeof value === "string" && PLUGIN_LOG_LEVELS.includes(value);
|
|
28
|
+
}
|
|
29
|
+
function pluginLogLevelEnabled(level, minimum) {
|
|
30
|
+
return PLUGIN_LOG_LEVELS.indexOf(level) >= PLUGIN_LOG_LEVELS.indexOf(minimum);
|
|
31
|
+
}
|
|
32
|
+
function normalizePluginLogSource(raw) {
|
|
33
|
+
const stripped = String(raw || "").trim().replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
34
|
+
const bounded = (/^[a-z]/.test(stripped) ? stripped : `p-${stripped}`).slice(0, 64).replace(/-+$/g, "");
|
|
35
|
+
return SOURCE_RE.test(bounded) ? bounded : "dsh-plugin";
|
|
36
|
+
}
|
|
37
|
+
function normalizePluginLogEvent(raw) {
|
|
38
|
+
const cleaned = String(raw || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z]+/, "").slice(0, 128);
|
|
39
|
+
return EVENT_RE.test(cleaned) ? cleaned : "plugin.event";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse a level specification like `debug` or `dsh-zhihu=debug,dsh-dot:warn,info`.
|
|
43
|
+
* Source-specific rules win over a bare fallback level.
|
|
44
|
+
*/
|
|
45
|
+
function parsePluginLogLevel(spec, source, fallback = DEFAULT_PLUGIN_LOG_LEVEL) {
|
|
46
|
+
if (!spec || typeof spec !== "string") return fallback;
|
|
47
|
+
const normalizedSource = source ? normalizePluginLogSource(source) : void 0;
|
|
48
|
+
let wildcard;
|
|
49
|
+
for (const token of spec.split(",").map((part) => part.trim()).filter(Boolean)) {
|
|
50
|
+
const lowered = token.toLowerCase();
|
|
51
|
+
if (isPluginLogLevel(lowered)) {
|
|
52
|
+
wildcard = lowered;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const match = /^([^=:]+)[=:]([a-z]+)$/i.exec(token);
|
|
56
|
+
if (!match) continue;
|
|
57
|
+
const target = match[1].trim();
|
|
58
|
+
const candidate = match[2].trim().toLowerCase();
|
|
59
|
+
if (!isPluginLogLevel(candidate)) continue;
|
|
60
|
+
if (target === "*") {
|
|
61
|
+
wildcard = candidate;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (normalizedSource && normalizePluginLogSource(target) === normalizedSource) return candidate;
|
|
65
|
+
}
|
|
66
|
+
return wildcard ?? fallback;
|
|
67
|
+
}
|
|
68
|
+
function sanitizePluginLogText(text, maxChars = PLUGIN_LOG_MAX_MESSAGE_CHARS) {
|
|
69
|
+
if (!text) return "";
|
|
70
|
+
if (CREDENTIAL_BODY_RE.test(text)) return "[omitted credential material]";
|
|
71
|
+
if (CHAT_PAYLOAD_RE.test(text)) return "[omitted chat payload]";
|
|
72
|
+
return truncateLogText(text.replace(AUTH_HEADER_RE, "$1: [redacted]").replace(BEARER_RE, "Bearer [redacted]").replace(SECRET_FIELD_RE, (full, prefix) => {
|
|
73
|
+
const value = full.slice(prefix.length);
|
|
74
|
+
if (value.startsWith("\"")) return `${prefix}"[redacted]"`;
|
|
75
|
+
if (value.startsWith("'")) return `${prefix}'[redacted]'`;
|
|
76
|
+
return `${prefix}[redacted]`;
|
|
77
|
+
}).replace(SECRET_QUERY_RE, "$1[redacted]").replace(URL_CREDENTIAL_RE, "$1[redacted]@").replace(BOOTSTRAP_RE, "/bootstrap/[redacted]").replace(KNOWN_TOKEN_RE, "[redacted]").replace(PATH_RE, "[path]"), maxChars);
|
|
78
|
+
}
|
|
79
|
+
function sanitizePluginLogContext(input) {
|
|
80
|
+
if (!input) return void 0;
|
|
81
|
+
const output = {};
|
|
82
|
+
for (const [key, value] of Object.entries(input).slice(0, 32)) {
|
|
83
|
+
if (value === void 0 || !CONTEXT_KEY_RE.test(key)) continue;
|
|
84
|
+
if (typeof value === "number" && !Number.isFinite(value)) continue;
|
|
85
|
+
if (SECRET_KEY_RE.test(key)) {
|
|
86
|
+
output[key] = "[redacted]";
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
output[key] = typeof value === "string" ? sanitizePluginLogText(value, 1e3) : value;
|
|
90
|
+
}
|
|
91
|
+
if (!Object.keys(output).length) return void 0;
|
|
92
|
+
if (JSON.stringify(output).length <= 8e3) return output;
|
|
93
|
+
return {
|
|
94
|
+
omitted: true,
|
|
95
|
+
reason: "Diagnostic context exceeded its size limit."
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function sanitizePluginLogError(error) {
|
|
99
|
+
if (error === void 0) return void 0;
|
|
100
|
+
const row = error && typeof error === "object" ? error : {};
|
|
101
|
+
const rawMessage = typeof row.message === "string" ? row.message : String(error);
|
|
102
|
+
const result = {
|
|
103
|
+
name: typeof row.name === "string" && row.name.trim() ? truncateLogText(row.name.trim(), 100) : "Error",
|
|
104
|
+
message: sanitizePluginLogText(rawMessage) || "Unknown error"
|
|
105
|
+
};
|
|
106
|
+
if (typeof row.stack === "string" && row.stack.trim()) result.stack = sanitizePluginLogText(row.stack, 24e3);
|
|
107
|
+
if (typeof row.code === "string" && /^[A-Za-z0-9_./:-]{1,100}$/.test(row.code)) result.code = row.code;
|
|
108
|
+
else if (typeof row.code === "number" && Number.isFinite(row.code)) result.code = String(row.code);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function mergePluginLogContext(base, extra) {
|
|
112
|
+
if (!base) return extra;
|
|
113
|
+
if (!extra) return base;
|
|
114
|
+
return {
|
|
115
|
+
...base,
|
|
116
|
+
...extra
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function buildPluginLogEntry(input, meta) {
|
|
120
|
+
const entry = {
|
|
121
|
+
version: 1,
|
|
122
|
+
at: meta.at.toISOString(),
|
|
123
|
+
level: isPluginLogLevel(input.level) ? input.level : "info",
|
|
124
|
+
source: normalizePluginLogSource(meta.source),
|
|
125
|
+
surface: meta.surface,
|
|
126
|
+
...meta.profile ? { profile: meta.profile } : {},
|
|
127
|
+
event: normalizePluginLogEvent(input.event),
|
|
128
|
+
message: sanitizePluginLogText(input.message || "No diagnostic message was provided.", PLUGIN_LOG_MAX_MESSAGE_CHARS),
|
|
129
|
+
...meta.pid !== void 0 ? { pid: meta.pid } : {}
|
|
130
|
+
};
|
|
131
|
+
const context = sanitizePluginLogContext(mergePluginLogContext(meta.correlation, input.context));
|
|
132
|
+
if (context) entry.context = context;
|
|
133
|
+
const error = sanitizePluginLogError(input.error);
|
|
134
|
+
if (error) entry.error = error;
|
|
135
|
+
return entry;
|
|
136
|
+
}
|
|
137
|
+
function createChildPluginLogger(parent, bound) {
|
|
138
|
+
return {
|
|
139
|
+
get source() {
|
|
140
|
+
return parent.source;
|
|
141
|
+
},
|
|
142
|
+
get file() {
|
|
143
|
+
return parent.file;
|
|
144
|
+
},
|
|
145
|
+
get lastWriteError() {
|
|
146
|
+
return parent.lastWriteError;
|
|
147
|
+
},
|
|
148
|
+
debug: (event, message, context) => parent.debug(event, message, mergePluginLogContext(bound, context)),
|
|
149
|
+
info: (event, message, context) => parent.info(event, message, mergePluginLogContext(bound, context)),
|
|
150
|
+
warn: (event, message, error, context) => parent.warn(event, message, error, mergePluginLogContext(bound, context)),
|
|
151
|
+
error: (event, message, error, context) => parent.error(event, message, error, mergePluginLogContext(bound, context)),
|
|
152
|
+
fault: (event, message, error, context) => parent.fault(event, message, error, mergePluginLogContext(bound, context)),
|
|
153
|
+
write: (input) => parent.write({
|
|
154
|
+
...input,
|
|
155
|
+
context: mergePluginLogContext(bound, input.context)
|
|
156
|
+
}),
|
|
157
|
+
child: (next) => createChildPluginLogger(parent, mergePluginLogContext(bound, next) ?? {})
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function truncateLogText(text, maxChars) {
|
|
161
|
+
if (text.length <= maxChars) return text;
|
|
162
|
+
const marker = "[truncated]\n";
|
|
163
|
+
if (maxChars <= 12) return text.slice(0, Math.max(0, maxChars));
|
|
164
|
+
return `${marker}${text.slice(-(maxChars - 12))}`;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { DEFAULT_PLUGIN_LOG_LEVEL, PLUGIN_LOG_LEVELS, PLUGIN_LOG_MAX_CONTEXT_CHARS, PLUGIN_LOG_MAX_MESSAGE_CHARS, buildPluginLogEntry, createChildPluginLogger, isPluginLogLevel, mergePluginLogContext, normalizePluginLogEvent, normalizePluginLogSource, parsePluginLogLevel, pluginLogLevelEnabled, sanitizePluginLogContext, sanitizePluginLogError, sanitizePluginLogText, truncateLogText };
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=log-shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-shared.js","names":[],"sources":["../src/log-shared.ts"],"sourcesContent":["export type PluginLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fault'\n\nexport const PLUGIN_LOG_LEVELS: readonly PluginLogLevel[] = ['debug', 'info', 'warn', 'error', 'fault']\nexport const DEFAULT_PLUGIN_LOG_LEVEL: PluginLogLevel = 'info'\nexport const PLUGIN_LOG_MAX_MESSAGE_CHARS = 16_000\nexport const PLUGIN_LOG_MAX_CONTEXT_CHARS = 8_000\n\nexport type PluginLogScalar = string | number | boolean | null\nexport type PluginLogContext = Record<string, PluginLogScalar | undefined>\n\nexport interface PluginLogInput {\n level: PluginLogLevel\n event: string\n message: string\n context?: PluginLogContext\n error?: unknown\n}\n\nexport interface PluginLogError {\n name: string\n message: string\n stack?: string\n code?: string\n}\n\nexport interface PluginLogEntry {\n version: 1\n at: string\n level: PluginLogLevel\n source: string\n surface: 'host' | 'client'\n profile?: string\n event: string\n message: string\n pid?: number\n context?: Record<string, PluginLogScalar>\n error?: PluginLogError\n}\n\nexport interface PluginLogger {\n readonly source: string\n readonly file: string | null\n readonly lastWriteError: string | null\n debug(event: string, message: string, context?: PluginLogContext): boolean\n info(event: string, message: string, context?: PluginLogContext): boolean\n warn(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean\n error(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean\n fault(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean\n write(input: PluginLogInput): boolean\n child(context: PluginLogContext): PluginLogger\n}\n\nconst SOURCE_RE = /^[a-z][a-z0-9-]{0,63}$/\nconst EVENT_RE = /^[a-z][a-z0-9._-]{0,127}$/\nconst CONTEXT_KEY_RE = /^[a-zA-Z][a-zA-Z0-9._-]{0,63}$/\nconst SECRET_KEY_RE = /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|bearer|authorization|cookie|set[_-]?cookie|nonce|credential|private[_-]?key|password|secret|app[_-]?secret|client[_-]?secret|z_c0|d_c0)$/i\n\nconst CHAT_PAYLOAD_RE =\n /\"role\"\\s*:\\s*\"(?:system|user|assistant|tool)\"|\"(?:messages|content|prompt|input_text|input_image|image_url)\"\\s*:\\s*[\\[{\"']/i\nconst CREDENTIAL_BODY_RE =\n /-----BEGIN (?:RSA |EC |OPENSSH |PRIVATE )?PRIVATE KEY-----|\"type\"\\s*:\\s*\"service_account\"|credentials\\.json|\\.dsh[/\\\\](?:credentials|auth)/i\nconst AUTH_HEADER_RE = /\\b(Authorization|Proxy-Authorization|Cookie|Set-Cookie)\\s*:\\s*[^\\r\\n]*/gi\nconst BEARER_RE = /\\bBearer\\s+[A-Za-z0-9._\\-+=/]{8,}/g\nconst SECRET_FIELD_RE =\n /([\"']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|bearer|authorization|cookie|set-cookie|nonce|credential|private[_-]?key|password|secret|app[_-]?secret|client[_-]?secret|password|z_c0|d_c0)[\"']?\\s*[:=]\\s*)(?:\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'|[^\\s,&;}]+)/gi\nconst SECRET_QUERY_RE =\n /([?&](?:token|api[_-]?key|key|secret|password|bootstrap|code|sig|signature|credential|password)=)[^\\s&#\"']+/gi\nconst URL_CREDENTIAL_RE = /\\b(https?:\\/\\/)[^\\s/:@]+:[^\\s/@]+@/gi\nconst BOOTSTRAP_RE = /\\/bootstrap\\/[^\\s\"'?&#]+/gi\nconst KNOWN_TOKEN_RE = /\\b(?:sk-[A-Za-z0-9_-]{10,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{20,}|tvly-[A-Za-z0-9_-]{10,}|AKIA[0-9A-Z]{16})\\b/g\nconst PATH_RE = /(?:[A-Za-z]:\\\\|\\\\\\\\)[^\\s\"'`<>|]+|(?:\\/(?:home|Users|tmp|var|root)\\/|~\\/)[^\\s\"'`<>|]+/g\n\nexport function isPluginLogLevel(value: unknown): value is PluginLogLevel {\n return typeof value === 'string' && (PLUGIN_LOG_LEVELS as readonly string[]).includes(value)\n}\n\nexport function pluginLogLevelEnabled(level: PluginLogLevel, minimum: PluginLogLevel): boolean {\n return PLUGIN_LOG_LEVELS.indexOf(level) >= PLUGIN_LOG_LEVELS.indexOf(minimum)\n}\n\nexport function normalizePluginLogSource(raw: string): string {\n const stripped = String(raw || '').trim().replace(/^@[^/]+\\//, '').toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '')\n const prefixed = /^[a-z]/.test(stripped) ? stripped : `p-${stripped}`\n const bounded = prefixed.slice(0, 64).replace(/-+$/g, '')\n return SOURCE_RE.test(bounded) ? bounded : 'dsh-plugin'\n}\n\nexport function normalizePluginLogEvent(raw: string): string {\n const cleaned = String(raw || '').trim().toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-').replace(/^[^a-z]+/, '').slice(0, 128)\n return EVENT_RE.test(cleaned) ? cleaned : 'plugin.event'\n}\n\n/**\n * Parse a level specification like `debug` or `dsh-zhihu=debug,dsh-dot:warn,info`.\n * Source-specific rules win over a bare fallback level.\n */\nexport function parsePluginLogLevel(\n spec: string | undefined | null,\n source?: string,\n fallback: PluginLogLevel = DEFAULT_PLUGIN_LOG_LEVEL,\n): PluginLogLevel {\n if (!spec || typeof spec !== 'string') return fallback\n const normalizedSource = source ? normalizePluginLogSource(source) : undefined\n let wildcard: PluginLogLevel | undefined\n for (const token of spec.split(',').map(part => part.trim()).filter(Boolean)) {\n const lowered = token.toLowerCase()\n if (isPluginLogLevel(lowered)) {\n wildcard = lowered\n continue\n }\n const match = /^([^=:]+)[=:]([a-z]+)$/i.exec(token)\n if (!match) continue\n const target = match[1]!.trim()\n const candidate = match[2]!.trim().toLowerCase()\n if (!isPluginLogLevel(candidate)) continue\n if (target === '*') {\n wildcard = candidate\n continue\n }\n if (normalizedSource && normalizePluginLogSource(target) === normalizedSource) {\n return candidate\n }\n }\n return wildcard ?? fallback\n}\n\nexport function sanitizePluginLogText(text: string, maxChars = PLUGIN_LOG_MAX_MESSAGE_CHARS): string {\n if (!text) return ''\n if (CREDENTIAL_BODY_RE.test(text)) return '[omitted credential material]'\n if (CHAT_PAYLOAD_RE.test(text)) return '[omitted chat payload]'\n const redacted = text\n .replace(AUTH_HEADER_RE, '$1: [redacted]')\n .replace(BEARER_RE, 'Bearer [redacted]')\n .replace(SECRET_FIELD_RE, (full, prefix: string) => {\n const value = full.slice(prefix.length)\n if (value.startsWith('\"')) return `${prefix}\"[redacted]\"`\n if (value.startsWith(\"'\")) return `${prefix}'[redacted]'`\n return `${prefix}[redacted]`\n })\n .replace(SECRET_QUERY_RE, '$1[redacted]')\n .replace(URL_CREDENTIAL_RE, '$1[redacted]@')\n .replace(BOOTSTRAP_RE, '/bootstrap/[redacted]')\n .replace(KNOWN_TOKEN_RE, '[redacted]')\n .replace(PATH_RE, '[path]')\n return truncateLogText(redacted, maxChars)\n}\n\nexport function sanitizePluginLogContext(\n input: PluginLogContext | undefined,\n): Record<string, PluginLogScalar> | undefined {\n if (!input) return undefined\n const output: Record<string, PluginLogScalar> = {}\n for (const [key, value] of Object.entries(input).slice(0, 32)) {\n if (value === undefined || !CONTEXT_KEY_RE.test(key)) continue\n if (typeof value === 'number' && !Number.isFinite(value)) continue\n if (SECRET_KEY_RE.test(key)) {\n output[key] = '[redacted]'\n continue\n }\n output[key] = typeof value === 'string' ? sanitizePluginLogText(value, 1_000) : value\n }\n if (!Object.keys(output).length) return undefined\n const encoded = JSON.stringify(output)\n if (encoded.length <= PLUGIN_LOG_MAX_CONTEXT_CHARS) return output\n return { omitted: true, reason: 'Diagnostic context exceeded its size limit.' }\n}\n\nexport function sanitizePluginLogError(error: unknown): PluginLogError | undefined {\n if (error === undefined) return undefined\n const row = error && typeof error === 'object'\n ? error as { name?: unknown; message?: unknown; stack?: unknown; code?: unknown }\n : {}\n const rawMessage = typeof row.message === 'string' ? row.message : String(error)\n const result: PluginLogError = {\n name: typeof row.name === 'string' && row.name.trim() ? truncateLogText(row.name.trim(), 100) : 'Error',\n message: sanitizePluginLogText(rawMessage) || 'Unknown error',\n }\n if (typeof row.stack === 'string' && row.stack.trim()) {\n result.stack = sanitizePluginLogText(row.stack, 24_000)\n }\n if (typeof row.code === 'string' && /^[A-Za-z0-9_./:-]{1,100}$/.test(row.code)) {\n result.code = row.code\n } else if (typeof row.code === 'number' && Number.isFinite(row.code)) {\n result.code = String(row.code)\n }\n return result\n}\n\nexport function mergePluginLogContext(\n base: PluginLogContext | undefined,\n extra: PluginLogContext | undefined,\n): PluginLogContext | undefined {\n if (!base) return extra\n if (!extra) return base\n return { ...base, ...extra }\n}\n\nexport function buildPluginLogEntry(\n input: PluginLogInput,\n meta: {\n at: Date\n source: string\n surface: 'host' | 'client'\n profile?: string\n pid?: number\n correlation?: PluginLogContext\n },\n): PluginLogEntry {\n const entry: PluginLogEntry = {\n version: 1,\n at: meta.at.toISOString(),\n level: isPluginLogLevel(input.level) ? input.level : 'info',\n source: normalizePluginLogSource(meta.source),\n surface: meta.surface,\n ...(meta.profile ? { profile: meta.profile } : {}),\n event: normalizePluginLogEvent(input.event),\n message: sanitizePluginLogText(input.message || 'No diagnostic message was provided.', PLUGIN_LOG_MAX_MESSAGE_CHARS),\n ...(meta.pid !== undefined ? { pid: meta.pid } : {}),\n }\n const context = sanitizePluginLogContext(mergePluginLogContext(meta.correlation, input.context))\n if (context) entry.context = context\n const error = sanitizePluginLogError(input.error)\n if (error) entry.error = error\n return entry\n}\n\nexport function createChildPluginLogger(parent: PluginLogger, bound: PluginLogContext): PluginLogger {\n return {\n get source() { return parent.source },\n get file() { return parent.file },\n get lastWriteError() { return parent.lastWriteError },\n debug: (event, message, context) => parent.debug(event, message, mergePluginLogContext(bound, context)),\n info: (event, message, context) => parent.info(event, message, mergePluginLogContext(bound, context)),\n warn: (event, message, error, context) => parent.warn(event, message, error, mergePluginLogContext(bound, context)),\n error: (event, message, error, context) => parent.error(event, message, error, mergePluginLogContext(bound, context)),\n fault: (event, message, error, context) => parent.fault(event, message, error, mergePluginLogContext(bound, context)),\n write: input => parent.write({ ...input, context: mergePluginLogContext(bound, input.context) }),\n child: next => createChildPluginLogger(parent, mergePluginLogContext(bound, next) ?? {}),\n }\n}\n\nexport function truncateLogText(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text\n const marker = '[truncated]\\n'\n if (maxChars <= marker.length) return text.slice(0, Math.max(0, maxChars))\n return `${marker}${text.slice(-(maxChars - marker.length))}`\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//#region src/log-shared.d.ts
|
|
2
|
+
type PluginLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fault';
|
|
3
|
+
declare const PLUGIN_LOG_LEVELS: readonly PluginLogLevel[];
|
|
4
|
+
declare const DEFAULT_PLUGIN_LOG_LEVEL: PluginLogLevel;
|
|
5
|
+
declare const PLUGIN_LOG_MAX_MESSAGE_CHARS = 16000;
|
|
6
|
+
declare const PLUGIN_LOG_MAX_CONTEXT_CHARS = 8000;
|
|
7
|
+
type PluginLogScalar = string | number | boolean | null;
|
|
8
|
+
type PluginLogContext = Record<string, PluginLogScalar | undefined>;
|
|
9
|
+
interface PluginLogInput {
|
|
10
|
+
level: PluginLogLevel;
|
|
11
|
+
event: string;
|
|
12
|
+
message: string;
|
|
13
|
+
context?: PluginLogContext;
|
|
14
|
+
error?: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface PluginLogError {
|
|
17
|
+
name: string;
|
|
18
|
+
message: string;
|
|
19
|
+
stack?: string;
|
|
20
|
+
code?: string;
|
|
21
|
+
}
|
|
22
|
+
interface PluginLogEntry {
|
|
23
|
+
version: 1;
|
|
24
|
+
at: string;
|
|
25
|
+
level: PluginLogLevel;
|
|
26
|
+
source: string;
|
|
27
|
+
surface: 'host' | 'client';
|
|
28
|
+
profile?: string;
|
|
29
|
+
event: string;
|
|
30
|
+
message: string;
|
|
31
|
+
pid?: number;
|
|
32
|
+
context?: Record<string, PluginLogScalar>;
|
|
33
|
+
error?: PluginLogError;
|
|
34
|
+
}
|
|
35
|
+
interface PluginLogger {
|
|
36
|
+
readonly source: string;
|
|
37
|
+
readonly file: string | null;
|
|
38
|
+
readonly lastWriteError: string | null;
|
|
39
|
+
debug(event: string, message: string, context?: PluginLogContext): boolean;
|
|
40
|
+
info(event: string, message: string, context?: PluginLogContext): boolean;
|
|
41
|
+
warn(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
42
|
+
error(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
43
|
+
fault(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
44
|
+
write(input: PluginLogInput): boolean;
|
|
45
|
+
child(context: PluginLogContext): PluginLogger;
|
|
46
|
+
}
|
|
47
|
+
declare function isPluginLogLevel(value: unknown): value is PluginLogLevel;
|
|
48
|
+
declare function pluginLogLevelEnabled(level: PluginLogLevel, minimum: PluginLogLevel): boolean;
|
|
49
|
+
declare function normalizePluginLogSource(raw: string): string;
|
|
50
|
+
declare function normalizePluginLogEvent(raw: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Parse a level specification like `debug` or `dsh-zhihu=debug,dsh-dot:warn,info`.
|
|
53
|
+
* Source-specific rules win over a bare fallback level.
|
|
54
|
+
*/
|
|
55
|
+
declare function parsePluginLogLevel(spec: string | undefined | null, source?: string, fallback?: PluginLogLevel): PluginLogLevel;
|
|
56
|
+
declare function sanitizePluginLogText(text: string, maxChars?: number): string;
|
|
57
|
+
declare function sanitizePluginLogContext(input: PluginLogContext | undefined): Record<string, PluginLogScalar> | undefined;
|
|
58
|
+
declare function sanitizePluginLogError(error: unknown): PluginLogError | undefined;
|
|
59
|
+
declare function mergePluginLogContext(base: PluginLogContext | undefined, extra: PluginLogContext | undefined): PluginLogContext | undefined;
|
|
60
|
+
declare function buildPluginLogEntry(input: PluginLogInput, meta: {
|
|
61
|
+
at: Date;
|
|
62
|
+
source: string;
|
|
63
|
+
surface: 'host' | 'client';
|
|
64
|
+
profile?: string;
|
|
65
|
+
pid?: number;
|
|
66
|
+
correlation?: PluginLogContext;
|
|
67
|
+
}): PluginLogEntry;
|
|
68
|
+
declare function createChildPluginLogger(parent: PluginLogger, bound: PluginLogContext): PluginLogger;
|
|
69
|
+
declare function truncateLogText(text: string, maxChars: number): string;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { truncateLogText as C, sanitizePluginLogText as S, normalizePluginLogSource as _, PluginLogContext as a, sanitizePluginLogContext as b, PluginLogInput as c, PluginLogger as d, buildPluginLogEntry as f, normalizePluginLogEvent as g, mergePluginLogContext as h, PLUGIN_LOG_MAX_MESSAGE_CHARS as i, PluginLogLevel as l, isPluginLogLevel as m, PLUGIN_LOG_LEVELS as n, PluginLogEntry as o, createChildPluginLogger as p, PLUGIN_LOG_MAX_CONTEXT_CHARS as r, PluginLogError as s, DEFAULT_PLUGIN_LOG_LEVEL as t, PluginLogScalar as u, parsePluginLogLevel as v, sanitizePluginLogError as x, pluginLogLevelEnabled as y };
|
|
72
|
+
//# sourceMappingURL=log-shared2.d.ts.map
|
package/lib/logger.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { C as truncateLogText, S as sanitizePluginLogText, _ as normalizePluginLogSource, a as PluginLogContext, b as sanitizePluginLogContext, c as PluginLogInput, d as PluginLogger, f as buildPluginLogEntry, g as normalizePluginLogEvent, h as mergePluginLogContext, i as PLUGIN_LOG_MAX_MESSAGE_CHARS, l as PluginLogLevel, m as isPluginLogLevel, n as PLUGIN_LOG_LEVELS, o as PluginLogEntry, p as createChildPluginLogger, r as PLUGIN_LOG_MAX_CONTEXT_CHARS, s as PluginLogError, t as DEFAULT_PLUGIN_LOG_LEVEL, u as PluginLogScalar, v as parsePluginLogLevel, x as sanitizePluginLogError, y as pluginLogLevelEnabled } from "./log-shared2.js";
|
|
2
|
+
//#region src/logger.d.ts
|
|
3
|
+
declare const PLUGIN_LOG_MAX_AGE_MS: number;
|
|
4
|
+
declare const PLUGIN_LOG_MAX_FILE_BYTES: number;
|
|
5
|
+
declare const PLUGIN_LOG_MAX_ARCHIVE_BYTES: number;
|
|
6
|
+
declare const PLUGIN_LOG_RECENT_LIMIT = 200;
|
|
7
|
+
interface RotatingPluginLoggerOptions {
|
|
8
|
+
directory?: string;
|
|
9
|
+
home?: string;
|
|
10
|
+
profile?: string;
|
|
11
|
+
minimumLevel?: PluginLogLevel;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
now?: () => Date;
|
|
14
|
+
pid?: number;
|
|
15
|
+
maxFileBytes?: number;
|
|
16
|
+
maxArchiveBytes?: number;
|
|
17
|
+
maxAgeMs?: number;
|
|
18
|
+
recentLimit?: number;
|
|
19
|
+
forwardHostWarnError?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface InstallPluginLoggerOptions extends RotatingPluginLoggerOptions {
|
|
22
|
+
loadedContext?: PluginLogContext;
|
|
23
|
+
}
|
|
24
|
+
declare function safeProfileName(profile: string | undefined | null): string;
|
|
25
|
+
declare function pluginLogDirectory(home: string, profile?: string): string;
|
|
26
|
+
declare function resolveHostLogTarget(ctx: unknown, options?: Pick<RotatingPluginLoggerOptions, 'directory' | 'home' | 'profile' | 'env'>): {
|
|
27
|
+
directory: string | null;
|
|
28
|
+
anchor: string | null;
|
|
29
|
+
profile?: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Synchronous, append-only rotating JSONL logger modeled on DSH Spaces
|
|
33
|
+
* diagnostics: 7-day or 100 MiB active rotation, 7-day / 100 MiB archive
|
|
34
|
+
* retention, secret and path redaction, and non-throwing write resilience.
|
|
35
|
+
*/
|
|
36
|
+
declare class RotatingPluginLogger implements PluginLogger {
|
|
37
|
+
readonly source: string;
|
|
38
|
+
readonly file: string | null;
|
|
39
|
+
private lastError;
|
|
40
|
+
private size;
|
|
41
|
+
private openedAt;
|
|
42
|
+
private initialized;
|
|
43
|
+
private writing;
|
|
44
|
+
private sequence;
|
|
45
|
+
private loggedWriteFailure;
|
|
46
|
+
private readonly recent;
|
|
47
|
+
private readonly directory;
|
|
48
|
+
private readonly anchor;
|
|
49
|
+
private readonly profile;
|
|
50
|
+
private readonly minimum;
|
|
51
|
+
private readonly now;
|
|
52
|
+
private readonly pid;
|
|
53
|
+
private readonly maxFileBytes;
|
|
54
|
+
private readonly maxArchiveBytes;
|
|
55
|
+
private readonly maxAgeMs;
|
|
56
|
+
private readonly recentLimit;
|
|
57
|
+
constructor(source: string, options?: RotatingPluginLoggerOptions & {
|
|
58
|
+
anchor?: string | null;
|
|
59
|
+
});
|
|
60
|
+
get lastWriteError(): string | null;
|
|
61
|
+
recentEntries(limit?: number): PluginLogEntry[];
|
|
62
|
+
debug(event: string, message: string, context?: PluginLogContext): boolean;
|
|
63
|
+
info(event: string, message: string, context?: PluginLogContext): boolean;
|
|
64
|
+
warn(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
65
|
+
error(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
66
|
+
fault(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean;
|
|
67
|
+
child(context: PluginLogContext): PluginLogger;
|
|
68
|
+
write(input: PluginLogInput): boolean;
|
|
69
|
+
private pushRecent;
|
|
70
|
+
private prepare;
|
|
71
|
+
private rotate;
|
|
72
|
+
private rotateStaleActive;
|
|
73
|
+
private prune;
|
|
74
|
+
private openManagedDirectory;
|
|
75
|
+
private regularFile;
|
|
76
|
+
}
|
|
77
|
+
declare function createPluginLogger(ctxOrSource: unknown, maybeSource?: string | RotatingPluginLoggerOptions, maybeOptions?: RotatingPluginLoggerOptions): PluginLogger;
|
|
78
|
+
declare function installPluginLogger(ctx: unknown, source: string, options?: InstallPluginLoggerOptions): PluginLogger;
|
|
79
|
+
declare function nextPluginOperationId(prefix?: string): string;
|
|
80
|
+
declare function currentPluginLogContext(): PluginLogContext | undefined;
|
|
81
|
+
declare function withPluginLogContext<T>(context: PluginLogContext, action: () => T): T;
|
|
82
|
+
declare function pluginLogStep<T>(logger: PluginLogger, step: string, context: PluginLogContext | undefined, action: () => Promise<T>, completedLevel?: PluginLogLevel): Promise<T>;
|
|
83
|
+
declare function pluginLogSyncStep<T>(logger: PluginLogger, step: string, context: PluginLogContext | undefined, action: () => T, completedLevel?: PluginLogLevel): T;
|
|
84
|
+
declare function readPluginLogTail(file: string, maxBytes?: number): string;
|
|
85
|
+
//#endregion
|
|
86
|
+
export { DEFAULT_PLUGIN_LOG_LEVEL, InstallPluginLoggerOptions, PLUGIN_LOG_LEVELS, PLUGIN_LOG_MAX_AGE_MS, PLUGIN_LOG_MAX_ARCHIVE_BYTES, PLUGIN_LOG_MAX_CONTEXT_CHARS, PLUGIN_LOG_MAX_FILE_BYTES, PLUGIN_LOG_MAX_MESSAGE_CHARS, PLUGIN_LOG_RECENT_LIMIT, PluginLogContext, PluginLogEntry, PluginLogError, PluginLogInput, PluginLogLevel, PluginLogScalar, PluginLogger, RotatingPluginLogger, RotatingPluginLoggerOptions, buildPluginLogEntry, createChildPluginLogger, createPluginLogger, currentPluginLogContext, installPluginLogger, isPluginLogLevel, mergePluginLogContext, nextPluginOperationId, normalizePluginLogEvent, normalizePluginLogSource, parsePluginLogLevel, pluginLogDirectory, pluginLogLevelEnabled, pluginLogStep, pluginLogSyncStep, readPluginLogTail, resolveHostLogTarget, safeProfileName, sanitizePluginLogContext, sanitizePluginLogError, sanitizePluginLogText, truncateLogText, withPluginLogContext };
|
|
87
|
+
//# sourceMappingURL=logger.d.ts.map
|