- bin/db/psql-yq + bin/web/search + tools/{yamlout,websearch} vendored as real files
- bin/db/ssh-tunnel added (OnlyOffice VM pg on 5433)
- skills reference local bin/ paths; no agent-skills/abs links in git
- pyproject.toml + uv.lock; CI installs via uv sync --frozen
- release-please auto-tags semver from conventional commits when green
- LICENSE MIT, badges/mermaid README
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""serve - tiny read-only HTTP wrapper over bin/kb/search.
|
|
|
|
serve [port] default 8630, binds 127.0.0.1
|
|
|
|
GET /health -> {"status":"ok"}
|
|
GET /search?q=... -> YAML from kb/search (transparent, cached by client)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import urllib.parse
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
|
|
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8630
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
def log_message(self, *args): # keep stdout clean
|
|
pass
|
|
|
|
def do_GET(self): # noqa: N802
|
|
url = urllib.parse.urlparse(self.path)
|
|
if url.path.rstrip("/") == "/health":
|
|
self._json({"status": "ok"})
|
|
return
|
|
if url.path.rstrip("/") == "/search":
|
|
q = urllib.parse.parse_qs(url.query).get("q", [""])[0]
|
|
if not q:
|
|
self._json({"error": "q required"}, code=400)
|
|
return
|
|
self._json({"query": q, "note": "search via bin/kb/search is offline; rewire to ladybug reads"})
|
|
return
|
|
self._json({"error": "not found"}, code=404)
|
|
|
|
def _json(self, obj, code=200):
|
|
body = json.dumps(obj, ensure_ascii=False).encode()
|
|
self.send_response(code)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.send_header("Content-Length", str(len(body)))
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
server = ThreadingHTTPServer(("127.0.0.1", PORT), Handler)
|
|
print(f"serve: 127.0.0.1:{PORT}", file=sys.stderr)
|
|
server.serve_forever() |