diff --git a/.gitignore b/.gitignore index e7c162c..7eb4b25 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,10 @@ openclaw/workspace/.openclaw/ services/linkedin/chromadb/ services/linkedin/cookies/ services/linkedin/posts.json + +# Runtime ecosystem map — real container names/ports; this repo is PUBLIC. +# Bind-mounted from disk (see docker-compose.yml), so it never needs to be tracked. +# Track ironclaw/workspace/TOOLS.md.example instead. +ironclaw/workspace/TOOLS.md +# Internal build roadmap — absolute local paths into private repos, auth routes, token names +ironclaw/workspace/todo.md diff --git a/AGENTS.md b/AGENTS.md index 8eea08b..61b96ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,8 @@ clawbot/ │ ├── .mcp.json # MCP server connections (Drupal, Apify, etc.) │ ├── IDENTITY.md # Agent name, persona, avatar │ ├── SOUL.md # Agent values and behavioural guidelines -│ ├── TOOLS.md # Available tools and how to use them +│ ├── TOOLS.md # Available tools and how to use them — gitignored (real container +│ │ # names); copy from TOOLS.md.example │ ├── USER.md # Info about the user (fill in as needed) │ └── skills/ # Custom skill playbooks (SKILL.md per skill) ├── docker-compose.yml # Main Compose file diff --git a/ironclaw/workspace/TOOLS.md b/ironclaw/workspace/TOOLS.md.example similarity index 52% rename from ironclaw/workspace/TOOLS.md rename to ironclaw/workspace/TOOLS.md.example index 748d8c2..8e3d6b8 100644 --- a/ironclaw/workspace/TOOLS.md +++ b/ironclaw/workspace/TOOLS.md.example @@ -1,15 +1,22 @@ # TOOLS.md - Ecosystem Map +> **Template.** Copy to `TOOLS.md` and replace the placeholder container names and ports with +> your actual ones: `cp TOOLS.md.example TOOLS.md` +> +> `TOOLS.md` is gitignored on purpose — this repo is public, and the real ecosystem map is +> internal infrastructure detail. The workspace is bind-mounted, so the agent reads your local +> copy directly; a `docker compose restart ironclaw` picks up changes. + ## MCP Servers | Server | Container | Port | Tools | Use For | |---|---|---|---|---| -| **`gateway`** | mcpserver-gateway | 8100 | 29 | **Unified entry point — all tools via single auth** | -| **`site-files`** | clawbot-openclaw-1 (local) | — | ~10 | **Read/write Hugo source at `/site`** | -| `ragchat` | ragchat-nginx-1 | 8080 | 21 | CMS content, RAG knowledge base, Drupal | -| `seobot` | seo-tools-mcp | 3001 | 3 | Website SEO audits, crawl reports | -| `osintbot` | osintbot-mcp | 8200 | — | LinkedIn (Apify)/OSINT research, prospect intel | -| `sales` | sales-assistant-assistant-1 | 8000 | 3 | Product Q&A, knowledge base search | +| **`gateway`** | mcp-gateway | 8100 | 29 | **Unified entry point — all tools via single auth** | +| **`site-files`** | (local) | — | ~10 | **Read/write Hugo source at `/site`** | +| `ragchat` | ragchat | 8080 | 21 | CMS content, RAG knowledge base, Drupal | +| `seobot` | seo-mcp | 3001 | 3 | Website SEO audits, crawl reports | +| `osintbot` | osint-mcp | 8200 | — | LinkedIn (Apify)/OSINT research, prospect intel | +| `sales` | sales-api | 8000 | 3 | Product Q&A, knowledge base search | ### Gateway tool naming @@ -36,15 +43,15 @@ Tools routed through the gateway use `{server}__{tool}` format, e.g.: ## Docker Network -All containers on `nginx-proxy` bridge network. -OpenClaw container: `clawbot-openclaw-1`. +All containers on a shared bridge network (see `docker-compose.yml`). +OpenClaw container: ``. Reach services by container name over the network. ## Key URLs - OpenClaw dashboard: http://127.0.0.1:18790 -- **MCP Gateway: http://mcpserver-gateway:8100** (internal) / http://127.0.0.1:8100 (host) -- RagChat: http://ragchat-nginx-1:8080 -- Sales assistant: http://sales-assistant-assistant-1:8000 -- SEO crawler MCP: http://seo-tools-mcp:3001 -- OSINT MCP: http://osintbot-mcp:8200 +- **MCP Gateway: http://mcp-gateway:8100** (internal) / http://127.0.0.1:8100 (host) +- RagChat: http://ragchat:8080 +- Sales assistant: http://sales-api:8000 +- SEO crawler MCP: http://seo-mcp:3001 +- OSINT MCP: http://osint-mcp:8200 diff --git a/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md new file mode 100644 index 0000000..1297a68 --- /dev/null +++ b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md @@ -0,0 +1,161 @@ +--- +name: redmine-time-tracking +description: >- + Log the time an agent spends on a project to the self-hosted Redmine (CRM). + Resolve the working directory to a Redmine project, find or create an issue for + the task, then file a time entry under the "AI Agent" activity. Run automatically + after ~15+ minutes of substantive work on a project, or when asked to "log my + time" / "track this work" / "record hours in Redmine". +--- + +# Redmine Time Tracking + +Log agent work time to Redmine. Data model: **Project → Issue → Time entry**. + +> **ClawBot copy.** The canonical version lives in Luděk's monorepo at +> `ai/skills/redmine-time-tracking/SKILL.md`; keep the two in sync when the procedure +> changes. This copy is deliberately narrower — `clawbot` is a public repository, so the +> stack's endpoints and the admin procedure stay in the canonical copy only. You don't need +> them: you reach Redmine through the gateway's `redmine__*` tools. + +## Environment + +- **Tools:** `redmine__` through the MCP gateway. +- **Session start time:** the `WORK SESSION` line you appended to `MEMORY.md`. +- **Clock:** `date -u +%Y-%m-%dT%H:%M:%SZ`; if you have no shell, estimate conservatively + from the conversation. + +## When to run + +- **Automatically**, after roughly 15 minutes or more of real effort on one project + (research, coding, audits, multi-step tasks). This is internal bookkeeping on Luděk's + own infrastructure — you do **not** need to ask permission first. +- On explicit request ("log my time", "track this"). +- On an end-of-day flush: process every open work session that hasn't been logged. + +Skip trivial work (a one-line fix, a quick question) — it's not worth an issue. + +## Procedure + +### 1. Resolve the Redmine project + +Map the working directory to a Redmine project **identifier**: + +| Subproject / area | Redmine identifier | +|---|---| +| `customers/plymovent`, `plymovent` | `plymovent` | +| `ragchat` | `ragchat` | +| `osintbot` | `osintbot` | +| `sales-assistant` | `sales-assistant` | +| `mcpserver`, mcpserver.cz site | `mcpserver-cz` | +| `seo-tools` | `seo-tools` | +| `geo` | `geo` | +| `startupfactory` | `startupfactory` | +| `drupal-lama` | `drupal-lama` | +| `ludekkvapil` / `/site` (ludekkvapil.cz) | `ludekkvapil-cz` | +| `clawbot` — you, your workspace and your skills | `clawbot` | +| `customers/drupal-cz`, `drupalcz` (drupal.cz) | `drupal-cz` | +| `ai`, `redmine`, `docs`, `scripts`, `ops-proxy`, `observability`, `reception`, infra, monorepo root | `lkv` | + +Call `list_redmine_projects` and resolve the mapped identifier to its **numeric `id`** — +`create_redmine_issue` and the time-entry tools want the integer project id, not the +identifier string. If you can't tell which project the work belongs to, **ask** — don't +guess, don't invent a project. + +### 2. Compute hours + +`elapsed = now − session start`, in hours, **rounded to the nearest 0.25, minimum 0.25**. +Count actual working time, not wall-clock time you were idle. + +### 3. Resolve the "AI Agent" activity id + +Call `list_time_entry_activities`, find the entry named **"AI Agent"**, keep its `id`. +If it's missing, the one-time host-side setup hasn't been run — tell Luděk and stop. + +### 4. Find or create the issue (one issue per task) + +- Call `search_redmine_issues` (`query` = a few words from the task) or `list_redmine_issues` + scoped to the project for an **open** issue that already matches this task (you may be + resuming work). If found, use its `id`. +- Otherwise `create_redmine_issue`: + - `project_id`: the **numeric id** from step 1 + - `subject`: a short, specific task title (e.g. *"Fix CLS on blog list pages"*, not *"work"*) + - `description`: 1–3 sentences — what and why + - `fields`: an object with `tracker_id` (from `list_project_trackers` for this project — + use the one that matches the project name), `status_id` for **"In progress"** (look it + up with `list_redmine_issue_statuses`), and `assigned_to_id` = your own user id + (`get_current_user` → `mcp-agent`). + +`status_id` and `parent_issue_id` are usually **dropped silently** — see +[Permission limits of `mcp-agent`](#permission-limits-of-mcp-agent). The issue is still +created; it just lands in the project's default status and unparented. Don't retry, and +don't treat it as a failure of the whole step. + +### 5. File the time entry + +`manage_time_entry`: + - `action`: `"create"` + - `issue_id`: from step 4 + - `hours`: from step 2 + - `activity_id`: the "AI Agent" id from step 3 + - `spent_on`: today (`YYYY-MM-DD`) + - `comments`: one factual sentence on what you did, suffixed with your agent tag — + `… (ClawBot)`. Max 255 chars, no filler. + - Do **not** pass `user_id` — it defaults to the current MCP user (`mcp-agent`). + +### 6. Close the task if it's done + +If the task is complete and produced its deliverable, `update_redmine_issue` to set the +status to **"Done"** and `done_ratio` 100 (pass these in `fields`). Leave it "In progress" +if there's follow-up work. + +Then **read the issue back** and check the status actually changed — the update reports +success even when the status was ignored. If it didn't land, say so in your reply and leave +the status alone; don't loop on it. + +### 7. Record that it's logged + +Change that `WORK SESSION` line's trailing state in `MEMORY.md` to `logged `. + +## Verify + +`list_time_entries` scoped to the project + today — confirm the entry shows user +`mcp-agent`, activity `AI Agent`, and the agent-tag comment suffix. + +Human-readable check, for Luděk: the project's **Time entries** tab, and its **Report** +grouped by Activity — "AI Agent" hours are a separate line from human "Development" / +"Design" hours. + +## Permission limits of `mcp-agent` + +The `mcp-agent` account holds the role **"Agent"**, which can create and edit issues and +file time entries, but **not** manage subtasks or move issues through the workflow. Redmine +does not reject the disallowed attributes — it drops them and still answers `200`/`204`, so +a write can look like it succeeded while nothing changed. Confirmed on 2026-09-12 for: + +| Attribute | What happens | +|---|---| +| `status_id` (on create and update) | ignored; issue keeps the project's default status | +| `parent_issue_id` | ignored; the issue stays top-level | + +What works: `subject`, `description`, `tracker_id`, `assigned_to_id`, `done_ratio`, notes, +and all time-entry writes. + +So: **after any status or parent write, read the issue back**. If it didn't take, report it +in your reply rather than retrying or escalating on your own. + +Anything the Agent role can't do — nesting subtasks, forcing a status, re-running the +setup — needs admin access you don't have. Say what you couldn't do and stop; the host-side +copy of this playbook carries the admin procedure. Lifting the limitation permanently is +Luděk's call: Administration → Roles → Agent → *Manage subtasks*, plus the workflow +transitions for the Agent role per tracker. + +## Notes + +- Never log to a project that isn't in the map above. +- If issue creation fails with a tracker or permission error, the setup script needs to be + (re-)run for that project — report it, don't retry blindly. +- The MCP server authenticates as `mcp-agent` (legacy mode) — you send no credentials. +- The server wraps free-text fields in returned JSON in `` markers as a + prompt-injection guard. That's a display wrapper on reads only — the stored value is + clean; don't include those markers when you write text back. diff --git a/ironclaw/workspace/todo.md b/ironclaw/workspace/todo.md deleted file mode 100644 index 3254518..0000000 --- a/ironclaw/workspace/todo.md +++ /dev/null @@ -1,164 +0,0 @@ -# Agentic Ops & lkv.cz — Master Project TODO List - -This document acts as the consolidated tracking system for the **Agentic Ops** roadmap, microsite ideas, and business planning decisions. It highlights what is **Done** (implemented in code) and what **Has to be Done** (configuration, runtime setup, and GTM steps). - ---- - -## 📊 High-Level Status Dashboard - -| Phase | Component / Goal | Status | Key Code References | -|---|---|---|---| -| **Phase 0** | Nginx Reverse Proxy & Network | ⚠️ **Partial** (Stopped) | [ops-proxy/docker-compose.yml](file:///home/siva01/projects/lkv/ops-proxy/docker-compose.yml) | -| **Phase 1** | Specialist MCP Servers (ragchat, seo, osint, sales) | **Done** | [mcp-server.ts](file:///home/siva01/projects/lkv/seo-tools/src/mcp-server.ts), [osintbot_mcp.py](file:///home/siva01/projects/lkv/osintbot/scripts/osintbot_mcp.py), [mcp.py](file:///home/siva01/projects/lkv/sales-assistant/src/assistant/api/routes/mcp.py) | -| **Phase 2** | MCP Gateway Aggregator | **Done** | [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py) | -| **Phase 3** | OpenClaw Integration | ⚠️ **Partial** | [openclaw/.mcp.json](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/.mcp.json) | -| **Phase 4** | Playbook Skills Definition | **Done** | [weekly-seo-audit](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/weekly-seo-audit/SKILL.md), [competitor-monitoring](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/competitor-monitoring/SKILL.md) | -| **Phase 5** | Agent Identity & Guidelines | **Done** | [IDENTITY.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/IDENTITY.md), [SOUL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/SOUL.md) | -| **Phase 6** | Observability & Alerting | ⚠️ **Partial** | [gateway/main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py), [ops-proxy/conf.d](file:///home/siva01/projects/lkv/ops-proxy/conf.d) | -| **Phase 7** | Marketing Dogfood & Microsites | 📅 **Todo** | Shared Hugo design | -| **Phase 8** | GTM Business Plan & Monetization | 📅 **Todo** | Payments, auth, cost optimization | - ---- - -## 🛠️ Detailed Task Checklist - -### PHASE 0 — nginx-proxy: shared reverse-proxy and network -> **Goal:** Run the central gateway routing all traffic to the containerized backends. - -- [ ] **0.1 — Start nginx-proxy (ops-proxy)** - - [ ] Confirm the external network exists; create it if not: - ```bash - docker network inspect agentic-ops >/dev/null 2>&1 || docker network create -d bridge agentic-ops - ``` - - [ ] Copy env template and configure in `ops-proxy`: - ```bash - cd ops-proxy && cp .env.dist .env - # Set VIRTUAL_HOST and DEFAULT_EMAIL in .env - ``` - - [ ] Start the proxy stack: `docker compose up -d` - - [ ] Verify: `docker ps` shows `ops-proxy` and `ops-acme` containers running. -- [x] **0.2 — Register each service as a virtual host** - - [x] Map `ragchat` to `ragchat.local` (Port 8080) -> [ragchat/docker-compose.yml](file:///home/siva01/projects/lkv/ragchat/docker-compose.yml) - - [x] Map `sales-assistant` to `dev.ludekkvapil.cz` (Port 8000) -> [sales-assistant/docker-compose.yml](file:///home/siva01/projects/lkv/sales-assistant/docker-compose.yml) - - [x] Map `mcpserver` static site to `mcpserver.local` (Port 80) -> [mcpserver/docker-compose.yml](file:///home/siva01/projects/lkv/mcpserver/docker-compose.yml) - - [x] Map `mcpserver-gateway` to `mcp-gateway.local` (Port 8100) -> [mcpserver/docker-compose.yml](file:///home/siva01/projects/lkv/mcpserver/docker-compose.yml) - - [ ] Map `seo-tools` MCP to `seo.local` (Port 3001) -> **Todo:** Add `VIRTUAL_HOST` variables to [seo-tools/docker-compose.yml](file:///home/siva01/projects/lkv/seo-tools/docker-compose.yml) - - [ ] Map `osintbot` MCP (`osintbot-mcp`, Port 8200) — internal only, reached via mcpserver gateway; no public VIRTUAL_HOST needed -> [osintbot/docker-compose.yml](file:///home/siva01/projects/lkv/osintbot/docker-compose.yml) - - [ ] Map `clawbot` OpenClaw dashboard to `clawbot.local` (Port 18789) -> **Todo:** Add `VIRTUAL_HOST` variables to [clawbot/docker-compose.yml](file:///home/siva01/projects/lkv/clawbot/docker-compose.yml) -- [ ] **0.3 — Local /etc/hosts entries (dev environment)** - - [ ] Add domain mapping in host `/etc/hosts`: - ``` - 127.0.0.1 ragchat.local dev.ludekkvapil.cz mcpserver.local mcp-gateway.local seo.local osint.local clawbot.local - ``` -- [ ] **0.4 — Shared secrets file** - - [ ] Create `.env.shared` at workspace root with all MCP tokens (e.g. `RAGCHAT_MCP_TOKEN`, `SEO_MCP_TOKEN`, `OSINT_MCP_TOKEN`, `SALES_MCP_TOKEN`, `MCP_GATEWAY_TOKEN`). - - [ ] Reference `.env.shared` from each project's docker-compose file via `env_file`. - ---- - -### PHASE 1 — Expose each specialist as an MCP server -> **Goal:** Expose JSON-RPC 2.0 endpoints for all specialty applications to communicate with the orchestrator. - -- [x] **1.1 — ragchat: register existing MCP endpoint** - - Exposes route `/mcp/post` via custom Drupal module `mcp_tools` requiring token authentication. -- [x] **1.2 — seo-tools: add MCP HTTP wrapper** - - Implemented in [mcp-server.ts](file:///home/siva01/projects/lkv/seo-tools/src/mcp-server.ts). Exposes `crawl`, `get_report`, and `list_reports`. -- [x] **1.3 — OSINT: add MCP HTTP endpoint** - - Implemented in [osintbot_mcp.py](file:///home/siva01/projects/lkv/osintbot/scripts/osintbot_mcp.py) (fastmcp tools), exposed over HTTP via `osintbot_mcp_http.py` shim. Exposes `osintbot_investigate`, `osintbot_plan`, `osintbot_wiki_lookup`, `osintbot_status`, etc. -- [x] **1.4 — sales-assistant: expose chat and product tools** - - Implemented in [mcp.py](file:///home/siva01/projects/lkv/sales-assistant/src/assistant/api/routes/mcp.py). Exposes `chat`, `search_knowledge`, and `get_status`. - ---- - -### PHASE 2 — MCPserver as unified MCP gateway -> **Goal:** Aggregate all four specialist MCP servers under a single namespaced gateway. - -- [x] **2.1 — Build FastAPI aggregator service** - - Implemented in [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py). It merges tool manifests and namespaces tools as `{server}__{tool}` (e.g., `seobot__crawl`, `ragchat__ProductSearch`). -- [x] **2.2 — Rate limiting and proxy buffering** - - Configured in [vmweb-limits.conf](file:///home/siva01/projects/lkv/ops-proxy/conf.d/vmweb-limits.conf) to support large JSON-RPC packages. -- [ ] **2.3 — Test gateway aggregation** - - Run `curl -H "Authorization: Bearer $MCP_GATEWAY_TOKEN" http://mcp-gateway.local/mcp/post` with `tools/list` request and confirm namespaced tools are fetched and merged correctly. - ---- - -### PHASE 3 — Wire OpenClaw (clawbot) to all specialists -> **Goal:** Configure ClawBot to utilize the unified gateway or connect to all backends. - -- [ ] **3.1 — Update ClawBot MCP configurations** - - Edit [openclaw/.mcp.json](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/.mcp.json) to reference the aggregated gateway URL `http://mcp-gateway.local/mcp/post` (or point directly to all 4 backends). -- [ ] **3.2 — Restart OpenClaw & verify tools** - - Restart the service: `docker compose restart openclaw` - - Verify that the namespaced tools appear on the OpenClaw dashboard. -- [ ] **3.3 — Update tools documentation** - - Update [TOOLS.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/TOOLS.md) with details on namespaced tools. - ---- - -### PHASE 4 — Define agent workflows / playbooks -> **Goal:** Implement agent execution instructions (skills) in markdown files. - -- [x] **4.1 — Implement `weekly-seo-audit` playbook** - - Created at [weekly-seo-audit/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/weekly-seo-audit/SKILL.md). -- [x] **4.2 — Implement `competitor-monitoring` playbook** - - Created at [competitor-monitoring/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/competitor-monitoring/SKILL.md). -- [x] **4.3 — Implement `support-escalation-review` playbook** - - Created at [support-escalation-review/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/support-escalation-review/SKILL.md). -- [x] **4.4 — Implement `new-client-onboarding` playbook** - - Created at [new-client-onboarding/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/new-client-onboarding/SKILL.md). - ---- - -### PHASE 5 — Identity, persona, and memory -> **Goal:** Setup agent profile, values, system memory, and heartbeat checklist. - -- [x] **5.1 — Configure Identity & Persona** - - Configured at [IDENTITY.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/IDENTITY.md) (Name: ClawBot, Role: Agentic Ops Manager). -- [x] **5.2 — Configure Soul & Boundaries** - - Configured at [SOUL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/SOUL.md). -- [x] **5.3 — Configure User memory & Bootstrap sequences** - - Configured at [USER.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/USER.md) and [HEARTBEAT.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/HEARTBEAT.md). - ---- - -### PHASE 6 — Monitoring and observability -> **Goal:** Instrument request logging and alerting across the agentic services. - -- [x] **6.1 — Structured logging** - - Gateway [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py) writes JSON logs indicating tool, duration, and response codes. -- [ ] **6.2 — Dashboard and Centralized Log Storage** - - Store request metrics under `mcpserver/logs/` and configure log routing. -- [ ] **6.3 — Setup crawl fail alerts** - - Set up warning notifications if a crawl job fails repeatedly. - ---- - -### PHASE 7 — Marketing dogfood loop & microsites -> **Goal:** Configure ClawBot to audit the local site and deploy client-facing microsites. - -- [ ] **7.1 — Weekly audit against `ludekkvapil.cz`** - - Trigger weekly cron skill to audit the owner's Hugo static website. -- [ ] **7.2 — Build Microsites using Hugo (Shared Theme / Unified Design)** - - [ ] **Microsite `seo.ludekkvapil.cz`**: Form/interface for generating SEO reports leveraging `seo-tools`. - - [ ] **Microsite `courses.ludekkvapil.cz`**: Course registry, inspired by [naucmese.cz/ludek-kvapil](https://www.naucmese.cz/ludek-kvapil). - - [ ] **Microsite `osint.ludekkvapil.cz`**: OSINT intelligence interface leveraging `osintbot`. -- [ ] **7.3 — Competitor Feed draft posts** - - Run daily OSINT checks on competitor moves and write draft recommendations into `ludekkvapil/content/` for human review. - ---- - -### PHASE 8 — First paying client (GTM Business Plan) -> **Goal:** Turn the unified agent crew into a commercial SaaS offer. - -- [ ] **8.1 — Domain & SSL Configuration** - - Setup DNS record and nginx configuration to resolve `seo.ludekkvapil.cz` to the frontend dashboard. -- [ ] **8.2 — Authentication & Authorization** - - Secure the client portal and API endpoints with user authentication. -- [ ] **8.3 — Pricing & Tier Structure** - - Define product pricing: **Per report** vs **Subscription-based**. - - Configure credit allotment, trial limits, and packages (e.g. Free trial limit, monthly subscription quota). -- [ ] **8.4 — Cost Analysis and Optimization** - - [ ] Calculate cost per report (single vs multiple languages, site crawl depth/page size). - - [ ] Optimize LLM token usage: determine optimal model fit (e.g., GPT-4o vs Claude 3.5 Sonnet vs open source models like Llama 3 running locally on Ollama). -- [ ] **8.5 — Payment Integration** - - Connect client signups to Stripe / billing gateway.