ultimate-pi 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,100 @@
1
+ """SearXNG JSON search API (self-hosted instances)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import ssl
7
+ from typing import Any
8
+ from urllib.error import HTTPError, URLError
9
+ from urllib.parse import urlencode
10
+ from urllib.request import ProxyHandler, Request, build_opener, urlopen
11
+
12
+ from .config import HarnessWebConfig
13
+
14
+
15
+ def _open_url(url: str, *, config: HarnessWebConfig) -> tuple[int, str]:
16
+ req = Request(
17
+ url,
18
+ headers={"Accept": "application/json", "User-Agent": "ultimate-pi-harness-web/1.0"},
19
+ method="GET",
20
+ )
21
+ if config.proxy:
22
+ opener = build_opener(ProxyHandler({"http": config.proxy, "https": config.proxy}))
23
+ resp = opener.open(req, timeout=config.timeout_sec)
24
+ else:
25
+ ctx = ssl.create_default_context()
26
+ resp = urlopen(req, timeout=config.timeout_sec, context=ctx)
27
+ try:
28
+ status = getattr(resp, "status", 200) or 200
29
+ body = resp.read().decode("utf-8", errors="replace")
30
+ return status, body
31
+ finally:
32
+ resp.close()
33
+
34
+
35
+ def _parse_results(payload: Any, limit: int) -> list[dict[str, str]]:
36
+ raw = payload.get("results") if isinstance(payload, dict) else None
37
+ if not isinstance(raw, list):
38
+ return []
39
+ out: list[dict[str, str]] = []
40
+ for item in raw:
41
+ if len(out) >= limit:
42
+ break
43
+ if not isinstance(item, dict):
44
+ continue
45
+ url = (item.get("url") or "").strip()
46
+ if not url.startswith("http"):
47
+ continue
48
+ title = (item.get("title") or "").strip()
49
+ description = (item.get("content") or item.get("snippet") or "").strip()
50
+ out.append({"url": url, "title": title, "description": description})
51
+ return out
52
+
53
+
54
+ def search_searxng(
55
+ query: str,
56
+ *,
57
+ limit: int,
58
+ config: HarnessWebConfig,
59
+ ) -> list[dict[str, str]]:
60
+ base = config.searxng_url
61
+ if not base:
62
+ raise SystemExit("HARNESS_WEB_SEARXNG_URL is not set")
63
+
64
+ qs = urlencode({"q": query, "format": "json", "pageno": "1"})
65
+ url = f"{base}/search?{qs}"
66
+
67
+ try:
68
+ status, body = _open_url(url, config=config)
69
+ except HTTPError as err:
70
+ status = err.code
71
+ body = err.read().decode("utf-8", errors="replace") if err.fp else ""
72
+ except URLError as err:
73
+ raise SystemExit(
74
+ f"SearXNG request failed ({err.reason}). "
75
+ f"Is the instance running at {base}? "
76
+ "Run: node \"$UP_PKG/.pi/scripts/harness-searxng-bootstrap.mjs\""
77
+ ) from err
78
+
79
+ if status == 403:
80
+ raise SystemExit(
81
+ "SearXNG returned 403 for format=json. Enable json under search.formats "
82
+ "in settings.yml (see .searxng/core-config/settings.yml or SearXNG docs)."
83
+ )
84
+ if status != 200:
85
+ snippet = body[:200].replace("\n", " ")
86
+ raise SystemExit(f"SearXNG search failed (HTTP {status}): {snippet}")
87
+
88
+ try:
89
+ payload = json.loads(body)
90
+ except json.JSONDecodeError as err:
91
+ raise SystemExit(f"SearXNG returned non-JSON response from {url}") from err
92
+
93
+ results = _parse_results(payload, limit)
94
+ if not results and isinstance(payload, dict):
95
+ unresponsive = payload.get("unresponsive_engines")
96
+ if unresponsive:
97
+ raise SystemExit(
98
+ f"SearXNG returned no results; upstream engines unresponsive: {unresponsive}"
99
+ )
100
+ return results
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [v0.5.0] — 2026-05-17
8
+
9
+ ### ✨ Features
10
+
11
+ - **web_search / web_fetch pi tools:** wrap `harness-web.py` with session injection and a bash guard so agents skip `UP_PKG` and scrapling import preflights.
12
+ - **SearXNG search backend:** pluggable `HARNESS_WEB_SEARCH_ENGINE` (`ddg_html` | `searxng`) with Docker bootstrap via `harness-searxng-bootstrap.mjs`.
13
+ - **harness-web status:** JSON config subcommand for setup and diagnostics.
14
+
15
+ ### 🔧 Chores
16
+
17
+ - Apply pre-commit format and refresh `graphify-out` after harness-web tools merge.
18
+
7
19
  ## [v0.4.1] — 2026-05-17
8
20
 
9
21
  ### ✨ Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-pi",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Ultimate AI coding harness for pi.dev — extensible skills, Obsidian wiki knowledge layer, compressed context, deterministic output",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -74,7 +74,7 @@
74
74
  "@mariozechner/pi-coding-agent": "*"
75
75
  },
76
76
  "scripts": {
77
- "check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/lib/harness-subagents/agent-loader.ts .pi/extensions/lib/harness-subagents/agent-parser.ts .pi/extensions/lib/harness-subagents/agent-manifest.ts .pi/extensions/lib/harness-subagents/blackboard.ts .pi/extensions/lib/harness-subagents/blackboard-tool.ts .pi/extensions/lib/harness-subagents/spawn-policy.ts .pi/extensions/lib/harness-subagents/types-blackboard.ts",
77
+ "check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/lib/harness-subagents/agent-loader.ts .pi/extensions/lib/harness-subagents/agent-parser.ts .pi/extensions/lib/harness-subagents/agent-manifest.ts .pi/extensions/lib/harness-subagents/blackboard.ts .pi/extensions/lib/harness-subagents/blackboard-tool.ts .pi/extensions/lib/harness-subagents/spawn-policy.ts .pi/extensions/lib/harness-subagents/types-blackboard.ts .pi/extensions/harness-web-tools.ts .pi/extensions/harness-web-guard.ts .pi/extensions/lib/harness-web/run-cli.ts",
78
78
  "vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
79
79
  "vendor:sync-vcc": "bash .pi/scripts/vendor-sync-pi-vcc.sh",
80
80
  "release": "bash .pi/scripts/release.sh",