External integrations connect OpenClaw-env-manager to tool ecosystems such as OpenRouter and Cisco Skill Scanner.

openenv.integrations

openenv.integrations

External integration modules.

openenv.integrations.scanner

openenv.integrations.scanner

Skill scanner integration for OpenClawenv.

materialize_skills(manifest, target_dir)

Write inline skills to a directory tree consumable by skill-scanner.

Source code in src/openenv/integrations/scanner.py
def materialize_skills(manifest: Manifest, target_dir: str | Path) -> Path:
    """Write inline skills to a directory tree consumable by skill-scanner."""
    skills_root = Path(target_dir)
    skills_root.mkdir(parents=True, exist_ok=True)
    for skill in manifest.skills:
        skill_dir = skills_root / skill.name
        skill_dir.mkdir(parents=True, exist_ok=True)
        (skill_dir / "SKILL.md").write_text(
            skill.rendered_content(
                state_dir=manifest.openclaw.state_dir,
                workspace=manifest.openclaw.workspace,
            ),
            encoding="utf-8",
        )
        for relative_path, content in sorted(skill.assets.items()):
            asset_path = skill_dir / relative_path
            asset_path.parent.mkdir(parents=True, exist_ok=True)
            asset_path.write_text(
                rewrite_openclaw_home_paths(
                    content,
                    state_dir=manifest.openclaw.state_dir,
                    workspace=manifest.openclaw.workspace,
                ),
                encoding="utf-8",
            )
    return skills_root

run_skill_scanner(manifest_path, manifest, *, scanner_bin='skill-scanner', scanner_args=None, keep_artifacts=False)

Materialize skills and invoke the external skill-scanner CLI.

Source code in src/openenv/integrations/scanner.py
def run_skill_scanner(
    manifest_path: str | Path,
    manifest: Manifest,
    *,
    scanner_bin: str = "skill-scanner",
    scanner_args: list[str] | None = None,
    keep_artifacts: bool = False,
) -> Path | None:
    """Materialize skills and invoke the external skill-scanner CLI."""
    manifest_root = Path(manifest_path).resolve().parent
    extra_args = list(scanner_args or [])
    if extra_args and extra_args[0] == "--":
        extra_args = extra_args[1:]

    scan_root = manifest_root / f"openclawenv-scan-tmp-{uuid.uuid4().hex}"
    scan_root.mkdir(parents=True, exist_ok=False)
    try:
        skills_root = materialize_skills(manifest, scan_root / "skills")
        command = [scanner_bin, "scan-all", str(skills_root), "--recursive", *extra_args]
        try:
            subprocess.run(command, check=True, cwd=manifest_root)
        except OSError as exc:
            raise CommandError(
                "skill-scanner is not available on PATH. "
                "Install it with `pip install .[scan]` or provide --scanner-bin."
            ) from exc
        except subprocess.CalledProcessError as exc:
            raise CommandError(
                f"skill-scanner failed with exit code {exc.returncode}.",
                exit_code=exc.returncode,
            ) from exc

        if keep_artifacts:
            destination = manifest_root / ".openclawenv-scan"
            if destination.exists():
                shutil.rmtree(destination, ignore_errors=True)
            shutil.copytree(scan_root, destination)
            return destination
    finally:
        shutil.rmtree(scan_root, ignore_errors=True)
    return None

openenv.integrations.openrouter

openenv.integrations.openrouter

OpenRouter integration for improving bot markdown documents with tool calling.

improve_markdown_documents_with_openrouter(*, api_key, bot_name, context_payload, instruction, write_document, model=None, output_language=DEFAULT_OUTPUT_LANGUAGE, batch_size=DEFAULT_DOCUMENT_BATCH_SIZE)

Use OpenRouter tool calling to inspect and rewrite bot markdown files.

Source code in src/openenv/integrations/openrouter.py
def improve_markdown_documents_with_openrouter(
    *,
    api_key: str,
    bot_name: str,
    context_payload: dict[str, Any],
    instruction: str,
    write_document: Callable[[str, str], None],
    model: str | None = None,
    output_language: str = DEFAULT_OUTPUT_LANGUAGE,
    batch_size: int = DEFAULT_DOCUMENT_BATCH_SIZE,
) -> str:
    """Use OpenRouter tool calling to inspect and rewrite bot markdown files."""
    documents = context_payload.get("documents")
    if not isinstance(documents, dict):
        raise OpenEnvError("context_payload.documents must be an object mapping files to text.")
    if batch_size < 1:
        raise OpenEnvError("batch_size must be at least 1.")

    working_context = _clone_context_payload(context_payload)
    allowed_files = sorted(working_context["documents"].keys())
    if not allowed_files:
        return "No markdown documents were available for improvement."

    batches = list(_document_batches(allowed_files, batch_size))
    summaries: list[str] = []

    def tracked_write_document(file_name: str, content: str) -> None:
        """Persist one updated file and refresh the working context for later batches."""
        write_document(file_name, content)
        working_context["documents"][file_name] = content

    for index, batch_files in enumerate(batches, start=1):
        batch_context = _context_payload_for_batch(
            working_context,
            batch_files=batch_files,
            batch_index=index,
            total_batches=len(batches),
        )
        summary = _improve_markdown_documents_batch(
            api_key=api_key,
            bot_name=bot_name,
            context_payload=batch_context,
            instruction=instruction,
            write_document=tracked_write_document,
            model=model,
            output_language=output_language,
        )
        if summary:
            summaries.append(summary)

    if len(summaries) == 1:
        return summaries[0]
    return " | ".join(
        f"Batch {index}: {summary}" for index, summary in enumerate(summaries, start=1)
    )