intelwatch 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +72 -118
- package/ROADMAP-PREMIUM.md +17 -17
- package/package.json +2 -2
- package/src/ai/client.js +82 -3
- package/src/commands/compare.js +323 -0
- package/src/commands/discover.js +3 -3
- package/src/commands/profile.js +90 -28
- package/src/index.js +5 -4
- package/src/providers/annuaire-entreprises.js +83 -0
- package/src/providers/index.js +2 -0
- package/src/providers/registry.js +156 -50
- package/src/scrapers/annuaire-entreprises.js +461 -0
- package/src/scrapers/searxng-search.js +486 -0
- package/src/trackers/competitor.js +3 -3
- package/src/trackers/keyword.js +3 -3
- package/src/trackers/person.js +3 -3
- package/src/utils/fetcher.js +123 -4
- package/src/utils/parser.js +5 -3
- package/Endrix-Intelwatch-DueDil.pdf +0 -0
- package/src/scrapers/brave-search.js +0 -281
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [1.3.2] - 2026-03-21
|
|
2
|
+
### Added
|
|
3
|
+
- **Google Gemini Provider**: Full support for Gemini models via Google API (`GEMINI_API_KEY` or `GOOGLE_API_KEY`) for Due Diligence AI analysis
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **BODACC Limit**: Reduced BODACC publication injection from 50 to 5 entries for AI context to drastically reduce token usage and prevent truncation
|
|
7
|
+
- **M&A Output**: Reduced AI verbosity on health scores by enforcing strict bullet points constraints
|
|
8
|
+
- **MaxTokens Override**: Removed hardcoded `maxTokens=1000` default and bumped to `8192` to allow full M&A history to generate without arbitrary cutoff
|
|
9
|
+
- **Group Structure Render**: Fixed logic that accidentally injected Private Equity shareholders (like BPIFrance) into the subsidiaries array
|
|
10
|
+
|
|
1
11
|
# CHANGELOG - v1.2 Draft
|
|
2
12
|
|
|
3
13
|
## Version 1.2.0 (en développement)
|
|
@@ -182,3 +192,8 @@ All notable changes to this project will be documented in this file.
|
|
|
182
192
|
- M&A History PDF generation bug: Stopped truncating AI timeline events, full timeline is now preserved.
|
|
183
193
|
- Group Structure Classification: Prevented PE Funds (BPIFrance, IK Partners, etc.) from being improperly categorized as operational subsidiaries in the AI due diligence report.
|
|
184
194
|
- Fixed `pdfData` passthrough bug that caused empty PDF exports.
|
|
195
|
+
|
|
196
|
+
### v1.3.1 (2026-03-21)
|
|
197
|
+
- **Bug Fix**: Fixed an issue where PDF exports could contain empty Group Structure and M&A History sections leading to ugly page breaks.
|
|
198
|
+
- **Bug Fix**: Preserved AI-generated subsidiaries when registry fallback has missing revenue data.
|
|
199
|
+
- **Bug Fix**: Fixed PDF context scope throwing an undefined error when exporting via the `--export pdf` flag instead of the legacy `--format` option.
|
package/README.md
CHANGED
|
@@ -1,174 +1,128 @@
|
|
|
1
1
|
# intelwatch
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Zero friction. Full context.**
|
|
4
|
+
> Competitive intelligence, M&A due diligence, and OSINT directly from your terminal.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
**Intelwatch** bridges the gap between hacker OSINT and B2B Sales/M&A data. It executes complex financial data aggregation, technology stack detection, and AI-powered due diligence in seconds. No dashboards, no bloated UI. Just pure data.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
6
11
|
|
|
7
12
|
```bash
|
|
8
13
|
npm install -g intelwatch
|
|
9
|
-
# or
|
|
10
|
-
|
|
14
|
+
# or run directly without installing:
|
|
15
|
+
npx intelwatch profile kpmg.fr --ai
|
|
11
16
|
```
|
|
12
17
|
|
|
13
18
|
**Requirements:** Node.js >=18
|
|
14
19
|
|
|
15
|
-
##
|
|
20
|
+
## 🚀 The Flagship Feature: Due Diligence
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
# Add trackers
|
|
19
|
-
intelwatch track competitor https://competitor.com --name "Acme Corp"
|
|
20
|
-
intelwatch track keyword "audit SEO"
|
|
21
|
-
intelwatch track brand "Recognity"
|
|
22
|
+
Generate a comprehensive M&A/PE due diligence report in seconds. Intelwatch uses Smart Routing to fetch the best data based on the company's location (Pappers for France, Apollo/Clearbit/OpenCorporates for International).
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
```bash
|
|
25
|
+
# Generate a deep profile with AI Due Diligence
|
|
26
|
+
intelwatch profile doctolib.fr --ai
|
|
25
27
|
|
|
26
|
-
#
|
|
27
|
-
intelwatch
|
|
28
|
+
# Export directly to a premium PDF report (Pro feature)
|
|
29
|
+
intelwatch profile kpmg.fr --ai --export pdf
|
|
28
30
|
|
|
29
|
-
#
|
|
30
|
-
intelwatch
|
|
31
|
+
# Export data to Excel or CSV
|
|
32
|
+
intelwatch profile "Acme Corp" --export xls
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
**What it extracts:**
|
|
36
|
+
- **Financials:** Revenue, net result, consolidated data, and growth analysis.
|
|
37
|
+
- **Group Structure:** Shareholders, PE sponsors, and subsidiaries tree.
|
|
38
|
+
- **Governance & M&A:** Board members, BODACC legal publications, and historical M&A timeline.
|
|
39
|
+
- **OSINT & Reputation:** Recent press mentions, Reddit/Hacker News discussions, and sentiment scoring.
|
|
40
|
+
- **Tech Stack:** Detection of 50+ technologies (CMS, Frameworks, Analytics, CDNs).
|
|
41
|
+
- **AI Analysis:** Executive summary, Strengths & Weaknesses, Competitor identification, and Risk flags.
|
|
42
|
+
|
|
43
|
+
---
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
## 🛠️ Core Commands
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- Pages found (via link extraction)
|
|
40
|
-
- Pricing page content and price changes
|
|
41
|
-
- Technology stack (35+ technologies detected)
|
|
42
|
-
- Open job positions (/careers, /jobs)
|
|
43
|
-
- Social links
|
|
44
|
-
- Meta title/description changes on key pages
|
|
47
|
+
### 1. Market & Competitor Discovery
|
|
48
|
+
Discover actual competitors for any website using web search and AI scoring.
|
|
45
49
|
|
|
46
50
|
```bash
|
|
47
|
-
intelwatch
|
|
48
|
-
intelwatch track competitor https://rival.io
|
|
51
|
+
intelwatch discover https://mycompany.com --export csv
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
### 2. Track Competitors & Keywords
|
|
55
|
+
Set up local trackers to monitor competitor websites, technology changes, and Google SERP rankings over time.
|
|
53
56
|
|
|
54
57
|
```bash
|
|
55
|
-
intelwatch track
|
|
58
|
+
intelwatch track competitor https://acme.com --name "Acme Corp"
|
|
56
59
|
intelwatch track keyword "audit SEO" --engine google
|
|
60
|
+
intelwatch track brand "Recognity"
|
|
57
61
|
```
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
### 3. Check & Digest
|
|
64
|
+
Run your trackers and see what changed since the last snapshot.
|
|
61
65
|
|
|
62
66
|
```bash
|
|
63
|
-
intelwatch
|
|
64
|
-
intelwatch
|
|
67
|
+
intelwatch check
|
|
68
|
+
intelwatch digest
|
|
69
|
+
intelwatch diff acme-com --days 7
|
|
65
70
|
```
|
|
66
71
|
|
|
67
|
-
###
|
|
72
|
+
### 4. AI Briefs & Sales Pitches
|
|
73
|
+
Generate AI-powered competitive briefs and sales pitches against your tracked competitors.
|
|
68
74
|
|
|
69
75
|
```bash
|
|
70
|
-
intelwatch
|
|
71
|
-
intelwatch
|
|
76
|
+
intelwatch ai-summary
|
|
77
|
+
intelwatch pitch acme-com
|
|
72
78
|
```
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
---
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
intelwatch check # Check all trackers
|
|
78
|
-
intelwatch check --tracker acme-com # Check one tracker
|
|
82
|
+
## 💎 Pro License ($49/mo)
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
intelwatch diff acme-com --days 7 # Compare with 7 days ago
|
|
82
|
-
```
|
|
84
|
+
Intelwatch operates on a freemium model. The **Free Tier** allows standard OSINT, basic company profiles, and CSV exports.
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
The **Pro Tier** unlocks:
|
|
87
|
+
- 📄 **Premium PDF & Excel (XLS) exports**
|
|
88
|
+
- 🤖 **AI Due Diligence Reports** (Health score, Risks, M&A timelines)
|
|
89
|
+
- 🌍 **International Routing** (Apollo & Clearbit integrations)
|
|
90
|
+
- 🕵️ **Deep OSINT** (Reddit & Hacker News tracking)
|
|
85
91
|
|
|
92
|
+
**Activate your license:**
|
|
86
93
|
```bash
|
|
87
|
-
intelwatch
|
|
88
|
-
intelwatch report # Markdown report (stdout)
|
|
89
|
-
intelwatch report --format html # HTML report (saved to ~/.intelwatch/reports/)
|
|
90
|
-
intelwatch report --format json # JSON report (stdout)
|
|
91
|
-
intelwatch report --format md --output ./weekly.md # Custom output file
|
|
94
|
+
intelwatch auth YOUR_LICENSE_KEY
|
|
92
95
|
```
|
|
96
|
+
*(Get your key at [recognity.fr/tools/intelwatch](https://recognity.fr/tools/intelwatch))*
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
intelwatch history acme-com # Show snapshot history
|
|
98
|
-
intelwatch history acme-com --limit 10 # Last 10 snapshots
|
|
98
|
+
---
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
```
|
|
100
|
+
## ⚙️ Configuration & API Keys
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
Intelwatch brings your own keys (BYOK) for maximum privacy and limit-less scaling. Set these in your environment variables (`~/.bashrc` or `~/.zshrc`):
|
|
104
103
|
|
|
105
104
|
```bash
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Notifications config is stored at `~/.intelwatch/config.yml`:
|
|
110
|
-
|
|
111
|
-
```yaml
|
|
112
|
-
notifications:
|
|
113
|
-
webhook: https://hooks.slack.com/services/xxx/yyy/zzz
|
|
114
|
-
events:
|
|
115
|
-
- competitor.new_page
|
|
116
|
-
- competitor.price_change
|
|
117
|
-
- keyword.position_change
|
|
118
|
-
- brand.new_mention
|
|
119
|
-
- brand.negative_mention
|
|
120
|
-
```
|
|
105
|
+
# Required for AI Features (Choose one)
|
|
106
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
107
|
+
export OPENAI_API_KEY="sk-proj-..."
|
|
121
108
|
|
|
122
|
-
|
|
109
|
+
# Required for Web Search & Discovery
|
|
110
|
+
export BRAVE_SEARCH_API_KEY="BSAeG..."
|
|
123
111
|
|
|
124
|
-
|
|
112
|
+
# Required for deep French corporate data
|
|
113
|
+
export PAPPERS_API_KEY="86c0dcc..."
|
|
125
114
|
|
|
115
|
+
# Required for deep International corporate data (Pro)
|
|
116
|
+
export APOLLO_API_KEY="..."
|
|
126
117
|
```
|
|
127
|
-
~/.intelwatch/
|
|
128
|
-
├── config.yml # Notification settings
|
|
129
|
-
├── trackers.json # Active trackers
|
|
130
|
-
├── snapshots/ # Historical snapshots (JSON)
|
|
131
|
-
└── reports/ # Generated HTML reports
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Technology Detection
|
|
135
|
-
|
|
136
|
-
Detects 35+ technologies via headers, meta tags, scripts, HTML patterns:
|
|
137
118
|
|
|
138
|
-
|
|
139
|
-
|----------|-------------|
|
|
140
|
-
| CMS | WordPress, Drupal, Joomla |
|
|
141
|
-
| E-commerce | Shopify, Magento |
|
|
142
|
-
| Website Builder | Wix, Squarespace, Webflow |
|
|
143
|
-
| JS Framework | React, Vue.js, Angular, Next.js, Nuxt.js, Gatsby, Svelte |
|
|
144
|
-
| JS Library | jQuery |
|
|
145
|
-
| CSS Framework | Bootstrap, Tailwind CSS |
|
|
146
|
-
| Analytics | Google Analytics, Google Tag Manager, Facebook Pixel, Hotjar |
|
|
147
|
-
| CRM/Marketing | HubSpot, Mailchimp, Intercom |
|
|
148
|
-
| CDN/Security | Cloudflare |
|
|
149
|
-
| Web Server | nginx, Apache |
|
|
150
|
-
| Hosting | Vercel, Netlify |
|
|
151
|
-
| Backend | PHP, Django, Ruby on Rails, Node.js/Express |
|
|
152
|
-
| Payment | Stripe |
|
|
119
|
+
*(You can also use a `.env` file in your working directory).*
|
|
153
120
|
|
|
154
|
-
##
|
|
155
|
-
|
|
156
|
-
English and French word lists for positive/negative detection in brand mentions. Categorizes mentions as: press, blog, forum, social, or review.
|
|
157
|
-
|
|
158
|
-
## Design Principles
|
|
159
|
-
|
|
160
|
-
- **No external APIs** — everything via respectful web scraping
|
|
161
|
-
- **Respectful scraping** — 1-2s delays, user-agent rotation, retry backoff
|
|
162
|
-
- **Graceful degradation** — saves what it can if a check partially fails
|
|
163
|
-
- **Local-first** — all data stays on your machine
|
|
164
|
-
|
|
165
|
-
## Tests
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
npm test
|
|
169
|
-
```
|
|
121
|
+
## 🔒 Privacy & Architecture
|
|
170
122
|
|
|
171
|
-
|
|
123
|
+
- **Local-first**: All tracker data, snapshots, and configurations are stored locally in `~/.intelwatch/`.
|
|
124
|
+
- **No intermediary servers**: The CLI talks directly to the data providers (Pappers, Apollo, Brave, Anthropic/OpenAI). We do not see your API keys or your searches.
|
|
125
|
+
- **Smart Routing**: `registry.js` automatically detects French companies (via SIREN/SIRET or country code) and routes them to Pappers, while international companies gracefully fallback to Apollo and OpenCorporates.
|
|
172
126
|
|
|
173
127
|
## License
|
|
174
128
|
|
package/ROADMAP-PREMIUM.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Intelwatch Deep Profile — Roadmap Premium
|
|
2
2
|
|
|
3
|
-
## Current (v1.
|
|
3
|
+
## Current (v1.3.x — Shipped)
|
|
4
4
|
- [x] Identity + finances 5 ans + consolidé groupe
|
|
5
5
|
- [x] Filiales via recherche-dirigeants + CA/résultat
|
|
6
6
|
- [x] Représentants / actionnariat
|
|
@@ -22,25 +22,25 @@
|
|
|
22
22
|
- [x] **Article scraping for M&A depth** — top 5 articles (2000 chars), content injected into AI
|
|
23
23
|
- [x] **Key date extraction from articles** — regex code-side, authoritative dates
|
|
24
24
|
- [x] **FLI code-built revenue target** — scans all articles, picks highest announced target, overrides AI
|
|
25
|
-
- [x] **Stale year warning** — red ⚠️ badge on subsidiaries
|
|
25
|
+
- [x] **Stale year warning** — red ⚠️ badge on subsidiaries avec data > 2 ans
|
|
26
|
+
- [x] Export JSON/CSV/XLS structuré
|
|
27
|
+
- [x] `--lang fr` option (PDF + AI prompts in French)
|
|
28
|
+
- [x] **OpenCorporates** — filiales internationales
|
|
29
|
+
- [x] **Smart Routing & International Pivot** — Apollo.io + Clearbit + OpenCorporates
|
|
30
|
+
- [x] OSINT scraping (Reddit, HackerNews)
|
|
31
|
+
- [x] Freemium gate (Free vs Pro) + license key check
|
|
32
|
+
- [x] Fix forces/faiblesses vides en terminal (parsing JSON)
|
|
33
|
+
- [x] Commit + npm publish as `intelwatch@1.3.x`
|
|
26
34
|
|
|
27
|
-
##
|
|
28
|
-
- [ ] Fix forces/faiblesses vides en terminal (parsing JSON)
|
|
29
|
-
- [ ] Export JSON/CSV structuré
|
|
30
|
-
- [ ] `--lang fr` option (PDF + AI prompts in French)
|
|
31
|
-
- [ ] Comparable transactions section (competitors' M&A/fundraising with article links)
|
|
32
|
-
- [ ] Geographic implantations scraping from company website
|
|
33
|
-
- [ ] Cross-reference press/journalists across sections
|
|
34
|
-
- [ ] Commit + npm publish as `intelwatch@1.2.0`
|
|
35
|
-
|
|
36
|
-
## V2 — Pro ($49/mo)
|
|
35
|
+
## V2 — Pro ($49/mo) — Next Releases
|
|
37
36
|
- [ ] `intelwatch compare SIREN1 SIREN2` — côte à côte
|
|
38
37
|
- [ ] **INPI integration** — brevets & marques du groupe (gratuit, data.inpi.fr)
|
|
39
|
-
- [ ] **OpenCorporates** — filiales internationales (gratuit)
|
|
40
38
|
- [ ] **Annuaire Entreprises / data.gouv** — données complémentaires
|
|
41
|
-
- [ ] Freemium gate (3 profiles/jour) + license key
|
|
42
39
|
- [ ] `--preview` mode limité (identity + dernier exercice)
|
|
43
40
|
- [ ] **BODACC détaillé enrichi** — timeline types d'actes, augmentations capital
|
|
41
|
+
- [ ] Comparable transactions section (competitors' M&A/fundraising with article links)
|
|
42
|
+
- [ ] Geographic implantations scraping from company website
|
|
43
|
+
- [ ] Cross-reference press/journalists across sections
|
|
44
44
|
|
|
45
45
|
## V3 — Deep Profile ($299/mo)
|
|
46
46
|
- [ ] **Graphe de liens** — visualisation SVG/HTML du réseau (Pappers graph-style)
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
| Pappers | ✅ (BYOK) | ✅ API | v1.0 ✅ |
|
|
65
65
|
| Brave Search | ✅ (BYOK) | ✅ API | v1.0 ✅ |
|
|
66
66
|
| INPI (brevets/marques) | ✅ | ✅ data.inpi.fr | V2 |
|
|
67
|
-
| OpenCorporates | ✅ | ✅ |
|
|
67
|
+
| OpenCorporates | ✅ | ✅ | v1.3 ✅ |
|
|
68
68
|
| Annuaire Entreprises | ✅ | ✅ data.gouv | V2 |
|
|
69
69
|
| BODACC détaillé | ✅ | ✅ bodacc.fr | V2 |
|
|
70
70
|
| Infogreffe | ❌ 3.80€/doc | API payante | V4 |
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
## Pricing
|
|
78
78
|
| Tier | Prix | Limites | Features |
|
|
79
79
|
|------|------|---------|----------|
|
|
80
|
-
| Free | 0€ |
|
|
81
|
-
| Pro | 49€/mo | Illimité | Full profile + AI + PDF + export + INPI +
|
|
80
|
+
| Free | 0€ | Bypass limites CSV | Identity + last year only |
|
|
81
|
+
| Pro | 49€/mo | Illimité | Full profile + AI + PDF + export XLS + INPI + Reddit/HN |
|
|
82
82
|
| Deep Profile | 299€/mo | Illimité | Tout Pro + graphe + alertes + batch + scoring |
|
|
83
83
|
| Enterprise | Custom | Custom | Tout Deep + Infogreffe + SEMrush + API REST |
|
|
84
84
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intelwatch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Competitive intelligence CLI — track competitors, keywords, and brand mentions from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"intelwatch": "./bin/intelwatch.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "node --test test/storage.test.js test/sentiment.test.js test/tech-detect.test.js test/export.test.js test/i18n.test.js test/error-handler.test.js test/reddit-hn.test.js test/license.test.js test/providers.test.js",
|
|
11
|
+
"test": "node --test test/storage.test.js test/sentiment.test.js test/tech-detect.test.js test/export.test.js test/i18n.test.js test/error-handler.test.js test/reddit-hn.test.js test/license.test.js test/providers.test.js test/searxng-search.test.js test/compare.test.js",
|
|
12
12
|
"start": "node bin/intelwatch.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
package/src/ai/client.js
CHANGED
|
@@ -5,9 +5,13 @@ import { loadConfig } from '../config.js';
|
|
|
5
5
|
* Returns null if no key is configured.
|
|
6
6
|
*/
|
|
7
7
|
export function getAIConfig() {
|
|
8
|
+
const envGoogle = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
8
9
|
const envOpenAI = process.env.OPENAI_API_KEY;
|
|
9
10
|
const envAnthropic = process.env.ANTHROPIC_API_KEY;
|
|
10
11
|
|
|
12
|
+
if (envGoogle) {
|
|
13
|
+
return { provider: 'google', apiKey: envGoogle, model: 'gemini-2.5-flash' };
|
|
14
|
+
}
|
|
11
15
|
if (envOpenAI) {
|
|
12
16
|
return { provider: 'openai', apiKey: envOpenAI, model: 'gpt-4o-mini' };
|
|
13
17
|
}
|
|
@@ -44,17 +48,28 @@ export function hasAIKey() {
|
|
|
44
48
|
* Throws on API errors.
|
|
45
49
|
*/
|
|
46
50
|
export async function callAI(systemPrompt, userPrompt, options = {}) {
|
|
51
|
+
const maxTokens = options.maxTokens || 1000;
|
|
52
|
+
|
|
53
|
+
if (options.uncensored) {
|
|
54
|
+
const host = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
55
|
+
// We default to llama3 for uncensored local OSINT if the user hasn't specified one
|
|
56
|
+
const model = process.env.OLLAMA_MODEL || 'llama3';
|
|
57
|
+
return callOllama(host, model, systemPrompt, userPrompt, maxTokens);
|
|
58
|
+
}
|
|
59
|
+
|
|
47
60
|
const aiConfig = getAIConfig();
|
|
48
61
|
if (!aiConfig) {
|
|
49
62
|
throw new Error(
|
|
50
|
-
'No AI API key configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY, ' +
|
|
51
|
-
'or add ai.api_key to ~/.intelwatch/config.yml'
|
|
63
|
+
'No AI API key configured. Set GEMINI_API_KEY, OPENAI_API_KEY or ANTHROPIC_API_KEY, ' +
|
|
64
|
+
'or add ai.api_key to ~/.intelwatch/config.yml. Use --uncensored for local Ollama.'
|
|
52
65
|
);
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
const { provider, apiKey, model } = aiConfig;
|
|
56
|
-
const maxTokens = options.maxTokens || 1000;
|
|
57
69
|
|
|
70
|
+
if (provider === 'google') {
|
|
71
|
+
return callGoogle(apiKey, model, systemPrompt, userPrompt, maxTokens);
|
|
72
|
+
}
|
|
58
73
|
if (provider === 'anthropic') {
|
|
59
74
|
return callAnthropic(apiKey, model, systemPrompt, userPrompt, maxTokens);
|
|
60
75
|
}
|
|
@@ -128,3 +143,67 @@ export function estimateCost(inputChars, outputChars, provider = 'openai') {
|
|
|
128
143
|
const cost = inputTokens * rates.in + outputTokens * rates.out;
|
|
129
144
|
return { inputTokens, outputTokens, cost: cost.toFixed(5) };
|
|
130
145
|
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async function callGoogle(apiKey, model, systemPrompt, userPrompt, maxTokens) {
|
|
149
|
+
// Use v1beta for Gemini 2.5
|
|
150
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
151
|
+
const res = await fetch(url, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
},
|
|
156
|
+
body: JSON.stringify({
|
|
157
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
158
|
+
contents: [{ role: 'user', parts: [{ text: userPrompt }] }],
|
|
159
|
+
generationConfig: {
|
|
160
|
+
maxOutputTokens: maxTokens,
|
|
161
|
+
temperature: 0.2
|
|
162
|
+
}
|
|
163
|
+
}),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
const body = await res.text();
|
|
168
|
+
throw new Error(`Google API ${res.status}: ${body.slice(0, 200)}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const data = await res.json();
|
|
172
|
+
if (!data.candidates || !data.candidates[0].content) {
|
|
173
|
+
throw new Error('Invalid Google API response');
|
|
174
|
+
}
|
|
175
|
+
return data.candidates[0].content.parts[0].text.trim();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function callOllama(host, model, systemPrompt, userPrompt, maxTokens) {
|
|
179
|
+
const url = `${host.replace(/\/$/, '')}/api/chat`;
|
|
180
|
+
const res = await fetch(url, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
model: model,
|
|
187
|
+
messages: [
|
|
188
|
+
{ role: 'system', content: systemPrompt },
|
|
189
|
+
{ role: 'user', content: userPrompt }
|
|
190
|
+
],
|
|
191
|
+
stream: false,
|
|
192
|
+
options: {
|
|
193
|
+
num_ctx: 16384,
|
|
194
|
+
num_predict: maxTokens
|
|
195
|
+
}
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
const body = await res.text();
|
|
201
|
+
throw new Error(`Ollama API ${res.status}: ${body.slice(0, 200)}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const data = await res.json();
|
|
205
|
+
if (!data.message || !data.message.content) {
|
|
206
|
+
throw new Error('Invalid Ollama API response');
|
|
207
|
+
}
|
|
208
|
+
return data.message.content.trim();
|
|
209
|
+
}
|