seo-intel 1.1.3 → 1.1.5

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.
@@ -1,27 +1,15 @@
1
1
  #!/bin/bash
2
- cd "$(dirname "$0")" || cd ~
3
- clear
2
+ cd "$(dirname "$0")"
4
3
  echo ""
5
- echo " 🦀 SEO Intel — Setup Wizard"
6
- echo " Opening in your browser..."
4
+ echo " SEO Intel — Setup Wizard"
5
+ echo " Opening setup in your browser..."
7
6
  echo ""
8
-
9
- # Start server in background if not already running
10
- if ! curl -s http://localhost:3000/ > /dev/null 2>&1; then
11
- npx seo-intel serve &
12
- SERVER_PID=$!
13
- sleep 2
14
- fi
15
-
16
- # Open setup wizard in browser
17
- open "http://localhost:3000/setup" 2>/dev/null || xdg-open "http://localhost:3000/setup" 2>/dev/null
18
-
19
- echo " Setup wizard is open at http://localhost:3000/setup"
20
- echo " Keep this window open while using the wizard."
21
- echo ""
22
- read -n 1 -s -r -p " Press any key to stop the server and exit..."
23
-
24
- # Clean up
25
- if [ -n "$SERVER_PID" ]; then
26
- kill $SERVER_PID 2>/dev/null
27
- fi
7
+ node cli.js serve &
8
+ SERVER_PID=$!
9
+ # Wait for server to be ready
10
+ for i in {1..10}; do
11
+ sleep 1
12
+ if curl -s http://localhost:3000/ > /dev/null 2>&1; then break; fi
13
+ done
14
+ open "http://localhost:3000/setup"
15
+ wait $SERVER_PID
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seo-intel",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Local Ahrefs-style SEO competitor intelligence. Crawl → SQLite → cloud analysis.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -31,7 +31,15 @@
31
31
  "scheduler.js",
32
32
  "seo-audit.js",
33
33
  "lib/",
34
- "setup/",
34
+ "setup/checks.js",
35
+ "setup/config-builder.js",
36
+ "setup/engine.js",
37
+ "setup/installers.js",
38
+ "setup/models.js",
39
+ "setup/openclaw-bridge.js",
40
+ "setup/validator.js",
41
+ "setup/web-routes.js",
42
+ "setup/wizard.html",
35
43
  "config/setup-wizard.js",
36
44
  "config/example.json",
37
45
  "crawler/",
@@ -2188,6 +2188,53 @@ function buildHtmlTemplate(data, opts = {}) {
2188
2188
  if (scopeIdx > -1 && parts[scopeIdx + 1]) extra.scope = parts[scopeIdx + 1];
2189
2189
  runCommand(cmd, proj, extra);
2190
2190
  });
2191
+
2192
+ // Autorun: if URL has ?autorun=setup-classic, fire seo-intel setup --classic via SSE
2193
+ (function() {
2194
+ const urlParams = new URLSearchParams(window.location.search);
2195
+ if (urlParams.get('autorun') === 'setup-classic') {
2196
+ // Remove the param so it doesn't re-trigger on refresh
2197
+ window.history.replaceState({}, '', window.location.pathname);
2198
+ // Wait a tick for the panel to be ready, then stream the command
2199
+ setTimeout(function() {
2200
+ if (!isServed) {
2201
+ appendLine('Not connected to server. Cannot run setup --classic automatically.', 'error');
2202
+ return;
2203
+ }
2204
+ running = true;
2205
+ status.textContent = 'running...';
2206
+ status.style.color = 'var(--color-warning)';
2207
+ appendLine('$ seo-intel setup --classic', 'cmd');
2208
+ const params = new URLSearchParams({ command: 'setup', classic: 'true' });
2209
+ eventSource = new EventSource('/api/terminal?' + params.toString());
2210
+ eventSource.onmessage = function(e) {
2211
+ try {
2212
+ const msg = JSON.parse(e.data);
2213
+ if (msg.type === 'stdout') appendLine(msg.data, 'stdout');
2214
+ else if (msg.type === 'stderr') appendLine(msg.data, 'stderr');
2215
+ else if (msg.type === 'error') { appendLine('Error: ' + msg.data, 'error'); }
2216
+ else if (msg.type === 'exit') {
2217
+ const code = msg.data?.code ?? msg.data;
2218
+ appendLine(code === 0 ? 'Done.' : 'Exited with code ' + code, code === 0 ? 'exit-ok' : 'exit-err');
2219
+ running = false;
2220
+ status.textContent = code === 0 ? 'done' : 'failed';
2221
+ status.style.color = code === 0 ? 'var(--color-success)' : 'var(--color-danger)';
2222
+ eventSource.close();
2223
+ eventSource = null;
2224
+ }
2225
+ } catch (_) {}
2226
+ };
2227
+ eventSource.onerror = function() {
2228
+ if (running) { appendLine('Connection lost.', 'error'); }
2229
+ running = false;
2230
+ status.textContent = 'disconnected';
2231
+ status.style.color = 'var(--color-danger)';
2232
+ eventSource?.close();
2233
+ eventSource = null;
2234
+ };
2235
+ }, 300);
2236
+ }
2237
+ })();
2191
2238
  })();
2192
2239
  </script>
2193
2240
 
@@ -3499,7 +3546,6 @@ function buildHtmlTemplate(data, opts = {}) {
3499
3546
  };
3500
3547
 
3501
3548
  window.stopJob = function() {
3502
- if (!confirm('Stop the running job?')) return;
3503
3549
  fetch('/api/stop', { method: 'POST' })
3504
3550
  .then(function(r) { return r.json(); })
3505
3551
  .then(function(data) {
@@ -3582,6 +3628,11 @@ function buildHtmlTemplate(data, opts = {}) {
3582
3628
  dot.className = 'es-dot';
3583
3629
  label.style.color = 'var(--color-success)';
3584
3630
  label.textContent = 'Completed (' + (data.extracted || 0) + ' extracted' + (data.failed ? ', ' + data.failed + ' failed' : '') + ')';
3631
+ } else if (data.status === 'stopped') {
3632
+ panel.classList.remove('is-running', 'is-crashed');
3633
+ dot.className = 'es-dot';
3634
+ label.style.color = 'var(--accent-gold)';
3635
+ label.textContent = 'Stopped' + (data.extracted ? ' (' + data.extracted + ' extracted)' : '');
3585
3636
  } else if (data.status === 'crashed') {
3586
3637
  panel.classList.remove('is-running');
3587
3638
  panel.classList.add('is-crashed');
@@ -4691,7 +4742,6 @@ function buildMultiHtmlTemplate(allProjectData) {
4691
4742
  };
4692
4743
 
4693
4744
  window.stopJob = function() {
4694
- if (!confirm('Stop the running job?')) return;
4695
4745
  fetch('/api/stop', { method: 'POST' })
4696
4746
  .then(function(r) { return r.json(); })
4697
4747
  .then(function(data) {
@@ -4750,6 +4800,10 @@ function buildMultiHtmlTemplate(allProjectData) {
4750
4800
  panel.classList.remove('is-running', 'is-crashed');
4751
4801
  dot.className = 'es-dot'; label.style.color = 'var(--color-success)';
4752
4802
  label.textContent = 'Completed (' + (data.extracted || 0) + ' extracted)';
4803
+ } else if (data.status === 'stopped') {
4804
+ panel.classList.remove('is-running', 'is-crashed');
4805
+ dot.className = 'es-dot'; label.style.color = 'var(--accent-gold)';
4806
+ label.textContent = 'Stopped' + (data.extracted ? ' (' + data.extracted + ' extracted)' : '');
4753
4807
  } else if (data.status === 'crashed') {
4754
4808
  panel.classList.remove('is-running'); panel.classList.add('is-crashed');
4755
4809
  dot.className = 'es-dot crashed'; label.style.color = 'var(--color-danger)';
package/server.js CHANGED
@@ -354,6 +354,15 @@ async function handleRequest(req, res) {
354
354
  if (e.code !== 'ESRCH') throw e;
355
355
  // Already dead
356
356
  }
357
+ // Update progress file to reflect stopped state
358
+ try {
359
+ writeFileSync(PROGRESS_FILE, JSON.stringify({
360
+ ...progress,
361
+ status: 'stopped',
362
+ stopped_at: Date.now(),
363
+ updated_at: Date.now(),
364
+ }, null, 2));
365
+ } catch { /* best-effort */ }
357
366
  json(res, 200, { stopped: true, pid: progress.pid, command: progress.command });
358
367
  } catch (e) {
359
368
  json(res, 500, { error: e.message });
@@ -66,13 +66,39 @@ async function askAgent(messages, opts = {}) {
66
66
  */
67
67
  export async function isGatewayReady() {
68
68
  try {
69
+ // Try to read token from env or openclaw config file
70
+ let token = process.env.OPENCLAW_TOKEN;
71
+ if (!token) {
72
+ try {
73
+ const configPath = join(process.env.HOME || '~', '.openclaw', 'openclaw.json');
74
+ const { readFileSync } = await import('fs');
75
+ const raw = readFileSync(configPath, 'utf8');
76
+ // Gateway auth token is a 48-char hex string in the gateway.auth block
77
+ const matches = [...raw.matchAll(/"token":\s*"([a-f0-9]{40,})"/g)];
78
+ if (matches.length > 0) token = matches[matches.length - 1][1];
79
+ } catch {}
80
+ }
81
+ if (!token) return false;
82
+
83
+ // Verify gateway is reachable via a lightweight chat completions ping
69
84
  const controller = new AbortController();
70
- const timeout = setTimeout(() => controller.abort(), 3000);
71
- const res = await fetch(`${OPENCLAW_API}/v1/models`, {
85
+ const timeout = setTimeout(() => controller.abort(), 5000);
86
+ const res = await fetch(`${OPENCLAW_API}/v1/chat/completions`, {
87
+ method: 'POST',
72
88
  signal: controller.signal,
89
+ headers: {
90
+ 'Authorization': `Bearer ${token}`,
91
+ 'Content-Type': 'application/json',
92
+ },
93
+ body: JSON.stringify({
94
+ model: 'anthropic/claude-haiku-4-5',
95
+ messages: [{ role: 'user', content: 'ping' }],
96
+ max_tokens: 1,
97
+ }),
73
98
  });
74
99
  clearTimeout(timeout);
75
- return res.ok;
100
+ const ct = res.headers.get('content-type') || '';
101
+ return res.ok && ct.includes('application/json');
76
102
  } catch {
77
103
  return false;
78
104
  }
package/setup/wizard.html CHANGED
@@ -54,6 +54,7 @@ body {
54
54
  max-width: var(--max-width);
55
55
  margin: 0 auto 24px;
56
56
  text-align: center;
57
+ position: relative;
57
58
  }
58
59
  .wizard-header h1 {
59
60
  font-family: var(--font-display);
@@ -1172,6 +1173,7 @@ input::placeholder {
1172
1173
  <div class="wizard-header">
1173
1174
  <h1>SEO Intel</h1>
1174
1175
  <div class="subtitle">Setup Wizard</div>
1176
+ <a href="http://localhost:3000/?autorun=setup-classic" title="Run setup in Terminal instead" style="position:absolute;top:14px;right:18px;font-size:0.68rem;color:var(--color-muted,#888);text-decoration:none;opacity:0.7;transition:opacity 0.15s;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.7'"><i class="fa-solid fa-terminal"></i> Terminal setup</a>
1175
1177
  </div>
1176
1178
 
1177
1179
  <!-- ═══════════════════════════════════════════════════════════════════════
package/setup/ROADMAP.md DELETED
@@ -1,109 +0,0 @@
1
- # SEO Intel Setup — Roadmap
2
-
3
- > From open-source CLI tool → standalone product
4
-
5
- ## Current State (v0.2)
6
- - [x] System detection (Node, npm, Ollama, Playwright, VRAM)
7
- - [x] Model recommendations (VRAM-based extraction + analysis tiers)
8
- - [x] Project configuration (target domain, competitors, crawl mode)
9
- - [x] API key setup (Gemini, Claude, OpenAI, DeepSeek)
10
- - [x] Pipeline validation (Ollama → API → crawl → extraction)
11
- - [x] CLI wizard + Web wizard at /setup
12
- - [x] GSC setup step (CSV upload + export guide + auto-detection)
13
- - [x] License system (lib/license.js + lib/gate.js)
14
- - [x] Free/Pro tier gating on all 23 CLI commands
15
- - [x] Page limit enforcement (500/domain on free tier)
16
- - [x] License status in `status` command
17
-
18
- ## Priority 1 — GSC Setup Guide
19
- **Status: ✅ Done (CSV upload, export guide, auto-detection)**
20
-
21
- Google Search Console is the #1 data source users need but can't figure out alone.
22
- Currently: manual CSV export, no API, no guidance in wizard.
23
-
24
- - [ ] Add Step 3.5: "Connect Google Search Console" in web wizard
25
- - [ ] Visual walkthrough: how to export CSVs from GSC UI (screenshots/steps)
26
- - [ ] Auto-detect existing GSC data in `gsc/` folder
27
- - [ ] GSC API integration (service account JSON key upload)
28
- - [ ] Auto-fetch GSC data on schedule (replaces manual CSV)
29
-
30
- ## Priority 2 — Ollama Auto-Install
31
- **Status: 📋 Planned**
32
-
33
- If Ollama isn't found, offer to install it instead of just warning.
34
-
35
- - [ ] macOS: `brew install ollama` or direct download
36
- - [ ] Linux: `curl -fsSL https://ollama.com/install.sh | sh`
37
- - [ ] Windows: direct user to installer URL
38
- - [ ] Auto-start Ollama after install
39
- - [ ] Auto-pull recommended model after install
40
-
41
- ## Priority 3 — Scheduling / Automation
42
- **Status: 📋 Planned**
43
-
44
- After setup, users need recurring crawls. "Set and forget."
45
-
46
- - [ ] "Schedule weekly crawl?" step in wizard
47
- - [ ] Cron job generator (macOS launchd / Linux cron / Windows Task Scheduler)
48
- - [ ] Built-in scheduler (node-cron or setTimeout loop in server.js)
49
- - [ ] Crawl → Extract → Analyze → Regenerate dashboard pipeline
50
- - [ ] "Last run" / "Next run" display on dashboard
51
-
52
- ## Priority 4 — First Run Experience
53
- **Status: 📋 Planned**
54
-
55
- Don't just show CLI commands — offer to run the first crawl right there.
56
-
57
- - [ ] "Run your first crawl now?" button on Step 5
58
- - [ ] SSE progress stream showing crawl progress in real-time
59
- - [ ] Auto-trigger extraction + analysis after crawl
60
- - [ ] Redirect to dashboard when done
61
- - [ ] Estimated time based on competitor count × pages per domain
62
-
63
- ## Priority 5 — Proxy & Rate Limiting
64
- **Status: 📋 Planned**
65
-
66
- Stealth mode users need proxy config to avoid blocks.
67
-
68
- - [ ] Proxy URL input (HTTP/SOCKS5)
69
- - [ ] Proxy rotation list upload
70
- - [ ] Rate limit slider (requests/minute)
71
- - [ ] Per-domain delay configuration
72
- - [ ] "Test proxy" validation step
73
-
74
- ## Priority 6 — Notifications
75
- **Status: 📋 Planned**
76
-
77
- Know when things happen without checking manually.
78
-
79
- - [ ] Email notifications (SMTP setup in wizard)
80
- - [ ] Slack webhook integration
81
- - [ ] Discord webhook integration
82
- - [ ] Configurable triggers: crawl complete, ranking drop, new competitor page
83
- - [ ] Weekly digest email with key metrics
84
-
85
- ## Priority 7 — Data & Backup
86
- **Status: 📋 Planned**
87
-
88
- Where data lives, how big it gets, how to manage it.
89
-
90
- - [ ] Show data directory + size in dashboard footer
91
- - [ ] One-click export (SQLite → JSON/CSV)
92
- - [ ] Auto-backup before major operations
93
- - [ ] Data retention settings (keep last N crawls)
94
- - [ ] Cloud backup option (S3/GCS)
95
-
96
- ---
97
-
98
- ## Open Source → Product Progression
99
-
100
- | Feature | Open Source (froggo.pro) | Standalone SaaS |
101
- |---------|------------------------|-----------------|
102
- | Setup | CLI wizard | Web wizard + onboarding email |
103
- | Auth | None (local) | User accounts + API keys |
104
- | GSC | Manual CSV or API key | OAuth "Connect GSC" button |
105
- | Scheduling | Cron jobs | Built-in + hosted workers |
106
- | Notifications | Webhook only | Email + Slack + in-app |
107
- | Data | Local SQLite | Cloud DB + CDN dashboards |
108
- | Multi-user | Single | Teams + permissions |
109
- | Billing | Free / one-time | Subscription tiers |