visualproof 0.3.0 → 0.4.1
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 +52 -11
- package/dist/assets/tips/phone-fingers.light.mp4 +0 -0
- package/dist/assets/tips/phone-fingers.light.webp +0 -0
- package/dist/assets/tips/tour-agent.webp +0 -0
- package/dist/assets/tips/tour-arrange.webp +0 -0
- package/dist/assets/tips/tour-artwork.webp +0 -0
- package/dist/assets/tips/tour-checks.webp +0 -0
- package/dist/assets/tips/tour-export.webp +0 -0
- package/dist/assets/tips/tour-keys.webp +0 -0
- package/dist/assets/tips/tour-library.webp +0 -0
- package/dist/assets/tips/tour-project.webp +0 -0
- package/dist/assets/tips/tour-review.webp +0 -0
- package/dist/assets/tips/tour-share.webp +0 -0
- package/dist/assets/tips/tour-source.webp +0 -0
- package/dist/assets/tips/tour-target.webp +0 -0
- package/dist/assets/tips/tour-view.webp +0 -0
- package/dist/assets/tips/tour.json +289 -0
- package/dist/assets/tips/tour.webp +0 -0
- package/dist/mcp/visualproof-mcp.mjs +3 -1
- package/dist/server/agent.js +10 -5
- package/dist/server/children.js +85 -0
- package/dist/server/doctor.js +105 -42
- package/dist/server/events.js +10 -0
- package/dist/server/export.js +11 -4
- package/dist/server/extract.js +3 -2
- package/dist/server/figures.js +3 -2
- package/dist/server/film.js +380 -0
- package/dist/server/history.js +11 -1
- package/dist/server/hover.js +8 -5
- package/dist/server/live.js +66 -9
- package/dist/server/main.js +217 -16
- package/dist/server/media.js +114 -0
- package/dist/server/pipeline.js +6 -1
- package/dist/server/poppler.js +3 -2
- package/dist/server/readonlygate.js +4 -4
- package/dist/server/sourcestore.js +3 -2
- package/dist/server/status.js +3 -2
- package/dist/server/supervise.js +219 -0
- package/dist/server/updates.js +951 -0
- package/dist/server/usage.js +3 -1
- package/dist/server/version.js +183 -0
- package/dist/shared/timeline.js +5 -0
- package/dist/web/bundle/app.js +2299 -255
- package/dist/web/index.html +13 -1
- package/dist/web/src/canvas.js +22 -0
- package/dist/web/style.css +189 -1
- package/docs/agent-api/export.md +2 -2
- package/docs/agent-api/history.md +3 -2
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
<p align="center"><sub>Five Lean codebases, drawn by <code>visualproof artwork</code>.</sub></p>
|
|
11
11
|
|
|
12
|
+
https://github.com/user-attachments/assets/98e435d6-f493-437d-b342-1833819c0b77
|
|
13
|
+
|
|
14
|
+
<p align="center"><sub>A review of Sphere-Packing-Lean in 74 seconds. The whole tour — four minutes in thirteen chapters, with captions — is in the page under <b>?</b> → Tips, and <a href="https://visualproof-demo.yingte.io/#tips">on the live demo</a>.</sub></p>
|
|
15
|
+
|
|
12
16
|
[](https://visualproof-demo.yingte.io)
|
|
13
17
|
|
|
14
18
|
<p align="center"><b>Live demo: <a href="https://visualproof-demo.yingte.io">https://visualproof-demo.yingte.io</a></b><br><sub>flt-regular in the middle of a review by three people and an AI assistant, read-only</sub></p>
|
|
@@ -20,6 +24,12 @@ Set up in your Lean codebase with agent prompt:
|
|
|
20
24
|
Set up VisualProof (https://github.com/LucianoXu/VisualProof) for my Lean project, and give me the address to open.
|
|
21
25
|
```
|
|
22
26
|
|
|
27
|
+
Or have your codebase drawn as a poster:
|
|
28
|
+
```text
|
|
29
|
+
Draw my Lean project as a poster with VisualProof (https://github.com/LucianoXu/VisualProof): pick its main theorem, light up what its statement rests on, and give me the PNG and the SVG.
|
|
30
|
+
```
|
|
31
|
+
What the agent does, step by step: [A poster from the command line](docs/MANUAL.md#a-poster-from-the-command-line).
|
|
32
|
+
|
|
23
33
|
Set up manually:
|
|
24
34
|
```bash
|
|
25
35
|
npm install --global visualproof
|
|
@@ -27,7 +37,9 @@ cd my-lean-project && lake build
|
|
|
27
37
|
visualproof serve .
|
|
28
38
|
```
|
|
29
39
|
|
|
30
|
-
It needs Node, elan and Poppler ([Requirements](#requirements)).
|
|
40
|
+
It needs Node, elan and Poppler ([Requirements](#requirements)). On the first visit the page offers
|
|
41
|
+
a short tour of your own project; `?` holds the tips — a film of a review on Sphere-Packing-Lean in
|
|
42
|
+
thirteen scenes, with captions, and a row for each thing it does not show — and every key.
|
|
31
43
|
|
|
32
44
|
## See what a Lean theorem actually rests on
|
|
33
45
|
|
|
@@ -38,16 +50,26 @@ keep a record of the review.
|
|
|
38
50
|
|
|
39
51
|
On [flt-regular](https://github.com/leanprover-community/flt-regular), Fermat's Last Theorem for
|
|
40
52
|
regular primes. Of the project's
|
|
41
|
-
273 declarations, two light up besides the theorem, `IsRegularPrime` and `IsRegularNumber`:
|
|
42
|
-
|
|
53
|
+
273 declarations, two light up besides the theorem, `IsRegularPrime` and `IsRegularNumber`: they
|
|
54
|
+
are what you need to read to believe what it says. The Mathlib definitions they use light up too,
|
|
55
|
+
trusted and assumed as Mathlib is, unless a review chooses to read them: the walk stops at the
|
|
56
|
+
project's edge.
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<img src="docs/images/tcb-view-theorem.webp" width="32%" alt="flt_regular alone, lit on the board">
|
|
60
|
+
<img src="docs/images/tcb-view-tcb.webp" width="32%" alt="the TCB of flt_regular: IsRegularPrime, IsRegularNumber and the Mathlib definitions they use, lit with the edges between them">
|
|
61
|
+
<img src="docs/images/tcb-view-keys.webp" width="32%" alt="the key declarations of flt_regular, IsRegularPrime and FermatLastTheoremFor, joined by dashed edges">
|
|
62
|
+
</p>
|
|
63
|
+
<p align="center"><sub>The theorem alone · what its statement rests on, its TCB · the few the review marked key, read first.<br>This review no longer trusts <code>Mathlib.NumberTheory</code>, so the definitions it holds that the statement means, <code>FermatLastTheoremFor</code> among them, are read too, and can be key.</sub></p>
|
|
43
64
|
|
|
44
65
|
## What it does
|
|
45
66
|
|
|
46
67
|
- **The declaration graph** of a built Lean project, clustered by namespace or by file, and updated
|
|
47
68
|
live as you rebuild.
|
|
48
|
-
- **The TCB of any theorem**: the definitions its statement rests on, followed through
|
|
49
|
-
instances and notation, and the axioms
|
|
50
|
-
out, since Lean has checked it
|
|
69
|
+
- **The TCB of any theorem**: the project's definitions its statement rests on, followed through
|
|
70
|
+
their bodies, instances and notation, and the axioms its statement and proof use. What lies only
|
|
71
|
+
inside proofs is left out, since Lean has checked it; what lies outside the project, Mathlib's
|
|
72
|
+
definitions, is lit but trusted and assumed.
|
|
51
73
|
- **A review together**: approve or comment on each declaration, mark targets and their key
|
|
52
74
|
declarations, leave notes, link papers to declarations, and see what changed since an opinion was
|
|
53
75
|
given. Everyone on the page sees it live.
|
|
@@ -57,7 +79,8 @@ the Mathlib definitions they use, they are what you need to read to believe what
|
|
|
57
79
|
- **A printed document** of what a view holds: definitions in full, proofs left out, typeset with
|
|
58
80
|
XeLaTeX.
|
|
59
81
|
- **A picture of the board**, PNG or SVG up to poster size: from the page, from an agent's link, or
|
|
60
|
-
without a browser (`visualproof artwork <project> --recipe recipe.json
|
|
82
|
+
without a browser (`visualproof artwork <project> --recipe recipe.json`,
|
|
83
|
+
[step by step](docs/MANUAL.md#a-poster-from-the-command-line)).
|
|
61
84
|
|
|
62
85
|
## Requirements
|
|
63
86
|
|
|
@@ -80,8 +103,10 @@ places:
|
|
|
80
103
|
`~/.local/share/visualproof/sites/<project>-<hash>/` (on macOS, under
|
|
81
104
|
`~/Library/Application Support/visualproof/`). It cannot be derived again and holds secrets, so
|
|
82
105
|
back it up and keep it out of git.
|
|
83
|
-
- **The cache** (`--cache <dir>`) holds what a rebuild makes again
|
|
84
|
-
|
|
106
|
+
- **The cache** (`--cache <dir>`) holds what a rebuild makes again, and the patch releases a server
|
|
107
|
+
took (`versions/`, with `updates.json`, the last check: code the server runs, so the cache is the
|
|
108
|
+
server's user's alone). By default it is `~/.cache/visualproof/` (on macOS,
|
|
109
|
+
`~/Library/Caches/visualproof/`).
|
|
85
110
|
|
|
86
111
|
## Reviewing with others
|
|
87
112
|
|
|
@@ -97,10 +122,26 @@ project with a read-only link (nothing changes, nobody sees the reader), or give
|
|
|
97
122
|
token and a prompt. To put a project online for anyone, read-only, add `--public`. The server
|
|
98
123
|
speaks plain HTTP: beyond a trusted network, put a TLS proxy in front. [SECURITY.md](SECURITY.md) says what the server trusts and what it exposes.
|
|
99
124
|
|
|
125
|
+
## Updating
|
|
126
|
+
|
|
127
|
+
A server keeps itself up to date with patch releases (`--updates auto`, the default): it asks npm
|
|
128
|
+
once a day and takes a newer release of the same minor version, 0.4.1 after 0.4.0, at a quiet
|
|
129
|
+
moment (nobody on a page and nothing running, a day later at the latest); the pages left open
|
|
130
|
+
reload where they were. A newer minor version, which may change the command line or the site
|
|
131
|
+
folder, is only announced — in the server's log, by `visualproof doctor` and in the About of the
|
|
132
|
+
page's `?` — with the command it prints to install it, e.g.
|
|
133
|
+
`npm install --global visualproof@0.5.0`; once it is installed, the server restarts into it by
|
|
134
|
+
itself within ten minutes. `--updates notify` announces patch releases too, and `--updates off`
|
|
135
|
+
never asks npm. 0.3.0 has no updater: upgrade it to 0.4.0 by hand
|
|
136
|
+
([the manual](docs/MANUAL.md#updates)).
|
|
137
|
+
|
|
100
138
|
## Documentation
|
|
101
139
|
|
|
102
|
-
-
|
|
103
|
-
|
|
140
|
+
- **In the page**: the tour, offered on the first visit, and the tips under `?`: a film of a review
|
|
141
|
+
in thirteen scenes, each with a few words, and a row for the rest. They are the documentation to
|
|
142
|
+
start with.
|
|
143
|
+
- [The manual](docs/MANUAL.md): how to start, the concepts, then every part of the page, the review,
|
|
144
|
+
views, history, the printed document, collaboration, and how the TCB is computed.
|
|
104
145
|
- [The agents' API](docs/agent-api/README.md), which the server also serves to agents, topic by topic.
|
|
105
146
|
- [SPEC/](SPEC/): the specification, and the design decisions behind each part.
|
|
106
147
|
- [CONTRIBUTING.md](CONTRIBUTING.md), for working on VisualProof itself.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
{
|
|
2
|
+
"film": {
|
|
3
|
+
"file": "tour-9d4241345aaf.mp4",
|
|
4
|
+
"sha256": "9d4241345aaf68755b689fd0ba6b3139a2b077a68996361445b8098398079620",
|
|
5
|
+
"size": 53706647,
|
|
6
|
+
"width": 2560,
|
|
7
|
+
"height": 1600,
|
|
8
|
+
"duration": 238.96666666666667
|
|
9
|
+
},
|
|
10
|
+
"chapters": [
|
|
11
|
+
{
|
|
12
|
+
"id": "target",
|
|
13
|
+
"start": 0,
|
|
14
|
+
"end": 19.466666666666665
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "view",
|
|
18
|
+
"start": 19.466666666666665,
|
|
19
|
+
"end": 31.833333333333332
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "arrange",
|
|
23
|
+
"start": 31.833333333333332,
|
|
24
|
+
"end": 53.666666666666664
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "source",
|
|
28
|
+
"start": 53.666666666666664,
|
|
29
|
+
"end": 66.06666666666666
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "keys",
|
|
33
|
+
"start": 66.06666666666666,
|
|
34
|
+
"end": 92.8
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "checks",
|
|
38
|
+
"start": 92.8,
|
|
39
|
+
"end": 111.3
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "review",
|
|
43
|
+
"start": 111.3,
|
|
44
|
+
"end": 129.73333333333332
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "project",
|
|
48
|
+
"start": 129.73333333333332,
|
|
49
|
+
"end": 138.66666666666666
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "share",
|
|
53
|
+
"start": 138.66666666666666,
|
|
54
|
+
"end": 152
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "agent",
|
|
58
|
+
"start": 152,
|
|
59
|
+
"end": 171.33333333333334
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "library",
|
|
63
|
+
"start": 171.33333333333334,
|
|
64
|
+
"end": 199.73333333333332
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "export",
|
|
68
|
+
"start": 199.73333333333332,
|
|
69
|
+
"end": 219.23333333333332
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "artwork",
|
|
73
|
+
"start": 219.23333333333332,
|
|
74
|
+
"end": 238.96666666666667
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"captions": [
|
|
78
|
+
{
|
|
79
|
+
"start": 4.1,
|
|
80
|
+
"end": 7.9,
|
|
81
|
+
"text": "Search the theorem to review: press /."
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"start": 7.9,
|
|
85
|
+
"end": 11.9,
|
|
86
|
+
"text": "There are two MainTheorems: take the one in Dim24."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"start": 11.9,
|
|
90
|
+
"end": 15.866666666666667,
|
|
91
|
+
"text": "Mark it as the target of the review."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"start": 15.866666666666667,
|
|
95
|
+
"end": 19.466666666666665,
|
|
96
|
+
"text": "The side panel lists the targets, with their progress."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"start": 19.466666666666665,
|
|
100
|
+
"end": 25.333333333333332,
|
|
101
|
+
"text": "Rests on · Uses: everything the theorem uses."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"start": 25.333333333333332,
|
|
105
|
+
"end": 28.833333333333332,
|
|
106
|
+
"text": "Zoom in: a view of just that, in a tab of its own."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"start": 28.833333333333332,
|
|
110
|
+
"end": 31.833333333333332,
|
|
111
|
+
"text": "The view is laid out for what it holds alone."
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"start": 34.13333333333333,
|
|
115
|
+
"end": 39.5,
|
|
116
|
+
"text": "Drag a cluster with Ctrl held: it stays there, for everyone."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"start": 39.5,
|
|
120
|
+
"end": 46.53333333333333,
|
|
121
|
+
"text": "Or let the lock go with l, and drag without Ctrl."
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"start": 46.53333333333333,
|
|
125
|
+
"end": 53.666666666666664,
|
|
126
|
+
"text": "Double-click a circle to fold it to a point; again to open."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"start": 53.666666666666664,
|
|
130
|
+
"end": 58.7,
|
|
131
|
+
"text": "Space opens the source of what is selected."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"start": 58.7,
|
|
135
|
+
"end": 62.5,
|
|
136
|
+
"text": "Rest on a name: Lean says what it is."
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"start": 62.5,
|
|
140
|
+
"end": 66.06666666666666,
|
|
141
|
+
"text": "Click it to go there: its source, its panel."
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"start": 66.06666666666666,
|
|
145
|
+
"end": 69.13333333333334,
|
|
146
|
+
"text": "Keys: the definitions that carry what it says."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"start": 69.13333333333334,
|
|
150
|
+
"end": 84.53333333333333,
|
|
151
|
+
"text": "Mark each as a key with the mouse, in its panel."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"start": 84.53333333333333,
|
|
155
|
+
"end": 92.8,
|
|
156
|
+
"text": "Rests on · Keys lights them, joined to the target."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"start": 93.4,
|
|
160
|
+
"end": 96.1,
|
|
161
|
+
"text": "Selection: the statement alone."
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"start": 96.1,
|
|
165
|
+
"end": 98.9,
|
|
166
|
+
"text": "Least effort — you trust every name in it."
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"start": 98.9,
|
|
170
|
+
"end": 101.36666666666666,
|
|
171
|
+
"text": "Keys: the few that carry the meaning."
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"start": 101.36666666666666,
|
|
175
|
+
"end": 103.43333333333334,
|
|
176
|
+
"text": "Little effort, good confidence."
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"start": 103.43333333333334,
|
|
180
|
+
"end": 105.63333333333334,
|
|
181
|
+
"text": "TCB: everything it rests on."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"start": 105.63333333333334,
|
|
185
|
+
"end": 107.63333333333334,
|
|
186
|
+
"text": "Most effort, most confidence."
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"start": 107.63333333333334,
|
|
190
|
+
"end": 111.3,
|
|
191
|
+
"text": "Choose how far to read: the counts say what each costs."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"start": 111.3,
|
|
195
|
+
"end": 116.6,
|
|
196
|
+
"text": "Read a definition, then say what you think of it."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"start": 116.6,
|
|
200
|
+
"end": 125.96666666666667,
|
|
201
|
+
"text": "Approve it, with why: the comment stays with it."
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"start": 125.96666666666667,
|
|
205
|
+
"end": 129.73333333333332,
|
|
206
|
+
"text": "The target's progress counts your approval."
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"start": 129.73333333333332,
|
|
210
|
+
"end": 135.03333333333333,
|
|
211
|
+
"text": "The Project tab: the whole codebase."
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"start": 135.03333333333333,
|
|
215
|
+
"end": 138.66666666666666,
|
|
216
|
+
"text": "Every board shows the same review."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"start": 138.66666666666666,
|
|
220
|
+
"end": 145.56666666666666,
|
|
221
|
+
"text": "Share → AI agent: a token, and a prompt for it."
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"start": 145.56666666666666,
|
|
225
|
+
"end": 148.76666666666668,
|
|
226
|
+
"text": "Paste the prompt into your agent: it joins here."
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"start": 148.76666666666668,
|
|
230
|
+
"end": 152,
|
|
231
|
+
"text": "The token is the agent's own: revoke it here."
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"start": 152.7,
|
|
235
|
+
"end": 160.13333333333333,
|
|
236
|
+
"text": "Click the agent to follow it, and ask."
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"start": 160.13333333333333,
|
|
240
|
+
"end": 165.83333333333334,
|
|
241
|
+
"text": "It reads the code, and takes your view along."
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"start": 165.83333333333334,
|
|
245
|
+
"end": 171.33333333333334,
|
|
246
|
+
"text": "Its answer names what it explains."
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"start": 172.13333333333333,
|
|
250
|
+
"end": 178.23333333333332,
|
|
251
|
+
"text": "Library: add a paper about the code."
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"start": 178.23333333333332,
|
|
255
|
+
"end": 191.53333333333333,
|
|
256
|
+
"text": "Link it to the theorem, at its page."
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"start": 191.53333333333333,
|
|
260
|
+
"end": 199.73333333333332,
|
|
261
|
+
"text": "Pin a note on the target: where to start."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"start": 199.73333333333332,
|
|
265
|
+
"end": 207.66666666666666,
|
|
266
|
+
"text": "Export prints a view: here, the target's TCB."
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"start": 207.66666666666666,
|
|
270
|
+
"end": 212.26666666666668,
|
|
271
|
+
"text": "Drag a section to change the document's order."
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"start": 212.26666666666668,
|
|
275
|
+
"end": 219.23333333333332,
|
|
276
|
+
"text": "Typeset: the PDF follows the order you set."
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"start": 219.23333333333332,
|
|
280
|
+
"end": 224,
|
|
281
|
+
"text": "Artwork: the board as a picture to keep."
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"start": 224,
|
|
285
|
+
"end": 235.36666666666667,
|
|
286
|
+
"text": "Frame it, then save it as PNG or SVG."
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|
|
Binary file
|
|
@@ -41,7 +41,9 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
41
41
|
import { basename, isAbsolute } from "node:path";
|
|
42
42
|
import { createInterface } from "node:readline";
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// the version of the VisualProof server it came from: the server writes its own here as it serves
|
|
45
|
+
// the file (GET /api/agent/mcp)
|
|
46
|
+
const VERSION = "0.0.0";
|
|
45
47
|
/** the MCP revisions this server speaks, newest first; a client asking for another (a newer one,
|
|
46
48
|
* 2026-07-28 among them, whose changes it does not make) is answered with the newest of these */
|
|
47
49
|
const REVISIONS = ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
|
package/dist/server/agent.js
CHANGED
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
import { historyEndpoints, historyWorld } from "./historyapi.js";
|
|
30
30
|
import { PastBuilds } from "./pastdoc.js";
|
|
31
31
|
import { spawnSync } from "node:child_process";
|
|
32
|
+
import { packageVersion } from "./version.js";
|
|
33
|
+
import { withoutReasons } from "../shared/timeline.js";
|
|
34
|
+
import { childEnv } from "./children.js";
|
|
32
35
|
import { readFileSync } from "node:fs";
|
|
33
36
|
import { join } from "node:path";
|
|
34
37
|
import { gunzipSync } from "node:zlib";
|
|
@@ -817,7 +820,7 @@ export class AgentApi {
|
|
|
817
820
|
const advise = !this.refuse(me, "advise");
|
|
818
821
|
const byKey = new Map(this.endpoints.map((e) => [`${e.method} ${e.path}`, e]));
|
|
819
822
|
return ok({
|
|
820
|
-
about:
|
|
823
|
+
about: `VisualProof ${packageVersion()} shows a Lean project's declarations, the TCB of its theorems (what a theorem's statement rests on) and the review people make of it. This is its API for AI agents.`,
|
|
821
824
|
you: {
|
|
822
825
|
name: me.name,
|
|
823
826
|
kind: me.kind,
|
|
@@ -1160,7 +1163,9 @@ export class AgentApi {
|
|
|
1160
1163
|
if (typeof t !== "string")
|
|
1161
1164
|
return t;
|
|
1162
1165
|
try {
|
|
1163
|
-
|
|
1166
|
+
// the file carries the version of the server it came from (UpdateSpec decision 11)
|
|
1167
|
+
const body = readFileSync(MCP_FILE, "utf8").replace(/^const VERSION = "[^"\n]*";$/m, `const VERSION = ${JSON.stringify(packageVersion())};`);
|
|
1168
|
+
return { status: 200, body: null, text: { type: "text/javascript; charset=utf-8", body } };
|
|
1164
1169
|
}
|
|
1165
1170
|
catch {
|
|
1166
1171
|
return fail(404, "The MCP server is not beside this server.", "Use the HTTP API (GET /api/agent).");
|
|
@@ -2021,7 +2026,7 @@ export class AgentApi {
|
|
|
2021
2026
|
out.push({ ...rest, ...who, moved: ops.flatMap((o) => (o.op === "clear" ? [] : [o.key])).slice(0, 100), undoneBy: undone.get(e.id) ?? null });
|
|
2022
2027
|
}
|
|
2023
2028
|
else
|
|
2024
|
-
out.push({ ...e, ...who });
|
|
2029
|
+
out.push({ ...withoutReasons(e), ...who });
|
|
2025
2030
|
}
|
|
2026
2031
|
const about = (a.query.get("name") ?? "").trim();
|
|
2027
2032
|
// a note is about the declarations it names; a link, about the declaration it is on, or was on
|
|
@@ -2895,7 +2900,7 @@ export class AgentApi {
|
|
|
2895
2900
|
{
|
|
2896
2901
|
method: "GET", path: "/api/agent/activity", brief: "the timeline", about: "The timeline, oldest first: opinions and advice, targets and key marks, files of the library added, retitled, archived or restored and links made, edited or removed (all from the review's logs), builds, TCB changes, targets taken off, prefixes trusted or no longer, views saved, renamed, rescoped, deleted or laid out afresh, notes added to a view or taken out of it, notes, layouts and their undoing.",
|
|
2897
2902
|
params: { since: "an ISO time (e.g. the `now` of your last answer) or an event id: what came after", name: "optional: only what is about this declaration (opinions, advice, notes that name it, key marks, links on it; a target's own events)", limit: "default 100, at most 1000 (the newest)" },
|
|
2898
|
-
returns: "{now, events: [{at, kind, by?, agent?, …}] oldest first, more}; kinds: opinion, target, key (a key declaration as marked now: target, name), build, tcb, untarget, trust, untrust, view (a view saved, renamed, rescoped, deleted or laid out afresh: what, view, name, was?, scope; or a note of the project added to it or taken out of it: what items, note, text, shown), note (a note on a view written, edited or deleted: what, view, title, note, text), file (a file of the library: what is added, edited — its title, wasTitle the one before —, archived or restored; sha, title), link (what is made, edited or removed; id, subject, label, file, and an edit's changed (subject, label, place), wasSubject?, wasLabel?, auto? where VisualProof carried it with a renamed cluster), layout (with moved and undoneBy), undo; relayout only in a timeline kept from before views",
|
|
2903
|
+
returns: "{now, events: [{at, kind, by?, agent?, …}] oldest first, more}; kinds: opinion, target, key (a key declaration as marked now: target, name), build, tcb, untarget, trust, untrust, view (a view saved, renamed, rescoped, deleted or laid out afresh: what, view, name, was?, scope; or a note of the project added to it or taken out of it: what items, note, text, shown), note (a note on a view written, edited or deleted: what, view, title, note, text), file (a file of the library: what is added, edited — its title, wasTitle the one before —, archived or restored; sha, title), link (what is made, edited or removed; id, subject, label, file, and an edit's changed (subject, label, place), wasSubject?, wasLabel?, auto? where VisualProof carried it with a renamed cluster), layout (with moved and undoneBy), undo, tool (the VisualProof version serving the site changed: from, to, setAside?); relayout only in a timeline kept from before views",
|
|
2899
2904
|
run: (a) => this.activity(a),
|
|
2900
2905
|
},
|
|
2901
2906
|
{
|
|
@@ -3256,7 +3261,7 @@ export function commitOf(root) {
|
|
|
3256
3261
|
if (was && now - was.at < 10_000)
|
|
3257
3262
|
return was.commit;
|
|
3258
3263
|
const git = (...args) => {
|
|
3259
|
-
const r = spawnSync("git", ["-C", root, ...args], { encoding: "utf8", timeout: 5000, env: {
|
|
3264
|
+
const r = spawnSync("git", ["-C", root, ...args], { encoding: "utf8", timeout: 5000, env: childEnv({ GIT_OPTIONAL_LOCKS: "0" }), stdio: ["ignore", "pipe", "ignore"] });
|
|
3260
3265
|
return r.status === 0 ? r.stdout.trim() : null;
|
|
3261
3266
|
};
|
|
3262
3267
|
const hash = git("rev-parse", "HEAD");
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// The programs the server runs and waits for — the extractor and the Lean programs' build, the
|
|
2
|
+
// usage pass's `lake env`, Lean's language server, XeLaTeX, ImageMagick, Poppler, git, ps — kept
|
|
3
|
+
// here while they run, so that a server stopping stops them (UpdateSpec decision 9) rather than
|
|
4
|
+
// leave them working for nobody. Those that start programs of their own (Lake starts Lean, Lean's
|
|
5
|
+
// server its workers, XeLaTeX kpathsea's, ImageMagick its delegates) run in a process group of
|
|
6
|
+
// their own (`grouped`, spawn's `detached`), and are stopped with all they started; what is left
|
|
7
|
+
// at the server's exit is ended at once. The usage pass's workers have a stop of their own
|
|
8
|
+
// (server/usage.ts).
|
|
9
|
+
const running = new Set();
|
|
10
|
+
/** the children that lead a process group of their own */
|
|
11
|
+
const groups = new WeakSet();
|
|
12
|
+
/** spawn's `detached` for a program run as a group (`grouped`): its own process group, where the
|
|
13
|
+
* platform has them */
|
|
14
|
+
export const DETACH = process.platform !== "win32";
|
|
15
|
+
/** Keep `child` as `tracked` does, as the leader of a process group of its own: spawned with
|
|
16
|
+
* `detached: DETACH`, and stopped with every program it started. */
|
|
17
|
+
export function grouped(child) {
|
|
18
|
+
if (DETACH)
|
|
19
|
+
groups.add(child);
|
|
20
|
+
return tracked(child);
|
|
21
|
+
}
|
|
22
|
+
/** End `child` with `signal`: where it leads a process group, the whole group. */
|
|
23
|
+
export function end(child, signal = "SIGTERM") {
|
|
24
|
+
if (groups.has(child) && child.pid && child.exitCode === null) {
|
|
25
|
+
try {
|
|
26
|
+
process.kill(-child.pid, signal);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// the group is gone: the child alone, if it is not
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
child.kill(signal);
|
|
34
|
+
}
|
|
35
|
+
// what still runs as the server's process ends goes with it
|
|
36
|
+
process.once("exit", () => {
|
|
37
|
+
for (const c of running)
|
|
38
|
+
end(c, "SIGKILL");
|
|
39
|
+
});
|
|
40
|
+
/** Keep `child` until it exits; the same child, for `spawn(…)` to be wrapped in place. */
|
|
41
|
+
export function tracked(child) {
|
|
42
|
+
running.add(child);
|
|
43
|
+
const gone = () => running.delete(child);
|
|
44
|
+
child.once("exit", gone);
|
|
45
|
+
child.once("error", gone);
|
|
46
|
+
return child;
|
|
47
|
+
}
|
|
48
|
+
/** The environment a program the server runs is given: the server's own, without what is the
|
|
49
|
+
* server's alone (`VISUALPROOF_*`: the supervisor's word to it, its test hooks; UpdateSpec decision
|
|
50
|
+
* 9), with `extra` over it. */
|
|
51
|
+
export function childEnv(extra = {}) {
|
|
52
|
+
const env = {};
|
|
53
|
+
for (const [k, v] of Object.entries(process.env))
|
|
54
|
+
if (!k.startsWith("VISUALPROOF_"))
|
|
55
|
+
env[k] = v;
|
|
56
|
+
return { ...env, ...extra };
|
|
57
|
+
}
|
|
58
|
+
/** How many tracked programs run now. */
|
|
59
|
+
export function childCount() {
|
|
60
|
+
return running.size;
|
|
61
|
+
}
|
|
62
|
+
/** Ask every tracked program to end (SIGTERM), and end those still there after `graceMs`
|
|
63
|
+
* (SIGKILL). Resolves once none runs, or at the end of the grace; the process waits for it. */
|
|
64
|
+
export function stopChildren(graceMs = 2000) {
|
|
65
|
+
if (!running.size)
|
|
66
|
+
return Promise.resolve();
|
|
67
|
+
for (const c of running)
|
|
68
|
+
end(c, "SIGTERM");
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const t0 = Date.now();
|
|
71
|
+
const tick = setInterval(() => {
|
|
72
|
+
if (!running.size) {
|
|
73
|
+
clearInterval(tick);
|
|
74
|
+
return resolve();
|
|
75
|
+
}
|
|
76
|
+
if (Date.now() - t0 >= graceMs) {
|
|
77
|
+
for (const c of running)
|
|
78
|
+
end(c, "SIGKILL");
|
|
79
|
+
running.clear();
|
|
80
|
+
clearInterval(tick);
|
|
81
|
+
resolve();
|
|
82
|
+
}
|
|
83
|
+
}, 50);
|
|
84
|
+
});
|
|
85
|
+
}
|