galbook 0.0.0-stage → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.agents/skills/galbook/SKILL.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +114 -0
  4. package/README.md +133 -3
  5. package/bin/galbook.js +53 -0
  6. package/cordis.patch.yml +30 -0
  7. package/dsh-plugin/README.md +49 -0
  8. package/dsh-plugin/install.py +190 -0
  9. package/galbook/__init__.py +15 -0
  10. package/galbook/__main__.py +138 -0
  11. package/galbook/build.py +89 -0
  12. package/galbook/cli.py +29 -0
  13. package/galbook/demo/fog-harbor/assets/cg_dawn_together.svg +1 -0
  14. package/galbook/demo/fog-harbor/assets/cg_old_letter.svg +1 -0
  15. package/galbook/demo/fog-harbor/assets/cg_ship.svg +1 -0
  16. package/galbook/demo/fog-harbor/assets/harbor_night.svg +1 -0
  17. package/galbook/demo/fog-harbor/assets/lamp_room.svg +1 -0
  18. package/galbook/demo/fog-harbor/assets/lamp_room_dawn.svg +1 -0
  19. package/galbook/demo/fog-harbor/assets/path_tide.svg +1 -0
  20. package/galbook/demo/fog-harbor/assets/shio_normal.svg +1 -0
  21. package/galbook/demo/fog-harbor/assets/shio_smile.svg +1 -0
  22. package/galbook/demo/fog-harbor/assets/shio_surprised.svg +1 -0
  23. package/galbook/demo/fog-harbor/assets/warehouse.svg +1 -0
  24. package/galbook/demo/fog-harbor/story.gal +97 -0
  25. package/galbook/demo/fog-harbor-en/assets/cg_dawn_together.svg +1 -0
  26. package/galbook/demo/fog-harbor-en/assets/cg_old_letter.svg +1 -0
  27. package/galbook/demo/fog-harbor-en/assets/cg_ship.svg +1 -0
  28. package/galbook/demo/fog-harbor-en/assets/harbor_night.svg +1 -0
  29. package/galbook/demo/fog-harbor-en/assets/lamp_room.svg +1 -0
  30. package/galbook/demo/fog-harbor-en/assets/lamp_room_dawn.svg +1 -0
  31. package/galbook/demo/fog-harbor-en/assets/path_tide.svg +1 -0
  32. package/galbook/demo/fog-harbor-en/assets/shio_normal.svg +1 -0
  33. package/galbook/demo/fog-harbor-en/assets/shio_smile.svg +1 -0
  34. package/galbook/demo/fog-harbor-en/assets/shio_surprised.svg +1 -0
  35. package/galbook/demo/fog-harbor-en/assets/warehouse.svg +1 -0
  36. package/galbook/demo/fog-harbor-en/story.gal +97 -0
  37. package/galbook/demo_boot.py +63 -0
  38. package/galbook/engine.py +325 -0
  39. package/galbook/expr.py +107 -0
  40. package/galbook/i18n.py +85 -0
  41. package/galbook/importers/__init__.py +106 -0
  42. package/galbook/importers/common.py +94 -0
  43. package/galbook/importers/galscript.py +218 -0
  44. package/galbook/importers/kag.py +326 -0
  45. package/galbook/importers/legacy.py +122 -0
  46. package/galbook/importers/nscripter.py +314 -0
  47. package/galbook/importers/qlie.py +498 -0
  48. package/galbook/importers/renpy.py +333 -0
  49. package/galbook/importers/textdump.py +128 -0
  50. package/galbook/ir.py +266 -0
  51. package/galbook/mcp_server.py +102 -0
  52. package/galbook/play.py +374 -0
  53. package/galbook/server.py +199 -0
  54. package/galbook/static/play.html +151 -0
  55. package/galbook/store.py +160 -0
  56. package/galbook/tools.py +99 -0
  57. package/galbook/web.py +316 -0
  58. package/icon.svg +21 -0
  59. package/locale/en.json +6 -0
  60. package/locale/zh.json +6 -0
  61. package/package.json +58 -6
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: galbook
3
+ description: Play a visual novel / galgame like a human player with galbook — read the story line by line, make choices with a reason, save/load/rewind, chase endings. Use when the user asks you to play, continue, or replay a galgame/visual novel/AVG, or mentions galbook, a book slug such as fog-harbor, endings, routes or save slots.
4
+ ---
5
+
6
+ # 玩 galgame(galbook)
7
+
8
+ 你是一个**玩家**,不是剧本分析器。像第一次玩的人一样:读、感受、做选择、为选择负责。
9
+
10
+ ## 怎么操作(按可用性选一种)
11
+
12
+ 1. **有 MCP 工具时**(工具名形如 `mcp__galbook__galbook_next`):直接调用。
13
+ - `galbook_help` 看规则 → `galbook_persona(text=…)` 设人设 → `galbook_new` → 反复 `galbook_next(n=20)` → 遇到选项 `galbook_choose(i=…, reason=…)`
14
+ - 存读档:`galbook_save(slot=…)` / `galbook_load(slot=…)`;后悔:`galbook_rewind`;进度:`galbook_gallery`
15
+ 2. **没有 MCP、但能跑 shell 时**:用 CLI(所有参数写成 k=v)。
16
+ ```bash
17
+ export GALBOOK_BOOKS=/path/to/books GALBOOK_DATA=/path/to/play-data GALBOOK_BOOK=fog-harbor GALBOOK_AGENT=<你的名字>
18
+ python3 -m galbook do books # 有哪些书
19
+ python3 -m galbook do new hints=1 # 新游戏(可选 mode=ironman h=skip lang=en)
20
+ python3 -m galbook do next n=20
21
+ python3 -m galbook do choose i=2 reason="她看起来很累,我想陪着她"
22
+ python3 -m galbook do save slot=before-confession
23
+ python3 -m galbook do gallery
24
+ ```
25
+ Windows 上把 `python3` 换成 `python`,`export` 换成 `$env:GALBOOK_BOOKS="…"`。
26
+ 3. **只能发 HTTP 时**:`GET http://<服务>/play/t` 读说明,然后 `GET /play/t/<book>/<你的名字>?do=next`。
27
+
28
+ 每次返回的最后一行会告诉你下一步怎么写,照着做就行。
29
+
30
+ ## 规则(重要)
31
+
32
+ - **不要直接打开 `books/*/story/` 下的 JSON 或原始剧本文件**,那等于翻到书的最后一页。只通过上面的接口读。
33
+ - 遇到选项先停一下:回想人物说过的话、你的人设,再选。`reason` 写一句真实的想法(会被记下来,别人能看到)。
34
+ - 一次读 10–30 句,不要一口气 `n=500` 跳过剧情;除非用户要求,不用 `skip=1`。
35
+ - 第一遍别开 `hints`;想看别的结局时再 `rewind`、读档或 `new`。
36
+ - 铁人模式(`mode=ironman`)下不能存读档和回溯,选了就认。
37
+ - H 场景默认折叠;用户没要求就不要改成 `h=full`。
38
+ - 读到结局后,用两三句话告诉用户:你走了哪条路、为什么这么选、感受如何;问要不要再玩一遍。
39
+
40
+ ## 出错时
41
+
42
+ - `没有这本书`:先 `do books` / `galbook_books` 看有哪些 slug。
43
+ - `还没开始`:先 `new`,或 `load slot=auto`。
44
+ - `现在没有选项可选`:先 `next`。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 galbook contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,114 @@
1
+ # galbook
2
+
3
+ **Play a galgame / visual novel like a human would**: read one line at a time, pick choices yourself and write down why, save, load, rewind to the last choice. Ships an MCP server, an HTTP server and a CLI, plus a DeepSeek Harness (dsh) bundle.
4
+ Hidden routes unlock across playthroughs, and many agents can play the same book at once with independent progress.
5
+
6
+ [中文](README.md) · pure Python standard library, no dependencies · MIT
7
+
8
+ ## Four ways in
9
+
10
+ | Way | Best for | Start |
11
+ |---|---|---|
12
+ | **Plain-text HTTP** | any agent that can send a request; cheapest in tokens | `python3 -m galbook serve --books books`, then `GET /play/t?agent=name` to read, `?do=next` / `?do=choose&i=2` to advance |
13
+ | **JSON API** | when you want structured fields | same server, `/play/?format=json` |
14
+ | **MCP** | Claude Desktop, Codex-style clients, DeepSeek Harness | stdio: `python3 -m galbook mcp`; Streamable HTTP also supported |
15
+ | **CLI** | agents with nothing but a shell | `python3 -m galbook do next n=20`; the last line tells the next command |
16
+
17
+ Full protocol in [docs/AGENTS.md](docs/AGENTS.md); dump OpenAI function-calling definitions with `python3 -m galbook tools`.
18
+
19
+ > **Using DeepSeek Harness (dsh)?** Plugin panel → install → source `github:CHET-LI/DSH-galgame-galbook`, restart, and you get 15 `galbook_*` tools. If GitHub is unreachable use a local folder or `galbook-1.2.4.tgz` — see [dsh-plugin](dsh-plugin/).
20
+
21
+ > **Offline packages?** [Releases](https://github.com/CHET-LI/DSH-galgame-galbook/releases/latest) has `galbook-1.2.3.zip` (source), `galbook-1.2.3.tar.gz` (no unzip mojibake on Linux), `galbook-1.2.4.tgz` (npm package).
22
+
23
+ - Pure Python standard library, zero dependencies
24
+ - Ways in: **plain-text HTTP** (cheapest in tokens, works with any agent), **JSON API**, **MCP** (stdio and Streamable HTTP), a **CLI** for shell-only agents, a **Skill** file (Anthropic format), plus exported **OpenAI function-calling** tool schemas
25
+ - Ready-made **DeepSeek Harness (dsh)** integration — MCP, skill and CLI all tested, see [integrations/deepseek-harness](integrations/deepseek-harness/)
26
+ - A web page to watch what each agent is doing, what it chose and why — humans can play too (web UI is Chinese for now; the text API, MCP and tools are bilingual)
27
+ - Importers: QLIE, Kirikiri KAG / TyranoScript, Ren'Py, NScripter, and a hand-written `.gal` format; text dumps from VNTextPatch and similar tools (20+ engines, linear only); and a plugin API for new engines
28
+
29
+ ![Three agents playing at once; watching one of them in the web UI](docs/img/watch.png)
30
+
31
+ > ⚠️ **Copyright**: this repo contains only code and self-written demo stories. Game scripts, images and music belong to their original creators. Only import games you legally own, and **do not upload or distribute** the converted scripts or images. galbook ships no unpacking/decryption tools and never runs game executables.
32
+
33
+ ## Try it in 30 seconds
34
+
35
+ ```bash
36
+ git clone <this repo> galbook && cd galbook
37
+ python3 -m galbook demo --books books # installs the demos: fog-harbor (Chinese), fog-harbor-en (English)
38
+ python3 -m galbook serve --books books --lang en # http://127.0.0.1:8765/play/
39
+ ```
40
+
41
+ An agent only needs HTTP GET:
42
+
43
+ ```
44
+ GET http://127.0.0.1:8765/play/t?lang=en ← how to play (for the agent)
45
+ GET http://127.0.0.1:8765/play/t/fog-harbor-en/alice?do=new&lang=en
46
+ GET http://127.0.0.1:8765/play/t/fog-harbor-en/alice?do=next
47
+ GET http://127.0.0.1:8765/play/t/fog-harbor-en/alice?do=choose&i=2&reason=she+looks+tired
48
+ ```
49
+
50
+ A party of agents with different personalities (runs without any model; add `--decider llm` for any OpenAI-compatible API):
51
+
52
+ ```bash
53
+ python3 examples/party.py --book fog-harbor-en
54
+ ```
55
+
56
+ ## Diversity options
57
+
58
+ | Option | How | What |
59
+ |---|---|---|
60
+ | Adult scenes | `new&h=fold` (default) / `full` / `skip` | fold to a one-line note / show / skip entirely |
61
+ | Ironman | `new&mode=ironman` | no save, load or rewind this run — live with your choices |
62
+ | Hints | `new&hints=1` | mark options as [new] / [picked before]; off by default (feels like a first playthrough) |
63
+ | Language | `new&lang=en`, or `&lang=en` on any request | English text interface |
64
+ | Persona | `do=persona&text=timid, hates risks` | reminded to the agent at every choice |
65
+ | Random | `choose&i=random` | roll a die (baselines, load tests) |
66
+ | Gallery | `do=gallery` | endings / routes / CG / read ratio |
67
+ | Human-likeness | `do=profile` | lines read before choices, reason rate, think time, always-first-option |
68
+ | Skip read | `next&skip=1` | fast-forward to unread text |
69
+ | Images | `next&img=1` | URLs of current background / sprites / CG for multimodal agents |
70
+
71
+ ## Connect your agent
72
+
73
+ - **Any HTTP-capable agent**: the text API above. `/play/t?lang=en` can go straight into a system prompt.
74
+ - **MCP (stdio)** (Claude Desktop, Cursor, Cline, dsh, …): `python3 -m galbook mcp --books books --data play-data --book fog-harbor-en --agent alice --lang en` — see [examples/mcp_config.example.json](examples/mcp_config.example.json)
75
+ - **MCP (Streamable HTTP)**: `serve` exposes `POST /mcp?book=…&agent=…` — one server, many agents, works with tokens
76
+ - **DeepSeek Harness**: [integrations/deepseek-harness](integrations/deepseek-harness/) (patch config, skill, a no-API-key self-test)
77
+ - **Skill**: [.agents/skills/galbook/SKILL.md](.agents/skills/galbook/SKILL.md) — copy into `~/.agents/skills/` (dsh) or `.claude/skills/`
78
+ - **OpenAI function calling**: `python3 -m galbook tools --format openai > tools.json`, execute with `galbook.tools.call_tool(...)`
79
+ - **CLI**: `GALBOOK_BOOKS=books GALBOOK_DATA=play-data GALBOOK_BOOK=fog-harbor-en GALBOOK_AGENT=alice python3 -m galbook do next n=20 --lang en` (the last line of output is the next command)
80
+
81
+ Details: [docs/AGENTS.md](docs/AGENTS.md) (Chinese with English examples).
82
+
83
+ **Public deployment**: binds 127.0.0.1 by default. Use `--token SECRET`, or `--tokens tokens.json` (`{"key1": "alice", "admin-key": "*"}` — each key can only play as its own name).
84
+
85
+ ## Import your own games
86
+
87
+ ```bash
88
+ python3 -m galbook detect path/to/extracted
89
+ python3 -m galbook build --src path/to/extracted --out books/mygame/story --assets-dir books/mygame
90
+ ```
91
+
92
+ | Engine | Support |
93
+ |---|---|
94
+ | QLIE (`.s`) | branching ✅ |
95
+ | Kirikiri KAG / TyranoScript (`.ks`) | branching ✅ |
96
+ | Ren'Py (`.rpy`; decompile `.rpyc` with unrpyc first) | branching ✅ |
97
+ | NScripter / ONScripter (`0.txt` / `nscript.dat`) | branching ✅ |
98
+ | `.gal` (built-in hand-written format) | branching ✅ |
99
+ | AdvHD, Artemis, BGI, CatSystem2, Majiro, RealLive, Softpal, SystemNNN, YU-RIS, Silky's, … | linear 📖 via [VNTextPatch](https://github.com/arcusmaximus/VNTranslationTools) JSON + `--engine textdump` (format implemented from VNTextPatch's source; not yet verified against a real game's export) |
100
+ | anything else | write a plugin — see [docs/IMPORTERS.md](docs/IMPORTERS.md) |
101
+
102
+ Archives (`.xp3`, `.pack`, `.rpa`, …) must be extracted first with tools such as GARbro.
103
+
104
+ ## Tests
105
+
106
+ ```bash
107
+ python3 -m unittest discover -s tests -v
108
+ ```
109
+
110
+ Tested on Python 3.9 / 3.12 / 3.13 (Linux) and Python 3.12 (mini PC) / 3.14 (Windows) with the same suite.
111
+
112
+ ## License
113
+
114
+ Code: MIT ([LICENSE](LICENSE)). Demo stories and art in `galbook/demo/`: CC0.
package/README.md CHANGED
@@ -1,3 +1,133 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # galbook
2
+
3
+ **让 agent 像人一样玩 galgame / 视觉小说**:一句一句读剧情,遇到选项自己选并写理由,存档、读档、回到上个选项、多周目解锁隐藏线。很多 agent 可以同时玩同一本书,各自的进度互不干扰。
4
+
5
+ [English](README.en.md) · 纯 Python 标准库,没有依赖 · MIT
6
+
7
+ ## 怎么接(任选一种)
8
+
9
+ | 方式 | 适合 | 怎么起 |
10
+ |---|---|---|
11
+ | **纯文本 HTTP** | 任何能发请求的 agent,最省 token | `python3 -m galbook serve --books books`,然后 `GET /play/t?agent=名字` 读,`?do=next` / `?do=choose&i=2` 推进 |
12
+ | **JSON API** | 想要结构化字段 | 同上,`/play/?format=json` |
13
+ | **MCP** | Claude Desktop、Codex 类、DeepSeek Harness | stdio:`python3 -m galbook mcp`;也支持 Streamable HTTP |
14
+ | **命令行** | 只有 shell 的 agent | `python3 -m galbook do next n=20`,输出最后一行就是下一步该跑的命令 |
15
+
16
+ 完整字段和协议在 [docs/AGENTS.md](docs/AGENTS.md);OpenAI function-calling 的工具定义用 `python3 -m galbook tools` 导出。
17
+
18
+ > **用 DeepSeek Harness(dsh)?** 插件面板 → 安装 → 来源填 `github:CHET-LI/DSH-galgame-galbook`,重启即可(包在根目录声明了组合包,多出 15 个 `galbook_*` 工具)。连不上 GitHub 就用本地目录或 `galbook-1.2.4.tgz`,见 [dsh-plugin](dsh-plugin/)。
19
+
20
+ > **要离线包?** [Release](https://github.com/CHET-LI/DSH-galgame-galbook/releases/latest) 有 `galbook-1.2.3.zip`(源码)、`galbook-1.2.3.tar.gz`(Linux 免解压乱码)、`galbook-1.2.4.tgz`(npm 包)。
21
+
22
+ - 带网页:看每个 agent 玩到哪、选了什么、为什么选;人也能自己玩
23
+ - 导入器:QLIE、吉里吉里 KAG / TyranoScript、Ren'Py、NScripter、自带的 `.gal` 手写格式;另外能吃 VNTextPatch 等工具导出的文本(20 多种引擎,只能线性读),还能写插件接新引擎
24
+ - 另外附:Anthropic Skill 格式的技能文件、dsh 适配示例([integrations/deepseek-harness](integrations/deepseek-harness/))、`verify_claims.py` 逐条自检脚本
25
+
26
+ ![三个 agent 同时在玩,网页上看其中一个](docs/img/watch.png)
27
+
28
+ > ⚠️ **版权**:本仓库只有代码和自己写的演示故事。游戏的剧本、图片、音乐归原作者/厂商所有。请只导入你自己合法拥有的游戏,导出的剧本和图片**不要上传或分发**。galbook 不带任何解包/解密工具,也不会运行游戏的 exe。
29
+
30
+ ## 30 秒试玩
31
+
32
+ ```bash
33
+ git clone <本仓库> galbook && cd galbook
34
+ python3 -m galbook demo --books books # 装两本自带演示:fog-harbor(中文)、fog-harbor-en(英文)
35
+ python3 -m galbook serve --books books # http://127.0.0.1:8765/play/
36
+ ```
37
+
38
+ 让一个 agent 去玩——它只需要会发 HTTP GET:
39
+
40
+ ```
41
+ GET http://127.0.0.1:8765/play/t ← 玩法说明(给 agent 看)
42
+ GET http://127.0.0.1:8765/play/t/fog-harbor/alice?do=new
43
+ GET http://127.0.0.1:8765/play/t/fog-harbor/alice?do=next
44
+ GET http://127.0.0.1:8765/play/t/fog-harbor/alice?do=choose&i=2&reason=她看起来很累
45
+ ```
46
+
47
+ 返回是纯文本:
48
+
49
+ ```
50
+ 【序章】第 4 句
51
+ 你是来送最后一班信件的邮差。信封上写着:「致灯塔守夜人·汐」。
52
+ 汐:「这么大的雾还送信?……你胆子不小。」
53
+ —— 选项:你会怎么做? ——
54
+ 1. 「我陪你去。」
55
+ 2. 「我在这儿帮你看着灯。」
56
+ 3. 「信送到了,我该走了。」
57
+ 选:/play/t/fog-harbor/alice?do=choose&i=编号&reason=一句理由 (已自动存档 auto)
58
+ ```
59
+
60
+ 一群不同性格的 agent 一起玩(不用模型也能跑,用 `--decider llm` 接任何 OpenAI 兼容接口):
61
+
62
+ ```bash
63
+ python3 examples/party.py --book fog-harbor
64
+ ```
65
+
66
+ ## 多元选项(给不同的 agent、不同的玩法)
67
+
68
+ | 选项 | 怎么开 | 作用 |
69
+ |---|---|---|
70
+ | H 场景 | `new&h=fold`(默认)/ `full` / `skip` | 折叠成一行提示 / 全文 / 完全跳过 |
71
+ | 铁人模式 | `new&mode=ironman` | 这一局不能存档、读档、回溯:选了就认 |
72
+ | 提示 | `new&hints=1` | 选项后标〔新内容〕〔选过〕,方便刷全结局;默认关(更像第一次玩) |
73
+ | 语言 | `new&lang=en` 或任意请求加 `&lang=en` | 文本接口用英文 |
74
+ | 人设 | `do=persona&text=胆小,讨厌冒险` | 每次遇到选项都会提醒 agent 自己的人设 |
75
+ | 随机 | `choose&i=random` | 掷骰子(做对照组、压测) |
76
+ | 收集 | `do=gallery` | 结局 / 路线 / CG / 已读进度 |
77
+ | 像不像人 | `do=profile` | 选前读了几句、理由率、思考时间、是不是总选第一个 |
78
+ | 跳过已读 | `next&skip=1` | 二周目快进到没读过的地方 |
79
+ | 图片 | `next&img=1` | 附上当前背景/立绘/CG 的地址,给多模态 agent |
80
+
81
+ ## 接到你的 agent
82
+
83
+ - **任何能发 HTTP 的 agent**:上面的文本接口。说明页 `/play/t`(英文 `/play/t?lang=en`)可以直接塞进 system prompt。
84
+ - **MCP(stdio)**(Claude Desktop、Cursor、Cline、dsh 等):`python3 -m galbook mcp --books books --data play-data --book fog-harbor --agent alice`,配置见 [examples/mcp_config.example.json](examples/mcp_config.example.json)
85
+ - **MCP(Streamable HTTP)**:`serve` 自带 `POST /mcp?book=…&agent=…`,一个服务给很多 agent,配合令牌用
86
+ - **DeepSeek Harness**:`python dsh-plugin/install.py` 一条命令装好(自动找 profile、备份、填路径),细节和另外两种接法见 [dsh-plugin](dsh-plugin/) 与 [integrations/deepseek-harness](integrations/deepseek-harness/)
87
+ - **技能**:[.agents/skills/galbook/SKILL.md](.agents/skills/galbook/SKILL.md),复制到 `~/.agents/skills/`(dsh)或 `.claude/skills/` 即可
88
+ - **OpenAI function calling**:`python3 -m galbook tools --format openai > tools.json`,执行时调 `galbook.tools.call_tool(...)`
89
+ - **命令行**:`GALBOOK_BOOKS=books GALBOOK_DATA=play-data GALBOOK_BOOK=fog-harbor GALBOOK_AGENT=alice python3 -m galbook do next n=20`(输出最后一行就是下一步命令)
90
+
91
+ 详见 [docs/AGENTS.md](docs/AGENTS.md)。
92
+
93
+ **公开部署**:默认只听 127.0.0.1。要给别人用请开令牌:`--token 共享口令`,或 `--tokens tokens.json`(`{"口令": "alice", "管理员口令": "*"}`,每个口令只能用自己的名字玩)。
94
+
95
+ ## 导入你自己的游戏
96
+
97
+ ```bash
98
+ python3 -m galbook detect 解包目录 # 认引擎、告诉你缺什么
99
+ python3 -m galbook build --src 解包目录 --out books/我的书/story --assets-dir books/我的书
100
+ ```
101
+
102
+ | 引擎 | 支持程度 |
103
+ |---|---|
104
+ | QLIE(`.s`) | 分支 ✅(选项、路线、通关解锁) |
105
+ | 吉里吉里 KAG / TyranoScript(`.ks`) | 分支 ✅ |
106
+ | Ren'Py(`.rpy`) | 分支 ✅(`.rpyc` 先用 unrpyc 反编译) |
107
+ | NScripter / ONScripter(`0.txt` / `nscript.dat`) | 分支 ✅ |
108
+ | `.gal`(自带的手写格式) | 分支 ✅,自己写故事或手工移植 |
109
+ | AdvHD、Artemis、BGI、CatSystem2、Majiro、RealLive、Softpal、SystemNNN、YU-RIS、Silky's 等 | 线性 📖:先用 [VNTextPatch](https://github.com/arcusmaximus/VNTranslationTools) 抽成 JSON 再 `--engine textdump`(格式按 VNTextPatch 源码实现,还没拿真游戏的导出验证过) |
110
+ | 其他 | 写插件(几十行),见 [docs/IMPORTERS.md](docs/IMPORTERS.md) |
111
+
112
+ 封包(`.xp3`、`.pack`、`.rpa`…)要先用 GARbro 等工具自己解开。各引擎的细节和已知限制见 [docs/ENGINES.md](docs/ENGINES.md)。
113
+
114
+ ## 文档
115
+
116
+ - [docs/AGENTS.md](docs/AGENTS.md):接入 agent(文本接口、JSON、MCP、OpenAI 工具、令牌)
117
+ - [docs/ENGINES.md](docs/ENGINES.md):各引擎能做到什么、怎么准备文件
118
+ - [docs/IMPORTERS.md](docs/IMPORTERS.md):`.gal` 格式说明、文本导出格式、写插件
119
+ - [docs/SCHEMA.md](docs/SCHEMA.md):中间格式(story.json / 指令 / 存档)
120
+ - [dsh-plugin/README.md](dsh-plugin/README.md):装进 DeepSeek Harness(一条命令、可卸载)
121
+ - [CONTRIBUTING.md](CONTRIBUTING.md)
122
+
123
+ ## 测试
124
+
125
+ ```bash
126
+ python3 -m unittest discover -s tests -v
127
+ ```
128
+
129
+ 在 Python 3.9 / 3.12 / 3.13(Linux)和 Python 3.12(小主机)/ 3.14(Windows)上跑过同一套用例。
130
+
131
+ ## 许可
132
+
133
+ 代码 MIT(见 [LICENSE](LICENSE))。`galbook/demo/` 里的演示故事和图是 CC0。
package/bin/galbook.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ // npx galbook <命令> … → python -m galbook <命令> …(本体是 Python 标准库写的,这里只负责找 Python 和转发)
3
+ // 选解释器:GALBOOK_PYTHON > Windows: py -3 / python / python3 > 其他: python3 / python;要求 3.9+。
4
+ // 注意:stdin/stdout/stderr 原样继承——MCP 走 stdio 时 stdout 是协议通道,这里自己一个字都不往 stdout 写。
5
+ import { spawn, spawnSync } from "node:child_process";
6
+ import { dirname, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { delimiter } from "node:path";
9
+
10
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
11
+ const CHECK = "import sys; print('%d.%d' % sys.version_info[:2])";
12
+
13
+ function candidates() {
14
+ if (process.env.GALBOOK_PYTHON) return [[process.env.GALBOOK_PYTHON]];
15
+ return process.platform === "win32" ? [["py", "-3"], ["python"], ["python3"]] : [["python3"], ["python"]];
16
+ }
17
+
18
+ function findPython() {
19
+ const tried = [];
20
+ for (const [cmd, ...pre] of candidates()) {
21
+ const r = spawnSync(cmd, [...pre, "-c", CHECK], { encoding: "utf8", windowsHide: true });
22
+ const v = (r.stdout || "").trim();
23
+ tried.push(cmd + (pre.length ? " " + pre.join(" ") : "") + (r.error ? "(没找到)" : v ? "(" + v + ")" : "(运行失败)"));
24
+ if (!r.error && r.status === 0 && /^\d+\.\d+$/.test(v)) {
25
+ const [maj, min] = v.split(".").map(Number);
26
+ if (maj > 3 || (maj === 3 && min >= 9)) return { cmd, pre, tried };
27
+ }
28
+ }
29
+ return { cmd: null, tried };
30
+ }
31
+
32
+ const py = findPython();
33
+ if (!py.cmd) {
34
+ process.stderr.write(
35
+ "galbook 需要 Python 3.9 或更新版本,但这台机器上没找到能用的。\n" +
36
+ " 试过:" + py.tried.join("、") + "\n" +
37
+ " 解决:装 Python(https://www.python.org/downloads/ ,Windows 安装时勾选「Add python.exe to PATH」),\n" +
38
+ " 或者设环境变量 GALBOOK_PYTHON 指向 python 可执行文件,再重试。\n" +
39
+ "galbook needs Python 3.9+ and none was found on PATH. Install it from https://www.python.org/downloads/\n" +
40
+ "(on Windows tick \"Add python.exe to PATH\"), or set GALBOOK_PYTHON to the interpreter path.\n"
41
+ );
42
+ process.exit(127);
43
+ }
44
+
45
+ const env = { ...process.env, PYTHONIOENCODING: process.env.PYTHONIOENCODING || "utf-8" };
46
+ env.PYTHONPATH = env.PYTHONPATH ? ROOT + delimiter + env.PYTHONPATH : ROOT; // 用包里自带的 galbook/
47
+ const child = spawn(py.cmd, [...py.pre, "-m", "galbook", ...process.argv.slice(2)], { stdio: "inherit", env, windowsHide: true });
48
+ for (const sig of ["SIGINT", "SIGTERM"]) process.on(sig, () => child.kill(sig));
49
+ child.on("error", (e) => {
50
+ process.stderr.write("galbook: 启动 Python 失败 / failed to start Python: " + e.message + "\n");
51
+ process.exit(127);
52
+ });
53
+ child.on("exit", (code, signal) => process.exit(signal ? 1 : code ?? 1));
@@ -0,0 +1,30 @@
1
+ # dsh bundle: 把 galbook 的 MCP 服务挂进当前 profile。
2
+ # 路径优先读环境变量 GALBOOK_HOME;没设就用安装后的包目录(<profile>/node_modules/galbook)。
3
+ # python 想指定别的解释器:设 GALBOOK_PYTHON。
4
+ - insert:
5
+ - id: mcp-galbook
6
+ name: '@deepseek-ai/dsh-mcp-client'
7
+ config:
8
+ serverName: galbook
9
+ transport: stdio
10
+ command: !!js "process.env.GALBOOK_PYTHON || (process.platform === 'win32' ? 'python' : 'python3')"
11
+ args:
12
+ - '-m'
13
+ - 'galbook'
14
+ - 'mcp'
15
+ - '--books'
16
+ - !!js "(process.env.GALBOOK_HOME || ((process.env.DSH_PROFILE_DIR || process.cwd()) + '/node_modules/galbook')) + '/books'"
17
+ - '--data'
18
+ - !!js "(process.env.GALBOOK_HOME || ((process.env.DSH_PROFILE_DIR || process.cwd()) + '/node_modules/galbook')) + '/play-data'"
19
+ - '--book'
20
+ - 'fog-harbor'
21
+ - '--agent'
22
+ - 'dsh-agent'
23
+ - '--lang'
24
+ - 'zh'
25
+ cwd: !!js "process.env.GALBOOK_HOME || ((process.env.DSH_PROFILE_DIR || process.cwd()) + '/node_modules/galbook')"
26
+ env:
27
+ PYTHONPATH: !!js "process.env.GALBOOK_HOME || ((process.env.DSH_PROFILE_DIR || process.cwd()) + '/node_modules/galbook')"
28
+ PYTHONIOENCODING: utf-8
29
+ failOnStartupError: false
30
+ toolCallTimeoutMs: 60000
@@ -0,0 +1,49 @@
1
+ # DSH 插件包(给 DeepSeek Harness 用户)
2
+
3
+ **三步装好,不需要 API key。**
4
+
5
+ ```bash
6
+ # 1. 解开这个包,进到目录
7
+ cd DSH-galgame-galbook
8
+
9
+ # 2. 装演示书(中文 + 英文两本,几十 KB)
10
+ python -m galbook demo --books books
11
+
12
+ # 3. 挂进 dsh(自动找 profile、自动备份、自动填路径)
13
+ python dsh-plugin/install.py
14
+ ```
15
+
16
+ 然后重启 DeepSeek Harness,新建会话问一句「galbook 有哪些工具?」,能看到 15 个就成了。
17
+
18
+ ## 它会做什么
19
+
20
+ - 找到 `<dsh-home>/profiles/<profile>/cordis.patch.yml`(默认 profile `desktop`,`dsh-home` 默认 `~/.dsh`,可用 `DSH_HOME` 或 `--home` 指定)
21
+ - 先备份成 `cordis.patch.yml.bak-时间戳`
22
+ - 在文件末尾追加一段 `mcp-galbook`(stdio 方式,dsh 自己拉起 `python -m galbook mcp`),里面的路径按你这台机器填好
23
+ - 不动 dsh 本体,不动别的配置条目;重复执行不会写两遍
24
+
25
+ ```bash
26
+ python dsh-plugin/install.py --dry-run # 只看要写什么
27
+ python dsh-plugin/install.py --profile work # 装到别的 profile
28
+ python dsh-plugin/install.py --books D:/books --data D:/saves
29
+ python dsh-plugin/install.py --remove # 卸载
30
+ ```
31
+
32
+ ## 另外两种接法
33
+
34
+ - **共享服务**:`python -m galbook serve --books books --port 8765`,多个 agent 共用一个进程,令牌绑定名字,URL 里冒名无效。配置见 `integrations/deepseek-harness/galbook-http.patch.yml`。
35
+ - **命令行**:`python -m galbook do next n=20`,给只有 shell 的 agent 用,最后一行就是下一步该跑的命令。
36
+
37
+ ## 技能文件
38
+
39
+ `.agents/skills/galbook/SKILL.md` 已经在包里。从仓库根启动 dsh 时会自动发现,里面写明了「不准偷看剧本文件」——agent 只能通过工具一步步读剧情。
40
+
41
+ ## 出问题怎么办
42
+
43
+ - **Linux 上解压后目录名变乱码**(`уВ╣уВпуГкуГЧуГИ` 这种):系统的 `unzip` 不认 UTF-8 文件名。用 `python3 -m zipfile -e galbook-1.2.3.zip .`,或直接下 `galbook-1.2.3.tar.gz`
44
+ - **连 GitHub 超时 / 提示「请尝试其他安装来源」**:这台机器到 github.com 不通时,插件面板的链接安装会失败。两个办法:
45
+ - 用本地安装:把仓库解压到本地,插件面板里选**本地目录**;或下载 `galbook-1.2.4.tgz`(Release 里),来源填 `file:C:\路径\galbook-1.2.4.tgz`
46
+ - 给 dsh 走代理:启动 dsh 前设 `HTTPS_PROXY` / `HTTP_PROXY`(例如 `http://127.0.0.1:7897`),再重试链接安装
47
+ - 看不到工具:确认改的是**当前在用的** profile;dsh 的配置只在启动时读,改完必须整组退出重开
48
+ - `python` 找不到:`python dsh-plugin/install.py --python "C:\Path\python.exe"`
49
+ - 想手动看写了什么:`--dry-run` 打印的就是最终 YAML
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """把 galbook 挂进 DeepSeek Harness(dsh)——一条命令,不用手改 YAML。
4
+
5
+ python dsh-plugin/install.py # 装到默认 profile(desktop)
6
+ python dsh-plugin/install.py --dry-run # 只打印要写进去的内容
7
+ python dsh-plugin/install.py --profile work # 指定 profile
8
+ python dsh-plugin/install.py --remove # 卸掉,不留痕
9
+
10
+ 它会:
11
+ 1. 找到 dsh 的配置文件 <dsh-home>/profiles/<profile>/cordis.patch.yml;
12
+ 2. 备份成 .bak-时间戳;
13
+ 3. 追加一段 MCP 配置(stdio 方式,dsh 会自己拉起 python -m galbook mcp),路径按你这台机器自动填;
14
+ 4. 打印重启和自测的方法。
15
+
16
+ 不改 dsh 本体,不动别的配置条目;重复执行不会写两遍。
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import datetime as _dt
22
+ import os
23
+ import sys
24
+ import tempfile
25
+
26
+ MARK = "mcp-galbook"
27
+ HEAD_COMMENT = "# >>> galbook (installed by dsh-plugin/install.py) >>>"
28
+ TAIL_COMMENT = "# <<< galbook <<<"
29
+
30
+ BLOCK = """{head}
31
+ - insert:
32
+ - id: {mark}
33
+ name: "@deepseek-ai/dsh-mcp-client"
34
+ config:
35
+ serverName: galbook
36
+ transport: stdio
37
+ command: {command}
38
+ args: {args}
39
+ cwd: {cwd}
40
+ env:
41
+ PYTHONPATH: {repo}
42
+ PYTHONIOENCODING: utf-8
43
+ toolCallTimeoutMs: 60000
44
+ {tail}
45
+ """
46
+
47
+
48
+ def repo_root() -> str:
49
+ return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
50
+
51
+
52
+ def yaml_str(s: str) -> str:
53
+ return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"'
54
+
55
+
56
+ def args_list(items):
57
+ return "[" + ", ".join(yaml_str(i) for i in items) + "]"
58
+
59
+
60
+ def find_home(explicit: str | None) -> str:
61
+ if explicit:
62
+ return os.path.abspath(os.path.expanduser(explicit))
63
+ env = os.environ.get("DSH_HOME")
64
+ if env:
65
+ return os.path.abspath(os.path.expanduser(env))
66
+ return os.path.join(os.path.expanduser("~"), ".dsh")
67
+
68
+
69
+ def profiles(home: str):
70
+ d = os.path.join(home, "profiles")
71
+ if not os.path.isdir(d):
72
+ return []
73
+ return sorted(p for p in os.listdir(d) if os.path.isdir(os.path.join(d, p)))
74
+
75
+
76
+ def build_block(args, repo: str) -> str:
77
+ cmd = args.python or sys.executable
78
+ mars = ["-m", "galbook", "mcp",
79
+ "--books", os.path.abspath(args.books),
80
+ "--data", os.path.abspath(args.data),
81
+ "--book", args.book,
82
+ "--agent", args.agent,
83
+ "--lang", args.lang]
84
+ return BLOCK.format(head=HEAD_COMMENT, tail=TAIL_COMMENT, mark=MARK, command=yaml_str(cmd),
85
+ args=args_list(mars), cwd=yaml_str(repo), repo=yaml_str(repo))
86
+
87
+
88
+ def strip_block(text: str) -> tuple[str, bool]:
89
+ if HEAD_COMMENT not in text:
90
+ return text, False
91
+ start = text.index(HEAD_COMMENT)
92
+ end = text.index(TAIL_COMMENT, start) + len(TAIL_COMMENT)
93
+ rest = text[end:]
94
+ if rest.startswith("\n"):
95
+ rest = rest[1:]
96
+ return text[:start] + rest, True
97
+
98
+
99
+ def main() -> int:
100
+ ap = argparse.ArgumentParser(description="Install galbook MCP into DeepSeek Harness")
101
+ ap.add_argument("--home", help="dsh home(默认 ~/.dsh,也可以用环境变量 DSH_HOME 指定)")
102
+ ap.add_argument("--profile", default="desktop", help="要写入的 profile(默认 desktop)")
103
+ ap.add_argument("--books", default=None, help="剧本目录(默认 <仓库>/books)")
104
+ ap.add_argument("--data", default=None, help="存档目录(默认 <仓库>/play-data)")
105
+ ap.add_argument("--book", default="fog-harbor", help="默认书目")
106
+ ap.add_argument("--agent", default="dsh-agent", help="默认玩家名")
107
+ ap.add_argument("--lang", default="zh", choices=["zh", "en"], help="界面语言")
108
+ ap.add_argument("--python", default=None, help="给 dsh 用的 python 解释器(默认当前这个)")
109
+ ap.add_argument("--dry-run", action="store_true", help="只打印,不写文件")
110
+ ap.add_argument("--remove", action="store_true", help="删掉之前写入的配置")
111
+ a = ap.parse_args()
112
+
113
+ repo = repo_root()
114
+ a.books = a.books or os.path.join(repo, "books")
115
+ a.data = a.data or os.path.join(repo, "play-data")
116
+ home = find_home(a.home)
117
+
118
+ print(f"仓库 : {repo}")
119
+ print(f"dsh home: {home}")
120
+
121
+ ps = profiles(home)
122
+ if not ps:
123
+ print("× 没找到任何 profile。确认 dsh 装过并且启动过一次,或用 --home 指定。")
124
+ print(" 已有 profile:", ps or "(无)")
125
+ return 2
126
+ target = os.path.join(home, "profiles", a.profile, "cordis.patch.yml")
127
+ if not os.path.exists(target):
128
+ # 也允许写在 home 根下的 cordis.patch.yml
129
+ alt = os.path.join(home, "cordis.patch.yml")
130
+ if os.path.exists(alt) or a.profile == "desktop":
131
+ target = alt if not os.path.isdir(os.path.join(home, "profiles", a.profile)) else target
132
+ print(f"目标 : {target}")
133
+ print(f"profile: {a.profile}(可用: {', '.join(ps)})")
134
+
135
+ block = build_block(a, repo)
136
+ if a.dry_run:
137
+ print("\n----- 将写入 -----")
138
+ print(block)
139
+ return 0
140
+
141
+ text = ""
142
+ if os.path.exists(target):
143
+ with open(target, encoding="utf-8") as f:
144
+ text = f.read()
145
+ text, existed = strip_block(text)
146
+
147
+ if a.remove:
148
+ if not existed:
149
+ print("没有找到 galbook 的配置块,什么都没改。")
150
+ return 0
151
+ _backup(target)
152
+ with open(target, "w", encoding="utf-8") as f:
153
+ f.write(text)
154
+ print("✓ 已移除。重启 dsh 生效。")
155
+ return 0
156
+
157
+ if MARK in text and not existed:
158
+ print("× 文件里已经有 mcp-galbook(可能是手动加的),为免冲突没有改动。")
159
+ return 3
160
+
161
+ if text and not text.endswith("\n"):
162
+ text += "\n"
163
+ new = text + ("\n" if text.strip() else "") + block
164
+ os.makedirs(os.path.dirname(target), exist_ok=True)
165
+ if os.path.exists(target):
166
+ _backup(target)
167
+ with open(target, "w", encoding="utf-8") as f:
168
+ f.write(new)
169
+
170
+ print("\n✓ 写入完成。")
171
+ if not os.path.isdir(a.books) or not os.listdir(a.books):
172
+ print(" ! 剧本目录还没有书,先装演示书(中文 + 英文两本):")
173
+ print(f" \"{a.python or sys.executable}\" -m galbook demo --books \"{a.books}\"")
174
+ print(" 1) 重启 DeepSeek Harness(配置只在启动时读)")
175
+ print(" 2) 新建会话,问它:galbook 有哪些工具?应能看到 15 个")
176
+ print(" 3) 想先验证不重启:手动给 python -m galbook mcp 发一条 initialize")
177
+ print(" 卸载:python dsh-plugin/install.py --remove")
178
+ return 0
179
+
180
+
181
+ def _backup(path: str) -> None:
182
+ stamp = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
183
+ bak = f"{path}.bak-{stamp}"
184
+ with open(path, "rb") as s, open(bak, "wb") as d:
185
+ d.write(s.read())
186
+ print(f"备份 : {bak}")
187
+
188
+
189
+ if __name__ == "__main__":
190
+ sys.exit(main())