austere-axi 0.6.0 → 0.7.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 +6 -4
- package/dist/austere.js +2 -2
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.js +5 -5
- package/dist/cli.mjs +552 -238
- package/dist/packs/mermaid/catalog.json +2 -2
- package/dist/packs/mermaid/pack.js +1 -1
- package/dist/packs/shadcn/catalog.json +2 -2
- package/dist/packs/shadcn/pack.js +1 -1
- package/dist/prompt.md +1 -1
- package/package.json +1 -1
- package/skills/austere/SKILL.md +2 -0
package/dist/cli.mjs
CHANGED
|
@@ -618,7 +618,7 @@ var init_paths = __esm({
|
|
|
618
618
|
HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
619
619
|
BUNDLED = true;
|
|
620
620
|
ASSET_DIR = BUNDLED ? HERE : path.resolve(HERE, "../../dist");
|
|
621
|
-
VERSION = true ? "0.
|
|
621
|
+
VERSION = true ? "0.7.0" : packageVersion();
|
|
622
622
|
CLI_ENTRY = BUNDLED ? fileURLToPath(import.meta.url) : path.resolve(HERE, "main.ts");
|
|
623
623
|
securedDir = null;
|
|
624
624
|
}
|
|
@@ -1953,8 +1953,8 @@ __export(install_exports, {
|
|
|
1953
1953
|
removePack: () => removePack,
|
|
1954
1954
|
untar: () => untar
|
|
1955
1955
|
});
|
|
1956
|
-
import
|
|
1957
|
-
import
|
|
1956
|
+
import fs11 from "node:fs";
|
|
1957
|
+
import path10 from "node:path";
|
|
1958
1958
|
import zlib from "node:zlib";
|
|
1959
1959
|
import crypto4 from "node:crypto";
|
|
1960
1960
|
function registryUrl() {
|
|
@@ -2091,15 +2091,15 @@ function readTarball(tgz, label) {
|
|
|
2091
2091
|
return { packJs, catalog, packageJson: files.get("package.json") || null };
|
|
2092
2092
|
}
|
|
2093
2093
|
function fromTarball(file3) {
|
|
2094
|
-
const abs =
|
|
2095
|
-
if (!
|
|
2096
|
-
return { ...readTarball(
|
|
2094
|
+
const abs = path10.resolve(file3.replace(/^~(?=[\\/])/, process.env.HOME || ""));
|
|
2095
|
+
if (!fs11.existsSync(abs)) throw new Error(`no such file: ${abs}`);
|
|
2096
|
+
return { ...readTarball(fs11.readFileSync(abs), path10.basename(abs)), source: "tarball", resolved: abs };
|
|
2097
2097
|
}
|
|
2098
2098
|
function fromFolder(dir) {
|
|
2099
|
-
const abs =
|
|
2100
|
-
if (!
|
|
2101
|
-
if (
|
|
2102
|
-
const read = (rel) =>
|
|
2099
|
+
const abs = path10.resolve(dir.replace(/^~(?=[\\/])/, process.env.HOME || ""));
|
|
2100
|
+
if (!fs11.existsSync(abs)) throw new Error(`no such folder: ${abs}`);
|
|
2101
|
+
if (fs11.statSync(abs).isFile()) return fromTarball(abs);
|
|
2102
|
+
const read = (rel) => fs11.existsSync(path10.join(abs, rel)) ? fs11.readFileSync(path10.join(abs, rel)) : null;
|
|
2103
2103
|
const packJs = read("dist/pack.js");
|
|
2104
2104
|
const catalog = read("dist/catalog.json");
|
|
2105
2105
|
if (!packJs || !catalog) throw new Error(`${abs} has no dist/pack.js and dist/catalog.json: run \`austere-axi components build ${dir}\` first`);
|
|
@@ -2139,10 +2139,10 @@ async function addPack(spec, { trust = false } = {}) {
|
|
|
2139
2139
|
}
|
|
2140
2140
|
const got = local ? fromFolder(spec) : await fromRegistry(spec);
|
|
2141
2141
|
if (got.source === "tarball" && !trust) {
|
|
2142
|
-
const peek = parseCatalog(got.catalog,
|
|
2142
|
+
const peek = parseCatalog(got.catalog, path10.basename(got.resolved));
|
|
2143
2143
|
throw Object.assign(
|
|
2144
2144
|
new Error(
|
|
2145
|
-
`${
|
|
2145
|
+
`${path10.basename(got.resolved)} (${peek.pack}) comes from a file, not the registry: its code would run inside your review pages. Rerun with --trust once you have looked at it.`
|
|
2146
2146
|
),
|
|
2147
2147
|
{ code: "untrusted" }
|
|
2148
2148
|
);
|
|
@@ -2167,14 +2167,14 @@ async function addPack(spec, { trust = false } = {}) {
|
|
|
2167
2167
|
}
|
|
2168
2168
|
const lock = readLock();
|
|
2169
2169
|
const previous = lock.packs[cat.pack];
|
|
2170
|
-
const dir =
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
if (got.packageJson)
|
|
2170
|
+
const dir = path10.join(packsDir(), packDirName(cat.pack, cat.version));
|
|
2171
|
+
fs11.rmSync(dir, { recursive: true, force: true });
|
|
2172
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
2173
|
+
fs11.writeFileSync(path10.join(dir, "pack.js"), got.packJs);
|
|
2174
|
+
fs11.writeFileSync(path10.join(dir, "catalog.json"), got.catalog);
|
|
2175
|
+
if (got.packageJson) fs11.writeFileSync(path10.join(dir, "package.json"), got.packageJson);
|
|
2176
2176
|
if (previous && previous.version !== cat.version) {
|
|
2177
|
-
|
|
2177
|
+
fs11.rmSync(path10.join(packsDir(), packDirName(cat.pack, previous.version)), { recursive: true, force: true });
|
|
2178
2178
|
}
|
|
2179
2179
|
lock.packs[cat.pack] = {
|
|
2180
2180
|
version: cat.version,
|
|
@@ -2194,7 +2194,7 @@ function removePack(name) {
|
|
|
2194
2194
|
if (!entry) return null;
|
|
2195
2195
|
delete lock.packs[name];
|
|
2196
2196
|
writeLock(lock);
|
|
2197
|
-
|
|
2197
|
+
fs11.rmSync(path10.join(packsDir(), packDirName(name, entry.version)), { recursive: true, force: true });
|
|
2198
2198
|
return { name, version: entry.version };
|
|
2199
2199
|
}
|
|
2200
2200
|
var MAX_TARBALL, MAX_PACK_JS;
|
|
@@ -2215,8 +2215,8 @@ __export(pack_dev_exports, {
|
|
|
2215
2215
|
buildPackDir: () => buildPackDir,
|
|
2216
2216
|
newPack: () => newPack
|
|
2217
2217
|
});
|
|
2218
|
-
import
|
|
2219
|
-
import
|
|
2218
|
+
import fs12 from "node:fs";
|
|
2219
|
+
import path11 from "node:path";
|
|
2220
2220
|
import { createRequire } from "node:module";
|
|
2221
2221
|
import { pathToFileURL } from "node:url";
|
|
2222
2222
|
function packageJson(name) {
|
|
@@ -2253,9 +2253,9 @@ Publish with \`npm publish\` after \`npm run build\`. Others install it with
|
|
|
2253
2253
|
}
|
|
2254
2254
|
function newPack(dir, { name } = {}) {
|
|
2255
2255
|
if (!dir) throw new Error("new needs a folder: austere-axi components new austere-pack-milestones");
|
|
2256
|
-
const abs =
|
|
2257
|
-
if (
|
|
2258
|
-
const pkgName = name ||
|
|
2256
|
+
const abs = path11.resolve(dir);
|
|
2257
|
+
if (fs12.existsSync(abs) && fs12.readdirSync(abs).length) throw new Error(`${abs} is not empty`);
|
|
2258
|
+
const pkgName = name || path11.basename(abs).toLowerCase().replace(/[^a-z0-9._-]+/g, "-");
|
|
2259
2259
|
if (!isValidPackName(pkgName)) throw new Error(`${JSON.stringify(pkgName)} is not an npm package name; pass --name`);
|
|
2260
2260
|
const files = {
|
|
2261
2261
|
"package.json": JSON.stringify(packageJson(pkgName), null, 2) + "\n",
|
|
@@ -2264,13 +2264,13 @@ function newPack(dir, { name } = {}) {
|
|
|
2264
2264
|
...TEMPLATE
|
|
2265
2265
|
};
|
|
2266
2266
|
for (const [rel, text] of Object.entries(files)) {
|
|
2267
|
-
|
|
2268
|
-
|
|
2267
|
+
fs12.mkdirSync(path11.dirname(path11.join(abs, rel)), { recursive: true });
|
|
2268
|
+
fs12.writeFileSync(path11.join(abs, rel), text);
|
|
2269
2269
|
}
|
|
2270
2270
|
return { dir: abs, name: pkgName, files: Object.keys(files) };
|
|
2271
2271
|
}
|
|
2272
2272
|
async function findEsbuild(dir) {
|
|
2273
|
-
for (const base of [
|
|
2273
|
+
for (const base of [path11.join(dir, "package.json"), path11.join(process.cwd(), "package.json"), import.meta.url]) {
|
|
2274
2274
|
try {
|
|
2275
2275
|
const resolved = createRequire(base).resolve("esbuild");
|
|
2276
2276
|
const esbuild = await import(pathToFileURL(resolved).href);
|
|
@@ -2281,8 +2281,8 @@ async function findEsbuild(dir) {
|
|
|
2281
2281
|
return null;
|
|
2282
2282
|
}
|
|
2283
2283
|
async function buildPackDir(dir = ".") {
|
|
2284
|
-
const abs =
|
|
2285
|
-
if (!
|
|
2284
|
+
const abs = path11.resolve(dir);
|
|
2285
|
+
if (!fs12.existsSync(path11.join(abs, "package.json"))) throw new Error(`${abs} has no package.json (\`austere-axi components new <dir>\` makes a pack)`);
|
|
2286
2286
|
const esbuild = await findEsbuild(abs);
|
|
2287
2287
|
if (!esbuild) throw new Error(`components build needs esbuild: run \`npm install -D esbuild\` in ${abs}`);
|
|
2288
2288
|
process.env.OPENUI_TELEMETRY_DISABLED ||= "1";
|
|
@@ -2406,7 +2406,7 @@ export default austereComponent({
|
|
|
2406
2406
|
|
|
2407
2407
|
// src/cli/net.ts
|
|
2408
2408
|
import { execFile } from "node:child_process";
|
|
2409
|
-
import
|
|
2409
|
+
import fs13 from "node:fs";
|
|
2410
2410
|
function run(bin, args) {
|
|
2411
2411
|
return new Promise((resolve) => {
|
|
2412
2412
|
execFile(bin, args, { timeout: 2500, windowsHide: true }, (err, stdout) => resolve(err ? null : stdout));
|
|
@@ -2417,7 +2417,7 @@ async function detectTailscale({ fresh = false } = {}) {
|
|
|
2417
2417
|
if (!fresh && Date.now() - cache.at < 3e4) return cache.value;
|
|
2418
2418
|
let value = null;
|
|
2419
2419
|
for (const bin of CANDIDATES) {
|
|
2420
|
-
if (bin.startsWith("/") && !
|
|
2420
|
+
if (bin.startsWith("/") && !fs13.existsSync(bin)) continue;
|
|
2421
2421
|
const out = await run(bin, ["status", "--json"]);
|
|
2422
2422
|
if (!out) continue;
|
|
2423
2423
|
try {
|
|
@@ -2514,8 +2514,8 @@ var init_items = __esm({
|
|
|
2514
2514
|
});
|
|
2515
2515
|
|
|
2516
2516
|
// src/cli/hub-page.ts
|
|
2517
|
-
import
|
|
2518
|
-
import
|
|
2517
|
+
import os3 from "node:os";
|
|
2518
|
+
import path12 from "node:path";
|
|
2519
2519
|
function day(ms) {
|
|
2520
2520
|
const d = new Date(ms);
|
|
2521
2521
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -2529,8 +2529,8 @@ function time3(ms) {
|
|
|
2529
2529
|
return new Date(ms).toLocaleTimeString("en", { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2530
2530
|
}
|
|
2531
2531
|
function tilde(p) {
|
|
2532
|
-
const home =
|
|
2533
|
-
return p === home || p.startsWith(home +
|
|
2532
|
+
const home = os3.homedir();
|
|
2533
|
+
return p === home || p.startsWith(home + path12.sep) ? "~" + p.slice(home.length) : p;
|
|
2534
2534
|
}
|
|
2535
2535
|
function counts(a) {
|
|
2536
2536
|
const waiting = a.open + a.received;
|
|
@@ -2566,13 +2566,13 @@ function hubPage(rows, { version: version2, q: q2, project, stats }) {
|
|
|
2566
2566
|
const body = [...days.entries()].map(
|
|
2567
2567
|
([d, items]) => `<h3>${escapeHtml(d)}</h3><ul>${items.map(
|
|
2568
2568
|
(a) => `<li><a href="/hub/open/${a.id}" title="${escapeHtml(a.file)}">
|
|
2569
|
-
<strong>${escapeHtml(a.title ||
|
|
2570
|
-
<span class="meta">${escapeHtml(
|
|
2569
|
+
<strong>${escapeHtml(a.title || path12.basename(a.file))}</strong>
|
|
2570
|
+
<span class="meta">${escapeHtml(path12.relative(proj, a.file) || path12.basename(a.file))} \xB7 ${time3(a.updated_at)} \xB7 ${state(a)}${counts(a) ? ` \xB7 ${counts(a)}` : ""}</span>
|
|
2571
2571
|
</a></li>`
|
|
2572
2572
|
).join("")}</ul>`
|
|
2573
2573
|
).join("");
|
|
2574
2574
|
const unanswered = list.reduce((n, a) => n + a.open + a.received, 0);
|
|
2575
|
-
return `<section><h2><a href="/?project=${encodeURIComponent(proj)}">${escapeHtml(
|
|
2575
|
+
return `<section><h2><a href="/?project=${encodeURIComponent(proj)}">${escapeHtml(path12.basename(proj) || proj)}</a> <small title="${escapeHtml(proj)}">${escapeHtml(
|
|
2576
2576
|
shortPath(proj)
|
|
2577
2577
|
)}</small>${unanswered ? ` <b class="open">${unanswered} unanswered</b>` : ""}</h2>${body}</section>`;
|
|
2578
2578
|
}).join("");
|
|
@@ -2595,7 +2595,7 @@ function notesPage(rows, { version: version2, q: q2, project, status, author = "
|
|
|
2595
2595
|
const when = `${day(n.at)} ${time3(n.at)}`;
|
|
2596
2596
|
return `<li class="n"><a href="/hub/open/${escapeHtml(n.artifact_id)}" title="${escapeHtml(n.file)}">
|
|
2597
2597
|
<p class="note">${escapeHtml(n.text || "(no text)")}</p>
|
|
2598
|
-
<span class="note-t">${n.author ? `<b class="by">${escapeHtml(n.author)}</b> \xB7 ` : ""}${n.target ? `on ${escapeHtml(n.target)} \xB7 ` : ""}<strong>${escapeHtml(n.title ||
|
|
2598
|
+
<span class="note-t">${n.author ? `<b class="by">${escapeHtml(n.author)}</b> \xB7 ` : ""}${n.target ? `on ${escapeHtml(n.target)} \xB7 ` : ""}<strong>${escapeHtml(n.title || path12.basename(n.file))}</strong> \xB7 ${escapeHtml(path12.basename(n.project) || n.project)} \xB7 ${escapeHtml(when)} \xB7 <span class="st ${waiting ? "waiting" : "done"}">${STATUS_LABEL[n.status] || escapeHtml(n.status)}</span></span>
|
|
2599
2599
|
</a></li>`;
|
|
2600
2600
|
}).join("");
|
|
2601
2601
|
const opt = (v, label) => `<option value="${v}"${status === v ? " selected" : ""}>${label}</option>`;
|
|
@@ -2650,9 +2650,9 @@ __export(server_exports, {
|
|
|
2650
2650
|
persistEnv: () => persistEnv,
|
|
2651
2651
|
runServer: () => runServer
|
|
2652
2652
|
});
|
|
2653
|
-
import
|
|
2654
|
-
import
|
|
2655
|
-
import
|
|
2653
|
+
import fs14 from "node:fs";
|
|
2654
|
+
import http3 from "node:http";
|
|
2655
|
+
import path13 from "node:path";
|
|
2656
2656
|
import crypto5 from "node:crypto";
|
|
2657
2657
|
function persistEnv() {
|
|
2658
2658
|
return /^(1|true|yes)$/i.test(process.env.AUSTERE_AXI_PERSIST || "");
|
|
@@ -2667,7 +2667,7 @@ async function runServer({
|
|
|
2667
2667
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${a.join(" ")}`;
|
|
2668
2668
|
if (verbose) process.stderr.write(line + "\n");
|
|
2669
2669
|
try {
|
|
2670
|
-
|
|
2670
|
+
fs14.appendFileSync(path13.join(stateDir(), "server.log"), line + "\n", { mode: 384 });
|
|
2671
2671
|
} catch {
|
|
2672
2672
|
}
|
|
2673
2673
|
};
|
|
@@ -2692,6 +2692,7 @@ async function runServer({
|
|
|
2692
2692
|
const graceTimers = /* @__PURE__ */ new Map();
|
|
2693
2693
|
const byeAt = /* @__PURE__ */ new Map();
|
|
2694
2694
|
const browserSeen = /* @__PURE__ */ new Set();
|
|
2695
|
+
const renderReported = /* @__PURE__ */ new Map();
|
|
2695
2696
|
let tailnet = null;
|
|
2696
2697
|
const linkBase = linkUrl();
|
|
2697
2698
|
let lastActivity = Date.now();
|
|
@@ -2848,7 +2849,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
2848
2849
|
}
|
|
2849
2850
|
function readFileSafe(file3) {
|
|
2850
2851
|
try {
|
|
2851
|
-
return
|
|
2852
|
+
return fs14.readFileSync(file3, "utf8");
|
|
2852
2853
|
} catch {
|
|
2853
2854
|
return null;
|
|
2854
2855
|
}
|
|
@@ -2860,14 +2861,14 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
2860
2861
|
const w = { html, program: art?.program ?? null, shell: art?.shell ?? null, timer: void 0 };
|
|
2861
2862
|
watchers.set(s.id, w);
|
|
2862
2863
|
if (art?.title) s.title = art.title;
|
|
2863
|
-
|
|
2864
|
+
fs14.watchFile(s.file, { interval: 300 }, () => {
|
|
2864
2865
|
clearTimeout(w.timer);
|
|
2865
2866
|
w.timer = setTimeout(() => onFileChange(s, w), 120);
|
|
2866
2867
|
});
|
|
2867
2868
|
}
|
|
2868
2869
|
function unwatch(id) {
|
|
2869
2870
|
const s = store.get(id);
|
|
2870
|
-
if (s)
|
|
2871
|
+
if (s) fs14.unwatchFile(s.file);
|
|
2871
2872
|
watchers.delete(id);
|
|
2872
2873
|
}
|
|
2873
2874
|
function onFileChange(s, w) {
|
|
@@ -3044,7 +3045,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3044
3045
|
const file3 = typeof body.file === "string" ? body.file : "";
|
|
3045
3046
|
if (!file3) return send(res, 400, { error: "file is required" });
|
|
3046
3047
|
if (route === "share") {
|
|
3047
|
-
if (!
|
|
3048
|
+
if (!fs14.existsSync(file3)) return send(res, 404, { error: `no such file: ${file3}` });
|
|
3048
3049
|
await ensureTailnetListener();
|
|
3049
3050
|
if (!remoteBase()) {
|
|
3050
3051
|
return send(res, 409, {
|
|
@@ -3064,7 +3065,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3064
3065
|
return send(res, 200, { id: inviteId(inv), name: inv.name, auto: inv.auto, link: inviteLink(inv), file: inv.file });
|
|
3065
3066
|
}
|
|
3066
3067
|
if (route === "open") {
|
|
3067
|
-
if (!
|
|
3068
|
+
if (!fs14.existsSync(file3)) return send(res, 404, { error: `no such file: ${file3}` });
|
|
3068
3069
|
await ensureTailnetListener();
|
|
3069
3070
|
const s2 = store.ensure(file3);
|
|
3070
3071
|
if (s2.closedAt && !body.reopen) {
|
|
@@ -3197,7 +3198,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3197
3198
|
}
|
|
3198
3199
|
const file3 = installedPackFile(spec.name, spec.version);
|
|
3199
3200
|
if (!file3) return send(res, 404, `pack ${spec.name}@${spec.version} is not installed (austere-axi components add ${spec.name}@${spec.version})`);
|
|
3200
|
-
|
|
3201
|
+
fs14.readFile(file3, (err, buf) => {
|
|
3201
3202
|
if (err) return send(res, 404, "not found");
|
|
3202
3203
|
const actual = `sha384-${crypto5.createHash("sha384").update(buf).digest("base64")}`;
|
|
3203
3204
|
const locked = readLock().packs[spec.name]?.integrity;
|
|
@@ -3221,13 +3222,13 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3221
3222
|
}
|
|
3222
3223
|
function serveAsset(res, rel, url2) {
|
|
3223
3224
|
if (rel.startsWith("installed-packs/")) return serveInstalledPack(res, rel, url2);
|
|
3224
|
-
const clean =
|
|
3225
|
-
const file3 =
|
|
3226
|
-
if (!file3.startsWith(ASSET_DIR +
|
|
3227
|
-
|
|
3225
|
+
const clean = path13.normalize(rel).replace(/^(\.\.[/\\])+/, "");
|
|
3226
|
+
const file3 = path13.join(ASSET_DIR, clean);
|
|
3227
|
+
if (!file3.startsWith(ASSET_DIR + path13.sep)) return send(res, 404, "not found");
|
|
3228
|
+
fs14.stat(file3, (err, st2) => {
|
|
3228
3229
|
if (err || !st2.isFile()) return send(res, 404, "not found");
|
|
3229
3230
|
streamFile(res, file3, {
|
|
3230
|
-
"content-type": MIME[
|
|
3231
|
+
"content-type": MIME[path13.extname(file3)] || "application/octet-stream",
|
|
3231
3232
|
"content-length": st2.size,
|
|
3232
3233
|
"cache-control": "no-cache",
|
|
3233
3234
|
"x-content-type-options": "nosniff"
|
|
@@ -3236,7 +3237,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3236
3237
|
}
|
|
3237
3238
|
const ARTIFACT_CSP = "sandbox allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms";
|
|
3238
3239
|
function streamFile(res, file3, headers) {
|
|
3239
|
-
const rs =
|
|
3240
|
+
const rs = fs14.createReadStream(file3);
|
|
3240
3241
|
rs.once("open", () => {
|
|
3241
3242
|
res.writeHead(200, headers);
|
|
3242
3243
|
rs.pipe(res);
|
|
@@ -3249,8 +3250,8 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3249
3250
|
res.once("close", () => rs.destroy());
|
|
3250
3251
|
}
|
|
3251
3252
|
function serveArtifact(req, res, s, rest) {
|
|
3252
|
-
const dir =
|
|
3253
|
-
const base =
|
|
3253
|
+
const dir = path13.dirname(s.file);
|
|
3254
|
+
const base = path13.basename(s.file);
|
|
3254
3255
|
if (rest === base || rest === "") {
|
|
3255
3256
|
const html = readFileSafe(s.file);
|
|
3256
3257
|
if (html == null) return send(res, 404, "artifact file is missing");
|
|
@@ -3264,19 +3265,19 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3264
3265
|
return send(res, 400, "bad path");
|
|
3265
3266
|
}
|
|
3266
3267
|
if (decoded.includes("\0")) return send(res, 404, "not found");
|
|
3267
|
-
const file3 =
|
|
3268
|
-
if (!file3.startsWith(dir +
|
|
3268
|
+
const file3 = path13.resolve(dir, decoded);
|
|
3269
|
+
if (!file3.startsWith(dir + path13.sep) || decoded.split(/[/\\]/).some((p) => p.startsWith("."))) {
|
|
3269
3270
|
return send(res, 404, "not found");
|
|
3270
3271
|
}
|
|
3271
|
-
|
|
3272
|
+
fs14.realpath(dir, (e1, realDir) => {
|
|
3272
3273
|
if (e1) return send(res, 404, "not found");
|
|
3273
|
-
|
|
3274
|
-
if (e2 || !real.startsWith(realDir +
|
|
3275
|
-
if (
|
|
3276
|
-
|
|
3274
|
+
fs14.realpath(file3, (e2, real) => {
|
|
3275
|
+
if (e2 || !real.startsWith(realDir + path13.sep)) return send(res, 404, "not found");
|
|
3276
|
+
if (path13.relative(realDir, real).split(path13.sep).some((p) => p.startsWith("."))) return send(res, 404, "not found");
|
|
3277
|
+
fs14.stat(real, (err, st2) => {
|
|
3277
3278
|
if (err || !st2.isFile() || st2.size > 64 * 1024 * 1024) return send(res, 404, "not found");
|
|
3278
3279
|
streamFile(res, real, {
|
|
3279
|
-
"content-type": MIME[
|
|
3280
|
+
"content-type": MIME[path13.extname(real).toLowerCase()] || "application/octet-stream",
|
|
3280
3281
|
"content-length": st2.size,
|
|
3281
3282
|
"cache-control": "no-cache",
|
|
3282
3283
|
"x-content-type-options": "nosniff",
|
|
@@ -3292,8 +3293,8 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3292
3293
|
title: s.title,
|
|
3293
3294
|
file: viewer.role === "owner" ? s.file : "",
|
|
3294
3295
|
viewer: { role: viewer.role, name: viewer.name, auto: viewer.role === "reviewer" ? viewer.auto : void 0 },
|
|
3295
|
-
name:
|
|
3296
|
-
artifactUrl: `/s/${s.id}/a/${encodeURIComponent(
|
|
3296
|
+
name: path13.basename(s.file),
|
|
3297
|
+
artifactUrl: `/s/${s.id}/a/${encodeURIComponent(path13.basename(s.file))}`,
|
|
3297
3298
|
version: VERSION
|
|
3298
3299
|
};
|
|
3299
3300
|
return `<!doctype html>
|
|
@@ -3327,7 +3328,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3327
3328
|
function hubOpen(res, id) {
|
|
3328
3329
|
const a = /^[a-f0-9]{12}$/.test(id) ? hub.artifact(id) : null;
|
|
3329
3330
|
if (!a) return send(res, 404, "not in the hub");
|
|
3330
|
-
if (!
|
|
3331
|
+
if (!fs14.existsSync(a.file)) return send(res, 410, `${a.file} no longer exists`);
|
|
3331
3332
|
const existed = store.byFile(a.file);
|
|
3332
3333
|
const s = store.ensure(a.file);
|
|
3333
3334
|
if (!existed) store.touch(s);
|
|
@@ -3503,6 +3504,30 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3503
3504
|
broadcastPresence(s);
|
|
3504
3505
|
return send(res, 200, { ok: true, entry });
|
|
3505
3506
|
}
|
|
3507
|
+
if (sub === "/render-errors" && req.method === "POST") {
|
|
3508
|
+
const body = await readBody(req);
|
|
3509
|
+
if (s.closedAt || body.revision !== s.revision) return send(res, 200, { ok: true, count: 0, stale: true });
|
|
3510
|
+
if (thirdPartyPacks(watchers.get(s.id)?.html).length) return send(res, 409, { error: "this page uses third-party packs; queue render errors by hand" });
|
|
3511
|
+
let seen = renderReported.get(s.id);
|
|
3512
|
+
if (!seen || seen.revision !== s.revision) renderReported.set(s.id, seen = { revision: s.revision, keys: /* @__PURE__ */ new Set() });
|
|
3513
|
+
const items = [];
|
|
3514
|
+
for (const e of Array.isArray(body.errors) ? body.errors.slice(0, MAX_RENDER_ERRORS) : []) {
|
|
3515
|
+
if (!e || typeof e !== "object" || typeof e.message !== "string" || !e.message) continue;
|
|
3516
|
+
const statement = typeof e.statement === "string" ? e.statement.slice(0, 200) : "";
|
|
3517
|
+
const message = e.message.slice(0, 1e3);
|
|
3518
|
+
const key3 = `${statement}
|
|
3519
|
+
${message}`;
|
|
3520
|
+
if (seen.keys.has(key3)) continue;
|
|
3521
|
+
seen.keys.add(key3);
|
|
3522
|
+
items.push({ kind: "render-error", statement: statement || void 0, target: statement || void 0, message, note: "Reported when the page rendered." });
|
|
3523
|
+
}
|
|
3524
|
+
if (!items.length) return send(res, 200, { ok: true, count: 0 });
|
|
3525
|
+
const entry = store.addUserBatch(s, items, { author: RENDER_CHECK_AUTHOR });
|
|
3526
|
+
broadcastTranscript(s, [entry]);
|
|
3527
|
+
wake(s);
|
|
3528
|
+
broadcastPresence(s);
|
|
3529
|
+
return send(res, 200, { ok: true, count: items.length });
|
|
3530
|
+
}
|
|
3506
3531
|
if ((sub === "/release" || sub === "/discard") && req.method === "POST") {
|
|
3507
3532
|
const body = await readBody(req);
|
|
3508
3533
|
const ids = Array.isArray(body.ids) ? body.ids.map(String) : sub === "/discard" && body.all ? null : [];
|
|
@@ -3543,7 +3568,7 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3543
3568
|
if (html == null) return send(res, 404, "artifact file is missing");
|
|
3544
3569
|
try {
|
|
3545
3570
|
const out = exportHtml(html);
|
|
3546
|
-
const name =
|
|
3571
|
+
const name = path13.basename(s.file).replace(/\.html?$/i, "") + ".export.html";
|
|
3547
3572
|
return send(res, 200, out.html, {
|
|
3548
3573
|
"content-type": "text/html; charset=utf-8",
|
|
3549
3574
|
"content-disposition": `attachment; filename="${name.replace(/"/g, "")}"`,
|
|
@@ -3558,7 +3583,7 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3558
3583
|
function listen(host) {
|
|
3559
3584
|
return new Promise((resolve) => {
|
|
3560
3585
|
if (listeners.has(host)) return resolve(true);
|
|
3561
|
-
const server =
|
|
3586
|
+
const server = http3.createServer((req, res) => {
|
|
3562
3587
|
handle(req, res).catch((err) => {
|
|
3563
3588
|
log("error", req.method, String(req.url).split("?")[0], String(err?.stack || err));
|
|
3564
3589
|
if (!res.headersSent) send(res, err.status || 500, { error: err.status ? String(err.message) : "internal error" });
|
|
@@ -3635,7 +3660,7 @@ data: ${JSON.stringify({ state: "stopped", reason })}
|
|
|
3635
3660
|
log("started", `v${VERSION}`, `pid=${process.pid}`);
|
|
3636
3661
|
return { port: port2, shutdown };
|
|
3637
3662
|
}
|
|
3638
|
-
var MIME, SHORT_GRACE_MS, LOGIN_TTL_MS, OWNER, REVIEWER_KINDS, PAGE, SSE_MAX_BUFFER, SSE_STALL_MS, SSE_HARD_MAX, SSE_MAX_PER_REVIEWER, MAX_HELD_PER_REVIEWER, SSE_MAX_PER_SESSION;
|
|
3663
|
+
var MIME, SHORT_GRACE_MS, LOGIN_TTL_MS, OWNER, REVIEWER_KINDS, RENDER_CHECK_AUTHOR, MAX_RENDER_ERRORS, PAGE, SSE_MAX_BUFFER, SSE_STALL_MS, SSE_HARD_MAX, SSE_MAX_PER_REVIEWER, MAX_HELD_PER_REVIEWER, SSE_MAX_PER_SESSION;
|
|
3639
3664
|
var init_server = __esm({
|
|
3640
3665
|
"src/cli/server.ts"() {
|
|
3641
3666
|
"use strict";
|
|
@@ -3681,6 +3706,8 @@ var init_server = __esm({
|
|
|
3681
3706
|
LOGIN_TTL_MS = 6e4;
|
|
3682
3707
|
OWNER = Object.freeze({ role: "owner", name: "" });
|
|
3683
3708
|
REVIEWER_KINDS = /* @__PURE__ */ new Set(["annotation", "message"]);
|
|
3709
|
+
RENDER_CHECK_AUTHOR = "Render check";
|
|
3710
|
+
MAX_RENDER_ERRORS = 20;
|
|
3684
3711
|
PAGE = 200;
|
|
3685
3712
|
SSE_MAX_BUFFER = 1 << 20;
|
|
3686
3713
|
SSE_STALL_MS = 5e3;
|
|
@@ -3696,12 +3723,12 @@ var mcp_exports = {};
|
|
|
3696
3723
|
__export(mcp_exports, {
|
|
3697
3724
|
runMcp: () => runMcp
|
|
3698
3725
|
});
|
|
3699
|
-
import { spawn as
|
|
3700
|
-
import
|
|
3726
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3727
|
+
import path14 from "node:path";
|
|
3701
3728
|
import readline from "node:readline";
|
|
3702
3729
|
function runCli({ args, stdin }, signal) {
|
|
3703
3730
|
return new Promise((resolve) => {
|
|
3704
|
-
const child =
|
|
3731
|
+
const child = spawn3(process.execPath, [CLI_ENTRY, ...args], {
|
|
3705
3732
|
env: process.env,
|
|
3706
3733
|
stdio: ["pipe", "pipe", "ignore"],
|
|
3707
3734
|
windowsHide: true
|
|
@@ -3740,7 +3767,7 @@ async function callTool(name, a, signal) {
|
|
|
3740
3767
|
const bad = validate2(tool, a);
|
|
3741
3768
|
if (bad) return { content: [{ type: "text", text: `status: error
|
|
3742
3769
|
error: ${bad}` }], isError: true };
|
|
3743
|
-
if (typeof a.file === "string") a = { ...a, file:
|
|
3770
|
+
if (typeof a.file === "string") a = { ...a, file: path14.resolve(a.file) };
|
|
3744
3771
|
const args = a;
|
|
3745
3772
|
let text = "";
|
|
3746
3773
|
let isError = false;
|
|
@@ -3842,7 +3869,7 @@ var init_mcp = __esm({
|
|
|
3842
3869
|
isRecord2 = (v) => !!v && typeof v === "object";
|
|
3843
3870
|
PROTOCOLS = ["2025-11-25", "2025-06-18", "2024-11-05"];
|
|
3844
3871
|
POLL_TIMEOUT_MS = 5e4;
|
|
3845
|
-
INSTRUCTIONS = `austere-axi turns plans, comparisons, decisions, code reviews and reports into review pages the user annotates in the browser. Call \`spec\` once per session for the workflow, the file skeleton and the component reference. Results are the CLI's output: where a \`next:\` hint names an \`austere-axi <command>\`, call the tool of that name instead. Pass absolute file paths.`;
|
|
3872
|
+
INSTRUCTIONS = `austere-axi turns plans, comparisons, decisions, code reviews and reports into review pages the user annotates in the browser. Call \`spec\` once per session for the workflow, the file skeleton and the component reference. Results are the CLI's output: where a \`next:\` hint names an \`austere-axi <command>\`, call the tool of that name instead. Pass absolute file paths. The reviewer judges how a page looks: do not open, screenshot or inspect it yourself unless the user asks.`;
|
|
3846
3873
|
file2 = { type: "string", description: "Path of the .html page. Use an absolute path; a relative one resolves against the server's working directory." };
|
|
3847
3874
|
TOOLS = {
|
|
3848
3875
|
spec: {
|
|
@@ -3857,9 +3884,13 @@ var init_mcp = __esm({
|
|
|
3857
3884
|
check: {
|
|
3858
3885
|
title: "Validate a page",
|
|
3859
3886
|
description: "Validate the page's OpenUI Lang program without a browser: syntax, component names, required props, unresolved references, unreachable statements.",
|
|
3860
|
-
properties: {
|
|
3887
|
+
properties: {
|
|
3888
|
+
file: file2,
|
|
3889
|
+
fix: { type: "boolean", description: 'Write the <meta name="austere-packs"> tag for the packs the page uses.' },
|
|
3890
|
+
render: { type: "boolean", description: "Also render the page in headless Chrome and report render errors and sideways scrolling as text. Only when the user asks to verify the page." }
|
|
3891
|
+
},
|
|
3861
3892
|
required: ["file"],
|
|
3862
|
-
run: (a) => [{ args: ["check", ...a.fix ? ["--fix"] : [], "--", a.file] }]
|
|
3893
|
+
run: (a) => [{ args: ["check", ...a.fix ? ["--fix"] : [], ...a.render ? ["--render"] : [], "--", a.file] }]
|
|
3863
3894
|
},
|
|
3864
3895
|
open: {
|
|
3865
3896
|
title: "Validate and open the review",
|
|
@@ -3921,8 +3952,8 @@ var init_mcp = __esm({
|
|
|
3921
3952
|
});
|
|
3922
3953
|
|
|
3923
3954
|
// src/cli/main.ts
|
|
3924
|
-
import
|
|
3925
|
-
import
|
|
3955
|
+
import fs15 from "node:fs";
|
|
3956
|
+
import path15 from "node:path";
|
|
3926
3957
|
|
|
3927
3958
|
// node_modules/@openuidev/lang-core/dist/shared-Dzgc28Wo.mjs
|
|
3928
3959
|
var TELEMETRY_REQUEST_TIMEOUT_MS = 2e3;
|
|
@@ -4783,10 +4814,10 @@ function mergeDefs(...defs) {
|
|
|
4783
4814
|
function cloneDef(schema) {
|
|
4784
4815
|
return mergeDefs(schema._zod.def);
|
|
4785
4816
|
}
|
|
4786
|
-
function getElementAtPath(obj,
|
|
4787
|
-
if (!
|
|
4817
|
+
function getElementAtPath(obj, path16) {
|
|
4818
|
+
if (!path16)
|
|
4788
4819
|
return obj;
|
|
4789
|
-
return
|
|
4820
|
+
return path16.reduce((acc, key2) => acc?.[key2], obj);
|
|
4790
4821
|
}
|
|
4791
4822
|
function promiseAllObject(promisesObj) {
|
|
4792
4823
|
const keys = Object.keys(promisesObj);
|
|
@@ -5126,11 +5157,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
5126
5157
|
}
|
|
5127
5158
|
return false;
|
|
5128
5159
|
}
|
|
5129
|
-
function prefixIssues(
|
|
5160
|
+
function prefixIssues(path16, issues) {
|
|
5130
5161
|
return issues.map((iss) => {
|
|
5131
5162
|
var _a3;
|
|
5132
5163
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
5133
|
-
iss.path.unshift(
|
|
5164
|
+
iss.path.unshift(path16);
|
|
5134
5165
|
return iss;
|
|
5135
5166
|
});
|
|
5136
5167
|
}
|
|
@@ -5592,16 +5623,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5592
5623
|
}
|
|
5593
5624
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
5594
5625
|
const fieldErrors = { _errors: [] };
|
|
5595
|
-
const processError = (error63,
|
|
5626
|
+
const processError = (error63, path16 = []) => {
|
|
5596
5627
|
for (const issue2 of error63.issues) {
|
|
5597
5628
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5598
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5629
|
+
issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
|
|
5599
5630
|
} else if (issue2.code === "invalid_key") {
|
|
5600
|
-
processError({ issues: issue2.issues }, [...
|
|
5631
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5601
5632
|
} else if (issue2.code === "invalid_element") {
|
|
5602
|
-
processError({ issues: issue2.issues }, [...
|
|
5633
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5603
5634
|
} else {
|
|
5604
|
-
const fullpath = [...
|
|
5635
|
+
const fullpath = [...path16, ...issue2.path];
|
|
5605
5636
|
if (fullpath.length === 0) {
|
|
5606
5637
|
fieldErrors._errors.push(mapper(issue2));
|
|
5607
5638
|
} else {
|
|
@@ -5640,17 +5671,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5640
5671
|
}
|
|
5641
5672
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
5642
5673
|
const result = { errors: [] };
|
|
5643
|
-
const processError = (error63,
|
|
5674
|
+
const processError = (error63, path16 = []) => {
|
|
5644
5675
|
var _a3;
|
|
5645
5676
|
for (const issue2 of error63.issues) {
|
|
5646
5677
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5647
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5678
|
+
issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
|
|
5648
5679
|
} else if (issue2.code === "invalid_key") {
|
|
5649
|
-
processError({ issues: issue2.issues }, [...
|
|
5680
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5650
5681
|
} else if (issue2.code === "invalid_element") {
|
|
5651
|
-
processError({ issues: issue2.issues }, [...
|
|
5682
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5652
5683
|
} else {
|
|
5653
|
-
const fullpath = [...
|
|
5684
|
+
const fullpath = [...path16, ...issue2.path];
|
|
5654
5685
|
if (fullpath.length === 0) {
|
|
5655
5686
|
result.errors.push(mapper(issue2));
|
|
5656
5687
|
continue;
|
|
@@ -5689,8 +5720,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5689
5720
|
}
|
|
5690
5721
|
function toDotPath(_path) {
|
|
5691
5722
|
const segs = [];
|
|
5692
|
-
const
|
|
5693
|
-
for (const seg of
|
|
5723
|
+
const path16 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5724
|
+
for (const seg of path16) {
|
|
5694
5725
|
if (typeof seg === "number")
|
|
5695
5726
|
segs.push(`[${seg}]`);
|
|
5696
5727
|
else if (typeof seg === "symbol")
|
|
@@ -22828,13 +22859,13 @@ function resolveRef(ref, ctx) {
|
|
|
22828
22859
|
if (!ref.startsWith("#")) {
|
|
22829
22860
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
22830
22861
|
}
|
|
22831
|
-
const
|
|
22832
|
-
if (
|
|
22862
|
+
const path16 = ref.slice(1).split("/").filter(Boolean);
|
|
22863
|
+
if (path16.length === 0) {
|
|
22833
22864
|
return ctx.rootSchema;
|
|
22834
22865
|
}
|
|
22835
22866
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
22836
|
-
if (
|
|
22837
|
-
const key2 =
|
|
22867
|
+
if (path16[0] === defsKey) {
|
|
22868
|
+
const key2 = path16[1] === void 0 ? void 0 : decodeJSONPointerSegment(path16[1]);
|
|
22838
22869
|
if (!key2 || !ctx.defs[key2]) {
|
|
22839
22870
|
throw new Error(`Reference not found: ${ref}`);
|
|
22840
22871
|
}
|
|
@@ -23716,11 +23747,11 @@ function defineAction(config2) {
|
|
|
23716
23747
|
ref: actionRef(config2.name, data)
|
|
23717
23748
|
};
|
|
23718
23749
|
}
|
|
23719
|
-
function resolveField(obj,
|
|
23720
|
-
if (!
|
|
23721
|
-
if (!
|
|
23750
|
+
function resolveField(obj, path16) {
|
|
23751
|
+
if (!path16 || obj == null) return void 0;
|
|
23752
|
+
if (!path16.includes(".")) return obj[path16];
|
|
23722
23753
|
let cur = obj;
|
|
23723
|
-
for (const p of
|
|
23754
|
+
for (const p of path16.split(".")) {
|
|
23724
23755
|
if (cur == null) return void 0;
|
|
23725
23756
|
cur = cur[p];
|
|
23726
23757
|
}
|
|
@@ -25096,14 +25127,14 @@ function checkTypeMismatch(value, info) {
|
|
|
25096
25127
|
};
|
|
25097
25128
|
return null;
|
|
25098
25129
|
}
|
|
25099
|
-
function validationMessage(component,
|
|
25130
|
+
function validationMessage(component, path16, issue2) {
|
|
25100
25131
|
switch (issue2.code) {
|
|
25101
25132
|
case "type-mismatch":
|
|
25102
|
-
return `field "${
|
|
25133
|
+
return `field "${path16}" expects ${issue2.expected} but got ${issue2.actual}`;
|
|
25103
25134
|
case "missing-required":
|
|
25104
|
-
return `missing required field "${
|
|
25135
|
+
return `missing required field "${path16}"${issue2.signature ? ` \u2014 signature: ${issue2.signature}` : ""}`;
|
|
25105
25136
|
case "null-required":
|
|
25106
|
-
return `required field "${
|
|
25137
|
+
return `required field "${path16}" cannot be null${issue2.signature ? ` \u2014 signature: ${issue2.signature}` : ""}`;
|
|
25107
25138
|
case "unknown-component":
|
|
25108
25139
|
return `Unknown component "${component}" \u2014 not found in catalog or builtins${issue2.available?.length ? `. Available components: ${issue2.available.join(", ")}` : ""}`;
|
|
25109
25140
|
case "inline-reserved":
|
|
@@ -25118,12 +25149,12 @@ function validationMessage(component, path15, issue2) {
|
|
|
25118
25149
|
return component ? `No root statement, and no statement calls the root component ${component}` : "No root statement";
|
|
25119
25150
|
}
|
|
25120
25151
|
}
|
|
25121
|
-
function pushValidationIssue(ctx, component,
|
|
25152
|
+
function pushValidationIssue(ctx, component, path16, issue2) {
|
|
25122
25153
|
ctx.errors.push({
|
|
25123
25154
|
code: issue2.code,
|
|
25124
25155
|
component,
|
|
25125
|
-
path:
|
|
25126
|
-
message: validationMessage(component,
|
|
25156
|
+
path: path16,
|
|
25157
|
+
message: validationMessage(component, path16, issue2),
|
|
25127
25158
|
statementId: ctx.currentStatementId,
|
|
25128
25159
|
...issue2.code === "no-root" && issue2.reason === "fallback" && { severity: "warning" }
|
|
25129
25160
|
});
|
|
@@ -25133,10 +25164,10 @@ function resolveInvalidValue(required2, defaultValue) {
|
|
|
25133
25164
|
if (defaultValue !== void 0) return defaultValue;
|
|
25134
25165
|
return required2 ? INVALID2 : void 0;
|
|
25135
25166
|
}
|
|
25136
|
-
function validateElementPosition(element, s, component,
|
|
25167
|
+
function validateElementPosition(element, s, component, path16, ctx) {
|
|
25137
25168
|
if (isCompositeSchema(s)) return false;
|
|
25138
25169
|
if (typeof s["type"] === "string" || Array.isArray(s["enum"]) || "const" in s) {
|
|
25139
|
-
pushValidationIssue(ctx, component,
|
|
25170
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25140
25171
|
code: "type-mismatch",
|
|
25141
25172
|
expected: typeof s["type"] === "string" ? s["type"] : "a literal value",
|
|
25142
25173
|
actual: `component "${element.typeName}"`
|
|
@@ -25152,9 +25183,9 @@ function isOnlyComponentSlot(s, ctx) {
|
|
|
25152
25183
|
const names = slotRefNames(s);
|
|
25153
25184
|
return names.length > 0 && names.every((n) => n !== void 0 && ctx.cat?.has(n));
|
|
25154
25185
|
}
|
|
25155
|
-
function validateObjectValue(value, s, component,
|
|
25186
|
+
function validateObjectValue(value, s, component, path16, ctx) {
|
|
25156
25187
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
25157
|
-
pushValidationIssue(ctx, component,
|
|
25188
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25158
25189
|
code: "type-mismatch",
|
|
25159
25190
|
expected: "object",
|
|
25160
25191
|
actual: jsType(value)
|
|
@@ -25174,14 +25205,14 @@ function validateObjectValue(value, s, component, path15, ctx) {
|
|
|
25174
25205
|
(out ??= { ...obj })[key2] = fallback;
|
|
25175
25206
|
continue;
|
|
25176
25207
|
}
|
|
25177
|
-
pushValidationIssue(ctx, component, `${
|
|
25208
|
+
pushValidationIssue(ctx, component, `${path16}/${key2}`, { code: present ? "null-required" : "missing-required" });
|
|
25178
25209
|
invalid = true;
|
|
25179
25210
|
}
|
|
25180
25211
|
}
|
|
25181
25212
|
for (const key2 of Object.keys(props)) {
|
|
25182
25213
|
if (!(key2 in obj)) continue;
|
|
25183
25214
|
const sub = props[key2];
|
|
25184
|
-
let next = validateSchemaValue(obj[key2], sub, component, `${
|
|
25215
|
+
let next = validateSchemaValue(obj[key2], sub, component, `${path16}/${key2}`, ctx);
|
|
25185
25216
|
if (next === INVALID2) {
|
|
25186
25217
|
next = resolveInvalidValue(required2.includes(key2), getSchemaDefaultValue(sub));
|
|
25187
25218
|
if (next === INVALID2) {
|
|
@@ -25196,9 +25227,9 @@ function validateObjectValue(value, s, component, path15, ctx) {
|
|
|
25196
25227
|
}
|
|
25197
25228
|
return invalid ? INVALID2 : out ?? obj;
|
|
25198
25229
|
}
|
|
25199
|
-
function validateArrayValue(value, s, component,
|
|
25230
|
+
function validateArrayValue(value, s, component, path16, ctx) {
|
|
25200
25231
|
if (!Array.isArray(value)) {
|
|
25201
|
-
pushValidationIssue(ctx, component,
|
|
25232
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25202
25233
|
code: "type-mismatch",
|
|
25203
25234
|
expected: "array",
|
|
25204
25235
|
actual: jsType(value)
|
|
@@ -25209,19 +25240,19 @@ function validateArrayValue(value, s, component, path15, ctx) {
|
|
|
25209
25240
|
if (!items || typeof items !== "object" || Array.isArray(items)) return value;
|
|
25210
25241
|
let out;
|
|
25211
25242
|
for (let i = 0; i < value.length; i++) {
|
|
25212
|
-
const next = validateSchemaValue(value[i], items, component, `${
|
|
25243
|
+
const next = validateSchemaValue(value[i], items, component, `${path16}/${i}`, ctx);
|
|
25213
25244
|
if (next !== value[i] && !out) out = value.slice(0, i);
|
|
25214
25245
|
if (out && next !== INVALID2) out.push(next);
|
|
25215
25246
|
}
|
|
25216
25247
|
return out ?? value;
|
|
25217
25248
|
}
|
|
25218
|
-
function validateLeafValue(value, s, component,
|
|
25249
|
+
function validateLeafValue(value, s, component, path16, ctx) {
|
|
25219
25250
|
const leaf = getScalarTypeInfo(s);
|
|
25220
25251
|
if (leaf.expectedType == null && leaf.enumValues == null) return false;
|
|
25221
25252
|
if (leaf.enumValues && ctx.partial) return false;
|
|
25222
25253
|
const mismatch = checkTypeMismatch(value, leaf);
|
|
25223
25254
|
if (mismatch) {
|
|
25224
|
-
pushValidationIssue(ctx, component,
|
|
25255
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25225
25256
|
code: "type-mismatch",
|
|
25226
25257
|
...mismatch
|
|
25227
25258
|
});
|
|
@@ -25229,15 +25260,15 @@ function validateLeafValue(value, s, component, path15, ctx) {
|
|
|
25229
25260
|
}
|
|
25230
25261
|
return false;
|
|
25231
25262
|
}
|
|
25232
|
-
function validateSchemaValue(value, schema, component,
|
|
25263
|
+
function validateSchemaValue(value, schema, component, path16, ctx) {
|
|
25233
25264
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return value;
|
|
25234
25265
|
const s = schema;
|
|
25235
25266
|
if (value == null) return value;
|
|
25236
25267
|
if (isASTNode(value)) return value;
|
|
25237
|
-
if (isElementNode(value)) return validateElementPosition(value, s, component,
|
|
25268
|
+
if (isElementNode(value)) return validateElementPosition(value, s, component, path16, ctx) ? INVALID2 : value;
|
|
25238
25269
|
if (isCompositeSchema(s)) {
|
|
25239
25270
|
if (!isOnlyComponentSlot(s, ctx)) return value;
|
|
25240
|
-
pushValidationIssue(ctx, component,
|
|
25271
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25241
25272
|
code: "type-mismatch",
|
|
25242
25273
|
expected: slotRefNames(s).join(" | "),
|
|
25243
25274
|
actual: Array.isArray(value) ? "array" : typeof value === "object" ? "plain object" : typeof value
|
|
@@ -25245,9 +25276,9 @@ function validateSchemaValue(value, schema, component, path15, ctx) {
|
|
|
25245
25276
|
return INVALID2;
|
|
25246
25277
|
}
|
|
25247
25278
|
const type = s["type"];
|
|
25248
|
-
if (type === "object") return validateObjectValue(value, s, component,
|
|
25249
|
-
if (type === "array") return validateArrayValue(value, s, component,
|
|
25250
|
-
return validateLeafValue(value, s, component,
|
|
25279
|
+
if (type === "object") return validateObjectValue(value, s, component, path16, ctx);
|
|
25280
|
+
if (type === "array") return validateArrayValue(value, s, component, path16, ctx);
|
|
25281
|
+
return validateLeafValue(value, s, component, path16, ctx) ? INVALID2 : value;
|
|
25251
25282
|
}
|
|
25252
25283
|
function containsDynamicValue(v) {
|
|
25253
25284
|
if (v == null || typeof v !== "object") return false;
|
|
@@ -28873,11 +28904,11 @@ function routeOrthogonal({ boxes, edges: input2, xLines = [], yLines = [], avoid
|
|
|
28873
28904
|
const blocked = solid.some(([id, r]) => id !== e.from && id !== e.to && seg.x0 < r.x + r.width && r.x < seg.x1 && seg.y0 < r.y + r.height && r.y < seg.y1);
|
|
28874
28905
|
if (!blocked) {
|
|
28875
28906
|
routes.set(e.id, [a, b]);
|
|
28876
|
-
const
|
|
28907
|
+
const path17 = [];
|
|
28877
28908
|
const [i0, j0, i1, j1] = [ix(a.x), iy(a.y), ix(b.x), iy(b.y)];
|
|
28878
|
-
if (j0 === j1) for (let i = Math.min(i0, i1); i <= Math.max(i0, i1); i++)
|
|
28879
|
-
else for (let j = Math.min(j0, j1); j <= Math.max(j0, j1); j++)
|
|
28880
|
-
if (
|
|
28909
|
+
if (j0 === j1) for (let i = Math.min(i0, i1); i <= Math.max(i0, i1); i++) path17.push([i, j0]);
|
|
28910
|
+
else for (let j = Math.min(j0, j1); j <= Math.max(j0, j1); j++) path17.push([i0, j]);
|
|
28911
|
+
if (path17.every((pt2) => pt2[0] !== void 0 && pt2[1] !== void 0)) mark(path17);
|
|
28881
28912
|
continue;
|
|
28882
28913
|
}
|
|
28883
28914
|
}
|
|
@@ -28887,12 +28918,12 @@ function routeOrthogonal({ boxes, edges: input2, xLines = [], yLines = [], avoid
|
|
|
28887
28918
|
const sj = iy(st2.y);
|
|
28888
28919
|
const ti = ix(tt2.x);
|
|
28889
28920
|
const tj = iy(tt2.y);
|
|
28890
|
-
let
|
|
28921
|
+
let path16 = null;
|
|
28891
28922
|
if (si !== void 0 && sj !== void 0 && ti !== void 0 && tj !== void 0) {
|
|
28892
|
-
|
|
28923
|
+
path16 = search(si, sj, dirOf(e.fromSide), ti, tj, reverse[dirOf(e.toSide)]);
|
|
28893
28924
|
}
|
|
28894
|
-
const pts =
|
|
28895
|
-
if (
|
|
28925
|
+
const pts = path16 ? path16.map(([i, j]) => ({ x: xs[i], y: ys[j] })) : elbow(st2, tt2, e.fromSide);
|
|
28926
|
+
if (path16) mark(path16);
|
|
28896
28927
|
routes.set(e.id, simplify([e.fromPort, ...pts, e.toPort]));
|
|
28897
28928
|
}
|
|
28898
28929
|
return routes;
|
|
@@ -28961,10 +28992,10 @@ function routeOrthogonal({ boxes, edges: input2, xLines = [], yLines = [], avoid
|
|
|
28961
28992
|
}
|
|
28962
28993
|
return out.reverse();
|
|
28963
28994
|
}
|
|
28964
|
-
function mark(
|
|
28965
|
-
for (let k = 1; k <
|
|
28966
|
-
const [i0, j0] =
|
|
28967
|
-
const [i1, j1] =
|
|
28995
|
+
function mark(path16) {
|
|
28996
|
+
for (let k = 1; k < path16.length; k++) {
|
|
28997
|
+
const [i0, j0] = path16[k - 1];
|
|
28998
|
+
const [i1, j1] = path16[k];
|
|
28968
28999
|
if (j0 === j1) {
|
|
28969
29000
|
hUse[j0 * nx + Math.min(i0, i1)]++;
|
|
28970
29001
|
hPass[j0 * nx + i0]++;
|
|
@@ -29669,6 +29700,280 @@ init_lock();
|
|
|
29669
29700
|
init_sessions();
|
|
29670
29701
|
init_export();
|
|
29671
29702
|
|
|
29703
|
+
// src/cli/render-check.ts
|
|
29704
|
+
init_export();
|
|
29705
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
29706
|
+
import fs10 from "node:fs";
|
|
29707
|
+
import http2 from "node:http";
|
|
29708
|
+
import os2 from "node:os";
|
|
29709
|
+
import path9 from "node:path";
|
|
29710
|
+
var WIDTHS = [1280, 390];
|
|
29711
|
+
var LAUNCH_TIMEOUT_MS = 15e3;
|
|
29712
|
+
var RENDER_TIMEOUT_MS = 3e4;
|
|
29713
|
+
var FRAME_SANDBOX = "allow-scripts";
|
|
29714
|
+
function findChrome() {
|
|
29715
|
+
const fromEnv = process.env.CHROME_PATH;
|
|
29716
|
+
if (fromEnv) return fs10.existsSync(fromEnv) ? fromEnv : null;
|
|
29717
|
+
const candidates = [];
|
|
29718
|
+
if (process.platform === "darwin") {
|
|
29719
|
+
for (const p of ["Google Chrome.app/Contents/MacOS/Google Chrome", "Chromium.app/Contents/MacOS/Chromium", "Microsoft Edge.app/Contents/MacOS/Microsoft Edge"]) {
|
|
29720
|
+
candidates.push(path9.join("/Applications", p));
|
|
29721
|
+
}
|
|
29722
|
+
} else if (process.platform === "win32") {
|
|
29723
|
+
const roots = [process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"], process.env.LOCALAPPDATA].filter((x) => typeof x === "string");
|
|
29724
|
+
for (const r of roots) candidates.push(path9.join(r, "Google/Chrome/Application/chrome.exe"), path9.join(r, "Microsoft/Edge/Application/msedge.exe"));
|
|
29725
|
+
} else {
|
|
29726
|
+
const dirs = (process.env.PATH || "").split(path9.delimiter).filter(Boolean);
|
|
29727
|
+
for (const name of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge"]) {
|
|
29728
|
+
for (const d of dirs) candidates.push(path9.join(d, name));
|
|
29729
|
+
}
|
|
29730
|
+
}
|
|
29731
|
+
return candidates.find((c) => fs10.existsSync(c)) ?? null;
|
|
29732
|
+
}
|
|
29733
|
+
var MEASURE = `<script>(() => {
|
|
29734
|
+
const exceptions = [];
|
|
29735
|
+
addEventListener("error", (e) => exceptions.push(String((e.error && e.error.message) || e.message || "error")));
|
|
29736
|
+
addEventListener("unhandledrejection", (e) => exceptions.push(String((e.reason && e.reason.message) || e.reason)));
|
|
29737
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
29738
|
+
addEventListener("message", async (e) => {
|
|
29739
|
+
const d = e.data;
|
|
29740
|
+
if (e.source !== parent || !d || d.austereMeasure !== 1) return;
|
|
29741
|
+
for (let i = 0; i < 100; i++) {
|
|
29742
|
+
const pending = [...document.querySelectorAll(".austere-mermaid")].filter((m) => !m.querySelector("svg, .austere-render-error"));
|
|
29743
|
+
if (!pending.length) break;
|
|
29744
|
+
await sleep(100);
|
|
29745
|
+
}
|
|
29746
|
+
await sleep(200);
|
|
29747
|
+
const shown = [...document.querySelectorAll(".austere-render-error")].map((el) => ({
|
|
29748
|
+
statement: (el.closest("[data-oui-id]") && el.closest("[data-oui-id]").getAttribute("data-oui-id")) || "",
|
|
29749
|
+
error: el.textContent.trim().slice(0, 300),
|
|
29750
|
+
}));
|
|
29751
|
+
const se = document.scrollingElement;
|
|
29752
|
+
let past = null;
|
|
29753
|
+
if (se.scrollWidth > se.clientWidth + 1) {
|
|
29754
|
+
const vw = se.clientWidth;
|
|
29755
|
+
// Past the right edge and not inside a box that clips or scrolls it (a wide table in its own scroller is fine).
|
|
29756
|
+
const sticksOut = (el) => {
|
|
29757
|
+
if (el.getBoundingClientRect().right <= vw + 1) return false;
|
|
29758
|
+
for (let a = el.parentElement; a && a !== document.body; a = a.parentElement) {
|
|
29759
|
+
if (getComputedStyle(a).overflowX !== "visible" && a.getBoundingClientRect().right <= vw + 1) return false;
|
|
29760
|
+
}
|
|
29761
|
+
return true;
|
|
29762
|
+
};
|
|
29763
|
+
// Statement wrappers have no box of their own (display: contents), so find the innermost
|
|
29764
|
+
// elements that stick out and name the statement around each.
|
|
29765
|
+
const out = [...document.body.querySelectorAll("*")].slice(0, 20000).filter(sticksOut);
|
|
29766
|
+
const outSet = new Set(out);
|
|
29767
|
+
const leaves = out.filter((el) => ![...el.children].some((c) => outSet.has(c)));
|
|
29768
|
+
const ids = leaves.map((el) => el.closest("[data-oui-id]") && el.closest("[data-oui-id]").getAttribute("data-oui-id")).filter(Boolean);
|
|
29769
|
+
past = [...new Set(ids)].slice(0, 8);
|
|
29770
|
+
}
|
|
29771
|
+
const statements = new Set([...document.querySelectorAll("[data-oui-id]")].map((el) => el.getAttribute("data-oui-id"))).size;
|
|
29772
|
+
parent.postMessage({ austereMeasured: 1, id: d.id, shown, exceptions: exceptions.slice(0, 20), past, statements }, "*");
|
|
29773
|
+
});
|
|
29774
|
+
})();</script>`;
|
|
29775
|
+
var HARNESS = `<!doctype html><html><head><meta charset="utf-8"></head><body style="margin:0">
|
|
29776
|
+
<iframe id="f" sandbox="${FRAME_SANDBOX}" src="/page.html" style="border:0;width:${WIDTHS[0]}px;height:900px"></iframe>
|
|
29777
|
+
<script>
|
|
29778
|
+
const f = document.getElementById("f");
|
|
29779
|
+
const errors = [];
|
|
29780
|
+
const answers = new Map();
|
|
29781
|
+
let ready = false;
|
|
29782
|
+
addEventListener("message", (e) => {
|
|
29783
|
+
const d = e.data;
|
|
29784
|
+
if (e.source !== f.contentWindow || !d) return;
|
|
29785
|
+
if (d.austereMeasured === 1) return answers.get(d.id)?.(d);
|
|
29786
|
+
if (d.austere !== 1) return;
|
|
29787
|
+
if (d.type === "hello") f.contentWindow.postMessage({ austere: 1, type: "init", mode: "explore", scrollY: 0 }, "*");
|
|
29788
|
+
else if (d.type === "ready") ready = true;
|
|
29789
|
+
else if (d.type === "errors") for (const x of d.errors || []) errors.push({ statement: String(x.statementId || ""), error: String(x.message || "") });
|
|
29790
|
+
else if (d.type === "runtime-error" && d.error) errors.push({ statement: String(d.error.statementId || ""), error: String(d.error.message || "") });
|
|
29791
|
+
});
|
|
29792
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
29793
|
+
let next = 0;
|
|
29794
|
+
const measure = () => new Promise((resolve) => {
|
|
29795
|
+
const id = ++next;
|
|
29796
|
+
answers.set(id, resolve);
|
|
29797
|
+
f.contentWindow.postMessage({ austereMeasure: 1, id }, "*");
|
|
29798
|
+
});
|
|
29799
|
+
window.__austereReport = async (widths) => {
|
|
29800
|
+
for (let i = 0; i < 150 && !ready; i++) await sleep(100);
|
|
29801
|
+
const overflow = [];
|
|
29802
|
+
let last = null;
|
|
29803
|
+
for (const w of widths) {
|
|
29804
|
+
f.style.width = w + "px";
|
|
29805
|
+
await sleep(300);
|
|
29806
|
+
last = await measure();
|
|
29807
|
+
if (last.past) overflow.push({ width: w, statements: last.past });
|
|
29808
|
+
}
|
|
29809
|
+
const all = [...errors];
|
|
29810
|
+
for (const s of last.shown) if (!all.some((x) => x.statement === s.statement)) all.push(s);
|
|
29811
|
+
for (const x of last.exceptions) if (!all.some((y) => y.error === x)) all.push({ statement: "", error: x.slice(0, 300) });
|
|
29812
|
+
return { rendered: ready, statements: last.statements, errors: all, overflow };
|
|
29813
|
+
};
|
|
29814
|
+
</script></body></html>`;
|
|
29815
|
+
function connect(url2) {
|
|
29816
|
+
const ws = new WebSocket(url2);
|
|
29817
|
+
let next = 0;
|
|
29818
|
+
const pending = /* @__PURE__ */ new Map();
|
|
29819
|
+
const listeners = [];
|
|
29820
|
+
ws.onmessage = (ev) => {
|
|
29821
|
+
const m = JSON.parse(String(ev.data));
|
|
29822
|
+
const p = m.id != null ? pending.get(m.id) : void 0;
|
|
29823
|
+
if (p && m.id != null) {
|
|
29824
|
+
pending.delete(m.id);
|
|
29825
|
+
if (m.error) p.reject(new Error(m.error.message));
|
|
29826
|
+
else p.resolve(m.result);
|
|
29827
|
+
} else for (const fn2 of listeners) fn2(m);
|
|
29828
|
+
};
|
|
29829
|
+
const opened = new Promise((resolve, reject) => {
|
|
29830
|
+
ws.onopen = () => resolve();
|
|
29831
|
+
ws.onerror = () => reject(new Error("could not connect to Chrome's DevTools endpoint"));
|
|
29832
|
+
});
|
|
29833
|
+
return {
|
|
29834
|
+
opened,
|
|
29835
|
+
send(method, params = {}, sessionId2) {
|
|
29836
|
+
const id = ++next;
|
|
29837
|
+
ws.send(JSON.stringify({ id, method, params, sessionId: sessionId2 }));
|
|
29838
|
+
return new Promise((resolve, reject) => pending.set(id, { resolve: (v) => resolve(v), reject }));
|
|
29839
|
+
},
|
|
29840
|
+
on(fn2) {
|
|
29841
|
+
listeners.push(fn2);
|
|
29842
|
+
},
|
|
29843
|
+
close() {
|
|
29844
|
+
ws.close();
|
|
29845
|
+
}
|
|
29846
|
+
};
|
|
29847
|
+
}
|
|
29848
|
+
function launch(bin, profile) {
|
|
29849
|
+
const args = [
|
|
29850
|
+
"--headless=new",
|
|
29851
|
+
"--remote-debugging-port=0",
|
|
29852
|
+
`--user-data-dir=${profile}`,
|
|
29853
|
+
"--no-first-run",
|
|
29854
|
+
"--no-default-browser-check",
|
|
29855
|
+
"--disable-gpu",
|
|
29856
|
+
"--disable-extensions",
|
|
29857
|
+
"--disable-background-networking",
|
|
29858
|
+
"--disable-component-update",
|
|
29859
|
+
"--disable-breakpad",
|
|
29860
|
+
"--disable-sync",
|
|
29861
|
+
"--disable-dev-shm-usage",
|
|
29862
|
+
// Without these, Chrome waits about 25 s for a desktop keyring (Linux) or keychain (macOS) that may never answer.
|
|
29863
|
+
"--password-store=basic",
|
|
29864
|
+
"--use-mock-keychain",
|
|
29865
|
+
"--mute-audio",
|
|
29866
|
+
"--window-size=1400,1000"
|
|
29867
|
+
];
|
|
29868
|
+
if (process.getuid?.() === 0) args.push("--no-sandbox");
|
|
29869
|
+
args.push("about:blank");
|
|
29870
|
+
const child = spawn2(bin, args, { stdio: ["ignore", "ignore", "pipe"], windowsHide: true });
|
|
29871
|
+
const exited = new Promise((resolve) => {
|
|
29872
|
+
child.once("exit", () => resolve());
|
|
29873
|
+
child.once("error", () => resolve());
|
|
29874
|
+
});
|
|
29875
|
+
const endpoint = new Promise((resolve, reject) => {
|
|
29876
|
+
let err = "";
|
|
29877
|
+
const timer = setTimeout(() => reject(new Error(`Chrome did not start within ${LAUNCH_TIMEOUT_MS / 1e3} s`)), LAUNCH_TIMEOUT_MS);
|
|
29878
|
+
child.stderr?.on("data", (c) => {
|
|
29879
|
+
err += c;
|
|
29880
|
+
const m = /DevTools listening on (ws:\/\/\S+)/.exec(err);
|
|
29881
|
+
if (m?.[1]) {
|
|
29882
|
+
clearTimeout(timer);
|
|
29883
|
+
resolve(m[1]);
|
|
29884
|
+
}
|
|
29885
|
+
});
|
|
29886
|
+
child.once("error", (e) => {
|
|
29887
|
+
clearTimeout(timer);
|
|
29888
|
+
reject(e);
|
|
29889
|
+
});
|
|
29890
|
+
child.once("exit", (code) => {
|
|
29891
|
+
clearTimeout(timer);
|
|
29892
|
+
reject(new Error(`Chrome exited (${code}): ${err.trim().split("\n").pop() || "no output"}`));
|
|
29893
|
+
});
|
|
29894
|
+
});
|
|
29895
|
+
return { child, endpoint, exited };
|
|
29896
|
+
}
|
|
29897
|
+
function serve(page) {
|
|
29898
|
+
const server = http2.createServer((req, res) => {
|
|
29899
|
+
if (req.url === "/") {
|
|
29900
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(HARNESS);
|
|
29901
|
+
} else if (req.url === "/page.html") {
|
|
29902
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "content-security-policy": `sandbox ${FRAME_SANDBOX}` }).end(page);
|
|
29903
|
+
} else res.writeHead(404).end();
|
|
29904
|
+
});
|
|
29905
|
+
return new Promise((resolve, reject) => {
|
|
29906
|
+
server.on("error", reject);
|
|
29907
|
+
server.listen(0, "127.0.0.1", () => {
|
|
29908
|
+
const addr = server.address();
|
|
29909
|
+
resolve({ url: `http://127.0.0.1:${addr.port}/`, close: () => server.close() });
|
|
29910
|
+
});
|
|
29911
|
+
});
|
|
29912
|
+
}
|
|
29913
|
+
function withMeasure(html) {
|
|
29914
|
+
const at2 = html.toLowerCase().lastIndexOf("</body>");
|
|
29915
|
+
return at2 < 0 ? html + MEASURE : html.slice(0, at2) + MEASURE + html.slice(at2);
|
|
29916
|
+
}
|
|
29917
|
+
async function renderCheck(html) {
|
|
29918
|
+
const bin = findChrome();
|
|
29919
|
+
if (!bin) {
|
|
29920
|
+
const reason = process.env.CHROME_PATH ? `CHROME_PATH does not exist: ${process.env.CHROME_PATH}` : "no Chrome, Chromium or Edge found; install one or set CHROME_PATH";
|
|
29921
|
+
return { status: "unavailable", reason };
|
|
29922
|
+
}
|
|
29923
|
+
const { html: page, notices } = exportHtml(html);
|
|
29924
|
+
const profile = fs10.mkdtempSync(path9.join(os2.tmpdir(), "austere-render-"));
|
|
29925
|
+
const site = await serve(withMeasure(page));
|
|
29926
|
+
const { child, endpoint, exited } = launch(bin, profile);
|
|
29927
|
+
let cdp = null;
|
|
29928
|
+
let timer;
|
|
29929
|
+
try {
|
|
29930
|
+
const run2 = (async () => {
|
|
29931
|
+
const conn = connect(await endpoint);
|
|
29932
|
+
cdp = conn;
|
|
29933
|
+
await conn.opened;
|
|
29934
|
+
const { targetId } = await conn.send("Target.createTarget", { url: "about:blank" });
|
|
29935
|
+
const { sessionId: sessionId2 } = await conn.send("Target.attachToTarget", { targetId, flatten: true });
|
|
29936
|
+
await conn.send("Page.enable", {}, sessionId2);
|
|
29937
|
+
const loaded = new Promise((resolve) => conn.on((m) => m.method === "Page.loadEventFired" && m.sessionId === sessionId2 && resolve()));
|
|
29938
|
+
await conn.send("Page.navigate", { url: site.url }, sessionId2);
|
|
29939
|
+
await loaded;
|
|
29940
|
+
const r = await conn.send(
|
|
29941
|
+
"Runtime.evaluate",
|
|
29942
|
+
{ expression: `window.__austereReport(${JSON.stringify(WIDTHS)})`, awaitPromise: true, returnByValue: true },
|
|
29943
|
+
sessionId2
|
|
29944
|
+
);
|
|
29945
|
+
if (r.exceptionDetails) throw new Error(`render report failed: ${r.exceptionDetails.text}`);
|
|
29946
|
+
const report = r.result.value;
|
|
29947
|
+
if (!report.rendered && !report.errors.length) report.errors.push({ statement: "", error: "the page did not finish rendering" });
|
|
29948
|
+
return { status: "ok", statements: report.statements, errors: report.errors, overflow: report.overflow, notices };
|
|
29949
|
+
})();
|
|
29950
|
+
const timeout = new Promise((_, reject) => {
|
|
29951
|
+
timer = setTimeout(() => reject(new Error(`rendering took longer than ${RENDER_TIMEOUT_MS / 1e3} s`)), RENDER_TIMEOUT_MS);
|
|
29952
|
+
});
|
|
29953
|
+
return await Promise.race([run2, timeout]);
|
|
29954
|
+
} catch (err) {
|
|
29955
|
+
return { status: "unavailable", reason: err instanceof Error ? err.message : String(err) };
|
|
29956
|
+
} finally {
|
|
29957
|
+
clearTimeout(timer);
|
|
29958
|
+
const conn = cdp;
|
|
29959
|
+
conn?.send("Browser.close").catch(() => {
|
|
29960
|
+
});
|
|
29961
|
+
let grace;
|
|
29962
|
+
await Promise.race([exited, new Promise((r) => grace = setTimeout(r, 3e3))]);
|
|
29963
|
+
clearTimeout(grace);
|
|
29964
|
+
conn?.close();
|
|
29965
|
+
if (child.exitCode == null && child.pid != null) child.kill();
|
|
29966
|
+
site.close();
|
|
29967
|
+
let killGrace;
|
|
29968
|
+
await Promise.race([exited, new Promise((r) => killGrace = setTimeout(r, 3e3))]);
|
|
29969
|
+
clearTimeout(killGrace);
|
|
29970
|
+
try {
|
|
29971
|
+
fs10.rmSync(profile, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
29972
|
+
} catch {
|
|
29973
|
+
}
|
|
29974
|
+
}
|
|
29975
|
+
}
|
|
29976
|
+
|
|
29672
29977
|
// src/cli/output.ts
|
|
29673
29978
|
var PLAIN = /^[A-Za-z0-9_./@:+-][A-Za-z0-9 _./@:+(),=<>*#%?!'-]*$/;
|
|
29674
29979
|
var json2 = (v) => JSON.stringify(v).replace(/[\u0085\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
@@ -29902,85 +30207,69 @@ WORKFLOW
|
|
|
29902
30207
|
intro = Card([CardHeader("Title", "Subtitle"), introText])
|
|
29903
30208
|
introText = MarkDownRenderer("...")
|
|
29904
30209
|
</script></body></html>
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29910
|
-
|
|
29911
|
-
|
|
29912
|
-
|
|
29913
|
-
|
|
29914
|
-
|
|
30210
|
+
Arguments are positional and typed: follow each signature in spec exactly (order and
|
|
30211
|
+
type; an array stays an array). Pass null to skip an optional one.
|
|
30212
|
+
3. ${BIN2} <file.html> validate + open for review; fix any errors it lists.
|
|
30213
|
+
--no-open: print the url for the user instead.
|
|
30214
|
+
4. ${BIN2} poll <file.html> wait for feedback (foreground; if the call times out, poll again).
|
|
30215
|
+
5. Revise with patch only: send just the changed statements; do not rewrite or re-read
|
|
30216
|
+
the file (each note carries its statement's line and source):
|
|
30217
|
+
${BIN2} patch <file.html> <<'EOF'
|
|
30218
|
+
risks = Table([...]) same name replaces, new name adds, \`name = null\` deletes
|
|
30219
|
+
EOF
|
|
30220
|
+
If a changed value appears in several statements, patch each of them.
|
|
30221
|
+
Then acknowledge and reply in one step:
|
|
29915
30222
|
${BIN2} poll <file.html> --cursor <n> --agent-reply "one line: what changed"
|
|
29916
|
-
|
|
29917
|
-
without either returns it again, so feedback lost on the way is not lost.
|
|
29918
|
-
6. On status: ended, stop polling and continue in chat. Never reopen uninvited. Notes
|
|
29919
|
-
the reviewer adds later are kept, and the next poll returns them.
|
|
30223
|
+
6. On status: ended, stop polling and continue in chat. Never reopen uninvited.
|
|
29920
30224
|
|
|
29921
|
-
FEEDBACK (
|
|
29922
|
-
status: feedback | ended | browser_disconnected | interrupted | replaced, plus
|
|
29923
|
-
cursor:
|
|
29924
|
-
items,
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
part: the exact piece clicked, e.g. a diff file:+line or a diagram node), note
|
|
29928
|
-
action: <@ToAssistant message> form: {<formName>: {<field>: value}}, context
|
|
29929
|
-
message: <free text from the composer>
|
|
29930
|
-
render_error: <statement> a render failure the reviewer asked you to fix
|
|
30225
|
+
FEEDBACK (poll)
|
|
30226
|
+
status: feedback | ended | browser_disconnected | interrupted | replaced, plus next:
|
|
30227
|
+
cursor: pass it back to acknowledge; unacknowledged feedback is returned again.
|
|
30228
|
+
items: annotation: <statement> (<Component>) with line, source, note
|
|
30229
|
+
action: <@ToAssistant message> with form values | message: <free text>
|
|
30230
|
+
render_error: <statement> sent by the open review page on its own
|
|
29931
30231
|
|
|
29932
30232
|
CHECK verifies syntax, component names, required props, unresolved references and
|
|
29933
|
-
statements unreachable from root.
|
|
29934
|
-
show in the reviewer's panel and can come back as render_error. CHECK does not judge how
|
|
29935
|
-
a page looks: open it for that. It does warn when an Architecture diagram will not fit the
|
|
30233
|
+
statements unreachable from root. It warns when an Architecture diagram will not fit the
|
|
29936
30234
|
page (it would scroll sideways).
|
|
29937
30235
|
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
30236
|
+
LOOKS
|
|
30237
|
+
The reviewer judges how the page looks. Do not open, screenshot or inspect the page
|
|
30238
|
+
yourself (browser tools, curl, headless runs) unless the user asks: check ok + open is
|
|
30239
|
+
done. Render failures come back through poll. When the user asks you to verify the
|
|
30240
|
+
page, run \`${BIN2} check --render <file>\` (headless Chrome, text report, no image).
|
|
29941
30241
|
|
|
29942
|
-
COMMANDS
|
|
29943
|
-
${BIN2}
|
|
29944
|
-
|
|
29945
|
-
${BIN2}
|
|
29946
|
-
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
${BIN2}
|
|
29950
|
-
${BIN2}
|
|
29951
|
-
${BIN2}
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
|
|
29955
|
-
|
|
29956
|
-
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
|
|
29960
|
-
|
|
29961
|
-
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
|
|
29965
|
-
packs add components; a page that uses one declares it
|
|
29966
|
-
(check --fix writes the <meta name="austere-packs"> tag)
|
|
29967
|
-
${BIN2} components new <dir> | build [dir] write your own pack
|
|
29968
|
-
${BIN2} export <file> single self-contained HTML [--out path]
|
|
29969
|
-
${BIN2} hub on|off|status|list|notes|open the owner's home page: every artifact by project/date, every note
|
|
29970
|
-
${BIN2} share <file> --as "<name>" [--auto] invite link for a teammate on your tailnet. Their notes
|
|
29971
|
-
wait for the owner to send them on; --auto skips the wait
|
|
29972
|
-
${BIN2} share list [--links] | revoke <name|id>
|
|
29973
|
-
${BIN2} stop stop the background server
|
|
29974
|
-
${BIN2} server [--persist] run the server in the foreground; --persist: never idle out
|
|
29975
|
-
(behind a reverse proxy, under systemd)
|
|
29976
|
-
${BIN2} mcp MCP server on stdio: spec check open poll reply patch end as tools
|
|
30242
|
+
COMMANDS
|
|
30243
|
+
${BIN2} check <file> validate without a browser: syntax, components, props,
|
|
30244
|
+
references [--fix: declare packs] [--render]
|
|
30245
|
+
${BIN2} reply <file> reply without polling --agent-reply "..." [--cursor n]
|
|
30246
|
+
${BIN2} end <file> stop as the agent; later notes wait for the next poll
|
|
30247
|
+
${BIN2} feedback <file> notes no agent has answered yet [--all] [--peek]
|
|
30248
|
+
${BIN2} login [file] single-use sign-in link for a browser the CLI did not open
|
|
30249
|
+
${BIN2} components search <words> | show <Name...> components spec leaves out
|
|
30250
|
+
${BIN2} <command> --help one command's flags and an example
|
|
30251
|
+
${BIN2} help all every command, notes and environment
|
|
30252
|
+
`;
|
|
30253
|
+
function helpAll() {
|
|
30254
|
+
const rows = Object.entries(COMMANDS).map(([, c]) => ` ${BIN2} ${c.usage}
|
|
30255
|
+
${c.summary}`);
|
|
30256
|
+
return `
|
|
30257
|
+
ALL COMMANDS
|
|
30258
|
+
${rows.join("\n")}
|
|
30259
|
+
|
|
30260
|
+
NOTES
|
|
30261
|
+
The CDN script tag lets the file open on its own (needs network); review and export work
|
|
30262
|
+
offline. A browser the CLI did not open gets 403 until it signs in with \`${BIN2} login\`.
|
|
30263
|
+
Mermaid syntax is only checked when it renders. check warns when an Architecture diagram
|
|
30264
|
+
will not fit the page. check --render needs Chrome, Chromium or Edge (or CHROME_PATH).
|
|
29977
30265
|
|
|
29978
30266
|
ENV
|
|
29979
30267
|
AUSTERE_AXI_PORT (4388) AUSTERE_AXI_STATE_DIR (~/.austere-axi) AUSTERE_AXI_NO_OPEN=1
|
|
29980
30268
|
AUSTERE_AXI_HOST AUSTERE_AXI_NO_TAILSCALE=1 AUSTERE_AXI_DISCONNECT_GRACE_MS (300000)
|
|
29981
30269
|
AUSTERE_AXI_IDLE_TIMEOUT_MS (1800000) AUSTERE_AXI_PERSIST=1 AUSTERE_AXI_ALLOWED_HOSTS AUSTERE_AXI_LINK_URL
|
|
29982
|
-
AUSTERE_AXI_NPM_REGISTRY
|
|
30270
|
+
AUSTERE_AXI_NPM_REGISTRY CHROME_PATH (check --render)
|
|
29983
30271
|
`;
|
|
30272
|
+
}
|
|
29984
30273
|
var COMMANDS = {
|
|
29985
30274
|
open: {
|
|
29986
30275
|
usage: "<file> [--no-open] [--reopen]",
|
|
@@ -30028,9 +30317,12 @@ var COMMANDS = {
|
|
|
30028
30317
|
example: "feedback plan.html"
|
|
30029
30318
|
},
|
|
30030
30319
|
check: {
|
|
30031
|
-
usage: "check <file> [--fix]",
|
|
30320
|
+
usage: "check <file> [--fix] [--render]",
|
|
30032
30321
|
summary: "validate the program without a browser: syntax, components, props, references, reachability",
|
|
30033
|
-
flags: {
|
|
30322
|
+
flags: {
|
|
30323
|
+
fix: 'declare the packs the page uses (writes the <meta name="austere-packs"> tag)',
|
|
30324
|
+
render: "also render it in headless Chrome and report render errors and sideways scrolling as text (needs Chrome or CHROME_PATH); only when the user asks"
|
|
30325
|
+
},
|
|
30034
30326
|
example: "check plan.html"
|
|
30035
30327
|
},
|
|
30036
30328
|
patch: {
|
|
@@ -30135,7 +30427,7 @@ function needFile(file3, cmd) {
|
|
|
30135
30427
|
return null;
|
|
30136
30428
|
}
|
|
30137
30429
|
const abs = canonicalFile(file3);
|
|
30138
|
-
if (!
|
|
30430
|
+
if (!fs15.existsSync(abs)) {
|
|
30139
30431
|
fail(`no such file: ${abs}`);
|
|
30140
30432
|
return null;
|
|
30141
30433
|
}
|
|
@@ -30152,7 +30444,7 @@ function readStdin() {
|
|
|
30152
30444
|
async function replyText(flags) {
|
|
30153
30445
|
if (typeof flags["agent-reply-file"] === "string") {
|
|
30154
30446
|
const f = flags["agent-reply-file"];
|
|
30155
|
-
return (f === "-" ? await readStdin() :
|
|
30447
|
+
return (f === "-" ? await readStdin() : fs15.readFileSync(f, "utf8")).trim();
|
|
30156
30448
|
}
|
|
30157
30449
|
if (typeof flags["agent-reply"] === "string") return flags["agent-reply"].trim();
|
|
30158
30450
|
return "";
|
|
@@ -30191,7 +30483,7 @@ async function cmdHome() {
|
|
|
30191
30483
|
async function cmdOpen(file3, flags) {
|
|
30192
30484
|
const abs = needFile(file3, "open");
|
|
30193
30485
|
if (!abs || file3 === void 0) return;
|
|
30194
|
-
const html =
|
|
30486
|
+
const html = fs15.readFileSync(abs, "utf8");
|
|
30195
30487
|
const check2 = checkHtml(html, { file: quote(file3) });
|
|
30196
30488
|
await ensureServer();
|
|
30197
30489
|
const r = await request("POST", "/api/open", { file: abs, reopen: !!flags.reopen });
|
|
@@ -30230,7 +30522,7 @@ function quote(file3) {
|
|
|
30230
30522
|
function describeItems(abs, items) {
|
|
30231
30523
|
let lines = /* @__PURE__ */ new Map();
|
|
30232
30524
|
try {
|
|
30233
|
-
lines = statementLines(
|
|
30525
|
+
lines = statementLines(fs15.readFileSync(abs, "utf8"));
|
|
30234
30526
|
} catch {
|
|
30235
30527
|
}
|
|
30236
30528
|
const at2 = locator(lines);
|
|
@@ -30247,14 +30539,14 @@ function describeItems(abs, items) {
|
|
|
30247
30539
|
}
|
|
30248
30540
|
function thirdPartyOf(abs) {
|
|
30249
30541
|
try {
|
|
30250
|
-
return pagePacks(
|
|
30542
|
+
return pagePacks(fs15.readFileSync(abs, "utf8")).thirdParty.map((p) => `${p.name}@${p.version}`);
|
|
30251
30543
|
} catch {
|
|
30252
30544
|
return [];
|
|
30253
30545
|
}
|
|
30254
30546
|
}
|
|
30255
30547
|
var NEXT = {
|
|
30256
|
-
feedback: (f, cursor) => `
|
|
30257
|
-
feedbackEnded: (f, cursor) => `The reviewer sent this and ended the review. Apply it, then \`${BIN2} reply ${f} --cursor ${cursor} --agent-reply "<what changed>"\` and summarize in chat. Do not poll or reopen.`,
|
|
30548
|
+
feedback: (f, cursor) => `Revise with \`${BIN2} patch ${f}\`: send only the changed statements on stdin (same name replaces, new name adds); do not rewrite or re-read the file. Then \`${BIN2} poll ${f} --cursor ${cursor} --agent-reply "<one line: what changed>"\`.`,
|
|
30549
|
+
feedbackEnded: (f, cursor) => `The reviewer sent this and ended the review. Apply it with \`${BIN2} patch ${f}\` (changed statements only), then \`${BIN2} reply ${f} --cursor ${cursor} --agent-reply "<what changed>"\` and summarize in chat. Do not poll or reopen.`,
|
|
30258
30550
|
ended: (f) => `The review ended. Stop polling and continue in chat. Notes the reviewer adds later are kept for the next poll. Reopen only if the user asks (\`${BIN2} ${f} --reopen\`).`,
|
|
30259
30551
|
browser_disconnected: (f) => `Every review tab has been closed for a while. Stop polling and continue in chat; do not end the review. Notes the reviewer adds later are kept, and \`${BIN2} poll ${f}\` returns them.`,
|
|
30260
30552
|
replaced: () => "Another poll took over this session. Stop polling here.",
|
|
@@ -30273,7 +30565,7 @@ async function cmdPoll(file3, flags) {
|
|
|
30273
30565
|
if (!reply && (flags["agent-reply"] !== void 0 || flags["agent-reply-file"] !== void 0)) {
|
|
30274
30566
|
return fail("the agent reply is empty");
|
|
30275
30567
|
}
|
|
30276
|
-
process.stderr.write(`[${BIN2}] waiting for feedback on ${
|
|
30568
|
+
process.stderr.write(`[${BIN2}] waiting for feedback on ${path15.basename(abs)} (interrupting is safe: feedback stays queued)
|
|
30277
30569
|
`);
|
|
30278
30570
|
const ticker = process.stderr.isTTY ? setInterval(() => process.stderr.write(`[${BIN2}] still waiting\u2026
|
|
30279
30571
|
`), 6e4) : null;
|
|
@@ -30370,13 +30662,13 @@ function fixPacks(abs, html) {
|
|
|
30370
30662
|
return { html, changes };
|
|
30371
30663
|
}
|
|
30372
30664
|
const out = setPacksMeta(html, next);
|
|
30373
|
-
if (out !== html)
|
|
30665
|
+
if (out !== html) fs15.writeFileSync(abs, out);
|
|
30374
30666
|
return { html: out, changes: changes.length ? changes : ["updated integrity hashes"] };
|
|
30375
30667
|
}
|
|
30376
30668
|
async function cmdCheck(file3, flags = {}) {
|
|
30377
30669
|
const abs = needFile(file3, "check");
|
|
30378
30670
|
if (!abs || file3 === void 0) return;
|
|
30379
|
-
let html =
|
|
30671
|
+
let html = fs15.readFileSync(abs, "utf8");
|
|
30380
30672
|
let fixed = [];
|
|
30381
30673
|
if (flags.fix) ({ html, changes: fixed } = fixPacks(abs, html));
|
|
30382
30674
|
const result = checkHtml(html, { file: quote(file3) });
|
|
@@ -30385,9 +30677,30 @@ async function cmdCheck(file3, flags = {}) {
|
|
|
30385
30677
|
if (fixed.length) out.fixed = fixed;
|
|
30386
30678
|
if (result.errors.length) out.errors = result.errors;
|
|
30387
30679
|
if (result.warnings.length) out.warnings = result.warnings;
|
|
30388
|
-
if (
|
|
30680
|
+
if (flags.render) {
|
|
30681
|
+
if (!result.ok) out.render = "skipped: fix the errors above first";
|
|
30682
|
+
else {
|
|
30683
|
+
const render = await renderOut(html);
|
|
30684
|
+
out.render = render;
|
|
30685
|
+
if (render.errors) out.status = "errors";
|
|
30686
|
+
else if (render.status === "unavailable") out.status = "render_unavailable";
|
|
30687
|
+
}
|
|
30688
|
+
} else if (result.ok && result.hasMermaid) {
|
|
30689
|
+
out.note = "Mermaid sources are checked when the review page renders them; failures come back through poll.";
|
|
30690
|
+
}
|
|
30389
30691
|
print(out);
|
|
30390
|
-
if (
|
|
30692
|
+
if (out.status !== "ok") process.exitCode = 1;
|
|
30693
|
+
}
|
|
30694
|
+
async function renderOut(html) {
|
|
30695
|
+
const r = await renderCheck(html);
|
|
30696
|
+
if (r.status === "unavailable") return { status: "unavailable", reason: r.reason };
|
|
30697
|
+
const out = { status: r.errors.length ? "errors" : "clean", widths: "1280, 390", statements: r.statements };
|
|
30698
|
+
if (r.errors.length) out.errors = r.errors.map((e) => ({ statement: e.statement || "(page)", message: e.error }));
|
|
30699
|
+
if (r.overflow.length) {
|
|
30700
|
+
out.scrolls_sideways = r.overflow.map((o) => ({ width: o.width, statements: o.statements.length ? o.statements.join(", ") : "(not one statement)" }));
|
|
30701
|
+
}
|
|
30702
|
+
if (r.notices.length) out.notices = r.notices;
|
|
30703
|
+
return out;
|
|
30391
30704
|
}
|
|
30392
30705
|
async function cmdPatch(file3) {
|
|
30393
30706
|
const abs = needFile(file3, "patch");
|
|
@@ -30395,7 +30708,7 @@ async function cmdPatch(file3) {
|
|
|
30395
30708
|
if (process.stdin.isTTY) return usage(`pipe the changed statements on stdin: ${BIN2} patch ${quote(file3)} <<'EOF' ... EOF`, "patch");
|
|
30396
30709
|
const patch = await readStdin();
|
|
30397
30710
|
if (!patch.trim()) return fail("stdin was empty");
|
|
30398
|
-
const html =
|
|
30711
|
+
const html = fs15.readFileSync(abs, "utf8");
|
|
30399
30712
|
const art = readArtifact(html);
|
|
30400
30713
|
if (art.program == null) return fail('no <script type="text/openui"> block to patch');
|
|
30401
30714
|
const merged = mergeStatements(art.program.trim(), patch.trim(), "root");
|
|
@@ -30403,7 +30716,7 @@ async function cmdPatch(file3) {
|
|
|
30403
30716
|
const patched = new Set(splitStatements(patch).filter((s) => !/=\s*null\s*;?$/.test(s.text)).map((s) => s.id));
|
|
30404
30717
|
const dropped = [...patched].filter((id) => !kept.has(id));
|
|
30405
30718
|
const next = replaceProgram(html, merged);
|
|
30406
|
-
|
|
30719
|
+
fs15.writeFileSync(abs, next);
|
|
30407
30720
|
const result = checkHtml(next, { file: quote(file3) });
|
|
30408
30721
|
await registerWithHub(abs, result.title, sessionId(abs));
|
|
30409
30722
|
const out = { status: "patched", file: abs };
|
|
@@ -30418,7 +30731,7 @@ function packSpec(p) {
|
|
|
30418
30731
|
return lines.join("\n") + "\n";
|
|
30419
30732
|
}
|
|
30420
30733
|
function cmdSpec(groups) {
|
|
30421
|
-
let text =
|
|
30734
|
+
let text = fs15.readFileSync(assetPath("prompt.md"), "utf8");
|
|
30422
30735
|
const packs = installedPacks();
|
|
30423
30736
|
const packArgs = groups.filter((g) => packs.some((p) => p.name === g));
|
|
30424
30737
|
if (packArgs.length) {
|
|
@@ -30452,9 +30765,9 @@ ${trimmed.join("\n")}
|
|
|
30452
30765
|
function cmdExport(file3, flags) {
|
|
30453
30766
|
const abs = needFile(file3, "export");
|
|
30454
30767
|
if (!abs) return;
|
|
30455
|
-
const out = typeof flags.out === "string" ?
|
|
30456
|
-
const { html, notices } = exportHtml(
|
|
30457
|
-
|
|
30768
|
+
const out = typeof flags.out === "string" ? path15.resolve(flags.out) : abs.replace(/\.html?$/i, "") + ".export.html";
|
|
30769
|
+
const { html, notices } = exportHtml(fs15.readFileSync(abs, "utf8"));
|
|
30770
|
+
fs15.writeFileSync(out, html);
|
|
30458
30771
|
print({ status: "exported", file: out, bytes: html.length, notices: notices.length ? notices : void 0 });
|
|
30459
30772
|
}
|
|
30460
30773
|
function printResult(out) {
|
|
@@ -30514,7 +30827,7 @@ async function cmdComponents(sub, args, flags) {
|
|
|
30514
30827
|
pack: r.name,
|
|
30515
30828
|
dir: r.dir,
|
|
30516
30829
|
files: r.files,
|
|
30517
|
-
next: `cd ${quote(
|
|
30830
|
+
next: `cd ${quote(path15.relative(process.cwd(), r.dir) || ".")} && npm install && npm run build, then \`${BIN2} components add .\``
|
|
30518
30831
|
});
|
|
30519
30832
|
} catch (err) {
|
|
30520
30833
|
return fail(messageOf(err));
|
|
@@ -30596,7 +30909,7 @@ async function cmdFeedback(file3, flags) {
|
|
|
30596
30909
|
file: abs,
|
|
30597
30910
|
review: status,
|
|
30598
30911
|
items,
|
|
30599
|
-
next: `Apply what still applies
|
|
30912
|
+
next: `Apply what still applies with \`${BIN2} patch ${f}\` (changed statements only; names are stable), then \`${BIN2} reply ${f} --agent-reply "<what changed>"\`, which marks it addressed. To continue live with the reviewer: \`${BIN2} ${f}\`${status === "ended" ? " --reopen, if the user asks" : ""}, then poll.`
|
|
30600
30913
|
});
|
|
30601
30914
|
}
|
|
30602
30915
|
async function cmdHub(sub, args, flags) {
|
|
@@ -30625,7 +30938,7 @@ async function cmdHub(sub, args, flags) {
|
|
|
30625
30938
|
server: h ? `running on ${port()}${h.persist ? " (persist)" : ""}` : "not running"
|
|
30626
30939
|
};
|
|
30627
30940
|
if (hubEnabled() && h) out.url = url2;
|
|
30628
|
-
if (
|
|
30941
|
+
if (fs15.existsSync(hubDbFile())) {
|
|
30629
30942
|
try {
|
|
30630
30943
|
const hub = await Hub.open();
|
|
30631
30944
|
try {
|
|
@@ -30641,8 +30954,8 @@ async function cmdHub(sub, args, flags) {
|
|
|
30641
30954
|
return print(out);
|
|
30642
30955
|
}
|
|
30643
30956
|
case "list": {
|
|
30644
|
-
if (!
|
|
30645
|
-
const project = typeof flags.project === "string" ?
|
|
30957
|
+
if (!fs15.existsSync(hubDbFile())) return print({ artifacts: "none", next: `\`${BIN2} hub on\` starts indexing.` });
|
|
30958
|
+
const project = typeof flags.project === "string" ? path15.resolve(flags.project) : "";
|
|
30646
30959
|
let hub;
|
|
30647
30960
|
try {
|
|
30648
30961
|
hub = await Hub.open();
|
|
@@ -30667,8 +30980,8 @@ async function cmdHub(sub, args, flags) {
|
|
|
30667
30980
|
});
|
|
30668
30981
|
}
|
|
30669
30982
|
case "notes": {
|
|
30670
|
-
if (!
|
|
30671
|
-
const project = typeof flags.project === "string" ?
|
|
30983
|
+
if (!fs15.existsSync(hubDbFile())) return print({ notes: "none", next: `\`${BIN2} hub on\` starts indexing.` });
|
|
30984
|
+
const project = typeof flags.project === "string" ? path15.resolve(flags.project) : "";
|
|
30672
30985
|
let hub;
|
|
30673
30986
|
try {
|
|
30674
30987
|
hub = await Hub.open();
|
|
@@ -30688,7 +31001,7 @@ async function cmdHub(sub, args, flags) {
|
|
|
30688
31001
|
text: r.text || void 0,
|
|
30689
31002
|
target: r.target || void 0,
|
|
30690
31003
|
status: r.status === "addressed" ? "answered" : r.status === "held" ? "held" : "unanswered",
|
|
30691
|
-
page: r.title ||
|
|
31004
|
+
page: r.title || path15.basename(r.file),
|
|
30692
31005
|
file: r.file,
|
|
30693
31006
|
sent: shortDate(r.at)
|
|
30694
31007
|
})),
|
|
@@ -30812,7 +31125,7 @@ async function main() {
|
|
|
30812
31125
|
let name = null;
|
|
30813
31126
|
if (cmd !== void 0) {
|
|
30814
31127
|
if (Object.hasOwn(COMMANDS, cmd)) name = cmd;
|
|
30815
|
-
else if (/\.html?$/i.test(cmd) ||
|
|
31128
|
+
else if (/\.html?$/i.test(cmd) || fs15.existsSync(cmd)) name = "open";
|
|
30816
31129
|
else if (flags.help) return process.stdout.write(HELP);
|
|
30817
31130
|
else return usage(`unknown command: ${cmd}. One of: ${Object.keys(COMMANDS).join(" ")}, or a page file`);
|
|
30818
31131
|
}
|
|
@@ -30868,7 +31181,8 @@ async function main() {
|
|
|
30868
31181
|
return cmdOpen(rest[0], flags);
|
|
30869
31182
|
case "help":
|
|
30870
31183
|
if (!rest[0]) return process.stdout.write(HELP);
|
|
30871
|
-
if (
|
|
31184
|
+
if (rest[0] === "all") return process.stdout.write(HELP + helpAll());
|
|
31185
|
+
if (!Object.hasOwn(COMMANDS, rest[0])) return usage(`no command named ${rest[0]}. One of: all ${Object.keys(COMMANDS).join(" ")}`, "help");
|
|
30872
31186
|
return commandHelp(rest[0]);
|
|
30873
31187
|
default:
|
|
30874
31188
|
return cmdOpen(cmd, flags);
|