@steipete/summarize-core 核心库全解析:内容提取、Prompt 构建与 DNS 钉扎网络安全抓取 📅 发布时间:2026/9/17 18:39:17 👁 浏览次数: steipete/summarize-core 核心库全解析内容提取、Prompt 构建与 DNS 钉扎网络安全抓取【免费下载链接】summarizePoint at any URL/YouTube/Podcast or file. Get the gist. CLI and Chrome Extension.项目地址: https://gitcode.com/GitHub_Trending/summarize/summarize本篇技术指南以 packages/core/README.md 为骨架深入剖析 Summarize 项目的核心库steipete/summarize-core它既是 CLIsteipete/summarize与 Chrome 扩展背后共用的内容提取 Prompt 构建底层库也对外提供一套独特的DNS 钉扎DNS-pinned网络抓取与 SSRF 防护机制。读完本文你将掌握该库的推荐导入方式、内容提取管线的公开入口、五种摘要长度的 Prompt 参数体系以及fetchWithDnsPinnedAddresses与createNetworkGuardedFetch的完整工作原理并能够在自己基于 Summarize 的二次开发中安全地组合使用它们。一、库定位CLI 与扩展共享的内容层 提示词层从 packages/core/README.md 的声明可以看出steipete/summarize-core的职责被精确定义为两件事内容提取content extraction把任意 URL、YouTube / 播客 / 本地文件转化为可供 LLM 消费的正文、转录文本与元数据Prompt 构建prompt builders把提取结果与用户参数语言、长度、覆写指令组装成结构化的模型提示词。整个仓库中 CLI 与扩展并不各自实现一套抓取和提示词逻辑而是统一收敛到这个包CLI 包是steipete/summarize作为库使用时官方推荐只导入两个子路径steipete/summarize-core/content与steipete/summarize-core/prompts。这种入口收敛从 content/index.ts 的导出表可以印证它集中导出了createLinkPreviewClient、fetchWithDnsPinnedAddresses、createNetworkGuardedFetch、assertNetworkTargetAllowed、isBlockedNetworkAddress、extractBrowserHtmlContent、YouTube 字幕解析工具parseYoutubeCaptionPayload、rankYoutubeCaptionTracks等以及 URL 识别工具isYouTubeUrl、isDirectMediaUrl、isPodcastHost、shouldPreferUrlMode等。而 prompts/index.ts 则导出buildLinkSummaryPrompt、buildFileSummaryPrompt、buildPathSummaryPrompt、SUMMARY_SYSTEM_PROMPT与长度规格常量。二、内容入口Link Preview Client 与依赖注入content子路径对外暴露的最高层抽象是Link Preview Clientlink-preview/client.tsconst client createLinkPreviewClient(options); const extracted await client.fetchLinkContent(url, contentOptions);createLinkPreviewClient(options)接受一个LinkPreviewClientOptions支持注入几乎所有底层依赖包括fetch自定义 fetch 实现默认globalThis.fetchscrapeWithFirecrawlFirecrawl 抓取回调默认null转录相关密钥falApiKey、groqApiKey、assemblyaiApiKey、deepgramApiKey、elevenlabsApiKey、geminiApiKey、openaiApiKeyytDlpPath本地 yt-dlp 可执行文件路径transcriptCache/mediaCache转录与媒体缓存readTweetWithBird/resolveTwitterCookiesTwitter 内容读取onProgress进度事件回调。fetchLinkContent(url, options)返回ExtractedLinkContent其中携带FetchLinkContentOptions定义的默认值在 content/types.ts 中导出DEFAULT_TIMEOUT_MS、DEFAULT_CACHE_MODE、DEFAULT_MAX_CONTENT_CHARACTERS以及applyContentBudget等内容预算工具。也就是说内容层不仅负责抓到什么还负责抓到多少——超长页面会被预算裁剪applyContentBudget即用于此目的避免把海量正文直接灌进 Prompt。从依赖图看内容提取管线覆盖的输入形态包括普通网页readability / HTML 清洗、YouTube字幕、原生媒体、yt-dlp、播客RSS、Spotify、Apple Podcasts、小宇宙、Twitter、Reddit、本地文件与浏览器 HTMLextractBrowserHtmlContent。三、DNS 钉扎抓取fetchWithDnsPinnedAddressesREADME 用一整段专门描述这套网络层机制它是本库区别于普通fetch封装的核心亮点。DNS 钉扎DNS pinning的含义是提前通过可信渠道解析出目标域名对应的 IP 地址然后在发起 HTTP 连接时绕过系统 DNS、直接向该 IP 发起请求从而防止两次 DNS 解析结果不一致带来的 TOCTOUTime-of-Check to Time-of-Use类 SSRF 绕过。3.1 使用契约README 明确给出三条使用契约fetchWithDnsPinnedAddresses只接受通过attachDnsPinnedAddresses挂载的地址调用方必须先校验这些地址即确认它们不是内网/回环等危险地址或者直接使用createNetworkGuardedFetch来代为完成校验传输层行为细节尊重来自Request对象或其 init 选项的取消信号cancellation对 HEAD 请求以及 HTTP 204/205/304 响应返回nullbody无效的 HTTP 响应会通过 Promise reject 拒绝。import { fetchWithDnsPinnedAddresses, attachDnsPinnedAddresses, } from steipete/summarize-core/content; // 必须先 attach 已验证的地址否则抛错 const response await fetchWithDnsPinnedAddresses(url, attachDnsPinnedAddresses(init, addresses));3.2 实现细节查看 dns-pinned-fetch.ts 的实现绕过环境代理模块顶部创建了directHttpAgent/directHttpsAgent并显式传入{ proxyEnv: {} }。原因在注释中写明DNS pinning must connect directly; an environment proxy would resolve the hostname itself——如果走环境代理代理端会自行解析域名钉扎就失效了。自建 lookupcreatePinnedLookup(addresses)把预先解析的地址列表固化为node:http(s).request的lookup回调all: true时返回全部地址否则返回第一个默认 IPv4 family。限制readDnsPinnedAddresses(init)读不到已挂载地址时直接抛Pinned DNS fetch missing validated addresses带请求体的请求会抛Pinned DNS fetch does not support request bodies。空响应体规则HEAD方法或状态码204/205/304时构造new Response(null, ...)并把流resume()掉避免挂起同时监听error以正确 reject。URL 覆写通过Object.defineProperty(response, url, ...)把响应 URL 固定为请求的url.href手动重定向模式下最终 URL 由守卫层维护。地址的挂载/读取机制实现在 fetch-capabilities.tsattachDnsPinnedAddresses通过Object.defineProperty把地址写到 init 对象上一个以Symbol.for(steipete/summarize.dnsPinnedAddresses)为键的隐藏属性上markFetchAsDnsPinned/resolveDnsPinnedFetch/supportsDnsPinnedFetch则负责标记和识别已被钉扎封装的 fetch 实现isNativeOrBoundGlobalFetch用于判断传入的 fetch 是否为原生实现直接比较globalThis.fetch或通过函数名bound fetch与[native code]特征识别。四、网络守卫createNetworkGuardedFetch单独使用fetchWithDnsPinnedAddresses要求调用方自己完成地址校验这容易出错。因此库提供了更高层的封装createNetworkGuardedFetch(fetchImpl, options)network-guard.ts把解析 → 校验 → 钉扎连接 → 重定向管理整条链路一体化。4.1 目标解析与校验resolveNetworkTarget在每次请求前执行校验 URL 合法且协议必须是http:或https:主机名归一化normalizeNetworkHostname去掉首尾方括号若命中isBlockedNetworkHostnamelocalhost、*.localhost、*.local直接拒绝若主机名本身就是 IPfamily 非 0用isBlockedNetworkAddress校验若是域名则用lookup默认node:dns/promises的dnsLookup(hostname, { all: true, verbatim: true })解析出所有地址任一地址被判定为阻塞地址即整体拒绝。assertNetworkTargetAllowed(rawUrl, { targetLabel, lookup })是这一校验逻辑的公开导出供只想校验不抓取的场景使用。4.2 钉扎分派策略根据解析结果与 fetch 实现类型守卫层选择不同的钉扎路径network-guard.ts域名解析出地址requiresPinnedDns为 true时若底层是原生 fetch则优先使用pinnedFetchImplBun 运行时用fetchWithDnsPinnedAddresses否则用 undici 的fetch并通过attachDnsPinnedAddresses挂载地址、注入一个createPinnedDispatcher创建的 undiciAgent配置autoSelectFamily: true、autoSelectFamilyAttemptTimeout: 250lookup 同样被固化为钉扎地址若底层 fetch 已被标记支持钉扎则resolveDnsPinnedFetch取出其钉扎实现否则抛错requires native fetch for DNS pinning。主机名本身是合法公网 IP无域名解析步骤时退化为普通 fetch。4.3 重定向的重新校验与敏感头剥离这是 README 第二段的全部内容也是安全设计最细腻的部分每个跟随的重定向都重新校验守卫以redirect: manual发起请求收到301/302/303/307/308后取出location用递归调用guardedFetch的方式对新 URL再次执行完整的 resolveNetworkTarget 校验——这样攻击者无法通过首次校验通过、重定向进内网的经典手法绕过。丢弃被放弃的重定向响应体await response.body?.cancel()显式取消旧响应体避免连接资源泄漏。同源重定向保留请求选项与凭据重定向后的新请求沿用原Request对象与 init 中的 headers、method仅 GET/HEAD 允许重定向其他方法抛redirected a non-GET request。跨源重定向剥离敏感头当新 URL 的 origin含协议与端口的变化因为比较的是new URL(location).origin与目标 origin 不一致时会从 headers 中删除authorization、proxy-authorization、cookie、cookie2——防止凭据被重定向到第三方域。手动重定向可用如果调用方传入redirect: manual守卫层不做任何自动跟随直接返回响应由调用者自行消费。const guardedFetch createNetworkGuardedFetch(globalThis.fetch, { targetLabel: link-preview, maxRedirects: 10, // 默认值 }); const response await guardedFetch(url, { signal });NetworkGuardOptions完整字段为targetLabel错误消息中的人类可读标签、lookup自定义 DNS 解析便于测试注入、maxRedirects默认DEFAULT_MAX_REDIRECTS 10、pinnedFetchImpl自定义钉扎 fetch 实现。4.4 底层地址黑名单network-safety.ts 实现了精细的 IPv4/IPv6 黑名单IPv4 阻塞段0.0.0.0/8、10.0.0.0/8、127.0.0.0/8、100.64.0.0/10CGNAT、169.254.0.0/16链路本地、172.16.0.0/12、192.168.0.0/16、192.0.0.0/24、192.0.2.0/24TEST-NET、198.18.0.0/15、198.51.100.0/24、203.0.113.0/24以及 224.0.0.0组播/保留IPv6 阻塞全零地址、回环::1、IPv4 映射地址::ffff:x.x.x.x、翻译地址、兼容地址、NAT6464:ff9b::/96及 well-known 前缀、fc00::/7ULA、fe80::/10链路本地、ff00::/8组播、2001:db8::/32文档、2002::/166to4等。这套黑名单正是isBlockedNetworkAddress、isBlockedNetworkHostname、getNetworkAddressFamily的底层支撑同时被仓库根目录下tests/network-safety.test.ts、tests/network-guard.test.ts、tests/dns-pinned-fetch.test.ts以及security.*系列测试覆盖验证。五、Prompts 入口把提取结果变成高质量提示词prompts子路径的核心职责是把提取的内容 用户意图参数渲染成一份结构化的 LLM 提示词。所有构建器最终都经由 format.ts 的两个基础函数完成组装buildInstructions({ base, overrides })指令合并规则为——若传了promptOverride则整体替换base 指令随后依次追加requiredInstructions如幻灯片模式下的强约束、lengthInstruction、languageInstructionbuildTaggedPrompt({ instructions, context, content })输出instructions.../instructions、context.../context、content.../content三段式模板其中 context 与 content 中的、、会被 HTML 转义防止正文内容破坏标签结构。5.1 五档摘要长度规格summary-lengths.ts 定义了全部长度档位SUMMARY_LENGTH_SPECS每个档位包含引导语、格式要求、目标/最小/最大字符数与最大 token 数档位目标字符数最小字符数最大字符数最大 token 数格式要点short9006001,2007681-2 段共 2-5 句medium1,8001,2002,5001,5361-3 段每段 2-3 句long4,2002,5006,0003,072最多 3 段每段 2-4 句xl9,0006,00014,0006,1442-5 段每段 2-4 句xxl17,00014,00022,00012,2883-7 段每段 2-4 句配套导出SUMMARY_LENGTH_TO_TOKENS每档最大 token、SUMMARY_LENGTH_TARGET_CHARACTERS、SUMMARY_LENGTH_MAX_CHARACTERS、resolveSummaryLengthSpec(length)、formatPresetLengthGuidance(length)生成目标约 N 字符可接受区间 min-max软约束的指导语。5.2 链接摘要构建器buildLinkSummaryPromptlink-summary.ts 是内容层与提示词层衔接的核心函数输入包括 URL、title、siteName、description、提取后的content、truncated标记、hasTranscript是否视频/播客转录、hasTranscriptTimestamps、slides幻灯片时间线、outputLanguage、summaryLength、shares分享者推文以及三组覆写promptOverride/lengthInstruction/languageInstruction。其关键行为长度自适应钳制当summaryLength以{ maxCharacters }传入且大于实际内容长度时会把目标长度钳制为内容长度避免模型过度扩写字符串档位则走formatPresetLengthGuidance。长度档位自动选择pickSummaryLengthForCharacters(max)按 1,200 / 2,500 / 6,000 / 14,000 字符阈值把任意maxCharacters映射到short到xxlestimateMaxCompletionTokensForCharacters用max(256, ceil(chars/4))估算完成 token。标题规则xl/xxl或目标超过 6,000 字符HEADING_LENGTH_CHAR_THRESHOLD时要求使用###前缀的 Markdown 标题分节、至少 3 个标题short/medium则不强制标题。硬性规则行包括绝不提赞助/广告、只允许直引号、不使用 emoji、不臆造细节、基于内容如实作答以及针对转录源的观众画像为想决定是否观看的读者总结视频/转录。分享者语境shares非空时生成Tweets from sharers列表块含 handle、时间戳、点赞/转发/回复数并指示模型仅在推文含实质评论时追加What sharers are saying小节否则省略、绝不捏造。时间戳与幻灯片模式有转录时间戳时强制生成标题为Key moments的小节每条以[mm:ss]/[hh:mm:ss]开头且禁止编造幻灯片模式则要求严格按[slide:N]标记 ##标题的三行块输出并明确禁止输出Title:、Slide 1/10字样。5.3 文件/路径摘要构建器buildPathSummaryPrompt({ kindLabel, filePath, filename, mediaType, ... })cli.ts面向 CLI 场景kindLabel为file或imagecontext 为Path/Filename/Media type三行。buildFileSummaryPrompt附件模式与buildFileTextSummaryPrompt内联文本模式file.ts共同约束为输出中完全禁止引号直引号与弯引号都不行突出金句用单星号斜体当mediaType以audio/或video/开头时自动追加忽略赞助广告的指令。文本模式还会带上Extracted content length硬上限行并支持把目标长度钳制到内容长度。六、实战组合从 URL 到最终提示词的最小闭环把内容层与提示词层串起来一个典型的最小使用流程是import { createLinkPreviewClient } from steipete/summarize-core/content; import { buildLinkSummaryPrompt } from steipete/summarize-core/prompts; const client createLinkPreviewClient({ onProgress: (e) console.log(e) }); const { title, description, content, hasTranscript, truncated } await client.fetchLinkContent(https://example.com/article, { cacheMode: read-write, timeoutMs: 30_000, }); const prompt buildLinkSummaryPrompt({ url: https://example.com/article, title, description, content, truncated, hasTranscript, summaryLength: medium, outputLanguage: { kind: zh }, shares: [], });这份prompt即可直接作为messages[0].content交给任意 LLM 提供商调用。content侧抓取已经默认跑在createLinkPreviewClient内部的网络守卫之上注入的fetch会经过守卫封装因此SSRF 防护对普通使用者是默认开启的。七、适用前提与限制运行环境fetchWithDnsPinnedAddresses依赖 Node 的node:http(s)与 undiciloadUndici守卫层在 Bun 运行时切换为内置的fetchWithDnsPinnedAddresses若自定义 fetch 既非原生也未被markFetchAsDnsPinned标记则域名场景会直接抛错——这是刻意为之的安全失败。能力边界钉扎 fetch 不支持请求体仅 GET/HEAD 会被自动重定向超过maxRedirects默认 10抛redirected too many times。库级事实以上默认值与常量均来自当前仓库源码network-guard.ts、dns-pinned-fetch.ts后续版本如有调整以对应源码与 CHANGELOG.md 为准。八、继续深入想看网络层测试如何覆盖重定向与 SSRF 分支tests/network-guard.test.ts、tests/dns-pinned-fetch.test.ts、tests/network-safety.test.ts、tests/security.remote-media-file-download-cap.test.ts想看内容提取与转录在真实调用链中的位置src/run/、src/application/与packages/core/src/content/link-preview/想看 CLI 如何消费这两个入口根目录src/cli-main.ts与src/application/execute-summarize.ts。steipete/summarize-core的价值在于把抓得安全、抽得干净、问得准确三件事收敛成一个可复用的库底层用 DNS 钉扎 逐跳重定向校验守住网络边界中层用 Link Preview Client 把网页、视频、播客、推文统一成结构化内容上层用带硬性长度/格式/语言约束的 Prompt 构建器把内容翻译成高质量摘要指令。理解了这三层就等于拿到了整个 Summarize 项目的核心引擎钥匙。【免费下载链接】summarizePoint at any URL/YouTube/Podcast or file. Get the gist. CLI and Chrome Extension.项目地址: https://gitcode.com/GitHub_Trending/summarize/summarize创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考