119 lines
4.0 KiB
Python
119 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Inventory local OSS Jyotish candidates as observation-only sources."""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
BASE = ROOT / "references" / "open_source_sources"
|
|
CANDIDATES = ["VedicAstro", "jyotishganit", "panchanga_api", "rishi-ai-mcp", "jaimini-tropical", "vedic-astro-skills"]
|
|
|
|
|
|
def sha256_file(path: Path) -> str:
|
|
h = hashlib.sha256()
|
|
with path.open("rb") as f:
|
|
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
|
h.update(chunk)
|
|
return h.hexdigest()
|
|
|
|
|
|
def git_commit(path: Path) -> str | None:
|
|
try:
|
|
return subprocess.check_output(["git", "-C", str(path), "rev-parse", "HEAD"], text=True, stderr=subprocess.DEVNULL).strip()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def first_existing(path: Path, names: list[str]) -> Path | None:
|
|
for name in names:
|
|
p = path / name
|
|
if p.exists():
|
|
return p
|
|
return None
|
|
|
|
|
|
def license_hint(path: Path) -> dict[str, Any]:
|
|
p = first_existing(path, ["LICENSE", "LICENSE.md", "LICENSE.txt", "COPYING"])
|
|
if not p:
|
|
return {"status": "missing", "file": None, "sha256": None, "hint": "unknown"}
|
|
text = p.read_text(encoding="utf-8", errors="ignore")[:3000].lower()
|
|
if "mit license" in text or "permission is hereby granted" in text:
|
|
hint = "MIT"
|
|
elif "apache license" in text:
|
|
hint = "Apache-2.0"
|
|
elif "bsd" in text:
|
|
hint = "BSD-like"
|
|
elif "gnu affero" in text:
|
|
hint = "AGPL"
|
|
elif "gnu general public license" in text:
|
|
hint = "GPL"
|
|
else:
|
|
hint = "unknown"
|
|
return {"status": "present", "file": str(p.relative_to(ROOT)), "sha256": sha256_file(p), "hint": hint}
|
|
|
|
|
|
def file_manifest(path: Path) -> list[dict[str, Any]]:
|
|
wanted = ["README.md", "README.rst", "pyproject.toml", "package.json", "pubspec.yaml", "requirements.txt"]
|
|
rows = []
|
|
for name in wanted:
|
|
p = path / name
|
|
if p.exists():
|
|
rows.append({"path": str(p.relative_to(ROOT)), "sha256": sha256_file(p)})
|
|
return rows
|
|
|
|
|
|
def api_hints(path: Path) -> list[str]:
|
|
hints = []
|
|
for pattern in ("*.py", "*.ts", "*.js", "*.dart"):
|
|
for p in list(path.rglob(pattern))[:200]:
|
|
if any(part in {".git", "node_modules", "__pycache__"} for part in p.parts):
|
|
continue
|
|
rel = str(p.relative_to(path))
|
|
name = p.stem.lower()
|
|
if any(k in name or k in rel.lower() for k in ["panch", "muhur", "dasha", "kp", "chart", "asht", "shadbala", "jaimini"]):
|
|
hints.append(rel)
|
|
if len(hints) >= 30:
|
|
return hints
|
|
return hints
|
|
|
|
|
|
def main() -> int:
|
|
rows = []
|
|
for name in CANDIDATES:
|
|
path = BASE / name
|
|
if not path.exists():
|
|
rows.append({"project_id": name, "status": "missing"})
|
|
continue
|
|
rows.append({
|
|
"project_id": name,
|
|
"status": "present",
|
|
"local_path": str(path.relative_to(ROOT)),
|
|
"git_commit": git_commit(path),
|
|
"license": license_hint(path),
|
|
"manifest_files": file_manifest(path),
|
|
"api_surface_hints": api_hints(path),
|
|
"claim_status": "observation_only",
|
|
"runtime_dependency_allowed": False,
|
|
"truth_upgrade_allowed": False,
|
|
})
|
|
out = {
|
|
"scope": "local_oss_observation_inventory",
|
|
"created_at": "2026-07-19",
|
|
"status": "complete",
|
|
"claim_status": "observation_only",
|
|
"production_tuning_allowed": False,
|
|
"truth_matrix_allowed": False,
|
|
"boundary": "Local OSS candidates are pinned for reuse/probe triage only. License and hash metadata do not validate astrological truth.",
|
|
"projects": rows,
|
|
}
|
|
print(json.dumps(out, ensure_ascii=False, indent=2, sort_keys=True))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|