feat: register jarvis scan command and add init privacy hook

Registers the new scan command in the CLI. Adds a lightweight
privacy check at the end of jarvis init that runs disk encryption
and cloud sync checks, with pointer to jarvis scan for full audit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jon Saad-Falcon
2026-03-24 18:22:13 -07:00
co-authored by Claude Sonnet 4.6
parent e2376d29d1
commit 02bc36c1f8
3 changed files with 51 additions and 0 deletions
+2
View File
@@ -26,6 +26,7 @@ from openjarvis.cli.operators_cmd import operators
from openjarvis.cli.optimize_cmd import optimize_group
from openjarvis.cli.quickstart_cmd import quickstart
from openjarvis.cli.registry_cmd import registry
from openjarvis.cli.scan_cmd import scan
from openjarvis.cli.scheduler_cmd import scheduler
from openjarvis.cli.serve import serve
from openjarvis.cli.skill_cmd import skill
@@ -87,6 +88,7 @@ cli.add_command(gateway, "gateway")
cli.add_command(tool, "tool")
cli.add_command(registry, "registry")
cli.add_command(config, "config")
cli.add_command(scan, "scan")
def main() -> None:
+19
View File
@@ -11,6 +11,7 @@ from rich.markup import escape
from rich.panel import Panel
from openjarvis.cli.model import find_model_spec, hf_download, ollama_pull
from openjarvis.cli.scan_cmd import PrivacyScanner
from openjarvis.core.config import (
DEFAULT_CONFIG_DIR,
DEFAULT_CONFIG_PATH,
@@ -156,6 +157,23 @@ def _next_steps_text(engine: str, model: str = "") -> str:
return steps.get(engine, steps["ollama"])
def _quick_privacy_check(console: Console) -> None:
"""Run critical privacy checks and print compact summary."""
scanner = PrivacyScanner()
results = scanner.run_quick()
if results:
console.print(" [bold]Privacy check:[/bold]")
for r in results:
if r.status == "ok":
console.print(f" [green]\u2713[/green] {r.message}")
elif r.status == "warn":
console.print(f" [yellow]![/yellow] {r.message}")
elif r.status == "fail":
console.print(f" [red]\u2717[/red] {r.message}")
console.print()
console.print(" Run [cyan]jarvis scan[/cyan] for a full environment audit.")
def _do_download(engine: str, model: str, spec, console: Console) -> None:
"""Dispatch model download based on engine type."""
import os
@@ -358,6 +376,7 @@ def init(
if click.confirm(prompt, default=True):
_do_download(selected_engine, model, spec, console)
_quick_privacy_check(console)
console.print()
console.print(
Panel(
+30
View File
@@ -19,6 +19,7 @@ class TestInitShowsNextSteps:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "llamacpp", "--no-download"])
assert result.exit_code == 0
@@ -33,6 +34,7 @@ class TestInitShowsNextSteps:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "llamacpp", "--no-download"])
assert result.exit_code == 0
@@ -92,6 +94,7 @@ class TestMinimalConfig:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "ollama", "--no-download"])
assert result.exit_code == 0
@@ -109,6 +112,7 @@ class TestMinimalConfig:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--full", "--engine", "ollama", "--no-download"])
assert result.exit_code == 0
@@ -126,6 +130,7 @@ class TestInitDownloadPrompt:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "ollama"], input="n\n")
assert result.exit_code == 0
@@ -137,6 +142,7 @@ class TestInitDownloadPrompt:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "ollama", "--no-download"])
assert result.exit_code == 0
@@ -151,6 +157,7 @@ class TestInitEmptyModelFallback:
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.recommend_model", return_value=""),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "llamacpp"])
assert result.exit_code == 0
@@ -179,6 +186,7 @@ class TestInitDownloadDispatch:
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.ollama_pull", return_value=True) as mock_pull,
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "ollama"], input="y\n")
assert result.exit_code == 0
@@ -190,7 +198,29 @@ class TestInitDownloadDispatch:
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner"),
):
result = CliRunner().invoke(cli, ["init", "--engine", "vllm"], input="y\n")
assert result.exit_code == 0
assert "automatically" in result.output
class TestInitPrivacyHook:
def test_init_shows_privacy_summary(self, tmp_path: Path) -> None:
config_dir = tmp_path / ".openjarvis"
config_path = config_dir / "config.toml"
with (
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_DIR", config_dir),
mock.patch("openjarvis.cli.init_cmd.DEFAULT_CONFIG_PATH", config_path),
mock.patch("openjarvis.cli.init_cmd.PrivacyScanner") as MockScanner,
):
from openjarvis.cli.scan_cmd import ScanResult
instance = MockScanner.return_value
instance.run_quick.return_value = [
ScanResult("FileVault", "ok", "FileVault enabled", "darwin"),
]
result = CliRunner().invoke(
cli, ["init", "--engine", "llamacpp", "--no-download"]
)
assert result.exit_code == 0
assert "jarvis scan" in result.output