qwen-code Daemon HTTP 入站 Trace Context 传播设计:让 W3C traceparent 在服务端落地
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
导读
qwen-code daemon(守护进程)对外提供 HTTP 服务(serve模式)时,过往只负责"记录"请求 span:每个qwen-code.daemon.requestspan 都会开启一条全新的 trace,即使调用方(企业代理、OTel 插桩客户端、ACP 网关)已经在 HTTP 请求头中携带了标准 W3Ctraceparent,服务端 span 也无法与调用方的 trace 建立父子关联,跨服务排障只能退回到"对时间戳"的老路。本文基于 2026-08-18-daemon-http-inbound-trace-context.md 设计文档,结合仓库源码,完整讲解该能力的设计动机、抽取/解析实现、采样策略、日志关联(含 telemetry 关闭场景)、非目标与验证方法,帮助读者理解 qwen-code 如何在 HTTP 服务边界上实现 W3C Trace Context 入站提取,并让 daemon 日志在零遥测配置下也能按 traceId 关联。
读完本文你将掌握:traceparent/tracestate头从请求到 span 父上下文的完整链路、shouldForceSampled()采样决策矩阵的运作方式、telemetry 开关两种模式下 traceId 的落点(span 前缀 vs 访问日志字段),以及如何用一条curl验证整条链路。
背景:出站传播早已就位,入站提取却缺席
出站:JSON-RPC_meta中的 traceparent
daemon 早已具备出站传播能力:prompt 请求会在 JSON-RPC 的_meta中携带traceparent,daemon 通过extractDaemonTraceContext提取它以作为 bridge span 的父级。源码实现位于 packages/core/src/telemetry/daemon-tracing.ts:
export const DAEMON_TRACEPARENT_META_KEY = 'qwen.telemetry.traceparent'; export const DAEMON_TRACESTATE_META_KEY = 'qwen.telemetry.tracestate'; export function extractDaemonTraceContext( source: unknown, ): Context | undefined { const meta = (source as { _meta?: unknown } | undefined)?._meta; if (!meta || typeof meta !== 'object' || Array.isArray(meta)) { return undefined; } const record = meta as Record<string, unknown>; const traceparent = record[DAEMON_TRACEPARENT_META_KEY]; if (typeof traceparent !== 'string' || traceparent.length === 0) { return undefined; } // _meta 路径可能来自两类调用方:进程内 bridge(injectDaemonTraceContext, // 值已被 SAMPLED,强制采样是 no-op)以及直接 ACP 客户端(其请求 _meta 与 // HTTP 头一样属于外部输入)——因此两条边界获得同样的采样保护。 return forceSampledUnderSampler( contextFromTraceparentValues( traceparent, record[DAEMON_TRACESTATE_META_KEY], ), ); }对应地,injectDaemonTraceContext(daemon-tracing.ts)负责把当前活动 span 的 trace context 写入出站请求的_meta,并先剥离被保留的 trace 元数据键(stripReservedTraceMeta),避免调用方伪造/污染。
入站缺口:HTTP 面只记录、不关联
问题在于 HTTP 面:qwen-code.daemon.requestspan 每次都是全新 trace 的根。设计文档 Motivation 明确指出:
The HTTP surface, however, onlyrecordsrequest spans — every
qwen-code.daemon.requestspan starts a new trace.
结果是,任何转发标准 W3Ctraceparent头的 HTTP 调用方都拿不到关联:服务端 span 无法接回调用方的 trace,跨服务调试只能靠时间戳对齐。
好在基础设施已经具备:withDaemonSpan接受显式的parentContext(见 daemon-tracing.ts):
export async function withDaemonSpan<T>( name: string, attributes: DaemonAttributes, fn: (span: Span) => Promise<T>, options: { autoOkOnSuccess?: boolean; parentContext?: Context; startTime?: Date } = {}, ): Promise<T> { ... return options.parentContext ? await tracer.startActiveSpan(name, spanOptions, options.parentContext, run) : await tracer.startActiveSpan(name, spanOptions, run); }W3C Trace Context 在 HTTP 服务端边界的提取,本就属于 OTel HTTP 语义约定中SpanKind.SERVER邻近的标准行为——管线是现成的,缺的只是"从 HTTP 头提取并接线"这一步。
设计总览:两处改动 + 一条轻量旁路
设计文档把改动划分为两大块,外加一条 telemetry 关闭时的日志关联旁路:
- Core(
daemon-tracing.ts):把既有_meta提取逻辑抽成共享的contextFromTraceparentValues辅助函数(全局propagation.extract优先,W3CTraceContextPropagator实例兜底),新增extractDaemonHttpTraceContext(headers)从 Node 风格(小写键)的 header 对象读取traceparent/tracestate;DaemonRequestSpanOptions增加可选parentContext,直接透传给withDaemonSpan。 - Serve 中间件(
daemonTelemetryMiddleware):每个请求从req.headers提取,失败时 fail-closed 返回undefined(遥测绝不影响请求处理),仅在提取成功时传入 context——没有合法头的请求保持与现在完全相同的 span 形态。提取行为受isTelemetrySdkInitialized()门控,telemetry 关闭的部署在热路径上不会触发任何 OTel 机制。 - 日志关联旁路:telemetry 关闭(默认)时,中间件用纯正则
extractInboundTraceId解析traceparent,把 traceId 存到响应对象的 telemetry context(与 workspace hash 共用机制的独立 symbol),访问日志的finish回调读出并输出为 camelCase 的traceId字段。
下面逐一深入。
Core 层实现:共享提取 + 强制采样
共享辅助函数contextFromTraceparentValues
提取逻辑的核心是contextFromTraceparentValues(daemon-tracing.ts):
function contextFromTraceparentValues( traceparent: string, tracestate: unknown, ): Context | undefined { const carrier: Record<string, string> = { traceparent }; if (typeof tracestate === 'string' && tracestate.length > 0) { carrier['tracestate'] = tracestate; } const extracted = propagation.extract(ROOT_CONTEXT, carrier); if (trace.getSpanContext(extracted)) return extracted; if (!daemonFallbackPropagator) return undefined; const fallback = daemonFallbackPropagator.extract( ROOT_CONTEXT, carrier, defaultTextMapGetter, ); return trace.getSpanContext(fallback) ? fallback : undefined; }它保证:无论是否注册了全局 propagator,接受规则(未来 traceparent 版本、tracestate、全零 id)都完全一致——先走全局propagation.extract,失败再走直接实例化的 W3C propagator 兜底。
一个关键的工程决策是daemonFallbackPropagator的懒加载注入:daemon-tracing.ts处于每次 CLI 启动的静态启动图上,而@opentelemetry/core是 CJS barrel,tree-shaking 无法瘦身(即使 telemetry 关闭,每次启动也有约 65 KB 开销)。因此该模块不直接构造 W3C propagator,而是由懒加载的 SDK chunk(sdk-impl.ts)在 telemetry 真正启用后通过setDaemonFallbackPropagator注入;SDK 初始化之前 holder 为空,提取直接返回 undefined——这正是 telemetry 关闭状态,无任何 OTel 副作用。
extractDaemonHttpTraceContext:HTTP 头 → 父 context
export function extractDaemonHttpTraceContext( headers: Record<string, unknown> | undefined, ): Context | undefined { const traceparent = headers?.['traceparent']; if (typeof traceparent !== 'string' || traceparent.length === 0) { return undefined; } const extracted = contextFromTraceparentValues( traceparent, headers?.['tracestate'], ); return forceSampledUnderSampler(extracted); }它接收 Node 风格(小写键)的 header 对象,复用contextFromTraceparentValues,最后过一遍forceSampledUnderSampler。注意与_meta路径的区别:_meta路径用的是保留键qwen.telemetry.traceparent,HTTP 路径用的是标准traceparent/tracestate头名。
DaemonRequestSpanOptions.parentContext
withDaemonRequestSpan把parentContext原样透传给withDaemonSpan(daemon-tracing.ts),而 span 创建时startActiveSpan(name, spanOptions, parentContext, run)会以该 context 作为父级——由此 HTTP 请求 span 便接入了调用方的 trace。
整棵子树随请求 span 一起迁移
设计文档特别提醒:迁移的是整棵子树,不只是daemon.request本身。经由_meta传播的 session 子进程 span(prompt / model / tool)也会加入调用方的 trace。这意味着任何按traceId聚合或告警的组件,都会看到 session 侧 span 的归属随之改变——这是引入该能力时必须知会的下游影响。
Serve 中间件:门控提取与 fail-closed
daemonTelemetryMiddleware的接入
中间件实现位于 packages/cli/src/serve/server/telemetry.ts。核心逻辑(节选):
let parentContext: ReturnType<typeof extractDaemonHttpTraceContext>; if (isTelemetrySdkInitialized()) { try { parentContext = extractDaemonHttpTraceContext(req.headers); } catch { // Telemetry must not affect request handling. parentContext = undefined; } // present-but-invalid header 时输出 debug 日志(限速) try { const inboundTraceparent = req.headers?.['traceparent']; if ( !parentContext && typeof inboundTraceparent === 'string' && inboundTraceparent.length > 0 && acquireBreadcrumbToken() ) { emitDaemonLog( 'Rejected invalid inbound traceparent header.', { 'http.route': route.route, 'http.request.header.traceparent': sanitizeLogText(inboundTraceparent, 128), }, { eventName: 'qwen-code.daemon.traceparent.invalid', severityNumber: 5, // SeverityNumber.DEBUG }, ); } } catch { // Telemetry must not affect request handling } }几个要点:
- 门控:
extractDaemonHttpTraceContext只在isTelemetrySdkInitialized()为真时执行,telemetry 关闭的部署在热路径上只付出日志关联那条"单正则 trace-id 捕获"的代价,不触碰任何 OTel 机制。 - fail-closed:提取包在 try/catch 里,任何异常都回落为
undefined,请求照常处理;设计文档反复强调 "telemetry never affects request handling"。 - 可诊断性:头存在但非法时,输出事件名为
qwen-code.daemon.traceparent.invalid的 DEBUG 日志(severityNumber: 5),让被拒绝的头仅凭 daemon 日志即可诊断,无需重放请求。日志内容经sanitizeLogText截断并中和控制字符,防止伪造头结构;traceparent 只含 trace-id/span-id/flags,不含用户内容,因此记录被拒绝的值在隐私上是安全的。 - 限速防刷:被拒绝头的面包屑按令牌桶限速(
TRACEPARENT_BREADCRUMB_BURST = 60初始令牌,TRACEPARENT_BREADCRUMB_REFILL_PER_SECOND = 2/秒),防止恶意客户端用大量非法 traceparent 头淹没 daemon 日志(telemetry.ts)。
随后,withDaemonRequestSpan在parentContext存在时才传入(...(parentContext ? { parentContext } : {})),保证没有合法头的请求保持与现在完全相同的 span 形状。
注意:session-subprocess span 的归属随之改变
由于_meta转发(injectDaemonTraceContext)会把请求 span 的 context 继续带入 session 子进程,子进程中的 prompt/model/tool span 也会一并归属到调用方 trace。文档特别注明:任何按 traceId 聚合、告警或做 RED 指标的组件,需要感知 session 侧 span 的 traceId 归属变化。
采样策略:不盲从调用方的 sampled 位
这是本设计最精巧的部分。W3Ctraceparent的 flags 中含sampled位,但HTTP 入站路径并不原样采纳调用方的 sampled 位。原因(源码注释与设计文档一致):
在默认的
parentbased_always_on采样器下(daemon SDK 未配置 sampler),一个远端未采样(sampled=0)的父级会委托给AlwaysOffSampler,从而静默删除请求 span、next()之下的一切,以及——经由_meta转发——session 子进程 span。
sampled=0只是调用方自己的 head-based 比率采样,不该成为丢弃 daemon 遥测的指令。因此入站 HTTP 父级通过与合成 session 根相同的shouldForceSampled()决策矩阵强制TraceFlags.SAMPLED。决策矩阵实现在 packages/core/src/telemetry/tracer.ts:
export function shouldForceSampled(): boolean { const sampler = process.env['OTEL_TRACES_SAMPLER']?.trim().toLowerCase() ?? ''; if (!sampler || sampler.startsWith('parentbased_')) { if (sampler.includes('always_off')) return false; return true; } return sampler === 'always_on'; }矩阵语义如下表:
OTEL_TRACES_SAMPLER配置 | 是否强制 SAMPLED | 理由 |
|---|---|---|
| 未设置(默认) | 是 | 等同parentbased_always_on,远端未采样父级会委托 AlwaysOff 导致整条子树被丢弃 |
parentbased_*(除 always_off) | 是 | 同上,parentbased_traceidratio下合成根必须带 SAMPLED 否则零导出 |
parentbased_always_off | 否 | 尊重运维显式关闭采样的意图 |
always_on | 是 | 忽略父级 flags,SAMPLED 无害且让决策矩阵保持显式 |
非 parentbased 采样器(如traceidratio、always_off) | 否 | 每个 span 独立评估,保留调用方 flags 由采样器逐 span 决策 |
强制采样的实现是forceSampledUnderSampler(daemon-tracing.ts):若shouldForceSampled()为真,就用trace.wrapSpanContext把提取出的 span context 的traceFlags或上TraceFlags.SAMPLED再包回 context。
_meta路径应用相同的强制逻辑:对于进程内 bridge(daemon → 子进程),父级是我们自己的 span,在该策略下本来就已 SAMPLED,强制是 no-op;而直接 ACP 客户端也可以附带_meta并携带调用方控制的sampled=0——这与 HTTP 头一样属于外部输入,获得同样的保护。两条入站边界(HTTP 头、_meta)的采样处理由此保持完全一致。
日志关联:telemetry 开与关都能按 traceId 找
telemetry 开启:span 前缀闭环
daemon 日志行在活动记录 span 内写出时,已携带[trace_id=… span_id=…]前缀——实现于getActiveTraceContext(packages/cli/src/serve/daemon-logger.ts):
function getActiveTraceContext(): DaemonTraceContext | undefined { try { const span = trace.getActiveSpan(); if (!span?.isRecording()) return undefined; const spanContext = span.spanContext(); if ( !isSpanContextValid(spanContext) || (spanContext.traceFlags & TraceFlags.SAMPLED) === 0 ) { return undefined; } return { traceId: spanContext.traceId, spanId: spanContext.spanId }; } catch { return undefined; } }注意两个守卫:span 必须isRecording(),且必须带SAMPLED标志——未采样 span 不会产出前缀。telemetry 开启时,请求 span 父级到调用方 trace,于是该请求的每一条daemon 日志(包括访问日志的request completed)都会被前缀以调用方的 trace id,实现端到端闭环。访问日志的finish监听器与日志器走同一个 AsyncLocalStorage 传播读取活动 span,因此两个finish监听器的注册顺序对前缀无影响。
telemetry 关闭(默认):纯正则旁路
telemetry 关闭(默认状态)时没有 span,前缀永不触发。但设计保留了一条轻量路径,让基于日志的关联在零遥测配置、零 trace 后端的情况下依然成立:
daemonInboundTraceIdCaptureMiddleware(telemetry.ts)在认证、限流、body 解析之前挂载,用纯正则extractInboundTraceId解析traceparent,把 traceId 存到响应对象上独立的 symbol(daemonInboundTraceIdContext):
export function daemonInboundTraceIdCaptureMiddleware( req: Request, res: Response, next: NextFunction, ): void { try { const inboundTraceId = extractInboundTraceId(req.headers); if (inboundTraceId !== undefined) { (res as InboundTraceIdResponse)[daemonInboundTraceIdContext] = inboundTraceId; } } catch { // Telemetry must not affect request handling. } next(); }早挂载的意义:即使请求在认证层(401)、限流层(429)、body 解析(400)或路由未命中(404)处被短路,访问日志行依然能关联到调用方 trace。
extractInboundTraceId(daemon-tracing.ts)是纯格式检查,不依赖任何 OTel 机制:
const TRACEPARENT_RE = /^\s?([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(-.*)?\s?$/; const ALL_ZERO_TRACE_ID = '0'.repeat(32); const ALL_ZERO_SPAN_ID = '0'.repeat(16); export function extractInboundTraceId( headers: Record<string, unknown> | undefined, ): string | undefined { const traceparent = headers?.['traceparent']; if (typeof traceparent !== 'string' || traceparent.length === 0) { return undefined; } const match = TRACEPARENT_RE.exec(traceparent); if (!match) return undefined; const [, version, traceId, spanId, , trailing] = match; // 版本 00 必须恰好四个字段;更高版本可携带解析器忽略的尾部扩展字段——与 propagator 一致。 if (version === '00' && trailing !== undefined) return undefined; if (version === 'ff') return undefined; if (traceId === ALL_ZERO_TRACE_ID || spanId === ALL_ZERO_SPAN_ID) { return undefined; } return traceId; }其接受规则与内置 W3C propagator 完全镜像——同样的 shape/全零/ff拒绝、单个可选首尾空白、版本00之上允许尾部扩展字段——保证一个头要么两条路径都加入,要么都不加入。tracestate留在 propagator 路径,因为日志行只需要一个合理的 trace id。
随后,访问日志的finish回调通过getDaemonTelemetryInboundTraceId读回,并输出为 camelCase 的traceId字段(packages/cli/src/serve/server/access-log.ts):
// telemetry 开启时,daemon 请求 span 已经给本行打上了 trace 前缀; // 该字段覆盖 telemetry 关闭的部署——在那里它是 daemon 日志行与发送 // traceparent 头的调用方之间唯一的 traceId 链接。 const inboundTraceId = getDaemonTelemetryInboundTraceId(res); const ctx = { route: route.value, ...(sessionId ? { sessionId: sessionId.value, ... } : {}), ...(clientId ? { clientId: clientId.value, ... } : {}), ...(inboundTraceId ? { traceId: inboundTraceId } : {}), status, durationMs: Math.max(0, Math.round(monotonicNow() - startMs)), }; if (status >= 400) daemonLog.warn('request completed', ctx); else daemonLog.info('request completed', ctx);为什么 camelCase 而不是 snake_case
设计文档明确解释了命名选择:camelCase 的traceId字段与 logger 保留的 snake_casetrace_id前缀键刻意区分,这样调用方无法伪造span 派生的前缀。两个细节:
- 只要解析到合法头,camelCase 字段在两种 telemetry 模式下都会被捕获,因此一种日志查询形态适用于所有部署;telemetry 开启时 snake_case 的 span 前缀会冗余携带同一个 id。
- 没有合法头时,字段是省略而非空值(
...(inboundTraceId ? { traceId: inboundTraceId } : {}),以及访问日志测试中expect('traceId' in (context ?? {})).toBe(false)),且每一步都 fail-closed。
存储 symbol 的独立性
daemonInboundTraceIdContext使用独立 symbol,而不是复用 telemetry response context(telemetry-context.ts):
被捕获的调用方 trace id 放在自己的 symbol 下:telemetry response context 的存在同时充当 handler-resolved workspace 归属的 opt-in 门(见
setDaemonTelemetryWorkspace),因此捕获 trace id 绝不能创建它——否则调用方仅仅发送一个 traceparent 头,就会静默改变 span 归属。
这是daemonInboundTraceIdCaptureMiddleware与daemonTelemetryMiddleware解耦、独立挂载的根因。同时telemetry-context.ts保持 import-light:访问日志位于 serve fast-path 的 pre-listen 静态闭包内,不能触及 telemetry 中间件的 core import 图。
非目标:明确边界
- 不改 span 种类与属性:既有
qwen-code.daemon.requestspan 保持SpanKind.INTERNAL与相同属性,存在合法头时仅改变父级链接。 - 不做
traceparent响应注入,也不支持 W3Ctracingresponse。 - 不新增采样配置面:入站策略复用现有
shouldForceSampled()矩阵;SDK 自身的 sampler 仍是唯一采样权威。
备选方案与取舍
设计文档记录了被否决的两个方案:
- 把请求 span 改为
SpanKind.SERVER(符合 HTTP semconv):这是一个已知缺口——qwen-code.daemon.request是 daemon 唯一的 SERVER 邻近 span(HttpInstrumentation从不 patch 服务端,因为 SDK 懒加载),依赖 SERVER span 推导服务拓扑/RED 指标的后端(Tempo service-graph、ARMS)将无法在调用方 trace 内把 daemon 识别为服务。但切换会改变每个既有 daemon.request span 的形态、可能移动后端分组,故推迟为后续工作。设计文档明确承认这是当前实现的已知缺口。 - 在 core 的
withDaemonRequestSpan内直接从一个原始 header bag 提取:被否决,因为 core 的 request-span 选项目前是传输原语(transport-primitive);只有中间件知道载体是 HTTP 头。
测试与验证
设计文档与源码给出了完整的三层验证矩阵:
单元测试:头提取
覆盖 packages/core/src/telemetry/daemon-tracing.test.ts 与 packages/cli/src/serve/server/telemetry.test.ts:
- 有效 / 缺失 / 畸形 / 全零 id / 数组值 / 版本
ff/ 版本00带扩展字段 / 未来版本01/ 入站tracestate - 通过
withDaemonRequestSpan的请求 span 父级化 - sampled 决策矩阵(默认强制、
parentbased_always_off与traceidratio原样、_meta路径默认采样器下强制 / opt-out 下原样) - 中间件透传(key 存在 vs 省略、telemetry 关闭跳过、被拒绝头的 debug 日志)
- 类型级守卫:
parentContext保留在DaemonRequestSpanOptions上
单元测试:telemetry 关闭的日志关联
extractInboundTraceId(有效 / 缺失 / 畸形 / 全零 / 版本ff/ 数组值 / 未来版本,以及 fresh-module 状态无需 propagator)- 中间件捕获(telemetry 关闭有/无头、telemetry 开启时与 span 前缀同时输出 camelCase 字段、handler 解析的 context 初始化不覆盖已存 id)
- 访问日志输出/省略
traceId字段(见 access-log.test.ts)
手工冒烟(dry run)
设计文档给出了可复现的验证步骤:
- 以
QWEN_TELEMETRY_OUTFILE启动serve; - 用固定
traceparent的curl打一个请求——导出的 span 必须共享头的 traceId 并以头的 spanId 为父; - 不带头的对照请求必须留在自己的 trace 上;
- telemetry 关闭时,同样的 curl 必须仍输出带
traceId字段(与头一致)的request completed日志。
实战建议与排查速查
结合源码与设计,给读者几条直接可用的操作结论:
- 想让 daemon HTTP 请求接入自己的 trace:作为调用方,在请求中携带标准 W3C
traceparent(和可选的tracestate)头即可;qwen-code.daemon.requestspan 及其子树(含经_meta传播的 session 子进程 span)会自动归属到你的 trace 下。 - telemetry 开启时:daemon 日志行的
[trace_id=… span_id=…]前缀与访问日志request completed的traceId字段同时携带调用方 trace id,日志与 trace 后端可互相跳转。 - telemetry 关闭时(默认):无需任何遥测配置与 trace 后端,访问日志
request completed的traceId字段就是日志侧唯一的 trace 关联点——一条日志查询形态通吃所有部署。 - 排障:如果头被拒绝,daemon 日志会出现
qwen-code.daemon.traceparent.invalid事件(DEBUG 级、限速),其中http.request.header.traceparent属性记录被拒绝的原始值(已 sanitize、截断 128 字符),可据此判断是版本ff、全零 id、格式错误还是其他原因。 - 采样注意:默认配置下服务端会强制 SAMPLED(调用方的
sampled=0不会丢弃 daemon 遥测);若使用parentbased_always_off则尊重关闭意图;若使用traceidratio等非 parentbased 采样器,服务端保留调用方 flags 由采样器逐 span 决策。 - 已知缺口:请求 span 目前仍是
SpanKind.INTERNAL,依赖 SERVER span 推导服务拓扑的后端(如 Tempo service-graph、ARMS)在调用方 trace 内识别不到 daemon 服务节点;切换为 SERVER 被列为后续工作。
关键文件索引
- 设计文档:docs/design/2026-08-18-daemon-http-inbound-trace-context.md
- Core 提取与传播实现:packages/core/src/telemetry/daemon-tracing.ts
- Core 采样决策矩阵:packages/core/src/telemetry/tracer.ts
- Core 单元测试:packages/core/src/telemetry/daemon-tracing.test.ts
- Serve 遥测中间件:packages/cli/src/serve/server/telemetry.ts
- 中间件单元测试:packages/cli/src/serve/server/telemetry.test.ts
- 响应侧 symbol 与读取器:packages/cli/src/serve/server/telemetry-context.ts
- 访问日志(
traceId字段输出):packages/cli/src/serve/server/access-log.ts - 访问日志测试:packages/cli/src/serve/server/access-log.test.ts
- daemon 日志
trace_id=前缀:packages/cli/src/serve/daemon-logger.ts
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考