{
  "schemaVersion": "1.0",
  "item": {
    "slug": "veille",
    "name": "Openclaw Skill Veille",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/Romain-Grosos/veille",
    "canonicalUrl": "https://clawhub.ai/Romain-Grosos/veille",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/veille",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=veille",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md",
      "config.example.json",
      "cron.example.json",
      "references/cron_prompt.md",
      "references/troubleshooting.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/veille"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/veille",
    "agentPageUrl": "https://openagent3.xyz/skills/veille/agent",
    "manifestUrl": "https://openagent3.xyz/skills/veille/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/veille/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Skill Veille - RSS Aggregator",
        "body": "RSS feed aggregator with URL deduplication and topic-based deduplication for OpenClaw agents.\nFetches articles from 20+ configured sources, filters already-seen URLs (TTL 14 days),\nand deduplicates articles covering the same story using Jaccard similarity + named entities.\n\nNo external dependencies: stdlib Python only (urllib, xml.etree, email.utils)."
      },
      {
        "title": "Trigger phrases",
        "body": "\"fais une veille\"\n\"quoi de neuf en securite / tech / crypto / IA ?\"\n\"donne-moi les news du jour\"\n\"articles recents sur [sujet]\"\n\"veille RSS\"\n\"digest du matin\"\n\"nouvelles non vues\""
      },
      {
        "title": "Quick Start",
        "body": "# 1. Setup\npython3 scripts/setup.py\n\n# 2. Validate\npython3 scripts/init.py\n\n# 3. Fetch + Score + Send (full pipeline)\npython3 scripts/veille.py fetch --filter-seen --filter-topic \\\n  | python3 scripts/veille.py score \\\n  | python3 scripts/veille.py send"
      },
      {
        "title": "Requirements",
        "body": "Python 3.9+\nNetwork access to RSS feeds (public, no auth required)\nNo pip installs needed"
      },
      {
        "title": "Installation",
        "body": "# From the skill directory\npython3 scripts/setup.py\n\n# Validate\npython3 scripts/init.py\n\nThe wizard creates:\n\n~/.openclaw/config/veille/config.json (from config.example.json)\n~/.openclaw/data/veille/ (data directory)"
      },
      {
        "title": "Customizing sources",
        "body": "Edit ~/.openclaw/config/veille/config.json and add/remove entries in the \"sources\" dict:\n\n{\n  \"sources\": {\n    \"My Blog\": \"https://example.com/feed.xml\",\n    \"BleepingComputer\": \"https://www.bleepingcomputer.com/feed/\"\n  }\n}"
      },
      {
        "title": "Files written by this skill",
        "body": "PathWritten byPurposeContains secrets~/.openclaw/config/veille/config.jsonsetup.pySources, outputs, optionsNO~/.openclaw/data/veille/seen_urls.jsonveille.pyURL dedup store (TTL 14d)NO~/.openclaw/data/veille/topic_seen.jsonveille.pyTopic dedup store (TTL 5d)NO"
      },
      {
        "title": "Files read from outside the skill",
        "body": "PathRead byKey accessedWhen~/.openclaw/openclaw.jsondispatch.pychannels.telegram.botToken (read-only)Only when telegram_bot output is enabled and no bot_token is set in the output config\n\nThis is the only cross-config read. To avoid it entirely, set bot_token explicitly in your output config:\n\n{ \"type\": \"telegram_bot\", \"bot_token\": \"YOUR_BOT_TOKEN\", \"chat_id\": \"...\", \"enabled\": true }"
      },
      {
        "title": "Output credentials (optional)",
        "body": "Credentials are only used if you enable the corresponding output. None are required for core functionality (RSS fetch + dedup).\n\nOutputCredential sourceWhat is usedtelegram_bot~/.openclaw/openclaw.json or bot_token in output configBot token (read-only)mail-clientDelegated to mail-client skill (its own creds)Nothing read directlymail-client (SMTP fallback)smtp_user / smtp_pass in output configSMTP loginnextcloudDelegated to nextcloud-files skill (its own creds)Nothing read directly"
      },
      {
        "title": "Cleanup on uninstall",
        "body": "python3 scripts/setup.py --cleanup"
      },
      {
        "title": "Credential isolation",
        "body": "API keys are read from dedicated files (default ~/.openclaw/secrets/), never from config.json. The scorer warns at runtime if a key file has overly permissive filesystem permissions.\nSMTP credentials (fallback only) are stored in the output config block — use the mail-client skill delegation to avoid storing SMTP passwords."
      },
      {
        "title": "Subprocess boundaries",
        "body": "Dispatch delegates to other OpenClaw skills via subprocess.run() (never shell=True). Script paths are validated to reside under ~/.openclaw/workspace/skills/ before execution, preventing path traversal.\nNo credentials are passed as subprocess arguments — each skill manages its own authentication."
      },
      {
        "title": "File output safety",
        "body": "The file output type validates the target path before writing: only ~/.openclaw/ is allowed by default. Additional directories can be whitelisted via config.security.allowed_output_dirs. Sensitive paths (.ssh, .gnupg, /etc/, .bashrc, etc.) are always blocked regardless of allowlist.\nWritten content is checked for suspicious patterns (shell shebangs, SSH keys, PGP blocks, code injection) and size-limited to 1 MB."
      },
      {
        "title": "Cross-config reads",
        "body": "The only cross-config file read is ~/.openclaw/openclaw.json for the Telegram bot token, and only when telegram_bot output is enabled without an explicit bot_token. This read is logged to stderr. Set bot_token in the output config to eliminate this read entirely."
      },
      {
        "title": "Autonomous dispatch",
        "body": "When scheduled (cron), the skill can send messages/files to configured outputs without user interaction. All dispatch actions are logged to stderr with an audit summary. Use enabled: false on any output to disable it without removing its config."
      },
      {
        "title": "fetch",
        "body": "python3 veille.py fetch [--hours N] [--filter-seen] [--filter-topic] [--sources FILE]\n\nOptions:\n\n--hours N : lookback window in hours (default: from config, usually 24)\n--filter-seen : filter already-seen URLs (uses seen_urls.json TTL store)\n--filter-topic : deduplicate by topic (uses topic_seen.json + Jaccard similarity)\n--sources FILE : path to custom JSON sources file\n\nOutput (JSON on stdout):\n\n{\n  \"hours\": 24,\n  \"count\": 42,\n  \"skipped_url\": 5,\n  \"skipped_topic\": 3,\n  \"articles\": [...],\n  \"wrapped_listing\": \"=== UNTRUSTED EXTERNAL CONTENT ...\"\n}"
      },
      {
        "title": "seen-stats",
        "body": "python3 veille.py seen-stats\n\nShows URL seen store statistics (count, TTL, file path)."
      },
      {
        "title": "topic-stats",
        "body": "python3 veille.py topic-stats\n\nShows topic deduplication store statistics."
      },
      {
        "title": "mark-seen",
        "body": "python3 veille.py mark-seen URL [URL ...]\n\nMarks one or more URLs as already seen (prevents them from appearing in future fetches with --filter-seen)."
      },
      {
        "title": "score",
        "body": "python3 veille.py score [--dry-run]\n\nReads a digest JSON from stdin (output of fetch) and scores articles using an OpenAI-compatible LLM.\nReturns enriched JSON with scored, ghost_picks, and per-article score/reason fields.\n\nOptions:\n\n--dry-run : print summary on stderr without calling the LLM API\n\nWhen llm.enabled is false (default), articles pass through unchanged (\"scored\": false).\n\nPipeline usage:\n\npython3 veille.py fetch --filter-seen --filter-topic | python3 veille.py score | python3 veille.py send"
      },
      {
        "title": "send",
        "body": "python3 veille.py send [--profile NAME]\n\nReads a digest JSON from stdin and dispatches to all enabled outputs configured in config.json.\nAccepts both raw fetch output (articles key) and LLM-processed digests (categories key).\n\nOutput types: telegram_bot, mail-client, nextcloud, file.\n\ntelegram_bot: bot token auto-read from OpenClaw config - no extra setup if Telegram already configured.\nmail-client: delegates to mail-client skill if installed, falls back to raw SMTP config.\nnextcloud: delegates to nextcloud-files skill if installed (append mode by default with date separator).\nfile: writes digest to a local file. Path must be under ~/.openclaw/ (default) or a directory listed in config.security.allowed_output_dirs. Sensitive paths and suspicious content are blocked (see Security model).\n\nConfigure outputs interactively:\n\npython3 scripts/setup.py --manage-outputs"
      },
      {
        "title": "config",
        "body": "python3 veille.py config\n\nPrints the active configuration (no secrets)."
      },
      {
        "title": "LLM scoring configuration",
        "body": "The llm key in config.json controls the optional LLM-based article scoring:\n\n{\n  \"llm\": {\n    \"enabled\": false,\n    \"base_url\": \"https://api.openai.com/v1\",\n    \"api_key_file\": \"~/.openclaw/secrets/openai_api_key\",\n    \"model\": \"gpt-4o-mini\",\n    \"top_n\": 10,\n    \"ghost_threshold\": 5\n  }\n}\n\nKeyDefaultDescriptionenabledfalseEnable LLM scoring (requires API key)base_urlhttps://api.openai.com/v1OpenAI-compatible API endpointapi_key_file~/.openclaw/secrets/openai_api_keyPath to file containing the API keymodelgpt-4o-miniModel to use for scoringtop_n10Max articles to send to LLM per batchghost_threshold5Score threshold for ghost_picks (blog-worthy articles)\n\nScoring rules:\n\nOnly the first top_n articles are sent to the LLM. Articles beyond top_n\nare excluded from the digest entirely. fetch returns articles sorted by date\ndesc, so top_n selects the most recent ones. Increase top_n to evaluate\nmore articles per run (higher token cost).\nScore >= ghost_threshold : added to ghost_picks list\nScore >= 3 : kept in articles list\nScore <= 2 : excluded from output\nArticles are sorted by score (descending)\n\nWhen disabled, the score subcommand passes data through unchanged."
      },
      {
        "title": "Nextcloud output mode",
        "body": "The nextcloud output now defaults to append mode with a date separator. Each dispatch adds content below a ## YYYY-MM-DD HH:MM header, preserving previous entries.\n\nSet \"mode\": \"overwrite\" in the output config to restore the old behavior:\n\n{ \"type\": \"nextcloud\", \"path\": \"/Veille/digest.md\", \"mode\": \"overwrite\" }"
      },
      {
        "title": "File output configuration",
        "body": "The file output writes digests to the local filesystem. By default, only paths under ~/.openclaw/ are allowed. To authorize additional directories, use config.security.allowed_output_dirs:\n\n{\n  \"security\": {\n    \"allowed_output_dirs\": [\n      \"~/Documents/veille\",\n      \"/srv/digests\"\n    ]\n  }\n}\n\nBlocked paths (always rejected, even if inside an allowed directory):\n.ssh, .gnupg, .config/systemd, crontab, /etc/, .bashrc, .profile, .bash_profile, .zshrc, .env\n\nContent validation — written content is rejected if it:\n\nExceeds 1 MB\nContains shell shebangs (#!/), SSH keys, PGP blocks, or code injection patterns (eval(, exec(, __import__(, import os, import subprocess)\n\nAll blocked attempts are logged to stderr with the reason."
      },
      {
        "title": "Basic digest",
        "body": "# In agent tool call:\nresult = exec(\"python3 scripts/veille.py fetch --hours 24 --filter-seen --filter-topic\")\ndata = json.loads(result.stdout)\n# data[\"wrapped_listing\"] is ready for LLM prompt injection\n# data[\"count\"] = number of new articles\n# data[\"articles\"] = list of article dicts"
      },
      {
        "title": "Prompt template",
        "body": "You are a news analyst. Here are today's articles:\n\n{data[\"wrapped_listing\"]}\n\nPlease summarize the 5 most important stories, focusing on security and tech."
      },
      {
        "title": "Agent workflow example",
        "body": "1. Call veille fetch --filter-seen --filter-topic\n2. Pipe through veille score (LLM scoring, if enabled)\n3. If count > 0: pass wrapped_listing to LLM for analysis\n4. LLM produces digest summary\n5. Pipe through veille send (dispatches to configured outputs)"
      },
      {
        "title": "Pipeline (CLI)",
        "body": "python3 scripts/veille.py fetch --filter-seen --filter-topic \\\n  | python3 scripts/veille.py score \\\n  | python3 scripts/veille.py send"
      },
      {
        "title": "Filtering by keyword (post-fetch)",
        "body": "data = json.loads(fetch_output)\nsecurity_articles = [\n    a for a in data[\"articles\"]\n    if any(kw in a[\"title\"].lower() for kw in [\"cve\", \"vuln\", \"patch\", \"breach\"])\n]"
      },
      {
        "title": "Ideas",
        "body": "Add keyword-based filtering (--keywords security,cve,linux)\nAdd per-source TTL override in config\nExport digest as HTML or Markdown\nSchedule with cron: 0 8 * * * python3 veille.py fetch --filter-seen --filter-topic\nWeight articles by source tier for LLM prioritization\nAdd OPML import/export for source list management\nIntegrate with ntfy or Telegram for real-time alerts on high-priority articles"
      },
      {
        "title": "Combine with",
        "body": "mail-client : send the digest by email after fetching\nveille fetch --filter-seen | ... | mail-client send\n\n\n\nnextcloud-files : archive the daily digest as a Markdown file\nveille fetch --filter-seen | jq .wrapped_listing -r > /tmp/digest.md\nnextcloud-files upload /tmp/digest.md /Digests/$(date +%Y-%m-%d).md"
      },
      {
        "title": "Troubleshooting",
        "body": "See references/troubleshooting.md for detailed troubleshooting steps.\n\nCommon issues:\n\nNo articles returned: check --hours value, verify feed URLs in config\nXML parse error on a feed: some feeds use non-standard XML; the skill skips broken items silently\nAll articles filtered as seen: run seen-stats to check store size; reset with rm seen_urls.json\nImport error: ensure you run veille.py from its directory or via full path\nFile output blocked: path is outside ~/.openclaw/ — add the target directory to config.security.allowed_output_dirs (see File output configuration)"
      }
    ],
    "body": "Skill Veille - RSS Aggregator\n\nRSS feed aggregator with URL deduplication and topic-based deduplication for OpenClaw agents. Fetches articles from 20+ configured sources, filters already-seen URLs (TTL 14 days), and deduplicates articles covering the same story using Jaccard similarity + named entities.\n\nNo external dependencies: stdlib Python only (urllib, xml.etree, email.utils).\n\nTrigger phrases\n\"fais une veille\"\n\"quoi de neuf en securite / tech / crypto / IA ?\"\n\"donne-moi les news du jour\"\n\"articles recents sur [sujet]\"\n\"veille RSS\"\n\"digest du matin\"\n\"nouvelles non vues\"\nQuick Start\n# 1. Setup\npython3 scripts/setup.py\n\n# 2. Validate\npython3 scripts/init.py\n\n# 3. Fetch + Score + Send (full pipeline)\npython3 scripts/veille.py fetch --filter-seen --filter-topic \\\n  | python3 scripts/veille.py score \\\n  | python3 scripts/veille.py send\n\nSetup\nRequirements\nPython 3.9+\nNetwork access to RSS feeds (public, no auth required)\nNo pip installs needed\nInstallation\n# From the skill directory\npython3 scripts/setup.py\n\n# Validate\npython3 scripts/init.py\n\n\nThe wizard creates:\n\n~/.openclaw/config/veille/config.json (from config.example.json)\n~/.openclaw/data/veille/ (data directory)\nCustomizing sources\n\nEdit ~/.openclaw/config/veille/config.json and add/remove entries in the \"sources\" dict:\n\n{\n  \"sources\": {\n    \"My Blog\": \"https://example.com/feed.xml\",\n    \"BleepingComputer\": \"https://www.bleepingcomputer.com/feed/\"\n  }\n}\n\nStorage and credentials\nFiles written by this skill\nPath\tWritten by\tPurpose\tContains secrets\n~/.openclaw/config/veille/config.json\tsetup.py\tSources, outputs, options\tNO\n~/.openclaw/data/veille/seen_urls.json\tveille.py\tURL dedup store (TTL 14d)\tNO\n~/.openclaw/data/veille/topic_seen.json\tveille.py\tTopic dedup store (TTL 5d)\tNO\nFiles read from outside the skill\nPath\tRead by\tKey accessed\tWhen\n~/.openclaw/openclaw.json\tdispatch.py\tchannels.telegram.botToken (read-only)\tOnly when telegram_bot output is enabled and no bot_token is set in the output config\n\nThis is the only cross-config read. To avoid it entirely, set bot_token explicitly in your output config:\n\n{ \"type\": \"telegram_bot\", \"bot_token\": \"YOUR_BOT_TOKEN\", \"chat_id\": \"...\", \"enabled\": true }\n\nOutput credentials (optional)\n\nCredentials are only used if you enable the corresponding output. None are required for core functionality (RSS fetch + dedup).\n\nOutput\tCredential source\tWhat is used\ntelegram_bot\t~/.openclaw/openclaw.json or bot_token in output config\tBot token (read-only)\nmail-client\tDelegated to mail-client skill (its own creds)\tNothing read directly\nmail-client (SMTP fallback)\tsmtp_user / smtp_pass in output config\tSMTP login\nnextcloud\tDelegated to nextcloud-files skill (its own creds)\tNothing read directly\nCleanup on uninstall\npython3 scripts/setup.py --cleanup\n\nSecurity model\nCredential isolation\nAPI keys are read from dedicated files (default ~/.openclaw/secrets/), never from config.json. The scorer warns at runtime if a key file has overly permissive filesystem permissions.\nSMTP credentials (fallback only) are stored in the output config block — use the mail-client skill delegation to avoid storing SMTP passwords.\nSubprocess boundaries\nDispatch delegates to other OpenClaw skills via subprocess.run() (never shell=True). Script paths are validated to reside under ~/.openclaw/workspace/skills/ before execution, preventing path traversal.\nNo credentials are passed as subprocess arguments — each skill manages its own authentication.\nFile output safety\nThe file output type validates the target path before writing: only ~/.openclaw/ is allowed by default. Additional directories can be whitelisted via config.security.allowed_output_dirs. Sensitive paths (.ssh, .gnupg, /etc/, .bashrc, etc.) are always blocked regardless of allowlist.\nWritten content is checked for suspicious patterns (shell shebangs, SSH keys, PGP blocks, code injection) and size-limited to 1 MB.\nCross-config reads\nThe only cross-config file read is ~/.openclaw/openclaw.json for the Telegram bot token, and only when telegram_bot output is enabled without an explicit bot_token. This read is logged to stderr. Set bot_token in the output config to eliminate this read entirely.\nAutonomous dispatch\nWhen scheduled (cron), the skill can send messages/files to configured outputs without user interaction. All dispatch actions are logged to stderr with an audit summary. Use enabled: false on any output to disable it without removing its config.\nCLI reference\nfetch\npython3 veille.py fetch [--hours N] [--filter-seen] [--filter-topic] [--sources FILE]\n\n\nOptions:\n\n--hours N : lookback window in hours (default: from config, usually 24)\n--filter-seen : filter already-seen URLs (uses seen_urls.json TTL store)\n--filter-topic : deduplicate by topic (uses topic_seen.json + Jaccard similarity)\n--sources FILE : path to custom JSON sources file\n\nOutput (JSON on stdout):\n\n{\n  \"hours\": 24,\n  \"count\": 42,\n  \"skipped_url\": 5,\n  \"skipped_topic\": 3,\n  \"articles\": [...],\n  \"wrapped_listing\": \"=== UNTRUSTED EXTERNAL CONTENT ...\"\n}\n\nseen-stats\npython3 veille.py seen-stats\n\n\nShows URL seen store statistics (count, TTL, file path).\n\ntopic-stats\npython3 veille.py topic-stats\n\n\nShows topic deduplication store statistics.\n\nmark-seen\npython3 veille.py mark-seen URL [URL ...]\n\n\nMarks one or more URLs as already seen (prevents them from appearing in future fetches with --filter-seen).\n\nscore\npython3 veille.py score [--dry-run]\n\n\nReads a digest JSON from stdin (output of fetch) and scores articles using an OpenAI-compatible LLM. Returns enriched JSON with scored, ghost_picks, and per-article score/reason fields.\n\nOptions:\n\n--dry-run : print summary on stderr without calling the LLM API\n\nWhen llm.enabled is false (default), articles pass through unchanged (\"scored\": false).\n\nPipeline usage:\n\npython3 veille.py fetch --filter-seen --filter-topic | python3 veille.py score | python3 veille.py send\n\nsend\npython3 veille.py send [--profile NAME]\n\n\nReads a digest JSON from stdin and dispatches to all enabled outputs configured in config.json. Accepts both raw fetch output (articles key) and LLM-processed digests (categories key).\n\nOutput types: telegram_bot, mail-client, nextcloud, file.\n\ntelegram_bot: bot token auto-read from OpenClaw config - no extra setup if Telegram already configured.\nmail-client: delegates to mail-client skill if installed, falls back to raw SMTP config.\nnextcloud: delegates to nextcloud-files skill if installed (append mode by default with date separator).\nfile: writes digest to a local file. Path must be under ~/.openclaw/ (default) or a directory listed in config.security.allowed_output_dirs. Sensitive paths and suspicious content are blocked (see Security model).\n\nConfigure outputs interactively:\n\npython3 scripts/setup.py --manage-outputs\n\nconfig\npython3 veille.py config\n\n\nPrints the active configuration (no secrets).\n\nLLM scoring configuration\n\nThe llm key in config.json controls the optional LLM-based article scoring:\n\n{\n  \"llm\": {\n    \"enabled\": false,\n    \"base_url\": \"https://api.openai.com/v1\",\n    \"api_key_file\": \"~/.openclaw/secrets/openai_api_key\",\n    \"model\": \"gpt-4o-mini\",\n    \"top_n\": 10,\n    \"ghost_threshold\": 5\n  }\n}\n\nKey\tDefault\tDescription\nenabled\tfalse\tEnable LLM scoring (requires API key)\nbase_url\thttps://api.openai.com/v1\tOpenAI-compatible API endpoint\napi_key_file\t~/.openclaw/secrets/openai_api_key\tPath to file containing the API key\nmodel\tgpt-4o-mini\tModel to use for scoring\ntop_n\t10\tMax articles to send to LLM per batch\nghost_threshold\t5\tScore threshold for ghost_picks (blog-worthy articles)\n\nScoring rules:\n\nOnly the first top_n articles are sent to the LLM. Articles beyond top_n are excluded from the digest entirely. fetch returns articles sorted by date desc, so top_n selects the most recent ones. Increase top_n to evaluate more articles per run (higher token cost).\nScore >= ghost_threshold : added to ghost_picks list\nScore >= 3 : kept in articles list\nScore <= 2 : excluded from output\nArticles are sorted by score (descending)\n\nWhen disabled, the score subcommand passes data through unchanged.\n\nNextcloud output mode\n\nThe nextcloud output now defaults to append mode with a date separator. Each dispatch adds content below a ## YYYY-MM-DD HH:MM header, preserving previous entries.\n\nSet \"mode\": \"overwrite\" in the output config to restore the old behavior:\n\n{ \"type\": \"nextcloud\", \"path\": \"/Veille/digest.md\", \"mode\": \"overwrite\" }\n\nFile output configuration\n\nThe file output writes digests to the local filesystem. By default, only paths under ~/.openclaw/ are allowed. To authorize additional directories, use config.security.allowed_output_dirs:\n\n{\n  \"security\": {\n    \"allowed_output_dirs\": [\n      \"~/Documents/veille\",\n      \"/srv/digests\"\n    ]\n  }\n}\n\n\nBlocked paths (always rejected, even if inside an allowed directory): .ssh, .gnupg, .config/systemd, crontab, /etc/, .bashrc, .profile, .bash_profile, .zshrc, .env\n\nContent validation — written content is rejected if it:\n\nExceeds 1 MB\nContains shell shebangs (#!/), SSH keys, PGP blocks, or code injection patterns (eval(, exec(, __import__(, import os, import subprocess)\n\nAll blocked attempts are logged to stderr with the reason.\n\nTemplates (agent usage)\nBasic digest\n# In agent tool call:\nresult = exec(\"python3 scripts/veille.py fetch --hours 24 --filter-seen --filter-topic\")\ndata = json.loads(result.stdout)\n# data[\"wrapped_listing\"] is ready for LLM prompt injection\n# data[\"count\"] = number of new articles\n# data[\"articles\"] = list of article dicts\n\nPrompt template\nYou are a news analyst. Here are today's articles:\n\n{data[\"wrapped_listing\"]}\n\nPlease summarize the 5 most important stories, focusing on security and tech.\n\nAgent workflow example\n1. Call veille fetch --filter-seen --filter-topic\n2. Pipe through veille score (LLM scoring, if enabled)\n3. If count > 0: pass wrapped_listing to LLM for analysis\n4. LLM produces digest summary\n5. Pipe through veille send (dispatches to configured outputs)\n\nPipeline (CLI)\npython3 scripts/veille.py fetch --filter-seen --filter-topic \\\n  | python3 scripts/veille.py score \\\n  | python3 scripts/veille.py send\n\nFiltering by keyword (post-fetch)\ndata = json.loads(fetch_output)\nsecurity_articles = [\n    a for a in data[\"articles\"]\n    if any(kw in a[\"title\"].lower() for kw in [\"cve\", \"vuln\", \"patch\", \"breach\"])\n]\n\nIdeas\nAdd keyword-based filtering (--keywords security,cve,linux)\nAdd per-source TTL override in config\nExport digest as HTML or Markdown\nSchedule with cron: 0 8 * * * python3 veille.py fetch --filter-seen --filter-topic\nWeight articles by source tier for LLM prioritization\nAdd OPML import/export for source list management\nIntegrate with ntfy or Telegram for real-time alerts on high-priority articles\nCombine with\n\nmail-client : send the digest by email after fetching\n\nveille fetch --filter-seen | ... | mail-client send\n\n\nnextcloud-files : archive the daily digest as a Markdown file\n\nveille fetch --filter-seen | jq .wrapped_listing -r > /tmp/digest.md\nnextcloud-files upload /tmp/digest.md /Digests/$(date +%Y-%m-%d).md\n\nTroubleshooting\n\nSee references/troubleshooting.md for detailed troubleshooting steps.\n\nCommon issues:\n\nNo articles returned: check --hours value, verify feed URLs in config\nXML parse error on a feed: some feeds use non-standard XML; the skill skips broken items silently\nAll articles filtered as seen: run seen-stats to check store size; reset with rm seen_urls.json\nImport error: ensure you run veille.py from its directory or via full path\nFile output blocked: path is outside ~/.openclaw/ — add the target directory to config.security.allowed_output_dirs (see File output configuration)"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/Romain-Grosos/veille",
    "publisherUrl": "https://clawhub.ai/Romain-Grosos/veille",
    "owner": "Romain-Grosos",
    "version": "1.2.6",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/veille",
    "downloadUrl": "https://openagent3.xyz/downloads/veille",
    "agentUrl": "https://openagent3.xyz/skills/veille/agent",
    "manifestUrl": "https://openagent3.xyz/skills/veille/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/veille/agent.md"
  }
}