agentimization 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -8
- package/dist/index.js +626 -307
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/agentimization)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
╰───────────────────────────────────────────────╯
|
|
12
|
-
```
|
|
5
|
+
<p align="center">
|
|
6
|
+
<picture>
|
|
7
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/antlio/agentimization/main/assets/hero-loading-dark.svg">
|
|
8
|
+
<img src="https://raw.githubusercontent.com/antlio/agentimization/main/assets/hero-loading-light.svg" alt="agentimization" width="620">
|
|
9
|
+
</picture>
|
|
10
|
+
</p>
|
|
13
11
|
|
|
14
12
|
geo audit for agent-ready websites and projects.
|
|
15
13
|
|
package/dist/index.js
CHANGED
|
@@ -4105,6 +4105,140 @@ var DEFAULT_CONFIG = {
|
|
|
4105
4105
|
onEvent: () => {
|
|
4106
4106
|
}
|
|
4107
4107
|
};
|
|
4108
|
+
var stripHtml = (html) => html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
4109
|
+
var extractMarkdownLinks = (markdown) => {
|
|
4110
|
+
const links = [];
|
|
4111
|
+
const linkRegex = /\[.+?\]\(([^)]+)\)/g;
|
|
4112
|
+
let match;
|
|
4113
|
+
while ((match = linkRegex.exec(markdown)) !== null) {
|
|
4114
|
+
links.push(match[1]);
|
|
4115
|
+
}
|
|
4116
|
+
return links;
|
|
4117
|
+
};
|
|
4118
|
+
var extractLinks = (html, baseUrl) => {
|
|
4119
|
+
const links = [];
|
|
4120
|
+
const linkRegex = /<a[^>]+href=["']([^"']+)["']/gi;
|
|
4121
|
+
let match;
|
|
4122
|
+
while ((match = linkRegex.exec(html)) !== null) {
|
|
4123
|
+
try {
|
|
4124
|
+
const resolved = new URL(match[1], baseUrl).href;
|
|
4125
|
+
links.push(resolved);
|
|
4126
|
+
} catch {
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
return links;
|
|
4130
|
+
};
|
|
4131
|
+
var extractMetaTags = (html) => {
|
|
4132
|
+
const meta = {};
|
|
4133
|
+
const metaRegex = /<meta[^>]+(?:name|property)=["']([^"']+)["'][^>]+content=["']([^"']+)["']/gi;
|
|
4134
|
+
let match;
|
|
4135
|
+
while ((match = metaRegex.exec(html)) !== null) {
|
|
4136
|
+
meta[match[1].toLowerCase()] = match[2];
|
|
4137
|
+
}
|
|
4138
|
+
const metaRegex2 = /<meta[^>]+content=["']([^"']+)["'][^>]+(?:name|property)=["']([^"']+)["']/gi;
|
|
4139
|
+
while ((match = metaRegex2.exec(html)) !== null) {
|
|
4140
|
+
meta[match[2].toLowerCase()] = match[1];
|
|
4141
|
+
}
|
|
4142
|
+
return meta;
|
|
4143
|
+
};
|
|
4144
|
+
var extractJsonLd = (html) => {
|
|
4145
|
+
const results = [];
|
|
4146
|
+
const regex = /<script[^>]+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
4147
|
+
let match;
|
|
4148
|
+
while ((match = regex.exec(html)) !== null) {
|
|
4149
|
+
try {
|
|
4150
|
+
results.push(JSON.parse(match[1]));
|
|
4151
|
+
} catch {
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
return results;
|
|
4155
|
+
};
|
|
4156
|
+
var readAttr = (attrs, name) => {
|
|
4157
|
+
const re = new RegExp(`\\b${name}=(?:"([^"]*)"|'([^']*)')`, "i");
|
|
4158
|
+
const m = attrs.match(re);
|
|
4159
|
+
if (!m) return void 0;
|
|
4160
|
+
return m[1] ?? m[2];
|
|
4161
|
+
};
|
|
4162
|
+
var extractImages = (html) => {
|
|
4163
|
+
const images = [];
|
|
4164
|
+
const imgRegex = /<img\b([^>]*)>/gi;
|
|
4165
|
+
let match;
|
|
4166
|
+
while ((match = imgRegex.exec(html)) !== null) {
|
|
4167
|
+
const attrs = match[1];
|
|
4168
|
+
const src = readAttr(attrs, "src");
|
|
4169
|
+
if (src === void 0) continue;
|
|
4170
|
+
images.push({ src, alt: readAttr(attrs, "alt") });
|
|
4171
|
+
}
|
|
4172
|
+
return images;
|
|
4173
|
+
};
|
|
4174
|
+
var extractHeadings = (html) => {
|
|
4175
|
+
const headings = [];
|
|
4176
|
+
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
4177
|
+
let match;
|
|
4178
|
+
while ((match = regex.exec(html)) !== null) {
|
|
4179
|
+
headings.push({
|
|
4180
|
+
level: parseInt(match[1], 10),
|
|
4181
|
+
text: stripHtml(match[2]).trim()
|
|
4182
|
+
});
|
|
4183
|
+
}
|
|
4184
|
+
return headings;
|
|
4185
|
+
};
|
|
4186
|
+
var hasServerRenderedContent = (html) => {
|
|
4187
|
+
const withoutScripts = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
4188
|
+
const textContent = stripHtml(withoutScripts);
|
|
4189
|
+
return textContent.length > 100;
|
|
4190
|
+
};
|
|
4191
|
+
var findContentStartPosition = (html) => {
|
|
4192
|
+
const markers = [
|
|
4193
|
+
/<main[\s>]/i,
|
|
4194
|
+
/<article[\s>]/i,
|
|
4195
|
+
/id=["']content["']/i,
|
|
4196
|
+
/id=["']main["']/i,
|
|
4197
|
+
/class=["'][^"']*content[^"']*["']/i,
|
|
4198
|
+
/role=["']main["']/i
|
|
4199
|
+
];
|
|
4200
|
+
for (const marker of markers) {
|
|
4201
|
+
const match = html.search(marker);
|
|
4202
|
+
if (match >= 0) {
|
|
4203
|
+
return match / html.length;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
const firstP = html.search(/<p[\s>]/i);
|
|
4207
|
+
if (firstP >= 0) {
|
|
4208
|
+
return firstP / html.length;
|
|
4209
|
+
}
|
|
4210
|
+
return 0.5;
|
|
4211
|
+
};
|
|
4212
|
+
var extractCodeFences = (markdown) => {
|
|
4213
|
+
const fences = [];
|
|
4214
|
+
const lines = markdown.split("\n");
|
|
4215
|
+
let inFence = false;
|
|
4216
|
+
let currentLang = "";
|
|
4217
|
+
for (const line of lines) {
|
|
4218
|
+
const openMatch = line.match(/^```(\w*)/);
|
|
4219
|
+
if (openMatch && !inFence) {
|
|
4220
|
+
inFence = true;
|
|
4221
|
+
currentLang = openMatch[1] ?? "";
|
|
4222
|
+
} else if (line.trim() === "```" && inFence) {
|
|
4223
|
+
fences.push({ lang: currentLang, closed: true });
|
|
4224
|
+
inFence = false;
|
|
4225
|
+
currentLang = "";
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
if (inFence) {
|
|
4229
|
+
fences.push({ lang: currentLang, closed: false });
|
|
4230
|
+
}
|
|
4231
|
+
return fences;
|
|
4232
|
+
};
|
|
4233
|
+
var parseSitemapUrls = (xml) => {
|
|
4234
|
+
const urls = [];
|
|
4235
|
+
const regex = /<loc>([^<]+)<\/loc>/gi;
|
|
4236
|
+
let match;
|
|
4237
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
4238
|
+
urls.push(match[1].trim());
|
|
4239
|
+
}
|
|
4240
|
+
return urls;
|
|
4241
|
+
};
|
|
4108
4242
|
var llmsTxtExists = {
|
|
4109
4243
|
id: "llms-txt-exists",
|
|
4110
4244
|
name: "llms.txt Exists",
|
|
@@ -4144,7 +4278,7 @@ var llmsTxtValid = {
|
|
|
4144
4278
|
name: "llms.txt Valid Structure",
|
|
4145
4279
|
category: "content-discoverability",
|
|
4146
4280
|
status: "skip",
|
|
4147
|
-
message: "Skipped
|
|
4281
|
+
message: "Skipped: no llms.txt found"
|
|
4148
4282
|
};
|
|
4149
4283
|
}
|
|
4150
4284
|
const issues = [];
|
|
@@ -4189,7 +4323,7 @@ var llmsTxtSize = {
|
|
|
4189
4323
|
name: "llms.txt Size",
|
|
4190
4324
|
category: "content-discoverability",
|
|
4191
4325
|
status: "skip",
|
|
4192
|
-
message: "Skipped
|
|
4326
|
+
message: "Skipped: no llms.txt found"
|
|
4193
4327
|
};
|
|
4194
4328
|
}
|
|
4195
4329
|
const size = ctx.llmsTxt.length;
|
|
@@ -4227,7 +4361,7 @@ var llmsTxtFreshness = {
|
|
|
4227
4361
|
name: "llms.txt Coverage",
|
|
4228
4362
|
category: "content-discoverability",
|
|
4229
4363
|
status: "skip",
|
|
4230
|
-
message: "Skipped
|
|
4364
|
+
message: "Skipped: no llms.txt found"
|
|
4231
4365
|
};
|
|
4232
4366
|
}
|
|
4233
4367
|
if (ctx.sitemapUrls.length === 0) {
|
|
@@ -4257,11 +4391,9 @@ var llmsTxtFreshness = {
|
|
|
4257
4391
|
return null;
|
|
4258
4392
|
}
|
|
4259
4393
|
};
|
|
4260
|
-
const linkRegex = /\[.+?\]\(([^)]+)\)/g;
|
|
4261
4394
|
const llmsKeys = /* @__PURE__ */ new Set();
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
const k = keyFor(match[1]);
|
|
4395
|
+
for (const link of extractMarkdownLinks(ctx.llmsTxt)) {
|
|
4396
|
+
const k = keyFor(link);
|
|
4265
4397
|
if (k) llmsKeys.add(k);
|
|
4266
4398
|
}
|
|
4267
4399
|
const sitemapKeys = /* @__PURE__ */ new Set();
|
|
@@ -4301,7 +4433,7 @@ var llmsTxtFreshness = {
|
|
|
4301
4433
|
category: "content-discoverability",
|
|
4302
4434
|
status: coveragePct >= 40 || freshnessPct >= 70 ? "warn" : "fail",
|
|
4303
4435
|
message: `${message}${missingFromLlms > 0 ? ` \xB7 ${missingFromLlms} sitemap pages not in llms.txt` : ""}${staleInLlms > 0 ? ` \xB7 ${staleInLlms} llms.txt links not in sitemap` : ""}`,
|
|
4304
|
-
suggestion: coveragePct < freshnessPct ? "Add missing sitemap pages to llms.txt to improve AI agent discoverability." : "Some llms.txt links aren't in the sitemap
|
|
4436
|
+
suggestion: coveragePct < freshnessPct ? "Add missing sitemap pages to llms.txt to improve AI agent discoverability." : "Some llms.txt links aren't in the sitemap. They may be stale or your sitemap may be incomplete.",
|
|
4305
4437
|
metadata: {
|
|
4306
4438
|
coveragePct,
|
|
4307
4439
|
freshnessPct,
|
|
@@ -4327,15 +4459,13 @@ var llmsTxtLinksResolve = {
|
|
|
4327
4459
|
name: "llms.txt Links Resolve",
|
|
4328
4460
|
category: "content-discoverability",
|
|
4329
4461
|
status: "skip",
|
|
4330
|
-
message: "Skipped
|
|
4462
|
+
message: "Skipped: no llms.txt found"
|
|
4331
4463
|
};
|
|
4332
4464
|
}
|
|
4333
|
-
const linkRegex = /\[.+?\]\(([^)]+)\)/g;
|
|
4334
4465
|
const urls = [];
|
|
4335
|
-
|
|
4336
|
-
while ((match = linkRegex.exec(ctx.llmsTxt)) !== null) {
|
|
4466
|
+
for (const link of extractMarkdownLinks(ctx.llmsTxt)) {
|
|
4337
4467
|
try {
|
|
4338
|
-
const resolved2 = new URL(
|
|
4468
|
+
const resolved2 = new URL(link, ctx.baseUrl.origin);
|
|
4339
4469
|
if (resolved2.origin === ctx.baseUrl.origin) {
|
|
4340
4470
|
urls.push(resolved2.href);
|
|
4341
4471
|
}
|
|
@@ -4376,7 +4506,7 @@ var llmsTxtLinksResolve = {
|
|
|
4376
4506
|
name: "llms.txt Links Resolve",
|
|
4377
4507
|
category: "content-discoverability",
|
|
4378
4508
|
status: "fail",
|
|
4379
|
-
message: `${resolved}/${sampled.length} sampled links resolve
|
|
4509
|
+
message: `${resolved}/${sampled.length} sampled links resolve, ${sampled.length - resolved} broken`,
|
|
4380
4510
|
suggestion: "Fix broken links in llms.txt. AI agents will fail to fetch these pages.",
|
|
4381
4511
|
metadata: { resolved, sampled: sampled.length, total: urls.length }
|
|
4382
4512
|
};
|
|
@@ -4395,15 +4525,10 @@ var llmsTxtLinksMarkdown = {
|
|
|
4395
4525
|
name: "llms.txt Links Markdown",
|
|
4396
4526
|
category: "content-discoverability",
|
|
4397
4527
|
status: "skip",
|
|
4398
|
-
message: "Skipped
|
|
4528
|
+
message: "Skipped: no llms.txt found"
|
|
4399
4529
|
};
|
|
4400
4530
|
}
|
|
4401
|
-
const
|
|
4402
|
-
const urls = [];
|
|
4403
|
-
let m;
|
|
4404
|
-
while ((m = linkRegex.exec(ctx.llmsTxt)) !== null) {
|
|
4405
|
-
urls.push(m[1]);
|
|
4406
|
-
}
|
|
4531
|
+
const urls = extractMarkdownLinks(ctx.llmsTxt);
|
|
4407
4532
|
if (urls.length === 0) {
|
|
4408
4533
|
return {
|
|
4409
4534
|
id: "llms-txt-links-markdown",
|
|
@@ -4451,7 +4576,7 @@ var llmsTxtLinksMarkdown = {
|
|
|
4451
4576
|
category: "content-discoverability",
|
|
4452
4577
|
status: "fail",
|
|
4453
4578
|
message: `Only ${mdLinks}/${urls.length} llms.txt links point to .md URLs (${pct}%)`,
|
|
4454
|
-
suggestion: "Most llms.txt links are HTML-only. Serve a markdown version at .md URLs and link to those
|
|
4579
|
+
suggestion: "Most llms.txt links are HTML-only. Serve a markdown version at .md URLs and link to those, so agents get cleaner content and fewer parse failures.",
|
|
4455
4580
|
metadata: { mdLinks, total: urls.length, pct }
|
|
4456
4581
|
};
|
|
4457
4582
|
}
|
|
@@ -4553,6 +4678,176 @@ var robotsTxtAgentRules = {
|
|
|
4553
4678
|
};
|
|
4554
4679
|
}
|
|
4555
4680
|
};
|
|
4681
|
+
var llmsFullExists = {
|
|
4682
|
+
id: "llms-full-exists",
|
|
4683
|
+
name: "llms-full.txt Exists",
|
|
4684
|
+
category: "content-discoverability",
|
|
4685
|
+
description: "Checks if llms-full.txt (the complete-content variant) is present at the site root",
|
|
4686
|
+
weight: 0.4,
|
|
4687
|
+
run: async (ctx) => {
|
|
4688
|
+
if (ctx.llmsFullTxt) {
|
|
4689
|
+
return {
|
|
4690
|
+
id: "llms-full-exists",
|
|
4691
|
+
name: "llms-full.txt Exists",
|
|
4692
|
+
category: "content-discoverability",
|
|
4693
|
+
status: "pass",
|
|
4694
|
+
message: ctx.mode === "local" ? "llms-full.txt found in project root" : `llms-full.txt found at ${ctx.baseUrl.origin}/llms-full.txt`
|
|
4695
|
+
};
|
|
4696
|
+
}
|
|
4697
|
+
return {
|
|
4698
|
+
id: "llms-full-exists",
|
|
4699
|
+
name: "llms-full.txt Exists",
|
|
4700
|
+
category: "content-discoverability",
|
|
4701
|
+
status: "info",
|
|
4702
|
+
message: "No llms-full.txt found (optional)",
|
|
4703
|
+
suggestion: "If your llms.txt is large or you want agents to get full content in one fetch, add a /llms-full.txt containing the concatenated markdown of your docs."
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4706
|
+
};
|
|
4707
|
+
var llmsFullValid = {
|
|
4708
|
+
id: "llms-full-valid",
|
|
4709
|
+
name: "llms-full.txt Valid Structure",
|
|
4710
|
+
category: "content-discoverability",
|
|
4711
|
+
description: "Checks if llms-full.txt has recognizable markdown structure (headings, content)",
|
|
4712
|
+
weight: 0.4,
|
|
4713
|
+
run: async (ctx) => {
|
|
4714
|
+
if (!ctx.llmsFullTxt) {
|
|
4715
|
+
return {
|
|
4716
|
+
id: "llms-full-valid",
|
|
4717
|
+
name: "llms-full.txt Valid Structure",
|
|
4718
|
+
category: "content-discoverability",
|
|
4719
|
+
status: "skip",
|
|
4720
|
+
message: "Skipped: no llms-full.txt found"
|
|
4721
|
+
};
|
|
4722
|
+
}
|
|
4723
|
+
const hasHeadings = /^#{1,3}\s+/m.test(ctx.llmsFullTxt);
|
|
4724
|
+
const hasProse = ctx.llmsFullTxt.length > 600;
|
|
4725
|
+
if (hasHeadings && hasProse) {
|
|
4726
|
+
return {
|
|
4727
|
+
id: "llms-full-valid",
|
|
4728
|
+
name: "llms-full.txt Valid Structure",
|
|
4729
|
+
category: "content-discoverability",
|
|
4730
|
+
status: "pass",
|
|
4731
|
+
message: "llms-full.txt has recognizable markdown structure"
|
|
4732
|
+
};
|
|
4733
|
+
}
|
|
4734
|
+
return {
|
|
4735
|
+
id: "llms-full-valid",
|
|
4736
|
+
name: "llms-full.txt Valid Structure",
|
|
4737
|
+
category: "content-discoverability",
|
|
4738
|
+
status: "warn",
|
|
4739
|
+
message: `llms-full.txt found but ${!hasHeadings ? "has no markdown headings" : "has little content"}`,
|
|
4740
|
+
suggestion: "llms-full.txt should contain the full markdown content of your docs, with headings, so agents can parse it."
|
|
4741
|
+
};
|
|
4742
|
+
}
|
|
4743
|
+
};
|
|
4744
|
+
var llmsFullSize = {
|
|
4745
|
+
id: "llms-full-size",
|
|
4746
|
+
name: "llms-full.txt Size",
|
|
4747
|
+
category: "content-discoverability",
|
|
4748
|
+
description: "Checks if llms-full.txt size is within the expected range (substantial but not excessive)",
|
|
4749
|
+
weight: 0.3,
|
|
4750
|
+
run: async (ctx) => {
|
|
4751
|
+
if (!ctx.llmsFullTxt) {
|
|
4752
|
+
return {
|
|
4753
|
+
id: "llms-full-size",
|
|
4754
|
+
name: "llms-full.txt Size",
|
|
4755
|
+
category: "content-discoverability",
|
|
4756
|
+
status: "skip",
|
|
4757
|
+
message: "Skipped: no llms-full.txt found"
|
|
4758
|
+
};
|
|
4759
|
+
}
|
|
4760
|
+
const size = ctx.llmsFullTxt.length;
|
|
4761
|
+
const MIN = 1e4;
|
|
4762
|
+
const MAX = 5e6;
|
|
4763
|
+
if (size >= MIN && size <= MAX) {
|
|
4764
|
+
return {
|
|
4765
|
+
id: "llms-full-size",
|
|
4766
|
+
name: "llms-full.txt Size",
|
|
4767
|
+
category: "content-discoverability",
|
|
4768
|
+
status: "pass",
|
|
4769
|
+
message: `llms-full.txt is ${size.toLocaleString()} characters (within expected range)`,
|
|
4770
|
+
metadata: { size }
|
|
4771
|
+
};
|
|
4772
|
+
}
|
|
4773
|
+
return {
|
|
4774
|
+
id: "llms-full-size",
|
|
4775
|
+
name: "llms-full.txt Size",
|
|
4776
|
+
category: "content-discoverability",
|
|
4777
|
+
status: "warn",
|
|
4778
|
+
message: size < MIN ? `llms-full.txt is only ${size.toLocaleString()} characters, smaller than expected for a full-content file` : `llms-full.txt is ${size.toLocaleString()} characters, large enough to overflow agent context windows`,
|
|
4779
|
+
suggestion: size < MIN ? "llms-full.txt should contain your complete documentation. If it's this small, llms.txt alone may be enough." : "Consider trimming llms-full.txt or splitting content so agents can fetch what fits their context window.",
|
|
4780
|
+
metadata: { size }
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4783
|
+
};
|
|
4784
|
+
var llmsFullLinksResolve = {
|
|
4785
|
+
id: "llms-full-links-resolve",
|
|
4786
|
+
name: "llms-full.txt Links Resolve",
|
|
4787
|
+
category: "content-discoverability",
|
|
4788
|
+
description: "Checks if links in llms-full.txt return 200 OK",
|
|
4789
|
+
weight: 0.4,
|
|
4790
|
+
requiresNetwork: true,
|
|
4791
|
+
run: async (ctx) => {
|
|
4792
|
+
if (!ctx.llmsFullTxt) {
|
|
4793
|
+
return {
|
|
4794
|
+
id: "llms-full-links-resolve",
|
|
4795
|
+
name: "llms-full.txt Links Resolve",
|
|
4796
|
+
category: "content-discoverability",
|
|
4797
|
+
status: "skip",
|
|
4798
|
+
message: "Skipped: no llms-full.txt found"
|
|
4799
|
+
};
|
|
4800
|
+
}
|
|
4801
|
+
const urls = [];
|
|
4802
|
+
for (const link of extractMarkdownLinks(ctx.llmsFullTxt)) {
|
|
4803
|
+
try {
|
|
4804
|
+
const resolved2 = new URL(link, ctx.baseUrl.origin);
|
|
4805
|
+
if (resolved2.origin === ctx.baseUrl.origin) {
|
|
4806
|
+
urls.push(resolved2.href);
|
|
4807
|
+
}
|
|
4808
|
+
} catch {
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
if (urls.length === 0) {
|
|
4812
|
+
return {
|
|
4813
|
+
id: "llms-full-links-resolve",
|
|
4814
|
+
name: "llms-full.txt Links Resolve",
|
|
4815
|
+
category: "content-discoverability",
|
|
4816
|
+
status: "info",
|
|
4817
|
+
message: "No same-origin links found in llms-full.txt"
|
|
4818
|
+
};
|
|
4819
|
+
}
|
|
4820
|
+
const sampled = urls.slice(0, 10);
|
|
4821
|
+
const results = await Promise.allSettled(
|
|
4822
|
+
sampled.map(async (url) => {
|
|
4823
|
+
const resp = await fetch(url, { method: "HEAD", redirect: "follow" });
|
|
4824
|
+
return { url, status: resp.status };
|
|
4825
|
+
})
|
|
4826
|
+
);
|
|
4827
|
+
const resolved = results.filter(
|
|
4828
|
+
(r) => r.status === "fulfilled" && r.value.status >= 200 && r.value.status < 400
|
|
4829
|
+
).length;
|
|
4830
|
+
if (resolved === sampled.length) {
|
|
4831
|
+
return {
|
|
4832
|
+
id: "llms-full-links-resolve",
|
|
4833
|
+
name: "llms-full.txt Links Resolve",
|
|
4834
|
+
category: "content-discoverability",
|
|
4835
|
+
status: "pass",
|
|
4836
|
+
message: `All ${resolved} sampled same-origin links resolve (${urls.length} total links)`,
|
|
4837
|
+
metadata: { resolved, sampled: sampled.length, total: urls.length }
|
|
4838
|
+
};
|
|
4839
|
+
}
|
|
4840
|
+
return {
|
|
4841
|
+
id: "llms-full-links-resolve",
|
|
4842
|
+
name: "llms-full.txt Links Resolve",
|
|
4843
|
+
category: "content-discoverability",
|
|
4844
|
+
status: "fail",
|
|
4845
|
+
message: `${resolved}/${sampled.length} sampled links resolve, ${sampled.length - resolved} broken`,
|
|
4846
|
+
suggestion: "Fix broken links in llms-full.txt. AI agents will fail to fetch these pages.",
|
|
4847
|
+
metadata: { resolved, sampled: sampled.length, total: urls.length }
|
|
4848
|
+
};
|
|
4849
|
+
}
|
|
4850
|
+
};
|
|
4556
4851
|
var contentDiscoverabilityChecks = [
|
|
4557
4852
|
llmsTxtExists,
|
|
4558
4853
|
llmsTxtValid,
|
|
@@ -4560,21 +4855,29 @@ var contentDiscoverabilityChecks = [
|
|
|
4560
4855
|
llmsTxtFreshness,
|
|
4561
4856
|
llmsTxtLinksResolve,
|
|
4562
4857
|
llmsTxtLinksMarkdown,
|
|
4858
|
+
llmsFullExists,
|
|
4859
|
+
llmsFullValid,
|
|
4860
|
+
llmsFullSize,
|
|
4861
|
+
llmsFullLinksResolve,
|
|
4563
4862
|
sitemapExists,
|
|
4564
4863
|
robotsTxtAgentRules
|
|
4565
4864
|
];
|
|
4566
|
-
var
|
|
4865
|
+
var BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
4866
|
+
var makeHeaders = (config, asBrowser = false) => asBrowser ? {
|
|
4867
|
+
"User-Agent": BROWSER_UA,
|
|
4868
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
4869
|
+
} : {
|
|
4567
4870
|
"User-Agent": config.userAgent ?? DEFAULT_CONFIG.userAgent,
|
|
4568
4871
|
Accept: "text/html,application/xhtml+xml,text/markdown,text/plain,*/*"
|
|
4569
|
-
}
|
|
4570
|
-
var fetchPage = async (url, config = {}) => {
|
|
4872
|
+
};
|
|
4873
|
+
var fetchPage = async (url, config = {}, asBrowser = false) => {
|
|
4571
4874
|
const timeout = config.timeout ?? DEFAULT_CONFIG.timeout;
|
|
4572
4875
|
const start = Date.now();
|
|
4573
4876
|
const controller = new AbortController();
|
|
4574
4877
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
4575
4878
|
try {
|
|
4576
4879
|
const response = await fetch(url, {
|
|
4577
|
-
headers: makeHeaders(config),
|
|
4880
|
+
headers: makeHeaders(config, asBrowser),
|
|
4578
4881
|
signal: controller.signal,
|
|
4579
4882
|
redirect: "follow"
|
|
4580
4883
|
});
|
|
@@ -4627,13 +4930,13 @@ var fetchWithContentNegotiation = async (url, accept, config = {}) => {
|
|
|
4627
4930
|
clearTimeout(timer);
|
|
4628
4931
|
}
|
|
4629
4932
|
};
|
|
4630
|
-
var fetchMany = async (urls, config = {}) => {
|
|
4933
|
+
var fetchMany = async (urls, config = {}, asBrowser = false) => {
|
|
4631
4934
|
const concurrency = config.concurrency ?? DEFAULT_CONFIG.concurrency;
|
|
4632
4935
|
const results = [];
|
|
4633
4936
|
for (let i = 0; i < urls.length; i += concurrency) {
|
|
4634
4937
|
const chunk = urls.slice(i, i + concurrency);
|
|
4635
4938
|
const chunkResults = await Promise.allSettled(
|
|
4636
|
-
chunk.map((url) => fetchPage(url, config))
|
|
4939
|
+
chunk.map((url) => fetchPage(url, config, asBrowser))
|
|
4637
4940
|
);
|
|
4638
4941
|
for (const result of chunkResults) {
|
|
4639
4942
|
if (result.status === "fulfilled") {
|
|
@@ -4796,131 +5099,6 @@ var markdownAvailabilityChecks = [
|
|
|
4796
5099
|
contentNegotiation,
|
|
4797
5100
|
markdownContentParity
|
|
4798
5101
|
];
|
|
4799
|
-
var stripHtml = (html) => html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
4800
|
-
var extractLinks = (html, baseUrl) => {
|
|
4801
|
-
const links = [];
|
|
4802
|
-
const linkRegex = /<a[^>]+href=["']([^"']+)["']/gi;
|
|
4803
|
-
let match;
|
|
4804
|
-
while ((match = linkRegex.exec(html)) !== null) {
|
|
4805
|
-
try {
|
|
4806
|
-
const resolved = new URL(match[1], baseUrl).href;
|
|
4807
|
-
links.push(resolved);
|
|
4808
|
-
} catch {
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
return links;
|
|
4812
|
-
};
|
|
4813
|
-
var extractMetaTags = (html) => {
|
|
4814
|
-
const meta = {};
|
|
4815
|
-
const metaRegex = /<meta[^>]+(?:name|property)=["']([^"']+)["'][^>]+content=["']([^"']+)["']/gi;
|
|
4816
|
-
let match;
|
|
4817
|
-
while ((match = metaRegex.exec(html)) !== null) {
|
|
4818
|
-
meta[match[1].toLowerCase()] = match[2];
|
|
4819
|
-
}
|
|
4820
|
-
const metaRegex2 = /<meta[^>]+content=["']([^"']+)["'][^>]+(?:name|property)=["']([^"']+)["']/gi;
|
|
4821
|
-
while ((match = metaRegex2.exec(html)) !== null) {
|
|
4822
|
-
meta[match[2].toLowerCase()] = match[1];
|
|
4823
|
-
}
|
|
4824
|
-
return meta;
|
|
4825
|
-
};
|
|
4826
|
-
var extractJsonLd = (html) => {
|
|
4827
|
-
const results = [];
|
|
4828
|
-
const regex = /<script[^>]+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
4829
|
-
let match;
|
|
4830
|
-
while ((match = regex.exec(html)) !== null) {
|
|
4831
|
-
try {
|
|
4832
|
-
results.push(JSON.parse(match[1]));
|
|
4833
|
-
} catch {
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
return results;
|
|
4837
|
-
};
|
|
4838
|
-
var readAttr = (attrs, name) => {
|
|
4839
|
-
const re = new RegExp(`\\b${name}=(?:"([^"]*)"|'([^']*)')`, "i");
|
|
4840
|
-
const m = attrs.match(re);
|
|
4841
|
-
if (!m) return void 0;
|
|
4842
|
-
return m[1] ?? m[2];
|
|
4843
|
-
};
|
|
4844
|
-
var extractImages = (html) => {
|
|
4845
|
-
const images = [];
|
|
4846
|
-
const imgRegex = /<img\b([^>]*)>/gi;
|
|
4847
|
-
let match;
|
|
4848
|
-
while ((match = imgRegex.exec(html)) !== null) {
|
|
4849
|
-
const attrs = match[1];
|
|
4850
|
-
const src = readAttr(attrs, "src");
|
|
4851
|
-
if (src === void 0) continue;
|
|
4852
|
-
images.push({ src, alt: readAttr(attrs, "alt") });
|
|
4853
|
-
}
|
|
4854
|
-
return images;
|
|
4855
|
-
};
|
|
4856
|
-
var extractHeadings = (html) => {
|
|
4857
|
-
const headings = [];
|
|
4858
|
-
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
4859
|
-
let match;
|
|
4860
|
-
while ((match = regex.exec(html)) !== null) {
|
|
4861
|
-
headings.push({
|
|
4862
|
-
level: parseInt(match[1], 10),
|
|
4863
|
-
text: stripHtml(match[2]).trim()
|
|
4864
|
-
});
|
|
4865
|
-
}
|
|
4866
|
-
return headings;
|
|
4867
|
-
};
|
|
4868
|
-
var hasServerRenderedContent = (html) => {
|
|
4869
|
-
const withoutScripts = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
4870
|
-
const textContent = stripHtml(withoutScripts);
|
|
4871
|
-
return textContent.length > 100;
|
|
4872
|
-
};
|
|
4873
|
-
var findContentStartPosition = (html) => {
|
|
4874
|
-
const markers = [
|
|
4875
|
-
/<main[\s>]/i,
|
|
4876
|
-
/<article[\s>]/i,
|
|
4877
|
-
/id=["']content["']/i,
|
|
4878
|
-
/id=["']main["']/i,
|
|
4879
|
-
/class=["'][^"']*content[^"']*["']/i,
|
|
4880
|
-
/role=["']main["']/i
|
|
4881
|
-
];
|
|
4882
|
-
for (const marker of markers) {
|
|
4883
|
-
const match = html.search(marker);
|
|
4884
|
-
if (match >= 0) {
|
|
4885
|
-
return match / html.length;
|
|
4886
|
-
}
|
|
4887
|
-
}
|
|
4888
|
-
const firstP = html.search(/<p[\s>]/i);
|
|
4889
|
-
if (firstP >= 0) {
|
|
4890
|
-
return firstP / html.length;
|
|
4891
|
-
}
|
|
4892
|
-
return 0.5;
|
|
4893
|
-
};
|
|
4894
|
-
var extractCodeFences = (markdown) => {
|
|
4895
|
-
const fences = [];
|
|
4896
|
-
const lines = markdown.split("\n");
|
|
4897
|
-
let inFence = false;
|
|
4898
|
-
let currentLang = "";
|
|
4899
|
-
for (const line of lines) {
|
|
4900
|
-
const openMatch = line.match(/^```(\w*)/);
|
|
4901
|
-
if (openMatch && !inFence) {
|
|
4902
|
-
inFence = true;
|
|
4903
|
-
currentLang = openMatch[1] ?? "";
|
|
4904
|
-
} else if (line.trim() === "```" && inFence) {
|
|
4905
|
-
fences.push({ lang: currentLang, closed: true });
|
|
4906
|
-
inFence = false;
|
|
4907
|
-
currentLang = "";
|
|
4908
|
-
}
|
|
4909
|
-
}
|
|
4910
|
-
if (inFence) {
|
|
4911
|
-
fences.push({ lang: currentLang, closed: false });
|
|
4912
|
-
}
|
|
4913
|
-
return fences;
|
|
4914
|
-
};
|
|
4915
|
-
var parseSitemapUrls = (xml) => {
|
|
4916
|
-
const urls = [];
|
|
4917
|
-
const regex = /<loc>([^<]+)<\/loc>/gi;
|
|
4918
|
-
let match;
|
|
4919
|
-
while ((match = regex.exec(xml)) !== null) {
|
|
4920
|
-
urls.push(match[1].trim());
|
|
4921
|
-
}
|
|
4922
|
-
return urls;
|
|
4923
|
-
};
|
|
4924
5102
|
var MAX_HTML_CHARS = 5e4;
|
|
4925
5103
|
var MAX_MD_CHARS = 5e4;
|
|
4926
5104
|
var renderingStrategy = {
|
|
@@ -6239,6 +6417,56 @@ var mcpServerCard = {
|
|
|
6239
6417
|
}
|
|
6240
6418
|
}
|
|
6241
6419
|
};
|
|
6420
|
+
var mcpToolCount = {
|
|
6421
|
+
id: "mcp-tool-count",
|
|
6422
|
+
name: "MCP Tool Count",
|
|
6423
|
+
category: "agent-protocols",
|
|
6424
|
+
description: "Checks that the MCP server card exposes at least one tool",
|
|
6425
|
+
weight: 0.4,
|
|
6426
|
+
run: async (ctx) => {
|
|
6427
|
+
if (!ctx.mcpServerCard) {
|
|
6428
|
+
return {
|
|
6429
|
+
id: "mcp-tool-count",
|
|
6430
|
+
name: "MCP Tool Count",
|
|
6431
|
+
category: "agent-protocols",
|
|
6432
|
+
status: "skip",
|
|
6433
|
+
message: "Skipped: no MCP server card found"
|
|
6434
|
+
};
|
|
6435
|
+
}
|
|
6436
|
+
let card;
|
|
6437
|
+
try {
|
|
6438
|
+
card = JSON.parse(ctx.mcpServerCard);
|
|
6439
|
+
} catch {
|
|
6440
|
+
return {
|
|
6441
|
+
id: "mcp-tool-count",
|
|
6442
|
+
name: "MCP Tool Count",
|
|
6443
|
+
category: "agent-protocols",
|
|
6444
|
+
status: "skip",
|
|
6445
|
+
message: "Skipped: MCP server card is invalid JSON"
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
6448
|
+
const toolCount = Array.isArray(card.tools) ? card.tools.length : Array.isArray(card.capabilities?.tools) ? card.capabilities.tools.length : 0;
|
|
6449
|
+
if (toolCount > 0) {
|
|
6450
|
+
return {
|
|
6451
|
+
id: "mcp-tool-count",
|
|
6452
|
+
name: "MCP Tool Count",
|
|
6453
|
+
category: "agent-protocols",
|
|
6454
|
+
status: "pass",
|
|
6455
|
+
message: `MCP server exposes ${toolCount} tool${toolCount === 1 ? "" : "s"}`,
|
|
6456
|
+
metadata: { toolCount }
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
return {
|
|
6460
|
+
id: "mcp-tool-count",
|
|
6461
|
+
name: "MCP Tool Count",
|
|
6462
|
+
category: "agent-protocols",
|
|
6463
|
+
status: "warn",
|
|
6464
|
+
message: "MCP server card found but exposes no tools",
|
|
6465
|
+
suggestion: "List your MCP server's tools in the server card so agents know what actions are available before connecting.",
|
|
6466
|
+
metadata: { toolCount }
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
};
|
|
6242
6470
|
var apiCatalog = {
|
|
6243
6471
|
id: "api-catalog",
|
|
6244
6472
|
name: "API Catalog (RFC 9727)",
|
|
@@ -6312,7 +6540,7 @@ var contentSignals = {
|
|
|
6312
6540
|
name: "Content Signals (AI Usage Declarations)",
|
|
6313
6541
|
category: "agent-protocols",
|
|
6314
6542
|
status: "info",
|
|
6315
|
-
message: "No robots.txt found
|
|
6543
|
+
message: "No robots.txt found, cannot check for content signals",
|
|
6316
6544
|
suggestion: "Add a robots.txt with Content Signals directives to declare how AI agents may use your content (ai-train, ai-input, search)."
|
|
6317
6545
|
};
|
|
6318
6546
|
}
|
|
@@ -6497,7 +6725,7 @@ var agentsMd = {
|
|
|
6497
6725
|
category: "agent-protocols",
|
|
6498
6726
|
status: "fail",
|
|
6499
6727
|
message: "No AGENTS.md or AGENT.md found",
|
|
6500
|
-
suggestion: "Add an AGENTS.md at the project root. This is the universal agent configuration file
|
|
6728
|
+
suggestion: "Add an AGENTS.md at the project root. This is the universal agent configuration file, a README for AI coding agents. Include build/test commands, architecture overview, conventions, and any gotchas. Used by 60k+ open-source projects."
|
|
6501
6729
|
};
|
|
6502
6730
|
}
|
|
6503
6731
|
const content = ctx.agentsMd;
|
|
@@ -6549,6 +6777,7 @@ var agentsMd = {
|
|
|
6549
6777
|
};
|
|
6550
6778
|
var agentProtocolChecks = [
|
|
6551
6779
|
mcpServerCard,
|
|
6780
|
+
mcpToolCount,
|
|
6552
6781
|
apiCatalog,
|
|
6553
6782
|
contentSignals,
|
|
6554
6783
|
linkHeaders,
|
|
@@ -6725,7 +6954,7 @@ var buildRemoteContext = async (targetUrl, config) => {
|
|
|
6725
6954
|
const apiCatalog2 = apiCatalogResult.status === "fulfilled" && apiCatalogResult.value?.statusCode === 200 ? apiCatalogResult.value.text : void 0;
|
|
6726
6955
|
const agentSkillsIndex2 = agentSkillsResult.status === "fulfilled" && agentSkillsResult.value?.statusCode === 200 ? agentSkillsResult.value.text : void 0;
|
|
6727
6956
|
const agentsMd2 = void 0;
|
|
6728
|
-
|
|
6957
|
+
let sitemapUrls = sitemapXml ? parseSitemapUrls(sitemapXml) : [];
|
|
6729
6958
|
if (!sitemapXml && robotsTxt) {
|
|
6730
6959
|
const sitemapMatch = robotsTxt.match(/Sitemap:\s*(.+)/i);
|
|
6731
6960
|
if (sitemapMatch) {
|
|
@@ -6735,10 +6964,28 @@ var buildRemoteContext = async (targetUrl, config) => {
|
|
|
6735
6964
|
}
|
|
6736
6965
|
}
|
|
6737
6966
|
}
|
|
6967
|
+
const isSitemapIndex = (sitemapXml ?? "").includes("<sitemapindex");
|
|
6968
|
+
if (isSitemapIndex && sitemapUrls.length > 0) {
|
|
6969
|
+
const nested = await Promise.allSettled(
|
|
6970
|
+
sitemapUrls.slice(0, 20).map((u) => fetchText(u, config))
|
|
6971
|
+
);
|
|
6972
|
+
sitemapUrls = nested.flatMap(
|
|
6973
|
+
(r) => r.status === "fulfilled" && r.value?.statusCode === 200 ? parseSitemapUrls(r.value.text) : []
|
|
6974
|
+
);
|
|
6975
|
+
}
|
|
6738
6976
|
let pagesToSample = [];
|
|
6739
6977
|
if (sitemapUrls.length > 0) {
|
|
6740
|
-
const
|
|
6741
|
-
|
|
6978
|
+
const pathPrefix = baseUrl.pathname.replace(/\/+$/, "");
|
|
6979
|
+
const scoped = pathPrefix.length > 1 ? sitemapUrls.filter((u) => {
|
|
6980
|
+
try {
|
|
6981
|
+
return new URL(u).pathname.startsWith(pathPrefix);
|
|
6982
|
+
} catch {
|
|
6983
|
+
return false;
|
|
6984
|
+
}
|
|
6985
|
+
}) : sitemapUrls;
|
|
6986
|
+
const pool = scoped.length > 0 ? scoped : sitemapUrls;
|
|
6987
|
+
const step = Math.max(1, Math.floor(pool.length / config.sampleSize));
|
|
6988
|
+
pagesToSample = pool.filter((_, i) => i % step === 0).slice(0, config.sampleSize);
|
|
6742
6989
|
} else {
|
|
6743
6990
|
const mainPage = await fetchPage(targetUrl, config);
|
|
6744
6991
|
const linkRegex = /<a[^>]+href=["']([^"'#]+)["']/gi;
|
|
@@ -6758,14 +7005,16 @@ var buildRemoteContext = async (targetUrl, config) => {
|
|
|
6758
7005
|
if (!pagesToSample.includes(targetUrl)) {
|
|
6759
7006
|
pagesToSample.unshift(targetUrl);
|
|
6760
7007
|
}
|
|
6761
|
-
const sampledPages = await fetchMany(pagesToSample, config);
|
|
7008
|
+
const sampledPages = await fetchMany(pagesToSample, config, true);
|
|
6762
7009
|
emit({ type: "context-ready", pageCount: sampledPages.length });
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
7010
|
+
await Promise.allSettled(
|
|
7011
|
+
sampledPages.map(async (page) => {
|
|
7012
|
+
const mdResult = await fetchWithContentNegotiation(page.url, "text/markdown", config);
|
|
7013
|
+
if (mdResult && mdResult.statusCode === 200 && (mdResult.contentType.includes("text/markdown") || mdResult.contentType.includes("text/plain"))) {
|
|
7014
|
+
page.markdown = mdResult.text;
|
|
7015
|
+
}
|
|
7016
|
+
})
|
|
7017
|
+
);
|
|
6769
7018
|
return {
|
|
6770
7019
|
mode: "remote",
|
|
6771
7020
|
targetUrl,
|
|
@@ -6892,11 +7141,11 @@ var auditLocal = async (dirPath, config = {}) => {
|
|
|
6892
7141
|
};
|
|
6893
7142
|
|
|
6894
7143
|
// src/ui/app.tsx
|
|
6895
|
-
import { useState as useState4
|
|
6896
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
7144
|
+
import { useState as useState4 } from "react";
|
|
7145
|
+
import { Box as Box6, Text as Text6, Static, useStdout } from "ink";
|
|
6897
7146
|
|
|
6898
7147
|
// src/ui/hero-card.tsx
|
|
6899
|
-
import {
|
|
7148
|
+
import { useState } from "react";
|
|
6900
7149
|
import { Box, Text } from "ink";
|
|
6901
7150
|
|
|
6902
7151
|
// src/ui/color.ts
|
|
@@ -6941,11 +7190,16 @@ var toInkColor = (input) => {
|
|
|
6941
7190
|
return out;
|
|
6942
7191
|
};
|
|
6943
7192
|
|
|
7193
|
+
// src/hooks/use-mount-effect.ts
|
|
7194
|
+
import { useEffect } from "react";
|
|
7195
|
+
var useMountEffect = (effect) => {
|
|
7196
|
+
useEffect(effect, []);
|
|
7197
|
+
};
|
|
7198
|
+
|
|
6944
7199
|
// src/ui/hero-card.tsx
|
|
6945
7200
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6946
|
-
var
|
|
7201
|
+
var DEFAULT_CARD_WIDTH = 46;
|
|
6947
7202
|
var PATTERN_ROWS = 4;
|
|
6948
|
-
var PATTERN_COLS = CARD_WIDTH - 4;
|
|
6949
7203
|
var PATTERN_GLYPHS = ["\u2591", "\u2592", "\u2593", "\u2588"];
|
|
6950
7204
|
var ACCENT_BY_PHASE = {
|
|
6951
7205
|
init: "oklch(0.787 0.119 304.8)",
|
|
@@ -7018,10 +7272,10 @@ var MiniLoader = ({
|
|
|
7018
7272
|
intervalMs = 120
|
|
7019
7273
|
}) => {
|
|
7020
7274
|
const [frame, setFrame] = useState(0);
|
|
7021
|
-
|
|
7275
|
+
useMountEffect(() => {
|
|
7022
7276
|
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), intervalMs);
|
|
7023
7277
|
return () => clearInterval(timer);
|
|
7024
|
-
}
|
|
7278
|
+
});
|
|
7025
7279
|
const segments = [];
|
|
7026
7280
|
let current = null;
|
|
7027
7281
|
for (let c = 0; c < MINI_COLS; c++) {
|
|
@@ -7043,6 +7297,7 @@ var MiniLoader = ({
|
|
|
7043
7297
|
var Pattern = ({
|
|
7044
7298
|
accent,
|
|
7045
7299
|
frame,
|
|
7300
|
+
cols,
|
|
7046
7301
|
overlay
|
|
7047
7302
|
}) => {
|
|
7048
7303
|
const overlayStart = overlay ? overlay.col : -1;
|
|
@@ -7051,7 +7306,7 @@ var Pattern = ({
|
|
|
7051
7306
|
const segments = [];
|
|
7052
7307
|
let current = null;
|
|
7053
7308
|
const isOverlayRow = overlay && r === overlay.row;
|
|
7054
|
-
for (let c = 0; c <
|
|
7309
|
+
for (let c = 0; c < cols; c++) {
|
|
7055
7310
|
let char;
|
|
7056
7311
|
let color;
|
|
7057
7312
|
if (isOverlayRow && c >= overlayStart && c < overlayEnd) {
|
|
@@ -7099,13 +7354,13 @@ var shimmerColor = (index, head, cycleLen, accent) => {
|
|
|
7099
7354
|
};
|
|
7100
7355
|
var Shimmer = ({ text, accent }) => {
|
|
7101
7356
|
const [head, setHead] = useState(0);
|
|
7102
|
-
|
|
7357
|
+
useMountEffect(() => {
|
|
7103
7358
|
const cycleLen2 = text.length + SHIMMER_GAP;
|
|
7104
7359
|
const timer = setInterval(() => {
|
|
7105
7360
|
setHead((h) => (h + 1) % cycleLen2);
|
|
7106
7361
|
}, SHIMMER_INTERVAL_MS);
|
|
7107
7362
|
return () => clearInterval(timer);
|
|
7108
|
-
}
|
|
7363
|
+
});
|
|
7109
7364
|
const cycleLen = text.length + SHIMMER_GAP;
|
|
7110
7365
|
const segments = [];
|
|
7111
7366
|
let current = null;
|
|
@@ -7122,6 +7377,14 @@ var Shimmer = ({ text, accent }) => {
|
|
|
7122
7377
|
if (current) segments.push(current);
|
|
7123
7378
|
return /* @__PURE__ */ jsx(Text, { children: segments.map((s, i) => /* @__PURE__ */ jsx(Text, { color: toInkColor(s.color), children: s.text }, i)) });
|
|
7124
7379
|
};
|
|
7380
|
+
var AnimatedPattern = ({ accent, cols, overlay }) => {
|
|
7381
|
+
const [frame, setFrame] = useState(0);
|
|
7382
|
+
useMountEffect(() => {
|
|
7383
|
+
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), 120);
|
|
7384
|
+
return () => clearInterval(timer);
|
|
7385
|
+
});
|
|
7386
|
+
return /* @__PURE__ */ jsx(Pattern, { accent, frame, cols, overlay });
|
|
7387
|
+
};
|
|
7125
7388
|
var HeroCard = ({
|
|
7126
7389
|
target,
|
|
7127
7390
|
isLocal,
|
|
@@ -7130,18 +7393,21 @@ var HeroCard = ({
|
|
|
7130
7393
|
checksTotal,
|
|
7131
7394
|
grade,
|
|
7132
7395
|
score,
|
|
7133
|
-
gradeColor
|
|
7396
|
+
gradeColor,
|
|
7397
|
+
width = DEFAULT_CARD_WIDTH
|
|
7134
7398
|
}) => {
|
|
7135
|
-
const
|
|
7136
|
-
useEffect(() => {
|
|
7137
|
-
if (phase === "done" || phase === "error") return;
|
|
7138
|
-
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), 120);
|
|
7139
|
-
return () => clearInterval(timer);
|
|
7140
|
-
}, [phase]);
|
|
7399
|
+
const cols = width - 4;
|
|
7141
7400
|
const accent = phase === "done" && gradeColor ? gradeColor : ACCENT_BY_PHASE[phase];
|
|
7142
7401
|
const status = STATUS_BY_PHASE(phase, isLocal, checksDone, checksTotal);
|
|
7143
|
-
const targetLabel = truncate(target,
|
|
7402
|
+
const targetLabel = truncate(target, cols);
|
|
7144
7403
|
const showGrade = phase === "done" && grade !== void 0 && score !== void 0;
|
|
7404
|
+
const isActive = phase !== "done" && phase !== "error";
|
|
7405
|
+
const overlay = {
|
|
7406
|
+
row: PATTERN_ROWS - 1,
|
|
7407
|
+
col: cols - "agentimization".length - 1,
|
|
7408
|
+
chars: Array.from(" agentimization"),
|
|
7409
|
+
color: accent
|
|
7410
|
+
};
|
|
7145
7411
|
return /* @__PURE__ */ jsxs(
|
|
7146
7412
|
Box,
|
|
7147
7413
|
{
|
|
@@ -7149,21 +7415,9 @@ var HeroCard = ({
|
|
|
7149
7415
|
borderStyle: "round",
|
|
7150
7416
|
borderColor: dim(accent, FRAME_INNER_FACTOR),
|
|
7151
7417
|
paddingX: 1,
|
|
7152
|
-
width
|
|
7418
|
+
width,
|
|
7153
7419
|
children: [
|
|
7154
|
-
/* @__PURE__ */ jsx(
|
|
7155
|
-
Pattern,
|
|
7156
|
-
{
|
|
7157
|
-
accent,
|
|
7158
|
-
frame,
|
|
7159
|
-
overlay: {
|
|
7160
|
-
row: PATTERN_ROWS - 1,
|
|
7161
|
-
col: PATTERN_COLS - "agentimization".length - 1,
|
|
7162
|
-
chars: Array.from(" agentimization"),
|
|
7163
|
-
color: accent
|
|
7164
|
-
}
|
|
7165
|
-
}
|
|
7166
|
-
),
|
|
7420
|
+
isActive ? /* @__PURE__ */ jsx(AnimatedPattern, { accent, cols, overlay }) : /* @__PURE__ */ jsx(Pattern, { accent, frame: 0, cols, overlay }),
|
|
7167
7421
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: targetLabel }) }),
|
|
7168
7422
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: showGrade ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7169
7423
|
/* @__PURE__ */ jsx(Text, { color: toInkColor(accent), children: grade }),
|
|
@@ -7176,7 +7430,7 @@ var HeroCard = ({
|
|
|
7176
7430
|
" ",
|
|
7177
7431
|
status
|
|
7178
7432
|
] })
|
|
7179
|
-
] }) : /* @__PURE__ */ jsx(Shimmer, { text: status, accent }) })
|
|
7433
|
+
] }) : /* @__PURE__ */ jsx(Shimmer, { text: status, accent }, status) })
|
|
7180
7434
|
]
|
|
7181
7435
|
}
|
|
7182
7436
|
);
|
|
@@ -7222,7 +7476,7 @@ var ACCENT_BLUE = "oklch(0.766 0.111 259.9)";
|
|
|
7222
7476
|
var TEXT_DIM = "oklch(0.550 0.034 277.1)";
|
|
7223
7477
|
|
|
7224
7478
|
// src/ui/result-card.tsx
|
|
7225
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7479
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7226
7480
|
var SCORE_PAIRS = {
|
|
7227
7481
|
pass: { bright: "oklch(0.858 0.109 142.7)", dim: "oklch(0.584 0.102 139.1)" },
|
|
7228
7482
|
warn: { bright: "oklch(0.824 0.101 52.6)", dim: "oklch(0.572 0.072 53.0)" },
|
|
@@ -7230,8 +7484,7 @@ var SCORE_PAIRS = {
|
|
|
7230
7484
|
empty: { bright: "oklch(0.404 0.032 280.2)", dim: "oklch(0.324 0.032 282.0)" }
|
|
7231
7485
|
};
|
|
7232
7486
|
var RESULT_CARD_WIDTH = 46;
|
|
7233
|
-
var
|
|
7234
|
-
var SCORE_FIELD_ROWS = 2;
|
|
7487
|
+
var SCORE_FIELD_ROWS = 1;
|
|
7235
7488
|
var SCORE_GLYPHS = ["\u2591", "\u2592", "\u2593", "\u2588"];
|
|
7236
7489
|
var scoreCell = (row, col) => {
|
|
7237
7490
|
const v = (row * 7 + col * 13) % 17;
|
|
@@ -7242,16 +7495,18 @@ var scoreCell = (row, col) => {
|
|
|
7242
7495
|
var ScoreField = ({
|
|
7243
7496
|
passWidth,
|
|
7244
7497
|
warnWidth,
|
|
7245
|
-
failWidth
|
|
7498
|
+
failWidth,
|
|
7499
|
+
barWidth
|
|
7246
7500
|
}) => {
|
|
7501
|
+
const emptyWidth = Math.max(0, barWidth - passWidth - warnWidth - failWidth);
|
|
7247
7502
|
const colorAtCol = (col, bright) => {
|
|
7248
|
-
const pair = col <
|
|
7503
|
+
const pair = col < emptyWidth ? SCORE_PAIRS.empty : col < emptyWidth + passWidth ? SCORE_PAIRS.pass : col < emptyWidth + passWidth + warnWidth ? SCORE_PAIRS.warn : SCORE_PAIRS.fail;
|
|
7249
7504
|
return bright ? pair.bright : pair.dim;
|
|
7250
7505
|
};
|
|
7251
7506
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: Array.from({ length: SCORE_FIELD_ROWS }, (_, r) => {
|
|
7252
7507
|
const segments = [];
|
|
7253
7508
|
let current = null;
|
|
7254
|
-
for (let c = 0; c <
|
|
7509
|
+
for (let c = 0; c < barWidth; c++) {
|
|
7255
7510
|
const cell = scoreCell(r, c);
|
|
7256
7511
|
const color = colorAtCol(c, cell.bright);
|
|
7257
7512
|
if (current && current.color === color) {
|
|
@@ -7265,15 +7520,21 @@ var ScoreField = ({
|
|
|
7265
7520
|
return /* @__PURE__ */ jsx2(Text2, { children: segments.map((s, i) => /* @__PURE__ */ jsx2(Text2, { color: toInkColor(s.color), children: s.text }, i)) }, r);
|
|
7266
7521
|
}) });
|
|
7267
7522
|
};
|
|
7268
|
-
var ResultCard = ({
|
|
7523
|
+
var ResultCard = ({
|
|
7524
|
+
result,
|
|
7525
|
+
target,
|
|
7526
|
+
width = RESULT_CARD_WIDTH
|
|
7527
|
+
}) => {
|
|
7269
7528
|
const { grade, overall_score, summary, latency_ms } = result;
|
|
7270
7529
|
const gc = GRADE_COLORS[grade] ?? "red";
|
|
7530
|
+
const barWidth = width - 4;
|
|
7271
7531
|
const total = summary.total || 1;
|
|
7272
|
-
const passWidth = Math.max(Math.round(summary.passed / total *
|
|
7273
|
-
const warnWidth = Math.max(Math.round(summary.warned / total *
|
|
7274
|
-
const failWidth = Math.max(Math.round(summary.failed / total *
|
|
7532
|
+
const passWidth = Math.max(Math.round(summary.passed / total * barWidth), summary.passed > 0 ? 1 : 0);
|
|
7533
|
+
const warnWidth = Math.max(Math.round(summary.warned / total * barWidth), summary.warned > 0 ? 1 : 0);
|
|
7534
|
+
const failWidth = Math.max(Math.round(summary.failed / total * barWidth), summary.failed > 0 ? 1 : 0);
|
|
7275
7535
|
const seconds = (latency_ms / 1e3).toFixed(1);
|
|
7276
|
-
const
|
|
7536
|
+
const headerChrome = `${grade} ${overall_score}/100`.length + 3 + 3 + `${seconds}s`.length;
|
|
7537
|
+
const targetMax = Math.max(8, barWidth - headerChrome);
|
|
7277
7538
|
const targetLabel = target.length <= targetMax ? target : target.slice(0, targetMax - 1) + "\u2026";
|
|
7278
7539
|
return /* @__PURE__ */ jsxs2(
|
|
7279
7540
|
Box2,
|
|
@@ -7283,7 +7544,7 @@ var ResultCard = ({ result, target }) => {
|
|
|
7283
7544
|
borderStyle: "round",
|
|
7284
7545
|
borderColor: dim(gc, FRAME_INNER_FACTOR),
|
|
7285
7546
|
paddingX: 1,
|
|
7286
|
-
width
|
|
7547
|
+
width,
|
|
7287
7548
|
children: [
|
|
7288
7549
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7289
7550
|
/* @__PURE__ */ jsx2(Text2, { color: toInkColor(gc), children: grade }),
|
|
@@ -7292,22 +7553,34 @@ var ResultCard = ({ result, target }) => {
|
|
|
7292
7553
|
overall_score,
|
|
7293
7554
|
"/100"
|
|
7294
7555
|
] }),
|
|
7556
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7557
|
+
" ",
|
|
7558
|
+
targetLabel
|
|
7559
|
+
] }),
|
|
7295
7560
|
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7296
7561
|
" ",
|
|
7297
7562
|
seconds,
|
|
7298
7563
|
"s"
|
|
7299
7564
|
] })
|
|
7300
7565
|
] }),
|
|
7301
|
-
/* @__PURE__ */ jsx2(Text2, { children: targetLabel }),
|
|
7302
7566
|
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(
|
|
7303
7567
|
ScoreField,
|
|
7304
7568
|
{
|
|
7305
7569
|
passWidth,
|
|
7306
7570
|
warnWidth,
|
|
7307
|
-
failWidth
|
|
7571
|
+
failWidth,
|
|
7572
|
+
barWidth
|
|
7308
7573
|
}
|
|
7309
7574
|
) }),
|
|
7310
7575
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
7576
|
+
summary.skipped > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
7577
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7578
|
+
STATUS_ICONS.skip,
|
|
7579
|
+
" ",
|
|
7580
|
+
summary.skipped
|
|
7581
|
+
] }),
|
|
7582
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " " })
|
|
7583
|
+
] }) : null,
|
|
7311
7584
|
/* @__PURE__ */ jsxs2(Text2, { color: toInkColor(SCORE_PAIRS.pass.bright), children: [
|
|
7312
7585
|
STATUS_ICONS.pass,
|
|
7313
7586
|
" ",
|
|
@@ -7324,12 +7597,6 @@ var ResultCard = ({ result, target }) => {
|
|
|
7324
7597
|
STATUS_ICONS.fail,
|
|
7325
7598
|
" ",
|
|
7326
7599
|
summary.failed
|
|
7327
|
-
] }),
|
|
7328
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " " }),
|
|
7329
|
-
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7330
|
-
STATUS_ICONS.skip,
|
|
7331
|
-
" ",
|
|
7332
|
-
summary.skipped
|
|
7333
7600
|
] })
|
|
7334
7601
|
] })
|
|
7335
7602
|
]
|
|
@@ -7363,14 +7630,13 @@ var CheckLine = ({ check, loaderColor }) => {
|
|
|
7363
7630
|
r.id
|
|
7364
7631
|
] })
|
|
7365
7632
|
] }),
|
|
7366
|
-
/* @__PURE__ */ jsxs3(
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
] }) : null
|
|
7633
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 2, children: [
|
|
7634
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: r.message }),
|
|
7635
|
+
showSuggestion ? /* @__PURE__ */ jsxs3(Text3, { color: toInkColor("oklch(0.858 0.109 142.7)"), children: [
|
|
7636
|
+
"\u2192 ",
|
|
7637
|
+
r.suggestion
|
|
7638
|
+
] }) : null
|
|
7639
|
+
] })
|
|
7374
7640
|
] });
|
|
7375
7641
|
}
|
|
7376
7642
|
return null;
|
|
@@ -7551,7 +7817,7 @@ var copyToClipboard = async (text) => {
|
|
|
7551
7817
|
// src/ui/action-menu.tsx
|
|
7552
7818
|
import { writeFileSync } from "fs";
|
|
7553
7819
|
import { resolve } from "path";
|
|
7554
|
-
import { Fragment as
|
|
7820
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
7555
7821
|
var FEEDBACK_OK = "oklch(0.858 0.109 142.7)";
|
|
7556
7822
|
var FEEDBACK_ERR = "oklch(0.718 0.181 10.0)";
|
|
7557
7823
|
var buildMenuOptions = (hasIssues) => [
|
|
@@ -7679,7 +7945,7 @@ var ActionMenu = ({
|
|
|
7679
7945
|
] }) : null
|
|
7680
7946
|
] }, opt.value);
|
|
7681
7947
|
}) }),
|
|
7682
|
-
feedback ? /* @__PURE__ */ jsxs4(
|
|
7948
|
+
feedback ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
7683
7949
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, paddingLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { color: toInkColor(feedback.startsWith("\u2713") ? FEEDBACK_OK : FEEDBACK_ERR), children: feedback }) }),
|
|
7684
7950
|
/* @__PURE__ */ jsx4(Box4, { height: 1, children: /* @__PURE__ */ jsx4(Text4, { children: " " }) })
|
|
7685
7951
|
] }) : null
|
|
@@ -7812,7 +8078,7 @@ var resolveTarget = (input) => {
|
|
|
7812
8078
|
};
|
|
7813
8079
|
|
|
7814
8080
|
// src/ui/app.tsx
|
|
7815
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8081
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
7816
8082
|
var App = ({
|
|
7817
8083
|
target: initialTarget,
|
|
7818
8084
|
isLocal: initialIsLocal,
|
|
@@ -7822,22 +8088,95 @@ var App = ({
|
|
|
7822
8088
|
}) => {
|
|
7823
8089
|
const [target, setTarget] = useState4(initialTarget);
|
|
7824
8090
|
const [isLocal, setIsLocal] = useState4(initialIsLocal);
|
|
8091
|
+
const [retryError, setRetryError] = useState4(null);
|
|
8092
|
+
const { stdout } = useStdout();
|
|
8093
|
+
const [columns, setColumns] = useState4(stdout?.columns ?? 80);
|
|
8094
|
+
useMountEffect(() => {
|
|
8095
|
+
if (!stdout) return;
|
|
8096
|
+
const onResize = () => setColumns(stdout.columns);
|
|
8097
|
+
stdout.on("resize", onResize);
|
|
8098
|
+
return () => {
|
|
8099
|
+
stdout.off("resize", onResize);
|
|
8100
|
+
};
|
|
8101
|
+
});
|
|
8102
|
+
const cardWidth = Math.max(40, Math.min(columns - 2, 72));
|
|
8103
|
+
const handleRetry = (newInput) => {
|
|
8104
|
+
const resolved = resolveTarget(newInput);
|
|
8105
|
+
if ("error" in resolved) {
|
|
8106
|
+
setRetryError(resolved.error);
|
|
8107
|
+
return;
|
|
8108
|
+
}
|
|
8109
|
+
setRetryError(null);
|
|
8110
|
+
setTarget(resolved.target);
|
|
8111
|
+
setIsLocal(resolved.isLocal);
|
|
8112
|
+
};
|
|
8113
|
+
if (retryError) {
|
|
8114
|
+
return /* @__PURE__ */ jsx6(ErrorCard, { message: retryError, target, cardWidth, onRetry: handleRetry });
|
|
8115
|
+
}
|
|
8116
|
+
return /* @__PURE__ */ jsx6(
|
|
8117
|
+
AuditRunner,
|
|
8118
|
+
{
|
|
8119
|
+
target,
|
|
8120
|
+
isLocal,
|
|
8121
|
+
categories,
|
|
8122
|
+
sampleSize,
|
|
8123
|
+
autoDetectedFrom,
|
|
8124
|
+
cardWidth,
|
|
8125
|
+
onRetry: handleRetry
|
|
8126
|
+
},
|
|
8127
|
+
`${target}|${isLocal}`
|
|
8128
|
+
);
|
|
8129
|
+
};
|
|
8130
|
+
var ErrorCard = ({
|
|
8131
|
+
message,
|
|
8132
|
+
target,
|
|
8133
|
+
cardWidth,
|
|
8134
|
+
onRetry
|
|
8135
|
+
}) => {
|
|
8136
|
+
const errorColor = "oklch(0.718 0.181 10.0)";
|
|
8137
|
+
const lines = message.split("\n");
|
|
8138
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
8139
|
+
/* @__PURE__ */ jsxs6(
|
|
8140
|
+
Box6,
|
|
8141
|
+
{
|
|
8142
|
+
flexDirection: "column",
|
|
8143
|
+
marginTop: 1,
|
|
8144
|
+
borderStyle: "round",
|
|
8145
|
+
borderColor: dim(errorColor, FRAME_INNER_FACTOR),
|
|
8146
|
+
paddingX: 1,
|
|
8147
|
+
width: cardWidth,
|
|
8148
|
+
children: [
|
|
8149
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: toInkColor(errorColor), children: "Error" }),
|
|
8150
|
+
/* @__PURE__ */ jsx6(Text6, { children: target }),
|
|
8151
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: i > 0, children: line }, i)) })
|
|
8152
|
+
]
|
|
8153
|
+
}
|
|
8154
|
+
),
|
|
8155
|
+
/* @__PURE__ */ jsx6(ErrorActions, { onRetry })
|
|
8156
|
+
] });
|
|
8157
|
+
};
|
|
8158
|
+
var AuditRunner = ({
|
|
8159
|
+
target,
|
|
8160
|
+
isLocal,
|
|
8161
|
+
categories,
|
|
8162
|
+
sampleSize,
|
|
8163
|
+
autoDetectedFrom,
|
|
8164
|
+
cardWidth,
|
|
8165
|
+
onRetry
|
|
8166
|
+
}) => {
|
|
7825
8167
|
const [phase, setPhase] = useState4("init");
|
|
7826
8168
|
const [checks, setChecks] = useState4([]);
|
|
7827
8169
|
const [result, setResult] = useState4(null);
|
|
7828
8170
|
const [error, setError] = useState4(null);
|
|
7829
8171
|
const [networkSkipped, setNetworkSkipped] = useState4(0);
|
|
7830
|
-
|
|
8172
|
+
useMountEffect(() => {
|
|
7831
8173
|
const onEvent = (event) => {
|
|
7832
8174
|
switch (event.type) {
|
|
7833
8175
|
case "phase":
|
|
7834
8176
|
setPhase(event.phase);
|
|
7835
8177
|
break;
|
|
7836
8178
|
case "check-start":
|
|
7837
|
-
setChecks((prev) => [
|
|
7838
|
-
...prev,
|
|
7839
|
-
{ ...event.check, status: "running" }
|
|
7840
|
-
]);
|
|
8179
|
+
setChecks((prev) => [...prev, { ...event.check, status: "running" }]);
|
|
7841
8180
|
break;
|
|
7842
8181
|
case "check-complete":
|
|
7843
8182
|
setChecks(
|
|
@@ -7868,89 +8207,69 @@ var App = ({
|
|
|
7868
8207
|
}
|
|
7869
8208
|
};
|
|
7870
8209
|
run();
|
|
7871
|
-
}
|
|
7872
|
-
const handleRetry = (newInput) => {
|
|
7873
|
-
const resolved = resolveTarget(newInput);
|
|
7874
|
-
if ("error" in resolved) {
|
|
7875
|
-
setError(resolved.error);
|
|
7876
|
-
return;
|
|
7877
|
-
}
|
|
7878
|
-
setError(null);
|
|
7879
|
-
setResult(null);
|
|
7880
|
-
setChecks([]);
|
|
7881
|
-
setNetworkSkipped(0);
|
|
7882
|
-
setPhase("init");
|
|
7883
|
-
setTarget(resolved.target);
|
|
7884
|
-
setIsLocal(resolved.isLocal);
|
|
7885
|
-
};
|
|
8210
|
+
});
|
|
7886
8211
|
const checksByCategory = /* @__PURE__ */ new Map();
|
|
7887
8212
|
for (const check of checks) {
|
|
7888
8213
|
const existing = checksByCategory.get(check.category) ?? [];
|
|
7889
8214
|
existing.push(check);
|
|
7890
8215
|
checksByCategory.set(check.category, existing);
|
|
7891
8216
|
}
|
|
8217
|
+
const categoryEntries = [...checksByCategory.entries()];
|
|
8218
|
+
const committedCount = phase === "done" ? categoryEntries.length : Math.max(0, categoryEntries.length - 1);
|
|
8219
|
+
const committedCategories = categoryEntries.slice(0, committedCount);
|
|
8220
|
+
const liveCategory = categoryEntries[committedCount];
|
|
7892
8221
|
if (phase === "error") {
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7896
|
-
/* @__PURE__ */ jsxs6(
|
|
7897
|
-
Box6,
|
|
7898
|
-
{
|
|
7899
|
-
flexDirection: "column",
|
|
7900
|
-
marginTop: 1,
|
|
7901
|
-
borderStyle: "round",
|
|
7902
|
-
borderColor: dim(errorColor, FRAME_INNER_FACTOR),
|
|
7903
|
-
paddingX: 1,
|
|
7904
|
-
width: RESULT_CARD_WIDTH,
|
|
7905
|
-
children: [
|
|
7906
|
-
/* @__PURE__ */ jsx6(Text6, { bold: true, color: toInkColor(errorColor), children: "Error" }),
|
|
7907
|
-
/* @__PURE__ */ jsx6(Text6, { children: target }),
|
|
7908
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: i > 0, children: line }, i)) })
|
|
7909
|
-
]
|
|
7910
|
-
}
|
|
7911
|
-
),
|
|
7912
|
-
/* @__PURE__ */ jsx6(ErrorActions, { onRetry: handleRetry })
|
|
7913
|
-
] });
|
|
7914
|
-
}
|
|
7915
|
-
const checksDone = checks.filter((c) => c.status === "done").length;
|
|
7916
|
-
const accent = result ? GRADE_COLORS[result.grade] ?? ACCENT_BY_PHASE[phase] : ACCENT_BY_PHASE[phase];
|
|
7917
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7918
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
7919
|
-
HeroCard,
|
|
8222
|
+
return /* @__PURE__ */ jsx6(
|
|
8223
|
+
ErrorCard,
|
|
7920
8224
|
{
|
|
8225
|
+
message: error ?? "Unknown error",
|
|
7921
8226
|
target,
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
checksDone,
|
|
7925
|
-
checksTotal: checks.length,
|
|
7926
|
-
grade: result?.grade,
|
|
7927
|
-
score: result?.overall_score,
|
|
7928
|
-
gradeColor: result ? GRADE_COLORS[result.grade] : void 0
|
|
8227
|
+
cardWidth,
|
|
8228
|
+
onRetry
|
|
7929
8229
|
}
|
|
7930
|
-
)
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
] })
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
/* @__PURE__ */
|
|
7952
|
-
|
|
7953
|
-
|
|
8230
|
+
);
|
|
8231
|
+
}
|
|
8232
|
+
const checksDone = checks.filter((c) => c.status === "done").length;
|
|
8233
|
+
const accent = result ? GRADE_COLORS[result.grade] ?? ACCENT_BY_PHASE[phase] : ACCENT_BY_PHASE[phase];
|
|
8234
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
8235
|
+
/* @__PURE__ */ jsx6(Static, { items: committedCategories, children: ([cat, catChecks]) => /* @__PURE__ */ jsx6(Box6, { paddingLeft: 1, children: /* @__PURE__ */ jsx6(CategorySection, { category: cat, checks: catChecks, loaderColor: accent }) }, cat) }),
|
|
8236
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
8237
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
8238
|
+
HeroCard,
|
|
8239
|
+
{
|
|
8240
|
+
target,
|
|
8241
|
+
isLocal,
|
|
8242
|
+
phase,
|
|
8243
|
+
checksDone,
|
|
8244
|
+
checksTotal: checks.length,
|
|
8245
|
+
grade: result?.grade,
|
|
8246
|
+
score: result?.overall_score,
|
|
8247
|
+
gradeColor: result ? GRADE_COLORS[result.grade] : void 0,
|
|
8248
|
+
width: cardWidth
|
|
8249
|
+
}
|
|
8250
|
+
) }),
|
|
8251
|
+
autoDetectedFrom ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8252
|
+
"auto-detected build output (was ",
|
|
8253
|
+
autoDetectedFrom,
|
|
8254
|
+
")"
|
|
8255
|
+
] }) : null,
|
|
8256
|
+
isLocal && networkSkipped > 0 && phase === "done" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8257
|
+
networkSkipped,
|
|
8258
|
+
" network-only checks skipped"
|
|
8259
|
+
] }) : null,
|
|
8260
|
+
liveCategory ? /* @__PURE__ */ jsx6(
|
|
8261
|
+
CategorySection,
|
|
8262
|
+
{
|
|
8263
|
+
category: liveCategory[0],
|
|
8264
|
+
checks: liveCategory[1],
|
|
8265
|
+
loaderColor: accent
|
|
8266
|
+
}
|
|
8267
|
+
) : null,
|
|
8268
|
+
result ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8269
|
+
/* @__PURE__ */ jsx6(ResultCard, { result, target, width: cardWidth }),
|
|
8270
|
+
/* @__PURE__ */ jsx6(ActionMenu, { result, target, isLocal, onRetry })
|
|
8271
|
+
] }) : null
|
|
8272
|
+
] })
|
|
7954
8273
|
] });
|
|
7955
8274
|
};
|
|
7956
8275
|
|