🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

虚构史学家 Kaede0614/dsh-history-fictionologists

DeepSeek Harness
Kaede061438 天前有提交已提供中文文档扫描:含敏感能力

用星穹铁道世界观生成科幻灵感与短篇故事

29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Kaede0614/dsh-history-fictionologists
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · tool
装得上吗
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 38 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 49 处,展示前 44 条)
  • 写入本地文件
    lib/shell.js:1547
    await mkdir(dir, { recursive: true })
  • 写入本地文件
    lib/shell.js:1548
    await writeFile(file, markdown, 'utf8')
  • 写入本地文件
    lib/shell.js:1549
    await writeFile(checkFile, report, 'utf8')
  • 写入本地文件
    lib/shell.js:1594
    await mkdir(dir, { recursive: true })
  • 写入本地文件
    lib/shell.js:1607
    await writeFile(file, front + body, 'utf8')
  • 读取本地文件
    lib/paths.js:25
    return WORKSPACE_MARKERS.some((marker) => existsSync(join(dir, marker)))
  • 读取本地文件
    lib/paths.js:35
    if (existsSync(join(dir, 'package.json'))) return dir
  • 读取本地文件
    lib/paths.js:119
    if (!existsSync(dir)) {
  • 写入本地文件
    lib/paths.js:120
    mkdirSync(dir, { recursive: true })
  • 读取本地文件
    lib/paths.js:143
    exists: Object.fromEntries(Object.entries(dirs).map(([key, value]) => [key, existsSync(value)])),
  • 读取本地文件
    lib/usercanon.mjs:98
    exists = existsSync(path)
  • 读取本地文件
    lib/usercanon.mjs:100
    data = JSON.parse(readFileSync(path, 'utf8'))
  • 读取本地文件
    lib/planets.mjs:306
    exists = existsSync(path)
  • 读取本地文件
    lib/planets.mjs:661
    if (!existsSync(path)) return false
  • 删除本地文件
    lib/planets.mjs:662
    rmSync(path, { force: true })
  • 读取本地文件
    lib/equations.mjs:134
    const raw = JSON.parse(readFileSync(file, 'utf8'))
  • 读取本地文件
    lib/wiki/cache.mjs:47
    if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
  • 写入本地文件
    lib/wiki/cache.mjs:47
    if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
  • 读取本地文件
    lib/wiki/cache.mjs:61
    if (!existsSync(path)) return { ok: false, data: null, missing: true, error: 'missing' }
  • 读取本地文件
    lib/wiki/cache.mjs:62
    const raw = readFileSync(path, 'utf8')
  • 写入本地文件
    lib/wiki/cache.mjs:89
    writeFileSync(tmp, serialized, 'utf8')
  • 写入本地文件
    lib/wiki/cache.mjs:94
    renameSync(tmp, path)
  • 读取本地文件
    lib/wiki/cache.mjs:139
    if (!existsSync(path)) return { exists: false, path }
  • 读取本地文件
    lib/wiki/cache.mjs:140
    const stat = statSync(path)
  • 读取本地文件
    lib/wiki/cache.mjs:151
    if (existsSync(tmp)) rmSync(tmp, { force: true })
  • 删除本地文件
    lib/wiki/cache.mjs:151
    if (existsSync(tmp)) rmSync(tmp, { force: true })
  • 读取本地文件
    lib/atomgit-kit.mjs:133
    const raw = readFileSync(join(root, TOKEN_FILE), 'utf8')
  • 发起网络请求
    lib/atomgit-kit.mjs:327
    const signed = await client.request('GET', path)
  • 读取本地文件
    lib/digest.js:149
    if (!existsSync(file)) return { ok: false, error: `文件不存在:${file}` }
  • 读取本地文件
    lib/digest.js:150
    const value = JSON.parse(readFileSync(file, 'utf8'))
  • 读取本地文件
    lib/digest.js:153
    bytes = statSync(file).size
  • 读取本地文件
    lib/missions.js:175
    if (!existsSync(file)) {
  • 读取本地文件
    lib/missions.js:178
    text = readFileSync(file, 'utf8')
  • 读取本地文件
    lib/missions.js:185
    bytes = statSync(file).size
  • 读取本地文件
    lib/missions.js:228
    if (existsSync(join(preferred, 'index.json'))) return preferred
  • 读取本地文件
    lib/missions.js:230
    const hit = readdirSync(missionsRoot, { withFileTypes: true })
  • 读取本地文件
    lib/missions.js:233
    .find((dir) => existsSync(join(dir, 'index.json')))
  • 读取本地文件
    lib/release-kit.mjs:109
    const raw = readFileSync(join(root, '.gh-token'), 'utf8')
  • 读取本地文件
    lib/release-kit.mjs:132
    const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
  • 读取本地文件
    lib/release-kit.mjs:197
    return { body: readFileSync(join(root, notesFile), 'utf8').trim(), source: notesFile, warnings }
  • 读取本地文件
    lib/release-kit.mjs:204
    const body = readFileSync(join(root, candidate), 'utf8').trim()
  • 读取本地文件
    lib/release-kit.mjs:211
    const section = extractChangelogSection(readFileSync(join(root, 'CHANGELOG.md'), 'utf8'), version)
  • 读取本地文件
    lib/release-kit.mjs:242
    stat = statSync(join(root, rel))
  • 读取本地文件
    lib/wiki/index.mjs:498
    hasCache: existsSync(dir),
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 生成科幻灵感
  • 撰写短篇故事
  • 编造星际新闻

基于《崩坏:星穹铁道》官方世界观的 DSH 二创插件(/gs):神人制造机出科幻灵感、构史文集写短篇、星际构史播报编新闻;12 个 Wiki 数据源增量抓取并本地缓存,抓不到就自动回退缓存。非营利二创,MIT。

README

由 DeepSeek 最新模型翻译生成
dsh-history-fictionologists · 虚构史学家 基于《崩坏:星穹铁道》官方世界观进行二次创作的 DSH 插件。 触发命令:/gs。风格基调:太空轻喜剧 + 一本正经地胡说八道。 核心参考游戏内「差分宇宙 · 方程一览」的想象力与命名逻辑:用最严肃的格式包装最离谱的内容。 能力清单(前置条件) | 能力 | 触发方式 | 前置条件 | | /gs 三步交互入口 | 在 DSH Web 输入框输入 /gs | 插件已装入 profile 的 bundles,进程已重启 | | gs_setup 缓存状态与更新建议 | 模型调用(/gs 第 2 步) | 无 | | gs_update 增量抓取 12 个 Wiki 数据源 | 模型调用 | 需要联网;本机可访问 wiki.biligame.com | | gs_read 读取世界观条目 | 模型调用 | 至少成功抓取过一次(缓存存在) | | gs_missions 既有故事目录(避让冲突) | 模型调用 | 工作区存在 hsr-missions/ | | gs_digest 命名逻辑 / 书架风格 / 播报格式素材 | 模型调用 | equation、broadcast-template 需缓存;mission-digest、book-digest 需 hsr-missions/ |
展开完整文档(README 剩余 1 节)
dsh-history-fictionologists · 虚构史学家

基于《崩坏:星穹铁道》官方世界观进行二次创作的 DSH 插件。
触发命令:/gs。风格基调:太空轻喜剧 + 一本正经地胡说八道。

核心参考游戏内「差分宇宙 · 方程一览」的想象力与命名逻辑:用最严肃的格式包装最离谱的内容。

能力清单(前置条件)

| 能力 | 触发方式 | 前置条件 |
| /gs 三步交互入口 | 在 DSH Web 输入框输入 /gs | 插件已装入 profile 的 bundles,进程已重启 |
| gs_setup 缓存状态与更新建议 | 模型调用(/gs 第 2 步) | 无 |
| gs_update 增量抓取 12 个 Wiki 数据源 | 模型调用 | 需要联网;本机可访问 wiki.biligame.com |
| gs_read 读取世界观条目 | 模型调用 | 至少成功抓取过一次(缓存存在) |
| gs_missions 既有故事目录(避让冲突) | 模型调用 | 工作区存在 hsr-missions/ |
| gs_digest 命名逻辑 / 书架风格 / 播报格式素材 | 模型调用 | equation、broadcast-template 需缓存;mission-digest、book-digest 需 hsr-missions/ |
| gs_planets 「星球列表」(原始 27 颗 + 已并入) | 模型调用(功能 4 第 1 步) | 无(原始列表编译在插件里,不需要网络) |
| gs_planet_save 把新星球并入列表 | 模型调用(用户答「加入」后) | 工作区可写 |
| gs_planet_reset 重置回原始 27 颗 | 模型调用(用户确认后带 confirm: true) | 工作区可写;不带 confirm 只报告状态 |
| gs_save 成品落盘 | 模型调用 | 工作区可写 |
| 系统提示「语言风格总则 + 星神纪律」 | 自动注入 | 无 |

降级行为(已实测):缓存目录不存在时 gs_setup / gs_read 仍返回合法结果(hasCache:false、 recommendation:"update"),不会抛错;/gs 在缺少子模块时仍能启动并如实报告状态; planet-list.json 缺失、损坏或字段非法时,星球列表一律退回插件内置的 27 颗原始列表并在 warnings 里说明(test/planets.test.mjs 覆盖)。

安装

本插件通过 GitHub / AtomGit 仓库与 Release 附件 双平台分发。它不在 npm registry 上—— package.json 里的 private: true 是刻意留的,防止这份二创包被误发到公共 registry。

方式 A(推荐):下载 Release 附件里的 tarball,再从本地路径安装
dsh plugin --profile web add C:\Users\\Downloads\dsh-history-fictionologists-0.3.0.tgz

方式 B:AtomGit 仓库直装(国内网络更稳)
dsh plugin --profile web add https://atomgit.com/Scombriformes/dsh-history-fictionologists

方式 C:GitHub 仓库直装
dsh plugin --profile web add github:Kaede0614/dsh-history-fictionologists

方式 D:本地开发(junction 安装,改源码立即生效)
dsh plugin --profile web add link:

AtomGit 是分发镜像,不是主仓:package.json 的 repository / homepage / bugs 仍指向 GitHub。发布流程与两个平台的 API 差异见
[docs/atomgit-description.md]()。

dsh plugin 是 profile 目录下 pnpm 的透传封装,所以本地 tarball 路径、github: 简写
都由 pnpm 解析;dsh plugin add ... 会重解析整棵依赖树,装进一个干净 profile 最稳。

安装后 必须进程级重启 DSH(新增 bundles 行只在启动时组合;Ctrl+Shift+R 热重载不生效)。

验证是否装载:

dsh --profile web --dump-config | Select-String history-fictionologists

应当看到一行 - id: dsh-history-fictionologists。

仓库里有什么 / 没有什么

| | 内容 |
| 有 | lib/(插件本体)、test/(171 个离线用例)、scripts/check.mjs、docs/、_evidence/(自证与独立复核证据)、BRIEF.md(实现规格)、cordis.patch.yml、hsr-worldview-cache/user-canon.json(手工维护的裁定层) |
| 没有 | hsr-missions/(游戏原始剧本文本,约 20 MB,版权归米哈游)、hsr-worldview-cache/.json(gs_update 可重新抓取)、_probe/(4.9 MB 原始渲染 HTML)、hsr-stories/ 与 hsr-broadcasts/(本机成品) |

被忽略的目录仍留在你的工作区里,只是不进版本库(见 [.gitignore]())。

功能 2 / 3 依赖工作区的 hsr-missions/:没有它插件照样装载, gs_missions 会如实降级报告「目录缺失」,gs_digest 的 mission-digest / book-digest 同理(这条降级有 test/missions.test.mjs 覆盖)。要用全功能,请自备并放到 /hsr-missions/:

json
trailblaze_missions.json 5 幕 / 18 系列 / 147 个开拓任务 adventure_other_tasks_full.json 7 章 / 33 个冒险任务 books_without_amphoreus.json 498 本书(「书架」风格参考) sr-开拓续闻-完整/ 7 个系列 / 52 个开拓续闻任务

怎么触发

1. 在 DSH Web 的输入框输入 /gs(可以附带主题,例如 /gs 欢愉命途的荒诞点子)。
2. 插件会:
- 本地读取缓存与既有故事状态,不发一次模型请求;
- 把「三步交互协议」作为一条 plugin 来源的用户消息交给当前 agent;
- 返回一行简短回执。
3. 模型据此依次弹出:
- 第 1 步 · 功能选择:1 神人制造机 / 2 构史文集 / 3 星际构史播报 / 4 星球制造机(单选弹窗);
- 第 2 步 · 世界观数据更新策略:Y 访问网页重新抓取 / N 使用本地缓存;
- 第 3 步 · 执行所选功能。功能 4 执行完还会再问两件事(见下)。

第 2 步的引导规则

- 本地无缓存 → 不问 Y/N,直接抓取,并告知「本地无缓存,已自动抓取」。
- 缓存较新(距上次更新 星神纪律(0.1.1):命途归属只是气质标签——星神与令使不出场,只能是背景、传闻与缺席;已陨/失踪的星神(贪饕、繁育、秩序、不朽、纯美、开拓)绝不写成在世、现身或归来。
正文写神人自己的职业、麻烦与出路:荒诞来自「命途概念 × 市井生活」的错位,不是「他和星神很熟」。

功能 2 · 构史文集

先读 hsr-missions 的既有故事避免冲突,参考「书架」书籍风格,默认约 2000 字短篇(可指定字数)。荒诞但不轻浮,喜剧底色下可有一丝温情或哲思。

功能 3 · 星际构史播报

约 800–1200 字、3–5 条新闻,固定格式:

(音乐)

女声:这里是星际和平播报,观众朋友们晚上好。
男声:晚上好。

女声:第一条消息。……
男声:第二条消息。……

女声:本次播报到此结束,请在指定时间收听下一周期的星际和平播报。
(音乐)

虚构文本必须足够科幻、足够太空、充满想象力——不是对已有故事的重组。

功能 4 · 星球制造机(0.3.0)

参照「星球列表」造新的星球:列表里的星球只提供世界观坐标与语感,新星球不得与其重名,也不得把既有星球改个说法再交一遍。默认 3 颗(config.planetCount 可调,范围 1–10)。输出格式:

【星域名(English Name)】一句话简介:……(不超过 40 字,写成「地点词条」而不是广告词)详细设定:……(约 60–160 字:靠什么活着、谁在管事、当地人最大的麻烦是什么)可能的故事方向:……(一到两句话)

生成结束后固定问两件事(模型不会替你决定):

| 询问 | 答「要」时发生什么 | 答「不要」时发生什么 |
| ① 是否把这批新星球加入「星球列表」,供下次参考? | gs_planet_save 把这批星球并入列表,下次生成会看到它们 | 不写盘,本次结果不影响以后的生成 |
| ② 是否把「星球列表」重置回原始 27 颗(清除已并入的新星球)? | gs_planet_reset(confirm=true) 清空增量,满意与不满意的批次一起清掉 | 什么也不做 |

「重置」是破坏性操作:gs_planet_reset 不带 confirm: true 时只回报当前状态、绝不改文件,所以模型必须先经你确认才能真的清空。清除的粒度为「全部新增」——只想清掉一部分,请手工编辑 planet-list.json 的 added 数组(见下一节)。

「星球列表」是什么

- 原始列表(27 颗)编译在插件里([lib/planets.mjs]() 的 PLANET_BASELINE_SOURCE),所以「重置回原始」永远拿得回底本——即使工作区文件被删掉或写坏。
- 已并入的新星球写在 /hsr-worldview-cache/planet-list.json 的 added[],与 user-canon.json 同理:gs_update 只重写 .json,永不碰这个文件。
- 判定重名按「星球名或英文名去空格后同名、且英文名不区分大小写」,且原始列表优先:与原始列表同名的候选会被拒绝并在 rejected 里说明撞的是哪一颗(例如 ARIVANTA 撞「阿丽万塔」,或候选的 en 字段撞上原始英文名);不会覆写你的原始设定。与已有增量同名的条目会被更新,便于修正上一轮不满意的描述。
- 一次最多并入 20 颗(MAX_BATCH)。超出的候选不会写入,但会出现在返回值的
overflow: {count, names} 与 warnings 里——模型必须如实转告你,而不是说「都加进去了」。
增量列表总上限 200 颗,到顶后拒绝写入并提示先重置。
- 「出处」标注(visited 已探访 / mentioned 文本提及 / ruined 已毁或失去开拓意义 / unknown / other)是本插件加的,只用于给模型分组参考,不改变用户原文一个字。

世界观数据

已发生的故事(本地,无需抓取)

工作区 hsr-missions/:

| 文件 | 内容 |
| sr-开拓续闻-完整/ | 7 个系列 / 52 个「开拓续闻」任务(含完整台词稿) |
| trailblaze_missions.json | 5 幕 / 18 系列 / 147 个开拓任务 |
| adventure_other_tasks_full.json | 7 章 / 33 个冒险任务 |
| books_without_amphoreus.json | 498 本书(「书架」风格参考) |

功能 2 / 3 生成前必须读它,避免与既有事件冲突(功能 1 与功能 4 不需要)。

背景设定(12 个 Wiki 数据源,需联网抓取)

| id | 页面 | 抓取内容 | 过滤 |
| relics | 遗器图鉴 | 每套遗器的「遗器来历」(各部位 故事) | — |
| lightcones | 光锥图鉴 | 每个光锥的「光锥故事」 | — |
| consumables | 消耗品筛选 | 每个消耗品的「介绍」 | 排除所属地区=翁法罗斯 |
| decorations | 装饰一览 | 每个装饰的「介绍」 | — |
| aeons | 星神 | 每个星神的介绍 | — |
| factions | 派系 | 每个派系的内容 | — |
| terms | 专有名词 | 每个专有名词的内容 | — |
| simuniverse | 模拟宇宙 | 「模拟宇宙图鉴 → 星神」里的开发日志 | — |
| curios | 奇物一览(差分) | 每个奇物的介绍/效果 | — |
| events | 事件一览 | 每个事件的内容 | 排除模式含「千面英雄」 |
| equations | 方程一览 | 每个方程的内容(效果全文) | 排除模式含「千面英雄」 |
| broadcast | 星际和平播报 | 全部内容 | — |

抓取礼仪:相邻请求间隔 ≥ requestIntervalMs(默认 2000 ms),带浏览器 UA 与 Referer,非 JSON 响应(WAF 会返回 HTTP 567 的错误页)走指数退避重试,重试耗尽则保留旧缓存并如实上报。

缓存目录

/hsr-worldview-cache/

hsr-worldview-cache/
├── index.json # 每个数据集的状态:lastUpdated / count / revisionId / ok / error ├── equations.json # 每个数据集一个文件,字段见下 ├── aeons.json ├── factions.json ├── terms.json ├── relics.json ├── lightcones.json ├── consumables.json ├── decorations.json ├── curios.json ├── events.json ├── simuniverse.json ├── broadcast.json ├── user-canon.json # 用户设定补充(手工维护,抓取永不改写) └── planet-list.json # 「星球列表」增量(功能 4 写入;重置=清空 added[])

planet-list.json 结构(baseline 是原始 27 颗的快照,仅供人工核对;程序只读 added[]):

{
"schema": "dsh-history-fictionologists/planet-list@1",
"updatedAt": "2026-09-27T10:00:00.000Z",
"baselineCount": 27,
"addedCount": 1,
"baseline": [ { "name": "阿丽万塔", "en": "Arivanta", "description": "…", "source": "visited" } ],
"added": [
{ "id": "added-1", "name": "洛珂萨", "en": "Loxa", "description": "…", "source": "mentioned", "addedAt": "2026-09-27T10:00:00.000Z" } ] }

单个缓存文件结构:

{
"dataset": "equations",
"title": "方程一览", "url": "https://wiki.biligame.com/sr/方程一览", "lastUpdated": "2026-09-26T02:00:00.000Z", "revisionId": 12345, "pageSha": "…", "count": 212, "excludedCount": 112,
"entries": [ { "name": "…", "content": "…" } ],
"warnings": []
}

提示:每个缓存文件都带 lastUpdated 时间戳。删掉整个目录就是彻底重置;想强制重抓某一天数据,用 gs_update 的 force: true(或删掉对应的单个 json 文件)。

工作区解析优先级:插件 config.workspace → DSH_HISTORY_FICTIONOLOGISTS_WORKSPACE → DSH_WORKSPACE → 当前工作目录(或其上层含 hsr-missions 的目录)→ 插件包所在目录。

用户设定补充(user-canon.json)

缓存是「抓来的事实」,你自己的设定裁定是另一层——它优先于缓存。

- 位置:/hsr-worldview-cache/user-canon.json,手工维护。gs_update 只重写 .json,永远不会碰这个文件(所以裁定不会被下一次抓取抹掉)。
- 生效方式:gs_read 结果末尾附上「用户设定补充」区块,并明确标注「冲突时以此为准」; gs_setup 报告条数与更新时间;系统提示的「语言风格总则」也写明了它的优先级。
- 作用域:dataset 写数据集 id 时只在该数据集生效(aeons / aeon 都认),写 "" 或省略则对全部数据集生效;match 是触发关键词(命中 gs_read 的 query,或与该数据集返回的条目同屏出现时带出);query 为空时,该数据集名下的裁定一律带出。
- note 必填:没有 note 的条目会被忽略并告警——空口裁定不算设定。

{json
"schema": "dsh-history-fictionologists/user-canon@1",
"updatedAt": "2026-09-26T03:30:00.000Z",
"entries": [
{
"dataset": "aeons",
"match": ["贪饕", "奥博洛斯"],
"name": "「贪饕」,奥博洛斯", "status": "已镇压(不得写成在世/失踪待返/即将归来)", "note": "奥博洛斯早已四分五裂、被镇压。缓存里的「已失踪无影」只是旧传闻,不得当作现状。", "source": "用户评审(2026-09-26)" } ] }

文件缺失 = 没有裁定(正常状态);文件损坏或 JSON 非法 = 降级为「0 条 + 告警」,
不影响缓存读取与其它工具。读取逻辑在 [lib/usercanon.mjs](),
测试在 [test/usercanon.test.mjs]()。

成品落在哪

| 类型 | 目录 |
| 短篇小说(功能 2) | /hsr-stories/-.md |
| 星际和平播报(功能 3) | /hsr-broadcasts/-.md |
| 灵感(功能 1) | /hsr-stories/inspirations/-.md |

文件带 YAML front-matter(kind / title / createdAt / generator)。
把 config.saveOutputs 设为 false 可关闭落盘。

配置项

在 profile 的 cordis.patch.yml 里按 id 覆盖(整行 config 会被替换,所以要把需要的键都写上): yaml
- id: dsh-history-fictionologists
config:
workspace: '' # 留空自动解析 requestIntervalMs: 2000 # 相邻 Wiki 请求最小间隔(毫秒) requestTimeoutMs: 30000 maxRetries: 3 staleAfterDays: 7 # 超过该天数即建议重新抓取 recentGuardDays: 7 # 不足该天数时,选 Y 也先确认一次 defaultStoryWords: 2000 defaultBroadcastWords: 1000 inspirationCount: 4 # 3–5 planetCount: 3 # 星球制造机默认颗数(1–10) saveOutputs: true userAgent: 'Mozilla/5.0 …'

目录结构(插件本体)

dsh-history-fictionologists/
├── package.json # dsh.bundle.patch 指向 cordis.patch.yml;main = lib/shell.js ├── cordis.patch.yml # bundle 挂载声明 ├── lib/ │ ├── shell.js # 插件外壳:/gs 命令、9 个工具、风格提示词、Config(包入口) │ ├── resolve.js # @deepseek-ai/ 可选依赖的多 base 解析链 │ ├── paths.js # 工作区 / 缓存 / 成品目录解析 │ ├── missions.js # hsr-missions 读取与索引 │ ├── digest.js # 命名逻辑 / 书架风格 / 播报格式素材 │ ├── usercanon.mjs # 用户设定补充(优先于抓取缓存) │ ├── planets.mjs # 原始 27 颗星球底本 + 增量列表读写/重置 │ └── wiki/ # 12 个数据源的抓取与解析 │ ├── datasets.mjs # 数据源登记表(含单复数 id 别名) │ ├── client.mjs # 限流 / 重试 / WAF 识别 │ ├── html.mjs # 通用 HTML 解析 │ ├── wikitext.mjs # {{模板|字段=值}} 解析 │ ├── extract.mjs # 12 个抽取器 │ ├── cache.mjs # 原子缓存 + revid 短路 │ └── index.mjs # status / update / read / sampleAll ├── test/ # node:test:离线重放 + mock ctx + 宿主校验器一致性(无网络) ├── docs/DESIGN.md # 数据源结构实测记录(抓取选择器依据) └── CHANGELOG.md

入口文件叫 lib/shell.js 而不是惯用的 lib/index.js:一次编辑事故把后者写成了非 UTF-8 字节,文件系统观察策略(正确地)拒绝再覆写一个读不出来的路径,于是安全修复是换一个干净文件名并把 package.json 的 main/exports 指过去。行为与 index.js 完全等价。

故障排查

| 现象 | 原因 / 处理 |
| 输入 /gs 没有补全、没有反应 | bundles 行未生效:确认 --dump-config 里有该行,然后进程级重启 |
| --dump-config 报告跳过该 bundle | 报的是原因(多半是解析不到包)。确认 node_modules/dsh-history-fictionologists 链接或已 dsh plugin add |
| --dump-config 报错、整棵插件树崩 | Config 被导出成普通对象(本插件已规避;若你改过 lib/shell.js 请检查) |
| 工具「静默消失」 | @deepseek-ai/ 解析失败。node -e "import('./lib/resolve.js').then(m=>console.log(m.resolutionReport()))" 看解析链 |
| 工具报 returned invalid output | 某个工具返回值违反了它自己声明的 schema(例如可选键出现了 null)。跑 node --test test/host-validator.test.mjs 会直接指出是哪一个 |
| 抓取全部失败 | 网络不通,或请求过于频繁被 WAF(HTTP 567)拦截。调大 requestIntervalMs 后重试 |
| 模型说「未读取到数据」 | 先跑一次 gs_update;或检查 config.workspace 是否指向了正确的工作区 |
| 想看抓取细节 | 缓存目录的 index.json 里有每个数据集的 ok / error / count / revisionId |
| 功能 4 生成的星球没进列表 | 看 gs_planet_save 返回里的 rejected:与原始 27 颗重名的候选会被拒绝(故意不覆写原始设定) |
| 「星球列表」被清空或写坏 | 原始 27 颗编译在插件里,不会丢:调用 gs_planet_reset(confirm=true) 即可重建文件 |
| 模型没问「是否并入 / 是否重置」 | 这两问同时写在系统提示与 /gs 协议文本里;直接提醒它「按功能 4 的收尾问两件事」 |

自检命令 powershell cd
node scripts/check.mjs # 首选:语法检查全部模块 + 跑全套测试(约 1.5 秒)
node --test # 只跑测试(Node 自动发现 test/ 下所有 .test.mjs)
node _evidence/e2e-wiring.mjs # 真实 Wiki 上跑通工具接线(会联网,约 15 秒)
node _evidence/run-extract.mjs --live # 12 个数据源真机抓取取证(会联网,约 60 秒)
node _evidence/verify-gs-e2e.mjs # 隔离实例里端到端验证 /gs(约 15 秒)

为什么不用 npm test:这台机器上 PATH 里只有 npm.ps1,而 PowerShell 执行策略禁止运行它,所以 npm test 在 PowerShell 里直接报 running scripts is disabled(用 cmd /c "npm test" 可以跑通)。
另外 node --test test/ 这种带斜杠的写法在 Node 24 上会失败(Cannot find module ...\test: Node 不会替 npm script 展开 glob),所以 package.json 的 test 用的是裸 node --test 自动发现。
为了让自检不依赖 npm,才把检查逻辑放进 scripts/check.mjs。

测试分层(每一层都补上一层的盲区):

| 层 | 文件 | 能证明什么 | 证明不了什么 |
| 契约 | test/plugin.test.mjs | 注册物齐全(9 工具 / 1 命令 / 1 section)、/gs handler 在异常与降级输入下不抛、规范 JSON | 返回值是否符合 output.schema(mock 不校验) |
| 宿主校验 | test/host-validator.test.mjs | 用宿主自己的 validateJsonSchemaValue 校验 9 个工具的全部降级路径;含元测试证明该断言会失败;含 args 层拒绝、卸载/重载与 stub 接缝的隔离断言 | 真实进程内的注册成功 |
| 解析 | test/wiki.test.mjs | 12 个抽取器对真实 HTML 的选择器正确性(离线重放)+ revid 短路 + WAF 重试 + 注入时钟的确定性限流断言 | 真机网络行为 |
| 连续性 | test/missions.test.mjs | hsr-missions 三种 JSON 的读取、截断上界、缺失降级 | — |
| 星球列表(0.3.0) | test/planets.test.mjs | 原始 27 颗与用户原文逐条一致、读取降级、重名规则(原始优先 + 星球名/英文名 + 大小写不敏感)、超限候选不静默丢弃、截断有告警、原子写、重置、上限、三个星球工具的 schema/渲染/无损 JSON、/gs 协议的两问 | 真机上模型是否照做(那取决于模型) |

全部离线(每个 new WikiClient 都注入了假 fetch,套件里 globalThis.fetch 一次都不会被调用)。
需要联网的验证在 _evidence/(run-extract.mjs --live、cache-roundtrip.mjs、e2e-wiring.mjs)。

在哪台机器上跑得出什么

test/ 里有一层用例重放本地夹具(_probe/html/.html)与真实语料(hsr-missions/)——这两份数据按体积与版权考虑不进版本库(见 .gitignore)。所以同一套用例在不同检出里给出的数字不同:

| 检出 | 命令 | 结果 |
| 作者工作区(_probe/ 与 hsr-missions/ 都在) | node --test | 171 用例 / 170 pass / 0 fail / 1 skip(2026-09-27 实测;唯一 skip 是「缓存已存在时不再跑降级断言」) |
| 全新克隆(两者都不在) | node --test | 用例总数相同,其中「重放 _probe/ 夹具 / 依赖 hsr-missions/」的那些显式 skip(每一条都带原因) |

夹具/语料缺失导致的每一条 skip 都带原因,直接印在输出里,例如:

﹣ update: unknown ids and a broken client degrade into failed[] # _probe/html/遗器图鉴.html, _probe/html/光锥图鉴.html, _probe/html/消耗品筛选.html, _probe/html/装饰一览.html (+8 more) 不在本次检出中(见 .gitignore)

设计口径:夹具缺失 → 显式 skip;从不静默通过,也从不弱化断言。
所有与磁盘无关的用例(client: 限流与 WAF 重试、HTML/wikitext 解析、合成缓存降级、 gs_ 工具输出的规范 JSON 与宿主校验器一致性、test/planets.test.mjs 的全部 22 条……)
在任何检出里都照跑。

唯一的例外是 test/host-validator.test.mjs:这一层要解析宿主的 validateJsonSchemaValue,所以前置条件是本机装过 dsh(解析基座见
[lib/resolve.js]())。没装时它是硬失败而不是 skip——
实测 10 fail / 2 pass / 0 skip。这是刻意的:这一层存在的意义正是证明其余断言不是恒真的,让它静默跳过就等于把「已验证」变成一句空话。装上 dsh 后这 10 条照跑。

想要全量(含夹具与语料层):把 _probe/(docs/DESIGN.md 里有每个页面的抓取依据)与 hsr-missions/ 准备好,或直接在作者工作区里跑。

最关键的一条产品证据(可复现):node _evidence/check-prompt-section.mjs

它从隔离实例的真实会话日志里解析出注入记录,证明模型确实收到了这份指导,而不只是「插件注册成功」:

record type=system/message role=system chars=3500 口径提醒(复核者 R3-5 纠正,我先前表述有夸大):3500 是整条系统提示的长度,不是风格总则的长度。
复核者另做了更强的一致性检查:日志里的风格文本 includes(STYLE_GUIDE) === true,即与抓取当次的源码逐字节一致。

版本提醒:上面这段是 0.1.0 的快照,当时 STYLE_GUIDE 为 725 字符。此后每次改提示词长度都会变: 0.1.1(加入「星神纪律」)为 1161 字符 / 55 行,0.2.1 为 1291 字符 / 57 行,当前源码(0.3.0,加入「星球制造机」格式与两问纪律)为 1748 字符 / 72 行。
_evidence/prompt-section-injection.txt 仍是 0.1.0 那次抓取,对 0.3.0 已过期;要刷新它需要在隔离实例里重跑 node _evidence/check-prompt-section.mjs(本机日常实例不重启)。
0.3.0 这条链路由 test/planets.test.mjs 的两条离线断言兜底:系统提示总则里必须出现星球格式与两问, /gs 协议文本里必须出现四个功能与两问。

开发/取证目录(不属于插件运行时,不随包发布)

| 目录 | 内容 |
| _probe/ | 抓取前的结构侦察产物:12 个页面的原始渲染 HTML、结构普查文本、真实 wikitext 样本。是 docs/DESIGN.md 里每条选择器的原始证据 |
| _evidence/ | 自证与复核证据:离线重放报告、真机抓取日志、缓存往返日志、接线检查、端到端验证脚本与输出、独立复核报告 review-shell.md |
| BRIEF.md | 实现规格(数据源结构实测记录 + 接口契约)。有勘误表,见文件头的回填说明 |

隔离实例端到端验证(绝不动你自己的 ~/.dsh):

node _evidence/verify-gs-e2e.mjs # 默认路径,约 15 秒
node _evidence/verify-gs-e2e.mjs --model # 额外让模型真调一次 gs_setup(隔离 home 无模型路由,会超时)
1) 建一个临时 DSH_HOME + web profile(bundles 含本插件)
2) 起 dsh --profile web --port 3081 --no-open
3) 用启动 token 换签名 cookie
4) session/create → commands/list(确认 /gs 出现)→ commands/execute "/gs"
5) 收尾:taskkill 自己的子进程树 + 杀掉「测试端口」的持有者

诚实边界:--model 那一步在隔离 home 里必然超时——启动 token 与模型路由都属于你自己的实例,隔离 home 两者都没有(实测:prompt 被接受后 180 秒内会话日志始终为空)。该契约因此由 test/host-validator.test.mjs(用宿主同一个校验函数,离线)与你实例里那次真实调用共同覆盖:插件首次装好后模型确实调到了 gs_setup,而正是那一次暴露了 lastUpdated: null 的 schema bug。

⚠️ 收尾只用 taskkill /PID /T 加「测试端口持有者」。不要改成 Get-Process node | Stop-Process——开发过程中这么写了一次,把用户正在使用的 dsh 实例一起杀掉了(已修正,教训记在 _evidence/verify-gs-e2e.ps1 文件头与脚本注释里)。

版权

插件源代码与文档以 MIT 许可发布,见 [LICENSE]()。
[NOTICE]() 另外声明了第三方归属:《崩坏:星穹铁道》的游戏文本与世界观设定版权归
米哈游(HoYoverse),Wiki 页面文本版权归 Bwiki 编辑者;MIT 不覆盖这些素材。

原始文本版权归米哈游(HoYoverse)及 Bwiki 编辑者所有。本插件产出的内容属于非营利性二创,米哈游对二创持开放态度。仅供个人学习、检索与研究使用。
预览分享卡片长什么样 ↓Kaede0614/dsh-history-fictionologists
分享 · DeepSeek Harness Hub
Kaede0614/dsh-history-fictionologists

用星穹铁道世界观生成科幻灵感与短篇故事

这是你的仓库?认领条目

回到顶部