Rene Fichtmueller
c0cd0dc1ca
feat: compatibility panel — verification_method, competitor prices, spec-match collapsible
...
- API: getCompatibleTransceivers() returns verification_method, orders vendor_compat first
- Dashboard: Flexoptix section splits vendor-tested vs spec-match (collapsed)
- Dashboard: Competitor section shows vendor-tested with prices, spec-match as chips
2026-04-20 22:52:49 +02:00
Rene Fichtmueller
0fb4850dfa
fix: price-comparison SKU lookup — wrong column refs (so.stock_level, search_url_template)
2026-04-19 00:12:18 +02:00
Rene Fichtmueller
446ac667b0
feat: side-by-side competitor comparison + fix 1.6T speed_gbps
...
- Fix OSFP-DR8-1.6T-FL and OSFP-2FR4-1.6T-FL: speed_gbps was 200, now 1600
→ FS.com 1.6T products now correctly match as comparables for Flexoptix O.1316T.C.05.M
- API: extend comparable price query to return comp_form_factor, comp_speed_gbps,
comp_reach_meters, comp_reach_label, comp_fiber_type, comp_wavelengths
- Dashboard: replace plain comparable price row with side-by-side spec comparison card
showing Flexoptix vs. competitor: Form Factor, Speed, Reach, Fiber, Wavelengths
with color coding (green=match, orange=mismatch) and savings badge (−45% günstiger)
2026-04-18 21:51:41 +02:00
Rene Fichtmueller
62d97a783c
feat: add claude-code LLM provider + update dashboard to fo-blog-v5
...
- client.ts: add claude-code provider routing BLOG_LLM_PROVIDER=claude-code
to claude-bridge (flat-rate, no API billing via Claude Code subscription)
- checkHealth() now pings /health on claude-bridge for real availability check
- Default OLLAMA_LLM_MODEL changed from qwen2.5:14b to fo-blog-v5
- Dashboard: add claude-code card (EMPFOHLEN), rename fo-blog-v3 → fo-blog-v5
- loadBlogLLMStatus() handles all 3 providers: claude-code/anthropic/ollama
- Grid expanded from 3 to 4 columns to accommodate new card
- ecosystem.config.js + .env on Erik: OLLAMA_LLM_MODEL=fo-blog-v5 confirmed
2026-04-18 20:45:14 +02:00
Rene Fichtmueller
1c8dec52c9
feat: Price Comparison dashboard + Eoptolink OEM scraper
...
- Add public /api/price-comparison API (summary, top-50, per-SKU detail)
— no auth required, 3 Express routes, DISTINCT ON latest-price logic
- Add '💲 Price Comparison' dashboard tab: stat cards, form-factor
breakdown, top-50 SKU table (clickable rows → SKU detail), per-vendor
price + stock + spread% lookup panel
- Add Eoptolink OEM catalog scraper (93 product-solution pages,
part-number regex EOLO-*/EOLQ-* etc., no prices, seeds transceivers
table as manufacturer entries)
- Register scrape:catalog:eoptolink in scheduler: schedule every 4h
(40 */4 * * *), lazy-import worker, added to known-jobs array
2026-04-18 01:02:08 +02:00
Rene Fichtmueller
e9fcda2811
feat: wire finder.ts + switch-docs + Ollama LLM tools to MCP server
...
MCP Server (packages/mcp-server/src/index.ts):
- Register registerSwitchDocTools (switch-docs.ts) — switch documentation lookup
- Register finderTools dynamically (finder.ts) — find_flexoptix_for_switch, get_competitor_alerts
- Add analyze_market_with_llm tool: qwen2.5:14b via Ollama, enriched with live hype cycle + pricing + news
- Add generate_blog_post tool: fo-blog-v5 (fine-tuned) with qwen2.5:14b fallback, enriched with live pricing data
- OLLAMA_BASE_URL env var (default: https://ollama.fichtmueller.org )
Also includes scraper improvements (ascentoptics, atgbics, gbics, skylane, ebay-enricher),
API route updates (blog, blog-sll, health, hot-topics, transceivers, queries),
and dashboard hot-topics refresh.
2026-04-18 00:21:58 +02:00
Rene Fichtmueller
b88a6e28cf
feat: /api/hype-cycle/analysis endpoint — DB-backed Bass-fitted results from hype_cycle_analysis table
2026-04-18 00:11:08 +02:00
Rene Fichtmueller
861243ea3f
feat: stock confidence badges, multi-vendor price comparison, expanded Cisco TMG + Juniper HCT
...
Stock API & Dashboard:
- /api/stock/summary: vendor_breakdown adds avg_confidence, currencies, conf_per_warehouse/aggregated/boolean
- /api/stock/summary: new price_comparison endpoint (multi-vendor SKUs, min/max/avg price)
- /api/stock/summary: totals adds multi_vendor_skus count
- Dashboard: 6th stat card (Multi-Vendor SKUs), confidence badge column (🟢 L3 / 🟡 L2 / ⚪ L1)
- Dashboard: price comparison table with vendor-by-vendor price breakdown
- Dashboard: subtitle updated to include QSFPTEK + NADDOD
- Dashboard: top sellers link to product URLs
Cisco TMG improvements:
- Added 5 new platform families: 8000 Series, NCS5500, NCS540, NCS560, NCS1000
- Per-device query strategy: iterates all switch model IDs from family filter
instead of getting only 1 switch per family → 58 switches per N9300 run
- Graceful error handling per device with rate limiting (1s between requests)
Juniper HCT: ran manually → 475 Juniper-brand transceivers seeded
2026-04-17 23:33:31 +02:00
Rene Fichtmueller
5b35b2b8be
feat(scraper+api): warehouse stock data pipeline — FS.com v2, SmartOptics v2, Stock API
...
Scraper changes:
- fs-com.ts v2: Playwright stealth patches + www.fs.com/de/ URL fix (de.fs.com DNS NXDOMAIN).
Extracts DE-Lager, Global-Lager, Nachlieferung, units_sold, compatible_brands, price_net.
Mac-side runner (run-fs-scraper-mac.sh) via SSH tunnel for residential IP access.
Fast-fail connectivity check on datacenter IPs that are blocked by Cloudflare.
- smartoptics.ts v2: WooCommerce REST API fallback + 8 catalog categories + relative URL fix.
Was finding only 8 products, now discovers 18+ with multi-category crawl.
DB layer:
- db.ts: add upsertStockObservation() — writes 10 new stock_observations columns
(warehouse_de_qty, warehouse_global_qty, backorder_qty, units_sold, compatible_brands,
price_net, product_url, delivery dates) with dedup check.
API:
- routes/stock.ts: GET /api/stock, /api/stock/summary, /api/stock/:id
Warehouse breakdowns per transceiver/vendor with top-sellers and vendor summary.
- routes/review.ts: equivalence review queue (approve/reject/bulk-approve).
- index.ts: register /api/stock and /api/review routes.
Dashboard:
- index.html: 🏭 Stock tab with stat cards (DE-Lager, Global-Lager, Nachlieferung totals),
top-sellers table, vendor breakdown, recently-restocked events, part-number lookup.
SQL migrations:
- 034: blog-review-tag, 035: price-observations is_anomalous, 036: transceiver-equivalences.
2026-04-17 10:45:59 +02:00
Rene Fichtmueller
cddc92c9d2
feat: TIP audit fixes — Qdrant init, switches columns, verification fix, crawler live status, demo data badges
...
- Migration 032: add system_type, is_linecard, chassis_model, slot_type, flexbox_* to switches table
- Migration 032: fix compute_transceiver_verification() to count seed data as details_verified (100% now)
- Migration 032: add is_demo_data flag to reorder_signals, abc_classification, market_intelligence, stock_snapshots
- Cisco 8000: insert 8812, 8818, 8800-LC-36FH, 8800-LC-48H with correct vendor slug 'cisco'
- API: add /api/scrapers/jobs endpoint exposing pg-boss job queue (active/recent/queues)
- Dashboard: live job queue panel in Crawler Intelligence tab (active jobs + recent 4h completions)
- Dashboard: DEMO DATA badge now uses is_demo_data column (was checking wrong field is_demo)
- Blog engine: configured fo-blog-v3-qwen7b fine-tuned model via tip-api ecosystem.config.js
- Qdrant: all 6 collections created, seeded (2135 products, 29 FAQs, 39 news, 20 troubleshooting)
2026-04-09 20:29:46 +02:00
Rene Fichtmueller
cf75eee8ad
feat: linecard system support, Cisco 8000 accuracy, price anomaly detection
...
API/finder:
- Add modular chassis support: sibling linecards fetched when is_linecard=true
- Add chassis linecards when system_type=modular
- Extend switch response: system_type, is_linecard, chassis_model, slot_type,
flexbox_compat_mode, flexbox_notes, description, switching_capacity_tbps,
total_ports, category, lifecycle_status, features, use_cases, linecards[]
API/transceivers:
- Filter price_observations with COALESCE(is_anomalous, false) = false
(direct prices + comparable market prices)
Scraper/db:
- Add PRICE_BOUNDS map (per form-factor min/max USD sanity bounds)
- Add isPriceAnomalous() — marks DB price_observations as is_anomalous=true
- Add competitor_verified flag: set true when valid competitor price stored
- upsertPriceObservation: skip prices outside sanity bounds, set competitor_verified
Scraper/hash:
- contentHash() now accepts Record<string,unknown> | string (union type)
to support both structured objects and legacy string callers
Scrapers (skylane, tscom, wiitek):
- Fix contentHash() call signature: pass objects not JSON.stringify strings
- Fix wiitek: remove invalid 'name' param, fix t.id → transceiverId
Migrations:
- Add is_anomalous, competitor_verified, competitor_verified_at,
image_primary columns
- Recreate sync_fully_verified trigger to include competitor_verified
- Add is_linecard, chassis_model, system_type, slot_type,
flexbox_compat_mode, flexbox_notes to switches table
2026-04-09 09:06:22 +02:00
Rene Fichtmueller
51af249361
Merge remote-tracking branch 'github/main'
...
# Conflicts:
# packages/api/src/llm/fo-blog-pipeline.ts
# packages/api/src/routes/blog.ts
# packages/scraper/src/scheduler.ts
# packages/scraper/src/scrapers/fs-com.ts
# packages/scraper/src/scrapers/gbics.ts
2026-04-06 18:03:36 +02:00
Rene Fichtmueller
3928755c60
fix: correct verified badge, comparable pricing, and clickable product images
...
- Reset details_verified=false for 298 products where reach_label is empty (DB migration)
- Runtime check in dashboard: dVer requires non-empty reach_label regardless of DB flag
- comparable price query: treat reach_meters=0 same as NULL so 800G OSFP products
find FS.com equivalent prices (was blocked by reach_meters=0 != NULL shortcircuit)
- Product image area now fully clickable with vendor link overlay when product_page_url exists
- Clear wrong image for O.Czz8HG.z.R (was showing unrelated OSFP product image)
2026-04-06 10:24:39 +02:00
Rene Fichtmueller
4acf293690
fix(llm): checkHealth uses key presence check, not live API call
...
Live Anthropic API call during health check causes 429 when the pipeline
is actively running, blocking all subsequent regenerate requests.
2026-04-06 04:07:21 +02:00
Rene Fichtmueller
72033ff5c5
fix(blog): fix claudeQueue deadlock from recursive 429 retry
...
The generateClaude() function was recursively calling itself inside
enqueueClaude(), creating a circular Promise dependency that permanently
deadlocked the claudeQueue. Any 429 rate-limit response would poison
the queue, blocking all future Claude API calls until server restart.
Fixes:
- Split retries into claudeApiCall() which is called from enqueueClaude
(not re-entering the queue on retry = no circular dependency)
- Max 3 retries with increasing backoff (10s/30s/60s)
- Add resetClaudeQueue() exported function
- Add 15-minute auto-reset stall detection to enqueueClaude
- Expose resetClaudeQueue in POST /api/blog/llm/reset-queue endpoint
- Fix merge conflict markers in index.ts (duplicate scraperRouter import)
2026-04-06 02:51:28 +02:00
Rene Fichtmueller
a35817c96d
fix: preserve user-provided title in blog generation + price floor validation
...
- blog/generate now uses caller title when provided; falls back to template
- Migration 027: hard price floor by speed class in verification function
(no medians, no estimates — only real prices above minimum thresholds)
- Deleted 474 obviously wrong price observations (shipping costs scraped as prices)
2026-04-06 01:14:37 +02:00
Rene Fichtmueller
b6928265bf
fix: serialize Claude API calls via queue to prevent 429 rate-limit spam
...
Tier-1 Anthropic API has 40K TPM — with ~20K tokens per pipeline step,
concurrent calls immediately hit the limit. enqueueClaude() serializes
all generateClaude() calls so only one runs at a time, eliminating
the flood of 429-retry-429-retry loops.
2026-04-06 00:57:03 +02:00
Rene Fichtmueller
cf04549b1b
feat: add Anthropic Claude provider to blog LLM client
...
- Auto-routes to Claude API when BLOG_LLM_PROVIDER=anthropic + ANTHROPIC_API_KEY set
- Fallback to Ollama queue when key not present
- Add rate-limit retry (429 → 10s backoff) for Claude API
- Add STEP_TECHNICAL_SANITY, STEP_SELF_HEAL, STEP_TITLE_CONTRACT_CHECK prompts
- Fix STEP_LINKEDIN_POST angle-specific hooks, remove Gold Reference repetition
2026-04-06 00:21:48 +02:00
Rene Fichtmueller
c43e1f881a
fix(blog): hard story blacklist in STEP4 + LinkedIn — ban 2AM/dirty connector/lab-vs-prod stories
...
10 specific story patterns banned directly in draft prompt.
LinkedIn banned hooks: 'Everything looks fine', 'CRC creeping', 'Same optics same setup'.
Title Contract now injected into STEP4 as binding constraint.
2026-04-05 23:55:56 +02:00
Rene Fichtmueller
80435f8e07
feat(blog): Post to Ghost + LinkedIn buttons in dashboard
...
- 'Post on blog.fichtmueller.org' → publishes via Ghost Admin API
- 'Post on LinkedIn' → modal with text + copy + open LinkedIn
- Ghost integration: TIP Blog Engine (JWT auth, mobiledoc format)
2026-04-05 23:33:58 +02:00
Rene Fichtmueller
3913372f10
feat(blog): Title Contract + Technical Sanity Check + Self-Heal + angle-aware LinkedIn generator
...
Pipeline now has 21 steps:
- STEP0: Title Contract binds LLM to headline promise
- STEP19: Technical Sanity Check (optical engineering accuracy)
- STEP20: Self-Heal (auto-fix technical errors preserving tone)
- STEP21: Title Contract Verification (final gate check)
- LinkedIn generator is now angle-aware (no more default Physical Layer hook)
2026-04-05 23:11:16 +02:00
Rene Fichtmueller
8e0eda6c41
fix(blog): anti-repetition engine — 6 angle types, forbidden structures, existing article context injection
2026-04-05 22:47:15 +02:00
Rene Fichtmueller
438225cf7c
fix(blog): raise word target to 1200-1600, fix power-budget false positive in validateArticle
2026-04-05 20:49:22 +02:00
Rene Fichtmueller
7c7d9f7b51
fix: make /api/hot-topics public — dashboard fetch has no auth token
2026-04-05 12:07:44 +02:00
Rene Fichtmueller
e6d042f827
fix: resolve merge conflict in index.ts + add untracked blog-sll, news, sql migration
2026-04-05 11:51:07 +02:00
root
5d8768b43b
fix: mount blogSllRouter + scraperRouter — SLL and Crawler Intelligence routes were missing
2026-04-05 09:50:30 +00:00
Rene Fichtmueller
e4dfd2a2db
feat(blog): AEM/APM pipeline steps + SLL context builder + LinkedIn v2 prompts
2026-04-05 01:26:09 +02:00
Rene Fichtmueller
95a8aa8552
fix: include linkedin_post in GET /api/blog response for SLL matching
2026-04-05 01:24:52 +02:00
Rene Fichtmueller
931588fffd
fix(verification): 100% Verified Badge war dramatisch zu großzügig
...
KERNPROBLEME BEHOBEN:
1. ATGBICS part_number = URL slug statt echte OEM-Nummer
extractOemPartNumber() entfernt -r-compatible-transceiver-* Suffix
+ trailing Vendor-Namen (nokia, cisco, juniper, ...)
Ergebnis: 3he16564aa-nokia-r-compatible-transceiver-... → 3HE16564AA
2. reach_label = '' (leer) wurde als details_verified akzeptiert
IS NOT NULL erlaubt leere Strings → Fix: AND reach_label != ''
3. details_verified = true trotz garbled part_number
Neue Kriterien: NOT ILIKE '%-compatible-transceiver%'
NOT ILIKE '%-r-compatible%'
4. data_confidence Werte falsch in Funktion ('scraped_unverified' etc)
Echte Werte: low/medium/high/garbage → NOT IN ('garbage','unknown')
ERGEBNIS nach recompute_all_verification():
fully_verified: 3.654 → 581 (Badge war 6x übertrieben)
details_verified: inflated → 1.075 (korrekt)
ATGBICS Scraper:
- extractOemPartNumber() für collection und product detail pages
- detectReach() jetzt auch auf URL-slug (120km im slug → reach_label)
Price Anomaly Detection:
- API: price_anomaly field wenn max/min ratio ≥ 10x
- Dashboard: ⚠ Preisanomalie Banner mit Ratio + EUR Range
SQL 025: Part number cleanup (30 records), reach from slug (12 records)
2026-04-04 15:41:57 +02:00
Rene Fichtmueller
1e789f67eb
fix(scrapers): Flexoptix Catalog zeigt 0 records statt 963
...
SCRAPERS list used 'flexoptix-catalog' as DB lookup key but vendors.slug
is 'flexoptix' — no match → 0 records shown.
Fix: added dbSlug override field to SCRAPERS entries; lookup now uses
dbSlug || name so flexoptix-catalog/vendors/supported all map to
the correct 'flexoptix' slug in sourceMap.
2026-04-04 15:26:04 +02:00
Rene Fichtmueller
fea0b0fb66
feat: blog engine v5 — Auto-Kill Layer, 16-step pipeline, longer content
...
Upgrades FO Blog Pipeline from 14 to 16 steps:
- NEW Step 8d: Auto-Kill Layer v1.0 (10 systematic categories A-J)
- NEW Step 15: Auto-Kill Scoring (cleanliness, narrative, non-AI, relevance)
- Updated banned phrases from Gold-standard editorial feedback
- Soft Delete List for conditional phrases
- Auto-Kill categories: spec blocks, formulas, section leakage,
generic transitions, repeated concepts, SKU mentions, false authority,
over-explained basics, whitepaper tone, fake precision
Content length changes per user feedback:
- Blog target: 1,200-2,000 words (was 700-1,000) — thorough and detailed
- LinkedIn target: 2,000-2,800 chars (was 350-600) — use maximum length
- Reduction pass: 25-30% cut (was 15-25%) — remove weak, keep depth
2026-04-04 11:02:45 +02:00
Rene Fichtmueller
ede4f5b966
feat: blog engine v3 — 8-stage pipeline with Auto-Kill Layer
...
Complete rewrite of blog prompts and pipeline based on editorial
Gold-standard feedback. Replaces 3-pass system with 8-stage pipeline:
1. Master generation (narrative voice, no spec dumps)
2. Narrative Control (kill visible structure, enforce flow)
3. Auto-Kill Layer (remove AI phrases, spec residue, repetition)
4. Reduction Engine (cut 40% — keep strongest ideas only)
5. Depth pass (add specifics where vague, no spec dumps)
6. Quality Control (hard delete list validation)
7. Procurement layer (optional, sales audience)
8. LinkedIn post generation (new)
Key changes:
- System prompt rewritten with Hard Delete List (29 banned phrases)
- Soft Delete List for conditional phrases
- Auto-Kill categories A-J (spec blocks, formulas, whitepaper tone, etc.)
- Master prompts enforce continuous narrative, no section headings
- Word count targets reduced (800-1200 instead of 1500+)
- Scoring pass added (cleanliness, narrative, non-AI feel, relevance)
- LinkedIn companion post auto-generated
- Context data injection reduced (fewer items, no dump instructions)
2026-04-04 10:52:31 +02:00
Rene Fichtmueller
c509251109
feat(blog): Spec dump hard fail + Gold Standards 6 + LinkedIn v2
...
- System prompt: SPEC DUMP ABSOLUTE HARD FAIL block (before FORMAT rules)
TX/RX tables, multi-optic comparison blocks, repeated sections = hard fail
Behavioral prose rule: "what actually happens" not "what the spec says"
- STEP9 QA: check 12a SPEC DUMP — removes datasheet blocks, flags
duplicate sections (e.g. "fiber types" twice), spec-heavy intros
- Gold Standard 6: 400G/800G deep dive corrected (8.8→10/10)
zero spec tables, pure behavioral narrative, 3 core ideas max,
ending is reframe not checklist
- LinkedIn Gold Example 2: sharper short format (346 chars vs 700)
reframe hook, short beats without bullet markers, no emoji, 4 hashtags
- STEP_LINKEDIN_POST: rewritten with new gold format
optimal 350-600 chars, beat rhythm, no bullet markers, gold example inline
- WRONG PATTERNS: +7 new entries (spec dump, duplicate section,
LinkedIn bullet list, LinkedIn "excited to share" hook, LinkedIn >800 chars)
2026-04-04 09:32:01 +02:00
Rene Fichtmueller
4f631fc61e
feat(blog): Reduction Engine v1.0 + LaTeX/connector hard fails
...
- Replace STEP8b_REDUCTION with 5-pass Reduction Engine:
Pass 1: Repetition Kill (one concept, one home)
Pass 2: Tech Prune (LaTeX hard delete, SKU removal, formula prose replacement)
Pass 3: Flow Rebuild (close gaps after cuts, no new content)
Pass 4: Weight Correction (title/content alignment throughout)
Pass 5: Humanization (rhythm variation, hedge removal, punch ending)
Target: 700-1000 words (600-1300 range, warnings outside)
- System prompt + STEP9 QA: add hard fails for
LaTeX formulas (\[...\], \frac{}, \text{} etc) — destroys blog flow
DR4 connector error (DR4=MPO-12, not LC duplex; FR4=LC duplex)
Title/content mismatch (title topic must be the spine, not just the intro)
- Gold Standard 5: market alert / pricing article template
(correct title alignment, no LaTeX, DR4=MPO-12, ending on topic)
- WRONG PATTERNS extended with 4 new entries covering above failures
- blog.ts: step log messages updated to 11-14/14; word count
output shows % reduction and range warning (>1300 or <600)
2026-04-04 08:57:21 +02:00
Rene Fichtmueller
1e19365e96
feat: blog engine v5 — narrative control + linkedin post + min words fix
...
- STEP4b_NARRATIVE_CONTROL: new pipeline step after draft; detects wrong
narrative (technology blamed instead of processes), applies anti-FUD filter,
reality reframe ("this becomes a problem when..."), Flexoptix voice check
- System prompt: NARRATIVE CONTROL RULE added as absolute rule #1
- Gold Standard 4: corrected "compatible vs OEM" article added as reference
- Minimum words: STEP4 raised from 1500 to 2500 words (final output was 750)
- Reduction pass: 25-35% → 15-25%, target 1500-2000 words final
- STEP_LINKEDIN_POST: generates LinkedIn post ≤2800 chars (hard limit 3000);
stores in blog_drafts.linkedin_post + linkedin_char_count column
- Pipeline now 14 steps: v5-narrative-control
- Migration 024: linkedin_post + linkedin_char_count columns in blog_drafts
2026-04-04 08:30:27 +02:00
Rene Fichtmueller
ae0bda9e06
feat: proxy network — geo-lookup, uptime tracking, dedup fix
...
- IP geo-lookup via ip-api.com on register/heartbeat (country_code, city)
- heartbeat_count column + uptime_pct computation on every heartbeat
- Deduplication: register returns existing token for same IP+port
- Heartbeat no longer overwrites registered IP (prevents IPv6 churn conflicts)
- Migration 023: heartbeat_count column + backfill existing nodes
2026-04-04 08:15:32 +02:00
Rene Fichtmueller
f616e0ebbe
feat: blog engine v4 (reduction+style-lock passes) + flexoptix scraper fixes
...
Blog engine (fo-blog-pipeline.ts):
- Add STEP8b_REDUCTION: cuts article 25-35%, removes repeated concepts
- Add STEP8c_STYLE_LOCK: enforces tone consistency, fixes scope/OPM confusion,
removes inline SKUs from article flow
- Add Gold Standard 3 to calibration (Style B troubleshooting example 2026-04-04)
- Pipeline now 12 steps (was 10), version bumped to v4-reduction-stylelock
blog.ts:
- Wire STEP8b and STEP8c into pipeline between Kill-AI-Tone and QA Check
- Update progress tracking to 12 total steps
- Update pipeline_version to 'v4-reduction-stylelock'
flexoptix-catalog.ts:
- Fix contentHash call: pass object directly, not JSON.stringify(object)
db.ts:
- price_verified=true set in content_hash early-return path (no new observation)
- image_verified=true auto-set in findOrCreateScrapedTransceiver on INSERT/UPDATE
2026-04-04 07:50:01 +02:00
Rene Fichtmueller
1026787318
feat: add proxy network, image backfill, and scraper improvements
...
- Add TIP Proxy Network (packages/proxy-agent): SOCKS5 proxy agent
for residential IP bypass of CloudFront WAF blocks
- Add /api/proxy/* routes: node registration, heartbeat, load balancing
- Add image extraction to Flexoptix catalog scraper (GraphQL small_image)
- Add image extraction to Optcore scraper (Playwright gallery img)
- Fix Fluxlight price scraping (BigCommerce HTML structure: data-product-price-without-tax)
- Add SmartOptics scraper (8 DWDM/coherent products, og:image extraction)
- Fix findOrCreateScrapedTransceiver to update image_url for existing records
- Add image backfill script (backfill-images.ts): 178 Flexoptix images added
- Fix DB connection pool: max 5, idleTimeoutMillis 10s (was unlimited, caused >100 connections)
- Add proxy.ts utility for scraper proxy rotation
2026-04-03 21:13:03 +02:00
Rene Fichtmueller
abea0cd8fa
fix: remove orphaned floating text causing TypeScript build error in fo-blog-pipeline
...
Dead code leftover from STEP4_MASTER_DRAFT rewrite was sitting outside
any template literal, causing compilation failure. Removed duplicate
CONTEXT DATA RULES block and orphaned `{{OUTLINE}}`/`{{CONTEXT_DATA}}`
placeholders that were not wrapped in a string.
2026-04-03 00:51:59 +02:00
Rene Fichtmueller
3a61723126
fix(blog): complete pipeline rewrite — eliminate sections/bullets, fix DR4 wavelength, fix scope description
...
Core changes:
- HARD RULES rewritten: zero tolerance for ## headers, #### Scenario: patterns, bullet sections
- Gold article added as reference standard in STEP4_MASTER_DRAFT
- MANDATORY SECTIONS removed — replaced with continuous prose requirement
- STEP3_OUTLINE: now a flow plan (3-4 beats), not a section list
- STEP5_REALITY_INJECTION: no longer adds 'What Breaks' sections — injects into prose
- STEP9_QA_CHECK: format violations now primary HARD FAIL, above content checks
- DR4 wavelength fix: 1310nm = 0.35 dB/km (not 1550nm = 0.22 dB/km)
- Scope description fix: visual inspection tool ≠ loss measurement device
- Invented firmware version numbers now explicit HARD FAIL
2026-04-03 00:43:14 +02:00
Rene Fichtmueller
c7697308f6
feat: NOG conference talks scraper + hot topics integration
...
NOG Talks Scraper (packages/scraper/src/scrapers/nog-talks.ts):
- Crawls DENOG (15-17), NANOG (91-93), RIPE (87-89), ENOG, NLNOG, Euro-IX
- Relevance scoring: optical keywords (+3pts each), network keywords (+1pt)
Only talks scoring ≥2 stored, high-relevance (≥6) also to market_intelligence
- CtxEvent cross-DB bridge: when ctxmeet DB has ConferenceTalk rows,
pulls directly via dblink (same Postgres instance, no network hop)
- Runs weekly Monday 06:00 UTC (pg-boss schedule)
- Output: news_articles (source='NOG Talks: EVENT') + market_intelligence
Hot Topics (packages/api/src/routes/hot-topics.ts):
- SOURCE 3c: NOG talk clusters displayed as conference topics in hot list
Grouped by event (DENOG15, NANOG93...) with speaker + abstract preview
Filtered: source LIKE 'NOG Talks:%' AND relevance > 0.4 AND < 6 months
- Limit raised to 20 topics (was 15)
- Added nog_talks to sources metadata
Scheduler & Pi fleet:
- scrape:nog-talks queue registered in scheduler.ts + index-pi.ts
- Weekly cron: Monday 06:00 UTC (every Pi can handle it independently)
- First job triggered immediately
2026-04-02 22:38:00 +02:00
Rene Fichtmueller
c81b9f5375
feat: hot topics now uses market_intelligence + LLM queue reset
...
Hot Topics:
- SOURCE 3b: market_intelligence table (15 items, 0.6+ relevance)
with urgency mapping per intel_type + buy signal angles
- Fix news_articles: url → source_url (correct column name)
- Fix template literals: ${year} in string literals → backticks
- Increase limit: 6 → 15 topics returned
- Lower news cluster threshold: 2 → 1 article to form topic
- More research topics per day: 2 → 3
- More evergreen topics per day: 3 → 4
- Result: 24 total topics, 15 shown (was 8 total, 6 shown)
LLM Queue:
- Add resetOllamaQueue() export + auto-reset after 15min stall
- Add getQueueDepth() for monitoring
- New endpoints: GET /api/blog/llm/status, POST /api/blog/llm/reset-queue
2026-04-02 22:23:21 +02:00
Rene Fichtmueller
69ef507d8a
fix: scrapers status endpoint — correct table and column names
...
- Replace vendor_slug → vendors.slug via JOIN (column doesn't exist on transceivers)
- Replace competitor_prices + ebay_listings → price_observations (correct table)
- Replace market_intelligence → knowledge_base in status DB stats (table exists, 73 rows)
- price_observations columns: transceiver_id, price, time (not part_number/price_eur/scraped_at)
- Result: 5602 transceivers, 359 vendors, 465 switches, 4252 prices, 73 KB entries all visible
2026-04-02 15:24:18 +02:00
Rene Fichtmueller
e3b53343d4
fix: crawler intelligence hot topics now reads market_intelligence table
...
- Fix trend_score → relevance_score (correct column name in news_articles)
- Hot Topics source: market_intelligence table (not news_articles)
→ 15 items immediately available (capex_cycle, supply_chain, standards, etc.)
- KB summary: grouped by intel_type with count + top_relevance + latest date
- knowledge_base table ref → market_intelligence in /api/health status query
- Hot topics cards: intel_type badge + buy_signal_implication + score %
- Dashboard KB table: Intel Type / Items / Top Relevance / Latest columns
2026-04-02 15:22:09 +02:00
Rene Fichtmueller
a066300cf2
feat: password-protected login page + API auth middleware
...
- POST /api/auth/login: HMAC-SHA256 signed 7-day token, password from DASHBOARD_PASSWORD env
- GET /api/auth/verify: stateless token validation
- requireAuth middleware applied to all /api/* routes (except /api/health + /api/auth)
- /dashboard/login.html: dark TIP-themed login page with show/hide password toggle
- index.html: auth guard redirect to login + Authorization header on all api() calls
- No secrets in code — password stored in .env only
2026-04-02 07:31:15 +02:00
Rene Fichtmueller
4020ec77d9
feat: product intelligence layer — eBay enricher, community issues, datasheets+manuals API
...
- Migration 020: product_issues table, condition/marketplace on price_observations, features JSONB
- eBay enricher: switch features/description/refurb prices + transceiver condition pricing
- Community issues scraper: Reddit/ServeTheHome/Arista/Cisco community bug reports
- 7 pre-seeded issues (DCS-7800R3, SG350, QFX5120, CRS326, USW-Pro etc.)
- API: /switches/:id/issues + /switches/:id/documents endpoints
- Dashboard switch modal: features from DB, description, eBay refurb price, issues+docs async
- Datasheet finder for Arista/Cisco/Juniper/HPE vendor pages
- Scheduler: 4 new jobs (ebay enrichment nightly, community issues weekly)
2026-04-01 22:46:27 +02:00
Rene Fichtmueller
64074f988f
feat: SMB/campus switch seed 26 models (Cisco/HPE/Ubiquiti/MikroTik/Netgear/Zyxel) + fix forecast.ts fiveYearProjection accessor
2026-04-01 22:34:58 +02:00
Rene Fichtmueller
4b1734379a
fix: Finder 404 shows helpful message + fuzzy switch name matching
...
- api() helper now parses JSON body on non-2xx responses so error.suggestion
is available in catch blocks
- runFinder() catch shows 'Switch not found' + suggestion instead of 'Error: HTTP 404'
- finder.ts: normalized search (removes hyphens/spaces) + token-based fallback
so 'sg350-28' → 'SG350-28', 'N9K-C93180' → Nexus 93180, etc.
2026-04-01 22:17:07 +02:00
Rene Fichtmueller
dad4750a86
feat: Changelog — CHANGELOG_PENDING.md, /api/changelog route, Overview tab widget
...
- CHANGELOG_PENDING.md: 26 entries from v0.1.0 to today in JSON-line format
- GET /api/changelog: parses and serves entries as JSON array
- Overview tab: changelog card with type badges (FEAT/FIX/UI/DATA/AI/INFRA),
dates, show recent/all toggle
2026-04-01 22:14:14 +02:00
Rene Fichtmueller
681da54523
feat: Procurement Intelligence Engine (WS0c)
...
- Migration 019: stock_snapshots, abc_classification, reorder_signals,
product_lifecycle_events, market_intelligence, crawler_llm_log tables
- Seeded 7 market intel events (OFC 2026, AWS/Azure CapEx, Coherent lead times,
EU TED tenders, ECOC 2026, IEEE 802.3df)
- Seeded 4 lifecycle events (Cisco SFP-10G-LR EOL, Juniper EOL,
400ZR ratified, 800G MSA draft)
- Crawler LLM: core.ts (Ollama-based extractor), stock-schema.ts (typed schemas
+ vendor profiles for Flexoptix/FS.com/10Gtek/ATGBICS/ProLabs/Farnell/Mouser),
validator.ts (rule-based sanity checks + cross-validation)
- market-intelligence.ts scraper: OFC/ECOC, LightReading, IEEE 802.3, EU TED,
Farnell/Mouser lead times, FierceTelecom — weekly via pg-boss
- computeAbcClassification(): dynamic A/B/C classification from price obs +
compat count + vendor breadth
- computeReorderSignals(): buy_now/wait/hold/monitor with reasons + signal strength
- API: GET /api/procurement/overview|signals|signals/:id|abc|market-intel|
stock-trends/:id|lifecycle
- Dashboard: Procurement Intel tab with Reorder Signals, ABC table,
Market Intel cards, Lifecycle Events
2026-04-01 22:04:33 +02:00