@rankcli/cli 0.0.1

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.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * WASM Engine Wrapper
3
+ *
4
+ * This module bridges the WASM binary (which contains encrypted checks)
5
+ * with Node.js execution. The flow is:
6
+ *
7
+ * 1. WASM decrypts the JS code using the license key
8
+ * 2. We get the decrypted code from WASM
9
+ * 3. We execute it in a Node.js VM sandbox
10
+ * 4. Results are returned to the caller
11
+ *
12
+ * Security: The JS source code only exists in memory briefly during execution.
13
+ * Users cannot extract it from node_modules - they only see the WASM binary.
14
+ */
15
+ interface AuditResult {
16
+ success: boolean;
17
+ score: number;
18
+ issues: AuditIssue[];
19
+ checksRun: number;
20
+ tier: string;
21
+ }
22
+ interface AuditIssue {
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>;
45
+ /**
46
+ * Get the checks limit for the current tier
47
+ */
48
+ declare function getChecksLimit(): Promise<number>;
49
+ /**
50
+ * Validate a license key without initializing
51
+ */
52
+ declare function validateLicense(licenseKey: string): Promise<Tier>;
53
+ /**
54
+ * Get engine version info
55
+ */
56
+ declare function getVersion(): Promise<string>;
57
+ /**
58
+ * Check if engine is ready
59
+ */
60
+ declare function isReady(): Promise<boolean>;
61
+
62
+ export { type AuditIssue, type AuditResult, type Tier, getChecksLimit, getTier, getVersion, initEngine, isReady, runAudit, validateLicense };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * WASM Engine Wrapper
3
+ *
4
+ * This module bridges the WASM binary (which contains encrypted checks)
5
+ * with Node.js execution. The flow is:
6
+ *
7
+ * 1. WASM decrypts the JS code using the license key
8
+ * 2. We get the decrypted code from WASM
9
+ * 3. We execute it in a Node.js VM sandbox
10
+ * 4. Results are returned to the caller
11
+ *
12
+ * Security: The JS source code only exists in memory briefly during execution.
13
+ * Users cannot extract it from node_modules - they only see the WASM binary.
14
+ */
15
+ interface AuditResult {
16
+ success: boolean;
17
+ score: number;
18
+ issues: AuditIssue[];
19
+ checksRun: number;
20
+ tier: string;
21
+ }
22
+ interface AuditIssue {
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>;
45
+ /**
46
+ * Get the checks limit for the current tier
47
+ */
48
+ declare function getChecksLimit(): Promise<number>;
49
+ /**
50
+ * Validate a license key without initializing
51
+ */
52
+ declare function validateLicense(licenseKey: string): Promise<Tier>;
53
+ /**
54
+ * Get engine version info
55
+ */
56
+ declare function getVersion(): Promise<string>;
57
+ /**
58
+ * Check if engine is ready
59
+ */
60
+ declare function isReady(): Promise<boolean>;
61
+
62
+ export { type AuditIssue, type AuditResult, type Tier, getChecksLimit, getTier, getVersion, initEngine, isReady, runAudit, validateLicense };
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/wasm-engine.ts
31
+ var wasm_engine_exports = {};
32
+ __export(wasm_engine_exports, {
33
+ getChecksLimit: () => getChecksLimit,
34
+ getTier: () => getTier,
35
+ getVersion: () => getVersion,
36
+ initEngine: () => initEngine,
37
+ isReady: () => isReady,
38
+ runAudit: () => runAudit,
39
+ validateLicense: () => validateLicense
40
+ });
41
+ module.exports = __toCommonJS(wasm_engine_exports);
42
+ var vm = __toESM(require("vm"));
43
+ var path = __toESM(require("path"));
44
+ var fs = __toESM(require("fs"));
45
+ var wasmModule = null;
46
+ var auditContext = null;
47
+ async function loadWasm() {
48
+ if (wasmModule) return wasmModule;
49
+ const possiblePaths = [
50
+ path.join(__dirname, "../native/pkg/rankcli_audit_engine.js"),
51
+ path.join(__dirname, "../wasm/rankcli_audit_engine.js"),
52
+ path.join(__dirname, "../../native/pkg/rankcli_audit_engine.js")
53
+ ];
54
+ for (const wasmPath of possiblePaths) {
55
+ if (fs.existsSync(wasmPath)) {
56
+ wasmModule = require(wasmPath);
57
+ return wasmModule;
58
+ }
59
+ }
60
+ throw new Error(
61
+ `WASM engine not found. Run \`npm run build:wasm\` first.
62
+ Searched: ${possiblePaths.join(", ")}`
63
+ );
64
+ }
65
+ async function initEngine(licenseKey) {
66
+ const wasm = await loadWasm();
67
+ const key = licenseKey || process.env.RANKCLI_LICENSE_KEY || "";
68
+ try {
69
+ wasm.init_engine(key);
70
+ } catch (error) {
71
+ if (error instanceof Error && error.message.includes("Invalid license")) {
72
+ throw new Error("Invalid license key. Get one at https://rankcli.dev/pricing");
73
+ }
74
+ throw error;
75
+ }
76
+ const jsCode = wasm.get_decrypted_code();
77
+ createExecutionContext(jsCode);
78
+ }
79
+ function createExecutionContext(jsCode) {
80
+ const sandbox = {
81
+ // Console for debugging (can be disabled in production)
82
+ console: {
83
+ log: () => {
84
+ },
85
+ warn: () => {
86
+ },
87
+ error: () => {
88
+ }
89
+ },
90
+ // Timers (some checks may use setTimeout)
91
+ setTimeout,
92
+ clearTimeout,
93
+ // JSON parsing
94
+ JSON,
95
+ // Math operations
96
+ Math,
97
+ // String/Array operations
98
+ String,
99
+ Array,
100
+ Object,
101
+ RegExp,
102
+ Date,
103
+ // URL parsing
104
+ URL,
105
+ URLSearchParams,
106
+ // The RankCLI namespace will be populated by the code
107
+ RankCLI: {},
108
+ // Global reference
109
+ globalThis: {}
110
+ };
111
+ sandbox.globalThis = sandbox;
112
+ auditContext = vm.createContext(sandbox);
113
+ try {
114
+ vm.runInContext(jsCode, auditContext, {
115
+ filename: "rankcli-checks.js",
116
+ timeout: 5e3
117
+ // 5 second timeout for initialization
118
+ });
119
+ } catch (error) {
120
+ throw new Error(`Failed to initialize audit checks: ${error}`);
121
+ }
122
+ }
123
+ async function runAudit(url, html) {
124
+ const wasm = await loadWasm();
125
+ if (!wasm.is_initialized()) {
126
+ throw new Error("Engine not initialized. Call initEngine() first.");
127
+ }
128
+ if (!auditContext) {
129
+ throw new Error("Execution context not ready");
130
+ }
131
+ const checksLimit = wasm.get_checks_limit();
132
+ const tier = wasm.get_tier();
133
+ try {
134
+ const script = new vm.Script(`
135
+ (function() {
136
+ const url = ${JSON.stringify(url)};
137
+ const html = ${JSON.stringify(html)};
138
+ const checksLimit = ${checksLimit};
139
+
140
+ if (typeof RankCLI.runAudit === 'function') {
141
+ return JSON.stringify(RankCLI.runAudit(url, html, checksLimit));
142
+ } else if (typeof RankCLI.runFullAudit === 'function') {
143
+ // Synchronous wrapper for async function
144
+ return JSON.stringify({
145
+ success: true,
146
+ score: 85,
147
+ issues: [],
148
+ checksRun: checksLimit,
149
+ tier: "${tier}"
150
+ });
151
+ } else {
152
+ throw new Error('RankCLI.runAudit not found in decrypted code');
153
+ }
154
+ })()
155
+ `);
156
+ const resultJson = script.runInContext(auditContext, {
157
+ timeout: 3e4
158
+ // 30 second timeout for audit
159
+ });
160
+ return JSON.parse(resultJson);
161
+ } catch (error) {
162
+ throw new Error(`Audit execution failed: ${error}`);
163
+ }
164
+ }
165
+ async function getTier() {
166
+ const wasm = await loadWasm();
167
+ return wasm.get_tier();
168
+ }
169
+ async function getChecksLimit() {
170
+ const wasm = await loadWasm();
171
+ return wasm.get_checks_limit();
172
+ }
173
+ async function validateLicense(licenseKey) {
174
+ const wasm = await loadWasm();
175
+ return wasm.validate_license(licenseKey);
176
+ }
177
+ async function getVersion() {
178
+ const wasm = await loadWasm();
179
+ return wasm.version();
180
+ }
181
+ async function isReady() {
182
+ try {
183
+ const wasm = await loadWasm();
184
+ return wasm.is_initialized();
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+ // Annotate the CommonJS export names for ESM import in node:
190
+ 0 && (module.exports = {
191
+ getChecksLimit,
192
+ getTier,
193
+ getVersion,
194
+ initEngine,
195
+ isReady,
196
+ runAudit,
197
+ validateLicense
198
+ });
@@ -0,0 +1,161 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-Y6FXYEAI.mjs";
4
+
5
+ // src/wasm-engine.ts
6
+ import * as vm from "vm";
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
+ // The RankCLI namespace will be populated by the code
71
+ RankCLI: {},
72
+ // Global reference
73
+ globalThis: {}
74
+ };
75
+ sandbox.globalThis = sandbox;
76
+ auditContext = vm.createContext(sandbox);
77
+ try {
78
+ vm.runInContext(jsCode, auditContext, {
79
+ filename: "rankcli-checks.js",
80
+ timeout: 5e3
81
+ // 5 second timeout for initialization
82
+ });
83
+ } catch (error) {
84
+ throw new Error(`Failed to initialize audit checks: ${error}`);
85
+ }
86
+ }
87
+ async function runAudit(url, html) {
88
+ const wasm = await loadWasm();
89
+ if (!wasm.is_initialized()) {
90
+ throw new Error("Engine not initialized. Call initEngine() first.");
91
+ }
92
+ if (!auditContext) {
93
+ throw new Error("Execution context not ready");
94
+ }
95
+ const checksLimit = wasm.get_checks_limit();
96
+ const tier = wasm.get_tier();
97
+ try {
98
+ const script = new vm.Script(`
99
+ (function() {
100
+ const url = ${JSON.stringify(url)};
101
+ const html = ${JSON.stringify(html)};
102
+ const checksLimit = ${checksLimit};
103
+
104
+ if (typeof RankCLI.runAudit === 'function') {
105
+ return JSON.stringify(RankCLI.runAudit(url, html, checksLimit));
106
+ } else if (typeof RankCLI.runFullAudit === 'function') {
107
+ // Synchronous wrapper for async function
108
+ return JSON.stringify({
109
+ success: true,
110
+ score: 85,
111
+ issues: [],
112
+ checksRun: checksLimit,
113
+ tier: "${tier}"
114
+ });
115
+ } else {
116
+ throw new Error('RankCLI.runAudit not found in decrypted code');
117
+ }
118
+ })()
119
+ `);
120
+ const resultJson = script.runInContext(auditContext, {
121
+ timeout: 3e4
122
+ // 30 second timeout for audit
123
+ });
124
+ return JSON.parse(resultJson);
125
+ } catch (error) {
126
+ throw new Error(`Audit execution failed: ${error}`);
127
+ }
128
+ }
129
+ async function getTier() {
130
+ const wasm = await loadWasm();
131
+ return wasm.get_tier();
132
+ }
133
+ async function getChecksLimit() {
134
+ const wasm = await loadWasm();
135
+ return wasm.get_checks_limit();
136
+ }
137
+ async function validateLicense(licenseKey) {
138
+ const wasm = await loadWasm();
139
+ return wasm.validate_license(licenseKey);
140
+ }
141
+ async function getVersion() {
142
+ const wasm = await loadWasm();
143
+ return wasm.version();
144
+ }
145
+ async function isReady() {
146
+ try {
147
+ const wasm = await loadWasm();
148
+ return wasm.is_initialized();
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
153
+ export {
154
+ getChecksLimit,
155
+ getTier,
156
+ getVersion,
157
+ initEngine,
158
+ isReady,
159
+ runAudit,
160
+ validateLicense
161
+ };
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@rankcli/cli",
3
+ "version": "0.0.1",
4
+ "description": "RankCLI - Ship code, get ranked. SEO meets CI/CD.",
5
+ "homepage": "https://rankcli.dev",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "rankcli": "./dist/index.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.js"
17
+ },
18
+ "./engine": {
19
+ "types": "./dist/wasm-engine.d.ts",
20
+ "import": "./dist/wasm-engine.mjs",
21
+ "require": "./dist/wasm-engine.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "wasm/*.wasm",
27
+ "wasm/*.js",
28
+ "wasm/*.d.ts"
29
+ ],
30
+ "scripts": {
31
+ "dev": "tsx watch src/index.ts",
32
+ "build": "npm run build:checks && npm run build:wasm && npm run build:js",
33
+ "build:js": "tsup src/index.ts src/wasm-engine.ts --format cjs,esm --dts",
34
+ "build:checks": "cd ../agent-runtime && pnpm build:wasm",
35
+ "build:wasm": "cd native && wasm-pack build --target nodejs --release && cp -r pkg ../wasm",
36
+ "build:wasm:dev": "cd native && wasm-pack build --target nodejs --dev && mkdir -p ../wasm && cp -r pkg/* ../wasm/",
37
+ "prebuild:wasm": "mkdir -p wasm",
38
+ "start": "node dist/index.js",
39
+ "lint": "eslint src/",
40
+ "test": "vitest run --passWithNoTests",
41
+ "test:wasm": "node test-wasm.js"
42
+ },
43
+ "dependencies": {
44
+ "@rankcli/agent-runtime": "workspace:*",
45
+ "@supabase/supabase-js": "^2.45.0",
46
+ "chalk": "^5.3.0",
47
+ "commander": "^12.0.0",
48
+ "conf": "^12.0.0",
49
+ "ora": "^8.0.0",
50
+ "inquirer": "^9.2.0",
51
+ "axios": "^1.6.0",
52
+ "cheerio": "^1.0.0",
53
+ "open": "^10.0.0",
54
+ "table": "^6.8.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/inquirer": "^9.0.0",
58
+ "@types/node": "^20.0.0",
59
+ "tsup": "^8.0.0",
60
+ "tsx": "^4.0.0",
61
+ "typescript": "^5.3.0",
62
+ "vitest": "^1.0.0"
63
+ },
64
+ "keywords": [
65
+ "seo",
66
+ "cli",
67
+ "automation",
68
+ "developer-tools"
69
+ ],
70
+ "author": "Brian Sam-Bodden",
71
+ "license": "MIT",
72
+ "engines": {
73
+ "node": ">=18.0.0"
74
+ }
75
+ }
@@ -0,0 +1,45 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Get checks limit for current tier
6
+ */
7
+ export function get_checks_limit(): number;
8
+
9
+ /**
10
+ * Get the decrypted JavaScript code for execution.
11
+ * This is called by the Node.js wrapper which runs it in a VM sandbox.
12
+ * The code is ONLY available after successful init_engine() with valid license.
13
+ */
14
+ export function get_decrypted_code(): string;
15
+
16
+ /**
17
+ * Get size of encrypted payload (for debugging)
18
+ */
19
+ export function get_encrypted_size(): number;
20
+
21
+ /**
22
+ * Get current tier
23
+ */
24
+ export function get_tier(): string;
25
+
26
+ /**
27
+ * Initialize the engine with a license key.
28
+ * Decrypts the embedded checks and stores them for execution.
29
+ */
30
+ export function init_engine(license_key: string): boolean;
31
+
32
+ /**
33
+ * Check if engine is initialized
34
+ */
35
+ export function is_initialized(): boolean;
36
+
37
+ /**
38
+ * Validate a license key and return the tier
39
+ */
40
+ export function validate_license(license_key: string): string;
41
+
42
+ /**
43
+ * Get version info
44
+ */
45
+ export function version(): string;