@mdvp/cli 1.6.0 → 1.8.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/cli.mjs +78 -14
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -81,32 +81,89 @@ function apiPost(path, data, apiKey, baseUrl = API) {
|
|
|
81
81
|
})
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
async function cmdAudit(domain, { json }) {
|
|
84
|
+
async function cmdAudit(domain, { json, raw, apiKey }) {
|
|
85
85
|
domain = parseDomain(domain)
|
|
86
|
+
|
|
87
|
+
if ((json || raw) && !apiKey) {
|
|
88
|
+
console.error(`${RED}--json and --raw require an API key (costs 1 credit).${R}`)
|
|
89
|
+
console.error(`${DIM}Run: npx @mdvp/cli login or npx @mdvp/cli balance${R}`)
|
|
90
|
+
process.exit(1)
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
process.stderr.write(`${DIM}fetching ${domain}...${R}\n`)
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
|
|
95
|
+
let site = null
|
|
96
|
+
try {
|
|
97
|
+
const direct = await apiGet(`/dataset/${domain}`)
|
|
98
|
+
if (direct && direct.id) site = direct
|
|
99
|
+
} catch (_) {}
|
|
100
|
+
|
|
101
|
+
if (!site) {
|
|
102
|
+
const data = await apiGet(`/dataset?limit=1000`)
|
|
103
|
+
site = (data.sites ?? []).find((s) => s.id === domain) ?? null
|
|
104
|
+
}
|
|
105
|
+
|
|
89
106
|
if (!site) {
|
|
90
|
-
if (json) { console.log(JSON.stringify({ error: "not_in_dataset", domain }, null, 2)); process.exit(1) }
|
|
91
|
-
console.error(`${RED}not in dataset: ${domain}${R}
|
|
107
|
+
if (json || raw) { console.log(JSON.stringify({ error: "not_in_dataset", domain }, null, 2)); process.exit(1) }
|
|
108
|
+
console.error(`${RED}not in dataset: ${domain}${R}`)
|
|
109
|
+
console.error(`${DIM}submit for crawl: npx @mdvp/cli submit ${domain}${R}`)
|
|
92
110
|
process.exit(1)
|
|
93
111
|
}
|
|
112
|
+
|
|
94
113
|
const bd = site.scores?.breakdown ?? []
|
|
114
|
+
const sorted = [...bd].sort((a, b) => a.s - b.s)
|
|
115
|
+
|
|
116
|
+
if (raw) {
|
|
117
|
+
const output = {
|
|
118
|
+
id: site.id,
|
|
119
|
+
url: site.url,
|
|
120
|
+
domain: site.domain ?? site.id,
|
|
121
|
+
grade: site.grade,
|
|
122
|
+
overall_score: site.overall_score,
|
|
123
|
+
label: site.label,
|
|
124
|
+
category: site.category,
|
|
125
|
+
scored_at: site.scored_at ?? null,
|
|
126
|
+
scores: {
|
|
127
|
+
overall: site.overall_score,
|
|
128
|
+
grade: site.grade,
|
|
129
|
+
breakdown: bd.map((b) => ({ category: CATS[b.c] ?? b.c, key: b.c, score: b.s })),
|
|
130
|
+
worst: sorted.slice(0, 3).map((b) => ({ key: b.c, category: CATS[b.c] ?? b.c, score: b.s })),
|
|
131
|
+
best: sorted.slice(-3).reverse().map((b) => ({ key: b.c, category: CATS[b.c] ?? b.c, score: b.s })),
|
|
132
|
+
},
|
|
133
|
+
metrics: site.metrics ?? null,
|
|
134
|
+
mdvp: { version: "1", api: "https://designsense.tixo-digital.workers.dev", dataset_url: `https://designsense.tixo-digital.workers.dev/dataset/${site.id}` },
|
|
135
|
+
}
|
|
136
|
+
console.log(JSON.stringify(output, null, 2))
|
|
137
|
+
if (apiKey) await apiPost("/audit/charge", { domain, type: "raw", amount: 0.20 }, apiKey).catch(() => {})
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
95
141
|
if (json) {
|
|
96
|
-
|
|
97
|
-
id: site.id,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
const output = {
|
|
143
|
+
id: site.id,
|
|
144
|
+
url: site.url,
|
|
145
|
+
grade: site.grade,
|
|
146
|
+
overall_score: site.overall_score,
|
|
147
|
+
label: site.label,
|
|
148
|
+
scored_at: site.scored_at ?? null,
|
|
149
|
+
scores: {
|
|
150
|
+
overall: site.overall_score,
|
|
151
|
+
grade: site.grade,
|
|
152
|
+
breakdown: Object.fromEntries(bd.map((b) => [b.c, b.s])),
|
|
153
|
+
worst: sorted.slice(0, 3).map((b) => ({ key: b.c, score: b.s })),
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
console.log(JSON.stringify(output, null, 2))
|
|
157
|
+
if (apiKey) await apiPost("/audit/charge", { domain, type: "json", amount: 0.10 }, apiKey).catch(() => {})
|
|
101
158
|
return
|
|
102
159
|
}
|
|
160
|
+
|
|
103
161
|
console.log(`\n${BOLD}${site.id}${R} ${scoreColor(site.overall_score)}${site.grade} ${site.overall_score}/100${R} ${DIM}${site.label}${R}\n`)
|
|
104
162
|
for (const cat of Object.keys(CATS)) {
|
|
105
163
|
const s = bd.find((b) => b.c === cat)?.s ?? 0
|
|
106
164
|
console.log(` ${CATS[cat].padEnd(16)} ${scoreColor(s)}${bar(s)}${R} ${s}`)
|
|
107
165
|
}
|
|
108
|
-
|
|
109
|
-
console.log(`\n${DIM}Lowest: ${sorted.map((i) => `${CATS[i.c] ?? i.c} (${i.s})`).join(" · ")}${R}\n`)
|
|
166
|
+
console.log(`\n${DIM}Lowest: ${sorted.slice(0, 3).map((i) => `${CATS[i.c] ?? i.c} (${i.s})`).join(" · ")}${R}\n`)
|
|
110
167
|
}
|
|
111
168
|
|
|
112
169
|
async function cmdCompare(da, db) {
|
|
@@ -291,7 +348,13 @@ async function cmdSubmit(domain, opts) {
|
|
|
291
348
|
domain = parseDomain(domain)
|
|
292
349
|
|
|
293
350
|
if (local) {
|
|
294
|
-
|
|
351
|
+
if (!apiKey) {
|
|
352
|
+
console.error(`${RED}API key required for local crawl. Run: npx @mdvp/cli login${R}`)
|
|
353
|
+
console.error(`${DIM}Free to crawl, but we need to know who you are.${R}`)
|
|
354
|
+
process.exit(1)
|
|
355
|
+
}
|
|
356
|
+
process.stderr.write(`${DIM}crawling ${domain} locally ($0.03, key: ${apiKey.slice(0, 8)}...)...${R}\n`)
|
|
357
|
+
await apiPost("/audit/charge", { domain, type: "local_crawl", amount: 0.03 }, apiKey).catch(() => {})
|
|
295
358
|
await cmdHire({ ...opts, domain, daemon: false, _url: `https://${domain}`, _once: true })
|
|
296
359
|
return
|
|
297
360
|
}
|
|
@@ -312,7 +375,8 @@ async function main() {
|
|
|
312
375
|
const [cmd, arg1, arg2] = positional
|
|
313
376
|
const cfg = loadConfig()
|
|
314
377
|
const opts = {
|
|
315
|
-
json: flags.has("--json")
|
|
378
|
+
json: flags.has("--json"),
|
|
379
|
+
raw: flags.has("--raw"),
|
|
316
380
|
apiKey: cfg.apiKey ?? null,
|
|
317
381
|
local: flags.has("--local"),
|
|
318
382
|
daemon: flags.has("--daemon") || flags.has("-d"),
|