austere-axi 0.5.2 → 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 +96 -101
- package/dist/austere.js +126 -126
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.js +5 -5
- package/dist/cli.mjs +837 -462
- package/dist/pack-kit.mjs +23 -23
- package/dist/packs/mermaid/catalog.json +3 -3
- package/dist/packs/mermaid/pack.js +4 -4
- package/dist/packs/shadcn/catalog.json +3 -3
- package/dist/packs/shadcn/pack.js +5 -5
- package/dist/prompt.md +1 -1
- package/package.json +17 -14
- package/skills/austere/SKILL.md +2 -0
- package/types/pack-kit.d.ts +4 -3
- package/types/pack.d.ts +27 -8
package/dist/cli.mjs
CHANGED
|
@@ -492,7 +492,7 @@ var require_ci_info = __commonJS({
|
|
|
492
492
|
}
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
-
// src/cli/artifact.
|
|
495
|
+
// src/cli/artifact.ts
|
|
496
496
|
function readArtifact(html) {
|
|
497
497
|
const text = String(html);
|
|
498
498
|
const m = PROGRAM_RE.exec(text);
|
|
@@ -544,28 +544,30 @@ function rewriteRuntime(html, replacement) {
|
|
|
544
544
|
return rep + "\n" + text;
|
|
545
545
|
}
|
|
546
546
|
function escapeHtml(s) {
|
|
547
|
-
return String(s).replace(/[&<>"']/g, (c) =>
|
|
547
|
+
return String(s).replace(/[&<>"']/g, (c) => ENTITIES[c]);
|
|
548
548
|
}
|
|
549
549
|
function decodeEntities(s) {
|
|
550
550
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
551
551
|
}
|
|
552
|
-
var CDN_RUNTIME_URL, PROGRAM_RE, RUNTIME_RE, TITLE_RE;
|
|
552
|
+
var CDN_RUNTIME_URL, PROGRAM_RE, RUNTIME_RE, TITLE_RE, ENTITIES;
|
|
553
553
|
var init_artifact = __esm({
|
|
554
|
-
"src/cli/artifact.
|
|
554
|
+
"src/cli/artifact.ts"() {
|
|
555
|
+
"use strict";
|
|
555
556
|
CDN_RUNTIME_URL = "https://cdn.jsdelivr.net/npm/austere-axi@0/dist/austere.js";
|
|
556
557
|
PROGRAM_RE = /(<script\b[^>]*\btype\s*=\s*["']text\/openui(?:-lang)?["'][^>]*>)([\s\S]*?)(<\/script\s*>)/i;
|
|
557
558
|
RUNTIME_RE = /<script\b[^>]*\bsrc\s*=\s*["']([^"']*\/austere(?:\.min)?\.js(?:\?[^"']*)?|austere(?:\.min)?\.js)["'][^>]*>\s*<\/script\s*>/i;
|
|
558
559
|
TITLE_RE = /<title[^>]*>([\s\S]*?)<\/title\s*>/i;
|
|
560
|
+
ENTITIES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
559
561
|
}
|
|
560
562
|
});
|
|
561
563
|
|
|
562
|
-
// src/cli/paths.
|
|
564
|
+
// src/cli/paths.ts
|
|
563
565
|
import fs from "node:fs";
|
|
564
566
|
import os from "node:os";
|
|
565
567
|
import path from "node:path";
|
|
566
568
|
import { fileURLToPath } from "node:url";
|
|
567
569
|
function packKitPath() {
|
|
568
|
-
return BUNDLED ? path.join(HERE, "pack-kit.mjs") : path.resolve(HERE, "../pack-kit/index.
|
|
570
|
+
return BUNDLED ? path.join(HERE, "pack-kit.mjs") : path.resolve(HERE, "../pack-kit/index.ts");
|
|
569
571
|
}
|
|
570
572
|
function assetPath(name) {
|
|
571
573
|
return path.join(ASSET_DIR, name);
|
|
@@ -611,17 +613,18 @@ function versionGt(a, b) {
|
|
|
611
613
|
}
|
|
612
614
|
var HERE, BUNDLED, ASSET_DIR, VERSION, CLI_ENTRY, securedDir;
|
|
613
615
|
var init_paths = __esm({
|
|
614
|
-
"src/cli/paths.
|
|
616
|
+
"src/cli/paths.ts"() {
|
|
617
|
+
"use strict";
|
|
615
618
|
HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
616
619
|
BUNDLED = true;
|
|
617
620
|
ASSET_DIR = BUNDLED ? HERE : path.resolve(HERE, "../../dist");
|
|
618
|
-
VERSION = true ? "0.
|
|
619
|
-
CLI_ENTRY = BUNDLED ? fileURLToPath(import.meta.url) : path.resolve(HERE, "main.
|
|
621
|
+
VERSION = true ? "0.7.0" : packageVersion();
|
|
622
|
+
CLI_ENTRY = BUNDLED ? fileURLToPath(import.meta.url) : path.resolve(HERE, "main.ts");
|
|
620
623
|
securedDir = null;
|
|
621
624
|
}
|
|
622
625
|
});
|
|
623
626
|
|
|
624
|
-
// src/shared/packs-meta.
|
|
627
|
+
// src/shared/packs-meta.ts
|
|
625
628
|
function splitSpec(spec) {
|
|
626
629
|
const at2 = spec.lastIndexOf("@");
|
|
627
630
|
if (at2 <= 0) return { name: spec, version: "" };
|
|
@@ -650,18 +653,16 @@ function isValidVersion(version2) {
|
|
|
650
653
|
}
|
|
651
654
|
var PACKS_META_RE, NAME_RE, VERSION_RE;
|
|
652
655
|
var init_packs_meta = __esm({
|
|
653
|
-
"src/shared/packs-meta.
|
|
656
|
+
"src/shared/packs-meta.ts"() {
|
|
657
|
+
"use strict";
|
|
654
658
|
PACKS_META_RE = /<meta\s+name=["']austere-packs["']\s+content=["']([^"']*)["']\s*\/?>/i;
|
|
655
659
|
NAME_RE = /^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
656
660
|
VERSION_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
657
661
|
}
|
|
658
662
|
});
|
|
659
663
|
|
|
660
|
-
// src/shared/catalog.
|
|
661
|
-
function searchCatalog(entries, query, { notesOf = (
|
|
662
|
-
/** @type {(entry: any) => string[]} */
|
|
663
|
-
(() => [])
|
|
664
|
-
), limit = 8 } = {}) {
|
|
664
|
+
// src/shared/catalog.ts
|
|
665
|
+
function searchCatalog(entries, query, { notesOf = () => [], limit = 8 } = {}) {
|
|
665
666
|
const q2 = String(query || "").trim().toLowerCase();
|
|
666
667
|
const terms = words(q2);
|
|
667
668
|
if (!terms.length) return [];
|
|
@@ -688,13 +689,14 @@ function searchCatalog(entries, query, { notesOf = (
|
|
|
688
689
|
}
|
|
689
690
|
var CATALOG_FORMAT, words;
|
|
690
691
|
var init_catalog = __esm({
|
|
691
|
-
"src/shared/catalog.
|
|
692
|
+
"src/shared/catalog.ts"() {
|
|
693
|
+
"use strict";
|
|
692
694
|
CATALOG_FORMAT = 1;
|
|
693
695
|
words = (text) => String(text || "").toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 1);
|
|
694
696
|
}
|
|
695
697
|
});
|
|
696
698
|
|
|
697
|
-
// src/cli/packs.
|
|
699
|
+
// src/cli/packs.ts
|
|
698
700
|
import fs2 from "node:fs";
|
|
699
701
|
import path2 from "node:path";
|
|
700
702
|
function isFirstParty(name) {
|
|
@@ -754,7 +756,7 @@ function installedPacks() {
|
|
|
754
756
|
return Object.entries(lock.packs).map(([name, e]) => {
|
|
755
757
|
const dir = path2.join(packsDir(), packDirName(name, e.version));
|
|
756
758
|
return { name, ...e, dir, catalog: readJson(path2.join(dir, "catalog.json"), null) };
|
|
757
|
-
}).filter((p) => p.catalog).sort((a, b) => a.name.localeCompare(b.name));
|
|
759
|
+
}).filter((p) => !!p.catalog).sort((a, b) => a.name.localeCompare(b.name));
|
|
758
760
|
}
|
|
759
761
|
function findInstalled(name) {
|
|
760
762
|
return installedPacks().find((p) => p.name === name) || null;
|
|
@@ -852,7 +854,8 @@ function mergePackSchemas(schema, catalogs) {
|
|
|
852
854
|
}
|
|
853
855
|
var PACK_API, TRUSTED_SCOPE, core, bundled;
|
|
854
856
|
var init_packs = __esm({
|
|
855
|
-
"src/cli/packs.
|
|
857
|
+
"src/cli/packs.ts"() {
|
|
858
|
+
"use strict";
|
|
856
859
|
init_paths();
|
|
857
860
|
init_packs_meta();
|
|
858
861
|
init_catalog();
|
|
@@ -863,10 +866,15 @@ var init_packs = __esm({
|
|
|
863
866
|
}
|
|
864
867
|
});
|
|
865
868
|
|
|
866
|
-
// src/cli/invites.
|
|
869
|
+
// src/cli/invites.ts
|
|
867
870
|
import crypto from "node:crypto";
|
|
868
871
|
import fs4 from "node:fs";
|
|
869
872
|
import path3 from "node:path";
|
|
873
|
+
function isInvite(i) {
|
|
874
|
+
if (!i || typeof i !== "object") return false;
|
|
875
|
+
const o = i;
|
|
876
|
+
return typeof o.token === "string" && o.token.length >= 22 && Boolean(o.name) && Boolean(o.sessionId);
|
|
877
|
+
}
|
|
870
878
|
function safeEqual(a, b) {
|
|
871
879
|
const x = crypto.createHash("sha256").update(String(a)).digest();
|
|
872
880
|
const y = crypto.createHash("sha256").update(String(b)).digest();
|
|
@@ -923,18 +931,21 @@ function ownerKey(dir) {
|
|
|
923
931
|
}
|
|
924
932
|
var COOKIE, RESERVED, InviteStore, OWNER_COOKIE, LoginCodes, hashCode;
|
|
925
933
|
var init_invites = __esm({
|
|
926
|
-
"src/cli/invites.
|
|
934
|
+
"src/cli/invites.ts"() {
|
|
935
|
+
"use strict";
|
|
927
936
|
COOKIE = "austere_rv";
|
|
928
937
|
RESERVED = /* @__PURE__ */ new Set(["owner", "you", "agent", "me"]);
|
|
929
938
|
InviteStore = class {
|
|
930
|
-
|
|
939
|
+
file;
|
|
940
|
+
invites;
|
|
941
|
+
/** @param dir state dir */
|
|
931
942
|
constructor(dir) {
|
|
932
943
|
this.file = path3.join(dir, "invites.json");
|
|
933
944
|
this.invites = [];
|
|
934
945
|
try {
|
|
935
946
|
const raw = JSON.parse(fs4.readFileSync(this.file, "utf8"));
|
|
936
947
|
if (Array.isArray(raw.invites)) {
|
|
937
|
-
this.invites = raw.invites.filter(
|
|
948
|
+
this.invites = raw.invites.filter(isInvite);
|
|
938
949
|
}
|
|
939
950
|
} catch {
|
|
940
951
|
}
|
|
@@ -944,7 +955,6 @@ var init_invites = __esm({
|
|
|
944
955
|
fs4.writeFileSync(tmp, JSON.stringify({ invites: this.invites }, null, 2) + "\n", { mode: 384 });
|
|
945
956
|
fs4.renameSync(tmp, this.file);
|
|
946
957
|
}
|
|
947
|
-
/** @returns {Invite} */
|
|
948
958
|
add({ name, sessionId: sessionId2, file: file3, auto = false }) {
|
|
949
959
|
const clean = cleanName(name);
|
|
950
960
|
if (this.invites.some((i) => i.sessionId === sessionId2 && i.name.toLowerCase() === clean.toLowerCase())) {
|
|
@@ -979,7 +989,10 @@ var init_invites = __esm({
|
|
|
979
989
|
};
|
|
980
990
|
OWNER_COOKIE = "austere_owner";
|
|
981
991
|
LoginCodes = class {
|
|
982
|
-
|
|
992
|
+
ttlMs;
|
|
993
|
+
now;
|
|
994
|
+
/** sha256(code) -> expiry */
|
|
995
|
+
codes;
|
|
983
996
|
constructor(ttlMs, now = Date.now) {
|
|
984
997
|
this.ttlMs = ttlMs;
|
|
985
998
|
this.now = now;
|
|
@@ -1005,7 +1018,7 @@ var init_invites = __esm({
|
|
|
1005
1018
|
}
|
|
1006
1019
|
});
|
|
1007
1020
|
|
|
1008
|
-
// src/cli/client.
|
|
1021
|
+
// src/cli/client.ts
|
|
1009
1022
|
import { spawn } from "node:child_process";
|
|
1010
1023
|
import crypto2 from "node:crypto";
|
|
1011
1024
|
import fs5 from "node:fs";
|
|
@@ -1091,7 +1104,8 @@ async function ensureServer() {
|
|
|
1091
1104
|
throw new Error(`port ${port2} is served by an austere-axi with another state dir (${h.stateDir}); set AUSTERE_AXI_PORT`);
|
|
1092
1105
|
}
|
|
1093
1106
|
if (h && (h.legacy || versionGt(VERSION, h.version))) {
|
|
1094
|
-
const
|
|
1107
|
+
const { tailnet } = h;
|
|
1108
|
+
const keep = (h.listen || []).filter((x) => x !== "127.0.0.1" && (!tailnet || x !== tailnet.ip));
|
|
1095
1109
|
const persist = !!h.persist;
|
|
1096
1110
|
await shutdownServer(h, port2);
|
|
1097
1111
|
for (let i = 0; i < 40 && await health(port2); i++) await sleep(100);
|
|
@@ -1163,7 +1177,8 @@ async function openBrowser(url2) {
|
|
|
1163
1177
|
}
|
|
1164
1178
|
var sleep, START_TIMEOUT_MS;
|
|
1165
1179
|
var init_client = __esm({
|
|
1166
|
-
"src/cli/client.
|
|
1180
|
+
"src/cli/client.ts"() {
|
|
1181
|
+
"use strict";
|
|
1167
1182
|
init_paths();
|
|
1168
1183
|
init_invites();
|
|
1169
1184
|
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -1171,7 +1186,7 @@ var init_client = __esm({
|
|
|
1171
1186
|
}
|
|
1172
1187
|
});
|
|
1173
1188
|
|
|
1174
|
-
// src/cli/hub.
|
|
1189
|
+
// src/cli/hub.ts
|
|
1175
1190
|
import fs6 from "node:fs";
|
|
1176
1191
|
import path5 from "node:path";
|
|
1177
1192
|
function hubConfigFile() {
|
|
@@ -1194,7 +1209,7 @@ async function loadSqlite() {
|
|
|
1194
1209
|
if (DatabaseSync) return DatabaseSync;
|
|
1195
1210
|
const emit2 = process.emitWarning;
|
|
1196
1211
|
process.emitWarning = function(warning, ...rest) {
|
|
1197
|
-
if (/sqlite/i.test(String(warning && warning.message ? warning.message : warning))) return;
|
|
1212
|
+
if (/sqlite/i.test(String(typeof warning !== "string" && warning && warning.message ? warning.message : warning))) return;
|
|
1198
1213
|
return emit2.call(process, warning, ...rest);
|
|
1199
1214
|
};
|
|
1200
1215
|
try {
|
|
@@ -1252,7 +1267,8 @@ async function registerWithHub(file3, title, id) {
|
|
|
1252
1267
|
}
|
|
1253
1268
|
var DatabaseSync, SCHEMA, NOTES, Hub;
|
|
1254
1269
|
var init_hub = __esm({
|
|
1255
|
-
"src/cli/hub.
|
|
1270
|
+
"src/cli/hub.ts"() {
|
|
1271
|
+
"use strict";
|
|
1256
1272
|
init_paths();
|
|
1257
1273
|
DatabaseSync = null;
|
|
1258
1274
|
SCHEMA = `
|
|
@@ -1308,10 +1324,12 @@ CREATE TABLE IF NOT EXISTS notes (
|
|
|
1308
1324
|
CREATE INDEX IF NOT EXISTS notes_artifact ON notes(artifact_id, at);
|
|
1309
1325
|
`;
|
|
1310
1326
|
Hub = class _Hub {
|
|
1327
|
+
db;
|
|
1328
|
+
q;
|
|
1311
1329
|
static async open(file3 = hubDbFile()) {
|
|
1312
1330
|
return new _Hub(await openDatabase(file3));
|
|
1313
1331
|
}
|
|
1314
|
-
/** @param
|
|
1332
|
+
/** @param db an open hub.db (openDatabase) */
|
|
1315
1333
|
constructor(db) {
|
|
1316
1334
|
this.db = db;
|
|
1317
1335
|
this.q = {
|
|
@@ -1350,13 +1368,15 @@ CREATE INDEX IF NOT EXISTS notes_artifact ON notes(artifact_id, at);
|
|
|
1350
1368
|
WHERE (? = '' OR a.title LIKE ? ESCAPE '\\' OR a.file LIKE ? ESCAPE '\\' OR a.project LIKE ? ESCAPE '\\')
|
|
1351
1369
|
AND (? = '' OR a.project = ?)
|
|
1352
1370
|
GROUP BY a.id ORDER BY a.updated_at DESC LIMIT ?`
|
|
1353
|
-
).all(q2, like, like, like, project, project, limit).map((
|
|
1371
|
+
).all(q2, like, like, like, project, project, limit).map((row) => {
|
|
1372
|
+
const r = row;
|
|
1373
|
+
return { ...r, open: r.open || 0, received: r.received || 0, addressed: r.addressed || 0, held: r.held || 0 };
|
|
1374
|
+
});
|
|
1354
1375
|
}
|
|
1355
1376
|
/**
|
|
1356
1377
|
* Notes (annotations and messages) across all pages, newest first.
|
|
1357
1378
|
* `q` matches note text, target and page title. `status`: "unanswered" (held, open and received), "answered", or "" for all.
|
|
1358
1379
|
* `author`: a reviewer's name, "owner" for the owner's own notes, or "" for everyone. Discarded notes are left out.
|
|
1359
|
-
* @param {{ q?: string, status?: string, project?: string, author?: string, limit?: number }} [o]
|
|
1360
1380
|
*/
|
|
1361
1381
|
notes({ q: q2 = "", status = "", project = "", author = "", limit = 200 } = {}) {
|
|
1362
1382
|
const anyAuthor = author === "" ? "all" : "one";
|
|
@@ -1388,7 +1408,7 @@ CREATE INDEX IF NOT EXISTS notes_artifact ON notes(artifact_id, at);
|
|
|
1388
1408
|
}
|
|
1389
1409
|
});
|
|
1390
1410
|
|
|
1391
|
-
// src/cli/lock.
|
|
1411
|
+
// src/cli/lock.ts
|
|
1392
1412
|
import fs7 from "node:fs";
|
|
1393
1413
|
import path6 from "node:path";
|
|
1394
1414
|
async function lockStateDir(dir, { port: port2 = null, wait = 0 } = {}) {
|
|
@@ -1445,8 +1465,8 @@ async function lockHolder(text, dir) {
|
|
|
1445
1465
|
}
|
|
1446
1466
|
if (typeof lock === "number") lock = { pid: lock };
|
|
1447
1467
|
const pid = Number(lock?.pid);
|
|
1448
|
-
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return null;
|
|
1449
|
-
const port2 = Number.isInteger(lock.port) && lock.port > 0 ? lock.port : void 0;
|
|
1468
|
+
if (!lock || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return null;
|
|
1469
|
+
const port2 = typeof lock.port === "number" && Number.isInteger(lock.port) && lock.port > 0 ? lock.port : void 0;
|
|
1450
1470
|
if (port2 && await serves(port2, pid, dir)) return { pid, port: port2, serving: true };
|
|
1451
1471
|
if (!processAlive(pid)) return null;
|
|
1452
1472
|
if (lock.at == null) return { pid, port: port2, serving: false };
|
|
@@ -1466,14 +1486,15 @@ function processAlive(pid) {
|
|
|
1466
1486
|
}
|
|
1467
1487
|
var STARTING_MS, sleep2;
|
|
1468
1488
|
var init_lock = __esm({
|
|
1469
|
-
"src/cli/lock.
|
|
1489
|
+
"src/cli/lock.ts"() {
|
|
1490
|
+
"use strict";
|
|
1470
1491
|
init_client();
|
|
1471
1492
|
STARTING_MS = 6e4;
|
|
1472
1493
|
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1473
1494
|
}
|
|
1474
1495
|
});
|
|
1475
1496
|
|
|
1476
|
-
// src/cli/sessions.
|
|
1497
|
+
// src/cli/sessions.ts
|
|
1477
1498
|
import crypto3 from "node:crypto";
|
|
1478
1499
|
import fs8 from "node:fs";
|
|
1479
1500
|
import path7 from "node:path";
|
|
@@ -1486,7 +1507,7 @@ function newId(prefix) {
|
|
|
1486
1507
|
function noteStatus(e, ackedSeq) {
|
|
1487
1508
|
if (e.discarded) return "discarded";
|
|
1488
1509
|
if (e.held) return "held";
|
|
1489
|
-
if (e.seq > ackedSeq) return "open";
|
|
1510
|
+
if (Number(e.seq) > ackedSeq) return "open";
|
|
1490
1511
|
if (e.doneAt) return "addressed";
|
|
1491
1512
|
return "received";
|
|
1492
1513
|
}
|
|
@@ -1523,7 +1544,6 @@ function hydrate(row) {
|
|
|
1523
1544
|
endedAt: row.ended_at || null,
|
|
1524
1545
|
closedAt: row.closed_at || null,
|
|
1525
1546
|
ackedSeq: Number(row.acked_seq || 0),
|
|
1526
|
-
/** @type {Record<string, any>[]} */
|
|
1527
1547
|
transcript: [],
|
|
1528
1548
|
// runtime-only
|
|
1529
1549
|
deliveredSeq: Number(row.acked_seq || 0),
|
|
@@ -1534,18 +1554,22 @@ function hydrate(row) {
|
|
|
1534
1554
|
}
|
|
1535
1555
|
var counter, deliverable, SessionStore;
|
|
1536
1556
|
var init_sessions = __esm({
|
|
1537
|
-
"src/cli/sessions.
|
|
1557
|
+
"src/cli/sessions.ts"() {
|
|
1558
|
+
"use strict";
|
|
1538
1559
|
init_hub();
|
|
1539
1560
|
counter = 0;
|
|
1540
1561
|
deliverable = (e) => e.role === "user" && e.seq != null && !e.held && !e.discarded;
|
|
1541
1562
|
SessionStore = class _SessionStore {
|
|
1563
|
+
db;
|
|
1564
|
+
sessions;
|
|
1565
|
+
q;
|
|
1542
1566
|
/** Open the store in a state dir: hub.db, after importing any session files from before it. */
|
|
1543
1567
|
static async open(dir) {
|
|
1544
1568
|
return new _SessionStore(await openDatabase(path7.join(dir, "hub.db")), dir);
|
|
1545
1569
|
}
|
|
1546
1570
|
/**
|
|
1547
|
-
* @param
|
|
1548
|
-
* @param
|
|
1571
|
+
* @param db hub.db, opened with openDatabase
|
|
1572
|
+
* @param dir the state dir
|
|
1549
1573
|
*/
|
|
1550
1574
|
constructor(db, dir) {
|
|
1551
1575
|
this.db = db;
|
|
@@ -1684,7 +1708,7 @@ var init_sessions = __esm({
|
|
|
1684
1708
|
}
|
|
1685
1709
|
/** Held entries, oldest first. */
|
|
1686
1710
|
heldEntries(s) {
|
|
1687
|
-
return s.transcript.filter((e) => e.role === "user" && e.held);
|
|
1711
|
+
return s.transcript.filter((e) => e.role === "user" && Boolean(e.held));
|
|
1688
1712
|
}
|
|
1689
1713
|
/**
|
|
1690
1714
|
* The owner sends the held notes named by `ids` on to the agent. A released note gets
|
|
@@ -1740,7 +1764,7 @@ var init_sessions = __esm({
|
|
|
1740
1764
|
const now = Date.now();
|
|
1741
1765
|
this.updateNotes(s, entries.filter((e) => !e.seenAt).map((e) => [e, { seenAt: now }]));
|
|
1742
1766
|
s.lastDeliveredAt = now;
|
|
1743
|
-
s.deliveredSeq = Math.max(s.deliveredSeq, ...entries.map((e) => e.seq));
|
|
1767
|
+
s.deliveredSeq = Math.max(s.deliveredSeq, ...entries.map((e) => Number(e.seq)));
|
|
1744
1768
|
}
|
|
1745
1769
|
/**
|
|
1746
1770
|
* The agent acknowledges every note up to `cursor`. A cursor past the last note in the
|
|
@@ -1775,10 +1799,9 @@ var init_sessions = __esm({
|
|
|
1775
1799
|
* Feedback no agent has acted on (`feedback <file>`): notes not yet acknowledged,
|
|
1776
1800
|
* plus acknowledged ones with no agent reply since; `all` adds the addressed ones.
|
|
1777
1801
|
* Unless `peek`, reading acknowledges them, so a later poll does not deliver them again.
|
|
1778
|
-
* @returns {{ entries: { id: string, at: number, author?: string, items: any[], status: string }[], fresh: any[] }}
|
|
1779
1802
|
*/
|
|
1780
1803
|
feedback(s, { all = false, peek = false } = {}) {
|
|
1781
|
-
const picked = s.transcript.filter((e) => deliverable(e) && (e.items || []).length && (all || e.seq > s.ackedSeq || !e.doneAt));
|
|
1804
|
+
const picked = s.transcript.filter((e) => deliverable(e) && (e.items || []).length > 0 && (all || e.seq > s.ackedSeq || !e.doneAt));
|
|
1782
1805
|
const entries = picked.map((e) => ({
|
|
1783
1806
|
id: e.id,
|
|
1784
1807
|
at: e.at,
|
|
@@ -1845,10 +1868,10 @@ var init_sessions = __esm({
|
|
|
1845
1868
|
}
|
|
1846
1869
|
importSession(d) {
|
|
1847
1870
|
const now = Date.now();
|
|
1848
|
-
const
|
|
1871
|
+
const known2 = !!this.q.hasNotes.get(d.id);
|
|
1849
1872
|
this.q.ensure.run(d.id, d.file, projectOf(d.file), d.title || path7.basename(d.file), d.createdAt || now, d.updatedAt || now);
|
|
1850
1873
|
const s = hydrate(this.q.session.get(d.id));
|
|
1851
|
-
if (!
|
|
1874
|
+
if (!known2) {
|
|
1852
1875
|
Object.assign(s, {
|
|
1853
1876
|
title: d.title || s.title,
|
|
1854
1877
|
updatedAt: Math.max(s.updatedAt, d.updatedAt || 0),
|
|
@@ -1859,10 +1882,10 @@ var init_sessions = __esm({
|
|
|
1859
1882
|
}
|
|
1860
1883
|
const transcript = Array.isArray(d.transcript) ? d.transcript : [];
|
|
1861
1884
|
const pending = new Set(Array.isArray(d.pending) ? d.pending : []);
|
|
1862
|
-
const users = transcript.filter((e) => e && e.role === "user" && e.id && !this.q.note.get(e.id));
|
|
1885
|
+
const users = transcript.filter((e) => Boolean(e && e.role === "user" && e.id && !this.q.note.get(e.id)));
|
|
1863
1886
|
const open2 = (e) => !e.held && !e.discarded;
|
|
1864
1887
|
const delivered = users.filter((e) => open2(e) && !pending.has(e.id));
|
|
1865
|
-
const waiting = [...pending].map((id) => users.find((e) => e.id === id)).filter((e) => e && open2(e));
|
|
1888
|
+
const waiting = [...pending].map((id) => users.find((e) => e.id === id)).filter((e) => Boolean(e && open2(e)));
|
|
1866
1889
|
const rest = users.filter((e) => !open2(e));
|
|
1867
1890
|
const save = (e, seq) => {
|
|
1868
1891
|
const entry = { ...e, items: Array.isArray(e.items) ? e.items : [], seq };
|
|
@@ -1871,7 +1894,7 @@ var init_sessions = __esm({
|
|
|
1871
1894
|
};
|
|
1872
1895
|
const saved = [];
|
|
1873
1896
|
for (const e of delivered) saved.push(save(e, this.nextSeq()));
|
|
1874
|
-
if (delivered.length) s.ackedSeq = Math.max(s.ackedSeq, saved[saved.length - 1].seq);
|
|
1897
|
+
if (delivered.length) s.ackedSeq = Math.max(s.ackedSeq, Number(saved[saved.length - 1].seq));
|
|
1875
1898
|
for (const e of waiting) saved.push(save(e, this.nextSeq()));
|
|
1876
1899
|
for (const e of rest) saved.push(save(e, null));
|
|
1877
1900
|
for (const e of saved) this.indexNote(s, e);
|
|
@@ -1884,7 +1907,7 @@ var init_sessions = __esm({
|
|
|
1884
1907
|
}
|
|
1885
1908
|
});
|
|
1886
1909
|
|
|
1887
|
-
// src/cli/export.
|
|
1910
|
+
// src/cli/export.ts
|
|
1888
1911
|
import fs9 from "node:fs";
|
|
1889
1912
|
import path8 from "node:path";
|
|
1890
1913
|
function inlineScript(code) {
|
|
@@ -1911,14 +1934,15 @@ function exportHtml(html) {
|
|
|
1911
1934
|
return { html: out, notices };
|
|
1912
1935
|
}
|
|
1913
1936
|
var init_export = __esm({
|
|
1914
|
-
"src/cli/export.
|
|
1937
|
+
"src/cli/export.ts"() {
|
|
1938
|
+
"use strict";
|
|
1915
1939
|
init_artifact();
|
|
1916
1940
|
init_paths();
|
|
1917
1941
|
init_packs();
|
|
1918
1942
|
}
|
|
1919
1943
|
});
|
|
1920
1944
|
|
|
1921
|
-
// src/cli/install.
|
|
1945
|
+
// src/cli/install.ts
|
|
1922
1946
|
var install_exports = {};
|
|
1923
1947
|
__export(install_exports, {
|
|
1924
1948
|
TRUSTED_SCOPE: () => TRUSTED_SCOPE,
|
|
@@ -1929,8 +1953,8 @@ __export(install_exports, {
|
|
|
1929
1953
|
removePack: () => removePack,
|
|
1930
1954
|
untar: () => untar
|
|
1931
1955
|
});
|
|
1932
|
-
import
|
|
1933
|
-
import
|
|
1956
|
+
import fs11 from "node:fs";
|
|
1957
|
+
import path10 from "node:path";
|
|
1934
1958
|
import zlib from "node:zlib";
|
|
1935
1959
|
import crypto4 from "node:crypto";
|
|
1936
1960
|
function registryUrl() {
|
|
@@ -2011,7 +2035,8 @@ async function fetchBuffer(url2, accept) {
|
|
|
2011
2035
|
let res;
|
|
2012
2036
|
try {
|
|
2013
2037
|
res = await fetch(url2, { headers: accept ? { accept } : {}, redirect: "follow" });
|
|
2014
|
-
} catch (
|
|
2038
|
+
} catch (caught) {
|
|
2039
|
+
const err = caught;
|
|
2015
2040
|
const e = Object.assign(new Error(`could not reach ${url2} (${err.cause?.code || err.message})`), { network: true });
|
|
2016
2041
|
throw e;
|
|
2017
2042
|
}
|
|
@@ -2033,7 +2058,7 @@ async function fromRegistry(spec) {
|
|
|
2033
2058
|
try {
|
|
2034
2059
|
meta3 = JSON.parse((await fetchBuffer(metaUrl, "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8")).toString("utf8"));
|
|
2035
2060
|
} catch (err) {
|
|
2036
|
-
if (err.status === 404) throw new Error(`no package ${name} on ${registryUrl()}`);
|
|
2061
|
+
if (err instanceof Error && "status" in err && err.status === 404) throw new Error(`no package ${name} on ${registryUrl()}`);
|
|
2037
2062
|
throw err;
|
|
2038
2063
|
}
|
|
2039
2064
|
const version2 = pickVersion(meta3, range);
|
|
@@ -2056,7 +2081,7 @@ function readTarball(tgz, label) {
|
|
|
2056
2081
|
try {
|
|
2057
2082
|
files = untar(zlib.gunzipSync(tgz));
|
|
2058
2083
|
} catch (err) {
|
|
2059
|
-
throw new Error(`${label}: not a gzipped tarball (${err.message})`);
|
|
2084
|
+
throw new Error(`${label}: not a gzipped tarball (${err instanceof Error ? err.message : void 0})`);
|
|
2060
2085
|
}
|
|
2061
2086
|
const packJs = files.get("dist/pack.js");
|
|
2062
2087
|
const catalog = files.get("dist/catalog.json");
|
|
@@ -2066,15 +2091,15 @@ function readTarball(tgz, label) {
|
|
|
2066
2091
|
return { packJs, catalog, packageJson: files.get("package.json") || null };
|
|
2067
2092
|
}
|
|
2068
2093
|
function fromTarball(file3) {
|
|
2069
|
-
const abs =
|
|
2070
|
-
if (!
|
|
2071
|
-
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 };
|
|
2072
2097
|
}
|
|
2073
2098
|
function fromFolder(dir) {
|
|
2074
|
-
const abs =
|
|
2075
|
-
if (!
|
|
2076
|
-
if (
|
|
2077
|
-
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;
|
|
2078
2103
|
const packJs = read("dist/pack.js");
|
|
2079
2104
|
const catalog = read("dist/catalog.json");
|
|
2080
2105
|
if (!packJs || !catalog) throw new Error(`${abs} has no dist/pack.js and dist/catalog.json: run \`austere-axi components build ${dir}\` first`);
|
|
@@ -2114,10 +2139,10 @@ async function addPack(spec, { trust = false } = {}) {
|
|
|
2114
2139
|
}
|
|
2115
2140
|
const got = local ? fromFolder(spec) : await fromRegistry(spec);
|
|
2116
2141
|
if (got.source === "tarball" && !trust) {
|
|
2117
|
-
const peek = parseCatalog(got.catalog,
|
|
2142
|
+
const peek = parseCatalog(got.catalog, path10.basename(got.resolved));
|
|
2118
2143
|
throw Object.assign(
|
|
2119
2144
|
new Error(
|
|
2120
|
-
`${
|
|
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.`
|
|
2121
2146
|
),
|
|
2122
2147
|
{ code: "untrusted" }
|
|
2123
2148
|
);
|
|
@@ -2142,14 +2167,14 @@ async function addPack(spec, { trust = false } = {}) {
|
|
|
2142
2167
|
}
|
|
2143
2168
|
const lock = readLock();
|
|
2144
2169
|
const previous = lock.packs[cat.pack];
|
|
2145
|
-
const dir =
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
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);
|
|
2151
2176
|
if (previous && previous.version !== cat.version) {
|
|
2152
|
-
|
|
2177
|
+
fs11.rmSync(path10.join(packsDir(), packDirName(cat.pack, previous.version)), { recursive: true, force: true });
|
|
2153
2178
|
}
|
|
2154
2179
|
lock.packs[cat.pack] = {
|
|
2155
2180
|
version: cat.version,
|
|
@@ -2169,12 +2194,13 @@ function removePack(name) {
|
|
|
2169
2194
|
if (!entry) return null;
|
|
2170
2195
|
delete lock.packs[name];
|
|
2171
2196
|
writeLock(lock);
|
|
2172
|
-
|
|
2197
|
+
fs11.rmSync(path10.join(packsDir(), packDirName(name, entry.version)), { recursive: true, force: true });
|
|
2173
2198
|
return { name, version: entry.version };
|
|
2174
2199
|
}
|
|
2175
2200
|
var MAX_TARBALL, MAX_PACK_JS;
|
|
2176
2201
|
var init_install = __esm({
|
|
2177
|
-
"src/cli/install.
|
|
2202
|
+
"src/cli/install.ts"() {
|
|
2203
|
+
"use strict";
|
|
2178
2204
|
init_packs();
|
|
2179
2205
|
init_packs_meta();
|
|
2180
2206
|
init_catalog();
|
|
@@ -2183,14 +2209,14 @@ var init_install = __esm({
|
|
|
2183
2209
|
}
|
|
2184
2210
|
});
|
|
2185
2211
|
|
|
2186
|
-
// src/cli/pack-dev.
|
|
2212
|
+
// src/cli/pack-dev.ts
|
|
2187
2213
|
var pack_dev_exports = {};
|
|
2188
2214
|
__export(pack_dev_exports, {
|
|
2189
2215
|
buildPackDir: () => buildPackDir,
|
|
2190
2216
|
newPack: () => newPack
|
|
2191
2217
|
});
|
|
2192
|
-
import
|
|
2193
|
-
import
|
|
2218
|
+
import fs12 from "node:fs";
|
|
2219
|
+
import path11 from "node:path";
|
|
2194
2220
|
import { createRequire } from "node:module";
|
|
2195
2221
|
import { pathToFileURL } from "node:url";
|
|
2196
2222
|
function packageJson(name) {
|
|
@@ -2227,9 +2253,9 @@ Publish with \`npm publish\` after \`npm run build\`. Others install it with
|
|
|
2227
2253
|
}
|
|
2228
2254
|
function newPack(dir, { name } = {}) {
|
|
2229
2255
|
if (!dir) throw new Error("new needs a folder: austere-axi components new austere-pack-milestones");
|
|
2230
|
-
const abs =
|
|
2231
|
-
if (
|
|
2232
|
-
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, "-");
|
|
2233
2259
|
if (!isValidPackName(pkgName)) throw new Error(`${JSON.stringify(pkgName)} is not an npm package name; pass --name`);
|
|
2234
2260
|
const files = {
|
|
2235
2261
|
"package.json": JSON.stringify(packageJson(pkgName), null, 2) + "\n",
|
|
@@ -2238,24 +2264,25 @@ function newPack(dir, { name } = {}) {
|
|
|
2238
2264
|
...TEMPLATE
|
|
2239
2265
|
};
|
|
2240
2266
|
for (const [rel, text] of Object.entries(files)) {
|
|
2241
|
-
|
|
2242
|
-
|
|
2267
|
+
fs12.mkdirSync(path11.dirname(path11.join(abs, rel)), { recursive: true });
|
|
2268
|
+
fs12.writeFileSync(path11.join(abs, rel), text);
|
|
2243
2269
|
}
|
|
2244
2270
|
return { dir: abs, name: pkgName, files: Object.keys(files) };
|
|
2245
2271
|
}
|
|
2246
2272
|
async function findEsbuild(dir) {
|
|
2247
|
-
for (const base of [
|
|
2273
|
+
for (const base of [path11.join(dir, "package.json"), path11.join(process.cwd(), "package.json"), import.meta.url]) {
|
|
2248
2274
|
try {
|
|
2249
2275
|
const resolved = createRequire(base).resolve("esbuild");
|
|
2250
|
-
|
|
2276
|
+
const esbuild = await import(pathToFileURL(resolved).href);
|
|
2277
|
+
return esbuild;
|
|
2251
2278
|
} catch {
|
|
2252
2279
|
}
|
|
2253
2280
|
}
|
|
2254
2281
|
return null;
|
|
2255
2282
|
}
|
|
2256
2283
|
async function buildPackDir(dir = ".") {
|
|
2257
|
-
const abs =
|
|
2258
|
-
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)`);
|
|
2259
2286
|
const esbuild = await findEsbuild(abs);
|
|
2260
2287
|
if (!esbuild) throw new Error(`components build needs esbuild: run \`npm install -D esbuild\` in ${abs}`);
|
|
2261
2288
|
process.env.OPENUI_TELEMETRY_DISABLED ||= "1";
|
|
@@ -2276,7 +2303,8 @@ async function buildPackDir(dir = ".") {
|
|
|
2276
2303
|
}
|
|
2277
2304
|
var TEMPLATE;
|
|
2278
2305
|
var init_pack_dev = __esm({
|
|
2279
|
-
"src/cli/pack-dev.
|
|
2306
|
+
"src/cli/pack-dev.ts"() {
|
|
2307
|
+
"use strict";
|
|
2280
2308
|
init_paths();
|
|
2281
2309
|
init_packs();
|
|
2282
2310
|
init_packs_meta();
|
|
@@ -2376,9 +2404,9 @@ export default austereComponent({
|
|
|
2376
2404
|
}
|
|
2377
2405
|
});
|
|
2378
2406
|
|
|
2379
|
-
// src/cli/net.
|
|
2407
|
+
// src/cli/net.ts
|
|
2380
2408
|
import { execFile } from "node:child_process";
|
|
2381
|
-
import
|
|
2409
|
+
import fs13 from "node:fs";
|
|
2382
2410
|
function run(bin, args) {
|
|
2383
2411
|
return new Promise((resolve) => {
|
|
2384
2412
|
execFile(bin, args, { timeout: 2500, windowsHide: true }, (err, stdout) => resolve(err ? null : stdout));
|
|
@@ -2389,7 +2417,7 @@ async function detectTailscale({ fresh = false } = {}) {
|
|
|
2389
2417
|
if (!fresh && Date.now() - cache.at < 3e4) return cache.value;
|
|
2390
2418
|
let value = null;
|
|
2391
2419
|
for (const bin of CANDIDATES) {
|
|
2392
|
-
if (bin.startsWith("/") && !
|
|
2420
|
+
if (bin.startsWith("/") && !fs13.existsSync(bin)) continue;
|
|
2393
2421
|
const out = await run(bin, ["status", "--json"]);
|
|
2394
2422
|
if (!out) continue;
|
|
2395
2423
|
try {
|
|
@@ -2436,7 +2464,8 @@ function isLoopback(addr) {
|
|
|
2436
2464
|
}
|
|
2437
2465
|
var CANDIDATES, cache;
|
|
2438
2466
|
var init_net = __esm({
|
|
2439
|
-
"src/cli/net.
|
|
2467
|
+
"src/cli/net.ts"() {
|
|
2468
|
+
"use strict";
|
|
2440
2469
|
CANDIDATES = [
|
|
2441
2470
|
"tailscale",
|
|
2442
2471
|
"/usr/bin/tailscale",
|
|
@@ -2448,13 +2477,14 @@ var init_net = __esm({
|
|
|
2448
2477
|
}
|
|
2449
2478
|
});
|
|
2450
2479
|
|
|
2451
|
-
// src/cli/items.
|
|
2480
|
+
// src/cli/items.ts
|
|
2452
2481
|
function defined(o) {
|
|
2453
|
-
|
|
2482
|
+
const out = Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
2483
|
+
return out;
|
|
2454
2484
|
}
|
|
2455
2485
|
function detailOf(d) {
|
|
2456
|
-
if (!d
|
|
2457
|
-
const out = defined({ part: str2(d.part), row: str2(d.row), column: str2(d.column), rowIndex: Number.isInteger(d.rowIndex) ? d.rowIndex : void 0 });
|
|
2486
|
+
if (!isRecord(d)) return void 0;
|
|
2487
|
+
const out = defined({ part: str2(d.part), row: str2(d.row), column: str2(d.column), rowIndex: typeof d.rowIndex === "number" && Number.isInteger(d.rowIndex) ? d.rowIndex : void 0 });
|
|
2458
2488
|
return Object.keys(out).length ? out : void 0;
|
|
2459
2489
|
}
|
|
2460
2490
|
function reviewerItem(x) {
|
|
@@ -2470,20 +2500,22 @@ function reviewerItem(x) {
|
|
|
2470
2500
|
detail: detailOf(x.detail)
|
|
2471
2501
|
});
|
|
2472
2502
|
}
|
|
2473
|
-
var INVISIBLE, str2, ID, COMPONENT, match;
|
|
2503
|
+
var INVISIBLE, str2, ID, COMPONENT, match, isRecord;
|
|
2474
2504
|
var init_items = __esm({
|
|
2475
|
-
"src/cli/items.
|
|
2505
|
+
"src/cli/items.ts"() {
|
|
2506
|
+
"use strict";
|
|
2476
2507
|
INVISIBLE = /[\u{E0000}-\u{E007F}\u200B\u200E\u200F\u202A-\u202E\u2066-\u2069]/gu;
|
|
2477
2508
|
str2 = (v) => typeof v === "string" ? v.replace(INVISIBLE, "") : void 0;
|
|
2478
2509
|
ID = /^[A-Za-z_$][\w$]{0,99}$/;
|
|
2479
2510
|
COMPONENT = /^[A-Za-z_][\w.-]{0,99}$/;
|
|
2480
2511
|
match = (re2, v) => typeof v === "string" && re2.test(v) ? v : void 0;
|
|
2512
|
+
isRecord = (v) => !!v && typeof v === "object";
|
|
2481
2513
|
}
|
|
2482
2514
|
});
|
|
2483
2515
|
|
|
2484
|
-
// src/cli/hub-page.
|
|
2485
|
-
import
|
|
2486
|
-
import
|
|
2516
|
+
// src/cli/hub-page.ts
|
|
2517
|
+
import os3 from "node:os";
|
|
2518
|
+
import path12 from "node:path";
|
|
2487
2519
|
function day(ms) {
|
|
2488
2520
|
const d = new Date(ms);
|
|
2489
2521
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -2497,8 +2529,8 @@ function time3(ms) {
|
|
|
2497
2529
|
return new Date(ms).toLocaleTimeString("en", { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2498
2530
|
}
|
|
2499
2531
|
function tilde(p) {
|
|
2500
|
-
const home =
|
|
2501
|
-
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;
|
|
2502
2534
|
}
|
|
2503
2535
|
function counts(a) {
|
|
2504
2536
|
const waiting = a.open + a.received;
|
|
@@ -2519,26 +2551,28 @@ function state(a) {
|
|
|
2519
2551
|
function hubPage(rows, { version: version2, q: q2, project, stats }) {
|
|
2520
2552
|
const byProject = /* @__PURE__ */ new Map();
|
|
2521
2553
|
for (const r of rows) {
|
|
2522
|
-
|
|
2523
|
-
byProject.
|
|
2554
|
+
let list = byProject.get(r.project);
|
|
2555
|
+
if (!list) byProject.set(r.project, list = []);
|
|
2556
|
+
list.push(r);
|
|
2524
2557
|
}
|
|
2525
2558
|
const sections = [...byProject.entries()].map(([proj, list]) => {
|
|
2526
2559
|
const days = /* @__PURE__ */ new Map();
|
|
2527
2560
|
for (const r of list) {
|
|
2528
2561
|
const k = day(r.updated_at);
|
|
2529
|
-
|
|
2530
|
-
days.
|
|
2562
|
+
let items = days.get(k);
|
|
2563
|
+
if (!items) days.set(k, items = []);
|
|
2564
|
+
items.push(r);
|
|
2531
2565
|
}
|
|
2532
2566
|
const body = [...days.entries()].map(
|
|
2533
2567
|
([d, items]) => `<h3>${escapeHtml(d)}</h3><ul>${items.map(
|
|
2534
2568
|
(a) => `<li><a href="/hub/open/${a.id}" title="${escapeHtml(a.file)}">
|
|
2535
|
-
<strong>${escapeHtml(a.title ||
|
|
2536
|
-
<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>
|
|
2537
2571
|
</a></li>`
|
|
2538
2572
|
).join("")}</ul>`
|
|
2539
2573
|
).join("");
|
|
2540
2574
|
const unanswered = list.reduce((n, a) => n + a.open + a.received, 0);
|
|
2541
|
-
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(
|
|
2542
2576
|
shortPath(proj)
|
|
2543
2577
|
)}</small>${unanswered ? ` <b class="open">${unanswered} unanswered</b>` : ""}</h2>${body}</section>`;
|
|
2544
2578
|
}).join("");
|
|
@@ -2561,7 +2595,7 @@ function notesPage(rows, { version: version2, q: q2, project, status, author = "
|
|
|
2561
2595
|
const when = `${day(n.at)} ${time3(n.at)}`;
|
|
2562
2596
|
return `<li class="n"><a href="/hub/open/${escapeHtml(n.artifact_id)}" title="${escapeHtml(n.file)}">
|
|
2563
2597
|
<p class="note">${escapeHtml(n.text || "(no text)")}</p>
|
|
2564
|
-
<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>
|
|
2565
2599
|
</a></li>`;
|
|
2566
2600
|
}).join("");
|
|
2567
2601
|
const opt = (v, label) => `<option value="${v}"${status === v ? " selected" : ""}>${label}</option>`;
|
|
@@ -2580,7 +2614,8 @@ ${filter}${items ? `<ul>${items}</ul>` : `<p class="empty">${empty}</p>`}
|
|
|
2580
2614
|
}
|
|
2581
2615
|
var STYLE, STATUS_LABEL;
|
|
2582
2616
|
var init_hub_page = __esm({
|
|
2583
|
-
"src/cli/hub-page.
|
|
2617
|
+
"src/cli/hub-page.ts"() {
|
|
2618
|
+
"use strict";
|
|
2584
2619
|
init_artifact();
|
|
2585
2620
|
STYLE = `body{margin:0;background:#0b0b0d;color:#e8e8ec;font:14px/1.5 ui-sans-serif,system-ui,-apple-system,sans-serif}
|
|
2586
2621
|
main{max-width:860px;margin:0 auto;padding:32px 16px 64px}
|
|
@@ -2609,25 +2644,30 @@ b.by{color:#e8e8ec}
|
|
|
2609
2644
|
}
|
|
2610
2645
|
});
|
|
2611
2646
|
|
|
2612
|
-
// src/cli/server.
|
|
2647
|
+
// src/cli/server.ts
|
|
2613
2648
|
var server_exports = {};
|
|
2614
2649
|
__export(server_exports, {
|
|
2615
2650
|
persistEnv: () => persistEnv,
|
|
2616
2651
|
runServer: () => runServer
|
|
2617
2652
|
});
|
|
2618
|
-
import
|
|
2619
|
-
import
|
|
2620
|
-
import
|
|
2653
|
+
import fs14 from "node:fs";
|
|
2654
|
+
import http3 from "node:http";
|
|
2655
|
+
import path13 from "node:path";
|
|
2621
2656
|
import crypto5 from "node:crypto";
|
|
2622
2657
|
function persistEnv() {
|
|
2623
2658
|
return /^(1|true|yes)$/i.test(process.env.AUSTERE_AXI_PERSIST || "");
|
|
2624
2659
|
}
|
|
2625
|
-
async function runServer({
|
|
2660
|
+
async function runServer({
|
|
2661
|
+
port: port2,
|
|
2662
|
+
verbose = false,
|
|
2663
|
+
alsoListen = [],
|
|
2664
|
+
persist = false
|
|
2665
|
+
}) {
|
|
2626
2666
|
const log = (...a) => {
|
|
2627
2667
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${a.join(" ")}`;
|
|
2628
2668
|
if (verbose) process.stderr.write(line + "\n");
|
|
2629
2669
|
try {
|
|
2630
|
-
|
|
2670
|
+
fs14.appendFileSync(path13.join(stateDir(), "server.log"), line + "\n", { mode: 384 });
|
|
2631
2671
|
} catch {
|
|
2632
2672
|
}
|
|
2633
2673
|
};
|
|
@@ -2652,6 +2692,7 @@ async function runServer({ port: port2, verbose = false, alsoListen = [], persis
|
|
|
2652
2692
|
const graceTimers = /* @__PURE__ */ new Map();
|
|
2653
2693
|
const byeAt = /* @__PURE__ */ new Map();
|
|
2654
2694
|
const browserSeen = /* @__PURE__ */ new Set();
|
|
2695
|
+
const renderReported = /* @__PURE__ */ new Map();
|
|
2655
2696
|
let tailnet = null;
|
|
2656
2697
|
const linkBase = linkUrl();
|
|
2657
2698
|
let lastActivity = Date.now();
|
|
@@ -2808,7 +2849,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
2808
2849
|
}
|
|
2809
2850
|
function readFileSafe(file3) {
|
|
2810
2851
|
try {
|
|
2811
|
-
return
|
|
2852
|
+
return fs14.readFileSync(file3, "utf8");
|
|
2812
2853
|
} catch {
|
|
2813
2854
|
return null;
|
|
2814
2855
|
}
|
|
@@ -2817,17 +2858,17 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
2817
2858
|
if (watchers.has(s.id)) return;
|
|
2818
2859
|
const html = readFileSafe(s.file);
|
|
2819
2860
|
const art = html == null ? null : readArtifact(html);
|
|
2820
|
-
const w = { html, program: art?.program ?? null, shell: art?.shell ?? null, timer:
|
|
2861
|
+
const w = { html, program: art?.program ?? null, shell: art?.shell ?? null, timer: void 0 };
|
|
2821
2862
|
watchers.set(s.id, w);
|
|
2822
2863
|
if (art?.title) s.title = art.title;
|
|
2823
|
-
|
|
2864
|
+
fs14.watchFile(s.file, { interval: 300 }, () => {
|
|
2824
2865
|
clearTimeout(w.timer);
|
|
2825
2866
|
w.timer = setTimeout(() => onFileChange(s, w), 120);
|
|
2826
2867
|
});
|
|
2827
2868
|
}
|
|
2828
2869
|
function unwatch(id) {
|
|
2829
2870
|
const s = store.get(id);
|
|
2830
|
-
if (s)
|
|
2871
|
+
if (s) fs14.unwatchFile(s.file);
|
|
2831
2872
|
watchers.delete(id);
|
|
2832
2873
|
}
|
|
2833
2874
|
function onFileChange(s, w) {
|
|
@@ -2841,7 +2882,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
2841
2882
|
s.revision += 1;
|
|
2842
2883
|
if (art.title) s.title = art.title;
|
|
2843
2884
|
log("change", s.id, `rev=${s.revision}`, shellChanged ? "shell" : "program");
|
|
2844
|
-
const touched = s.transcript.filter((e) => e.role === "user" && e.seenAt && !e.doneAt && !e.workAt);
|
|
2885
|
+
const touched = s.transcript.filter((e) => e.role === "user" && Boolean(e.seenAt) && !e.doneAt && !e.workAt);
|
|
2845
2886
|
if (touched.length) {
|
|
2846
2887
|
store.markWorking(s, touched);
|
|
2847
2888
|
broadcastTranscript(s, touched);
|
|
@@ -3004,7 +3045,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3004
3045
|
const file3 = typeof body.file === "string" ? body.file : "";
|
|
3005
3046
|
if (!file3) return send(res, 400, { error: "file is required" });
|
|
3006
3047
|
if (route === "share") {
|
|
3007
|
-
if (!
|
|
3048
|
+
if (!fs14.existsSync(file3)) return send(res, 404, { error: `no such file: ${file3}` });
|
|
3008
3049
|
await ensureTailnetListener();
|
|
3009
3050
|
if (!remoteBase()) {
|
|
3010
3051
|
return send(res, 409, {
|
|
@@ -3024,7 +3065,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3024
3065
|
return send(res, 200, { id: inviteId(inv), name: inv.name, auto: inv.auto, link: inviteLink(inv), file: inv.file });
|
|
3025
3066
|
}
|
|
3026
3067
|
if (route === "open") {
|
|
3027
|
-
if (!
|
|
3068
|
+
if (!fs14.existsSync(file3)) return send(res, 404, { error: `no such file: ${file3}` });
|
|
3028
3069
|
await ensureTailnetListener();
|
|
3029
3070
|
const s2 = store.ensure(file3);
|
|
3030
3071
|
if (s2.closedAt && !body.reopen) {
|
|
@@ -3157,7 +3198,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3157
3198
|
}
|
|
3158
3199
|
const file3 = installedPackFile(spec.name, spec.version);
|
|
3159
3200
|
if (!file3) return send(res, 404, `pack ${spec.name}@${spec.version} is not installed (austere-axi components add ${spec.name}@${spec.version})`);
|
|
3160
|
-
|
|
3201
|
+
fs14.readFile(file3, (err, buf) => {
|
|
3161
3202
|
if (err) return send(res, 404, "not found");
|
|
3162
3203
|
const actual = `sha384-${crypto5.createHash("sha384").update(buf).digest("base64")}`;
|
|
3163
3204
|
const locked = readLock().packs[spec.name]?.integrity;
|
|
@@ -3181,13 +3222,13 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3181
3222
|
}
|
|
3182
3223
|
function serveAsset(res, rel, url2) {
|
|
3183
3224
|
if (rel.startsWith("installed-packs/")) return serveInstalledPack(res, rel, url2);
|
|
3184
|
-
const clean =
|
|
3185
|
-
const file3 =
|
|
3186
|
-
if (!file3.startsWith(ASSET_DIR +
|
|
3187
|
-
|
|
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) => {
|
|
3188
3229
|
if (err || !st2.isFile()) return send(res, 404, "not found");
|
|
3189
3230
|
streamFile(res, file3, {
|
|
3190
|
-
"content-type": MIME[
|
|
3231
|
+
"content-type": MIME[path13.extname(file3)] || "application/octet-stream",
|
|
3191
3232
|
"content-length": st2.size,
|
|
3192
3233
|
"cache-control": "no-cache",
|
|
3193
3234
|
"x-content-type-options": "nosniff"
|
|
@@ -3196,7 +3237,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3196
3237
|
}
|
|
3197
3238
|
const ARTIFACT_CSP = "sandbox allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms";
|
|
3198
3239
|
function streamFile(res, file3, headers) {
|
|
3199
|
-
const rs =
|
|
3240
|
+
const rs = fs14.createReadStream(file3);
|
|
3200
3241
|
rs.once("open", () => {
|
|
3201
3242
|
res.writeHead(200, headers);
|
|
3202
3243
|
rs.pipe(res);
|
|
@@ -3209,8 +3250,8 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3209
3250
|
res.once("close", () => rs.destroy());
|
|
3210
3251
|
}
|
|
3211
3252
|
function serveArtifact(req, res, s, rest) {
|
|
3212
|
-
const dir =
|
|
3213
|
-
const base =
|
|
3253
|
+
const dir = path13.dirname(s.file);
|
|
3254
|
+
const base = path13.basename(s.file);
|
|
3214
3255
|
if (rest === base || rest === "") {
|
|
3215
3256
|
const html = readFileSafe(s.file);
|
|
3216
3257
|
if (html == null) return send(res, 404, "artifact file is missing");
|
|
@@ -3224,19 +3265,19 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3224
3265
|
return send(res, 400, "bad path");
|
|
3225
3266
|
}
|
|
3226
3267
|
if (decoded.includes("\0")) return send(res, 404, "not found");
|
|
3227
|
-
const file3 =
|
|
3228
|
-
if (!file3.startsWith(dir +
|
|
3268
|
+
const file3 = path13.resolve(dir, decoded);
|
|
3269
|
+
if (!file3.startsWith(dir + path13.sep) || decoded.split(/[/\\]/).some((p) => p.startsWith("."))) {
|
|
3229
3270
|
return send(res, 404, "not found");
|
|
3230
3271
|
}
|
|
3231
|
-
|
|
3272
|
+
fs14.realpath(dir, (e1, realDir) => {
|
|
3232
3273
|
if (e1) return send(res, 404, "not found");
|
|
3233
|
-
|
|
3234
|
-
if (e2 || !real.startsWith(realDir +
|
|
3235
|
-
if (
|
|
3236
|
-
|
|
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) => {
|
|
3237
3278
|
if (err || !st2.isFile() || st2.size > 64 * 1024 * 1024) return send(res, 404, "not found");
|
|
3238
3279
|
streamFile(res, real, {
|
|
3239
|
-
"content-type": MIME[
|
|
3280
|
+
"content-type": MIME[path13.extname(real).toLowerCase()] || "application/octet-stream",
|
|
3240
3281
|
"content-length": st2.size,
|
|
3241
3282
|
"cache-control": "no-cache",
|
|
3242
3283
|
"x-content-type-options": "nosniff",
|
|
@@ -3252,8 +3293,8 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3252
3293
|
title: s.title,
|
|
3253
3294
|
file: viewer.role === "owner" ? s.file : "",
|
|
3254
3295
|
viewer: { role: viewer.role, name: viewer.name, auto: viewer.role === "reviewer" ? viewer.auto : void 0 },
|
|
3255
|
-
name:
|
|
3256
|
-
artifactUrl: `/s/${s.id}/a/${encodeURIComponent(
|
|
3296
|
+
name: path13.basename(s.file),
|
|
3297
|
+
artifactUrl: `/s/${s.id}/a/${encodeURIComponent(path13.basename(s.file))}`,
|
|
3257
3298
|
version: VERSION
|
|
3258
3299
|
};
|
|
3259
3300
|
return `<!doctype html>
|
|
@@ -3287,7 +3328,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3287
3328
|
function hubOpen(res, id) {
|
|
3288
3329
|
const a = /^[a-f0-9]{12}$/.test(id) ? hub.artifact(id) : null;
|
|
3289
3330
|
if (!a) return send(res, 404, "not in the hub");
|
|
3290
|
-
if (!
|
|
3331
|
+
if (!fs14.existsSync(a.file)) return send(res, 410, `${a.file} no longer exists`);
|
|
3291
3332
|
const existed = store.byFile(a.file);
|
|
3292
3333
|
const s = store.ensure(a.file);
|
|
3293
3334
|
if (!existed) store.touch(s);
|
|
@@ -3303,7 +3344,7 @@ data: ${JSON.stringify({ state: "revoked" })}
|
|
|
3303
3344
|
<body><main><h1>austere-axi <small style="color:#777;font-weight:400">${VERSION}</small></h1><ul>${rows || "<li>No sessions yet.</li>"}</ul></main></body></html>`;
|
|
3304
3345
|
}
|
|
3305
3346
|
async function handle(req, res) {
|
|
3306
|
-
const url2 = new URL(req.url, "http://x");
|
|
3347
|
+
const url2 = new URL(String(req.url), "http://x");
|
|
3307
3348
|
const host = hostOnly(req.headers.host);
|
|
3308
3349
|
if (!hostsAllowed().has(host) && process.env.AUSTERE_AXI_ALLOWED_HOSTS !== "*") {
|
|
3309
3350
|
return send(res, 403, "host not allowed");
|
|
@@ -3420,8 +3461,9 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3420
3461
|
|
|
3421
3462
|
`);
|
|
3422
3463
|
const client = { res, viewer: who };
|
|
3423
|
-
|
|
3424
|
-
clients.
|
|
3464
|
+
let set2 = clients.get(s.id);
|
|
3465
|
+
if (!set2) clients.set(s.id, set2 = /* @__PURE__ */ new Set());
|
|
3466
|
+
set2.add(client);
|
|
3425
3467
|
browserSeen.add(s.id);
|
|
3426
3468
|
clearTimeout(graceTimers.get(s.id));
|
|
3427
3469
|
graceTimers.delete(s.id);
|
|
@@ -3462,6 +3504,30 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3462
3504
|
broadcastPresence(s);
|
|
3463
3505
|
return send(res, 200, { ok: true, entry });
|
|
3464
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
|
+
}
|
|
3465
3531
|
if ((sub === "/release" || sub === "/discard") && req.method === "POST") {
|
|
3466
3532
|
const body = await readBody(req);
|
|
3467
3533
|
const ids = Array.isArray(body.ids) ? body.ids.map(String) : sub === "/discard" && body.all ? null : [];
|
|
@@ -3502,7 +3568,7 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3502
3568
|
if (html == null) return send(res, 404, "artifact file is missing");
|
|
3503
3569
|
try {
|
|
3504
3570
|
const out = exportHtml(html);
|
|
3505
|
-
const name =
|
|
3571
|
+
const name = path13.basename(s.file).replace(/\.html?$/i, "") + ".export.html";
|
|
3506
3572
|
return send(res, 200, out.html, {
|
|
3507
3573
|
"content-type": "text/html; charset=utf-8",
|
|
3508
3574
|
"content-disposition": `attachment; filename="${name.replace(/"/g, "")}"`,
|
|
@@ -3517,9 +3583,9 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3517
3583
|
function listen(host) {
|
|
3518
3584
|
return new Promise((resolve) => {
|
|
3519
3585
|
if (listeners.has(host)) return resolve(true);
|
|
3520
|
-
const server =
|
|
3586
|
+
const server = http3.createServer((req, res) => {
|
|
3521
3587
|
handle(req, res).catch((err) => {
|
|
3522
|
-
log("error", req.method, req.url.split("?")[0], String(err?.stack || err));
|
|
3588
|
+
log("error", req.method, String(req.url).split("?")[0], String(err?.stack || err));
|
|
3523
3589
|
if (!res.headersSent) send(res, err.status || 500, { error: err.status ? String(err.message) : "internal error" });
|
|
3524
3590
|
else res.end();
|
|
3525
3591
|
});
|
|
@@ -3528,12 +3594,7 @@ data: ${JSON.stringify(snapshot)}
|
|
|
3528
3594
|
server.requestTimeout = 0;
|
|
3529
3595
|
server.headersTimeout = 6e4;
|
|
3530
3596
|
server.once("error", (err) => {
|
|
3531
|
-
log(
|
|
3532
|
-
"listen-failed",
|
|
3533
|
-
host,
|
|
3534
|
-
/** @type {NodeJS.ErrnoException} */
|
|
3535
|
-
err.code || err.message
|
|
3536
|
-
);
|
|
3597
|
+
log("listen-failed", host, err.code || err.message);
|
|
3537
3598
|
resolve(false);
|
|
3538
3599
|
});
|
|
3539
3600
|
server.listen(port2, host, () => {
|
|
@@ -3599,9 +3660,10 @@ data: ${JSON.stringify({ state: "stopped", reason })}
|
|
|
3599
3660
|
log("started", `v${VERSION}`, `pid=${process.pid}`);
|
|
3600
3661
|
return { port: port2, shutdown };
|
|
3601
3662
|
}
|
|
3602
|
-
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;
|
|
3603
3664
|
var init_server = __esm({
|
|
3604
|
-
"src/cli/server.
|
|
3665
|
+
"src/cli/server.ts"() {
|
|
3666
|
+
"use strict";
|
|
3605
3667
|
init_artifact();
|
|
3606
3668
|
init_export();
|
|
3607
3669
|
init_net();
|
|
@@ -3644,6 +3706,8 @@ var init_server = __esm({
|
|
|
3644
3706
|
LOGIN_TTL_MS = 6e4;
|
|
3645
3707
|
OWNER = Object.freeze({ role: "owner", name: "" });
|
|
3646
3708
|
REVIEWER_KINDS = /* @__PURE__ */ new Set(["annotation", "message"]);
|
|
3709
|
+
RENDER_CHECK_AUTHOR = "Render check";
|
|
3710
|
+
MAX_RENDER_ERRORS = 20;
|
|
3647
3711
|
PAGE = 200;
|
|
3648
3712
|
SSE_MAX_BUFFER = 1 << 20;
|
|
3649
3713
|
SSE_STALL_MS = 5e3;
|
|
@@ -3654,17 +3718,17 @@ var init_server = __esm({
|
|
|
3654
3718
|
}
|
|
3655
3719
|
});
|
|
3656
3720
|
|
|
3657
|
-
// src/cli/mcp.
|
|
3721
|
+
// src/cli/mcp.ts
|
|
3658
3722
|
var mcp_exports = {};
|
|
3659
3723
|
__export(mcp_exports, {
|
|
3660
3724
|
runMcp: () => runMcp
|
|
3661
3725
|
});
|
|
3662
|
-
import { spawn as
|
|
3663
|
-
import
|
|
3726
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3727
|
+
import path14 from "node:path";
|
|
3664
3728
|
import readline from "node:readline";
|
|
3665
3729
|
function runCli({ args, stdin }, signal) {
|
|
3666
3730
|
return new Promise((resolve) => {
|
|
3667
|
-
const child =
|
|
3731
|
+
const child = spawn3(process.execPath, [CLI_ENTRY, ...args], {
|
|
3668
3732
|
env: process.env,
|
|
3669
3733
|
stdio: ["pipe", "pipe", "ignore"],
|
|
3670
3734
|
windowsHide: true
|
|
@@ -3687,12 +3751,13 @@ error: ${err.message}
|
|
|
3687
3751
|
}
|
|
3688
3752
|
function validate2(tool, a) {
|
|
3689
3753
|
for (const key2 of tool.required || []) {
|
|
3690
|
-
|
|
3754
|
+
const v = a[key2];
|
|
3755
|
+
if (typeof v !== "string" || !v.trim()) return `${key2} is required`;
|
|
3691
3756
|
}
|
|
3692
3757
|
for (const [key2, v] of Object.entries(a)) {
|
|
3693
3758
|
const p = tool.properties[key2];
|
|
3694
3759
|
if (!p) return `unknown argument: ${key2}`;
|
|
3695
|
-
const ok = p.type === "array" ? Array.isArray(v) && v.every((x) => typeof x === "string") : p.type === "integer" ? Number.isInteger(v) && v >= 0 : typeof v === p.type;
|
|
3760
|
+
const ok = p.type === "array" ? Array.isArray(v) && v.every((x) => typeof x === "string") : p.type === "integer" ? typeof v === "number" && Number.isInteger(v) && v >= 0 : typeof v === p.type;
|
|
3696
3761
|
if (!ok) return `${key2} must be ${p.type === "integer" ? "a non-negative integer" : `a ${p.type}`}`;
|
|
3697
3762
|
}
|
|
3698
3763
|
return null;
|
|
@@ -3702,10 +3767,11 @@ async function callTool(name, a, signal) {
|
|
|
3702
3767
|
const bad = validate2(tool, a);
|
|
3703
3768
|
if (bad) return { content: [{ type: "text", text: `status: error
|
|
3704
3769
|
error: ${bad}` }], isError: true };
|
|
3705
|
-
if (typeof a.file === "string") a = { ...a, file:
|
|
3770
|
+
if (typeof a.file === "string") a = { ...a, file: path14.resolve(a.file) };
|
|
3771
|
+
const args = a;
|
|
3706
3772
|
let text = "";
|
|
3707
3773
|
let isError = false;
|
|
3708
|
-
for (const run2 of tool.run(
|
|
3774
|
+
for (const run2 of tool.run(args)) {
|
|
3709
3775
|
const r = await runCli(run2, signal);
|
|
3710
3776
|
text += r.text;
|
|
3711
3777
|
if (r.code !== 0) {
|
|
@@ -3731,7 +3797,7 @@ function runMcp() {
|
|
|
3731
3797
|
async function handle(msg) {
|
|
3732
3798
|
const { id, method, params = {} } = msg;
|
|
3733
3799
|
if (id === void 0 || id === null) {
|
|
3734
|
-
if (method === "notifications/cancelled") inflight.get(params.requestId)?.abort();
|
|
3800
|
+
if (method === "notifications/cancelled" && params.requestId !== void 0) inflight.get(params.requestId)?.abort();
|
|
3735
3801
|
return;
|
|
3736
3802
|
}
|
|
3737
3803
|
switch (method) {
|
|
@@ -3740,7 +3806,7 @@ function runMcp() {
|
|
|
3740
3806
|
return send({
|
|
3741
3807
|
id,
|
|
3742
3808
|
result: {
|
|
3743
|
-
protocolVersion: PROTOCOLS.includes(asked) ? asked : PROTOCOLS[0],
|
|
3809
|
+
protocolVersion: asked !== void 0 && PROTOCOLS.includes(asked) ? asked : PROTOCOLS[0],
|
|
3744
3810
|
capabilities: { tools: { listChanged: false } },
|
|
3745
3811
|
serverInfo: { name: "austere-axi", title: "austere-axi", version: VERSION },
|
|
3746
3812
|
instructions: INSTRUCTIONS
|
|
@@ -3752,9 +3818,9 @@ function runMcp() {
|
|
|
3752
3818
|
case "tools/list":
|
|
3753
3819
|
return send({ id, result: { tools: listTools() } });
|
|
3754
3820
|
case "tools/call": {
|
|
3755
|
-
if (!Object.hasOwn(TOOLS, params.name)) return error62(id, -32602, `unknown tool: ${params.name}`);
|
|
3821
|
+
if (typeof params.name !== "string" || !Object.hasOwn(TOOLS, params.name)) return error62(id, -32602, `unknown tool: ${params.name}`);
|
|
3756
3822
|
const args = params.arguments ?? {};
|
|
3757
|
-
if (
|
|
3823
|
+
if (!isRecord2(args) || Array.isArray(args)) return error62(id, -32602, "arguments must be an object");
|
|
3758
3824
|
const ctl = new AbortController();
|
|
3759
3825
|
inflight.set(id, ctl);
|
|
3760
3826
|
try {
|
|
@@ -3779,12 +3845,13 @@ function runMcp() {
|
|
|
3779
3845
|
return error62(null, -32700, "parse error");
|
|
3780
3846
|
}
|
|
3781
3847
|
if (Array.isArray(msg)) return error62(null, -32600, "batch not supported");
|
|
3782
|
-
if (!msg || typeof msg
|
|
3783
|
-
if (msg &&
|
|
3848
|
+
if (!isRecord2(msg) || typeof msg.method !== "string") {
|
|
3849
|
+
if (isRecord2(msg) && "id" in msg && !("result" in msg || "error" in msg)) error62(msg.id ?? null, -32600, "invalid request");
|
|
3784
3850
|
return;
|
|
3785
3851
|
}
|
|
3786
|
-
|
|
3787
|
-
|
|
3852
|
+
const req = msg;
|
|
3853
|
+
handle(req).catch((err) => {
|
|
3854
|
+
if (req.id !== void 0 && req.id !== null) error62(req.id, -32603, String((isRecord2(err) ? err.message : void 0) || err));
|
|
3788
3855
|
});
|
|
3789
3856
|
});
|
|
3790
3857
|
return new Promise((resolve) => {
|
|
@@ -3794,13 +3861,15 @@ function runMcp() {
|
|
|
3794
3861
|
});
|
|
3795
3862
|
});
|
|
3796
3863
|
}
|
|
3797
|
-
var PROTOCOLS, POLL_TIMEOUT_MS, INSTRUCTIONS, file2, TOOLS;
|
|
3864
|
+
var isRecord2, PROTOCOLS, POLL_TIMEOUT_MS, INSTRUCTIONS, file2, TOOLS;
|
|
3798
3865
|
var init_mcp = __esm({
|
|
3799
|
-
"src/cli/mcp.
|
|
3866
|
+
"src/cli/mcp.ts"() {
|
|
3867
|
+
"use strict";
|
|
3800
3868
|
init_paths();
|
|
3869
|
+
isRecord2 = (v) => !!v && typeof v === "object";
|
|
3801
3870
|
PROTOCOLS = ["2025-11-25", "2025-06-18", "2024-11-05"];
|
|
3802
3871
|
POLL_TIMEOUT_MS = 5e4;
|
|
3803
|
-
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.`;
|
|
3804
3873
|
file2 = { type: "string", description: "Path of the .html page. Use an absolute path; a relative one resolves against the server's working directory." };
|
|
3805
3874
|
TOOLS = {
|
|
3806
3875
|
spec: {
|
|
@@ -3815,9 +3884,13 @@ var init_mcp = __esm({
|
|
|
3815
3884
|
check: {
|
|
3816
3885
|
title: "Validate a page",
|
|
3817
3886
|
description: "Validate the page's OpenUI Lang program without a browser: syntax, component names, required props, unresolved references, unreachable statements.",
|
|
3818
|
-
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
|
+
},
|
|
3819
3892
|
required: ["file"],
|
|
3820
|
-
run: (a) => [{ args: ["check", ...a.fix ? ["--fix"] : [], "--", a.file] }]
|
|
3893
|
+
run: (a) => [{ args: ["check", ...a.fix ? ["--fix"] : [], ...a.render ? ["--render"] : [], "--", a.file] }]
|
|
3821
3894
|
},
|
|
3822
3895
|
open: {
|
|
3823
3896
|
title: "Validate and open the review",
|
|
@@ -3878,9 +3951,9 @@ var init_mcp = __esm({
|
|
|
3878
3951
|
}
|
|
3879
3952
|
});
|
|
3880
3953
|
|
|
3881
|
-
// src/cli/main.
|
|
3882
|
-
import
|
|
3883
|
-
import
|
|
3954
|
+
// src/cli/main.ts
|
|
3955
|
+
import fs15 from "node:fs";
|
|
3956
|
+
import path15 from "node:path";
|
|
3884
3957
|
|
|
3885
3958
|
// node_modules/@openuidev/lang-core/dist/shared-Dzgc28Wo.mjs
|
|
3886
3959
|
var TELEMETRY_REQUEST_TIMEOUT_MS = 2e3;
|
|
@@ -4741,10 +4814,10 @@ function mergeDefs(...defs) {
|
|
|
4741
4814
|
function cloneDef(schema) {
|
|
4742
4815
|
return mergeDefs(schema._zod.def);
|
|
4743
4816
|
}
|
|
4744
|
-
function getElementAtPath(obj,
|
|
4745
|
-
if (!
|
|
4817
|
+
function getElementAtPath(obj, path16) {
|
|
4818
|
+
if (!path16)
|
|
4746
4819
|
return obj;
|
|
4747
|
-
return
|
|
4820
|
+
return path16.reduce((acc, key2) => acc?.[key2], obj);
|
|
4748
4821
|
}
|
|
4749
4822
|
function promiseAllObject(promisesObj) {
|
|
4750
4823
|
const keys = Object.keys(promisesObj);
|
|
@@ -5084,11 +5157,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
5084
5157
|
}
|
|
5085
5158
|
return false;
|
|
5086
5159
|
}
|
|
5087
|
-
function prefixIssues(
|
|
5160
|
+
function prefixIssues(path16, issues) {
|
|
5088
5161
|
return issues.map((iss) => {
|
|
5089
5162
|
var _a3;
|
|
5090
5163
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
5091
|
-
iss.path.unshift(
|
|
5164
|
+
iss.path.unshift(path16);
|
|
5092
5165
|
return iss;
|
|
5093
5166
|
});
|
|
5094
5167
|
}
|
|
@@ -5550,16 +5623,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5550
5623
|
}
|
|
5551
5624
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
5552
5625
|
const fieldErrors = { _errors: [] };
|
|
5553
|
-
const processError = (error63,
|
|
5626
|
+
const processError = (error63, path16 = []) => {
|
|
5554
5627
|
for (const issue2 of error63.issues) {
|
|
5555
5628
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5556
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5629
|
+
issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
|
|
5557
5630
|
} else if (issue2.code === "invalid_key") {
|
|
5558
|
-
processError({ issues: issue2.issues }, [...
|
|
5631
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5559
5632
|
} else if (issue2.code === "invalid_element") {
|
|
5560
|
-
processError({ issues: issue2.issues }, [...
|
|
5633
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5561
5634
|
} else {
|
|
5562
|
-
const fullpath = [...
|
|
5635
|
+
const fullpath = [...path16, ...issue2.path];
|
|
5563
5636
|
if (fullpath.length === 0) {
|
|
5564
5637
|
fieldErrors._errors.push(mapper(issue2));
|
|
5565
5638
|
} else {
|
|
@@ -5598,17 +5671,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5598
5671
|
}
|
|
5599
5672
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
5600
5673
|
const result = { errors: [] };
|
|
5601
|
-
const processError = (error63,
|
|
5674
|
+
const processError = (error63, path16 = []) => {
|
|
5602
5675
|
var _a3;
|
|
5603
5676
|
for (const issue2 of error63.issues) {
|
|
5604
5677
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5605
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5678
|
+
issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
|
|
5606
5679
|
} else if (issue2.code === "invalid_key") {
|
|
5607
|
-
processError({ issues: issue2.issues }, [...
|
|
5680
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5608
5681
|
} else if (issue2.code === "invalid_element") {
|
|
5609
|
-
processError({ issues: issue2.issues }, [...
|
|
5682
|
+
processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
|
|
5610
5683
|
} else {
|
|
5611
|
-
const fullpath = [...
|
|
5684
|
+
const fullpath = [...path16, ...issue2.path];
|
|
5612
5685
|
if (fullpath.length === 0) {
|
|
5613
5686
|
result.errors.push(mapper(issue2));
|
|
5614
5687
|
continue;
|
|
@@ -5647,8 +5720,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5647
5720
|
}
|
|
5648
5721
|
function toDotPath(_path) {
|
|
5649
5722
|
const segs = [];
|
|
5650
|
-
const
|
|
5651
|
-
for (const seg of
|
|
5723
|
+
const path16 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5724
|
+
for (const seg of path16) {
|
|
5652
5725
|
if (typeof seg === "number")
|
|
5653
5726
|
segs.push(`[${seg}]`);
|
|
5654
5727
|
else if (typeof seg === "symbol")
|
|
@@ -22786,13 +22859,13 @@ function resolveRef(ref, ctx) {
|
|
|
22786
22859
|
if (!ref.startsWith("#")) {
|
|
22787
22860
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
22788
22861
|
}
|
|
22789
|
-
const
|
|
22790
|
-
if (
|
|
22862
|
+
const path16 = ref.slice(1).split("/").filter(Boolean);
|
|
22863
|
+
if (path16.length === 0) {
|
|
22791
22864
|
return ctx.rootSchema;
|
|
22792
22865
|
}
|
|
22793
22866
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
22794
|
-
if (
|
|
22795
|
-
const key2 =
|
|
22867
|
+
if (path16[0] === defsKey) {
|
|
22868
|
+
const key2 = path16[1] === void 0 ? void 0 : decodeJSONPointerSegment(path16[1]);
|
|
22796
22869
|
if (!key2 || !ctx.defs[key2]) {
|
|
22797
22870
|
throw new Error(`Reference not found: ${ref}`);
|
|
22798
22871
|
}
|
|
@@ -23674,11 +23747,11 @@ function defineAction(config2) {
|
|
|
23674
23747
|
ref: actionRef(config2.name, data)
|
|
23675
23748
|
};
|
|
23676
23749
|
}
|
|
23677
|
-
function resolveField(obj,
|
|
23678
|
-
if (!
|
|
23679
|
-
if (!
|
|
23750
|
+
function resolveField(obj, path16) {
|
|
23751
|
+
if (!path16 || obj == null) return void 0;
|
|
23752
|
+
if (!path16.includes(".")) return obj[path16];
|
|
23680
23753
|
let cur = obj;
|
|
23681
|
-
for (const p of
|
|
23754
|
+
for (const p of path16.split(".")) {
|
|
23682
23755
|
if (cur == null) return void 0;
|
|
23683
23756
|
cur = cur[p];
|
|
23684
23757
|
}
|
|
@@ -25054,14 +25127,14 @@ function checkTypeMismatch(value, info) {
|
|
|
25054
25127
|
};
|
|
25055
25128
|
return null;
|
|
25056
25129
|
}
|
|
25057
|
-
function validationMessage(component,
|
|
25130
|
+
function validationMessage(component, path16, issue2) {
|
|
25058
25131
|
switch (issue2.code) {
|
|
25059
25132
|
case "type-mismatch":
|
|
25060
|
-
return `field "${
|
|
25133
|
+
return `field "${path16}" expects ${issue2.expected} but got ${issue2.actual}`;
|
|
25061
25134
|
case "missing-required":
|
|
25062
|
-
return `missing required field "${
|
|
25135
|
+
return `missing required field "${path16}"${issue2.signature ? ` \u2014 signature: ${issue2.signature}` : ""}`;
|
|
25063
25136
|
case "null-required":
|
|
25064
|
-
return `required field "${
|
|
25137
|
+
return `required field "${path16}" cannot be null${issue2.signature ? ` \u2014 signature: ${issue2.signature}` : ""}`;
|
|
25065
25138
|
case "unknown-component":
|
|
25066
25139
|
return `Unknown component "${component}" \u2014 not found in catalog or builtins${issue2.available?.length ? `. Available components: ${issue2.available.join(", ")}` : ""}`;
|
|
25067
25140
|
case "inline-reserved":
|
|
@@ -25076,12 +25149,12 @@ function validationMessage(component, path15, issue2) {
|
|
|
25076
25149
|
return component ? `No root statement, and no statement calls the root component ${component}` : "No root statement";
|
|
25077
25150
|
}
|
|
25078
25151
|
}
|
|
25079
|
-
function pushValidationIssue(ctx, component,
|
|
25152
|
+
function pushValidationIssue(ctx, component, path16, issue2) {
|
|
25080
25153
|
ctx.errors.push({
|
|
25081
25154
|
code: issue2.code,
|
|
25082
25155
|
component,
|
|
25083
|
-
path:
|
|
25084
|
-
message: validationMessage(component,
|
|
25156
|
+
path: path16,
|
|
25157
|
+
message: validationMessage(component, path16, issue2),
|
|
25085
25158
|
statementId: ctx.currentStatementId,
|
|
25086
25159
|
...issue2.code === "no-root" && issue2.reason === "fallback" && { severity: "warning" }
|
|
25087
25160
|
});
|
|
@@ -25091,10 +25164,10 @@ function resolveInvalidValue(required2, defaultValue) {
|
|
|
25091
25164
|
if (defaultValue !== void 0) return defaultValue;
|
|
25092
25165
|
return required2 ? INVALID2 : void 0;
|
|
25093
25166
|
}
|
|
25094
|
-
function validateElementPosition(element, s, component,
|
|
25167
|
+
function validateElementPosition(element, s, component, path16, ctx) {
|
|
25095
25168
|
if (isCompositeSchema(s)) return false;
|
|
25096
25169
|
if (typeof s["type"] === "string" || Array.isArray(s["enum"]) || "const" in s) {
|
|
25097
|
-
pushValidationIssue(ctx, component,
|
|
25170
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25098
25171
|
code: "type-mismatch",
|
|
25099
25172
|
expected: typeof s["type"] === "string" ? s["type"] : "a literal value",
|
|
25100
25173
|
actual: `component "${element.typeName}"`
|
|
@@ -25110,9 +25183,9 @@ function isOnlyComponentSlot(s, ctx) {
|
|
|
25110
25183
|
const names = slotRefNames(s);
|
|
25111
25184
|
return names.length > 0 && names.every((n) => n !== void 0 && ctx.cat?.has(n));
|
|
25112
25185
|
}
|
|
25113
|
-
function validateObjectValue(value, s, component,
|
|
25186
|
+
function validateObjectValue(value, s, component, path16, ctx) {
|
|
25114
25187
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
25115
|
-
pushValidationIssue(ctx, component,
|
|
25188
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25116
25189
|
code: "type-mismatch",
|
|
25117
25190
|
expected: "object",
|
|
25118
25191
|
actual: jsType(value)
|
|
@@ -25132,14 +25205,14 @@ function validateObjectValue(value, s, component, path15, ctx) {
|
|
|
25132
25205
|
(out ??= { ...obj })[key2] = fallback;
|
|
25133
25206
|
continue;
|
|
25134
25207
|
}
|
|
25135
|
-
pushValidationIssue(ctx, component, `${
|
|
25208
|
+
pushValidationIssue(ctx, component, `${path16}/${key2}`, { code: present ? "null-required" : "missing-required" });
|
|
25136
25209
|
invalid = true;
|
|
25137
25210
|
}
|
|
25138
25211
|
}
|
|
25139
25212
|
for (const key2 of Object.keys(props)) {
|
|
25140
25213
|
if (!(key2 in obj)) continue;
|
|
25141
25214
|
const sub = props[key2];
|
|
25142
|
-
let next = validateSchemaValue(obj[key2], sub, component, `${
|
|
25215
|
+
let next = validateSchemaValue(obj[key2], sub, component, `${path16}/${key2}`, ctx);
|
|
25143
25216
|
if (next === INVALID2) {
|
|
25144
25217
|
next = resolveInvalidValue(required2.includes(key2), getSchemaDefaultValue(sub));
|
|
25145
25218
|
if (next === INVALID2) {
|
|
@@ -25154,9 +25227,9 @@ function validateObjectValue(value, s, component, path15, ctx) {
|
|
|
25154
25227
|
}
|
|
25155
25228
|
return invalid ? INVALID2 : out ?? obj;
|
|
25156
25229
|
}
|
|
25157
|
-
function validateArrayValue(value, s, component,
|
|
25230
|
+
function validateArrayValue(value, s, component, path16, ctx) {
|
|
25158
25231
|
if (!Array.isArray(value)) {
|
|
25159
|
-
pushValidationIssue(ctx, component,
|
|
25232
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25160
25233
|
code: "type-mismatch",
|
|
25161
25234
|
expected: "array",
|
|
25162
25235
|
actual: jsType(value)
|
|
@@ -25167,19 +25240,19 @@ function validateArrayValue(value, s, component, path15, ctx) {
|
|
|
25167
25240
|
if (!items || typeof items !== "object" || Array.isArray(items)) return value;
|
|
25168
25241
|
let out;
|
|
25169
25242
|
for (let i = 0; i < value.length; i++) {
|
|
25170
|
-
const next = validateSchemaValue(value[i], items, component, `${
|
|
25243
|
+
const next = validateSchemaValue(value[i], items, component, `${path16}/${i}`, ctx);
|
|
25171
25244
|
if (next !== value[i] && !out) out = value.slice(0, i);
|
|
25172
25245
|
if (out && next !== INVALID2) out.push(next);
|
|
25173
25246
|
}
|
|
25174
25247
|
return out ?? value;
|
|
25175
25248
|
}
|
|
25176
|
-
function validateLeafValue(value, s, component,
|
|
25249
|
+
function validateLeafValue(value, s, component, path16, ctx) {
|
|
25177
25250
|
const leaf = getScalarTypeInfo(s);
|
|
25178
25251
|
if (leaf.expectedType == null && leaf.enumValues == null) return false;
|
|
25179
25252
|
if (leaf.enumValues && ctx.partial) return false;
|
|
25180
25253
|
const mismatch = checkTypeMismatch(value, leaf);
|
|
25181
25254
|
if (mismatch) {
|
|
25182
|
-
pushValidationIssue(ctx, component,
|
|
25255
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25183
25256
|
code: "type-mismatch",
|
|
25184
25257
|
...mismatch
|
|
25185
25258
|
});
|
|
@@ -25187,15 +25260,15 @@ function validateLeafValue(value, s, component, path15, ctx) {
|
|
|
25187
25260
|
}
|
|
25188
25261
|
return false;
|
|
25189
25262
|
}
|
|
25190
|
-
function validateSchemaValue(value, schema, component,
|
|
25263
|
+
function validateSchemaValue(value, schema, component, path16, ctx) {
|
|
25191
25264
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return value;
|
|
25192
25265
|
const s = schema;
|
|
25193
25266
|
if (value == null) return value;
|
|
25194
25267
|
if (isASTNode(value)) return value;
|
|
25195
|
-
if (isElementNode(value)) return validateElementPosition(value, s, component,
|
|
25268
|
+
if (isElementNode(value)) return validateElementPosition(value, s, component, path16, ctx) ? INVALID2 : value;
|
|
25196
25269
|
if (isCompositeSchema(s)) {
|
|
25197
25270
|
if (!isOnlyComponentSlot(s, ctx)) return value;
|
|
25198
|
-
pushValidationIssue(ctx, component,
|
|
25271
|
+
pushValidationIssue(ctx, component, path16, {
|
|
25199
25272
|
code: "type-mismatch",
|
|
25200
25273
|
expected: slotRefNames(s).join(" | "),
|
|
25201
25274
|
actual: Array.isArray(value) ? "array" : typeof value === "object" ? "plain object" : typeof value
|
|
@@ -25203,9 +25276,9 @@ function validateSchemaValue(value, schema, component, path15, ctx) {
|
|
|
25203
25276
|
return INVALID2;
|
|
25204
25277
|
}
|
|
25205
25278
|
const type = s["type"];
|
|
25206
|
-
if (type === "object") return validateObjectValue(value, s, component,
|
|
25207
|
-
if (type === "array") return validateArrayValue(value, s, component,
|
|
25208
|
-
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;
|
|
25209
25282
|
}
|
|
25210
25283
|
function containsDynamicValue(v) {
|
|
25211
25284
|
if (v == null || typeof v !== "object") return false;
|
|
@@ -26090,16 +26163,16 @@ function mergeStatements(existing, patch, rootId = "root") {
|
|
|
26090
26163
|
return order.filter((id) => merged.has(id)).map((id) => merged.get(id)).join("\n");
|
|
26091
26164
|
}
|
|
26092
26165
|
|
|
26093
|
-
// src/cli/main.
|
|
26166
|
+
// src/cli/main.ts
|
|
26094
26167
|
init_artifact();
|
|
26095
26168
|
|
|
26096
|
-
// src/cli/check.
|
|
26169
|
+
// src/cli/check.ts
|
|
26097
26170
|
import fs3 from "node:fs";
|
|
26098
26171
|
init_artifact();
|
|
26099
26172
|
init_paths();
|
|
26100
26173
|
init_packs();
|
|
26101
26174
|
|
|
26102
|
-
// src/shared/statements.
|
|
26175
|
+
// src/shared/statements.ts
|
|
26103
26176
|
var IDENT_START = /[A-Za-z_$]/;
|
|
26104
26177
|
function splitStatements(source) {
|
|
26105
26178
|
const out = [];
|
|
@@ -28078,7 +28151,8 @@ function Ce(e) {
|
|
|
28078
28151
|
var xr = { graphlib: ie, version: ue, layout: Oe, debug: Ce, util: { time: le, notime: q } };
|
|
28079
28152
|
var Uo = xr;
|
|
28080
28153
|
|
|
28081
|
-
// src/runtime/components/_architecture-lanes.
|
|
28154
|
+
// src/runtime/components/_architecture-lanes.ts
|
|
28155
|
+
var known = (map2, key2) => map2.get(key2);
|
|
28082
28156
|
var LANES = {
|
|
28083
28157
|
margin: 16,
|
|
28084
28158
|
// around the drawing
|
|
@@ -28116,7 +28190,8 @@ function lanesPlanWidth(model) {
|
|
|
28116
28190
|
}
|
|
28117
28191
|
var plans = /* @__PURE__ */ new WeakMap();
|
|
28118
28192
|
function planColumns(model) {
|
|
28119
|
-
|
|
28193
|
+
const cached2 = plans.get(model);
|
|
28194
|
+
if (cached2 !== void 0) return cached2;
|
|
28120
28195
|
const lanes = prepareLanes(model);
|
|
28121
28196
|
if (!lanes) {
|
|
28122
28197
|
plans.set(model, null);
|
|
@@ -28185,9 +28260,10 @@ function assignLanes(model) {
|
|
|
28185
28260
|
const top = model.groups.filter((g) => !g.parent);
|
|
28186
28261
|
if (!top.length) return null;
|
|
28187
28262
|
const laneOf = /* @__PURE__ */ new Map();
|
|
28188
|
-
const
|
|
28263
|
+
const newLane = (group, nodes) => ({ group, nodes, index: 0, laneOf, rows: [], phys: /* @__PURE__ */ new Map(), inner: 0, headLine: "", frame: { x: 0, y: 0, w: 0, h: 0 } });
|
|
28264
|
+
const lanes = top.map((group) => newLane(group, []));
|
|
28189
28265
|
for (const lane of lanes) {
|
|
28190
|
-
for (const m of lane.group
|
|
28266
|
+
for (const m of lane.group?.members ?? []) {
|
|
28191
28267
|
if (laneOf.has(m)) continue;
|
|
28192
28268
|
laneOf.set(m, lane);
|
|
28193
28269
|
lane.nodes.push(m);
|
|
@@ -28202,7 +28278,7 @@ function assignLanes(model) {
|
|
|
28202
28278
|
if (isLoose.has(e.from) && !isLoose.has(e.to)) out++;
|
|
28203
28279
|
if (!isLoose.has(e.from) && isLoose.has(e.to)) inn++;
|
|
28204
28280
|
}
|
|
28205
|
-
const lane =
|
|
28281
|
+
const lane = newLane(null, loose);
|
|
28206
28282
|
for (const id of loose) laneOf.set(id, lane);
|
|
28207
28283
|
if (out >= inn) lanes.unshift(lane);
|
|
28208
28284
|
else lanes.push(lane);
|
|
@@ -28221,14 +28297,14 @@ function laneRows(lane, edges) {
|
|
|
28221
28297
|
const parent = new Map(ids.map((id) => [id, id]));
|
|
28222
28298
|
const find = (x) => {
|
|
28223
28299
|
while (parent.get(x) !== x) {
|
|
28224
|
-
parent.set(x, parent
|
|
28225
|
-
x = parent
|
|
28300
|
+
parent.set(x, known(parent, known(parent, x)));
|
|
28301
|
+
x = known(parent, x);
|
|
28226
28302
|
}
|
|
28227
28303
|
return x;
|
|
28228
28304
|
};
|
|
28229
28305
|
for (const e of intra) parent.set(find(e.from), find(e.to));
|
|
28230
28306
|
const adj = new Map(ids.map((id) => [id, []]));
|
|
28231
|
-
for (const e of intra) adj
|
|
28307
|
+
for (const e of intra) known(adj, e.from).push(e.to);
|
|
28232
28308
|
const state2 = /* @__PURE__ */ new Map();
|
|
28233
28309
|
const kept = [];
|
|
28234
28310
|
for (const root of ids) {
|
|
@@ -28238,7 +28314,7 @@ function laneRows(lane, edges) {
|
|
|
28238
28314
|
while (stack.length) {
|
|
28239
28315
|
const top = stack[stack.length - 1];
|
|
28240
28316
|
const [u, k] = top;
|
|
28241
|
-
const next = adj
|
|
28317
|
+
const next = known(adj, u)[k];
|
|
28242
28318
|
if (next === void 0) {
|
|
28243
28319
|
state2.set(u, 2);
|
|
28244
28320
|
stack.pop();
|
|
@@ -28257,8 +28333,8 @@ function laneRows(lane, edges) {
|
|
|
28257
28333
|
for (let changed = true, guard = 0; changed && guard <= ids.length; guard++) {
|
|
28258
28334
|
changed = false;
|
|
28259
28335
|
for (const [u, v] of kept) {
|
|
28260
|
-
if (rank
|
|
28261
|
-
rank.set(v, rank
|
|
28336
|
+
if (known(rank, v) < known(rank, u) + 1) {
|
|
28337
|
+
rank.set(v, known(rank, u) + 1);
|
|
28262
28338
|
changed = true;
|
|
28263
28339
|
}
|
|
28264
28340
|
}
|
|
@@ -28267,20 +28343,20 @@ function laneRows(lane, edges) {
|
|
|
28267
28343
|
for (const id of ids) {
|
|
28268
28344
|
const root = find(id);
|
|
28269
28345
|
if (!blocks.has(root)) blocks.set(root, []);
|
|
28270
|
-
blocks
|
|
28346
|
+
known(blocks, root).push(id);
|
|
28271
28347
|
}
|
|
28272
28348
|
const rows = [];
|
|
28273
|
-
for (const members2 of [...blocks.values()].sort((a, b) => pos
|
|
28349
|
+
for (const members2 of [...blocks.values()].sort((a, b) => known(pos, a[0]) - known(pos, b[0]))) {
|
|
28274
28350
|
const byRank = /* @__PURE__ */ new Map();
|
|
28275
28351
|
for (const id of members2) {
|
|
28276
|
-
const r = rank
|
|
28352
|
+
const r = known(rank, id);
|
|
28277
28353
|
if (!byRank.has(r)) byRank.set(r, []);
|
|
28278
|
-
byRank
|
|
28354
|
+
known(byRank, r).push(id);
|
|
28279
28355
|
}
|
|
28280
28356
|
for (const r of [...byRank.keys()].sort((a, b) => a - b)) {
|
|
28281
|
-
const ids2 = byRank
|
|
28282
|
-
if (ids2.length <= 2) rows.push({ ids: ids2, pin: null });
|
|
28283
|
-
else for (const id of ids2) rows.push({ ids: [id], pin: null });
|
|
28357
|
+
const ids2 = known(byRank, r);
|
|
28358
|
+
if (ids2.length <= 2) rows.push({ ids: ids2, pin: null, g: 0 });
|
|
28359
|
+
else for (const id of ids2) rows.push({ ids: [id], pin: null, g: 0 });
|
|
28284
28360
|
}
|
|
28285
28361
|
}
|
|
28286
28362
|
return rows;
|
|
@@ -28297,13 +28373,13 @@ function applyPins(rows, byId) {
|
|
|
28297
28373
|
continue;
|
|
28298
28374
|
}
|
|
28299
28375
|
if (!pinned.has(pin)) {
|
|
28300
|
-
const pr2 = { ids: [], pin };
|
|
28376
|
+
const pr2 = { ids: [], pin, g: 0 };
|
|
28301
28377
|
pinned.set(pin, pr2);
|
|
28302
28378
|
out.push(pr2);
|
|
28303
28379
|
}
|
|
28304
|
-
pinned
|
|
28380
|
+
known(pinned, pin).ids.push(id);
|
|
28305
28381
|
}
|
|
28306
|
-
if (free.length) out.push({ ids: free, pin: null });
|
|
28382
|
+
if (free.length) out.push({ ids: free, pin: null, g: 0 });
|
|
28307
28383
|
}
|
|
28308
28384
|
return out;
|
|
28309
28385
|
}
|
|
@@ -28332,7 +28408,7 @@ function assignGlobalRows(lanes, edges) {
|
|
|
28332
28408
|
const neighbors = /* @__PURE__ */ new Map();
|
|
28333
28409
|
const link = (a, b) => {
|
|
28334
28410
|
if (!neighbors.has(a)) neighbors.set(a, []);
|
|
28335
|
-
neighbors
|
|
28411
|
+
known(neighbors, a).push(b);
|
|
28336
28412
|
};
|
|
28337
28413
|
for (const e of edges) {
|
|
28338
28414
|
const a = rowOf.get(e.from);
|
|
@@ -28368,9 +28444,9 @@ function assignGlobalRows(lanes, edges) {
|
|
|
28368
28444
|
for (const lane of lanes) {
|
|
28369
28445
|
lane.phys = /* @__PURE__ */ new Map();
|
|
28370
28446
|
for (const r of lane.rows) {
|
|
28371
|
-
const g = index
|
|
28447
|
+
const g = known(index, r.g);
|
|
28372
28448
|
if (!lane.phys.has(g)) lane.phys.set(g, []);
|
|
28373
|
-
lane.phys
|
|
28449
|
+
known(lane.phys, g).push(...r.ids);
|
|
28374
28450
|
}
|
|
28375
28451
|
}
|
|
28376
28452
|
return used.length;
|
|
@@ -28411,7 +28487,7 @@ function placeColumns(lanes, byId, rowCount, sizes, model) {
|
|
|
28411
28487
|
for (const lane of lanes) {
|
|
28412
28488
|
let inner = L.laneMinW;
|
|
28413
28489
|
for (const ids of lane.phys.values()) {
|
|
28414
|
-
const w = ids.reduce((sum, id) => sum + natural
|
|
28490
|
+
const w = ids.reduce((sum, id) => sum + known(natural, id).width, 0) + L.colGap * (ids.length - 1);
|
|
28415
28491
|
inner = Math.max(inner, w);
|
|
28416
28492
|
}
|
|
28417
28493
|
const label = lane.group?.label || "";
|
|
@@ -28422,13 +28498,13 @@ function placeColumns(lanes, byId, rowCount, sizes, model) {
|
|
|
28422
28498
|
const sized = /* @__PURE__ */ new Map();
|
|
28423
28499
|
for (const lane of lanes) {
|
|
28424
28500
|
for (const ids of lane.phys.values()) {
|
|
28425
|
-
const sum = ids.reduce((acc, id) => acc + natural
|
|
28501
|
+
const sum = ids.reduce((acc, id) => acc + known(natural, id).width, 0);
|
|
28426
28502
|
const extra = (lane.inner - sum - L.colGap * (ids.length - 1)) / ids.length;
|
|
28427
|
-
for (const id of ids) sized.set(id, sizeNode(byId
|
|
28503
|
+
for (const id of ids) sized.set(id, sizeNode(known(byId, id), Math.floor(known(natural, id).width + extra)));
|
|
28428
28504
|
}
|
|
28429
28505
|
}
|
|
28430
28506
|
const rowH = new Array(rowCount).fill(0);
|
|
28431
|
-
for (const lane of lanes) for (const [g, ids] of lane.phys) for (const id of ids) rowH[g] = Math.max(rowH[g], sized
|
|
28507
|
+
for (const lane of lanes) for (const [g, ids] of lane.phys) for (const id of ids) rowH[g] = Math.max(rowH[g], known(sized, id).height);
|
|
28432
28508
|
const rowTop = [];
|
|
28433
28509
|
let y = L.margin + headH + L.lanePad;
|
|
28434
28510
|
for (let g = 0; g < rowCount; g++) {
|
|
@@ -28446,7 +28522,7 @@ function placeColumns(lanes, byId, rowCount, sizes, model) {
|
|
|
28446
28522
|
for (const [g, ids] of lane.phys) {
|
|
28447
28523
|
let bx = x + L.lanePad;
|
|
28448
28524
|
for (const id of ids) {
|
|
28449
|
-
const b = sized
|
|
28525
|
+
const b = known(sized, id);
|
|
28450
28526
|
boxes.set(id, { ...b, x: bx, y: Math.round(rowTop[g] + (rowH[g] - b.height) / 2), rowIndex: g, laneIndex: lane.index });
|
|
28451
28527
|
bx += b.width + L.colGap;
|
|
28452
28528
|
}
|
|
@@ -28465,8 +28541,8 @@ function placeColumns(lanes, byId, rowCount, sizes, model) {
|
|
|
28465
28541
|
channels.push({ key: "outB", axis: "y", lo: frameBottom, hi: height, fixed: true });
|
|
28466
28542
|
const frameLines = { xs: lanes.flatMap((l) => [l.frame.x, l.frame.x + l.frame.w]), ys: [frameTop, frameBottom] };
|
|
28467
28543
|
const sides = (e) => {
|
|
28468
|
-
const a = boxes
|
|
28469
|
-
const b = boxes
|
|
28544
|
+
const a = known(boxes, e.from);
|
|
28545
|
+
const b = known(boxes, e.to);
|
|
28470
28546
|
if (a.laneIndex !== b.laneIndex) return b.laneIndex > a.laneIndex ? ["right", "left"] : ["left", "right"];
|
|
28471
28547
|
if (b.rowIndex > a.rowIndex) return ["bottom", "top"];
|
|
28472
28548
|
if (b.rowIndex < a.rowIndex) return ["top", "bottom"];
|
|
@@ -28476,8 +28552,8 @@ function placeColumns(lanes, byId, rowCount, sizes, model) {
|
|
|
28476
28552
|
}
|
|
28477
28553
|
function placeBands(lanes, byId, sizes, model, targetWidth) {
|
|
28478
28554
|
const L = LANES;
|
|
28479
|
-
const left = sizes
|
|
28480
|
-
const right = sizes
|
|
28555
|
+
const left = known(sizes, "left");
|
|
28556
|
+
const right = known(sizes, "right");
|
|
28481
28557
|
const target = (targetWidth || 360) / 0.85;
|
|
28482
28558
|
const room = Math.max(180, Math.min(720, target - left - right - L.lanePad * 2 - 4));
|
|
28483
28559
|
sizeLaneHeads(lanes, () => room);
|
|
@@ -28495,7 +28571,7 @@ function placeBands(lanes, byId, sizes, model, targetWidth) {
|
|
|
28495
28571
|
if (prevBottom !== null) channels.push({ key: `band${lane.index}`, axis: "y", lo: prevBottom, hi: top, fixed: true });
|
|
28496
28572
|
let first = true;
|
|
28497
28573
|
for (const row of lane.rows) {
|
|
28498
|
-
const natural = row.ids.map((id) => sizeNode(byId
|
|
28574
|
+
const natural = row.ids.map((id) => sizeNode(known(byId, id)));
|
|
28499
28575
|
const fits = natural.reduce((sum, n) => sum + n.width, 0) + L.colGap * (row.ids.length - 1) <= room;
|
|
28500
28576
|
const lines = fits ? [row.ids] : row.ids.map((id) => [id]);
|
|
28501
28577
|
for (const ids of lines) {
|
|
@@ -28512,7 +28588,7 @@ function placeBands(lanes, byId, sizes, model, targetWidth) {
|
|
|
28512
28588
|
}
|
|
28513
28589
|
first = false;
|
|
28514
28590
|
const w = (room - L.colGap * (ids.length - 1)) / ids.length;
|
|
28515
|
-
const sized = ids.map((id) => sizeNode(byId
|
|
28591
|
+
const sized = ids.map((id) => sizeNode(known(byId, id), Math.floor(w)));
|
|
28516
28592
|
const h = Math.max(...sized.map((b) => b.height));
|
|
28517
28593
|
let bx = x0;
|
|
28518
28594
|
ids.forEach((id, k) => {
|
|
@@ -28534,8 +28610,8 @@ function placeBands(lanes, byId, sizes, model, targetWidth) {
|
|
|
28534
28610
|
channels.push({ key: "left", axis: "x", lo: 0, hi: left }, { key: "right", axis: "x", lo: width - right, hi: width });
|
|
28535
28611
|
const frameLines = { xs: [left, width - right], ys: lanes.flatMap((l) => [l.frame.y, l.frame.y + l.frame.h]) };
|
|
28536
28612
|
const sides = (e) => {
|
|
28537
|
-
const a = boxes
|
|
28538
|
-
const b = boxes
|
|
28613
|
+
const a = known(boxes, e.from);
|
|
28614
|
+
const b = known(boxes, e.to);
|
|
28539
28615
|
if (a.rowIndex === b.rowIndex) return b.x > a.x ? ["right", "left"] : ["left", "right"];
|
|
28540
28616
|
if (b.rowIndex === a.rowIndex + 1 && b.x < a.x + a.width && a.x < b.x + b.width) return ["bottom", "top"];
|
|
28541
28617
|
return b.rowIndex > a.rowIndex ? ["right", "right"] : ["left", "left"];
|
|
@@ -28560,8 +28636,8 @@ function routeAll(geo, model) {
|
|
|
28560
28636
|
continue;
|
|
28561
28637
|
}
|
|
28562
28638
|
const [fromSide, toSide] = geo.sides(e);
|
|
28563
|
-
const a = boxes
|
|
28564
|
-
const b = boxes
|
|
28639
|
+
const a = known(boxes, e.from);
|
|
28640
|
+
const b = known(boxes, e.to);
|
|
28565
28641
|
edges.push({ id: e.id, from: e.from, to: e.to, fromSide: openSide(a, fromSide, b, boxes), toSide: openSide(b, toSide, a, boxes) });
|
|
28566
28642
|
}
|
|
28567
28643
|
const xs = [];
|
|
@@ -28576,7 +28652,7 @@ function routeAll(geo, model) {
|
|
|
28576
28652
|
bounds: { x0: 1, y0: 1, x1: geo.width - 1, y1: geo.height - 1 }
|
|
28577
28653
|
});
|
|
28578
28654
|
for (const e of loops) {
|
|
28579
|
-
const b = boxes
|
|
28655
|
+
const b = known(boxes, e.from);
|
|
28580
28656
|
const r = b.x + b.width;
|
|
28581
28657
|
const bottom = b.y + b.height;
|
|
28582
28658
|
routes.set(e.id, [
|
|
@@ -28635,19 +28711,19 @@ function placePorts(boxes, edges) {
|
|
|
28635
28711
|
const add = (node2, side, other, edge, end) => {
|
|
28636
28712
|
const key2 = `${node2}\0${side}`;
|
|
28637
28713
|
if (!sides.has(key2)) sides.set(key2, { node: node2, side, items: [] });
|
|
28638
|
-
sides
|
|
28714
|
+
known(sides, key2).items.push({ other, edge, end, want: 0, pos: 0 });
|
|
28639
28715
|
};
|
|
28640
28716
|
for (const e of edges) {
|
|
28641
28717
|
add(e.from, e.fromSide, e.to, e, "fromPort");
|
|
28642
28718
|
add(e.to, e.toSide, e.from, e, "toPort");
|
|
28643
28719
|
}
|
|
28644
28720
|
for (const { node: node2, side, items } of sides.values()) {
|
|
28645
|
-
const b = boxes
|
|
28721
|
+
const b = known(boxes, node2);
|
|
28646
28722
|
const horizontal = side === "left" || side === "right";
|
|
28647
28723
|
const lo = horizontal ? b.y + 9 : b.x + 12;
|
|
28648
28724
|
const hi = horizontal ? b.y + b.height - 9 : b.x + b.width - 12;
|
|
28649
28725
|
for (const it2 of items) {
|
|
28650
|
-
const o = boxes
|
|
28726
|
+
const o = known(boxes, it2.other);
|
|
28651
28727
|
const oLo = horizontal ? o.y : o.x;
|
|
28652
28728
|
const oHi = horizontal ? o.y + o.height : o.x + o.width;
|
|
28653
28729
|
const sLo = horizontal ? b.y : b.x;
|
|
@@ -28728,10 +28804,11 @@ var Heap = class {
|
|
|
28728
28804
|
};
|
|
28729
28805
|
var COST = { bend: 36, overlap: 8, cross: 22, hug: 0.6 };
|
|
28730
28806
|
var SEARCH = { weight: 1.5, maxSteps: 12e4, maxTracks: 14 };
|
|
28731
|
-
function routeOrthogonal({ boxes, edges, xLines = [], yLines = [], avoid = { xs: [], ys: [] }, bounds }) {
|
|
28807
|
+
function routeOrthogonal({ boxes, edges: input2, xLines = [], yLines = [], avoid = { xs: [], ys: [] }, bounds }) {
|
|
28732
28808
|
const C = LANES.clear;
|
|
28733
28809
|
const S2 = LANES.stub;
|
|
28734
|
-
placePorts(boxes,
|
|
28810
|
+
placePorts(boxes, input2);
|
|
28811
|
+
const edges = input2;
|
|
28735
28812
|
const stubOf = (p, side) => ({ x: p.x + NORMAL[side][0] * S2, y: p.y + NORMAL[side][1] * S2 });
|
|
28736
28813
|
const rnd = (v) => Math.round(v * 2) / 2;
|
|
28737
28814
|
const hugXs = /* @__PURE__ */ new Set();
|
|
@@ -28827,11 +28904,11 @@ function routeOrthogonal({ boxes, edges, xLines = [], yLines = [], avoid = { xs:
|
|
|
28827
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);
|
|
28828
28905
|
if (!blocked) {
|
|
28829
28906
|
routes.set(e.id, [a, b]);
|
|
28830
|
-
const
|
|
28907
|
+
const path17 = [];
|
|
28831
28908
|
const [i0, j0, i1, j1] = [ix(a.x), iy(a.y), ix(b.x), iy(b.y)];
|
|
28832
|
-
if (j0 === j1) for (let i = Math.min(i0, i1); i <= Math.max(i0, i1); i++)
|
|
28833
|
-
else for (let j = Math.min(j0, j1); j <= Math.max(j0, j1); j++)
|
|
28834
|
-
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);
|
|
28835
28912
|
continue;
|
|
28836
28913
|
}
|
|
28837
28914
|
}
|
|
@@ -28841,12 +28918,12 @@ function routeOrthogonal({ boxes, edges, xLines = [], yLines = [], avoid = { xs:
|
|
|
28841
28918
|
const sj = iy(st2.y);
|
|
28842
28919
|
const ti = ix(tt2.x);
|
|
28843
28920
|
const tj = iy(tt2.y);
|
|
28844
|
-
let
|
|
28921
|
+
let path16 = null;
|
|
28845
28922
|
if (si !== void 0 && sj !== void 0 && ti !== void 0 && tj !== void 0) {
|
|
28846
|
-
|
|
28923
|
+
path16 = search(si, sj, dirOf(e.fromSide), ti, tj, reverse[dirOf(e.toSide)]);
|
|
28847
28924
|
}
|
|
28848
|
-
const pts =
|
|
28849
|
-
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);
|
|
28850
28927
|
routes.set(e.id, simplify([e.fromPort, ...pts, e.toPort]));
|
|
28851
28928
|
}
|
|
28852
28929
|
return routes;
|
|
@@ -28915,10 +28992,10 @@ function routeOrthogonal({ boxes, edges, xLines = [], yLines = [], avoid = { xs:
|
|
|
28915
28992
|
}
|
|
28916
28993
|
return out.reverse();
|
|
28917
28994
|
}
|
|
28918
|
-
function mark(
|
|
28919
|
-
for (let k = 1; k <
|
|
28920
|
-
const [i0, j0] =
|
|
28921
|
-
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];
|
|
28922
28999
|
if (j0 === j1) {
|
|
28923
29000
|
hUse[j0 * nx + Math.min(i0, i1)]++;
|
|
28924
29001
|
hPass[j0 * nx + i0]++;
|
|
@@ -29003,7 +29080,7 @@ function placeLabels(edges, routes, geo) {
|
|
|
29003
29080
|
segs.push({ a, b, vertical, score });
|
|
29004
29081
|
}
|
|
29005
29082
|
segs.sort((p, q2) => q2.score - p.score);
|
|
29006
|
-
const sizesToTry = [full, ...[GUTTER_LABEL_W, 76].filter((w) => w < full.width).map((w) => narrowLabel(e.label, w)).filter(
|
|
29083
|
+
const sizesToTry = [full, ...[GUTTER_LABEL_W, 76].filter((w) => w < full.width).map((w) => narrowLabel(e.label, w)).filter((lb) => !!lb)];
|
|
29007
29084
|
let choice = null;
|
|
29008
29085
|
let best = null;
|
|
29009
29086
|
for (const lb of sizesToTry) {
|
|
@@ -29048,9 +29125,9 @@ function placeLabels(edges, routes, geo) {
|
|
|
29048
29125
|
}
|
|
29049
29126
|
function finish({ geo, routed }, model) {
|
|
29050
29127
|
const { boxes, lanes } = geo;
|
|
29051
|
-
const nodes = model.nodes.filter((n) => boxes.has(n.id)).map((n) => ({ ...n, ...boxes
|
|
29128
|
+
const nodes = model.nodes.filter((n) => boxes.has(n.id)).map((n) => ({ ...n, ...known(boxes, n.id) }));
|
|
29052
29129
|
const labels = placeLabels(model.edges, routed, geo);
|
|
29053
|
-
const edges = model.edges.filter((e) => routed.has(e.id)).map((e) => ({ ...e, orthogonal: true, points: routed
|
|
29130
|
+
const edges = model.edges.filter((e) => routed.has(e.id)).map((e) => ({ ...e, orthogonal: true, points: known(routed, e.id), labelBox: labels.get(e.id) || null }));
|
|
29054
29131
|
const groups = [];
|
|
29055
29132
|
for (const lane of lanes) {
|
|
29056
29133
|
if (!lane.group) continue;
|
|
@@ -29059,7 +29136,7 @@ function finish({ geo, routed }, model) {
|
|
|
29059
29136
|
}
|
|
29060
29137
|
for (const g of model.groups) {
|
|
29061
29138
|
if (!g.parent) continue;
|
|
29062
|
-
const ms = g.members.map((id) => boxes.get(id)).filter(
|
|
29139
|
+
const ms = g.members.map((id) => boxes.get(id)).filter((b) => !!b);
|
|
29063
29140
|
if (!ms.length) continue;
|
|
29064
29141
|
const pad = 9;
|
|
29065
29142
|
const x0 = Math.min(...ms.map((b) => b.x)) - pad;
|
|
@@ -29084,7 +29161,7 @@ function finish({ geo, routed }, model) {
|
|
|
29084
29161
|
return { rankdir: "lanes", nodes, edges, groups, width, height };
|
|
29085
29162
|
}
|
|
29086
29163
|
|
|
29087
|
-
// src/runtime/components/_architecture-model.
|
|
29164
|
+
// src/runtime/components/_architecture-model.ts
|
|
29088
29165
|
var NODE_KINDS = ["frontend", "backend", "database", "messagebus", "cloud", "security", "external"];
|
|
29089
29166
|
var EDGE_STYLES = ["solid", "dashed", "emphasis", "security"];
|
|
29090
29167
|
var GROUP_KINDS = ["region", "security", "plain"];
|
|
@@ -29156,8 +29233,9 @@ function wrapText(text, size, maxWidth, maxLines = 2, mono = false) {
|
|
|
29156
29233
|
kept[maxLines - 1] = `${last.join("").trimEnd()}\u2026`;
|
|
29157
29234
|
return kept;
|
|
29158
29235
|
}
|
|
29159
|
-
var isElement = (v) => v && typeof v === "object" && v.props && typeof v.props === "object";
|
|
29236
|
+
var isElement = (v) => !!v && typeof v === "object" && "props" in v && !!v.props && typeof v.props === "object";
|
|
29160
29237
|
var str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
|
|
29238
|
+
var oneOf = (list, v) => list.includes(v);
|
|
29161
29239
|
function buildModel(props) {
|
|
29162
29240
|
const problems = [];
|
|
29163
29241
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -29165,7 +29243,8 @@ function buildModel(props) {
|
|
|
29165
29243
|
let anon = 0;
|
|
29166
29244
|
const addNode = (el) => {
|
|
29167
29245
|
if (!isElement(el)) return null;
|
|
29168
|
-
|
|
29246
|
+
const seen = seenEls.get(el);
|
|
29247
|
+
if (seen !== void 0) return seen;
|
|
29169
29248
|
const id = el.statementId || `_node${anon++}`;
|
|
29170
29249
|
if (!nodes.has(id)) {
|
|
29171
29250
|
const p = el.props;
|
|
@@ -29174,11 +29253,11 @@ function buildModel(props) {
|
|
|
29174
29253
|
id,
|
|
29175
29254
|
named: !!el.statementId,
|
|
29176
29255
|
label,
|
|
29177
|
-
kind: NODE_KINDS
|
|
29256
|
+
kind: oneOf(NODE_KINDS, p.kind) ? p.kind : "backend",
|
|
29178
29257
|
detail: str(p.detail).trim(),
|
|
29179
29258
|
tag: str(p.tag).trim(),
|
|
29180
|
-
tone: NODE_TONES
|
|
29181
|
-
row: Number.isInteger(p.row) && p.row > 0 ? Math.min(p.row, 200) : null
|
|
29259
|
+
tone: oneOf(NODE_TONES, p.tone) ? p.tone : "",
|
|
29260
|
+
row: typeof p.row === "number" && Number.isInteger(p.row) && p.row > 0 ? Math.min(p.row, 200) : null
|
|
29182
29261
|
});
|
|
29183
29262
|
}
|
|
29184
29263
|
seenEls.set(el, id);
|
|
@@ -29201,8 +29280,8 @@ function buildModel(props) {
|
|
|
29201
29280
|
from,
|
|
29202
29281
|
to,
|
|
29203
29282
|
label: str(el.props.label).trim(),
|
|
29204
|
-
style: EDGE_STYLES
|
|
29205
|
-
tone: EDGE_TONES
|
|
29283
|
+
style: oneOf(EDGE_STYLES, el.props.style) ? el.props.style : "solid",
|
|
29284
|
+
tone: oneOf(EDGE_TONES, el.props.tone) ? el.props.tone : ""
|
|
29206
29285
|
});
|
|
29207
29286
|
}
|
|
29208
29287
|
const groups = [];
|
|
@@ -29211,19 +29290,19 @@ function buildModel(props) {
|
|
|
29211
29290
|
if (!isElement(el)) continue;
|
|
29212
29291
|
const id = el.statementId || `_group${anon++}`;
|
|
29213
29292
|
if (groupIds.has(id)) continue;
|
|
29214
|
-
const members2 = [...new Set((Array.isArray(el.props.members) ? el.props.members : []).map(addNode).filter(
|
|
29293
|
+
const members2 = [...new Set((Array.isArray(el.props.members) ? el.props.members : []).map(addNode).filter((m) => !!m))];
|
|
29215
29294
|
if (!members2.length) continue;
|
|
29216
29295
|
groupIds.add(id);
|
|
29217
29296
|
groups.push({
|
|
29218
29297
|
id,
|
|
29219
29298
|
named: !!el.statementId,
|
|
29220
29299
|
label: str(el.props.label).trim(),
|
|
29221
|
-
kind: GROUP_KINDS
|
|
29300
|
+
kind: oneOf(GROUP_KINDS, el.props.kind) ? el.props.kind : "region",
|
|
29222
29301
|
members: members2
|
|
29223
29302
|
});
|
|
29224
29303
|
}
|
|
29225
29304
|
nestGroups(groups, problems);
|
|
29226
|
-
const direction = DIRECTIONS
|
|
29305
|
+
const direction = oneOf(DIRECTIONS, props?.direction) ? props.direction : "auto";
|
|
29227
29306
|
return { nodes: [...nodes.values()], edges, groups, direction, problems };
|
|
29228
29307
|
}
|
|
29229
29308
|
function nestGroups(groups, problems) {
|
|
@@ -29240,8 +29319,9 @@ function nestGroups(groups, problems) {
|
|
|
29240
29319
|
else {
|
|
29241
29320
|
let first = groups.find((x) => x.id === owner.get(m));
|
|
29242
29321
|
if (!isAncestor(groups, g.id, first.id)) {
|
|
29243
|
-
for (let up = groups.find((x) => x.id === first.parent); up; up = groups.find((x) => x.id === up
|
|
29244
|
-
|
|
29322
|
+
for (let up = groups.find((x) => x.id === first.parent); up; up = groups.find((x) => x.id === up?.parent)) {
|
|
29323
|
+
const set2 = up.memberSet;
|
|
29324
|
+
if (g.members.every((x) => set2.has(x))) break;
|
|
29245
29325
|
first = up;
|
|
29246
29326
|
}
|
|
29247
29327
|
problems.push(
|
|
@@ -29257,7 +29337,8 @@ function isAncestor(groups, ancestorId, id) {
|
|
|
29257
29337
|
let cur = groups.find((g) => g.id === id);
|
|
29258
29338
|
while (cur?.parent) {
|
|
29259
29339
|
if (cur.parent === ancestorId) return true;
|
|
29260
|
-
|
|
29340
|
+
const parent = cur.parent;
|
|
29341
|
+
cur = groups.find((g) => g.id === parent);
|
|
29261
29342
|
}
|
|
29262
29343
|
return false;
|
|
29263
29344
|
}
|
|
@@ -29267,7 +29348,7 @@ function detailLinesFor(detail, width) {
|
|
|
29267
29348
|
const lines = [];
|
|
29268
29349
|
for (let i = 0; i < parts.length; i++) {
|
|
29269
29350
|
if (lines.length === 2) {
|
|
29270
|
-
const last = lines.pop().replace(/…$/, "");
|
|
29351
|
+
const last = (lines.pop() ?? "").replace(/…$/, "");
|
|
29271
29352
|
lines.push(wrapText(`${last} ${parts.slice(i).join(" ")}`, SIZES.detail, width, 1, true)[0]);
|
|
29272
29353
|
break;
|
|
29273
29354
|
}
|
|
@@ -29311,7 +29392,7 @@ function runLayout(model, rankdir) {
|
|
|
29311
29392
|
for (const grp of model.groups) g.setNode(`group:${grp.id}`, {});
|
|
29312
29393
|
for (const grp of model.groups) {
|
|
29313
29394
|
if (grp.parent) g.setParent(`group:${grp.id}`, `group:${grp.parent}`);
|
|
29314
|
-
for (const m of grp.directMembers) g.setParent(m, `group:${grp.id}`);
|
|
29395
|
+
for (const m of grp.directMembers ?? []) g.setParent(m, `group:${grp.id}`);
|
|
29315
29396
|
}
|
|
29316
29397
|
const labels = new Map(model.edges.map((e) => [e.id, sizeEdgeLabel(e.label)]));
|
|
29317
29398
|
for (const e of model.edges) {
|
|
@@ -29351,8 +29432,10 @@ function runLayout(model, rankdir) {
|
|
|
29351
29432
|
const cx = n.x + n.width / 2;
|
|
29352
29433
|
const cy = n.y + n.height / 2;
|
|
29353
29434
|
const placed = Number.isFinite(d.x) && Number.isFinite(d.y);
|
|
29435
|
+
const dx = d.x;
|
|
29436
|
+
const dy = d.y;
|
|
29354
29437
|
if (rankdir === "TB") {
|
|
29355
|
-
const lx = placed ?
|
|
29438
|
+
const lx = placed ? dx : r + 30 + (e.labelBox ? e.labelBox.width / 2 : 0);
|
|
29356
29439
|
const reach = Math.max(r + 18, e.labelBox ? lx - e.labelBox.width / 2 - 3 : lx);
|
|
29357
29440
|
const y1 = cy - n.height * 0.22;
|
|
29358
29441
|
const y2 = cy + n.height * 0.22;
|
|
@@ -29364,7 +29447,7 @@ function runLayout(model, rankdir) {
|
|
|
29364
29447
|
];
|
|
29365
29448
|
if (e.labelBox) e.labelBox = { ...e.labelBox, cx: Math.max(lx, reach + 3 + e.labelBox.width / 2), cy };
|
|
29366
29449
|
} else {
|
|
29367
|
-
const ly = placed ?
|
|
29450
|
+
const ly = placed ? dy : b + 30 + (e.labelBox ? e.labelBox.height / 2 : 0);
|
|
29368
29451
|
const reach = Math.max(b + 16, e.labelBox ? ly - e.labelBox.height / 2 - 3 : ly);
|
|
29369
29452
|
const spread = Math.min(30, n.width * 0.3);
|
|
29370
29453
|
e.points = [
|
|
@@ -29431,7 +29514,7 @@ function architectureNotes(props) {
|
|
|
29431
29514
|
return notes;
|
|
29432
29515
|
}
|
|
29433
29516
|
|
|
29434
|
-
// src/shared/check-program.
|
|
29517
|
+
// src/shared/check-program.ts
|
|
29435
29518
|
function elementsOf(root, typeName) {
|
|
29436
29519
|
const out = [];
|
|
29437
29520
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -29440,8 +29523,9 @@ function elementsOf(root, typeName) {
|
|
|
29440
29523
|
const v = stack.pop();
|
|
29441
29524
|
if (!v || typeof v !== "object" || seen.has(v)) continue;
|
|
29442
29525
|
seen.add(v);
|
|
29443
|
-
|
|
29444
|
-
|
|
29526
|
+
const node2 = v;
|
|
29527
|
+
if (node2.typeName === typeName && node2.props) out.push(node2);
|
|
29528
|
+
for (const child of Array.isArray(v) ? v : Object.values(node2.props || {})) stack.push(child);
|
|
29445
29529
|
}
|
|
29446
29530
|
return out;
|
|
29447
29531
|
}
|
|
@@ -29452,10 +29536,7 @@ function checkProgram(program, { parser, lineOf = () => void 0, decorate = (entr
|
|
|
29452
29536
|
try {
|
|
29453
29537
|
parsed = parser().parse(program);
|
|
29454
29538
|
} catch (err) {
|
|
29455
|
-
errors.push({ code: "parse-failed", message: String(
|
|
29456
|
-
/** @type {any} */
|
|
29457
|
-
err?.message || err
|
|
29458
|
-
) });
|
|
29539
|
+
errors.push({ code: "parse-failed", message: String((err && typeof err === "object" && "message" in err ? err.message : void 0) || err) });
|
|
29459
29540
|
return { statements: 0, errors, warnings };
|
|
29460
29541
|
}
|
|
29461
29542
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -29513,22 +29594,24 @@ function checkProgram(program, { parser, lineOf = () => void 0, decorate = (entr
|
|
|
29513
29594
|
return { statements: parsed.meta.statementCount, errors, warnings };
|
|
29514
29595
|
}
|
|
29515
29596
|
|
|
29516
|
-
// src/cli/check.
|
|
29597
|
+
// src/cli/check.ts
|
|
29517
29598
|
var coreSpec = null;
|
|
29518
29599
|
var packParsers = /* @__PURE__ */ new Map();
|
|
29519
29600
|
var customParsers = /* @__PURE__ */ new WeakMap();
|
|
29520
29601
|
function getParser(schema, packs = []) {
|
|
29521
29602
|
if (schema) {
|
|
29522
|
-
|
|
29523
|
-
|
|
29603
|
+
let custom2 = customParsers.get(schema);
|
|
29604
|
+
if (!custom2) customParsers.set(schema, custom2 = createParser(schema, schema.root || "Stack"));
|
|
29605
|
+
return custom2;
|
|
29524
29606
|
}
|
|
29525
29607
|
const key2 = packs.map((p) => `${p.name}@${p.version}:${p.integrity || ""}`).join(",");
|
|
29526
|
-
|
|
29527
|
-
|
|
29528
|
-
const
|
|
29529
|
-
|
|
29608
|
+
let parser = packParsers.get(key2);
|
|
29609
|
+
if (!parser) {
|
|
29610
|
+
const core2 = coreSpec ||= JSON.parse(fs3.readFileSync(assetPath("spec.json"), "utf8"));
|
|
29611
|
+
const spec = mergePackSchemas(core2, packs.map((p) => p.catalog));
|
|
29612
|
+
packParsers.set(key2, parser = createParser(spec, spec.root || "Stack"));
|
|
29530
29613
|
}
|
|
29531
|
-
return
|
|
29614
|
+
return parser;
|
|
29532
29615
|
}
|
|
29533
29616
|
function unknownComponent(entry, packs, file3) {
|
|
29534
29617
|
const name = entry.component;
|
|
@@ -29561,7 +29644,7 @@ function statementLines(html) {
|
|
|
29561
29644
|
}
|
|
29562
29645
|
function checkHtml(html, { schema, file: file3 = "<file>" } = {}) {
|
|
29563
29646
|
const art = readArtifact(html);
|
|
29564
|
-
const packs = schema ? { installed: [], missing: [], mismatched: [], stale: [], declared: [] } : pagePacks(html);
|
|
29647
|
+
const packs = schema ? { installed: [], missing: [], mismatched: [], stale: [], declared: [], thirdParty: [] } : pagePacks(html);
|
|
29565
29648
|
const result = { ok: true, errors: [], warnings: [], statements: 0, title: art.title, hasMermaid: false };
|
|
29566
29649
|
if (art.program == null) {
|
|
29567
29650
|
result.ok = false;
|
|
@@ -29609,7 +29692,7 @@ function checkHtml(html, { schema, file: file3 = "<file>" } = {}) {
|
|
|
29609
29692
|
return result;
|
|
29610
29693
|
}
|
|
29611
29694
|
|
|
29612
|
-
// src/cli/main.
|
|
29695
|
+
// src/cli/main.ts
|
|
29613
29696
|
init_client();
|
|
29614
29697
|
init_hub();
|
|
29615
29698
|
init_invites();
|
|
@@ -29617,7 +29700,281 @@ init_lock();
|
|
|
29617
29700
|
init_sessions();
|
|
29618
29701
|
init_export();
|
|
29619
29702
|
|
|
29620
|
-
// src/cli/
|
|
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
|
+
|
|
29977
|
+
// src/cli/output.ts
|
|
29621
29978
|
var PLAIN = /^[A-Za-z0-9_./@:+-][A-Za-z0-9 _./@:+(),=<>*#%?!'-]*$/;
|
|
29622
29979
|
var json2 = (v) => JSON.stringify(v).replace(/[\u0085\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
29623
29980
|
function scalar(v) {
|
|
@@ -29629,13 +29986,14 @@ function scalar(v) {
|
|
|
29629
29986
|
return json2(s);
|
|
29630
29987
|
}
|
|
29631
29988
|
function isPlainObject2(v) {
|
|
29632
|
-
return v && typeof v === "object" && !Array.isArray(v);
|
|
29989
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
29633
29990
|
}
|
|
29991
|
+
var isList = (v) => Array.isArray(v);
|
|
29634
29992
|
var KEY = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
|
|
29635
29993
|
var key = (k) => KEY.test(k) ? k : json2(k);
|
|
29636
29994
|
function emit(value, indent, lines) {
|
|
29637
29995
|
const pad = " ".repeat(indent);
|
|
29638
|
-
if (
|
|
29996
|
+
if (isList(value)) {
|
|
29639
29997
|
if (!value.length) return;
|
|
29640
29998
|
for (const item of value) {
|
|
29641
29999
|
if (isPlainObject2(item)) {
|
|
@@ -29655,7 +30013,7 @@ function emit(value, indent, lines) {
|
|
|
29655
30013
|
}
|
|
29656
30014
|
for (const [k, v] of Object.entries(value)) {
|
|
29657
30015
|
if (v === void 0) continue;
|
|
29658
|
-
if (
|
|
30016
|
+
if (isList(v)) {
|
|
29659
30017
|
if (!v.length) continue;
|
|
29660
30018
|
if (v.every((x) => !isPlainObject2(x) && !Array.isArray(x)) && v.join(", ").length < 100) {
|
|
29661
30019
|
lines.push(`${pad}${key(k)}: [${v.map(scalar).join(", ")}]`);
|
|
@@ -29694,10 +30052,10 @@ function print(obj) {
|
|
|
29694
30052
|
process.stdout.write(format(obj) + "\n");
|
|
29695
30053
|
}
|
|
29696
30054
|
|
|
29697
|
-
// src/shared/feedback.
|
|
30055
|
+
// src/shared/feedback.ts
|
|
29698
30056
|
function locator(lines) {
|
|
29699
30057
|
return (id) => {
|
|
29700
|
-
const l = lines.get(id);
|
|
30058
|
+
const l = id === void 0 ? void 0 : lines.get(id);
|
|
29701
30059
|
if (!l) return {};
|
|
29702
30060
|
const first = l.text.split("\n")[0];
|
|
29703
30061
|
return { line: l.line, source: first.length > 110 ? first.slice(0, 109) + "\u2026" : first };
|
|
@@ -29726,10 +30084,10 @@ function describeItem(it2, at2) {
|
|
|
29726
30084
|
return { message: it2.text ?? it2.note ?? "" };
|
|
29727
30085
|
}
|
|
29728
30086
|
|
|
29729
|
-
// src/cli/main.
|
|
30087
|
+
// src/cli/main.ts
|
|
29730
30088
|
init_paths();
|
|
29731
30089
|
|
|
29732
|
-
// src/cli/components.
|
|
30090
|
+
// src/cli/components.ts
|
|
29733
30091
|
init_packs();
|
|
29734
30092
|
init_catalog();
|
|
29735
30093
|
var BIN = "austere-axi";
|
|
@@ -29826,9 +30184,13 @@ function showComponents(names) {
|
|
|
29826
30184
|
return out;
|
|
29827
30185
|
}
|
|
29828
30186
|
|
|
29829
|
-
// src/cli/main.
|
|
30187
|
+
// src/cli/main.ts
|
|
29830
30188
|
init_packs();
|
|
29831
30189
|
process.env.OPENUI_TELEMETRY_DISABLED = process.env.OPENUI_TELEMETRY_DISABLED || "1";
|
|
30190
|
+
function messageOf(err) {
|
|
30191
|
+
const e = err;
|
|
30192
|
+
return e.message;
|
|
30193
|
+
}
|
|
29832
30194
|
var BIN2 = "austere-axi";
|
|
29833
30195
|
var HELP = `${BIN2} ${VERSION} \u2014 review artifacts written in OpenUI Lang. You write a short
|
|
29834
30196
|
program instead of a whole HTML page; the reviewer annotates it in the browser;
|
|
@@ -29845,85 +30207,69 @@ WORKFLOW
|
|
|
29845
30207
|
intro = Card([CardHeader("Title", "Subtitle"), introText])
|
|
29846
30208
|
introText = MarkDownRenderer("...")
|
|
29847
30209
|
</script></body></html>
|
|
29848
|
-
|
|
29849
|
-
|
|
29850
|
-
|
|
29851
|
-
|
|
29852
|
-
|
|
29853
|
-
|
|
29854
|
-
|
|
29855
|
-
|
|
29856
|
-
|
|
29857
|
-
|
|
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:
|
|
29858
30222
|
${BIN2} poll <file.html> --cursor <n> --agent-reply "one line: what changed"
|
|
29859
|
-
|
|
29860
|
-
without either returns it again, so feedback lost on the way is not lost.
|
|
29861
|
-
6. On status: ended, stop polling and continue in chat. Never reopen uninvited. Notes
|
|
29862
|
-
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.
|
|
29863
30224
|
|
|
29864
|
-
FEEDBACK (
|
|
29865
|
-
status: feedback | ended | browser_disconnected | interrupted | replaced, plus
|
|
29866
|
-
cursor:
|
|
29867
|
-
items,
|
|
29868
|
-
|
|
29869
|
-
|
|
29870
|
-
part: the exact piece clicked, e.g. a diff file:+line or a diagram node), note
|
|
29871
|
-
action: <@ToAssistant message> form: {<formName>: {<field>: value}}, context
|
|
29872
|
-
message: <free text from the composer>
|
|
29873
|
-
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
|
|
29874
30231
|
|
|
29875
30232
|
CHECK verifies syntax, component names, required props, unresolved references and
|
|
29876
|
-
statements unreachable from root.
|
|
29877
|
-
show in the reviewer's panel and can come back as render_error. CHECK does not judge how
|
|
29878
|
-
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
|
|
29879
30234
|
page (it would scroll sideways).
|
|
29880
30235
|
|
|
29881
|
-
|
|
29882
|
-
|
|
29883
|
-
|
|
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).
|
|
30241
|
+
|
|
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")}
|
|
29884
30259
|
|
|
29885
|
-
|
|
29886
|
-
|
|
29887
|
-
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
everything after n again. One poll per session; --takeover
|
|
29891
|
-
replaces a stuck one
|
|
29892
|
-
${BIN2} reply <file> post a reply without polling --agent-reply "..." | --agent-reply-file f|- [--cursor n]
|
|
29893
|
-
${BIN2} end <file> stop as the agent; the reviewer can still add notes for later
|
|
29894
|
-
${BIN2} close <file> close the review for the owner: no more notes until reopen
|
|
29895
|
-
${BIN2} reopen <file> reopen an ended or closed review
|
|
29896
|
-
${BIN2} login [file] single-use link that signs a browser in as you (SSH, --no-open,
|
|
29897
|
-
another browser), to the file's review or the session list
|
|
29898
|
-
${BIN2} feedback <file> feedback no agent has answered yet (sent after polling stopped,
|
|
29899
|
-
or in an earlier session) [--all] [--peek]; answer with reply
|
|
29900
|
-
${BIN2} check <file> validate the program (no browser) [--fix: declare the packs it uses]
|
|
29901
|
-
${BIN2} patch <file> merge statements from stdin into the program: same name
|
|
29902
|
-
replaces, new name adds, \`name = null\` deletes;
|
|
29903
|
-
reports added statements dropped as unreferenced
|
|
29904
|
-
${BIN2} spec [group|pack...] component reference (groups: Layout Content Data Charts Forms Buttons)
|
|
29905
|
-
${BIN2} components [group] every component, including ones spec leaves out
|
|
29906
|
-
${BIN2} components search <words> | show <Name...>
|
|
29907
|
-
${BIN2} components add <npm-package|folder|file.tgz> [--trust] | remove <package>
|
|
29908
|
-
packs add components; a page that uses one declares it
|
|
29909
|
-
(check --fix writes the <meta name="austere-packs"> tag)
|
|
29910
|
-
${BIN2} components new <dir> | build [dir] write your own pack
|
|
29911
|
-
${BIN2} export <file> single self-contained HTML [--out path]
|
|
29912
|
-
${BIN2} hub on|off|status|list|notes|open the owner's home page: every artifact by project/date, every note
|
|
29913
|
-
${BIN2} share <file> --as "<name>" [--auto] invite link for a teammate on your tailnet. Their notes
|
|
29914
|
-
wait for the owner to send them on; --auto skips the wait
|
|
29915
|
-
${BIN2} share list [--links] | revoke <name|id>
|
|
29916
|
-
${BIN2} stop stop the background server
|
|
29917
|
-
${BIN2} server [--persist] run the server in the foreground; --persist: never idle out
|
|
29918
|
-
(behind a reverse proxy, under systemd)
|
|
29919
|
-
${BIN2} mcp MCP server on stdio: spec check open poll reply patch end as tools
|
|
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).
|
|
29920
30265
|
|
|
29921
30266
|
ENV
|
|
29922
30267
|
AUSTERE_AXI_PORT (4388) AUSTERE_AXI_STATE_DIR (~/.austere-axi) AUSTERE_AXI_NO_OPEN=1
|
|
29923
30268
|
AUSTERE_AXI_HOST AUSTERE_AXI_NO_TAILSCALE=1 AUSTERE_AXI_DISCONNECT_GRACE_MS (300000)
|
|
29924
30269
|
AUSTERE_AXI_IDLE_TIMEOUT_MS (1800000) AUSTERE_AXI_PERSIST=1 AUSTERE_AXI_ALLOWED_HOSTS AUSTERE_AXI_LINK_URL
|
|
29925
|
-
AUSTERE_AXI_NPM_REGISTRY
|
|
30270
|
+
AUSTERE_AXI_NPM_REGISTRY CHROME_PATH (check --render)
|
|
29926
30271
|
`;
|
|
30272
|
+
}
|
|
29927
30273
|
var COMMANDS = {
|
|
29928
30274
|
open: {
|
|
29929
30275
|
usage: "<file> [--no-open] [--reopen]",
|
|
@@ -29971,9 +30317,12 @@ var COMMANDS = {
|
|
|
29971
30317
|
example: "feedback plan.html"
|
|
29972
30318
|
},
|
|
29973
30319
|
check: {
|
|
29974
|
-
usage: "check <file> [--fix]",
|
|
30320
|
+
usage: "check <file> [--fix] [--render]",
|
|
29975
30321
|
summary: "validate the program without a browser: syntax, components, props, references, reachability",
|
|
29976
|
-
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
|
+
},
|
|
29977
30326
|
example: "check plan.html"
|
|
29978
30327
|
},
|
|
29979
30328
|
patch: {
|
|
@@ -30043,7 +30392,7 @@ function parseArgs(argv) {
|
|
|
30043
30392
|
if (i + 1 >= argv.length) return { flags, pos, error: `--${key2} needs a value` };
|
|
30044
30393
|
val = argv[++i];
|
|
30045
30394
|
}
|
|
30046
|
-
if (key2 === "also-listen") (flags[
|
|
30395
|
+
if (key2 === "also-listen") (flags["also-listen"] ||= []).push(String(val));
|
|
30047
30396
|
else flags[key2] = val;
|
|
30048
30397
|
} else if (a === "-h") flags.help = true;
|
|
30049
30398
|
else if (a === "-v" || a === "-V") flags.version = true;
|
|
@@ -30078,7 +30427,7 @@ function needFile(file3, cmd) {
|
|
|
30078
30427
|
return null;
|
|
30079
30428
|
}
|
|
30080
30429
|
const abs = canonicalFile(file3);
|
|
30081
|
-
if (!
|
|
30430
|
+
if (!fs15.existsSync(abs)) {
|
|
30082
30431
|
fail(`no such file: ${abs}`);
|
|
30083
30432
|
return null;
|
|
30084
30433
|
}
|
|
@@ -30095,7 +30444,7 @@ function readStdin() {
|
|
|
30095
30444
|
async function replyText(flags) {
|
|
30096
30445
|
if (typeof flags["agent-reply-file"] === "string") {
|
|
30097
30446
|
const f = flags["agent-reply-file"];
|
|
30098
|
-
return (f === "-" ? await readStdin() :
|
|
30447
|
+
return (f === "-" ? await readStdin() : fs15.readFileSync(f, "utf8")).trim();
|
|
30099
30448
|
}
|
|
30100
30449
|
if (typeof flags["agent-reply"] === "string") return flags["agent-reply"].trim();
|
|
30101
30450
|
return "";
|
|
@@ -30133,8 +30482,8 @@ async function cmdHome() {
|
|
|
30133
30482
|
}
|
|
30134
30483
|
async function cmdOpen(file3, flags) {
|
|
30135
30484
|
const abs = needFile(file3, "open");
|
|
30136
|
-
if (!abs) return;
|
|
30137
|
-
const html =
|
|
30485
|
+
if (!abs || file3 === void 0) return;
|
|
30486
|
+
const html = fs15.readFileSync(abs, "utf8");
|
|
30138
30487
|
const check2 = checkHtml(html, { file: quote(file3) });
|
|
30139
30488
|
await ensureServer();
|
|
30140
30489
|
const r = await request("POST", "/api/open", { file: abs, reopen: !!flags.reopen });
|
|
@@ -30173,14 +30522,15 @@ function quote(file3) {
|
|
|
30173
30522
|
function describeItems(abs, items) {
|
|
30174
30523
|
let lines = /* @__PURE__ */ new Map();
|
|
30175
30524
|
try {
|
|
30176
|
-
lines = statementLines(
|
|
30525
|
+
lines = statementLines(fs15.readFileSync(abs, "utf8"));
|
|
30177
30526
|
} catch {
|
|
30178
30527
|
}
|
|
30179
30528
|
const at2 = locator(lines);
|
|
30180
30529
|
return items.map((it2) => {
|
|
30181
30530
|
let o;
|
|
30182
30531
|
try {
|
|
30183
|
-
|
|
30532
|
+
const item = it2;
|
|
30533
|
+
o = describeItem(item, at2);
|
|
30184
30534
|
} catch {
|
|
30185
30535
|
o = { malformed: `could not read this ${typeof it2?.kind === "string" ? it2.kind : "feedback"} item` };
|
|
30186
30536
|
}
|
|
@@ -30189,14 +30539,14 @@ function describeItems(abs, items) {
|
|
|
30189
30539
|
}
|
|
30190
30540
|
function thirdPartyOf(abs) {
|
|
30191
30541
|
try {
|
|
30192
|
-
return pagePacks(
|
|
30542
|
+
return pagePacks(fs15.readFileSync(abs, "utf8")).thirdParty.map((p) => `${p.name}@${p.version}`);
|
|
30193
30543
|
} catch {
|
|
30194
30544
|
return [];
|
|
30195
30545
|
}
|
|
30196
30546
|
}
|
|
30197
30547
|
var NEXT = {
|
|
30198
|
-
feedback: (f, cursor) => `
|
|
30199
|
-
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.`,
|
|
30200
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\`).`,
|
|
30201
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.`,
|
|
30202
30552
|
replaced: () => "Another poll took over this session. Stop polling here.",
|
|
@@ -30204,17 +30554,18 @@ var NEXT = {
|
|
|
30204
30554
|
timeout: (f) => `No feedback yet. Run \`${BIN2} poll ${f}\` again.`
|
|
30205
30555
|
};
|
|
30206
30556
|
function seqFlag(flags, name) {
|
|
30207
|
-
|
|
30557
|
+
const v = flags[name];
|
|
30558
|
+
return typeof v === "string" ? v : void 0;
|
|
30208
30559
|
}
|
|
30209
30560
|
async function cmdPoll(file3, flags) {
|
|
30210
30561
|
const abs = needFile(file3, "poll");
|
|
30211
|
-
if (!abs) return;
|
|
30562
|
+
if (!abs || file3 === void 0) return;
|
|
30212
30563
|
await ensureServer();
|
|
30213
30564
|
const reply = await replyText(flags);
|
|
30214
30565
|
if (!reply && (flags["agent-reply"] !== void 0 || flags["agent-reply-file"] !== void 0)) {
|
|
30215
30566
|
return fail("the agent reply is empty");
|
|
30216
30567
|
}
|
|
30217
|
-
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)
|
|
30218
30569
|
`);
|
|
30219
30570
|
const ticker = process.stderr.isTTY ? setInterval(() => process.stderr.write(`[${BIN2}] still waiting\u2026
|
|
30220
30571
|
`), 6e4) : null;
|
|
@@ -30229,7 +30580,7 @@ async function cmdPoll(file3, flags) {
|
|
|
30229
30580
|
takeover: !!flags.takeover,
|
|
30230
30581
|
timeoutMs: flags["timeout-ms"] ? Number(flags["timeout-ms"]) : void 0
|
|
30231
30582
|
});
|
|
30232
|
-
} catch
|
|
30583
|
+
} catch {
|
|
30233
30584
|
if (ticker) clearInterval(ticker);
|
|
30234
30585
|
return print({ status: "interrupted", file: abs, next: NEXT.interrupted(quote(file3)) });
|
|
30235
30586
|
}
|
|
@@ -30261,7 +30612,7 @@ async function cmdPoll(file3, flags) {
|
|
|
30261
30612
|
const out = { status: d.status || "unknown", file: abs };
|
|
30262
30613
|
if (d.endedBy) out.ended_by = d.endedBy;
|
|
30263
30614
|
if (d.closed) out.closed = "by the owner";
|
|
30264
|
-
if (NEXT[d.status]) out.next = NEXT[d.status](f);
|
|
30615
|
+
if (d.status && NEXT[d.status]) out.next = NEXT[d.status](f);
|
|
30265
30616
|
print(out);
|
|
30266
30617
|
}
|
|
30267
30618
|
async function cmdReply(file3, flags) {
|
|
@@ -30285,7 +30636,7 @@ async function cmdEnd(file3) {
|
|
|
30285
30636
|
}
|
|
30286
30637
|
async function cmdLock(file3, route) {
|
|
30287
30638
|
const abs = needFile(file3, route);
|
|
30288
|
-
if (!abs) return;
|
|
30639
|
+
if (!abs || file3 === void 0) return;
|
|
30289
30640
|
await ensureServer();
|
|
30290
30641
|
const r = await request("POST", `/api/${route}`, { file: abs }, { timeoutMs: 1e4 });
|
|
30291
30642
|
if (r.status === 404) return fail(`no review session for this file; open it first: ${BIN2} ${quote(file3)}`);
|
|
@@ -30311,13 +30662,13 @@ function fixPacks(abs, html) {
|
|
|
30311
30662
|
return { html, changes };
|
|
30312
30663
|
}
|
|
30313
30664
|
const out = setPacksMeta(html, next);
|
|
30314
|
-
if (out !== html)
|
|
30665
|
+
if (out !== html) fs15.writeFileSync(abs, out);
|
|
30315
30666
|
return { html: out, changes: changes.length ? changes : ["updated integrity hashes"] };
|
|
30316
30667
|
}
|
|
30317
30668
|
async function cmdCheck(file3, flags = {}) {
|
|
30318
30669
|
const abs = needFile(file3, "check");
|
|
30319
|
-
if (!abs) return;
|
|
30320
|
-
let html =
|
|
30670
|
+
if (!abs || file3 === void 0) return;
|
|
30671
|
+
let html = fs15.readFileSync(abs, "utf8");
|
|
30321
30672
|
let fixed = [];
|
|
30322
30673
|
if (flags.fix) ({ html, changes: fixed } = fixPacks(abs, html));
|
|
30323
30674
|
const result = checkHtml(html, { file: quote(file3) });
|
|
@@ -30326,17 +30677,38 @@ async function cmdCheck(file3, flags = {}) {
|
|
|
30326
30677
|
if (fixed.length) out.fixed = fixed;
|
|
30327
30678
|
if (result.errors.length) out.errors = result.errors;
|
|
30328
30679
|
if (result.warnings.length) out.warnings = result.warnings;
|
|
30329
|
-
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
|
+
}
|
|
30330
30691
|
print(out);
|
|
30331
|
-
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;
|
|
30332
30704
|
}
|
|
30333
30705
|
async function cmdPatch(file3) {
|
|
30334
30706
|
const abs = needFile(file3, "patch");
|
|
30335
|
-
if (!abs) return;
|
|
30707
|
+
if (!abs || file3 === void 0) return;
|
|
30336
30708
|
if (process.stdin.isTTY) return usage(`pipe the changed statements on stdin: ${BIN2} patch ${quote(file3)} <<'EOF' ... EOF`, "patch");
|
|
30337
30709
|
const patch = await readStdin();
|
|
30338
30710
|
if (!patch.trim()) return fail("stdin was empty");
|
|
30339
|
-
const html =
|
|
30711
|
+
const html = fs15.readFileSync(abs, "utf8");
|
|
30340
30712
|
const art = readArtifact(html);
|
|
30341
30713
|
if (art.program == null) return fail('no <script type="text/openui"> block to patch');
|
|
30342
30714
|
const merged = mergeStatements(art.program.trim(), patch.trim(), "root");
|
|
@@ -30344,7 +30716,7 @@ async function cmdPatch(file3) {
|
|
|
30344
30716
|
const patched = new Set(splitStatements(patch).filter((s) => !/=\s*null\s*;?$/.test(s.text)).map((s) => s.id));
|
|
30345
30717
|
const dropped = [...patched].filter((id) => !kept.has(id));
|
|
30346
30718
|
const next = replaceProgram(html, merged);
|
|
30347
|
-
|
|
30719
|
+
fs15.writeFileSync(abs, next);
|
|
30348
30720
|
const result = checkHtml(next, { file: quote(file3) });
|
|
30349
30721
|
await registerWithHub(abs, result.title, sessionId(abs));
|
|
30350
30722
|
const out = { status: "patched", file: abs };
|
|
@@ -30359,7 +30731,7 @@ function packSpec(p) {
|
|
|
30359
30731
|
return lines.join("\n") + "\n";
|
|
30360
30732
|
}
|
|
30361
30733
|
function cmdSpec(groups) {
|
|
30362
|
-
let text =
|
|
30734
|
+
let text = fs15.readFileSync(assetPath("prompt.md"), "utf8");
|
|
30363
30735
|
const packs = installedPacks();
|
|
30364
30736
|
const packArgs = groups.filter((g) => packs.some((p) => p.name === g));
|
|
30365
30737
|
if (packArgs.length) {
|
|
@@ -30393,14 +30765,14 @@ ${trimmed.join("\n")}
|
|
|
30393
30765
|
function cmdExport(file3, flags) {
|
|
30394
30766
|
const abs = needFile(file3, "export");
|
|
30395
30767
|
if (!abs) return;
|
|
30396
|
-
const out = typeof flags.out === "string" ?
|
|
30397
|
-
const { html, notices } = exportHtml(
|
|
30398
|
-
|
|
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);
|
|
30399
30771
|
print({ status: "exported", file: out, bytes: html.length, notices: notices.length ? notices : void 0 });
|
|
30400
30772
|
}
|
|
30401
30773
|
function printResult(out) {
|
|
30402
30774
|
print(out);
|
|
30403
|
-
if (out && out.status === "error") process.exitCode = 1;
|
|
30775
|
+
if (out && "status" in out && out.status === "error") process.exitCode = 1;
|
|
30404
30776
|
}
|
|
30405
30777
|
async function cmdComponents(sub, args, flags) {
|
|
30406
30778
|
switch (sub) {
|
|
@@ -30417,7 +30789,8 @@ async function cmdComponents(sub, args, flags) {
|
|
|
30417
30789
|
let r;
|
|
30418
30790
|
try {
|
|
30419
30791
|
r = await addPack2(args[0], { trust: !!flags.trust });
|
|
30420
|
-
} catch (
|
|
30792
|
+
} catch (caught) {
|
|
30793
|
+
const err = caught;
|
|
30421
30794
|
const out = { status: err.code === "untrusted" ? "needs --trust" : "error", error: err.message };
|
|
30422
30795
|
if (err.network) out.next = `Offline or behind a proxy? \`npm pack ${args[0]}\` downloads it; then \`${BIN2} components add ./<file>.tgz\`.`;
|
|
30423
30796
|
print(out);
|
|
@@ -30454,10 +30827,10 @@ async function cmdComponents(sub, args, flags) {
|
|
|
30454
30827
|
pack: r.name,
|
|
30455
30828
|
dir: r.dir,
|
|
30456
30829
|
files: r.files,
|
|
30457
|
-
next: `cd ${quote(
|
|
30830
|
+
next: `cd ${quote(path15.relative(process.cwd(), r.dir) || ".")} && npm install && npm run build, then \`${BIN2} components add .\``
|
|
30458
30831
|
});
|
|
30459
30832
|
} catch (err) {
|
|
30460
|
-
return fail(err
|
|
30833
|
+
return fail(messageOf(err));
|
|
30461
30834
|
}
|
|
30462
30835
|
}
|
|
30463
30836
|
case "build": {
|
|
@@ -30474,7 +30847,7 @@ async function cmdComponents(sub, args, flags) {
|
|
|
30474
30847
|
next: `\`${BIN2} components add ${quote(args[0] || ".")}\` to use it here (again after each build), then \`${BIN2} check --fix <file>\` on pages that use it.`
|
|
30475
30848
|
});
|
|
30476
30849
|
} catch (err) {
|
|
30477
|
-
return fail(err
|
|
30850
|
+
return fail(messageOf(err));
|
|
30478
30851
|
}
|
|
30479
30852
|
}
|
|
30480
30853
|
default: {
|
|
@@ -30491,7 +30864,7 @@ function shortDate(ms) {
|
|
|
30491
30864
|
}
|
|
30492
30865
|
async function cmdFeedback(file3, flags) {
|
|
30493
30866
|
const abs = needFile(file3, "feedback");
|
|
30494
|
-
if (!abs) return;
|
|
30867
|
+
if (!abs || file3 === void 0) return;
|
|
30495
30868
|
const opts = { all: !!flags.all, peek: !!flags.peek };
|
|
30496
30869
|
let entries = null;
|
|
30497
30870
|
let status = null;
|
|
@@ -30536,7 +30909,7 @@ async function cmdFeedback(file3, flags) {
|
|
|
30536
30909
|
file: abs,
|
|
30537
30910
|
review: status,
|
|
30538
30911
|
items,
|
|
30539
|
-
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.`
|
|
30540
30913
|
});
|
|
30541
30914
|
}
|
|
30542
30915
|
async function cmdHub(sub, args, flags) {
|
|
@@ -30565,7 +30938,7 @@ async function cmdHub(sub, args, flags) {
|
|
|
30565
30938
|
server: h ? `running on ${port()}${h.persist ? " (persist)" : ""}` : "not running"
|
|
30566
30939
|
};
|
|
30567
30940
|
if (hubEnabled() && h) out.url = url2;
|
|
30568
|
-
if (
|
|
30941
|
+
if (fs15.existsSync(hubDbFile())) {
|
|
30569
30942
|
try {
|
|
30570
30943
|
const hub = await Hub.open();
|
|
30571
30944
|
try {
|
|
@@ -30574,20 +30947,20 @@ async function cmdHub(sub, args, flags) {
|
|
|
30574
30947
|
hub.close();
|
|
30575
30948
|
}
|
|
30576
30949
|
} catch (err) {
|
|
30577
|
-
out.index = `${hubDbFile()} cannot be opened: ${err
|
|
30950
|
+
out.index = `${hubDbFile()} cannot be opened: ${messageOf(err)}`;
|
|
30578
30951
|
}
|
|
30579
30952
|
}
|
|
30580
30953
|
if (!hubEnabled()) out.next = `\`${BIN2} hub on\` shows a home page with every artifact and note.`;
|
|
30581
30954
|
return print(out);
|
|
30582
30955
|
}
|
|
30583
30956
|
case "list": {
|
|
30584
|
-
if (!
|
|
30585
|
-
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) : "";
|
|
30586
30959
|
let hub;
|
|
30587
30960
|
try {
|
|
30588
30961
|
hub = await Hub.open();
|
|
30589
30962
|
} catch (err) {
|
|
30590
|
-
return fail(`${hubDbFile()} cannot be opened: ${err
|
|
30963
|
+
return fail(`${hubDbFile()} cannot be opened: ${messageOf(err)}`);
|
|
30591
30964
|
}
|
|
30592
30965
|
let rows;
|
|
30593
30966
|
try {
|
|
@@ -30607,13 +30980,13 @@ async function cmdHub(sub, args, flags) {
|
|
|
30607
30980
|
});
|
|
30608
30981
|
}
|
|
30609
30982
|
case "notes": {
|
|
30610
|
-
if (!
|
|
30611
|
-
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) : "";
|
|
30612
30985
|
let hub;
|
|
30613
30986
|
try {
|
|
30614
30987
|
hub = await Hub.open();
|
|
30615
30988
|
} catch (err) {
|
|
30616
|
-
return fail(`${hubDbFile()} cannot be opened: ${err
|
|
30989
|
+
return fail(`${hubDbFile()} cannot be opened: ${messageOf(err)}`);
|
|
30617
30990
|
}
|
|
30618
30991
|
let rows;
|
|
30619
30992
|
try {
|
|
@@ -30628,7 +31001,7 @@ async function cmdHub(sub, args, flags) {
|
|
|
30628
31001
|
text: r.text || void 0,
|
|
30629
31002
|
target: r.target || void 0,
|
|
30630
31003
|
status: r.status === "addressed" ? "answered" : r.status === "held" ? "held" : "unanswered",
|
|
30631
|
-
page: r.title ||
|
|
31004
|
+
page: r.title || path15.basename(r.file),
|
|
30632
31005
|
file: r.file,
|
|
30633
31006
|
sent: shortDate(r.at)
|
|
30634
31007
|
})),
|
|
@@ -30721,7 +31094,7 @@ async function cmdLogin(file3) {
|
|
|
30721
31094
|
if (!abs) return;
|
|
30722
31095
|
}
|
|
30723
31096
|
await ensureServer();
|
|
30724
|
-
if (abs) {
|
|
31097
|
+
if (abs && file3) {
|
|
30725
31098
|
const r = await request("GET", "/api/sessions");
|
|
30726
31099
|
if (r.status !== 200) return fail(r.data?.error || `login failed (${r.status})`);
|
|
30727
31100
|
const s = (r.data.sessions || []).find((x) => x.file === abs);
|
|
@@ -30752,7 +31125,7 @@ async function main() {
|
|
|
30752
31125
|
let name = null;
|
|
30753
31126
|
if (cmd !== void 0) {
|
|
30754
31127
|
if (Object.hasOwn(COMMANDS, cmd)) name = cmd;
|
|
30755
|
-
else if (/\.html?$/i.test(cmd) ||
|
|
31128
|
+
else if (/\.html?$/i.test(cmd) || fs15.existsSync(cmd)) name = "open";
|
|
30756
31129
|
else if (flags.help) return process.stdout.write(HELP);
|
|
30757
31130
|
else return usage(`unknown command: ${cmd}. One of: ${Object.keys(COMMANDS).join(" ")}, or a page file`);
|
|
30758
31131
|
}
|
|
@@ -30808,14 +31181,16 @@ async function main() {
|
|
|
30808
31181
|
return cmdOpen(rest[0], flags);
|
|
30809
31182
|
case "help":
|
|
30810
31183
|
if (!rest[0]) return process.stdout.write(HELP);
|
|
30811
|
-
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");
|
|
30812
31186
|
return commandHelp(rest[0]);
|
|
30813
31187
|
default:
|
|
30814
31188
|
return cmdOpen(cmd, flags);
|
|
30815
31189
|
}
|
|
30816
31190
|
}
|
|
30817
|
-
main().catch((
|
|
30818
|
-
|
|
31191
|
+
main().catch((caught) => {
|
|
31192
|
+
const err = caught;
|
|
31193
|
+
process.stdout.write(format({ status: "error", error: String(err?.message || caught) }) + "\n");
|
|
30819
31194
|
process.exitCode = 1;
|
|
30820
31195
|
});
|
|
30821
31196
|
/*! Bundled license information:
|