@heretek-ai/epistemic-swarm 0.2.2 → 0.3.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.
Files changed (138) hide show
  1. package/.agents/skills/brainstorming/SKILL.md +13 -0
  2. package/.agents/skills/code_audit/SKILL.md +13 -0
  3. package/.agents/skills/epistemic_search/SKILL.md +13 -0
  4. package/.agents/skills/grilling/SKILL.md +13 -0
  5. package/.agents/skills/oss_scout/SKILL.md +13 -0
  6. package/.agents/skills/research_cache/SKILL.md +13 -0
  7. package/.agents/skills/swarm_config/SKILL.md +13 -0
  8. package/.claude-plugin/plugin.json +15 -5
  9. package/.omp/README.md +39 -0
  10. package/.omp/SYSTEM.md +12 -0
  11. package/.omp/commands/audit.md +10 -0
  12. package/.omp/commands/brainstorming.md +12 -0
  13. package/.omp/commands/grill.md +9 -0
  14. package/.omp/commands/scout.md +10 -0
  15. package/.omp/commands/swarm-config.md +10 -0
  16. package/.omp/commands/swarm.md +9 -0
  17. package/.omp/hooks/post/epistemic-audit.ts +16 -0
  18. package/.omp/hooks/pre/epistemic-redirect.ts +21 -0
  19. package/.omp/prompts/brainstorming.md +8 -0
  20. package/.omp/prompts/swarm.md +8 -0
  21. package/MARKETPLACE.md +8 -0
  22. package/README.md +53 -8
  23. package/bin/cli.js +27 -3
  24. package/config/domain_packs/biopharma.json +23 -0
  25. package/config/domain_packs/legal.json +19 -0
  26. package/config/domain_packs/quant.json +19 -0
  27. package/config/mcp-research-servers.json +7 -0
  28. package/config/mcp_launcher.py +48 -137
  29. package/config/opencode-snippet.json +58 -3
  30. package/config/searxng_mcp.py +42 -83
  31. package/extensions/pi/index.js +196 -28
  32. package/install.sh +20 -4
  33. package/package.json +40 -5
  34. package/plugins/antigravity/README.md +28 -0
  35. package/plugins/antigravity/agents/alpha-thesis.md +6 -0
  36. package/plugins/antigravity/agents/beta-antithesis.md +7 -0
  37. package/plugins/antigravity/agents/brainstormer.md +7 -0
  38. package/plugins/antigravity/agents/epistemic-auditor.md +5 -0
  39. package/plugins/antigravity/hooks.json +23 -0
  40. package/plugins/antigravity/mcp_config.json +33 -0
  41. package/plugins/antigravity/plugin.json +21 -0
  42. package/plugins/antigravity/rules/epistemic-integrity.md +6 -0
  43. package/plugins/antigravity/skills/brainstorming/SKILL.md +13 -0
  44. package/plugins/antigravity/skills/code_audit/SKILL.md +13 -0
  45. package/plugins/antigravity/skills/epistemic_search/SKILL.md +13 -0
  46. package/plugins/antigravity/skills/grilling/SKILL.md +13 -0
  47. package/plugins/antigravity/skills/oss_scout/SKILL.md +13 -0
  48. package/plugins/antigravity/skills/research_cache/SKILL.md +13 -0
  49. package/plugins/antigravity/skills/swarm_config/SKILL.md +13 -0
  50. package/plugins/codex/AGENTS.md.snippet +10 -0
  51. package/plugins/codex/README.md +37 -0
  52. package/plugins/codex/config.toml.snippet +28 -0
  53. package/plugins/codex/openai.yaml +24 -0
  54. package/plugins/codex/skills/brainstorming/SKILL.md +13 -0
  55. package/plugins/codex/skills/code_audit/SKILL.md +13 -0
  56. package/plugins/codex/skills/epistemic_search/SKILL.md +13 -0
  57. package/plugins/codex/skills/grilling/SKILL.md +13 -0
  58. package/plugins/codex/skills/oss_scout/SKILL.md +13 -0
  59. package/plugins/codex/skills/research_cache/SKILL.md +13 -0
  60. package/plugins/codex/skills/swarm_config/SKILL.md +13 -0
  61. package/plugins/gemini/GEMINI.md +15 -0
  62. package/plugins/gemini/README.md +19 -0
  63. package/plugins/gemini/commands/audit.toml +6 -0
  64. package/plugins/gemini/commands/brainstorming.toml +10 -0
  65. package/plugins/gemini/commands/grill.toml +6 -0
  66. package/plugins/gemini/commands/scout.toml +7 -0
  67. package/plugins/gemini/commands/swarm-config.toml +7 -0
  68. package/plugins/gemini/commands/swarm.toml +8 -0
  69. package/plugins/gemini/gemini-extension.json +38 -0
  70. package/plugins/gemini/hooks/hooks.json +11 -0
  71. package/plugins/gemini/skills/brainstorming/SKILL.md +13 -0
  72. package/plugins/gemini/skills/code_audit/SKILL.md +13 -0
  73. package/plugins/gemini/skills/epistemic_search/SKILL.md +13 -0
  74. package/plugins/gemini/skills/grilling/SKILL.md +13 -0
  75. package/plugins/gemini/skills/oss_scout/SKILL.md +13 -0
  76. package/plugins/gemini/skills/research_cache/SKILL.md +13 -0
  77. package/plugins/gemini/skills/swarm_config/SKILL.md +13 -0
  78. package/plugins/opencode/index.js +335 -118
  79. package/prompts/agent_brainstormer.md +97 -0
  80. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  81. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  82. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  83. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  84. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  85. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  86. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  87. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  88. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  89. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  90. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  91. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  92. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  93. package/runner/auctioneer.py +169 -0
  94. package/runner/auditor_engine.py +127 -12
  95. package/runner/claim_store.py +361 -0
  96. package/runner/claim_witness.py +183 -0
  97. package/runner/living_dossiers.py +355 -0
  98. package/runner/mcp_protocol.py +188 -0
  99. package/runner/mcp_server.py +567 -0
  100. package/runner/pcrb.py +212 -0
  101. package/runner/pcrb_verify.py +237 -0
  102. package/runner/refinement.py +335 -0
  103. package/runner/research_swarm.py +378 -94
  104. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  105. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  106. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  107. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  108. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  109. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  110. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  111. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  112. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  113. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  114. package/runner/tests/fixtures/auction_objective.json +35 -0
  115. package/runner/tests/fixtures/borderline_claims.json +27 -0
  116. package/runner/tests/fixtures/divergence_objectives.json +31 -0
  117. package/runner/tests/test_auction_order.py +173 -0
  118. package/runner/tests/test_claim_store.py +162 -0
  119. package/runner/tests/test_claim_witness.py +186 -0
  120. package/runner/tests/test_domain_packs.py +217 -0
  121. package/runner/tests/test_fleet_seam.py +113 -0
  122. package/runner/tests/test_living_dossiers.py +204 -0
  123. package/runner/tests/test_mcp_server.py +212 -0
  124. package/runner/tests/test_pcrb.py +240 -0
  125. package/runner/tests/test_refinement.py +255 -0
  126. package/runner/tests/test_swarm.py +173 -15
  127. package/scripts/auction_experiment.py +180 -0
  128. package/scripts/build_adapters.py +183 -0
  129. package/scripts/divergence_experiment.py +184 -0
  130. package/skills/brainstorming/SKILL.md +106 -0
  131. package/skills/brainstorming/__init__.py +1 -0
  132. package/skills/brainstorming/scripts/brainstorm.py +200 -0
  133. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  134. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  135. package/skills/swarm_config/SKILL.md +1 -1
  136. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  137. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
  138. package/skills/swarm_config/configure.py +45 -11
@@ -36,144 +36,55 @@ def run_zero_key_mcp_server(service: str):
36
36
  """Run lightweight JSON-RPC MCP server handling search and fetch without API keys."""
37
37
  from skills.epistemic_search.scripts.search import search_duckduckgo
38
38
  from skills.epistemic_search.scripts.fetch import fetch_and_cache
39
+ from runner.mcp_protocol import StdioJsonRpcServer, ToolSpec
39
40
 
40
- def send_response(response: Dict[str, Any]):
41
- out = json.dumps(response)
42
- sys.stdout.write(f"Content-Length: {len(out)}\r\n\r\n{out}")
43
- sys.stdout.flush()
44
-
45
- def send_json(response: Dict[str, Any]):
46
- # Direct newline JSON-RPC for stdio transports
47
- sys.stdout.write(json.dumps(response) + "\n")
48
- sys.stdout.flush()
49
-
50
- while True:
51
- try:
52
- line = sys.stdin.readline()
53
- if not line:
54
- break
55
- line = line.strip()
56
- if not line:
57
- continue
58
-
59
- # Check if line is Content-Length header or raw JSON
60
- if line.startswith("Content-Length:"):
61
- length = int(line.split(":")[1].strip())
62
- # Read blank line
63
- sys.stdin.readline()
64
- body = sys.stdin.read(length)
65
- req = json.loads(body)
66
- use_headers = True
67
- else:
68
- req = json.loads(line)
69
- use_headers = False
70
-
71
- req_id = req.get("id")
72
- method = req.get("method")
73
- params = req.get("params", {})
74
-
75
- if method == "initialize":
76
- res = {
77
- "jsonrpc": "2.0",
78
- "id": req_id,
79
- "result": {
80
- "protocolVersion": "2024-11-05",
81
- "serverInfo": {
82
- "name": f"epistemic-{service}-fallback",
83
- "version": "0.2.1"
84
- },
85
- "capabilities": {
86
- "tools": {}
87
- }
88
- }
89
- }
90
- elif method == "tools/list":
91
- if service == "brave-search":
92
- tools = [
93
- {
94
- "name": "brave_web_search",
95
- "description": "Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
96
- "inputSchema": {
97
- "type": "object",
98
- "properties": {
99
- "query": {"type": "string", "description": "The search query terms"},
100
- "count": {"type": "number", "description": "Number of results (1-20)", "default": 10}
101
- },
102
- "required": ["query"]
103
- }
104
- }
105
- ]
106
- elif service == "firecrawl":
107
- tools = [
108
- {
109
- "name": "firecrawl_scrape",
110
- "description": "Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
111
- "inputSchema": {
112
- "type": "object",
113
- "properties": {
114
- "url": {"type": "string", "description": "URL to scrape and cache"}
115
- },
116
- "required": ["url"]
117
- }
118
- }
119
- ]
120
- else:
121
- tools = []
122
-
123
- res = {
124
- "jsonrpc": "2.0",
125
- "id": req_id,
126
- "result": {"tools": tools}
127
- }
128
- elif method == "tools/call":
129
- tool_name = params.get("name")
130
- args = params.get("arguments", {})
131
-
132
- if tool_name in ("brave_web_search", "search"):
133
- q = args.get("query", "")
134
- count = args.get("count", 10)
135
- results = search_duckduckgo(query=q, max_results=count)
136
- text_out = json.dumps(results, indent=2)
137
- res = {
138
- "jsonrpc": "2.0",
139
- "id": req_id,
140
- "result": {
141
- "content": [{"type": "text", "text": text_out}]
142
- }
143
- }
144
- elif tool_name in ("firecrawl_scrape", "scrape", "fetch"):
145
- target_url = args.get("url", "")
146
- content = fetch_and_cache(url=target_url)
147
- res = {
148
- "jsonrpc": "2.0",
149
- "id": req_id,
150
- "result": {
151
- "content": [{"type": "text", "text": content}]
152
- }
153
- }
154
- else:
155
- res = {
156
- "jsonrpc": "2.0",
157
- "id": req_id,
158
- "error": {"code": -32601, "message": f"Tool {tool_name} not found"}
159
- }
160
- elif method == "notifications/initialized":
161
- continue
162
- else:
163
- res = {
164
- "jsonrpc": "2.0",
165
- "id": req_id,
166
- "error": {"code": -32601, "message": f"Method {method} not handled"}
167
- }
168
-
169
- if use_headers:
170
- send_response(res)
171
- else:
172
- send_json(res)
173
-
174
- except Exception as e:
175
- sys.stderr.write(f"[mcp-launcher] Error handling request: {e}\n")
176
- break
41
+ tools = []
42
+ if service == "brave-search":
43
+ def handle_brave_search(args: Dict[str, Any]) -> str:
44
+ results = search_duckduckgo(
45
+ query=args.get("query", ""), max_results=args.get("count", 10)
46
+ )
47
+ return json.dumps(results, indent=2)
48
+
49
+ tools.append(ToolSpec(
50
+ name="brave_web_search",
51
+ description="Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
52
+ input_schema={
53
+ "type": "object",
54
+ "properties": {
55
+ "query": {"type": "string", "description": "The search query terms"},
56
+ "count": {"type": "number", "description": "Number of results (1-20)", "default": 10},
57
+ },
58
+ "required": ["query"],
59
+ },
60
+ handler=handle_brave_search,
61
+ aliases=("search",),
62
+ ))
63
+ elif service == "firecrawl":
64
+ def handle_firecrawl_scrape(args: Dict[str, Any]) -> str:
65
+ return fetch_and_cache(url=args.get("url", ""))
66
+
67
+ tools.append(ToolSpec(
68
+ name="firecrawl_scrape",
69
+ description="Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
70
+ input_schema={
71
+ "type": "object",
72
+ "properties": {
73
+ "url": {"type": "string", "description": "URL to scrape and cache"},
74
+ },
75
+ "required": ["url"],
76
+ },
77
+ handler=handle_firecrawl_scrape,
78
+ aliases=("scrape", "fetch"),
79
+ ))
80
+
81
+ server = StdioJsonRpcServer(
82
+ server_name=f"epistemic-{service}-fallback",
83
+ version="0.2.1",
84
+ tools=tools,
85
+ log_prefix="[mcp-launcher]",
86
+ )
87
+ server.serve_forever()
177
88
 
178
89
  def main():
179
90
  service = sys.argv[1] if len(sys.argv) > 1 else "brave-search"
@@ -1,9 +1,45 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
+ "mcp": {
4
+ "iumbtems": {
5
+ "type": "local",
6
+ "command": ["python3", "<IUMBTEMS_ROOT>/runner/mcp_server.py"],
7
+ "enabled": true
8
+ }
9
+ },
3
10
  "plugin": [
4
- "@heretek-ai/epistemic-swarm"
11
+ [
12
+ "@heretek-ai/epistemic-swarm",
13
+ {
14
+ "search_engine": "duckduckgo",
15
+ "max_iterations": 2,
16
+ "mode": "research"
17
+ }
18
+ ]
5
19
  ],
6
20
  "agent": {
21
+ "code-auditor": {
22
+ "description": "IUMBTEMS Codebase Auditor: Dialectic architecture mapping and vulnerability red-teaming with line-level proof.",
23
+ "tools": {
24
+ "read": true,
25
+ "write": true,
26
+ "grep": true,
27
+ "glob": true,
28
+ "iumbtems_code_audit": true,
29
+ "iumbtems_config": true
30
+ }
31
+ },
32
+ "oss-scout": {
33
+ "description": "IUMBTEMS Open Source Scout: Discovers mature OSS packages, audits copyleft licensing, and generates clean-room blueprints.",
34
+ "tools": {
35
+ "read": true,
36
+ "write": true,
37
+ "grep": true,
38
+ "glob": true,
39
+ "iumbtems_oss_scout": true,
40
+ "iumbtems_config": true
41
+ }
42
+ },
7
43
  "alpha-thesis": {
8
44
  "description": "IUMBTEMS Proponent Agent: Gathers corroborating primary sources, benchmark results, and empirical proofs.",
9
45
  "tools": {
@@ -13,7 +49,9 @@
13
49
  "grep": true,
14
50
  "glob": true,
15
51
  "task": true,
16
- "iumbtems_verify_quote": true
52
+ "iumbtems_swarm_research": true,
53
+ "iumbtems_verify_quote": true,
54
+ "iumbtems_config": true
17
55
  }
18
56
  },
19
57
  "beta-redteam": {
@@ -25,6 +63,7 @@
25
63
  "grep": true,
26
64
  "glob": true,
27
65
  "task": true,
66
+ "iumbtems_swarm_research": true,
28
67
  "iumbtems_verify_quote": true
29
68
  }
30
69
  },
@@ -37,12 +76,28 @@
37
76
  "glob": true,
38
77
  "iumbtems_verify_quote": true
39
78
  }
79
+ },
80
+ "brainstormer": {
81
+ "description": "IUMBTEMS Lateral Brainstormer: divergent what-if features, paradigm inversions, falsifiable spikes (never bug fixes).",
82
+ "tools": {
83
+ "read": true,
84
+ "write": true,
85
+ "grep": true,
86
+ "glob": true,
87
+ "iumbtems_brainstorm": true,
88
+ "iumbtems_config": true
89
+ }
40
90
  }
41
91
  },
42
92
  "skills": {
43
93
  "paths": [
44
94
  "./skills/grilling",
45
- "./skills/research-cache"
95
+ "./skills/research_cache",
96
+ "./skills/epistemic_search",
97
+ "./skills/swarm_config",
98
+ "./skills/code_audit",
99
+ "./skills/oss_scout",
100
+ "./skills/brainstorming"
46
101
  ]
47
102
  }
48
103
  }
@@ -11,6 +11,15 @@ import urllib.request
11
11
  import urllib.parse
12
12
  from typing import Dict, Any, List
13
13
 
14
+ # Add parent dir to path so the shared protocol module resolves when this
15
+ # script is launched directly as `python3 config/searxng_mcp.py`.
16
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
17
+ PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
18
+ if PROJECT_ROOT not in sys.path:
19
+ sys.path.insert(0, PROJECT_ROOT)
20
+
21
+ from runner.mcp_protocol import StdioJsonRpcServer, ToolSpec # noqa: E402
22
+
14
23
  SEARXNG_URL = os.environ.get("SEARXNG_URL", "http://localhost:8080")
15
24
 
16
25
  def search_searxng(query: str, categories: str = "general", max_results: int = 10) -> List[Dict[str, Any]]:
@@ -23,7 +32,7 @@ def search_searxng(query: str, categories: str = "general", max_results: int = 1
23
32
  }
24
33
  url = f"{SEARXNG_URL.rstrip('/')}/search?{urllib.parse.urlencode(params)}"
25
34
  req = urllib.request.Request(url, headers={"User-Agent": "EpistemicSwarm/1.0"})
26
-
35
+
27
36
  try:
28
37
  with urllib.request.urlopen(req, timeout=10) as resp:
29
38
  data = json.loads(resp.read().decode("utf-8"))
@@ -41,93 +50,43 @@ def search_searxng(query: str, categories: str = "general", max_results: int = 1
41
50
  return [{"error": f"Failed to query SearXNG at {SEARXNG_URL}: {str(e)}"}]
42
51
 
43
52
 
44
- def handle_json_rpc(line: str):
45
- """Minimal JSON-RPC 2.0 loop for MCP protocol."""
46
- try:
47
- msg = json.loads(line)
48
- except Exception:
49
- return
50
-
51
- msg_id = msg.get("id")
52
- method = msg.get("method")
53
+ def build_server() -> StdioJsonRpcServer:
54
+ def handle_searxng_search(args: Dict[str, Any]) -> str:
55
+ res = search_searxng(
56
+ query=args.get("query", ""),
57
+ categories=args.get("categories", "general"),
58
+ max_results=args.get("max_results", 10),
59
+ )
60
+ return json.dumps(res, indent=2)
53
61
 
54
- if method == "initialize":
55
- response = {
56
- "jsonrpc": "2.0",
57
- "id": msg_id,
58
- "result": {
59
- "protocolVersion": "2024-11-05",
60
- "capabilities": {
61
- "tools": {}
62
+ return StdioJsonRpcServer(
63
+ server_name="searxng-mcp",
64
+ version="1.0.0",
65
+ tools=[
66
+ ToolSpec(
67
+ name="searxng_search",
68
+ description="Execute an unbiased multi-engine metasearch via SearXNG.",
69
+ input_schema={
70
+ "type": "object",
71
+ "properties": {
72
+ "query": {"type": "string", "description": "Search query"},
73
+ "categories": {
74
+ "type": "string",
75
+ "description": "Categories (general, science, it)",
76
+ "default": "general"
77
+ },
78
+ "max_results": {"type": "integer", "default": 10}
79
+ },
80
+ "required": ["query"]
62
81
  },
63
- "serverInfo": {
64
- "name": "searxng-mcp",
65
- "version": "1.0.0"
66
- }
67
- }
68
- }
69
- sys.stdout.write(json.dumps(response) + "\n")
70
- sys.stdout.flush()
71
-
72
- elif method == "tools/list":
73
- response = {
74
- "jsonrpc": "2.0",
75
- "id": msg_id,
76
- "result": {
77
- "tools": [
78
- {
79
- "name": "searxng_search",
80
- "description": "Execute an unbiased multi-engine metasearch via SearXNG.",
81
- "inputSchema": {
82
- "type": "object",
83
- "properties": {
84
- "query": {"type": "string", "description": "Search query"},
85
- "categories": {
86
- "type": "string",
87
- "description": "Categories (general, science, it)",
88
- "default": "general"
89
- },
90
- "max_results": {"type": "integer", "default": 10}
91
- },
92
- "required": ["query"]
93
- }
94
- }
95
- ]
96
- }
97
- }
98
- sys.stdout.write(json.dumps(response) + "\n")
99
- sys.stdout.flush()
100
-
101
- elif method == "tools/call":
102
- params = msg.get("params", {})
103
- tool_name = params.get("name")
104
- args = params.get("arguments", {})
105
-
106
- if tool_name == "searxng_search":
107
- query = args.get("query", "")
108
- cat = args.get("categories", "general")
109
- limit = args.get("max_results", 10)
110
- res = search_searxng(query, categories=cat, max_results=limit)
111
- response = {
112
- "jsonrpc": "2.0",
113
- "id": msg_id,
114
- "result": {
115
- "content": [
116
- {
117
- "type": "text",
118
- "text": json.dumps(res, indent=2)
119
- }
120
- ]
121
- }
122
- }
123
- sys.stdout.write(json.dumps(response) + "\n")
124
- sys.stdout.flush()
82
+ handler=handle_searxng_search,
83
+ )
84
+ ],
85
+ )
125
86
 
126
87
 
127
88
  def main():
128
- for line in sys.stdin:
129
- if line.strip():
130
- handle_json_rpc(line.strip())
89
+ build_server().serve_forever()
131
90
 
132
91
  if __name__ == "__main__":
133
92
  main()