@rankcli/cli 0.0.22 → 0.0.24
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 +18 -8
- package/dist/{chunk-YX67Q5F6.mjs → chunk-NF755NKY.mjs} +28 -0
- package/dist/index.js +36 -12
- package/dist/index.mjs +9 -13
- package/dist/wasm-engine.js +28 -0
- package/dist/wasm-engine.mjs +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,17 @@ Developer-first SEO. CLI-native. Optimizes for Google *and* ChatGPT, Perplexity,
|
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
+
Standalone binary, no Node required:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
brew install integrallis/tap/rankcli
|
|
23
|
+
# or on Windows:
|
|
24
|
+
scoop bucket add integrallis https://github.com/integrallis/scoop-bucket
|
|
25
|
+
scoop install rankcli
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or via npm (requires Node 18+):
|
|
29
|
+
|
|
19
30
|
```bash
|
|
20
31
|
npm install -g @rankcli/cli
|
|
21
32
|
# or
|
|
@@ -59,23 +70,22 @@ Analyzes:
|
|
|
59
70
|
|
|
60
71
|
### 🔌 MCP Server (For AI Assistants)
|
|
61
72
|
|
|
62
|
-
Let Claude, Cursor, or any MCP-compatible AI run SEO audits for you. **
|
|
73
|
+
Let Claude, Cursor, or any MCP-compatible AI run SEO audits for you. **Free, local, no signup**:
|
|
63
74
|
|
|
64
75
|
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
65
76
|
```json
|
|
66
77
|
{
|
|
67
78
|
"mcpServers": {
|
|
68
79
|
"rankcli": {
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"Authorization": "Bearer YOUR_API_KEY"
|
|
72
|
-
}
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["@rankcli/mcp-server"]
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
85
|
```
|
|
77
86
|
|
|
78
|
-
|
|
87
|
+
Nothing is sent to RankCLI's servers — see [`@rankcli/mcp-server`](https://www.npmjs.com/package/@rankcli/mcp-server).
|
|
88
|
+
A hosted endpoint (with your [API key](https://rankcli.dev/dashboard)) is also available for teams that want dashboard sync.
|
|
79
89
|
|
|
80
90
|
**Available tools:**
|
|
81
91
|
- `seo_audit` — Full 280+ check audit on any URL
|
|
@@ -207,12 +217,12 @@ jobs:
|
|
|
207
217
|
| GEO Analysis | Basic | Full |
|
|
208
218
|
| Framework Fixes | - | ✅ 25+ |
|
|
209
219
|
| Auto-Fix PRs | - | ✅ |
|
|
210
|
-
| Sites | 1 |
|
|
220
|
+
| Sites | 1 | 3+ |
|
|
211
221
|
|
|
212
222
|
## Links
|
|
213
223
|
|
|
214
224
|
- **Docs**: [rankcli.dev/docs](https://rankcli.dev/docs)
|
|
215
|
-
- **GitHub**: [github.com/
|
|
225
|
+
- **GitHub**: [github.com/integrallis/rankcli-cli](https://github.com/integrallis/rankcli-cli)
|
|
216
226
|
- **Support**: support@rankcli.dev
|
|
217
227
|
|
|
218
228
|
## License
|
|
@@ -9,6 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
import * as vm from "vm";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import * as fs from "fs";
|
|
12
|
+
import * as os from "os";
|
|
12
13
|
var PREMIUM_CHECK_NAMES = [
|
|
13
14
|
"analyzeFeaturedSnippet",
|
|
14
15
|
"analyzeInternalLinkGraph",
|
|
@@ -36,6 +37,13 @@ var wasmModule = null;
|
|
|
36
37
|
var auditContext = null;
|
|
37
38
|
async function loadWasm() {
|
|
38
39
|
if (wasmModule) return wasmModule;
|
|
40
|
+
const sea = __require("sea");
|
|
41
|
+
if (sea.isSea()) {
|
|
42
|
+
const gluePath = extractSeaWasmAssets();
|
|
43
|
+
const dynamicRequire = __require("module").createRequire(gluePath);
|
|
44
|
+
wasmModule = dynamicRequire(gluePath);
|
|
45
|
+
return wasmModule;
|
|
46
|
+
}
|
|
39
47
|
const possiblePaths = [
|
|
40
48
|
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
41
49
|
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
@@ -52,6 +60,26 @@ async function loadWasm() {
|
|
|
52
60
|
Searched: ${possiblePaths.join(", ")}`
|
|
53
61
|
);
|
|
54
62
|
}
|
|
63
|
+
function extractSeaWasmAssets() {
|
|
64
|
+
const sea = __require("sea");
|
|
65
|
+
const dir = path.join(os.tmpdir(), "rankcli-wasm-sea");
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
const glueAsset = sea.getRawAsset("rankcli_audit_engine.js");
|
|
68
|
+
const wasmAsset = sea.getRawAsset("rankcli_audit_engine_bg.wasm");
|
|
69
|
+
if (typeof glueAsset === "string" || typeof wasmAsset === "string") {
|
|
70
|
+
throw new Error("SEA assets returned as string, expected binary ArrayBuffer");
|
|
71
|
+
}
|
|
72
|
+
const glueBuffer = Buffer.from(glueAsset);
|
|
73
|
+
const wasmBuffer = Buffer.from(wasmAsset);
|
|
74
|
+
const gluePath = path.join(dir, "rankcli_audit_engine.js");
|
|
75
|
+
const wasmPath = path.join(dir, "rankcli_audit_engine_bg.wasm");
|
|
76
|
+
const existingSize = fs.existsSync(wasmPath) ? fs.statSync(wasmPath).size : -1;
|
|
77
|
+
if (existingSize !== wasmBuffer.length) {
|
|
78
|
+
fs.writeFileSync(gluePath, glueBuffer);
|
|
79
|
+
fs.writeFileSync(wasmPath, wasmBuffer);
|
|
80
|
+
}
|
|
81
|
+
return gluePath;
|
|
82
|
+
}
|
|
55
83
|
async function initEngine(decryptPassphrase) {
|
|
56
84
|
const wasm = await loadWasm();
|
|
57
85
|
try {
|
package/dist/index.js
CHANGED
|
@@ -672,6 +672,7 @@ function hasFeature(tier, feature) {
|
|
|
672
672
|
var vm = __toESM(require("vm"));
|
|
673
673
|
var path = __toESM(require("path"));
|
|
674
674
|
var fs = __toESM(require("fs"));
|
|
675
|
+
var os = __toESM(require("os"));
|
|
675
676
|
var PREMIUM_CHECK_NAMES = [
|
|
676
677
|
"analyzeFeaturedSnippet",
|
|
677
678
|
"analyzeInternalLinkGraph",
|
|
@@ -699,6 +700,13 @@ var wasmModule = null;
|
|
|
699
700
|
var auditContext = null;
|
|
700
701
|
async function loadWasm() {
|
|
701
702
|
if (wasmModule) return wasmModule;
|
|
703
|
+
const sea = require("sea");
|
|
704
|
+
if (sea.isSea()) {
|
|
705
|
+
const gluePath = extractSeaWasmAssets();
|
|
706
|
+
const dynamicRequire = require("module").createRequire(gluePath);
|
|
707
|
+
wasmModule = dynamicRequire(gluePath);
|
|
708
|
+
return wasmModule;
|
|
709
|
+
}
|
|
702
710
|
const possiblePaths = [
|
|
703
711
|
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
704
712
|
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
@@ -715,6 +723,26 @@ async function loadWasm() {
|
|
|
715
723
|
Searched: ${possiblePaths.join(", ")}`
|
|
716
724
|
);
|
|
717
725
|
}
|
|
726
|
+
function extractSeaWasmAssets() {
|
|
727
|
+
const sea = require("sea");
|
|
728
|
+
const dir = path.join(os.tmpdir(), "rankcli-wasm-sea");
|
|
729
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
730
|
+
const glueAsset = sea.getRawAsset("rankcli_audit_engine.js");
|
|
731
|
+
const wasmAsset = sea.getRawAsset("rankcli_audit_engine_bg.wasm");
|
|
732
|
+
if (typeof glueAsset === "string" || typeof wasmAsset === "string") {
|
|
733
|
+
throw new Error("SEA assets returned as string, expected binary ArrayBuffer");
|
|
734
|
+
}
|
|
735
|
+
const glueBuffer = Buffer.from(glueAsset);
|
|
736
|
+
const wasmBuffer = Buffer.from(wasmAsset);
|
|
737
|
+
const gluePath = path.join(dir, "rankcli_audit_engine.js");
|
|
738
|
+
const wasmPath = path.join(dir, "rankcli_audit_engine_bg.wasm");
|
|
739
|
+
const existingSize = fs.existsSync(wasmPath) ? fs.statSync(wasmPath).size : -1;
|
|
740
|
+
if (existingSize !== wasmBuffer.length) {
|
|
741
|
+
fs.writeFileSync(gluePath, glueBuffer);
|
|
742
|
+
fs.writeFileSync(wasmPath, wasmBuffer);
|
|
743
|
+
}
|
|
744
|
+
return gluePath;
|
|
745
|
+
}
|
|
718
746
|
async function initEngine(decryptPassphrase) {
|
|
719
747
|
const wasm = await loadWasm();
|
|
720
748
|
try {
|
|
@@ -806,7 +834,7 @@ async function getPremiumChecks() {
|
|
|
806
834
|
}
|
|
807
835
|
|
|
808
836
|
// src/commands/audit.ts
|
|
809
|
-
var { detectFramework: detectFramework2, getFrameworkDisplayName
|
|
837
|
+
var { detectFramework: detectFramework2, getFrameworkDisplayName } = import_agent_runtime.frameworks;
|
|
810
838
|
async function fetchHtml(url, timeout = 1e4) {
|
|
811
839
|
try {
|
|
812
840
|
const controller = new AbortController();
|
|
@@ -1069,6 +1097,11 @@ async function audit(options) {
|
|
|
1069
1097
|
url: pageUrl,
|
|
1070
1098
|
maxPages: 1,
|
|
1071
1099
|
checkBrokenLinks: options.checkLinks || false,
|
|
1100
|
+
// Bundled with --check-links: both mean "verify actual link
|
|
1101
|
+
// targets, which costs real requests" - hreflang reciprocity and
|
|
1102
|
+
// broken-target checks were previously unreachable from any real
|
|
1103
|
+
// caller since nothing ever set this true.
|
|
1104
|
+
checkHreflangUrls: options.checkLinks || false,
|
|
1072
1105
|
checksLimit,
|
|
1073
1106
|
premiumChecks
|
|
1074
1107
|
});
|
|
@@ -1101,16 +1134,7 @@ async function audit(options) {
|
|
|
1101
1134
|
return;
|
|
1102
1135
|
}
|
|
1103
1136
|
auditSpinner.succeed(`Audited ${successCount}/${pagesToAudit.length} pages in ${parallelTime}ms`);
|
|
1104
|
-
|
|
1105
|
-
if (detectedFramework.framework !== "unknown" && homepageHtml) {
|
|
1106
|
-
try {
|
|
1107
|
-
const frameworkIssues = await getFrameworkIssues(detectedFramework, homepageHtml, url);
|
|
1108
|
-
if (frameworkIssues.length > 0) {
|
|
1109
|
-
mergedIssues = [...mergedIssues, ...frameworkIssues];
|
|
1110
|
-
}
|
|
1111
|
-
} catch {
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1137
|
+
const mergedIssues = mergeIssues(allIssues);
|
|
1114
1138
|
let aiAnalysis = null;
|
|
1115
1139
|
if (enableAI && aiKey) {
|
|
1116
1140
|
const aiSpinner = (0, import_ora.default)(`Running AI analysis (${aiProvider})...`).start();
|
|
@@ -3382,7 +3406,7 @@ async function fetchAndSaveSubscription() {
|
|
|
3382
3406
|
}
|
|
3383
3407
|
|
|
3384
3408
|
// src/version.ts
|
|
3385
|
-
var VERSION = "0.0.
|
|
3409
|
+
var VERSION = "0.0.23";
|
|
3386
3410
|
|
|
3387
3411
|
// src/index.ts
|
|
3388
3412
|
var program = new import_commander.Command();
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getPremiumChecks,
|
|
4
4
|
initEngine
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NF755NKY.mjs";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -650,7 +650,7 @@ function hasFeature(tier, feature) {
|
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
// src/commands/audit.ts
|
|
653
|
-
var { detectFramework: detectFramework2, getFrameworkDisplayName
|
|
653
|
+
var { detectFramework: detectFramework2, getFrameworkDisplayName } = frameworks;
|
|
654
654
|
async function fetchHtml(url, timeout = 1e4) {
|
|
655
655
|
try {
|
|
656
656
|
const controller = new AbortController();
|
|
@@ -913,6 +913,11 @@ async function audit(options) {
|
|
|
913
913
|
url: pageUrl,
|
|
914
914
|
maxPages: 1,
|
|
915
915
|
checkBrokenLinks: options.checkLinks || false,
|
|
916
|
+
// Bundled with --check-links: both mean "verify actual link
|
|
917
|
+
// targets, which costs real requests" - hreflang reciprocity and
|
|
918
|
+
// broken-target checks were previously unreachable from any real
|
|
919
|
+
// caller since nothing ever set this true.
|
|
920
|
+
checkHreflangUrls: options.checkLinks || false,
|
|
916
921
|
checksLimit,
|
|
917
922
|
premiumChecks
|
|
918
923
|
});
|
|
@@ -945,16 +950,7 @@ async function audit(options) {
|
|
|
945
950
|
return;
|
|
946
951
|
}
|
|
947
952
|
auditSpinner.succeed(`Audited ${successCount}/${pagesToAudit.length} pages in ${parallelTime}ms`);
|
|
948
|
-
|
|
949
|
-
if (detectedFramework.framework !== "unknown" && homepageHtml) {
|
|
950
|
-
try {
|
|
951
|
-
const frameworkIssues = await getFrameworkIssues(detectedFramework, homepageHtml, url);
|
|
952
|
-
if (frameworkIssues.length > 0) {
|
|
953
|
-
mergedIssues = [...mergedIssues, ...frameworkIssues];
|
|
954
|
-
}
|
|
955
|
-
} catch {
|
|
956
|
-
}
|
|
957
|
-
}
|
|
953
|
+
const mergedIssues = mergeIssues(allIssues);
|
|
958
954
|
let aiAnalysis = null;
|
|
959
955
|
if (enableAI && aiKey) {
|
|
960
956
|
const aiSpinner = ora(`Running AI analysis (${aiProvider})...`).start();
|
|
@@ -3265,7 +3261,7 @@ async function fetchAndSaveSubscription() {
|
|
|
3265
3261
|
}
|
|
3266
3262
|
|
|
3267
3263
|
// src/version.ts
|
|
3268
|
-
var VERSION = "0.0.
|
|
3264
|
+
var VERSION = "0.0.23";
|
|
3269
3265
|
|
|
3270
3266
|
// src/index.ts
|
|
3271
3267
|
var program = new Command();
|
package/dist/wasm-engine.js
CHANGED
|
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(wasm_engine_exports);
|
|
|
39
39
|
var vm = __toESM(require("vm"));
|
|
40
40
|
var path = __toESM(require("path"));
|
|
41
41
|
var fs = __toESM(require("fs"));
|
|
42
|
+
var os = __toESM(require("os"));
|
|
42
43
|
var PREMIUM_CHECK_NAMES = [
|
|
43
44
|
"analyzeFeaturedSnippet",
|
|
44
45
|
"analyzeInternalLinkGraph",
|
|
@@ -66,6 +67,13 @@ var wasmModule = null;
|
|
|
66
67
|
var auditContext = null;
|
|
67
68
|
async function loadWasm() {
|
|
68
69
|
if (wasmModule) return wasmModule;
|
|
70
|
+
const sea = require("sea");
|
|
71
|
+
if (sea.isSea()) {
|
|
72
|
+
const gluePath = extractSeaWasmAssets();
|
|
73
|
+
const dynamicRequire = require("module").createRequire(gluePath);
|
|
74
|
+
wasmModule = dynamicRequire(gluePath);
|
|
75
|
+
return wasmModule;
|
|
76
|
+
}
|
|
69
77
|
const possiblePaths = [
|
|
70
78
|
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
71
79
|
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
@@ -82,6 +90,26 @@ async function loadWasm() {
|
|
|
82
90
|
Searched: ${possiblePaths.join(", ")}`
|
|
83
91
|
);
|
|
84
92
|
}
|
|
93
|
+
function extractSeaWasmAssets() {
|
|
94
|
+
const sea = require("sea");
|
|
95
|
+
const dir = path.join(os.tmpdir(), "rankcli-wasm-sea");
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
const glueAsset = sea.getRawAsset("rankcli_audit_engine.js");
|
|
98
|
+
const wasmAsset = sea.getRawAsset("rankcli_audit_engine_bg.wasm");
|
|
99
|
+
if (typeof glueAsset === "string" || typeof wasmAsset === "string") {
|
|
100
|
+
throw new Error("SEA assets returned as string, expected binary ArrayBuffer");
|
|
101
|
+
}
|
|
102
|
+
const glueBuffer = Buffer.from(glueAsset);
|
|
103
|
+
const wasmBuffer = Buffer.from(wasmAsset);
|
|
104
|
+
const gluePath = path.join(dir, "rankcli_audit_engine.js");
|
|
105
|
+
const wasmPath = path.join(dir, "rankcli_audit_engine_bg.wasm");
|
|
106
|
+
const existingSize = fs.existsSync(wasmPath) ? fs.statSync(wasmPath).size : -1;
|
|
107
|
+
if (existingSize !== wasmBuffer.length) {
|
|
108
|
+
fs.writeFileSync(gluePath, glueBuffer);
|
|
109
|
+
fs.writeFileSync(wasmPath, wasmBuffer);
|
|
110
|
+
}
|
|
111
|
+
return gluePath;
|
|
112
|
+
}
|
|
85
113
|
async function initEngine(decryptPassphrase) {
|
|
86
114
|
const wasm = await loadWasm();
|
|
87
115
|
try {
|
package/dist/wasm-engine.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "RankCLI - Ship code, get ranked. SEO meets CI/CD.",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"update-version": "node scripts/update-version.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@rankcli/agent-runtime": "^0.0.
|
|
48
|
+
"@rankcli/agent-runtime": "^0.0.17",
|
|
49
49
|
"@supabase/supabase-js": "^2.45.0",
|
|
50
50
|
"chalk": "^5.3.0",
|
|
51
51
|
"commander": "^12.0.0",
|