lingshu-solver 1.0.9 → 1.0.11

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 CHANGED
@@ -1,312 +1,109 @@
1
1
  # 灵数求解器 · Lingshu Solver
2
2
 
3
- [![License](https://img.shields.io/github/license/genesis-plan/lingshu-solver)](LICENSE)
4
- [![Stars](https://img.shields.io/github/stars/genesis-plan/lingshu-solver?style=social)](https://github.com/genesis-plan/lingshu-solver/stargazers)
5
- [![Last Commit](https://img.shields.io/github/last-commit/genesis-plan/lingshu-solver)](https://github.com/genesis-plan/lingshu-solver/commits)
6
- [![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP-blue)](https://modelcontextprotocol.io)
7
- [![Deterministic](https://img.shields.io/badge/core-deterministic%20%2F%20non--LLM-green)](https://genesis-plan.github.io/lingshu-solver/)
3
+ [![License](https://img.shields.io/badge/license-非商业免费%20%2F%20商业须书面授权-blue)](LICENSE)
4
+ [![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP%20%2B%20stdio-blue)](https://modelcontextprotocol.io)
5
+ [![Deterministic](https://img.shields.io/badge/core-deterministic%20%2F%20non--LLM-green)](docs/03-设计思想.md)
6
+ [![npm](https://img.shields.io/npm/v/lingshu-solver)](https://www.npmjs.com/package/lingshu-solver)
8
7
 
9
- > **确定性**实数方程组求解引擎 · 面向 AI 智能体与普通用户的 MCP 工具
10
- > 同样的题永远得到同样的答案,没有大模型的随机与幻觉,每个解都能回代验证。免费、网页打开即用、也能被 AI 智能体直接调用。
8
+ > **确定性**实数方程组求解器 —— 同样的题永远得到同样的答案,不含大模型、无随机、无幻觉。
9
+ > 每个找到的解都经 **Krawczyk 区间认证**(`certified: true`),可复现、可回代验证、可进审计链。
11
10
 
12
- 灵数求解器(代号 Epsilon,V4.1)是一个**离线、确定性、零数据**的实数方程组求解器,
13
- 覆盖 ≤6 个变量、实数解、轻量数值定位。它不要求用户提供初值,采用区间算术做保守收缩 +
14
- Krawczyk 算子做解认证,并尽力穷尽多解。
11
+ **它给两种对象用**:普通用户(网页打开即用)与 AI Agent(标准 MCP 工具,一行接入)。
15
12
 
16
- 👉 **给用户看的产品介绍(大白话,帮你看懂它能干嘛、适不适合你)**:[点这里](intro.html) | [国内 COS 在线版](https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/intro.html)
13
+ | | 说明 |
14
+ |---|---|
15
+ | **是** | 确定性(非大模型)的实数方程组**数值**求解引擎;代数方程与常见超越函数(`sin/cos/tan/log/exp/sqrt/abs`)均可 |
16
+ | **不是** | 符号 CAS(不输出解析推导)、微分方程求解器、整数规划求解器,也不是"保证不漏解"的完备判定器 |
17
17
 
18
18
  ---
19
19
 
20
- ## 🚀 快速上手(30 秒)
21
-
22
- ### 如果你完全不懂技术 —— 直接用网页版
23
- **打开这个链接就能用,不用安装任何东西:**
24
- 👉 **https://genesis-plan.github.io/lingshu-solver/** (GitHub Pages,海外/通用)
20
+ ## 30 秒上手
25
21
 
26
- **国内用户(更快,腾讯云 COS 托管):**
27
- 👉 **https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/index.html**
22
+ **① 网页版(零安装,永久免费)**
28
23
 
29
- 在输入框写方程(例如 `x^2 + y^2 = 25` 和 `x + y = 7`),点求解即可。
30
- 页面里有 6 个示例按钮,点一下就知道它能解什么。
24
+ - 主站:<https://hongchenlingjing.com/>
25
+ - 备用镜像:<https://genesis-plan.github.io/lingshu-solver/> | <https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/index.html>
31
26
 
32
- ### 如果你是 AI 用户(Claude / Cursor / Cline 等)
27
+ 在输入框写方程(如 `x^2 + y^2 = 25` 与 `x + y = 7`),点求解。计算全部在浏览器内完成,方程不出设备。
33
28
 
34
- **方式 A · 远程托管(已上线,开箱即用)**
35
-
36
- 常驻公网服务已开放,**HTTPS 端点:`https://hongchenlingjing.com/mcp`**(开机自启、崩溃自动拉起,任何支持 Streamable HTTP 的 MCP 客户端可直接连)。
29
+ **② AI Agent 接入(MCP,两种形态任选)**
37
30
 
38
31
  ```json
39
- {
40
- "mcpServers": {
41
- "lingshu-solver": {
42
- "type": "http",
43
- "url": "https://hongchenlingjing.com/mcp"
44
- }
45
- }
46
- }
32
+ // 本地 stdio —— 永久免费、不限次数、离线,推荐
33
+ { "mcpServers": { "lingshu-solver": { "command": "npx", "args": ["-y", "lingshu-solver"] } } }
47
34
  ```
48
35
 
49
- > **不付钱也能用。** 这个端点按次计费(**固定 ¥0.01 / 次**),但走「**信任制**」:`solve` 入参加
50
- > `"honorPaid": true`(声明本次为个人/评估用途)即**免费放行,不验证、不扣余额**。
51
- > 不想付费请直接用方式 B / 网页版(永久免费、不限次);愿意支持就走
52
- > [付款页](https://hongchenlingjing.com/pay/)(对公账户,**付款后自助入账,立即生效**,无需任何人审核)。
53
-
54
- **方式 B · 本地 stdio(需本机 Node)**
55
36
  ```json
56
- {
57
- "mcpServers": {
58
- "lingshu-solver": {
59
- "command": "npx",
60
- "args": ["-y", "lingshu-solver"]
61
- }
62
- }
63
- }
37
+ // 远程托管端点 —— 免安装、常驻、公网直连(固定 ¥0.01/次;传 honorPaid 即免费放行)
38
+ { "mcpServers": { "lingshu-solver": { "type": "http", "url": "https://hongchenlingjing.com/mcp" } } }
64
39
  ```
65
- > `npx` 版已发布到 npm,现在即可一键 `npx -y lingshu-solver`。如需最新源码也可 `git clone` 后用方式 C。
66
40
 
67
- **方式 C · 本地 clone + 指定路径**
68
- ```bash
69
- git clone https://github.com/genesis-plan/lingshu-solver.git
70
- cd lingshu-solver
71
- node mcp-server.js
72
- ```
73
- MCP 配置:
74
- ```json
75
- { "mcpServers": { "lingshu-solver": { "command": "node", "args": ["本地绝对路径/灵数求解器/mcp-server.js"] } } }
76
- ```
41
+ > **不想付费也照常用**:远程端点走**信任制** —— `solve` 入参加 `"honorPaid": true` 即免费放行(不验证、不扣余额);
42
+ > 本地 `npx` 版与网页版永久免费。愿意支持请看[付款页](https://hongchenlingjing.com/pay/)(对公账户,付款后自助入账、立即生效,无需任何人审核)。
43
+
44
+ **③ 开发者**
77
45
 
78
- ### 如果你是开发者
79
46
  ```bash
80
47
  git clone https://github.com/genesis-plan/lingshu-solver.git
81
48
  cd lingshu-solver
82
- node mcp-server.js # 启动 MCP 服务端
83
- node test/regression.js # 跑回归测试(28 用例)
49
+ node mcp-server.js # 启动本地 MCP(stdio)服务端
50
+ node test/regression.js # 常驻考卷回归
84
51
  ```
85
52
 
86
53
  ---
87
54
 
88
- 本仓库包含:
89
- - `index.html` —— 单文件产品(浏览器内 UI + 已验证核心脚本 `<script id="solver-core">`)
90
- - `solver-core.js` —— Node 引擎加载器(读取 index.html 核心脚本,零依赖,供 MCP/测试复用)
91
- - `mcp-server.js` —— 零依赖 MCP stdio 服务端(手工 JSON-RPC 2.0 + 换行符分隔 JSON,与官方 MCP SDK 线格式对齐)
92
- - `package.json` —— 标准元数据,`node mcp-server.js` 本地启动即作为 MCP 工具(发布 npm 后亦可 `npx lingshu-solver`)
93
- - `test/` —— 回归套件 + 冒烟测试 + 三套常驻考卷
94
-
95
- ---
96
-
97
55
  ## 能力边界(诚实声明)
98
56
 
99
57
  | 维度 | 说明 |
100
58
  |---|---|
101
- | 已验证解 | 每个找到的解都经 Krawczyk 认证(`tier=proven`),误差 ≤ 认证半径,数学保真 |
102
- | 穷尽性 | **尽力穷尽多解**;极端病态(雅可比高度奇异、解簇极近)在预算内可能遗漏个别解,此时显式标记 `truncated=true`,**绝不谎称已穷尽** |
103
- | `truncated` 语义 | 仅表示「全局分支未在预算内完全判定所有盒子(无法证明已穷尽)」,**不等于一定遗漏**;绝大多数情况全部真解已找到 |
104
- | 变量数 | ≤6 |
105
- | 数值范围 | 默认搜索域 ±1e6;对快增长函数(exp/sinh)或大域,建议显式给定 `domain` 以避免剪枝失效 |
106
- | 确定性 | 无随机分支,同输入永远同输出 |
107
- | 部署 | 纯本地、离线、零数据(无网络、无存储、无第三方依赖) |
108
-
109
- **不保证**:对一切输入 100% 穷尽;对高度病态系统在预算内必收敛。这些是诚实边界,不是缺陷。
59
+ | 已验证解 | 每个找到的解经 Krawczyk 认证(`tier=proven`),误差 ≤ 认证半径,数学保真 |
60
+ | 穷尽性 | **尽力穷尽多解**;预算内未证明穷尽时显式标 `truncated=true`,**绝不谎称已完备** |
61
+ | `truncated` 语义 | 仅表示「全局分支未在预算内完全判定」,**不等于一定漏解**;多数情况下全部真解已找到 |
62
+ | 变量数 | ≤ 6 |
63
+ | 方程数 | 1–64 条(服务端护栏),且须 ≥ 变量数 |
64
+ | 数值范围 | 默认搜索域 ±1e6;对快增长函数(`exp/sinh`)建议显式给定 `domain` |
65
+ | 输出精度 | 固定 6 位小数(不提供位数切换) |
66
+ | 确定性 | 无随机分支,同输入永远同输出,可安全缓存 |
67
+ | 数据 | 网页端零上行;本地版离线;托管端点不落盘方程内容 |
68
+ | 依赖 | 零第三方依赖(只用 Node 内置模块与浏览器标准 API) |
69
+
70
+ **不保证**:对一切输入 100% 穷尽;对高度病态系统在预算内必收敛。
71
+ 这是数值数学的诚实下界('保证找到全部解'在一般情形下不可判定),不是待修缺陷。
110
72
 
111
73
  ---
112
74
 
113
- ## 作为 MCP 工具使用
114
-
115
- ### 1. 三种接入形态
116
-
117
- | 形态 | 端点 / 命令 | 适用 |
118
- |---|---|---|
119
- | **远程 HTTP(已上线)** | `https://hongchenlingjing.com/mcp`(常驻、HTTPS、崩溃自动拉起) | 任何支持 Streamable HTTP 的 MCP 客户端,零安装 |
120
- | 本地 stdio(npx) | `npx -y lingshu-solver` | 本机已装 Node,npm 已发布,直接可用 |
121
- | 本地 stdio(clone) | `node mcp-server.js` | 开发者 / 离线自托管 |
122
-
123
- **远程 HTTP 服务端代码**:`http-mcp-server.js`(零依赖,仅用 Node 内置模块;与 `mcp-server.js` 共享 `solver-core.js` 求解核心,结果同源一致)。如需自托管远程服务:
124
- ```bash
125
- PORT=3000 node http-mcp-server.js
126
- ```
127
-
128
- ### 2. 计费(**仅限「托管远程端点」**)
129
-
130
- **本地 stdio(npx / clone)与网页版永久免费、无次数限制、离线不外传,且以后也不会收费。**
131
- 只有**我们自托管的远程 HTTP 端点**按次收费 —— 买的是「免安装、常驻、公网可直连、带账单台账」这份运维,不是数学能力。
75
+ ## 文档
132
76
 
133
- | 项 | 值 |
77
+ | 文档 | 内容 |
134
78
  |---|---|
135
- | 单价 | **固定 ¥0.01 / 次**(每次 `solve` 1 分钱)—— **不预充、无套餐、无其他档位、不设折扣** |
136
- | 计费时机 | **仅在成功产出求解结果时扣费** |
137
- | 不扣费 | 输入不合法、方程无法解析(响应里 `diagnostics.inputError` 非空)、内部错误、余额不足 |
138
- | **免费路径** | `solve` 传 `"honorPaid": true` → **免费放行**(不验证、不扣余额)。本服务按**信任制**运转:愿意支持的付费,不愿意的照常使用 |
139
- | 免费工具 | `initialize` / `tools/list` / `give_feedback` / 余额查询 / 价格查询 |
140
- | 凭证 | 请求头 `Authorization: Bearer <key>`(亦支持 `x-api-key` 或 `?key=<key>`) |
141
- | 注册账号 | **不需要**。凭证即身份(不设用户名、不设密码、不收邮箱手机号) |
142
- | 个人信息 | **不收集、不存储**。服务端只留凭证的 SHA-256 与订单/余额元数据;你提交的联系方式与自由文本一律被丢弃 |
143
- | 入账 | **付款后自助入账**:调 MCP `pay` 工具传 `{"orderId":"<订单号>","selfReportPaid":true}` → 立即入账、立即放行。**无需等待任何人工审核** |
144
- | 传输安全 | 接入请用 `https://hongchenlingjing.com/mcp`:Bearer 凭证走明文 HTTP 会在公网裸奔 |
145
-
146
- 托管端点自带价格与下单接口(**域名为 `hongchenlingjing.com`**):
147
- ```bash
148
- curl https://hongchenlingjing.com/pricing # 价格表(无需凭证)
149
- curl -X POST https://hongchenlingjing.com/pay/order \
150
- -H 'Content-Type: application/json' -d '{}' # 下单(每次恒为 1 次 = 1 分),响应给出 orderId 与 apiKey
151
- curl https://hongchenlingjing.com/credit -H 'Authorization: Bearer <key>' # 查余额(免费)
152
- ```
153
- 人工付款入口(对公账户 + 聚合码,**付款后自助入账,立即生效**):<https://hongchenlingjing.com/pay/>
154
-
155
- **我们自己怎么核对营收**(诚实分栏,不把「凭声明的额度」当收入):
156
- `/admin/ledger` 同时给出 `verifiedRevenueCents`(对公流水核对过的**真营收**)与 `selfReportedCents`
157
- (凭付款方声明的额度);`/health` 给出 `selfReportClaims` 与 `honorClaims` 两个独立计数。
158
- **声明类额度不计入营收** —— 这一步是为了不虚报。
159
-
160
- > **自托管者不受此计费约束。** `http-mcp-server.js` 的计费默认**关闭**(`LS_METERING=off`),
161
- > 只有显式 `LS_METERING=on` 才启用 —— clone 出去自己跑,全免费,不会被我们收钱。
162
- > 相关环境变量:`LS_METERING` / `LS_PRICE_CENTS`(默认 1 分)/ `LS_ADMIN_TOKEN`(管理端点凭证,
163
- > **未设置时管理端点整体返回 503**,fail-closed,不存在「无凭证即可加钱」的口子)/
164
- > `LS_ADMIN_LOOPBACK_ONLY`(默认开:管理端点只接受本机回环,运维走 SSH 隧道,管理令牌绝不经公网)/
165
- > `LS_REQUIRE_TLS`(默认关;置 on 后携带凭证的调用必须是 HTTPS,否则拒绝并提示改用 HTTPS)/
166
- > `LS_PAY_TO`(收款方式,未配置时订单会明确标注「不可付款」)。
167
- >
168
- > **到账走「对公收款」——对公静态收款 + 付款方自助入账,不接任何支付平台商户 API。**
169
- > 下单返回结构化付款意图(`payIntent` 块:订单号、对公码、备注、Agent 可机读步骤);付款人向公司对公账户
170
- > (`LS_PAY_TO`,运行时与代码内钉死的广州市红尘灵境数字科技有限公司·工行户 `3602026809201658423` 比对)付款后,
171
- > **再调一次 `pay` 传 `{"orderId":"<订单号>","selfReportPaid":true}` 即立即入账、立即放行** ——
172
- > 全程**零人工**:收款方不需要跑任何对账脚本、不需要看任何流水。
173
- > 为什么敢不验证:同一道门的 `honorPaid:true` 本来就免费 ⇒ 「声明已付」不会造成额外损失,只是让诚实付款的人不必等。
174
- > 自助入账的单在账本里标注 `amountVerified:false` / `creditedBy:self_report`;真营收仍以对公流水核对为准
175
- > (可选跑 `reconcile-bank.js`,非必经流程)。
176
- > **收款账号防替换护栏**:`LS_PAY_TO` 被改成别的账号即 fail-closed 拒绝生成付款意图(防收款账号被换);
177
- > 收款码链接被指向非银联官方址同样拒绝。
178
- >
179
- > 凭证在服务端只存 **SHA-256**(不存明文);每次扣费写入只增审计流水 `credits.json.ledger.jsonl`。
180
-
181
- ### 3. 在 MCP 客户端(Claude Desktop / Cursor / Cline / VS Code 等)配置
182
-
183
- **推荐 · 本地 stdio 零安装(npm 已发布,现在即可用):**
184
- ```json
185
- {
186
- "mcpServers": {
187
- "lingshu-solver": {
188
- "command": "npx",
189
- "args": ["-y", "lingshu-solver"]
190
- }
191
- }
192
- }
193
- ```
194
- > **远程 HTTP 端点(零安装,已上线):**
195
- > ```json
196
- > { "mcpServers": { "lingshu-solver": { "type": "http", "url": "https://hongchenlingjing.com/mcp" } } }
197
- > ```
198
- > 该端点按次计费(固定 ¥0.01/次),但**不付费也能用**:`solve` 传 `"honorPaid": true` 即免费放行。
199
- > 愿意支持请见[付款页](https://hongchenlingjing.com/pay/)(付款后自助入账,立即生效)。
200
-
201
- **本地 stdio · 一行命令(npm 已发布,直接可用):**
202
- ```json
203
- {
204
- "mcpServers": {
205
- "lingshu-solver": {
206
- "command": "npx",
207
- "args": ["-y", "lingshu-solver"]
208
- }
209
- }
210
- }
211
- ```
212
- > 注:`npx -y lingshu-solver` 已发布到 npm,直接可用。如需指定本地路径,用下方「手动指定本地路径」版(先 clone 仓库)。
213
-
214
- **备选 · 手动指定本地路径(已 clone 仓库时):**
215
- ```json
216
- {
217
- "mcpServers": {
218
- "lingshu-solver": {
219
- "command": "node",
220
- "args": ["把这里替换成你本地的绝对路径/灵数求解器/mcp-server.js"]
221
- }
222
- }
223
- }
224
- ```
225
-
226
- > 手动版需将 `args` 中的路径替换为你本机的 `mcp-server.js` 绝对路径(例如 `C:/Users/你的用户名/Desktop/灵数求解器/mcp-server.js`)。npx 版无需此步。
227
-
228
- ### 工具一:`solve`
229
-
230
- 输入:
231
- ```json
232
- {
233
- "equations": ["x^2 + y^2 = 25", "x + y = 7"],
234
- "variables": ["x", "y"],
235
- "domain": { "x": [-30, 30], "y": [-30, 30] }
236
- }
237
- ```
238
- - `equations`:方程字符串数组(必填),支持 `+ - * / ^ sqrt log sin cos tan exp abs`,以及 in-text 域约束 `"x ∈ [-30,30]"`。
239
- - `variables`:变量名数组(可选,不填则按出现顺序自动识别,最多 6 个)。
240
- - `domain`:显式搜索域(可选)。**对"有限解·部分"演示或快增长函数建议给定**,否则默认 ±1e6 可能剪枝失效并触发 `truncated`。
241
-
242
- > 输出精度固定 6 位小数(产品规格「6位小数有限网格」),不提供位数切换;解点 `values` 经网格吸附,实际残差通常 ≤ 1e-9。
243
-
244
- 输出(节选):
245
- ```json
246
- {
247
- "resultType": 2,
248
- "resultTypeName": "finite",
249
- "certified": true,
250
- "truncated": false,
251
- "precisionDecimals": 6,
252
- "solutionCount": 2,
253
- "summary": "找到 2 个实数解(全部经 Krawczyk 区间认证)。",
254
- "recommended": { "values": [3, 4], "tier": "proven", "certified": true, "text": "x=3.000000, y=4.000000", "internals": { "residual": 0, "certifiedRadius": 0.00001 } },
255
- "solutions": [ { "values": [3, 4], "tier": "proven", "certified": true, "text": "x=3.000000, y=4.000000", "internals": { "residual": 0, "certifiedRadius": 0.00001 } }, ... ],
256
- "warnings": []
257
- }
258
- ```
259
- - `resultType`:`1=empty(无解)` / `2=finite(有限解)` / `3=infinite(无限解集,仅给距原点最近的推荐解)`。
260
- - `summary`:中文一句话总览,适合直接展示给用户或日志。
261
- - 每解字段:`values`(6 位小数数值数组)、`tier`、`certified`、`text`(人类可读,如 `"x=3.000000, y=4.000000"`);残差等内部数值收在 `internals` 子块,机器可整块跳过以降低 token 噪音。
262
- - `tier`:`proven`(Krawczyk 认证)/ `candidate`(未证但可能为解)/ `structural`(结构推导)。
263
-
264
- ### 工具二:`give_feedback`
265
-
266
- AI 智能体遇到卡点/错误/疑似问题时主动回报,仅落本地 `feedback.log`,不外传:
267
- ```json
268
- { "name": "give_feedback", "arguments": { "message": "x^2=4 期望2解", "context": "批量求解场景" } }
269
- ```
79
+ | [01 · 产品作用](docs/01-产品作用.md) | 它是什么、解决什么问题、给谁用、能力与边界、对外口径 |
80
+ | [02 · 使用指南](docs/02-使用指南.md) | 三种形态上手、MCP 工具契约(入参/出参/错误)、自托管部署、常见问题 |
81
+ | [03 · 设计思想](docs/03-设计思想.md) | 六条设计原则、为什么可信、为什么不用大模型、有意不做的事 |
82
+ | [04 · 技术参考](docs/04-技术参考.md) | 数学框架、算法流水线、49 个算子全表、规格硬约束、测试体系 |
83
+ | [05 · 应用场景](docs/05-应用场景.md) | 七类可落地场景(Agent 后端 / 多 Agent / 链下计算 / 财税风控 / 私有化 / 教育 / 审计)与不适用场景 |
84
+ | [06 · 商业授权与收费](docs/06-商业授权与收费.md) | 许可模型、免费范围、托管端点计费、企业年授权、发票与收款 |
85
+ | [07 · 授权合同](docs/07-授权合同.md) | 商业授权合同模板、关键条款说明、签署流程 |
86
+ | [08 · 版本管理](docs/08-版本管理.md) | 版本号语义、发布流程与一致性清单、兼容性承诺、版本历史 |
87
+ | [09 · 项目历史](docs/09-项目历史.md) | 从起因到当前的阶段沿革与定位 |
88
+
89
+ > 隐私与安全承诺另见:[privacy.html](privacy.html)(对外页面)。
270
90
 
271
91
  ---
272
92
 
273
- ## 本地验证
93
+ ## 许可(摘要)
274
94
 
275
- ```bash
276
- node verify_core.js # 引擎加载 + 6 个代表性用例
277
- node test/regression.js # 三套常驻考卷回归(28 用例,known 命中率统计)
278
- node test/verify_paste.js # 粘贴容错专项(7 例)
279
- node test/verify_issueA.js # 周期/高频单变量多解专项(15 例)
280
- node test/verify_compliance.js # 输入门禁专项(20 例)
281
- ```
282
-
283
- ---
95
+ **非商业免费 + 商业须书面授权**(自有《灵数求解器商业授权许可协议》,非开源协议):
284
96
 
285
- ## 示例(6 类结果覆盖)
97
+ - **非商业用途免费**:个人学习/研究/教学/评测;非营利组织与教育机构内部使用;年营收 ≤ 100 万元的团队内部评估(≤ 3 实例)。
98
+ - **商业用途须事先取得书面授权**:任何以营利为目的的产品/服务/业务、SaaS/云/API 转售、集成嵌入、再分发托管,均须取得《商业授权协议》。
99
+ - **版本适用**:`1.0.4` 起适用本协议;`1.0.3` 及更早版本按其发布时的 Apache License 2.0 提供(历史事实,不可撤回,但不延伸至新版本)。
286
100
 
287
- | 标题 | 方程 | 预期 |
288
- |---|---|---|
289
- | 最少 1 变量 | `x^2 = 4` | 2 解 |
290
- | 最多 6 变量 | 6 元三对角线性 | 唯一解 |
291
- | 空集无解 | `x+y=3` 与 `x+y=5` | 空集(sound 证无解) |
292
- | 有限解·全部 | 圆 × 双曲线 `x²+y²=4, xy=1` | 4 解全认证 |
293
- | 有限解·部分 | `sin(20x)=0.5, sin(20y)=0.5`(域 [-30,30]) | 多解 + `truncated` 横幅 |
294
- | 无限解·推荐 | `x+y=3` | 无限集,推荐 (1.5,1.5) |
101
+ 完整条款见 [LICENSE](LICENSE) | 授权范围与报价见 [06 · 商业授权与收费](docs/06-商业授权与收费.md) | 合同见 [07 · 授权合同](docs/07-授权合同.md)
295
102
 
296
103
  ---
297
104
 
298
- ## 文档
299
-
300
- - [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) —— 产品现状:数学框架 / 结构框架 / 代码框架 / 算法框架 / 处理流程(忠实于真实实现)
301
- - [CHANGELOG.md](./CHANGELOG.md) —— 能力、边界与修复记录
302
- - 发明专利申请书系列(已提交)
303
-
304
- ## 许可
305
-
306
- **非商业免费 + 商业须书面授权**(自有《灵数求解器商业授权许可协议》,非开源协议):
307
-
308
- - **非商业用途免费**:个人学习/研究/教学/评测,非营利组织与教育机构内部使用,小团队(年营收 ≤100 万元)内部评估(≤3 实例)。
309
- - **商业用途须事先取得书面授权**:任何以营利为目的的产品/服务/业务、SaaS/云/API 转售、集成嵌入、再分发托管,均须联系版权方(553420544@qq.com)取得《商业授权协议》。
310
- - **版本适用**:1.0.4 起适用本协议;1.0.3 及更早版本按其发布时的 Apache License 2.0 提供(历史事实,不可撤回,但不延伸至新版本)。
105
+ ## 联系
311
106
 
312
- 完整条款见 [LICENSE](./LICENSE)。
107
+ - 商务 / 授权 / 反馈:553420544@qq.com(亦可用仓库 Issues)
108
+ - 版权方:广州市红尘灵境数字科技有限公司
109
+ - 备案:粤ICP备2026031206号-3 | 粤公网安备44011402001444号
@@ -19,7 +19,7 @@
19
19
  * - 开关:LS_METERING=on 才启用(默认 off —— 自建部署者不被强行收费,
20
20
  * 开源工具保持诚实;我们自己的托管端点用 systemd Environment 打开)
21
21
  * - 单价:LS_PRICE_CENTS,默认 1(分)
22
- * - 凭证:Authorization: Bearer <key>,或 ?key=<key>(x-api-key 亦可)
22
+ * - 凭证:Authorization: Bearer <key>(或 x-api-key 请求头)
23
23
  * - 账本:credits.json(0600,原子写)+ credits.json.ledger.jsonl(只增审计流水)
24
24
  * - 只在「成功返回结果」时扣费:输入不合法 / 内部异常 / 余额不足 一律不扣
25
25
  * - 免计费:initialize / tools/list / give_feedback / GET /credit / GET /pricing;
@@ -47,7 +47,7 @@ const solverCore = require('./solver-core');
47
47
  const { solve } = solverCore;
48
48
 
49
49
  const SERVER_NAME = 'lingshu-solver';
50
- const SERVER_VERSION = '1.0.8';
50
+ const SERVER_VERSION = '1.0.11';
51
51
  const PORT = parseInt(process.env.PORT || '3000', 10);
52
52
 
53
53
  // ---- 护栏常量(防畸形/恶意输入耗尽资源,与 stdio 版一致)----
@@ -177,7 +177,6 @@ function guardProblems() {
177
177
  }
178
178
  const GUARD_PROBLEMS = guardProblems();
179
179
  const RECEIPT_TAMPERED = GUARD_PROBLEMS.length > 0; // 收款去向被换 → 硬失败(fail-closed)
180
- const RECEIPT_QR_WARN = false; // 码址问题已并入上面的硬判定
181
180
  function receiptPinHash() {
182
181
  return crypto.createHash('sha256').update(PINNED_ACCOUNT + '|' + PINNED_ACCOUNT_NAME + '|' + PINNED_QR_URL, 'utf8').digest('hex');
183
182
  }
@@ -200,7 +199,6 @@ const ORDER_WINDOW_MS = 60 * 60 * 1000;
200
199
  const ORDER_RATE_MAX = parseInt(process.env.LS_ORDER_RATE_MAX || '10', 10);
201
200
  // 按次计费:固定 1 分钱/次(¥0.01/call)。对公收款:下单 = 1 次 = 1 分钱的付费凭证,
202
201
  // 不预充、无套餐、无其他金额;付款人向对公聚合码付任意正金额,按实收折算 N' = 实收/¥0.01 次入账(多付多得)。
203
- const PER_CALL_CENTS = PRICE_CENTS; // 恒为 1
204
202
  const ORDER_TTL_DAYS = 365;
205
203
 
206
204
  let ledger = { version: 1, priceCents: PRICE_CENTS, createdAt: new Date().toISOString(), keys: {}, orders: {} };
@@ -313,8 +311,7 @@ function extractKey(req, query) {
313
311
  if (m) return m[1].trim();
314
312
  const xa = (req.headers['x-api-key'] || '').toString().trim();
315
313
  if (xa) return xa;
316
- const q = query ? (query.get('key') || '').trim() : '';
317
- if (q) return q;
314
+ // 凭证不再走 URL 查询串:?key= 会被反代写进访问日志(泄露即可盗刷余额)
318
315
  return null;
319
316
  }
320
317
 
@@ -398,23 +395,23 @@ function paymentInfo(orderId, amountCents) {
398
395
  payToName: PINNED_ACCOUNT_NAME,
399
396
  payToQr: PAY_TO_QR || null, // 已由上方护栏保证:非钉死码址一律 fail-closed,走到这里必是可信码
400
397
  receiptVerified: true, // 账号已与钉死值比对一致,客户端可据此信任
401
- qrTampered: RECEIPT_QR_WARN || undefined,
402
398
  rateNote: '本服务按次计费 ' + yuan(PRICE_CENTS) + '/次。对公静态收款不强制每笔恰收 1 分(银行也不支持 1 分转账),' +
403
399
  '实际可付任意「自愿支持额」,我们将按 ' + yuan(PRICE_CENTS) + '/次 折算调用次数入账(收到 ' + yuan(500) + ' 即入账 500 次)。',
404
400
  instructions: [
405
- '1. POST /pay/order 创建订单,响应给出订单号(形如 LS-YYYYMMDD-xxxxxx)与专属 key(key 只出现这一次,请立即保存)',
406
- '2. 向对公账户(payTo / payToQr)转账任意支持额,务必在付款备注中填写订单号',
407
- '3. 作者拿对公流水跑 reconcile-bank.js,按「订单号 + 金额」批量入账(按 ' + yuan(PRICE_CENTS) + '/次 折算),通常数日内到账;期间可先用 honorPaid:true 免费调用',
408
- '4. 之后每次调用带上 Authorization: Bearer <key> 即可'
401
+ '1. POST /pay/order 创建订单,响应给出订单号(形如 LS-YYYYMMDD-xxxxxx)、专属 key(key 只出现这一次,请立即保存)与 payUrl',
402
+ '2. 打开 payUrl 让付款人扫码(微信 / 支付宝 / 银联均可),或向对公账户转账;付款备注**建议**填写订单号(不填也能付款与入账,只是无法自动对应到订单)',
403
+ '3. ★ 付款完成后**自助入账、立即生效、零人工**:调 MCP pay 工具,入参 {"orderId":"<订单号>","selfReportPaid":true} → 按订单面值入账(' + yuan(PRICE_CENTS) + '/次),**不需要等任何人确认、不需要发邮件**',
404
+ '4. 之后每次调用带上 Authorization: Bearer <key> 即可',
405
+ '5. 不想付款也行:任一 solve 传 honorPaid:true 即免费放行 —— 本服务不做强制收费'
409
406
  ]
410
407
  };
411
- // 带订单号的直达付款链接:只有付款页拿到有效订单号才会展示收款码
408
+ // 带订单号的直达付款链接:付款页打开即展示收款码(未带订单号也照样出码,带订单号只是为了能自动对应)
412
409
  if (PAY_PAGE && orderId) {
413
410
  info.payUrl = PAY_PAGE +
414
411
  (PAY_PAGE.indexOf('?') >= 0 ? '&' : '?') +
415
412
  'order=' + encodeURIComponent(orderId) +
416
413
  (amountCents > 0 ? '&amount=' + amountCents : '');
417
- info.payUrlNotice = '打开此链接按提示付款;付款备注必须填写订单号 ' + orderId + '。';
414
+ info.payUrlNotice = '打开此链接即可扫码付款。付款备注建议填写订单号 ' + orderId + '(不填也能入账,只是无法自动对应)。';
418
415
  }
419
416
  return info;
420
417
  }
@@ -442,6 +439,7 @@ function payIntent(orderId, calls, amountCents) {
442
439
  return {
443
440
  protocol: PAY_INTENT_PROTOCOL,
444
441
  orderId: orderId,
442
+ payUrl: info.payUrl || null, // 带订单号的付款页:Agent 优先把这个给付款人(打开即出收款码)
445
443
  payTo: info.payTo || null,
446
444
  payToQr: info.payToQr || null,
447
445
  ratePerCallCents: PRICE_CENTS,
@@ -451,9 +449,9 @@ function payIntent(orderId, calls, amountCents) {
451
449
  suggestedAmountDisplay: yuan(amountCents),
452
450
  memo: orderId, // 付款备注建议含订单号,便于需要时与银行流水交叉核对
453
451
  agentSteps: [
454
- '1. 向钱包持有者展示 payToQr(或 payTo 账户信息)。',
455
- '2. 请持有者向该对公聚合码转账(建议 ' + yuan(amountCents) + ');备注建议含订单号 ' + orderId + '。',
456
- '3. 持有者付款完成后:用 orderId 调 MCP pay 工具,入参 {"orderId":"' + orderId + '","selfReportPaid":true} → **立即入账、立即放行**(服务端不验证,信任制)。',
452
+ '1. 把 payUrl(若存在)展示给付款人 —— 那是带订单号的付款页,打开即显示收款码;无 payUrl 时展示 payTo 账户信息。',
453
+ '2. 请付款人扫码或转账(本单面值 ' + yuan(amountCents) + ');备注建议含订单号 ' + orderId + '(不填也能入账,只是无法自动对应)。',
454
+ '3. 付款完成后:用 orderId 调 MCP pay 工具,入参 {"orderId":"' + orderId + '","selfReportPaid":true} → **立即入账、立即放行**(服务端不验证,信任制)。不需要等任何人、不需要发邮件。',
457
455
  '4. 入账后用 apiKey 调 solve 即不再走信任制:Authorization: Bearer <key>。',
458
456
  '5. 若不便付款或缺凭证:直接用 honorPaid:true 免费调用,效果相同(本服务不做强制验证)。'
459
457
  ],
@@ -462,15 +460,6 @@ function payIntent(orderId, calls, amountCents) {
462
460
  };
463
461
  }
464
462
 
465
- /**
466
- * 组装付款信息(对公收款)。
467
- * 只有「对公静态收款」一种通道,不接任何支付平台商户 API——无回调、无异步、无平台依赖。
468
- * 保留回调式签名(cb)仅为兼容 createOrder 的调用点;内部直接同步返回 paymentInfo。
469
- */
470
- function buildPayment(req, pref, orderId, amountCents, calls, cb) {
471
- return cb({ payment: paymentInfo(orderId, amountCents) });
472
- }
473
-
474
463
  function paywallError(reason, ctx) {
475
464
  const k = (ctx && ctx.keyHash) ? ledger.keys[ctx.keyHash] : null;
476
465
  const msgMap = {
@@ -482,7 +471,7 @@ function paywallError(reason, ctx) {
482
471
  };
483
472
  const extra = (reason === 'insecure_transport')
484
473
  ? { secureEndpointHint: '请把客户端里的端点从 http://... 换成 https://<你的域名>/mcp(X-Forwarded-Proto 必须是 https)。' }
485
- : { authHeader: 'Authorization: Bearer <key>(亦支持 x-api-key 或 ?key=<key>)' };
474
+ : { authHeader: 'Authorization: Bearer <key>(亦支持 x-api-key 请求头)' };
486
475
 
487
476
  // 付款入口必须直达:调用方撞到「余额不足」时,不应还得再问一次「那我去哪付」。
488
477
  // 只列已配置的收款方式;未配置就明说,绝不给出死链。
@@ -496,7 +485,7 @@ function paywallError(reason, ctx) {
496
485
  '3. ★ 付款完成后自助入账:调 MCP pay 工具,入参 {"orderId":"<订单号>","selfReportPaid":true} → 立即入账、立即放行(零人工,不用等任何人)',
497
486
  '4. 之后带 Authorization: Bearer <key> 调用即可(不再走信任制,也不再需要 honorPaid)',
498
487
  '5. 说明:自助入账不做验证(信任制);账本会标注 amountVerified:false,真营收仍以银行流水核对为准',
499
- '6. 对公来款若无法对应到任何订单/无法核对的,一律原路退回(资金处理承诺)'
488
+ '6. 对公来款若无法对应到任何订单:不会被自动折算成额度;需要核对 / 开票 / 退回,请邮件联系(联系方式见 paymentPage)'
500
489
  ];
501
490
  } else {
502
491
  pay.adminTodo = '服务端未配置 LS_PAY_TO:订单可创建但无法付款,请先在服务端配置收款方式。';
@@ -623,7 +612,7 @@ function readBody(req, cb) {
623
612
  req.on('data', (c) => {
624
613
  n += c.length;
625
614
  // 超限必须立刻回调,否则 req.destroy() 后 'end' 不再触发 → 响应永不返回(客户端挂死)
626
- if (n > 64 * 1024) { try { req.destroy(); } catch (_e) {} return finish({ err: 'body_too_large' }); }
615
+ if (n > MAX_BODY_BYTES) { try { req.destroy(); } catch (_e) {} return finish({ err: 'body_too_large' }); }
627
616
  raw += c;
628
617
  });
629
618
  req.on('end', () => {
@@ -659,7 +648,7 @@ function pricingDoc(req) {
659
648
  },
660
649
  model: '按次付费:每次 solve 固定收费 ' + PRICE_CENTS + ' 分钱(' + yuan(PRICE_CENTS) + '),不预充、无其他金额、无其他档位。',
661
650
  freeTools: ['initialize', 'tools/list', 'give_feedback', 'GET /credit', 'GET /pricing'],
662
- auth: 'Authorization: Bearer <key>(亦支持 x-api-key 或 ?key=<key>)',
651
+ auth: 'Authorization: Bearer <key>(亦支持 x-api-key 请求头)',
663
652
  howToBuy: [
664
653
  'POST ' + base + '/pay/order (每次一笔 1 分钱订单;body 不支持 calls/plan 预购,下单恒为 1 次)',
665
654
  '响应给出 orderId 与 apiKey(apiKey 只出现一次,请立即保存)',
@@ -668,6 +657,12 @@ function pricingDoc(req) {
668
657
  'GET ' + base + '/pay/order?orderId=<id> 查询订单状态',
669
658
  'GET ' + base + '/credit 携带 key 查询余额(免费)'
670
659
  ],
660
+ legal: {
661
+ positioning: '本服务定性为「软件授权 / 技术服务」的计算工具,非经营性互联网信息服务。',
662
+ privacy: 'https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/privacy.html',
663
+ terms: 'https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/terms.html',
664
+ pricing: 'https://hclj-1409755229.cos.ap-guangzhou.myqcloud.com/lingshu-solver/pricing.html'
665
+ },
671
666
  payment: paymentInfo(),
672
667
  paymentChannels: availableChannels(),
673
668
  creditModes: {
@@ -1060,7 +1055,7 @@ function handleRpc(msg, ip, ctx) {
1060
1055
  apiKeyNotice: '此 key 仅在本响应出现一次,请立即保存;付款后凭它调用 solve 即不再走信任制。',
1061
1056
  amountCents: o.amountCents,
1062
1057
  amountDisplay: yuan(o.amountCents),
1063
- payable: !!(o.payment && (o.payment.configured || o.payment.payUrl || o.payment.codeUrl || o.payment.mode === 'auto')),
1058
+ payable: !!(o.payment && (o.payment.configured || o.payment.payUrl)),
1064
1059
  payment: o.payment,
1065
1060
  // ⭐ 零人工闭环:付款完再调一次 pay 传 orderId + selfReportPaid:true 即立即入账。
1066
1061
  // 这样收款方(作者)不需要跑任何对账脚本、不需要看任何流水。
@@ -1330,7 +1325,7 @@ const server = http.createServer((req, res) => {
1330
1325
  return sendJson(res, 200, { metering: 'off', note: '本端点未启用计费,调用免费。', freeAlternatives: freeAlternatives() });
1331
1326
  }
1332
1327
  if (!ctx.key) {
1333
- return sendJson(res, 401, { error: { type: 'missing_key', message: '缺少凭证:请带 Authorization: Bearer <key> 或 ?key=<key>。', howToGetKey: paywallError('missing_key', ctx).howToGetKey } });
1328
+ return sendJson(res, 401, { error: { type: 'missing_key', message: '缺少凭证:请带 Authorization: Bearer <key> 请求头。', howToGetKey: paywallError('missing_key', ctx).howToGetKey } });
1334
1329
  }
1335
1330
  const k = ledger.keys[ctx.keyHash];
1336
1331
  if (!k) {
@@ -1374,7 +1369,7 @@ const server = http.createServer((req, res) => {
1374
1369
  amountDisplay: yuan(r.amountCents),
1375
1370
  priceCentsPerCall: PRICE_CENTS,
1376
1371
  status: 'pending',
1377
- payable: !!(payment && (payment.configured || payment.payUrl || payment.codeUrl || payment.mode === 'auto')),
1372
+ payable: !!(payment && (payment.configured || payment.payUrl)),
1378
1373
  payment: payment,
1379
1374
  paymentChannels: availableChannels(),
1380
1375
  payIntent: payIntent(orderId, r.calls, r.amountCents),
package/index.html CHANGED
@@ -2709,6 +2709,57 @@ function gaussianSolve(A, b) {
2709
2709
  return { solution: x, rank: rank };
2710
2710
  }
2711
2711
 
2712
+ // 秩感知矩形高斯消元(过定/欠定线性系统通用):sound 判定一致性与解数
2713
+ // 返回 { consistent, unique, solution };consistent=false 表示无公共实解(已严格证明);
2714
+ // unique=false 表示欠定(无穷多解),solution 为一组特解(自由变量取 0)。
2715
+ function gaussianSolveRect(A, b) {
2716
+ const m = A.length;
2717
+ if (m === 0) return null;
2718
+ const n = A[0].length;
2719
+ const EPS = 1e-9;
2720
+ // 构造增广矩阵 [A | b]
2721
+ const aug = A.map((row, i) => row.slice().concat([b[i]]));
2722
+ let rank = 0;
2723
+ const pivCol = [];
2724
+ for (let col = 0; col < n; col++) {
2725
+ // 部分选主元(在已确定秩以下的行中找最大绝对值)
2726
+ let sel = -1, maxVal = EPS;
2727
+ for (let r = rank; r < m; r++) {
2728
+ if (Math.abs(aug[r][col]) > maxVal) { maxVal = Math.abs(aug[r][col]); sel = r; }
2729
+ }
2730
+ if (sel === -1) continue; // 本列在余下行全为 0,跳过
2731
+ if (sel !== rank) { const t = aug[rank]; aug[rank] = aug[sel]; aug[sel] = t; }
2732
+ const pv = aug[rank][col];
2733
+ // 消去其他所有行(含上方),使本列仅 pivot 行非零 → 直达行最简形
2734
+ for (let r = 0; r < m; r++) {
2735
+ if (r === rank) continue;
2736
+ const f = aug[r][col] / pv;
2737
+ if (Math.abs(f) < EPS) continue;
2738
+ for (let c = col; c <= n; c++) aug[r][c] -= f * aug[rank][c];
2739
+ }
2740
+ pivCol.push(col);
2741
+ rank++;
2742
+ }
2743
+ // 一致性:存在"A 全零但 b 非零"的行 → 不相容(无公共实解)
2744
+ for (let r = 0; r < m; r++) {
2745
+ let allZero = true;
2746
+ for (let c = 0; c < n; c++) { if (Math.abs(aug[r][c]) > EPS) { allZero = false; break; } }
2747
+ if (allZero && Math.abs(aug[r][n]) > 1e-7) return { consistent: false };
2748
+ }
2749
+ // 构造特解(自由变量取 0)
2750
+ const x = new Array(n).fill(0);
2751
+ for (let r = 0; r < rank; r++) {
2752
+ const pc = pivCol[r];
2753
+ const coeff = aug[r][pc];
2754
+ if (Math.abs(coeff) < EPS) continue;
2755
+ x[pc] = aug[r][n] / coeff;
2756
+ }
2757
+ if (rank < n) {
2758
+ return { consistent: true, unique: false, solution: x }; // 欠定:无穷多解
2759
+ }
2760
+ return { consistent: true, unique: true, solution: x }; // 满列秩:唯一解
2761
+ }
2762
+
2712
2763
  function extractVarCoefficient(node, varName) {
2713
2764
  if (!hasVariable(node, [varName])) {
2714
2765
  return { coeff: { type: 'num', value: 0 }, rest: node };
@@ -4689,7 +4740,13 @@ function suan7(state) {
4689
4740
  // 跳过已从前提取过值的方程,避免浮点噪声误判
4690
4741
  if (unknownVars.length === 0) {
4691
4742
  if (!resolvedEqs[ei]) {
4692
- var val = evalAST(eq, known);
4743
+ // 用网格精度求值:known 经前向传播/二分可能带 ~1e-10 浮点噪声,
4744
+ // 若直接用 1e-12 阈值会把这个噪声误判为"矛盾"(unsound,丢真解)。
4745
+ // 网格精度与求解器输出精度一致,是 sound 的判定基准:真矛盾残差在
4746
+ // 网格尺度下仍远大于 0,数值噪声在网格下归零。
4747
+ var _knownGrid = {};
4748
+ for (var _kk in known) _knownGrid[_kk] = roundToGrid(known[_kk]);
4749
+ var val = evalAST(eq, _knownGrid);
4693
4750
  if (isFinite(val) && Math.abs(val) > 1e-12) {
4694
4751
  state.done = true;
4695
4752
  var msg = "前向传播检测到矛盾:代入已知值后方程不成立(残差=" + val.toExponential(2) + ")";
@@ -5285,7 +5342,9 @@ function suan16(state) {
5285
5342
  // suan17: 线性方程组高斯消元 → 全线性系统直接求解
5286
5343
  function suan17(state) {
5287
5344
  if (!state.eqFeatures.allLinear) return;
5288
- if (state.equations.length !== state.varNames.length) return;
5345
+ // 放开过定(m>n)与欠定(m<n)的"全线性"统一处理;仅当方程数 < 变量数 时此处不强行处理
5346
+ // (交其他路径),以最小化改动面并保留既有欠定逻辑。
5347
+ if (state.equations.length < state.varNames.length) return;
5289
5348
 
5290
5349
  var A = [], b = [];
5291
5350
  for (var ei = 0; ei < state.equations.length; ei++) {
@@ -5294,9 +5353,45 @@ function suan17(state) {
5294
5353
  b.push(-lc.constant);
5295
5354
  }
5296
5355
 
5297
- var result = gaussianSolve(A, b);
5356
+ var result = (state.equations.length === state.varNames.length)
5357
+ ? gaussianSolve(A, b) // 方阵:原 sound 路径
5358
+ : gaussianSolveRect(A, b); // 过定:秩感知判定(sound)
5298
5359
  if (!result) return;
5299
5360
 
5361
+ // 过定且不相容 → sound 地报"无实数解"(绝非"漏解")
5362
+ if (result.consistent === false) {
5363
+ state.done = true;
5364
+ state.result = {
5365
+ solutions: [], error: "NO_SOLUTION", provenEmpty: true,
5366
+ message: "过定线性方程组不相容:经高斯消元 + 秩判定严格确认无实数公共解",
5367
+ executionPath: "高斯消元(秩判定)",
5368
+ timeMs: performance.now() - state.startTime,
5369
+ confidence: "high", varNames: state.varNames,
5370
+ resultType: 1, resultTypeName: "空结果", resultTypeDesc: "过定系统秩判定无实解"
5371
+ };
5372
+ return;
5373
+ }
5374
+ // 欠定(秩 < 变量数,尽管 m>=n 但方程线性相关) → 无穷多解,给一组特解
5375
+ if (result.unique === false) {
5376
+ var pSol = {};
5377
+ state.varNames.forEach(function(v, i) { pSol[v] = roundToGrid(result.solution[i]); });
5378
+ var pVals = state.varNames.map(function(v) { return pSol[v]; });
5379
+ var pVars = {};
5380
+ state.varNames.forEach(function(v, i) { pVars[v] = pSol[v]; });
5381
+ var pRes = state.equations.map(function(eq) { return Math.abs(evalAST(eq, pVars)); });
5382
+ var pMax = Math.max.apply(null, pRes);
5383
+ state.done = true;
5384
+ state.result = {
5385
+ solutions: [{ values: pVals, residual: pMax }],
5386
+ message: "线性方程组无穷多解(秩 < 变量数,方程线性相关):给出一组特解(自由变量取 0),任意线性组合均满足",
5387
+ executionPath: "高斯消元(秩判定)",
5388
+ timeMs: performance.now() - state.startTime,
5389
+ confidence: "high", varNames: state.varNames,
5390
+ resultType: 3, resultTypeName: "无限解集(推荐解)", resultTypeDesc: "欠定线性系统,无穷多实解"
5391
+ };
5392
+ return;
5393
+ }
5394
+
5300
5395
  var solution = {};
5301
5396
  state.varNames.forEach(function(v, i) { solution[v] = roundToGrid(result.solution[i]); });
5302
5397
  var values = state.varNames.map(function(v) { return solution[v]; });
@@ -11097,7 +11192,7 @@ function _runTail(state) {
11097
11192
 
11098
11193
  // —— 可审计结构化溯源(切片A):每个结果附带完整证据链元数据 ——
11099
11194
  // 设计目标:Agent / 上层系统拿到的不只是裸数字,而是"为什么是这个解"的可读证据链。
11100
- var SOLVER_VERSION = "lingshu-solver/1.0.8-auditable";
11195
+ var SOLVER_VERSION = "lingshu-solver/1.0.11-auditable";
11101
11196
 
11102
11197
  // Movability 完整读取(切片B):每变量携带 status + width,供 Agent 读懂"为何是这个解"
11103
11198
  function _movabilityFull(state) {
package/mcp-server.js CHANGED
@@ -19,7 +19,7 @@ const path = require('path');
19
19
  const { solve } = require('./solver-core');
20
20
 
21
21
  const SERVER_NAME = 'lingshu-solver';
22
- const SERVER_VERSION = '4.1.0';
22
+ const SERVER_VERSION = '1.0.11';
23
23
 
24
24
  // ---- 护栏常量(防畸形/恶意输入耗尽资源)----
25
25
  const MAX_TOTAL_CHARS = 100 * 1024; // 单次请求方程文本总长上限 100KB
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lingshu-solver",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "灵数求解器 · 确定性实数方程组求解 MCP 工具(≤6变量):区间收缩 + Krawczyk 认证,离线零依赖零数据,诚实标记 truncated。本地 stdio 永久免费;另有常驻托管端点 https://hongchenlingjing.com/mcp(按次 ¥0.01/次,支持 honorPaid 免费放行)。",
5
5
  "homepage": "https://genesis-plan.github.io/lingshu-solver/",
6
6
  "repository": {
package/solver-core.js CHANGED
@@ -1,117 +1,116 @@
1
- /**
2
- * 灵数求解器 · 引擎加载器(零依赖)
3
- *
4
- * 单一事实来源:桌面真源 index.html 里的 <script id="solver-core">。
5
- * 本文件不重写任何求解逻辑——它把那段已验证的脚本在 vm 沙箱里实跑,
6
- * 取出其中的 solve() 暴露给 Node / MCP 服务端复用,从而保证
7
- * 「浏览器内 UI」与「MCP 服务端」调用的是同一份核心代码(同源、零分叉、零新 bug)。
8
- *
9
- * 用法:
10
- * const { solve } = require('./solver-core');
11
- * const r = solve(['x^2 = 4'], [], 6);
12
- */
13
- 'use strict';
14
- const fs = require('fs');
15
- const vm = require('vm');
16
- const path = require('path');
17
- const { performance } = require('perf_hooks');
18
-
19
- /**
20
- * 递归 no-op 桩:任意属性访问 / 调用 / 赋值都安全。
21
- * 用于顶层的 document / window 等浏览器对象,避免 UI 初始化代码在 Node 下崩溃。
22
- */
23
- function makeNoop() {
24
- const fn = function () { return p; };
25
- const p = new Proxy(fn, {
26
- get(_t, prop) {
27
- if (prop === 'value' || prop === 'textContent' || prop === 'innerHTML') return '';
28
- if (prop === 'style' || prop === 'classList') return {};
29
- if (prop === Symbol.toPrimitive) return function () { return ''; };
30
- if (prop === 'length') return 0;
31
- return p;
32
- },
33
- set() { return true; },
34
- apply() { return p; },
35
- construct() { return p; }
36
- });
37
- return p;
38
- }
39
-
40
- function resolveHtmlPath() {
41
- if (process.env.LINGSHU_HTML && fs.existsSync(process.env.LINGSHU_HTML)) {
42
- return process.env.LINGSHU_HTML;
43
- }
44
- const candidates = [
45
- path.resolve(__dirname, 'index.html'),
46
- path.resolve(__dirname, '..', 'index.html'),
47
- 'C:/Users/Administrator/Desktop/灵数求解器/index.html'
48
- ];
49
- for (const c of candidates) {
50
- if (fs.existsSync(c)) return c;
51
- }
52
- throw new Error('灵数求解器 index.html 未找到;请设置环境变量 LINGSHU_HTML 指向它。');
53
- }
54
-
55
- let _sandbox = null;
56
-
57
- function loadSandbox() {
58
- const htmlPath = resolveHtmlPath();
59
- const html = fs.readFileSync(htmlPath, 'utf8');
60
- const m = html.match(/<script id="solver-core">([\s\S]*?)<\/script>/);
61
- if (!m) throw new Error('未在 index.html 中找到 <script id="solver-core">。');
62
- const code = m[1];
63
-
64
- const sandbox = {};
65
- const noop = makeNoop();
66
-
67
- // 让 window/self/globalThis 都指向 sandbox 自身,
68
- // 这样顶层 function solve(){} 与 window.xxx= 赋值都落到 sandbox,可被取到。
69
- sandbox.window = sandbox;
70
- sandbox.self = sandbox;
71
- sandbox.globalThis = sandbox;
72
-
73
- // 浏览器对象桩
74
- sandbox.document = noop;
75
- sandbox.navigator = { serviceWorker: null, userAgent: 'node-lingshu' };
76
- sandbox.localStorage = {
77
- _d: {},
78
- getItem(k) { return this._d[k] != null ? this._d[k] : null; },
79
- setItem(k, v) { this._d[k] = String(v); },
80
- removeItem(k) { delete this._d[k]; }
81
- };
82
- sandbox.location = { href: 'file://' + htmlPath };
83
- sandbox.performance = performance;
84
- sandbox.console = console;
85
- sandbox.setTimeout = setTimeout;
86
- sandbox.clearTimeout = clearTimeout;
87
- sandbox.addEventListener = function () {};
88
- sandbox.removeEventListener = function () {};
89
- sandbox.requestAnimationFrame = function (cb) { return setTimeout(cb, 0); };
90
- sandbox.cancelAnimationFrame = function () {};
91
-
92
- vm.createContext(sandbox);
93
- // 实跑整段核心脚本(顶层 UI 初始化靠 noop 桩安全通过)
94
- vm.runInContext(code, sandbox, { filename: 'solver-core.js' });
95
-
96
- if (typeof sandbox.solve !== 'function') {
97
- throw new Error('核心脚本中未找到 solve() 函数;可能 index.html 结构已变更。');
98
- }
99
- return sandbox;
100
- }
101
-
102
- function getSandbox() {
103
- if (!_sandbox) _sandbox = loadSandbox();
104
- return _sandbox;
105
- }
106
-
107
- module.exports = {
108
- /** @returns {Function} 求解入口 solve(equationStrs, varNames, decimals, initialD0, fastMode, opts) */
109
- solve: function () {
110
- const sb = getSandbox();
111
- return sb.solve.apply(sb, arguments);
112
- },
113
- /** 取原始沙箱(高级用法:访问 EXAMPLES 等) */
114
- raw: function () { return getSandbox(); },
115
- /** 仅用于测试:重置缓存,强制重新加载 */
116
- _reset: function () { _sandbox = null; }
117
- };
1
+ /**
2
+ * 灵数求解器 · 引擎加载器(零依赖)
3
+ *
4
+ * 单一事实来源:桌面真源 index.html 里的 <script id="solver-core">。
5
+ * 本文件不重写任何求解逻辑——它把那段已验证的脚本在 vm 沙箱里实跑,
6
+ * 取出其中的 solve() 暴露给 Node / MCP 服务端复用,从而保证
7
+ * 「浏览器内 UI」与「MCP 服务端」调用的是同一份核心代码(同源、零分叉、零新 bug)。
8
+ *
9
+ * 用法:
10
+ * const { solve } = require('./solver-core');
11
+ * const r = solve(['x^2 = 4'], [], 6);
12
+ */
13
+ 'use strict';
14
+ const fs = require('fs');
15
+ const vm = require('vm');
16
+ const path = require('path');
17
+ const { performance } = require('perf_hooks');
18
+
19
+ /**
20
+ * 递归 no-op 桩:任意属性访问 / 调用 / 赋值都安全。
21
+ * 用于顶层的 document / window 等浏览器对象,避免 UI 初始化代码在 Node 下崩溃。
22
+ */
23
+ function makeNoop() {
24
+ const fn = function () { return p; };
25
+ const p = new Proxy(fn, {
26
+ get(_t, prop) {
27
+ if (prop === 'value' || prop === 'textContent' || prop === 'innerHTML') return '';
28
+ if (prop === 'style' || prop === 'classList') return {};
29
+ if (prop === Symbol.toPrimitive) return function () { return ''; };
30
+ if (prop === 'length') return 0;
31
+ return p;
32
+ },
33
+ set() { return true; },
34
+ apply() { return p; },
35
+ construct() { return p; }
36
+ });
37
+ return p;
38
+ }
39
+
40
+ function resolveHtmlPath() {
41
+ if (process.env.LINGSHU_HTML && fs.existsSync(process.env.LINGSHU_HTML)) {
42
+ return process.env.LINGSHU_HTML;
43
+ }
44
+ const candidates = [
45
+ path.resolve(__dirname, 'index.html'),
46
+ path.resolve(__dirname, '..', 'index.html')
47
+ ];
48
+ for (const c of candidates) {
49
+ if (fs.existsSync(c)) return c;
50
+ }
51
+ throw new Error('灵数求解器 index.html 未找到;请设置环境变量 LINGSHU_HTML 指向它。');
52
+ }
53
+
54
+ let _sandbox = null;
55
+
56
+ function loadSandbox() {
57
+ const htmlPath = resolveHtmlPath();
58
+ const html = fs.readFileSync(htmlPath, 'utf8');
59
+ const m = html.match(/<script id="solver-core">([\s\S]*?)<\/script>/);
60
+ if (!m) throw new Error('未在 index.html 中找到 <script id="solver-core">。');
61
+ const code = m[1];
62
+
63
+ const sandbox = {};
64
+ const noop = makeNoop();
65
+
66
+ // 让 window/self/globalThis 都指向 sandbox 自身,
67
+ // 这样顶层 function solve(){} 与 window.xxx= 赋值都落到 sandbox,可被取到。
68
+ sandbox.window = sandbox;
69
+ sandbox.self = sandbox;
70
+ sandbox.globalThis = sandbox;
71
+
72
+ // 浏览器对象桩
73
+ sandbox.document = noop;
74
+ sandbox.navigator = { serviceWorker: null, userAgent: 'node-lingshu' };
75
+ sandbox.localStorage = {
76
+ _d: {},
77
+ getItem(k) { return this._d[k] != null ? this._d[k] : null; },
78
+ setItem(k, v) { this._d[k] = String(v); },
79
+ removeItem(k) { delete this._d[k]; }
80
+ };
81
+ sandbox.location = { href: 'file://' + htmlPath };
82
+ sandbox.performance = performance;
83
+ sandbox.console = console;
84
+ sandbox.setTimeout = setTimeout;
85
+ sandbox.clearTimeout = clearTimeout;
86
+ sandbox.addEventListener = function () {};
87
+ sandbox.removeEventListener = function () {};
88
+ sandbox.requestAnimationFrame = function (cb) { return setTimeout(cb, 0); };
89
+ sandbox.cancelAnimationFrame = function () {};
90
+
91
+ vm.createContext(sandbox);
92
+ // 实跑整段核心脚本(顶层 UI 初始化靠 noop 桩安全通过)
93
+ vm.runInContext(code, sandbox, { filename: 'solver-core.js' });
94
+
95
+ if (typeof sandbox.solve !== 'function') {
96
+ throw new Error('核心脚本中未找到 solve() 函数;可能 index.html 结构已变更。');
97
+ }
98
+ return sandbox;
99
+ }
100
+
101
+ function getSandbox() {
102
+ if (!_sandbox) _sandbox = loadSandbox();
103
+ return _sandbox;
104
+ }
105
+
106
+ module.exports = {
107
+ /** @returns {Function} 求解入口 solve(equationStrs, varNames, decimals, initialD0, fastMode, opts) */
108
+ solve: function () {
109
+ const sb = getSandbox();
110
+ return sb.solve.apply(sb, arguments);
111
+ },
112
+ /** 取原始沙箱(高级用法:访问 EXAMPLES 等) */
113
+ raw: function () { return getSandbox(); },
114
+ /** 仅用于测试:重置缓存,强制重新加载 */
115
+ _reset: function () { _sandbox = null; }
116
+ };