@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.
Files changed (2) hide show
  1. package/cli.mjs +78 -14
  2. 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
- const data = await apiGet(`/dataset?limit=800`)
88
- const site = (data.sites ?? []).find((s) => s.id === domain)
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}\n${DIM}run: npx mdvp submit ${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
- console.log(JSON.stringify({
97
- id: site.id, url: site.url, grade: site.grade,
98
- overall_score: site.overall_score, label: site.label,
99
- scores: { overall: site.overall_score, grade: site.grade, breakdown: Object.fromEntries(bd.map((b) => [b.c, b.s])) },
100
- }, null, 2))
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
- const sorted = [...bd].sort((a, b) => a.s - b.s).slice(0, 3)
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
- process.stderr.write(`${DIM}crawling ${domain} locally...${R}\n`)
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") || flags.has("--raw"),
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"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdvp/cli",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Machine Design Vision Protocol — gives agents eyes to understand design quality",
5
5
  "bin": {
6
6
  "mdvp": "./cli.mjs"