@telorun/cli 0.76.0 → 0.78.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Verification of a module's declared `requires.telo` range — by RUNNING the CLI
3
+ * at each edge of it.
4
+ *
5
+ * This is the half of declared runtime requirements that cannot live in the
6
+ * analyzer: it spawns a process and reaches the network, while the analyzer is
7
+ * browser-safe. It is also a *publishing* concern — an author editing a manifest
8
+ * needs to know whether their runtime can read a module, not whether someone
9
+ * else's declaration is honest.
10
+ *
11
+ * **Why execution rather than a `since:` table.** The obvious design annotates
12
+ * every vocabulary entry with the version that introduced it and takes the
13
+ * maximum over what a manifest uses. That re-creates the discipline problem one
14
+ * level down — every future additive change must remember its `since`, and
15
+ * forgetting is silent, which is the failure mode this whole mechanism exists to
16
+ * remove — and it cannot see a *shape* change (an object where a zone annotation
17
+ * used to take a pointer, a new key on a closed kernel-owned schema) without a
18
+ * second annotation mechanism. Running the old CLI is not a prediction of the
19
+ * property; it IS the property, executed.
20
+ *
21
+ * **Two edges bound the whole range**, rather than sampling it, because syntax
22
+ * support is monotonic: a construct added in 0.43 works in 0.44 and later, one
23
+ * removed in 0.60 works in 0.59 and earlier. Nothing in the middle can fail
24
+ * while both edges pass. For a range open above the high edge is HEAD, which
25
+ * normal CI already checks, so an open declaration costs one run.
26
+ *
27
+ * **Infrastructure failure warns; evidence of breakage fails.** A CLI that
28
+ * cannot be installed (offline, a registry outage) leaves the claim unverified,
29
+ * and blocking a publish on network reachability trades one failure for a worse
30
+ * one. A CLI that runs and rejects the manifest is evidence, and evidence is
31
+ * what this gate is for.
32
+ */
33
+ import { readRequires, lowerBound, upperBound } from "@telorun/analyzer";
34
+ import { execFile } from "node:child_process";
35
+ import { promisify } from "node:util";
36
+ const run = promisify(execFile);
37
+ /** How long a single edge check may take. An old CLI has to be fetched on first
38
+ * use, so this is generous; it exists to bound a hang, not to pace the work. */
39
+ const EDGE_TIMEOUT_MS = 300_000;
40
+ /**
41
+ * Verify a module manifest against the edges of its own declared range.
42
+ *
43
+ * `manifestPath` is checked, not the module directory, so the caller controls
44
+ * which document is the subject. A module declaring nothing returns immediately:
45
+ * absent means no requirement, permanently, for everything published before this
46
+ * mechanism existed.
47
+ */
48
+ export async function verifyRequires(manifestPath, moduleDoc, options) {
49
+ const { block } = readRequires(moduleDoc);
50
+ const declared = block.telo;
51
+ if (!declared)
52
+ return { outcomes: [], refuted: false };
53
+ const edges = [];
54
+ const low = lowerBound(declared);
55
+ if (low)
56
+ edges.push(low.raw);
57
+ const high = upperBound(declared);
58
+ // The high edge of an open range is HEAD, which the ordinary check already
59
+ // covers; only a closed bound names a version worth installing. A bound equal
60
+ // to the low edge is one edge, not two.
61
+ if (high && high.raw !== low?.raw)
62
+ edges.push(high.raw);
63
+ const outcomes = [];
64
+ for (const edge of edges) {
65
+ // The running CLI is the edge, and the caller has ALREADY checked this
66
+ // manifest with it — `publishOne` runs static analysis before reaching here
67
+ // and returns on any error. Spawning `telo check` would re-run that same
68
+ // analysis to learn what we know; worse, `telo` is not on PATH in a
69
+ // development checkout (it is `pnpm run telo`), so the spawn would ENOENT and
70
+ // report a spurious "could not run" against every module.
71
+ if (edge === options.currentVersion) {
72
+ outcomes.push({ edge, status: "passed" });
73
+ continue;
74
+ }
75
+ outcomes.push(await runEdge(manifestPath, edge));
76
+ }
77
+ return { declared, outcomes, refuted: outcomes.some((o) => o.status === "failed") };
78
+ }
79
+ async function runEdge(manifestPath, edge) {
80
+ try {
81
+ await run("npx", ["-y", `@telorun/cli@${edge}`, "check", manifestPath], {
82
+ timeout: EDGE_TIMEOUT_MS,
83
+ maxBuffer: 8 * 1024 * 1024,
84
+ });
85
+ return { edge, status: "passed" };
86
+ }
87
+ catch (err) {
88
+ const e = err;
89
+ if (typeof e.code !== "number") {
90
+ // Never ran: the binary is missing, a fetch failed, the timeout fired. The
91
+ // claim is unverified, not disproven, and the two must not be conflated.
92
+ return { edge, status: "unavailable", reason: e.message ?? String(err) };
93
+ }
94
+ const output = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim();
95
+ if (!mentionsManifest(output, manifestPath)) {
96
+ // It ran and exited non-zero WITHOUT saying anything about the manifest
97
+ // under test. `telo check` also exits non-zero for an argument shape an
98
+ // older parser does not accept, an unreachable remote import, or a bad
99
+ // registry URL — and reporting any of those as "the declared range is
100
+ // false" is precisely the misattribution this mechanism exists to remove.
101
+ // Refuting a range needs evidence about THIS manifest.
102
+ return {
103
+ edge,
104
+ status: "unavailable",
105
+ reason: `telo ${edge} exited non-zero without reporting on ${manifestPath} — ` +
106
+ `treating the range as unverified rather than refuted. Output: ` +
107
+ `${output.slice(0, 400) || "(none)"}`,
108
+ };
109
+ }
110
+ return { edge, status: "failed", output: output || (e.message ?? "check failed") };
111
+ }
112
+ }
113
+ /** Whether the edge CLI's output actually concerns the manifest under test.
114
+ * Compared on the basename as well as the full path, since a CLI renders a
115
+ * path relative to its own cwd. */
116
+ function mentionsManifest(output, manifestPath) {
117
+ if (!output)
118
+ return false;
119
+ const base = manifestPath.split(/[\\/]/).filter(Boolean).slice(-2).join("/");
120
+ return output.includes(manifestPath) || (base.length > 0 && output.includes(base));
121
+ }
122
+ /**
123
+ * The published `@telorun/cli` versions, for the "an upper bound must already
124
+ * exist" check. `null` when the registry could not be reached — the caller warns
125
+ * rather than blocking, since the rule gates a bound absent from almost every
126
+ * module and an unreachable npm should not stop a publish.
127
+ *
128
+ * **Memoized for the process**, because `telo publish` runs per module and
129
+ * `scripts/publish-modules.mjs` publishes the whole standard library in one
130
+ * pass: without this, one release is ~60 `npm view` round trips (each with a
131
+ * 60-second timeout) for a single answer that cannot change mid-run. The failed
132
+ * lookup is cached too — a registry unreachable for the first module is
133
+ * unreachable for the rest, and retrying it sixty times turns a warning into a
134
+ * minutes-long stall.
135
+ *
136
+ * `@telorun/cli` is deliberately the package queried: it is the one the
137
+ * verification path installs (`npx @telorun/cli@<edge>`) and the one
138
+ * `TELO_SURFACE_VERSION` is generated from, so the constant, the existence check
139
+ * and the thing that actually runs all name one package.
140
+ */
141
+ let publishedVersionsCache;
142
+ export function resetPublishedTeloVersionsCache() {
143
+ publishedVersionsCache = undefined;
144
+ }
145
+ export function publishedTeloVersions() {
146
+ publishedVersionsCache ??= fetchPublishedTeloVersions();
147
+ return publishedVersionsCache;
148
+ }
149
+ async function fetchPublishedTeloVersions() {
150
+ try {
151
+ const { stdout } = await run("npm", ["view", "@telorun/cli", "versions", "--json"], {
152
+ timeout: 60_000,
153
+ maxBuffer: 8 * 1024 * 1024,
154
+ });
155
+ const parsed = JSON.parse(stdout);
156
+ if (Array.isArray(parsed))
157
+ return parsed.filter((v) => typeof v === "string");
158
+ return typeof parsed === "string" ? [parsed] : null;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ /** The declared upper bound when it names a version the registry does not have —
165
+ * an unverifiable bound, which the grammar exists to forbid. `undefined` when
166
+ * the range is open above, the bound exists, or the registry was unreachable. */
167
+ export function unpublishedUpperBound(declared, published) {
168
+ if (!declared || published === null)
169
+ return undefined;
170
+ const high = upperBound(declared);
171
+ if (!high)
172
+ return undefined;
173
+ const normalized = new Set(published.map((v) => (v.startsWith("v") ? v.slice(1) : v)));
174
+ return normalized.has(high.raw) ? undefined : high.raw;
175
+ }
176
+ //# sourceMappingURL=verify-requires.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-requires.js","sourceRoot":"","sources":["../../src/release/verify-requires.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/cli",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "description": "Telo CLI - Command-line interface for the Telo runtime.",
5
5
  "keywords": [
6
6
  "telo",
@@ -34,11 +34,11 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@telorun/analyzer": "0.61.0",
37
+ "@telorun/analyzer": "0.62.1",
38
38
  "@telorun/glob": "0.2.0",
39
- "@telorun/ide-support": "0.13.3",
40
- "@telorun/kernel": "0.76.0",
41
- "@telorun/sdk": "0.75.0",
39
+ "@telorun/ide-support": "0.14.1",
40
+ "@telorun/kernel": "0.78.0",
41
+ "@telorun/sdk": "0.77.0",
42
42
  "@telorun/templating": "0.16.0",
43
43
  "dotenv": "^17.4.0",
44
44
  "packageurl-js": "^2.0.1",