@rankcli/cli 0.0.21 → 0.0.23
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-NF755NKY.mjs +190 -0
- package/dist/index.js +205 -23
- package/dist/index.mjs +37 -17
- package/dist/wasm-engine.d.mts +28 -40
- package/dist/wasm-engine.d.ts +28 -40
- package/dist/wasm-engine.js +81 -65
- package/dist/wasm-engine.mjs +7 -162
- package/package.json +5 -2
- package/dist/chunk-Y6FXYEAI.mjs +0 -10
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
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/wasm-engine.ts
|
|
9
|
+
import * as vm from "vm";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
var PREMIUM_CHECK_NAMES = [
|
|
14
|
+
"analyzeFeaturedSnippet",
|
|
15
|
+
"analyzeInternalLinkGraph",
|
|
16
|
+
"analyzeEEATSignals",
|
|
17
|
+
"analyzeContentScience",
|
|
18
|
+
"analyzeKeywordCannibalizationSinglePage",
|
|
19
|
+
"analyzeConversionElements",
|
|
20
|
+
"analyzeKeywordPlacement",
|
|
21
|
+
"analyzeTopicalClusters",
|
|
22
|
+
"analyzePlatformPresence",
|
|
23
|
+
"analyzeInteractiveTools",
|
|
24
|
+
"analyzeFunnelIntent",
|
|
25
|
+
"analyzeNavBoostSignals",
|
|
26
|
+
"analyzeEntitySEO",
|
|
27
|
+
"analyzeFreshnessSignals",
|
|
28
|
+
"analyzeAIContentStructure",
|
|
29
|
+
"analyzeRAGChunkReadiness",
|
|
30
|
+
"analyzeCitationQuality",
|
|
31
|
+
"analyzeAnswerConciseness",
|
|
32
|
+
"analyzeBrandMentionOptimization",
|
|
33
|
+
"analyzeAICitationWorthiness",
|
|
34
|
+
"analyzeReviewEcosystem"
|
|
35
|
+
];
|
|
36
|
+
var wasmModule = null;
|
|
37
|
+
var auditContext = null;
|
|
38
|
+
async function loadWasm() {
|
|
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
|
+
}
|
|
47
|
+
const possiblePaths = [
|
|
48
|
+
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
49
|
+
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
50
|
+
path.join(__dirname, "../../native/pkg/rankcli_audit_engine.js")
|
|
51
|
+
];
|
|
52
|
+
for (const wasmPath of possiblePaths) {
|
|
53
|
+
if (fs.existsSync(wasmPath)) {
|
|
54
|
+
wasmModule = __require(wasmPath);
|
|
55
|
+
return wasmModule;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw new Error(
|
|
59
|
+
`WASM engine not found. Run \`npm run build:wasm\` first.
|
|
60
|
+
Searched: ${possiblePaths.join(", ")}`
|
|
61
|
+
);
|
|
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
|
+
}
|
|
83
|
+
async function initEngine(decryptPassphrase) {
|
|
84
|
+
const wasm = await loadWasm();
|
|
85
|
+
try {
|
|
86
|
+
wasm.init_engine(decryptPassphrase);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`Failed to decrypt premium checks: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
if (!wasm.is_initialized()) {
|
|
91
|
+
throw new Error("Failed to decrypt premium checks: engine did not initialize");
|
|
92
|
+
}
|
|
93
|
+
const jsCode = wasm.get_decrypted_code();
|
|
94
|
+
createExecutionContext(jsCode);
|
|
95
|
+
}
|
|
96
|
+
function createExecutionContext(jsCode) {
|
|
97
|
+
const sandbox = {
|
|
98
|
+
// Console for debugging (can be disabled in production)
|
|
99
|
+
console: {
|
|
100
|
+
log: () => {
|
|
101
|
+
},
|
|
102
|
+
warn: () => {
|
|
103
|
+
},
|
|
104
|
+
error: () => {
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
// Timers (some checks may use setTimeout)
|
|
108
|
+
setTimeout,
|
|
109
|
+
clearTimeout,
|
|
110
|
+
// JSON parsing
|
|
111
|
+
JSON,
|
|
112
|
+
// Math operations
|
|
113
|
+
Math,
|
|
114
|
+
// String/Array operations
|
|
115
|
+
String,
|
|
116
|
+
Array,
|
|
117
|
+
Object,
|
|
118
|
+
RegExp,
|
|
119
|
+
Date,
|
|
120
|
+
// URL parsing
|
|
121
|
+
URL,
|
|
122
|
+
URLSearchParams,
|
|
123
|
+
// Node.js globals needed by bundled dependencies (e.g., cheerio)
|
|
124
|
+
Buffer,
|
|
125
|
+
process: { env: {} },
|
|
126
|
+
// TextEncoder/TextDecoder for string handling
|
|
127
|
+
TextEncoder,
|
|
128
|
+
TextDecoder,
|
|
129
|
+
// The RankCLI namespace will be populated by the code
|
|
130
|
+
RankCLI: {},
|
|
131
|
+
// Global reference
|
|
132
|
+
globalThis: {}
|
|
133
|
+
};
|
|
134
|
+
sandbox.globalThis = sandbox;
|
|
135
|
+
auditContext = vm.createContext(sandbox);
|
|
136
|
+
try {
|
|
137
|
+
vm.runInContext(jsCode, auditContext, {
|
|
138
|
+
filename: "rankcli-checks.js",
|
|
139
|
+
timeout: 5e3
|
|
140
|
+
// 5 second timeout for initialization
|
|
141
|
+
});
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw new Error(`Failed to initialize audit checks: ${error}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function callSandboxCheck(fnName, html, url) {
|
|
147
|
+
if (!auditContext) {
|
|
148
|
+
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
149
|
+
}
|
|
150
|
+
const script = new vm.Script(`
|
|
151
|
+
(function() {
|
|
152
|
+
if (typeof RankCLI.${fnName} !== 'function') {
|
|
153
|
+
throw new Error('RankCLI.${fnName} not found in decrypted code');
|
|
154
|
+
}
|
|
155
|
+
return JSON.stringify(RankCLI.${fnName}(${JSON.stringify(html)}, ${JSON.stringify(url)}));
|
|
156
|
+
})()
|
|
157
|
+
`);
|
|
158
|
+
const resultJson = script.runInContext(auditContext, { timeout: 1e4 });
|
|
159
|
+
return JSON.parse(resultJson);
|
|
160
|
+
}
|
|
161
|
+
async function getPremiumChecks() {
|
|
162
|
+
const wasm = await loadWasm();
|
|
163
|
+
if (!wasm.is_initialized()) {
|
|
164
|
+
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
165
|
+
}
|
|
166
|
+
const checks = {};
|
|
167
|
+
for (const name of PREMIUM_CHECK_NAMES) {
|
|
168
|
+
checks[name] = (html, url) => callSandboxCheck(name, html, url);
|
|
169
|
+
}
|
|
170
|
+
return checks;
|
|
171
|
+
}
|
|
172
|
+
async function getVersion() {
|
|
173
|
+
const wasm = await loadWasm();
|
|
174
|
+
return wasm.version();
|
|
175
|
+
}
|
|
176
|
+
async function isReady() {
|
|
177
|
+
try {
|
|
178
|
+
const wasm = await loadWasm();
|
|
179
|
+
return wasm.is_initialized();
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
initEngine,
|
|
187
|
+
getPremiumChecks,
|
|
188
|
+
getVersion,
|
|
189
|
+
isReady
|
|
190
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -289,7 +289,7 @@ var import_chalk2 = __toESM(require("chalk"));
|
|
|
289
289
|
// src/lib/config.ts
|
|
290
290
|
var import_conf = __toESM(require("conf"));
|
|
291
291
|
var import_supabase_js = require("@supabase/supabase-js");
|
|
292
|
-
var INJECTED_CONFIG = {"supabaseUrl":"https://bspljbxwbjiqueeyzyat.supabase.co","supabaseAnonKey":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJzcGxqYnh3YmppcXVlZXl6eWF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzQ1NTksImV4cCI6MjA4NDg1MDU1OX0.7gprxM2gT9cZ99sjZURLltrln78dEnwyFFKYpTzBaUU","webAppUrl":"https://rankcli.dev"};
|
|
292
|
+
var INJECTED_CONFIG = {"supabaseUrl":"https://bspljbxwbjiqueeyzyat.supabase.co","supabaseAnonKey":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJzcGxqYnh3YmppcXVlZXl6eWF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzQ1NTksImV4cCI6MjA4NDg1MDU1OX0.7gprxM2gT9cZ99sjZURLltrln78dEnwyFFKYpTzBaUU","webAppUrl":"https://rankcli.dev","decryptPassphrase":"lKFLeIw/xGPa6E8c4XswZX/oiLtJf/yLF/Y6zwovte8="};
|
|
293
293
|
function getSupabaseUrl() {
|
|
294
294
|
const url = process.env.RANKCLI_SUPABASE_URL || process.env.VITE_SUPABASE_URL || INJECTED_CONFIG?.supabaseUrl;
|
|
295
295
|
if (!url) {
|
|
@@ -319,6 +319,9 @@ function getSupabaseAnonKeyLazy() {
|
|
|
319
319
|
return _supabaseAnonKey;
|
|
320
320
|
}
|
|
321
321
|
var WEB_APP_URL = process.env.RANKCLI_WEB_URL || INJECTED_CONFIG?.webAppUrl || "https://rankcli.dev";
|
|
322
|
+
function getDecryptPassphrase() {
|
|
323
|
+
return process.env.RANKCLI_BUILD_KEY || INJECTED_CONFIG?.decryptPassphrase;
|
|
324
|
+
}
|
|
322
325
|
function getApiUrl() {
|
|
323
326
|
return process.env.RANKCLI_API_URL || getSupabaseUrlLazy();
|
|
324
327
|
}
|
|
@@ -462,6 +465,7 @@ function getUserInfo() {
|
|
|
462
465
|
return {
|
|
463
466
|
userId: config.get("userId"),
|
|
464
467
|
email: config.get("email"),
|
|
468
|
+
planId: config.get("planId"),
|
|
465
469
|
planName: config.get("planName") || "Free",
|
|
466
470
|
sitesLimit: config.get("sitesLimit") || 1
|
|
467
471
|
};
|
|
@@ -664,8 +668,173 @@ function hasFeature(tier, feature) {
|
|
|
664
668
|
return limits?.features?.[feature] ?? false;
|
|
665
669
|
}
|
|
666
670
|
|
|
671
|
+
// src/wasm-engine.ts
|
|
672
|
+
var vm = __toESM(require("vm"));
|
|
673
|
+
var path = __toESM(require("path"));
|
|
674
|
+
var fs = __toESM(require("fs"));
|
|
675
|
+
var os = __toESM(require("os"));
|
|
676
|
+
var PREMIUM_CHECK_NAMES = [
|
|
677
|
+
"analyzeFeaturedSnippet",
|
|
678
|
+
"analyzeInternalLinkGraph",
|
|
679
|
+
"analyzeEEATSignals",
|
|
680
|
+
"analyzeContentScience",
|
|
681
|
+
"analyzeKeywordCannibalizationSinglePage",
|
|
682
|
+
"analyzeConversionElements",
|
|
683
|
+
"analyzeKeywordPlacement",
|
|
684
|
+
"analyzeTopicalClusters",
|
|
685
|
+
"analyzePlatformPresence",
|
|
686
|
+
"analyzeInteractiveTools",
|
|
687
|
+
"analyzeFunnelIntent",
|
|
688
|
+
"analyzeNavBoostSignals",
|
|
689
|
+
"analyzeEntitySEO",
|
|
690
|
+
"analyzeFreshnessSignals",
|
|
691
|
+
"analyzeAIContentStructure",
|
|
692
|
+
"analyzeRAGChunkReadiness",
|
|
693
|
+
"analyzeCitationQuality",
|
|
694
|
+
"analyzeAnswerConciseness",
|
|
695
|
+
"analyzeBrandMentionOptimization",
|
|
696
|
+
"analyzeAICitationWorthiness",
|
|
697
|
+
"analyzeReviewEcosystem"
|
|
698
|
+
];
|
|
699
|
+
var wasmModule = null;
|
|
700
|
+
var auditContext = null;
|
|
701
|
+
async function loadWasm() {
|
|
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
|
+
}
|
|
710
|
+
const possiblePaths = [
|
|
711
|
+
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
712
|
+
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
713
|
+
path.join(__dirname, "../../native/pkg/rankcli_audit_engine.js")
|
|
714
|
+
];
|
|
715
|
+
for (const wasmPath of possiblePaths) {
|
|
716
|
+
if (fs.existsSync(wasmPath)) {
|
|
717
|
+
wasmModule = require(wasmPath);
|
|
718
|
+
return wasmModule;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
throw new Error(
|
|
722
|
+
`WASM engine not found. Run \`npm run build:wasm\` first.
|
|
723
|
+
Searched: ${possiblePaths.join(", ")}`
|
|
724
|
+
);
|
|
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
|
+
}
|
|
746
|
+
async function initEngine(decryptPassphrase) {
|
|
747
|
+
const wasm = await loadWasm();
|
|
748
|
+
try {
|
|
749
|
+
wasm.init_engine(decryptPassphrase);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
throw new Error(`Failed to decrypt premium checks: ${error}`);
|
|
752
|
+
}
|
|
753
|
+
if (!wasm.is_initialized()) {
|
|
754
|
+
throw new Error("Failed to decrypt premium checks: engine did not initialize");
|
|
755
|
+
}
|
|
756
|
+
const jsCode = wasm.get_decrypted_code();
|
|
757
|
+
createExecutionContext(jsCode);
|
|
758
|
+
}
|
|
759
|
+
function createExecutionContext(jsCode) {
|
|
760
|
+
const sandbox = {
|
|
761
|
+
// Console for debugging (can be disabled in production)
|
|
762
|
+
console: {
|
|
763
|
+
log: () => {
|
|
764
|
+
},
|
|
765
|
+
warn: () => {
|
|
766
|
+
},
|
|
767
|
+
error: () => {
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
// Timers (some checks may use setTimeout)
|
|
771
|
+
setTimeout,
|
|
772
|
+
clearTimeout,
|
|
773
|
+
// JSON parsing
|
|
774
|
+
JSON,
|
|
775
|
+
// Math operations
|
|
776
|
+
Math,
|
|
777
|
+
// String/Array operations
|
|
778
|
+
String,
|
|
779
|
+
Array,
|
|
780
|
+
Object,
|
|
781
|
+
RegExp,
|
|
782
|
+
Date,
|
|
783
|
+
// URL parsing
|
|
784
|
+
URL,
|
|
785
|
+
URLSearchParams,
|
|
786
|
+
// Node.js globals needed by bundled dependencies (e.g., cheerio)
|
|
787
|
+
Buffer,
|
|
788
|
+
process: { env: {} },
|
|
789
|
+
// TextEncoder/TextDecoder for string handling
|
|
790
|
+
TextEncoder,
|
|
791
|
+
TextDecoder,
|
|
792
|
+
// The RankCLI namespace will be populated by the code
|
|
793
|
+
RankCLI: {},
|
|
794
|
+
// Global reference
|
|
795
|
+
globalThis: {}
|
|
796
|
+
};
|
|
797
|
+
sandbox.globalThis = sandbox;
|
|
798
|
+
auditContext = vm.createContext(sandbox);
|
|
799
|
+
try {
|
|
800
|
+
vm.runInContext(jsCode, auditContext, {
|
|
801
|
+
filename: "rankcli-checks.js",
|
|
802
|
+
timeout: 5e3
|
|
803
|
+
// 5 second timeout for initialization
|
|
804
|
+
});
|
|
805
|
+
} catch (error) {
|
|
806
|
+
throw new Error(`Failed to initialize audit checks: ${error}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function callSandboxCheck(fnName, html, url) {
|
|
810
|
+
if (!auditContext) {
|
|
811
|
+
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
812
|
+
}
|
|
813
|
+
const script = new vm.Script(`
|
|
814
|
+
(function() {
|
|
815
|
+
if (typeof RankCLI.${fnName} !== 'function') {
|
|
816
|
+
throw new Error('RankCLI.${fnName} not found in decrypted code');
|
|
817
|
+
}
|
|
818
|
+
return JSON.stringify(RankCLI.${fnName}(${JSON.stringify(html)}, ${JSON.stringify(url)}));
|
|
819
|
+
})()
|
|
820
|
+
`);
|
|
821
|
+
const resultJson = script.runInContext(auditContext, { timeout: 1e4 });
|
|
822
|
+
return JSON.parse(resultJson);
|
|
823
|
+
}
|
|
824
|
+
async function getPremiumChecks() {
|
|
825
|
+
const wasm = await loadWasm();
|
|
826
|
+
if (!wasm.is_initialized()) {
|
|
827
|
+
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
828
|
+
}
|
|
829
|
+
const checks = {};
|
|
830
|
+
for (const name of PREMIUM_CHECK_NAMES) {
|
|
831
|
+
checks[name] = (html, url) => callSandboxCheck(name, html, url);
|
|
832
|
+
}
|
|
833
|
+
return checks;
|
|
834
|
+
}
|
|
835
|
+
|
|
667
836
|
// src/commands/audit.ts
|
|
668
|
-
var { detectFramework: detectFramework2, getFrameworkDisplayName
|
|
837
|
+
var { detectFramework: detectFramework2, getFrameworkDisplayName } = import_agent_runtime.frameworks;
|
|
669
838
|
async function fetchHtml(url, timeout = 1e4) {
|
|
670
839
|
try {
|
|
671
840
|
const controller = new AbortController();
|
|
@@ -868,6 +1037,21 @@ async function audit(options) {
|
|
|
868
1037
|
const aiProvider = options.aiProvider || "openai";
|
|
869
1038
|
const aiKey = options.aiKey || (aiProvider === "anthropic" ? process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY : process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY) || "";
|
|
870
1039
|
const loggedIn = isLoggedIn();
|
|
1040
|
+
const { planId } = getUserInfo();
|
|
1041
|
+
const tierId = planId && planId in TIER_LIMITS ? planId : loggedIn ? "free" : "free_anon";
|
|
1042
|
+
const checksLimit = TIER_LIMITS[tierId].checks;
|
|
1043
|
+
let premiumChecks;
|
|
1044
|
+
if (checksLimit > TIER_LIMITS.free.checks) {
|
|
1045
|
+
const passphrase = getDecryptPassphrase();
|
|
1046
|
+
if (passphrase) {
|
|
1047
|
+
try {
|
|
1048
|
+
await initEngine(passphrase);
|
|
1049
|
+
premiumChecks = await getPremiumChecks();
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
console.error(import_chalk2.default.dim(`(premium checks unavailable: ${error instanceof Error ? error.message : error})`));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
871
1055
|
if (!url) {
|
|
872
1056
|
console.log("\n\u26A0\uFE0F Please provide a URL to audit:");
|
|
873
1057
|
console.log(" rankcli audit --url https://your-site.com");
|
|
@@ -912,7 +1096,14 @@ async function audit(options) {
|
|
|
912
1096
|
const report = await (0, import_agent_runtime.runFullAudit)({
|
|
913
1097
|
url: pageUrl,
|
|
914
1098
|
maxPages: 1,
|
|
915
|
-
checkBrokenLinks: options.checkLinks || false
|
|
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,
|
|
1105
|
+
checksLimit,
|
|
1106
|
+
premiumChecks
|
|
916
1107
|
});
|
|
917
1108
|
auditSpinner.text = `Auditing pages... (${index + 1}/${pagesToAudit.length} complete)`;
|
|
918
1109
|
return { report, url: pageUrl };
|
|
@@ -943,16 +1134,7 @@ async function audit(options) {
|
|
|
943
1134
|
return;
|
|
944
1135
|
}
|
|
945
1136
|
auditSpinner.succeed(`Audited ${successCount}/${pagesToAudit.length} pages in ${parallelTime}ms`);
|
|
946
|
-
|
|
947
|
-
if (detectedFramework.framework !== "unknown" && homepageHtml) {
|
|
948
|
-
try {
|
|
949
|
-
const frameworkIssues = await getFrameworkIssues(detectedFramework, homepageHtml, url);
|
|
950
|
-
if (frameworkIssues.length > 0) {
|
|
951
|
-
mergedIssues = [...mergedIssues, ...frameworkIssues];
|
|
952
|
-
}
|
|
953
|
-
} catch {
|
|
954
|
-
}
|
|
955
|
-
}
|
|
1137
|
+
const mergedIssues = mergeIssues(allIssues);
|
|
956
1138
|
let aiAnalysis = null;
|
|
957
1139
|
if (enableAI && aiKey) {
|
|
958
1140
|
const aiSpinner = (0, import_ora.default)(`Running AI analysis (${aiProvider})...`).start();
|
|
@@ -2673,13 +2855,13 @@ function extractBodyText(html) {
|
|
|
2673
2855
|
|
|
2674
2856
|
// src/commands/setup.ts
|
|
2675
2857
|
var readline2 = __toESM(require("readline"));
|
|
2676
|
-
var
|
|
2677
|
-
var
|
|
2858
|
+
var fs2 = __toESM(require("fs"));
|
|
2859
|
+
var path2 = __toESM(require("path"));
|
|
2678
2860
|
var import_agent_runtime8 = require("@rankcli/agent-runtime");
|
|
2679
2861
|
async function setup(options) {
|
|
2680
2862
|
console.log("\n\u{1F527} SEO Autopilot - Setup\n");
|
|
2681
2863
|
const projectPath = process.cwd();
|
|
2682
|
-
if (!
|
|
2864
|
+
if (!fs2.existsSync(path2.join(projectPath, "package.json")) && !fs2.existsSync(path2.join(projectPath, "index.html"))) {
|
|
2683
2865
|
console.log("\u26A0\uFE0F No package.json or index.html found. Are you in a web project directory?\n");
|
|
2684
2866
|
}
|
|
2685
2867
|
if (options.all || !options.ga4 && !options.gsc && !options.githubAction) {
|
|
@@ -2790,16 +2972,16 @@ async function setupGA4(projectPath, measurementId) {
|
|
|
2790
2972
|
console.log(result.code);
|
|
2791
2973
|
}
|
|
2792
2974
|
}
|
|
2793
|
-
const envExamplePath =
|
|
2975
|
+
const envExamplePath = path2.join(projectPath, ".env.example");
|
|
2794
2976
|
const envContent = (0, import_agent_runtime8.generateGA4EnvTemplate)();
|
|
2795
|
-
if (
|
|
2796
|
-
const existing =
|
|
2977
|
+
if (fs2.existsSync(envExamplePath)) {
|
|
2978
|
+
const existing = fs2.readFileSync(envExamplePath, "utf-8");
|
|
2797
2979
|
if (!existing.includes("GA4_MEASUREMENT_ID")) {
|
|
2798
|
-
|
|
2980
|
+
fs2.appendFileSync(envExamplePath, "\n" + envContent);
|
|
2799
2981
|
console.log(" Updated: .env.example");
|
|
2800
2982
|
}
|
|
2801
2983
|
} else {
|
|
2802
|
-
|
|
2984
|
+
fs2.writeFileSync(envExamplePath, envContent);
|
|
2803
2985
|
console.log(" Created: .env.example");
|
|
2804
2986
|
}
|
|
2805
2987
|
console.log("");
|
|
@@ -2833,7 +3015,7 @@ async function setupGitHubAction(projectPath, schedule, siteUrl) {
|
|
|
2833
3015
|
const { files, instructions } = (0, import_agent_runtime8.writeGitHubActionFiles)(projectPath, config2);
|
|
2834
3016
|
console.log("\u2705 GitHub Action files created:");
|
|
2835
3017
|
for (const file of files) {
|
|
2836
|
-
console.log(` - ${
|
|
3018
|
+
console.log(` - ${path2.relative(projectPath, file)}`);
|
|
2837
3019
|
}
|
|
2838
3020
|
console.log(instructions);
|
|
2839
3021
|
}
|
|
@@ -3224,7 +3406,7 @@ async function fetchAndSaveSubscription() {
|
|
|
3224
3406
|
}
|
|
3225
3407
|
|
|
3226
3408
|
// src/version.ts
|
|
3227
|
-
var VERSION = "0.0.
|
|
3409
|
+
var VERSION = "0.0.23";
|
|
3228
3410
|
|
|
3229
3411
|
// src/index.ts
|
|
3230
3412
|
var program = new import_commander.Command();
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
getPremiumChecks,
|
|
4
|
+
initEngine
|
|
5
|
+
} from "./chunk-NF755NKY.mjs";
|
|
3
6
|
|
|
4
7
|
// src/index.ts
|
|
5
8
|
import { Command } from "commander";
|
|
@@ -267,7 +270,7 @@ import chalk2 from "chalk";
|
|
|
267
270
|
// src/lib/config.ts
|
|
268
271
|
import Conf from "conf";
|
|
269
272
|
import { createClient } from "@supabase/supabase-js";
|
|
270
|
-
const INJECTED_CONFIG = {"supabaseUrl":"https://bspljbxwbjiqueeyzyat.supabase.co","supabaseAnonKey":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJzcGxqYnh3YmppcXVlZXl6eWF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzQ1NTksImV4cCI6MjA4NDg1MDU1OX0.7gprxM2gT9cZ99sjZURLltrln78dEnwyFFKYpTzBaUU","webAppUrl":"https://rankcli.dev"};
|
|
273
|
+
const INJECTED_CONFIG = {"supabaseUrl":"https://bspljbxwbjiqueeyzyat.supabase.co","supabaseAnonKey":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJzcGxqYnh3YmppcXVlZXl6eWF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzQ1NTksImV4cCI6MjA4NDg1MDU1OX0.7gprxM2gT9cZ99sjZURLltrln78dEnwyFFKYpTzBaUU","webAppUrl":"https://rankcli.dev","decryptPassphrase":"lKFLeIw/xGPa6E8c4XswZX/oiLtJf/yLF/Y6zwovte8="};
|
|
271
274
|
function getSupabaseUrl() {
|
|
272
275
|
const url = process.env.RANKCLI_SUPABASE_URL || process.env.VITE_SUPABASE_URL || INJECTED_CONFIG?.supabaseUrl;
|
|
273
276
|
if (!url) {
|
|
@@ -297,6 +300,9 @@ function getSupabaseAnonKeyLazy() {
|
|
|
297
300
|
return _supabaseAnonKey;
|
|
298
301
|
}
|
|
299
302
|
var WEB_APP_URL = process.env.RANKCLI_WEB_URL || INJECTED_CONFIG?.webAppUrl || "https://rankcli.dev";
|
|
303
|
+
function getDecryptPassphrase() {
|
|
304
|
+
return process.env.RANKCLI_BUILD_KEY || INJECTED_CONFIG?.decryptPassphrase;
|
|
305
|
+
}
|
|
300
306
|
function getApiUrl() {
|
|
301
307
|
return process.env.RANKCLI_API_URL || getSupabaseUrlLazy();
|
|
302
308
|
}
|
|
@@ -440,6 +446,7 @@ function getUserInfo() {
|
|
|
440
446
|
return {
|
|
441
447
|
userId: config.get("userId"),
|
|
442
448
|
email: config.get("email"),
|
|
449
|
+
planId: config.get("planId"),
|
|
443
450
|
planName: config.get("planName") || "Free",
|
|
444
451
|
sitesLimit: config.get("sitesLimit") || 1
|
|
445
452
|
};
|
|
@@ -643,7 +650,7 @@ function hasFeature(tier, feature) {
|
|
|
643
650
|
}
|
|
644
651
|
|
|
645
652
|
// src/commands/audit.ts
|
|
646
|
-
var { detectFramework: detectFramework2, getFrameworkDisplayName
|
|
653
|
+
var { detectFramework: detectFramework2, getFrameworkDisplayName } = frameworks;
|
|
647
654
|
async function fetchHtml(url, timeout = 1e4) {
|
|
648
655
|
try {
|
|
649
656
|
const controller = new AbortController();
|
|
@@ -846,6 +853,21 @@ async function audit(options) {
|
|
|
846
853
|
const aiProvider = options.aiProvider || "openai";
|
|
847
854
|
const aiKey = options.aiKey || (aiProvider === "anthropic" ? process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY : process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY) || "";
|
|
848
855
|
const loggedIn = isLoggedIn();
|
|
856
|
+
const { planId } = getUserInfo();
|
|
857
|
+
const tierId = planId && planId in TIER_LIMITS ? planId : loggedIn ? "free" : "free_anon";
|
|
858
|
+
const checksLimit = TIER_LIMITS[tierId].checks;
|
|
859
|
+
let premiumChecks;
|
|
860
|
+
if (checksLimit > TIER_LIMITS.free.checks) {
|
|
861
|
+
const passphrase = getDecryptPassphrase();
|
|
862
|
+
if (passphrase) {
|
|
863
|
+
try {
|
|
864
|
+
await initEngine(passphrase);
|
|
865
|
+
premiumChecks = await getPremiumChecks();
|
|
866
|
+
} catch (error) {
|
|
867
|
+
console.error(chalk2.dim(`(premium checks unavailable: ${error instanceof Error ? error.message : error})`));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
849
871
|
if (!url) {
|
|
850
872
|
console.log("\n\u26A0\uFE0F Please provide a URL to audit:");
|
|
851
873
|
console.log(" rankcli audit --url https://your-site.com");
|
|
@@ -890,7 +912,14 @@ async function audit(options) {
|
|
|
890
912
|
const report = await runFullAudit({
|
|
891
913
|
url: pageUrl,
|
|
892
914
|
maxPages: 1,
|
|
893
|
-
checkBrokenLinks: options.checkLinks || false
|
|
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,
|
|
921
|
+
checksLimit,
|
|
922
|
+
premiumChecks
|
|
894
923
|
});
|
|
895
924
|
auditSpinner.text = `Auditing pages... (${index + 1}/${pagesToAudit.length} complete)`;
|
|
896
925
|
return { report, url: pageUrl };
|
|
@@ -921,16 +950,7 @@ async function audit(options) {
|
|
|
921
950
|
return;
|
|
922
951
|
}
|
|
923
952
|
auditSpinner.succeed(`Audited ${successCount}/${pagesToAudit.length} pages in ${parallelTime}ms`);
|
|
924
|
-
|
|
925
|
-
if (detectedFramework.framework !== "unknown" && homepageHtml) {
|
|
926
|
-
try {
|
|
927
|
-
const frameworkIssues = await getFrameworkIssues(detectedFramework, homepageHtml, url);
|
|
928
|
-
if (frameworkIssues.length > 0) {
|
|
929
|
-
mergedIssues = [...mergedIssues, ...frameworkIssues];
|
|
930
|
-
}
|
|
931
|
-
} catch {
|
|
932
|
-
}
|
|
933
|
-
}
|
|
953
|
+
const mergedIssues = mergeIssues(allIssues);
|
|
934
954
|
let aiAnalysis = null;
|
|
935
955
|
if (enableAI && aiKey) {
|
|
936
956
|
const aiSpinner = ora(`Running AI analysis (${aiProvider})...`).start();
|
|
@@ -1615,7 +1635,7 @@ ${chalk3.green("\u2705")} Changes committed`);
|
|
|
1615
1635
|
import ora3 from "ora";
|
|
1616
1636
|
import chalk4 from "chalk";
|
|
1617
1637
|
import { table } from "table";
|
|
1618
|
-
import { RankTracker, TIER_LIMITS as
|
|
1638
|
+
import { RankTracker, TIER_LIMITS as TIER_LIMITS2 } from "@rankcli/agent-runtime";
|
|
1619
1639
|
async function getProjectForUrl(url) {
|
|
1620
1640
|
const supabase = await getAuthenticatedClient();
|
|
1621
1641
|
if (!supabase) return null;
|
|
@@ -1645,7 +1665,7 @@ async function getProjectForUrl(url) {
|
|
|
1645
1665
|
};
|
|
1646
1666
|
}
|
|
1647
1667
|
function getTierLimits(tier) {
|
|
1648
|
-
const limits =
|
|
1668
|
+
const limits = TIER_LIMITS2[tier] || TIER_LIMITS2.free;
|
|
1649
1669
|
return {
|
|
1650
1670
|
maxKeywords: limits.maxKeywords,
|
|
1651
1671
|
checksPerDay: limits.checksPerDay
|
|
@@ -3241,7 +3261,7 @@ async function fetchAndSaveSubscription() {
|
|
|
3241
3261
|
}
|
|
3242
3262
|
|
|
3243
3263
|
// src/version.ts
|
|
3244
|
-
var VERSION = "0.0.
|
|
3264
|
+
var VERSION = "0.0.23";
|
|
3245
3265
|
|
|
3246
3266
|
// src/index.ts
|
|
3247
3267
|
var program = new Command();
|
package/dist/wasm-engine.d.mts
CHANGED
|
@@ -1,55 +1,43 @@
|
|
|
1
|
+
import { AuditIssue } from '@rankcli/agent-runtime';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* WASM Engine Wrapper
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
* with Node.js execution
|
|
6
|
+
* Bridges the WASM binary (which contains AES-encrypted paid-tier check
|
|
7
|
+
* source) with Node.js execution:
|
|
6
8
|
*
|
|
7
|
-
* 1. WASM decrypts the JS code using
|
|
9
|
+
* 1. WASM decrypts the JS code using a fixed, build-embedded passphrase
|
|
8
10
|
* 2. We get the decrypted code from WASM
|
|
9
11
|
* 3. We execute it in a Node.js VM sandbox
|
|
10
|
-
* 4.
|
|
12
|
+
* 4. Individual premium check functions are called from that sandbox
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* This is code protection, not access control: entitlement (is this user
|
|
15
|
+
* actually paid?) is decided by the caller, from the real Supabase-backed
|
|
16
|
+
* apiKey/plan the CLI validates on login - see commands/audit.ts. This
|
|
17
|
+
* module should only ever be initialized after that check passes; there
|
|
18
|
+
* is no per-user license key, and passing an arbitrary string here does
|
|
19
|
+
* not grant anything a validated apiKey didn't already grant.
|
|
14
20
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
score: number;
|
|
21
|
+
|
|
22
|
+
type PremiumCheckFn = (html: string, url: string) => {
|
|
18
23
|
issues: AuditIssue[];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
code: string;
|
|
24
|
-
severity: 'error' | 'warning' | 'notice';
|
|
25
|
-
category: string;
|
|
26
|
-
title: string;
|
|
27
|
-
description?: string;
|
|
28
|
-
impact?: string;
|
|
29
|
-
howToFix?: string;
|
|
30
|
-
affectedUrls?: string[];
|
|
31
|
-
}
|
|
32
|
-
type Tier = 'free' | 'registered' | 'solo' | 'pro' | 'agency';
|
|
33
|
-
/**
|
|
34
|
-
* Initialize the engine with a license key
|
|
35
|
-
*/
|
|
36
|
-
declare function initEngine(licenseKey?: string): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Run an SEO audit on a URL
|
|
39
|
-
*/
|
|
40
|
-
declare function runAudit(url: string, html: string): Promise<AuditResult>;
|
|
41
|
-
/**
|
|
42
|
-
* Get the current tier
|
|
43
|
-
*/
|
|
44
|
-
declare function getTier(): Promise<Tier>;
|
|
24
|
+
data: unknown;
|
|
25
|
+
};
|
|
26
|
+
declare const PREMIUM_CHECK_NAMES: readonly ["analyzeFeaturedSnippet", "analyzeInternalLinkGraph", "analyzeEEATSignals", "analyzeContentScience", "analyzeKeywordCannibalizationSinglePage", "analyzeConversionElements", "analyzeKeywordPlacement", "analyzeTopicalClusters", "analyzePlatformPresence", "analyzeInteractiveTools", "analyzeFunnelIntent", "analyzeNavBoostSignals", "analyzeEntitySEO", "analyzeFreshnessSignals", "analyzeAIContentStructure", "analyzeRAGChunkReadiness", "analyzeCitationQuality", "analyzeAnswerConciseness", "analyzeBrandMentionOptimization", "analyzeAICitationWorthiness", "analyzeReviewEcosystem"];
|
|
27
|
+
type PremiumChecks = Record<(typeof PREMIUM_CHECK_NAMES)[number], PremiumCheckFn>;
|
|
45
28
|
/**
|
|
46
|
-
*
|
|
29
|
+
* Decrypt the embedded premium checks and prepare them for execution.
|
|
30
|
+
* Only call this once the caller has independently confirmed the user is
|
|
31
|
+
* entitled to premium checks (a validated paid apiKey) - see the module
|
|
32
|
+
* doc comment above.
|
|
47
33
|
*/
|
|
48
|
-
declare function
|
|
34
|
+
declare function initEngine(decryptPassphrase: string): Promise<void>;
|
|
49
35
|
/**
|
|
50
|
-
*
|
|
36
|
+
* Build a PremiumChecks object (matching agent-runtime's
|
|
37
|
+
* premium-checks.types.ts) whose methods each call into the decrypted
|
|
38
|
+
* VM sandbox. Pass the result as runFullAudit({ premiumChecks }).
|
|
51
39
|
*/
|
|
52
|
-
declare function
|
|
40
|
+
declare function getPremiumChecks(): Promise<PremiumChecks>;
|
|
53
41
|
/**
|
|
54
42
|
* Get engine version info
|
|
55
43
|
*/
|
|
@@ -59,4 +47,4 @@ declare function getVersion(): Promise<string>;
|
|
|
59
47
|
*/
|
|
60
48
|
declare function isReady(): Promise<boolean>;
|
|
61
49
|
|
|
62
|
-
export { type
|
|
50
|
+
export { type PremiumCheckFn, type PremiumChecks, getPremiumChecks, getVersion, initEngine, isReady };
|
package/dist/wasm-engine.d.ts
CHANGED
|
@@ -1,55 +1,43 @@
|
|
|
1
|
+
import { AuditIssue } from '@rankcli/agent-runtime';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* WASM Engine Wrapper
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
* with Node.js execution
|
|
6
|
+
* Bridges the WASM binary (which contains AES-encrypted paid-tier check
|
|
7
|
+
* source) with Node.js execution:
|
|
6
8
|
*
|
|
7
|
-
* 1. WASM decrypts the JS code using
|
|
9
|
+
* 1. WASM decrypts the JS code using a fixed, build-embedded passphrase
|
|
8
10
|
* 2. We get the decrypted code from WASM
|
|
9
11
|
* 3. We execute it in a Node.js VM sandbox
|
|
10
|
-
* 4.
|
|
12
|
+
* 4. Individual premium check functions are called from that sandbox
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* This is code protection, not access control: entitlement (is this user
|
|
15
|
+
* actually paid?) is decided by the caller, from the real Supabase-backed
|
|
16
|
+
* apiKey/plan the CLI validates on login - see commands/audit.ts. This
|
|
17
|
+
* module should only ever be initialized after that check passes; there
|
|
18
|
+
* is no per-user license key, and passing an arbitrary string here does
|
|
19
|
+
* not grant anything a validated apiKey didn't already grant.
|
|
14
20
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
score: number;
|
|
21
|
+
|
|
22
|
+
type PremiumCheckFn = (html: string, url: string) => {
|
|
18
23
|
issues: AuditIssue[];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
code: string;
|
|
24
|
-
severity: 'error' | 'warning' | 'notice';
|
|
25
|
-
category: string;
|
|
26
|
-
title: string;
|
|
27
|
-
description?: string;
|
|
28
|
-
impact?: string;
|
|
29
|
-
howToFix?: string;
|
|
30
|
-
affectedUrls?: string[];
|
|
31
|
-
}
|
|
32
|
-
type Tier = 'free' | 'registered' | 'solo' | 'pro' | 'agency';
|
|
33
|
-
/**
|
|
34
|
-
* Initialize the engine with a license key
|
|
35
|
-
*/
|
|
36
|
-
declare function initEngine(licenseKey?: string): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Run an SEO audit on a URL
|
|
39
|
-
*/
|
|
40
|
-
declare function runAudit(url: string, html: string): Promise<AuditResult>;
|
|
41
|
-
/**
|
|
42
|
-
* Get the current tier
|
|
43
|
-
*/
|
|
44
|
-
declare function getTier(): Promise<Tier>;
|
|
24
|
+
data: unknown;
|
|
25
|
+
};
|
|
26
|
+
declare const PREMIUM_CHECK_NAMES: readonly ["analyzeFeaturedSnippet", "analyzeInternalLinkGraph", "analyzeEEATSignals", "analyzeContentScience", "analyzeKeywordCannibalizationSinglePage", "analyzeConversionElements", "analyzeKeywordPlacement", "analyzeTopicalClusters", "analyzePlatformPresence", "analyzeInteractiveTools", "analyzeFunnelIntent", "analyzeNavBoostSignals", "analyzeEntitySEO", "analyzeFreshnessSignals", "analyzeAIContentStructure", "analyzeRAGChunkReadiness", "analyzeCitationQuality", "analyzeAnswerConciseness", "analyzeBrandMentionOptimization", "analyzeAICitationWorthiness", "analyzeReviewEcosystem"];
|
|
27
|
+
type PremiumChecks = Record<(typeof PREMIUM_CHECK_NAMES)[number], PremiumCheckFn>;
|
|
45
28
|
/**
|
|
46
|
-
*
|
|
29
|
+
* Decrypt the embedded premium checks and prepare them for execution.
|
|
30
|
+
* Only call this once the caller has independently confirmed the user is
|
|
31
|
+
* entitled to premium checks (a validated paid apiKey) - see the module
|
|
32
|
+
* doc comment above.
|
|
47
33
|
*/
|
|
48
|
-
declare function
|
|
34
|
+
declare function initEngine(decryptPassphrase: string): Promise<void>;
|
|
49
35
|
/**
|
|
50
|
-
*
|
|
36
|
+
* Build a PremiumChecks object (matching agent-runtime's
|
|
37
|
+
* premium-checks.types.ts) whose methods each call into the decrypted
|
|
38
|
+
* VM sandbox. Pass the result as runFullAudit({ premiumChecks }).
|
|
51
39
|
*/
|
|
52
|
-
declare function
|
|
40
|
+
declare function getPremiumChecks(): Promise<PremiumChecks>;
|
|
53
41
|
/**
|
|
54
42
|
* Get engine version info
|
|
55
43
|
*/
|
|
@@ -59,4 +47,4 @@ declare function getVersion(): Promise<string>;
|
|
|
59
47
|
*/
|
|
60
48
|
declare function isReady(): Promise<boolean>;
|
|
61
49
|
|
|
62
|
-
export { type
|
|
50
|
+
export { type PremiumCheckFn, type PremiumChecks, getPremiumChecks, getVersion, initEngine, isReady };
|
package/dist/wasm-engine.js
CHANGED
|
@@ -30,22 +30,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/wasm-engine.ts
|
|
31
31
|
var wasm_engine_exports = {};
|
|
32
32
|
__export(wasm_engine_exports, {
|
|
33
|
-
|
|
34
|
-
getTier: () => getTier,
|
|
33
|
+
getPremiumChecks: () => getPremiumChecks,
|
|
35
34
|
getVersion: () => getVersion,
|
|
36
35
|
initEngine: () => initEngine,
|
|
37
|
-
isReady: () => isReady
|
|
38
|
-
runAudit: () => runAudit,
|
|
39
|
-
validateLicense: () => validateLicense
|
|
36
|
+
isReady: () => isReady
|
|
40
37
|
});
|
|
41
38
|
module.exports = __toCommonJS(wasm_engine_exports);
|
|
42
39
|
var vm = __toESM(require("vm"));
|
|
43
40
|
var path = __toESM(require("path"));
|
|
44
41
|
var fs = __toESM(require("fs"));
|
|
42
|
+
var os = __toESM(require("os"));
|
|
43
|
+
var PREMIUM_CHECK_NAMES = [
|
|
44
|
+
"analyzeFeaturedSnippet",
|
|
45
|
+
"analyzeInternalLinkGraph",
|
|
46
|
+
"analyzeEEATSignals",
|
|
47
|
+
"analyzeContentScience",
|
|
48
|
+
"analyzeKeywordCannibalizationSinglePage",
|
|
49
|
+
"analyzeConversionElements",
|
|
50
|
+
"analyzeKeywordPlacement",
|
|
51
|
+
"analyzeTopicalClusters",
|
|
52
|
+
"analyzePlatformPresence",
|
|
53
|
+
"analyzeInteractiveTools",
|
|
54
|
+
"analyzeFunnelIntent",
|
|
55
|
+
"analyzeNavBoostSignals",
|
|
56
|
+
"analyzeEntitySEO",
|
|
57
|
+
"analyzeFreshnessSignals",
|
|
58
|
+
"analyzeAIContentStructure",
|
|
59
|
+
"analyzeRAGChunkReadiness",
|
|
60
|
+
"analyzeCitationQuality",
|
|
61
|
+
"analyzeAnswerConciseness",
|
|
62
|
+
"analyzeBrandMentionOptimization",
|
|
63
|
+
"analyzeAICitationWorthiness",
|
|
64
|
+
"analyzeReviewEcosystem"
|
|
65
|
+
];
|
|
45
66
|
var wasmModule = null;
|
|
46
67
|
var auditContext = null;
|
|
47
68
|
async function loadWasm() {
|
|
48
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
|
+
}
|
|
49
77
|
const possiblePaths = [
|
|
50
78
|
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
51
79
|
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
@@ -62,16 +90,35 @@ async function loadWasm() {
|
|
|
62
90
|
Searched: ${possiblePaths.join(", ")}`
|
|
63
91
|
);
|
|
64
92
|
}
|
|
65
|
-
|
|
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
|
+
}
|
|
113
|
+
async function initEngine(decryptPassphrase) {
|
|
66
114
|
const wasm = await loadWasm();
|
|
67
|
-
const key = licenseKey || process.env.RANKCLI_LICENSE_KEY || "";
|
|
68
115
|
try {
|
|
69
|
-
wasm.init_engine(
|
|
116
|
+
wasm.init_engine(decryptPassphrase);
|
|
70
117
|
} catch (error) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
throw
|
|
118
|
+
throw new Error(`Failed to decrypt premium checks: ${error}`);
|
|
119
|
+
}
|
|
120
|
+
if (!wasm.is_initialized()) {
|
|
121
|
+
throw new Error("Failed to decrypt premium checks: engine did not initialize");
|
|
75
122
|
}
|
|
76
123
|
const jsCode = wasm.get_decrypted_code();
|
|
77
124
|
createExecutionContext(jsCode);
|
|
@@ -126,59 +173,31 @@ function createExecutionContext(jsCode) {
|
|
|
126
173
|
throw new Error(`Failed to initialize audit checks: ${error}`);
|
|
127
174
|
}
|
|
128
175
|
}
|
|
129
|
-
|
|
176
|
+
function callSandboxCheck(fnName, html, url) {
|
|
177
|
+
if (!auditContext) {
|
|
178
|
+
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
179
|
+
}
|
|
180
|
+
const script = new vm.Script(`
|
|
181
|
+
(function() {
|
|
182
|
+
if (typeof RankCLI.${fnName} !== 'function') {
|
|
183
|
+
throw new Error('RankCLI.${fnName} not found in decrypted code');
|
|
184
|
+
}
|
|
185
|
+
return JSON.stringify(RankCLI.${fnName}(${JSON.stringify(html)}, ${JSON.stringify(url)}));
|
|
186
|
+
})()
|
|
187
|
+
`);
|
|
188
|
+
const resultJson = script.runInContext(auditContext, { timeout: 1e4 });
|
|
189
|
+
return JSON.parse(resultJson);
|
|
190
|
+
}
|
|
191
|
+
async function getPremiumChecks() {
|
|
130
192
|
const wasm = await loadWasm();
|
|
131
193
|
if (!wasm.is_initialized()) {
|
|
132
194
|
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
133
195
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const checksLimit = wasm.get_checks_limit();
|
|
138
|
-
const tier = wasm.get_tier();
|
|
139
|
-
try {
|
|
140
|
-
const script = new vm.Script(`
|
|
141
|
-
(function() {
|
|
142
|
-
const url = ${JSON.stringify(url)};
|
|
143
|
-
const html = ${JSON.stringify(html)};
|
|
144
|
-
const checksLimit = ${checksLimit};
|
|
145
|
-
|
|
146
|
-
if (typeof RankCLI.runAudit === 'function') {
|
|
147
|
-
return JSON.stringify(RankCLI.runAudit(url, html, checksLimit));
|
|
148
|
-
} else if (typeof RankCLI.runFullAudit === 'function') {
|
|
149
|
-
// Synchronous wrapper for async function
|
|
150
|
-
return JSON.stringify({
|
|
151
|
-
success: true,
|
|
152
|
-
score: 85,
|
|
153
|
-
issues: [],
|
|
154
|
-
checksRun: checksLimit,
|
|
155
|
-
tier: "${tier}"
|
|
156
|
-
});
|
|
157
|
-
} else {
|
|
158
|
-
throw new Error('RankCLI.runAudit not found in decrypted code');
|
|
159
|
-
}
|
|
160
|
-
})()
|
|
161
|
-
`);
|
|
162
|
-
const resultJson = script.runInContext(auditContext, {
|
|
163
|
-
timeout: 3e4
|
|
164
|
-
// 30 second timeout for audit
|
|
165
|
-
});
|
|
166
|
-
return JSON.parse(resultJson);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
throw new Error(`Audit execution failed: ${error}`);
|
|
196
|
+
const checks = {};
|
|
197
|
+
for (const name of PREMIUM_CHECK_NAMES) {
|
|
198
|
+
checks[name] = (html, url) => callSandboxCheck(name, html, url);
|
|
169
199
|
}
|
|
170
|
-
|
|
171
|
-
async function getTier() {
|
|
172
|
-
const wasm = await loadWasm();
|
|
173
|
-
return wasm.get_tier();
|
|
174
|
-
}
|
|
175
|
-
async function getChecksLimit() {
|
|
176
|
-
const wasm = await loadWasm();
|
|
177
|
-
return wasm.get_checks_limit();
|
|
178
|
-
}
|
|
179
|
-
async function validateLicense(licenseKey) {
|
|
180
|
-
const wasm = await loadWasm();
|
|
181
|
-
return wasm.validate_license(licenseKey);
|
|
200
|
+
return checks;
|
|
182
201
|
}
|
|
183
202
|
async function getVersion() {
|
|
184
203
|
const wasm = await loadWasm();
|
|
@@ -194,11 +213,8 @@ async function isReady() {
|
|
|
194
213
|
}
|
|
195
214
|
// Annotate the CommonJS export names for ESM import in node:
|
|
196
215
|
0 && (module.exports = {
|
|
197
|
-
|
|
198
|
-
getTier,
|
|
216
|
+
getPremiumChecks,
|
|
199
217
|
getVersion,
|
|
200
218
|
initEngine,
|
|
201
|
-
isReady
|
|
202
|
-
runAudit,
|
|
203
|
-
validateLicense
|
|
219
|
+
isReady
|
|
204
220
|
});
|
package/dist/wasm-engine.mjs
CHANGED
|
@@ -1,167 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
import * as fs from "fs";
|
|
9
|
-
var wasmModule = null;
|
|
10
|
-
var auditContext = null;
|
|
11
|
-
async function loadWasm() {
|
|
12
|
-
if (wasmModule) return wasmModule;
|
|
13
|
-
const possiblePaths = [
|
|
14
|
-
path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
|
|
15
|
-
path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
|
|
16
|
-
path.join(__dirname, "../../native/pkg/rankcli_audit_engine.js")
|
|
17
|
-
];
|
|
18
|
-
for (const wasmPath of possiblePaths) {
|
|
19
|
-
if (fs.existsSync(wasmPath)) {
|
|
20
|
-
wasmModule = __require(wasmPath);
|
|
21
|
-
return wasmModule;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
throw new Error(
|
|
25
|
-
`WASM engine not found. Run \`npm run build:wasm\` first.
|
|
26
|
-
Searched: ${possiblePaths.join(", ")}`
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
async function initEngine(licenseKey) {
|
|
30
|
-
const wasm = await loadWasm();
|
|
31
|
-
const key = licenseKey || process.env.RANKCLI_LICENSE_KEY || "";
|
|
32
|
-
try {
|
|
33
|
-
wasm.init_engine(key);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
if (error instanceof Error && error.message.includes("Invalid license")) {
|
|
36
|
-
throw new Error("Invalid license key. Get one at https://rankcli.dev/pricing");
|
|
37
|
-
}
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
const jsCode = wasm.get_decrypted_code();
|
|
41
|
-
createExecutionContext(jsCode);
|
|
42
|
-
}
|
|
43
|
-
function createExecutionContext(jsCode) {
|
|
44
|
-
const sandbox = {
|
|
45
|
-
// Console for debugging (can be disabled in production)
|
|
46
|
-
console: {
|
|
47
|
-
log: () => {
|
|
48
|
-
},
|
|
49
|
-
warn: () => {
|
|
50
|
-
},
|
|
51
|
-
error: () => {
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
// Timers (some checks may use setTimeout)
|
|
55
|
-
setTimeout,
|
|
56
|
-
clearTimeout,
|
|
57
|
-
// JSON parsing
|
|
58
|
-
JSON,
|
|
59
|
-
// Math operations
|
|
60
|
-
Math,
|
|
61
|
-
// String/Array operations
|
|
62
|
-
String,
|
|
63
|
-
Array,
|
|
64
|
-
Object,
|
|
65
|
-
RegExp,
|
|
66
|
-
Date,
|
|
67
|
-
// URL parsing
|
|
68
|
-
URL,
|
|
69
|
-
URLSearchParams,
|
|
70
|
-
// Node.js globals needed by bundled dependencies (e.g., cheerio)
|
|
71
|
-
Buffer,
|
|
72
|
-
process: { env: {} },
|
|
73
|
-
// TextEncoder/TextDecoder for string handling
|
|
74
|
-
TextEncoder,
|
|
75
|
-
TextDecoder,
|
|
76
|
-
// The RankCLI namespace will be populated by the code
|
|
77
|
-
RankCLI: {},
|
|
78
|
-
// Global reference
|
|
79
|
-
globalThis: {}
|
|
80
|
-
};
|
|
81
|
-
sandbox.globalThis = sandbox;
|
|
82
|
-
auditContext = vm.createContext(sandbox);
|
|
83
|
-
try {
|
|
84
|
-
vm.runInContext(jsCode, auditContext, {
|
|
85
|
-
filename: "rankcli-checks.js",
|
|
86
|
-
timeout: 5e3
|
|
87
|
-
// 5 second timeout for initialization
|
|
88
|
-
});
|
|
89
|
-
} catch (error) {
|
|
90
|
-
throw new Error(`Failed to initialize audit checks: ${error}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async function runAudit(url, html) {
|
|
94
|
-
const wasm = await loadWasm();
|
|
95
|
-
if (!wasm.is_initialized()) {
|
|
96
|
-
throw new Error("Engine not initialized. Call initEngine() first.");
|
|
97
|
-
}
|
|
98
|
-
if (!auditContext) {
|
|
99
|
-
throw new Error("Execution context not ready");
|
|
100
|
-
}
|
|
101
|
-
const checksLimit = wasm.get_checks_limit();
|
|
102
|
-
const tier = wasm.get_tier();
|
|
103
|
-
try {
|
|
104
|
-
const script = new vm.Script(`
|
|
105
|
-
(function() {
|
|
106
|
-
const url = ${JSON.stringify(url)};
|
|
107
|
-
const html = ${JSON.stringify(html)};
|
|
108
|
-
const checksLimit = ${checksLimit};
|
|
109
|
-
|
|
110
|
-
if (typeof RankCLI.runAudit === 'function') {
|
|
111
|
-
return JSON.stringify(RankCLI.runAudit(url, html, checksLimit));
|
|
112
|
-
} else if (typeof RankCLI.runFullAudit === 'function') {
|
|
113
|
-
// Synchronous wrapper for async function
|
|
114
|
-
return JSON.stringify({
|
|
115
|
-
success: true,
|
|
116
|
-
score: 85,
|
|
117
|
-
issues: [],
|
|
118
|
-
checksRun: checksLimit,
|
|
119
|
-
tier: "${tier}"
|
|
120
|
-
});
|
|
121
|
-
} else {
|
|
122
|
-
throw new Error('RankCLI.runAudit not found in decrypted code');
|
|
123
|
-
}
|
|
124
|
-
})()
|
|
125
|
-
`);
|
|
126
|
-
const resultJson = script.runInContext(auditContext, {
|
|
127
|
-
timeout: 3e4
|
|
128
|
-
// 30 second timeout for audit
|
|
129
|
-
});
|
|
130
|
-
return JSON.parse(resultJson);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
throw new Error(`Audit execution failed: ${error}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async function getTier() {
|
|
136
|
-
const wasm = await loadWasm();
|
|
137
|
-
return wasm.get_tier();
|
|
138
|
-
}
|
|
139
|
-
async function getChecksLimit() {
|
|
140
|
-
const wasm = await loadWasm();
|
|
141
|
-
return wasm.get_checks_limit();
|
|
142
|
-
}
|
|
143
|
-
async function validateLicense(licenseKey) {
|
|
144
|
-
const wasm = await loadWasm();
|
|
145
|
-
return wasm.validate_license(licenseKey);
|
|
146
|
-
}
|
|
147
|
-
async function getVersion() {
|
|
148
|
-
const wasm = await loadWasm();
|
|
149
|
-
return wasm.version();
|
|
150
|
-
}
|
|
151
|
-
async function isReady() {
|
|
152
|
-
try {
|
|
153
|
-
const wasm = await loadWasm();
|
|
154
|
-
return wasm.is_initialized();
|
|
155
|
-
} catch {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
2
|
+
getPremiumChecks,
|
|
3
|
+
getVersion,
|
|
4
|
+
initEngine,
|
|
5
|
+
isReady
|
|
6
|
+
} from "./chunk-NF755NKY.mjs";
|
|
159
7
|
export {
|
|
160
|
-
|
|
161
|
-
getTier,
|
|
8
|
+
getPremiumChecks,
|
|
162
9
|
getVersion,
|
|
163
10
|
initEngine,
|
|
164
|
-
isReady
|
|
165
|
-
runAudit,
|
|
166
|
-
validateLicense
|
|
11
|
+
isReady
|
|
167
12
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "RankCLI - Ship code, get ranked. SEO meets CI/CD.",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"build:wasm:dev": "cd native && wasm-pack build --target nodejs --dev && mkdir -p ../wasm && cp -r pkg/* ../wasm/",
|
|
37
37
|
"prebuild:wasm": "mkdir -p wasm",
|
|
38
38
|
"inject-config": "node scripts/inject-config.js",
|
|
39
|
+
"build:sea": "bash scripts/build-sea.sh",
|
|
39
40
|
"prepublishOnly": "npm run build && npm run inject-config",
|
|
40
41
|
"start": "node dist/index.js",
|
|
41
42
|
"lint": "eslint src/",
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"update-version": "node scripts/update-version.js"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@rankcli/agent-runtime": "^0.0.
|
|
48
|
+
"@rankcli/agent-runtime": "^0.0.17",
|
|
48
49
|
"@supabase/supabase-js": "^2.45.0",
|
|
49
50
|
"chalk": "^5.3.0",
|
|
50
51
|
"commander": "^12.0.0",
|
|
@@ -59,6 +60,8 @@
|
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@types/inquirer": "^9.0.0",
|
|
61
62
|
"@types/node": "^20.0.0",
|
|
63
|
+
"esbuild": "^0.28.2",
|
|
64
|
+
"postject": "^1.0.0-alpha.6",
|
|
62
65
|
"tsup": "^8.0.0",
|
|
63
66
|
"tsx": "^4.0.0",
|
|
64
67
|
"typescript": "^5.3.0",
|
package/dist/chunk-Y6FXYEAI.mjs
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
__require
|
|
10
|
-
};
|