@randomcode-seolint/cli 0.1.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/dist/index.js +117 -0
  2. package/package.json +21 -0
package/dist/index.js ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ const API_BASE = process.env.SEOLINT_API_URL ?? "https://seolint.dev";
3
+ const API_KEY = process.env.SEOLINT_API_KEY ?? "";
4
+ function headers() {
5
+ const h = { "Content-Type": "application/json" };
6
+ if (API_KEY)
7
+ h["Authorization"] = `Bearer ${API_KEY}`;
8
+ return h;
9
+ }
10
+ function printUsage() {
11
+ console.log(`
12
+ seolint — scan any website for SEO issues
13
+
14
+ Usage:
15
+ seolint scan <url> Scan a URL and print the report
16
+ seolint scan <url> --json Output raw JSON instead of markdown
17
+ seolint help Show this message
18
+
19
+ Environment:
20
+ SEOLINT_API_KEY Your API key (get one at seolint.dev/dashboard)
21
+
22
+ Examples:
23
+ seolint scan https://example.com
24
+ SEOLINT_API_KEY=sl_xxx seolint scan https://mysite.com --json
25
+ npx seolint scan https://example.com
26
+ `);
27
+ }
28
+ async function scan(url, json) {
29
+ // Normalise URL
30
+ let fullUrl = url;
31
+ if (!/^https?:\/\//i.test(fullUrl))
32
+ fullUrl = `https://${fullUrl}`;
33
+ process.stderr.write(`Scanning ${fullUrl}...\n`);
34
+ // Start scan
35
+ const startRes = await fetch(`${API_BASE}/api/v1/scan`, {
36
+ method: "POST",
37
+ headers: headers(),
38
+ body: JSON.stringify({ url: fullUrl }),
39
+ });
40
+ if (!startRes.ok) {
41
+ const err = await startRes.json().catch(() => ({}));
42
+ console.error(`Error: ${err.error ?? startRes.statusText}`);
43
+ process.exit(1);
44
+ }
45
+ const { scanId } = await startRes.json();
46
+ // Poll
47
+ const maxWait = 90_000;
48
+ const start = Date.now();
49
+ while (Date.now() - start < maxWait) {
50
+ const res = await fetch(`${API_BASE}/api/v1/scan/${scanId}`, { headers: headers() });
51
+ if (!res.ok) {
52
+ console.error(`Poll error: ${res.status}`);
53
+ process.exit(1);
54
+ }
55
+ const data = await res.json();
56
+ if (data.status === "complete") {
57
+ if (json) {
58
+ console.log(JSON.stringify(data.issues ?? [], null, 2));
59
+ }
60
+ else if (data.markdown) {
61
+ console.log(data.markdown);
62
+ }
63
+ else {
64
+ // Fallback: format issues as text
65
+ const issues = (data.issues ?? []);
66
+ if (issues.length === 0) {
67
+ console.log("No issues found. The site looks good!");
68
+ }
69
+ else {
70
+ console.log(`\n${issues.length} issues found:\n`);
71
+ for (const issue of issues) {
72
+ console.log(` [${issue.severity.toUpperCase()}] ${issue.title}`);
73
+ console.log(` ${issue.description}`);
74
+ console.log(` Fix: ${issue.fix}\n`);
75
+ }
76
+ }
77
+ }
78
+ process.stderr.write(`Done. Report: ${API_BASE}/scan/${scanId}\n`);
79
+ return;
80
+ }
81
+ if (data.status === "error") {
82
+ console.error(`Scan failed: ${data.error_message ?? "Unknown error"}`);
83
+ process.exit(1);
84
+ }
85
+ // Still pending
86
+ const elapsed = Math.floor((Date.now() - start) / 1000);
87
+ process.stderr.write(`\r ${elapsed}s elapsed...`);
88
+ await new Promise(r => setTimeout(r, 3000));
89
+ }
90
+ console.error("Scan timed out after 90 seconds");
91
+ process.exit(1);
92
+ }
93
+ // Parse args
94
+ const args = process.argv.slice(2);
95
+ const command = args[0];
96
+ if (!command || command === "help" || command === "--help" || command === "-h") {
97
+ printUsage();
98
+ process.exit(0);
99
+ }
100
+ if (command === "scan") {
101
+ const url = args[1];
102
+ if (!url) {
103
+ console.error("Error: URL required. Usage: seolint scan <url>");
104
+ process.exit(1);
105
+ }
106
+ const json = args.includes("--json");
107
+ scan(url, json).catch((err) => {
108
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
109
+ process.exit(1);
110
+ });
111
+ }
112
+ else {
113
+ console.error(`Unknown command: ${command}`);
114
+ printUsage();
115
+ process.exit(1);
116
+ }
117
+ export {};
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@randomcode-seolint/cli",
3
+ "version": "0.1.0",
4
+ "description": "Scan any website for SEO issues from your terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "seolint": "./dist/index.js"
8
+ },
9
+ "files": ["dist"],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": ["seo", "audit", "cli", "seolint", "ai", "claude"],
15
+ "author": "Random Code",
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "typescript": "^5.5.0",
19
+ "@types/node": "^22.0.0"
20
+ }
21
+ }