visualproof 0.3.0 → 0.4.0
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 +2298 -254
- 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/dist/server/usage.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// again. A module that cannot be elaborated is reported and keeps the text-based context and no
|
|
19
19
|
// notation edges; the others are not held back by it.
|
|
20
20
|
import { spawn } from "node:child_process";
|
|
21
|
+
import { childEnv, DETACH, grouped } from "./children.js";
|
|
21
22
|
import { createHash } from "node:crypto";
|
|
22
23
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
23
24
|
import { join } from "node:path";
|
|
@@ -62,7 +63,8 @@ export function traceOptions(p, module) {
|
|
|
62
63
|
* Asked once per pass, and every worker started with it, rather than through `lake env` each. */
|
|
63
64
|
function lakeEnv(root) {
|
|
64
65
|
return new Promise((resolveP, reject) => {
|
|
65
|
-
|
|
66
|
+
// the server's own variables are not the workers': they are given this environment
|
|
67
|
+
const child = grouped(spawn("lake", ["env", "env", "-0"], { cwd: root, stdio: ["ignore", "pipe", "pipe"], detached: DETACH, env: childEnv() }));
|
|
66
68
|
const out = [];
|
|
67
69
|
let err = "";
|
|
68
70
|
child.stdout.on("data", (b) => out.push(b));
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Versions: this package's own, and the ranges package.json's `engines` writes, as `visualproof
|
|
2
|
+
// doctor` checks Node against them and a server checks a release it would take (server/updates.ts).
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
let own = null;
|
|
6
|
+
/** The version of this package: read once, as the process started it, so that a package installed
|
|
7
|
+
* over it meanwhile does not change what it says it runs (UpdateSpec decision 5). */
|
|
8
|
+
export function packageVersion() {
|
|
9
|
+
return (own ??= JSON.parse(readFileSync(join(import.meta.dirname, "..", "..", "package.json"), "utf8")).version);
|
|
10
|
+
}
|
|
11
|
+
export function parseVersion(v) {
|
|
12
|
+
const m = /(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
13
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
14
|
+
}
|
|
15
|
+
export function cmp(a, b) {
|
|
16
|
+
for (let i = 0; i < 3; i++)
|
|
17
|
+
if (a[i] !== b[i])
|
|
18
|
+
return a[i] - b[i];
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
/** npm's order: by the numbers, then a prerelease before its release, then the prerelease
|
|
22
|
+
* identifiers one by one (numbers before words, numbers by value, words as text), fewer first. */
|
|
23
|
+
function order(a, b) {
|
|
24
|
+
const c = cmp(a.v, b.v);
|
|
25
|
+
if (c)
|
|
26
|
+
return c;
|
|
27
|
+
if (!a.pre.length || !b.pre.length)
|
|
28
|
+
return (b.pre.length ? 1 : 0) - (a.pre.length ? 1 : 0);
|
|
29
|
+
for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
|
|
30
|
+
const x = a.pre[i], y = b.pre[i];
|
|
31
|
+
if (x === undefined)
|
|
32
|
+
return -1;
|
|
33
|
+
if (y === undefined)
|
|
34
|
+
return 1;
|
|
35
|
+
if (x === y)
|
|
36
|
+
continue;
|
|
37
|
+
if (typeof x === "number" && typeof y === "number")
|
|
38
|
+
return x - y;
|
|
39
|
+
if (typeof x === "number")
|
|
40
|
+
return -1;
|
|
41
|
+
if (typeof y === "number")
|
|
42
|
+
return 1;
|
|
43
|
+
return x < y ? -1 : 1;
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
/** Prerelease identifiers, as npm takes them: none empty, a number without a leading zero. */
|
|
48
|
+
function prerelease(text) {
|
|
49
|
+
if (text === undefined)
|
|
50
|
+
return [];
|
|
51
|
+
const ids = text.split(".");
|
|
52
|
+
if (ids.some((id) => !/^[0-9A-Za-z-]+$/.test(id) || /^0\d+$/.test(id)))
|
|
53
|
+
return null;
|
|
54
|
+
return ids.map((id) => (/^\d+$/.test(id) ? Number(id) : id));
|
|
55
|
+
}
|
|
56
|
+
const NUM = "0|[1-9]\\d*";
|
|
57
|
+
const PART = `(${NUM}|[xX*])`;
|
|
58
|
+
const PARTIAL = new RegExp(`^v?${PART}(?:\\.${PART})?(?:\\.${PART})?(?:-([^+]+))?(?:\\+[0-9A-Za-z.-]+)?$`);
|
|
59
|
+
function partial(text) {
|
|
60
|
+
const m = PARTIAL.exec(text);
|
|
61
|
+
if (!m)
|
|
62
|
+
return null;
|
|
63
|
+
const part = (x) => (x === undefined || /^[xX*]$/.test(x) ? null : Number(x));
|
|
64
|
+
const p = [part(m[1]), part(m[2]), part(m[3])];
|
|
65
|
+
// a wildcard leaves the parts after it wild (`1.x.3` is `1.x`)
|
|
66
|
+
for (let i = 1; i < 3; i++)
|
|
67
|
+
if (p[i - 1] === null)
|
|
68
|
+
p[i] = null;
|
|
69
|
+
const pre = prerelease(m[4]);
|
|
70
|
+
if (pre === null || (pre.length && p[2] === null))
|
|
71
|
+
return null;
|
|
72
|
+
return { p, pre };
|
|
73
|
+
}
|
|
74
|
+
/** the least version a partial one covers: a version written whole is itself; one with a wild part
|
|
75
|
+
* (`22`, `22.18.x`) starts at its first prerelease (`22.0.0-0`), as npm's includePrerelease has it */
|
|
76
|
+
const lower = (x) => ({ v: [x.p[0] ?? 0, x.p[1] ?? 0, x.p[2] ?? 0], pre: x.pre.length ? x.pre : x.p[2] === null ? [0] : [] });
|
|
77
|
+
/** the first version past what a partial version covers (`1.2` covers 1.2.x: up to 1.3.0's first
|
|
78
|
+
* prerelease, 1.3.0-0); null for `*` */
|
|
79
|
+
function past(x) {
|
|
80
|
+
const [a, b, c] = x.p;
|
|
81
|
+
if (a === null)
|
|
82
|
+
return null;
|
|
83
|
+
if (b === null)
|
|
84
|
+
return { v: [a + 1, 0, 0], pre: [0] };
|
|
85
|
+
if (c === null)
|
|
86
|
+
return { v: [a, b + 1, 0], pre: [0] };
|
|
87
|
+
return { v: [a, b, c + 1], pre: [0] };
|
|
88
|
+
}
|
|
89
|
+
/** One comparator as npm reads it (node-semver's desugaring), as bounds; null where it is no comparator. */
|
|
90
|
+
function bounds(op, text) {
|
|
91
|
+
const x = partial(text);
|
|
92
|
+
if (!x)
|
|
93
|
+
return null;
|
|
94
|
+
const lo = lower(x);
|
|
95
|
+
const hi = past(x);
|
|
96
|
+
const wild = x.p[2] === null;
|
|
97
|
+
switch (op) {
|
|
98
|
+
case "":
|
|
99
|
+
case "=":
|
|
100
|
+
// one version, or an x-range
|
|
101
|
+
return !wild ? [["=", lo]] : hi ? [[">=", lo], ["<", hi]] : [];
|
|
102
|
+
case ">=":
|
|
103
|
+
return [[">=", lo]];
|
|
104
|
+
case ">":
|
|
105
|
+
// past what it covers: `>1.2` is `>=1.3.0`; `>*`, nothing
|
|
106
|
+
return !wild ? [[">", lo]] : hi ? [[">=", hi]] : [["<", { v: [0, 0, 0], pre: [0] }]];
|
|
107
|
+
case "<":
|
|
108
|
+
return [["<", lo]];
|
|
109
|
+
case "<=":
|
|
110
|
+
return !wild ? [["<=", lo]] : hi ? [["<", hi]] : [];
|
|
111
|
+
case "~":
|
|
112
|
+
case "~>": {
|
|
113
|
+
// patches of the minor given (of the major, where only it is)
|
|
114
|
+
const [a, b] = x.p;
|
|
115
|
+
if (a === null)
|
|
116
|
+
return [];
|
|
117
|
+
return [[">=", lo], ["<", { v: b === null ? [a + 1, 0, 0] : [a, b + 1, 0], pre: [0] }]];
|
|
118
|
+
}
|
|
119
|
+
case "^": {
|
|
120
|
+
// the same leftmost part that is not zero
|
|
121
|
+
const [a, b, c] = x.p;
|
|
122
|
+
if (a === null)
|
|
123
|
+
return [];
|
|
124
|
+
const top = a > 0 || b === null ? [a + 1, 0, 0] : b > 0 || c === null ? [0, b + 1, 0] : [0, 0, c + 1];
|
|
125
|
+
return [[">=", lo], ["<", { v: top, pre: [0] }]];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/** an operator, spaces, a version: `>= 22.0.0` as `>=22.0.0`; never `> = 22` */
|
|
131
|
+
const COMPARATOR = /^(<=|>=|<|>|=|\^|~>|~)?\s*([^\s<>=^~]+)\s*/;
|
|
132
|
+
/** One alternative of a range (no `||`): a hyphen range, or comparators joined by spaces; null where
|
|
133
|
+
* npm could not read it. */
|
|
134
|
+
function alternative(text) {
|
|
135
|
+
const t = text.trim();
|
|
136
|
+
if (!t)
|
|
137
|
+
return [];
|
|
138
|
+
const hyphen = /^(\S+)\s+-\s+(\S+)$/.exec(t);
|
|
139
|
+
if (hyphen) {
|
|
140
|
+
const a = partial(hyphen[1]);
|
|
141
|
+
const b = partial(hyphen[2]);
|
|
142
|
+
if (!a || !b)
|
|
143
|
+
return null;
|
|
144
|
+
const top = b.p[2] === null ? past(b) : null;
|
|
145
|
+
return [[">=", lower(a)], ...(b.p[0] === null ? [] : top ? [["<", top]] : [["<=", lower(b)]])];
|
|
146
|
+
}
|
|
147
|
+
const out = [];
|
|
148
|
+
for (let rest = t; rest;) {
|
|
149
|
+
const m = COMPARATOR.exec(rest);
|
|
150
|
+
if (!m)
|
|
151
|
+
return null;
|
|
152
|
+
const b = bounds(m[1] ?? "", m[2]);
|
|
153
|
+
if (!b)
|
|
154
|
+
return null;
|
|
155
|
+
out.push(...b);
|
|
156
|
+
rest = rest.slice(m[0].length);
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
/** Whether `version` meets a range as package.json's `engines` writes one, as npm checks a Node
|
|
161
|
+
* against engines (npm-install-checks: semver with includePrerelease): alternatives joined by
|
|
162
|
+
* `||`, each a hyphen range (`22.18.0 - 24`) or comparators joined by spaces — `>=`, `>`, `<=`,
|
|
163
|
+
* `<`, `=`, `^`, `~`, a version, an x-range (`22`, `22.x`, `22.18.*`, `*`) — an operator apart from
|
|
164
|
+
* its version or not. An empty range, or one of spaces alone, is any version; one npm could not
|
|
165
|
+
* read, in any of its alternatives, none. Prereleases in npm's order — 22.18.0 is past
|
|
166
|
+
* 22.18.0-rc.1, so `<=22.18.0-rc.1` excludes it — and a prerelease Node is taken as any version
|
|
167
|
+
* (`24.0.0-rc.1` meets `>=22`; `22.0.0-rc.1` meets `22`, which starts at `22.0.0-0`). */
|
|
168
|
+
export function satisfies(version, range) {
|
|
169
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+.*)?$/.exec(version.trim());
|
|
170
|
+
const pre = m ? prerelease(m[4]) : null;
|
|
171
|
+
if (!m || pre === null)
|
|
172
|
+
return false;
|
|
173
|
+
const v = { v: [Number(m[1]), Number(m[2]), Number(m[3])], pre };
|
|
174
|
+
if (range === undefined || range === null || !range.trim())
|
|
175
|
+
return true;
|
|
176
|
+
const alts = range.split("||").map(alternative);
|
|
177
|
+
if (alts.some((a) => a === null))
|
|
178
|
+
return false;
|
|
179
|
+
return alts.some((bs) => bs.every(([op, w]) => {
|
|
180
|
+
const c = order(v, w);
|
|
181
|
+
return op === ">=" ? c >= 0 : op === ">" ? c > 0 : op === "<=" ? c <= 0 : op === "<" ? c < 0 : c === 0;
|
|
182
|
+
}));
|
|
183
|
+
}
|
package/dist/shared/timeline.js
CHANGED
|
@@ -15,6 +15,11 @@ import { SITE_BODY, SITE_STMT } from "./graph.js";
|
|
|
15
15
|
import { byName } from "./review.js";
|
|
16
16
|
/** at most this many names are listed in one event; the counts stay exact */
|
|
17
17
|
export const NAMES_LISTED = 100;
|
|
18
|
+
/** An event as a read-only reader and an agent are told it (UpdateSpec decision 11): a version set
|
|
19
|
+
* aside without why (what this machine and its servers said of it). */
|
|
20
|
+
export function withoutReasons(e) {
|
|
21
|
+
return e.kind === "tool" && e.setAside ? { ...e, setAside: { version: e.setAside.version } } : e;
|
|
22
|
+
}
|
|
18
23
|
/** The graph as the TCB needs it: names, content hashes, statement and body edges. */
|
|
19
24
|
export class TcbIndex {
|
|
20
25
|
names;
|