clearfront 2.5.0__tar.gz → 2.6.0__tar.gz
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.
- {clearfront-2.5.0 → clearfront-2.6.0}/PKG-INFO +28 -28
- {clearfront-2.5.0 → clearfront-2.6.0}/README.md +1 -1
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/agent.py +15 -1
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/mcp_server.py +1 -1
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/web/index.html +58 -5
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/web_server.py +68 -12
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront.egg-info/PKG-INFO +28 -28
- clearfront-2.6.0/clearfront.egg-info/requires.txt +53 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/pyproject.toml +28 -28
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_web_server.py +51 -0
- clearfront-2.5.0/clearfront.egg-info/requires.txt +0 -53
- {clearfront-2.5.0 → clearfront-2.6.0}/LICENSE +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/__init__.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/brightdata.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/cli.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/correlation.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/corroboration.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/exceptions.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/extractors.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/json_output.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/multi_target.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/pdf_report.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/pivot.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/prompts.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/regexes.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/repl.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/serp.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/session_history.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/sponsors.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tool_cache.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/__init__.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/data/NOTICE +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/data/wmn-data-unique.json +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/exceptions.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/generate_dorks.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/scrape_url.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_abuseipdb.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_breach.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_censys.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_crt.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_crypto.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_dns.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_domain.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_dorks_live.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_email.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_emailrep.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_exif.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_exposure.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_footprint.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_github.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_gravatar.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_greynoise.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_harvester.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_hudsonrock.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_ip.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_ip2location.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_maigret.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_paste.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_phone.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_shodan.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_username.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_virustotal.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_wayback.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/search_whois.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/tools/whatsmyname.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront/utils.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront.egg-info/SOURCES.txt +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront.egg-info/dependency_links.txt +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront.egg-info/entry_points.txt +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/clearfront.egg-info/top_level.txt +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/setup.cfg +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_authorized_use.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_brightdata.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_cli_openai.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_cli_output.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_corroboration.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_crt.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_dns.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_exif.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_footprint.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_github_code_search.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_graph_export.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_hudsonrock.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_json_export.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_mcp_authorized_use.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_openai_agent.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_pivot_freetier.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_regexes.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_repl_first_run.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_report_export.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_security.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_serp.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_tool_cache.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_tool_parity.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_tools.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_v2140.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_v240.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_wayback_greynoise.py +0 -0
- {clearfront-2.5.0 → clearfront-2.6.0}/tests/test_web_server_openai.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clearfront
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: AI-powered OSINT agent, MCP server, and CLI. Interactive REPL + 30 tools. Anthropic Claude or local Ollama. For authorized security research use only.
|
|
5
5
|
Author: Scott Martin Anderson
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,50 +23,50 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: mcp>=1.28.1
|
|
26
|
-
Requires-Dist: anthropic>=0.
|
|
27
|
-
Requires-Dist: requests>=2.
|
|
26
|
+
Requires-Dist: anthropic>=0.116.0
|
|
27
|
+
Requires-Dist: requests>=2.34.2
|
|
28
28
|
Requires-Dist: python-whois>=0.9.6
|
|
29
|
-
Requires-Dist: rich>=
|
|
30
|
-
Requires-Dist: prompt_toolkit>=3.0.
|
|
31
|
-
Requires-Dist: python-dotenv>=1.
|
|
29
|
+
Requires-Dist: rich>=15.0.0
|
|
30
|
+
Requires-Dist: prompt_toolkit>=3.0.52
|
|
31
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
32
32
|
Requires-Dist: aiohttp>=3.14.1
|
|
33
33
|
Requires-Dist: dnspython>=2.8.0
|
|
34
|
-
Requires-Dist: ddgs>=9.
|
|
34
|
+
Requires-Dist: ddgs>=9.14.4
|
|
35
35
|
Requires-Dist: fastapi>=0.139.0
|
|
36
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
37
|
-
Requires-Dist: httpx>=0.
|
|
36
|
+
Requires-Dist: uvicorn[standard]>=0.51.0
|
|
37
|
+
Requires-Dist: httpx>=0.28.1
|
|
38
38
|
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: pytest>=
|
|
40
|
-
Requires-Dist: pytest-asyncio>=0
|
|
39
|
+
Requires-Dist: pytest>=9.1.1; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=1.4.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
42
42
|
Requires-Dist: pytest-freezegun>=0.4.2; extra == "dev"
|
|
43
|
-
Requires-Dist: ruff>=0.
|
|
43
|
+
Requires-Dist: ruff>=0.15.20; extra == "dev"
|
|
44
44
|
Provides-Extra: shodan
|
|
45
45
|
Requires-Dist: shodan>=1.31.0; extra == "shodan"
|
|
46
46
|
Provides-Extra: ollama
|
|
47
|
-
Requires-Dist: ollama>=0.
|
|
47
|
+
Requires-Dist: ollama>=0.6.2; extra == "ollama"
|
|
48
48
|
Provides-Extra: openai
|
|
49
|
-
Requires-Dist: openai>=
|
|
49
|
+
Requires-Dist: openai>=2.44.0; extra == "openai"
|
|
50
50
|
Provides-Extra: pdf
|
|
51
|
-
Requires-Dist: reportlab>=
|
|
51
|
+
Requires-Dist: reportlab>=5.0.0; extra == "pdf"
|
|
52
52
|
Provides-Extra: censys
|
|
53
|
-
Requires-Dist: censys-platform>=0.
|
|
53
|
+
Requires-Dist: censys-platform>=0.15.1; extra == "censys"
|
|
54
54
|
Provides-Extra: web
|
|
55
55
|
Requires-Dist: fastapi>=0.139.0; extra == "web"
|
|
56
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
57
|
-
Requires-Dist: sse-starlette>=
|
|
58
|
-
Requires-Dist: python-dotenv>=1.
|
|
59
|
-
Requires-Dist: anthropic>=0.
|
|
60
|
-
Requires-Dist: playwright>=1.
|
|
56
|
+
Requires-Dist: uvicorn[standard]>=0.51.0; extra == "web"
|
|
57
|
+
Requires-Dist: sse-starlette>=3.4.5; extra == "web"
|
|
58
|
+
Requires-Dist: python-dotenv>=1.2.2; extra == "web"
|
|
59
|
+
Requires-Dist: anthropic>=0.116.0; extra == "web"
|
|
60
|
+
Requires-Dist: playwright>=1.61.0; extra == "web"
|
|
61
61
|
Provides-Extra: all
|
|
62
62
|
Requires-Dist: shodan>=1.31.0; extra == "all"
|
|
63
|
-
Requires-Dist: ollama>=0.
|
|
64
|
-
Requires-Dist: openai>=
|
|
65
|
-
Requires-Dist: reportlab>=
|
|
66
|
-
Requires-Dist: censys-platform>=0.
|
|
63
|
+
Requires-Dist: ollama>=0.6.2; extra == "all"
|
|
64
|
+
Requires-Dist: openai>=2.44.0; extra == "all"
|
|
65
|
+
Requires-Dist: reportlab>=5.0.0; extra == "all"
|
|
66
|
+
Requires-Dist: censys-platform>=0.15.1; extra == "all"
|
|
67
67
|
Requires-Dist: fastapi>=0.139.0; extra == "all"
|
|
68
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
69
|
-
Requires-Dist: sse-starlette>=
|
|
68
|
+
Requires-Dist: uvicorn[standard]>=0.51.0; extra == "all"
|
|
69
|
+
Requires-Dist: sse-starlette>=3.4.5; extra == "all"
|
|
70
70
|
Dynamic: license-file
|
|
71
71
|
|
|
72
72
|
<div align="center">
|
|
@@ -81,7 +81,7 @@ Dynamic: license-file
|
|
|
81
81
|
[](https://www.python.org/)
|
|
82
82
|
[](LICENSE)
|
|
83
83
|
[](https://modelcontextprotocol.io/)
|
|
84
|
-
[](CHANGELOG.md)
|
|
85
85
|
|
|
86
86
|
**[clearfront.sh](https://clearfront.sh)** · [Disclaimer](./DISCLAIMER.md)
|
|
87
87
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://www.python.org/)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://modelcontextprotocol.io/)
|
|
13
|
-
[](CHANGELOG.md)
|
|
14
14
|
|
|
15
15
|
**[clearfront.sh](https://clearfront.sh)** · [Disclaimer](./DISCLAIMER.md)
|
|
16
16
|
|
|
@@ -827,12 +827,26 @@ class OISAgent:
|
|
|
827
827
|
)
|
|
828
828
|
try:
|
|
829
829
|
while True:
|
|
830
|
+
# Prompt caching: a breakpoint on the (single) system block caches the
|
|
831
|
+
# system prompt + all tool definitions together (tools render before
|
|
832
|
+
# system), so that large stable prefix is re-billed at ~0.1x on every
|
|
833
|
+
# round instead of full price. Top-level cache_control auto-places a
|
|
834
|
+
# second breakpoint on the last message block, so the growing tool-output
|
|
835
|
+
# history is cached incrementally across rounds. Caching changes billing
|
|
836
|
+
# only; the model sees identical content and produces identical output.
|
|
830
837
|
response = await self.client.messages.create(
|
|
831
838
|
model=self.model,
|
|
832
839
|
max_tokens=_MAX_TOKENS,
|
|
833
|
-
system=
|
|
840
|
+
system=[
|
|
841
|
+
{
|
|
842
|
+
"type": "text",
|
|
843
|
+
"text": SYSTEM_PROMPT,
|
|
844
|
+
"cache_control": {"type": "ephemeral"},
|
|
845
|
+
}
|
|
846
|
+
],
|
|
834
847
|
tools=TOOL_DEFINITIONS, # type: ignore[arg-type]
|
|
835
848
|
messages=ctx.messages, # type: ignore[arg-type]
|
|
849
|
+
cache_control={"type": "ephemeral"},
|
|
836
850
|
)
|
|
837
851
|
if response.stop_reason == "end_turn":
|
|
838
852
|
text = _extract_first_text(response.content)
|
|
@@ -514,15 +514,51 @@
|
|
|
514
514
|
|
|
515
515
|
<!-- right: backend status readout + controls -->
|
|
516
516
|
<div class="flex items-center gap-1.5 flex-none">
|
|
517
|
+
<!-- sweep-depth selector: no-noun chip, opens a level menu whose footer explains the cost -->
|
|
518
|
+
<div class="relative hidden sm:block" @keydown.escape.window="depthOpen = false">
|
|
519
|
+
<button @click="depthOpen = !depthOpen"
|
|
520
|
+
:aria-expanded="depthOpen"
|
|
521
|
+
aria-haspopup="true"
|
|
522
|
+
aria-label="Sweep depth"
|
|
523
|
+
title="Sweep depth: how far each investigation reaches. Trades time and API budget, never analyst quality."
|
|
524
|
+
class="flex items-center gap-1.5 font-mono text-xs px-2.5 py-1 rounded border border-app text-primary bg-transparent hover:border-strong transition-colors cursor-pointer whitespace-nowrap">
|
|
525
|
+
<span x-text="depthCurrent().name"></span>
|
|
526
|
+
<svg width="9" height="9" viewBox="0 0 10 10" fill="none" stroke="currentColor"
|
|
527
|
+
class="transition-transform" :class="depthOpen && 'rotate-180'">
|
|
528
|
+
<path d="M2.5 4 L5 6.5 L7.5 4" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
529
|
+
</svg>
|
|
530
|
+
</button>
|
|
531
|
+
<div x-show="depthOpen" x-cloak
|
|
532
|
+
x-transition:enter="transition ease-out duration-100" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
|
533
|
+
@click.outside="depthOpen = false" @mouseleave="depthHover = null"
|
|
534
|
+
role="radiogroup" aria-label="Sweep depth"
|
|
535
|
+
class="absolute right-0 mt-2 w-60 p-1.5 rounded-lg border border-app bg-surface z-50"
|
|
536
|
+
style="box-shadow:0 8px 24px rgba(0,0,0,0.28);">
|
|
537
|
+
<template x-for="l in depthLevels" :key="l.v">
|
|
538
|
+
<button @click="setDepth(l.v)" @mouseenter="depthHover = l.v" @focus="depthHover = l.v"
|
|
539
|
+
role="radio" :aria-checked="settings.depth === l.v"
|
|
540
|
+
class="flex items-center justify-between gap-2 w-full text-left font-mono text-xs px-2.5 py-2 rounded transition-colors"
|
|
541
|
+
:class="settings.depth === l.v ? 'bg-accent text-bg' : 'text-secondary hover:text-primary'">
|
|
542
|
+
<span x-text="l.name"></span>
|
|
543
|
+
<svg x-show="settings.depth === l.v" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor">
|
|
544
|
+
<path d="M2.5 6.5 L4.8 8.8 L9.5 3.5" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
545
|
+
</svg>
|
|
546
|
+
</button>
|
|
547
|
+
</template>
|
|
548
|
+
<div class="mt-1 pt-2 px-2 border-t border-app text-secondary"
|
|
549
|
+
style="font-size:12px;line-height:1.42;" x-text="depthExplained().desc"></div>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
<div class="hidden sm:block w-px h-5 bg-border mx-0.5"></div>
|
|
517
553
|
<!-- backend status readout -->
|
|
518
554
|
<button @click="showSettings = true"
|
|
519
555
|
:title="aiBackend === 'none' ? 'No AI backend configured, click to set up' : 'AI backend, click to change'"
|
|
520
556
|
class="hidden sm:flex items-center gap-2 font-mono text-xs px-2.5 py-1 rounded border border-app text-secondary bg-transparent hover:text-primary hover:border-strong transition-colors cursor-pointer whitespace-nowrap">
|
|
521
557
|
<span class="w-1.5 h-1.5 rounded-full flex-none"
|
|
522
|
-
:style="aiBackend === 'none' ? 'background-color:var(--red)' : 'background-color:var(--
|
|
523
|
-
<span x-show="aiBackend === 'claude'" x-text="claudeModelLabel"></span>
|
|
524
|
-
<span x-show="aiBackend === 'ollama'">Ollama <span x-text="settings.ollamaModel"></span></span>
|
|
525
|
-
<span x-show="aiBackend === 'openai'">OpenAI <span x-text="settings.openaiModel || 'model'"></span></span>
|
|
558
|
+
:style="aiBackend === 'none' ? 'background-color:var(--red)' : 'background-color:var(--accent)'"></span>
|
|
559
|
+
<span x-show="aiBackend !== 'none' && settings.chatModel === 'claude'" x-text="claudeModelLabel"></span>
|
|
560
|
+
<span x-show="aiBackend !== 'none' && settings.chatModel === 'ollama'">Ollama <span x-text="settings.ollamaModel || 'model'"></span></span>
|
|
561
|
+
<span x-show="aiBackend !== 'none' && settings.chatModel === 'openai'">OpenAI <span x-text="settings.openaiModel || 'model'"></span></span>
|
|
526
562
|
<span x-show="aiBackend === 'none'">Not configured</span>
|
|
527
563
|
</button>
|
|
528
564
|
<div class="hidden sm:block w-px h-5 bg-border mx-0.5"></div>
|
|
@@ -884,7 +920,7 @@ OPEN-SOURCE INTELLIGENCE AUTHORIZED USE · v
|
|
|
884
920
|
<script>
|
|
885
921
|
document.addEventListener('alpine:init', () => {
|
|
886
922
|
Alpine.data('clearfront', () => ({
|
|
887
|
-
version: '2.
|
|
923
|
+
version: '2.6.0',
|
|
888
924
|
booting: false,
|
|
889
925
|
bootLines: [],
|
|
890
926
|
bootAnim: false,
|
|
@@ -919,6 +955,16 @@ document.addEventListener('alpine:init', () => {
|
|
|
919
955
|
setupData: {},
|
|
920
956
|
isLight: false,
|
|
921
957
|
|
|
958
|
+
// Sweep depth: header control for how far each investigation fans out. Trades
|
|
959
|
+
// collection breadth (tool rounds), never analyst quality. Persisted in settings.
|
|
960
|
+
depthOpen: false,
|
|
961
|
+
depthHover: null,
|
|
962
|
+
depthLevels: [
|
|
963
|
+
{ v: 'faster', name: 'Faster', desc: 'Checks fewer sources for a quick sweep. Finishes fast and uses less of your API budget.' },
|
|
964
|
+
{ v: 'balanced', name: 'Balanced', desc: 'Covers the main sources. A middle ground on time and cost.' },
|
|
965
|
+
{ v: 'deeper', name: 'Deeper', desc: 'Follows every lead for the most complete map, but takes longer and uses more of your API budget.' },
|
|
966
|
+
],
|
|
967
|
+
|
|
922
968
|
settings: {
|
|
923
969
|
chatModel: 'claude',
|
|
924
970
|
ollamaModel: 'llama3.2',
|
|
@@ -926,6 +972,7 @@ document.addEventListener('alpine:init', () => {
|
|
|
926
972
|
openaiBaseUrl: '',
|
|
927
973
|
openaiModel: '',
|
|
928
974
|
openaiApiKey: '',
|
|
975
|
+
depth: 'deeper',
|
|
929
976
|
},
|
|
930
977
|
|
|
931
978
|
// one-click self-checks (no input needed), the thesis-core actions
|
|
@@ -1192,6 +1239,11 @@ document.addEventListener('alpine:init', () => {
|
|
|
1192
1239
|
try { localStorage.setItem('clearfront_settings', JSON.stringify(this.settings)); } catch {}
|
|
1193
1240
|
},
|
|
1194
1241
|
|
|
1242
|
+
// sweep-depth control
|
|
1243
|
+
depthCurrent() { return this.depthLevels.find(l => l.v === this.settings.depth) || this.depthLevels[2]; },
|
|
1244
|
+
depthExplained() { return this.depthLevels.find(l => l.v === (this.depthHover || this.settings.depth)) || this.depthCurrent(); },
|
|
1245
|
+
setDepth(v) { this.settings.depth = v; this.saveLocalSettings(); this.depthOpen = false; },
|
|
1246
|
+
|
|
1195
1247
|
// health / tools
|
|
1196
1248
|
async fetchHealth() {
|
|
1197
1249
|
try {
|
|
@@ -1281,6 +1333,7 @@ document.addEventListener('alpine:init', () => {
|
|
|
1281
1333
|
openai_base_url: this.settings.openaiBaseUrl,
|
|
1282
1334
|
openai_model: this.settings.openaiModel,
|
|
1283
1335
|
openai_api_key: this.settings.openaiApiKey,
|
|
1336
|
+
depth: this.settings.depth,
|
|
1284
1337
|
};
|
|
1285
1338
|
|
|
1286
1339
|
let fullText = '';
|
|
@@ -670,6 +670,7 @@ class ChatRequest(BaseModel):
|
|
|
670
670
|
message: str
|
|
671
671
|
history: list[dict] = []
|
|
672
672
|
model: str = "claude"
|
|
673
|
+
depth: str = "deeper"
|
|
673
674
|
ollama_model: str = "llama3.2"
|
|
674
675
|
ollama_host: str = "http://localhost:11434"
|
|
675
676
|
openai_base_url: str = ""
|
|
@@ -800,7 +801,64 @@ _GRAPH_SYSTEM_PROMPT = (
|
|
|
800
801
|
)
|
|
801
802
|
|
|
802
803
|
|
|
803
|
-
|
|
804
|
+
# ---------------------------------------------------------------------------
|
|
805
|
+
# Sweep depth: how far the analyst fans out per investigation. It trades collection
|
|
806
|
+
# breadth (number of tool rounds) and the matching enrichment instruction, never the
|
|
807
|
+
# analyst's reasoning or output quality. "deeper" is the full default fan-out and is
|
|
808
|
+
# byte-identical to the historical prompt (no mode preamble, unchanged enrichment
|
|
809
|
+
# clause); "balanced" and "faster" run fewer rounds with a softened enrichment clause
|
|
810
|
+
# so the report reads deliberate, not truncated at the cap. The Deeper ceiling stays
|
|
811
|
+
# env-tunable via OIS_MAX_TOOL_ROUNDS; the lighter levels are capped relative to it.
|
|
812
|
+
# ---------------------------------------------------------------------------
|
|
813
|
+
_GRAPH_SUFFIX = (
|
|
814
|
+
"Do not emit any graph block yourself; the evidence graph is generated separately "
|
|
815
|
+
"after your report."
|
|
816
|
+
)
|
|
817
|
+
_DEPTH_ENRICH = {
|
|
818
|
+
"deeper": (
|
|
819
|
+
"- Enrich aggressively before finalising. The goal is a complete map of how the target's "
|
|
820
|
+
"footprint connects, not a minimal answer. Every time a pivot surfaces (an email, domain, "
|
|
821
|
+
"company, real name, phone, or an additional handle), expand it with the applicable tools, "
|
|
822
|
+
"then expand the new entities those reveal, chaining outward until the tool budget is "
|
|
823
|
+
"reached. Do not stop at one or two pivots. The more real, connected entities you surface, "
|
|
824
|
+
"the stronger the report and the denser the evidence graph. "
|
|
825
|
+
),
|
|
826
|
+
"balanced": (
|
|
827
|
+
"- Enrich the strongest pivots before finalising. When a high-value pivot surfaces (a "
|
|
828
|
+
"confirmed email, domain, real name, or primary handle), expand it with the applicable "
|
|
829
|
+
"tools, then expand the entities those reveal. Prioritise the connections most likely to "
|
|
830
|
+
"be real over exhaustively chasing every lead, and deliver a solid map of the main "
|
|
831
|
+
"footprint within the tool budget. "
|
|
832
|
+
),
|
|
833
|
+
"faster": (
|
|
834
|
+
"- This is a focused first-look sweep. Run the highest-signal tools for this target type, "
|
|
835
|
+
"expand only the single strongest pivot if one clearly surfaces, and do not chase secondary "
|
|
836
|
+
"leads. Deliver a tight, accurate report from what the priority sources return, and note "
|
|
837
|
+
"that a deeper sweep is available if the user wants the full map. "
|
|
838
|
+
),
|
|
839
|
+
}
|
|
840
|
+
# Mode preamble is added ONLY for the lighter levels so the opening tasking line scales
|
|
841
|
+
# its scope and completion estimate. Deeper gets no preamble, keeping its prompt unchanged.
|
|
842
|
+
_DEPTH_MODE_LINE = {
|
|
843
|
+
"balanced": (
|
|
844
|
+
"COLLECTION MODE: Balanced. Run a moderate sweep of the main sources. Scale the opening "
|
|
845
|
+
"completion estimate down accordingly, not to the full deep-sweep timing."
|
|
846
|
+
),
|
|
847
|
+
"faster": (
|
|
848
|
+
"COLLECTION MODE: Faster. Run a quick focused pass over priority sources. Keep the opening "
|
|
849
|
+
"completion estimate short and do not promise mapping every reachable data point."
|
|
850
|
+
),
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
def _depth_rounds(depth: str) -> int:
|
|
855
|
+
"""Tool-round ceiling for a depth level. Deeper stays env-tunable (default 12); the
|
|
856
|
+
lighter levels are capped below it and never exceed it."""
|
|
857
|
+
ceiling = int(os.environ.get("OIS_MAX_TOOL_ROUNDS", "12"))
|
|
858
|
+
return {"faster": min(4, ceiling), "balanced": min(8, ceiling)}.get(depth, ceiling)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
async def _stream_claude(messages: list[dict], depth: str = "deeper") -> AsyncIterator[dict]:
|
|
804
862
|
"""Yield SSE event dicts while running an agentic Claude loop with tool_use."""
|
|
805
863
|
try:
|
|
806
864
|
import anthropic as _anthropic
|
|
@@ -816,12 +874,13 @@ async def _stream_claude(messages: list[dict]) -> AsyncIterator[dict]:
|
|
|
816
874
|
yield {"type": "error", "message": "ANTHROPIC_API_KEY not set."}
|
|
817
875
|
return
|
|
818
876
|
|
|
877
|
+
depth = depth if depth in _DEPTH_ENRICH else "deeper"
|
|
819
878
|
client = _anthropic.AsyncAnthropic(api_key=api_key)
|
|
820
879
|
msgs = list(messages)
|
|
821
880
|
# Tool-calling rounds drive how far the analyst fans out across pivots, which is what
|
|
822
881
|
# makes the evidence graph dense (more enriched entities = more nodes and connectors).
|
|
823
|
-
#
|
|
824
|
-
_MAX_TOOL_ROUNDS =
|
|
882
|
+
# Scaled by the selected sweep depth; the Deeper ceiling stays env-tunable.
|
|
883
|
+
_MAX_TOOL_ROUNDS = _depth_rounds(depth)
|
|
825
884
|
_tool_rounds = 0
|
|
826
885
|
tool_ran = False
|
|
827
886
|
# Memoize (tool, args) for this one investigation so the multi-round loop does not
|
|
@@ -830,7 +889,9 @@ async def _stream_claude(messages: list[dict]) -> AsyncIterator[dict]:
|
|
|
830
889
|
# Cross-tool corroboration for this one investigation (additive notes only).
|
|
831
890
|
corroboration_ledger = CorroborationLedger()
|
|
832
891
|
|
|
833
|
-
|
|
892
|
+
# Deeper gets no preamble (prompt unchanged); lighter levels get a mode line up top.
|
|
893
|
+
_preamble = f"{_DEPTH_MODE_LINE[depth]}\n\n" if depth in _DEPTH_MODE_LINE else ""
|
|
894
|
+
system_prompt = _preamble + (
|
|
834
895
|
"You are CLEARFRONT, an open-source intelligence (OSINT) analyst. You write to the analytic "
|
|
835
896
|
"tradecraft of the intelligence community, the standard behind platforms like Palantir "
|
|
836
897
|
"Gotham. When the user gives a target, use the available tools to gather intelligence, then "
|
|
@@ -899,13 +960,8 @@ async def _stream_claude(messages: list[dict]) -> AsyncIterator[dict]:
|
|
|
899
960
|
"'- Sherlock: 8 accounts verified. Reliability: high.', "
|
|
900
961
|
"'- Pastebin: unavailable (network error).'\n"
|
|
901
962
|
"- Use '- ' for every bullet. Never leave a blank line between consecutive bullets.\n\n"
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
"company, real name, phone, or an additional handle), expand it with the applicable tools, "
|
|
905
|
-
"then expand the new entities those reveal, chaining outward until the tool budget is "
|
|
906
|
-
"reached. Do not stop at one or two pivots. The more real, connected entities you surface, "
|
|
907
|
-
"the stronger the report and the denser the evidence graph. Do not emit any graph block "
|
|
908
|
-
"yourself; the evidence graph is generated separately after your report."
|
|
963
|
+
+ _DEPTH_ENRICH[depth]
|
|
964
|
+
+ _GRAPH_SUFFIX
|
|
909
965
|
)
|
|
910
966
|
|
|
911
967
|
while True:
|
|
@@ -1722,7 +1778,7 @@ def create_app(*, host_guard: bool = False) -> FastAPI:
|
|
|
1722
1778
|
elif backend == "ollama":
|
|
1723
1779
|
gen = _stream_ollama(messages, req.ollama_host, req.ollama_model)
|
|
1724
1780
|
else:
|
|
1725
|
-
gen = _stream_claude(messages)
|
|
1781
|
+
gen = _stream_claude(messages, depth=req.depth)
|
|
1726
1782
|
|
|
1727
1783
|
async for event in gen:
|
|
1728
1784
|
yield f"data: {json.dumps(event)}\n\n"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clearfront
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: AI-powered OSINT agent, MCP server, and CLI. Interactive REPL + 30 tools. Anthropic Claude or local Ollama. For authorized security research use only.
|
|
5
5
|
Author: Scott Martin Anderson
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,50 +23,50 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: mcp>=1.28.1
|
|
26
|
-
Requires-Dist: anthropic>=0.
|
|
27
|
-
Requires-Dist: requests>=2.
|
|
26
|
+
Requires-Dist: anthropic>=0.116.0
|
|
27
|
+
Requires-Dist: requests>=2.34.2
|
|
28
28
|
Requires-Dist: python-whois>=0.9.6
|
|
29
|
-
Requires-Dist: rich>=
|
|
30
|
-
Requires-Dist: prompt_toolkit>=3.0.
|
|
31
|
-
Requires-Dist: python-dotenv>=1.
|
|
29
|
+
Requires-Dist: rich>=15.0.0
|
|
30
|
+
Requires-Dist: prompt_toolkit>=3.0.52
|
|
31
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
32
32
|
Requires-Dist: aiohttp>=3.14.1
|
|
33
33
|
Requires-Dist: dnspython>=2.8.0
|
|
34
|
-
Requires-Dist: ddgs>=9.
|
|
34
|
+
Requires-Dist: ddgs>=9.14.4
|
|
35
35
|
Requires-Dist: fastapi>=0.139.0
|
|
36
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
37
|
-
Requires-Dist: httpx>=0.
|
|
36
|
+
Requires-Dist: uvicorn[standard]>=0.51.0
|
|
37
|
+
Requires-Dist: httpx>=0.28.1
|
|
38
38
|
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: pytest>=
|
|
40
|
-
Requires-Dist: pytest-asyncio>=0
|
|
39
|
+
Requires-Dist: pytest>=9.1.1; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=1.4.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
42
42
|
Requires-Dist: pytest-freezegun>=0.4.2; extra == "dev"
|
|
43
|
-
Requires-Dist: ruff>=0.
|
|
43
|
+
Requires-Dist: ruff>=0.15.20; extra == "dev"
|
|
44
44
|
Provides-Extra: shodan
|
|
45
45
|
Requires-Dist: shodan>=1.31.0; extra == "shodan"
|
|
46
46
|
Provides-Extra: ollama
|
|
47
|
-
Requires-Dist: ollama>=0.
|
|
47
|
+
Requires-Dist: ollama>=0.6.2; extra == "ollama"
|
|
48
48
|
Provides-Extra: openai
|
|
49
|
-
Requires-Dist: openai>=
|
|
49
|
+
Requires-Dist: openai>=2.44.0; extra == "openai"
|
|
50
50
|
Provides-Extra: pdf
|
|
51
|
-
Requires-Dist: reportlab>=
|
|
51
|
+
Requires-Dist: reportlab>=5.0.0; extra == "pdf"
|
|
52
52
|
Provides-Extra: censys
|
|
53
|
-
Requires-Dist: censys-platform>=0.
|
|
53
|
+
Requires-Dist: censys-platform>=0.15.1; extra == "censys"
|
|
54
54
|
Provides-Extra: web
|
|
55
55
|
Requires-Dist: fastapi>=0.139.0; extra == "web"
|
|
56
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
57
|
-
Requires-Dist: sse-starlette>=
|
|
58
|
-
Requires-Dist: python-dotenv>=1.
|
|
59
|
-
Requires-Dist: anthropic>=0.
|
|
60
|
-
Requires-Dist: playwright>=1.
|
|
56
|
+
Requires-Dist: uvicorn[standard]>=0.51.0; extra == "web"
|
|
57
|
+
Requires-Dist: sse-starlette>=3.4.5; extra == "web"
|
|
58
|
+
Requires-Dist: python-dotenv>=1.2.2; extra == "web"
|
|
59
|
+
Requires-Dist: anthropic>=0.116.0; extra == "web"
|
|
60
|
+
Requires-Dist: playwright>=1.61.0; extra == "web"
|
|
61
61
|
Provides-Extra: all
|
|
62
62
|
Requires-Dist: shodan>=1.31.0; extra == "all"
|
|
63
|
-
Requires-Dist: ollama>=0.
|
|
64
|
-
Requires-Dist: openai>=
|
|
65
|
-
Requires-Dist: reportlab>=
|
|
66
|
-
Requires-Dist: censys-platform>=0.
|
|
63
|
+
Requires-Dist: ollama>=0.6.2; extra == "all"
|
|
64
|
+
Requires-Dist: openai>=2.44.0; extra == "all"
|
|
65
|
+
Requires-Dist: reportlab>=5.0.0; extra == "all"
|
|
66
|
+
Requires-Dist: censys-platform>=0.15.1; extra == "all"
|
|
67
67
|
Requires-Dist: fastapi>=0.139.0; extra == "all"
|
|
68
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
69
|
-
Requires-Dist: sse-starlette>=
|
|
68
|
+
Requires-Dist: uvicorn[standard]>=0.51.0; extra == "all"
|
|
69
|
+
Requires-Dist: sse-starlette>=3.4.5; extra == "all"
|
|
70
70
|
Dynamic: license-file
|
|
71
71
|
|
|
72
72
|
<div align="center">
|
|
@@ -81,7 +81,7 @@ Dynamic: license-file
|
|
|
81
81
|
[](https://www.python.org/)
|
|
82
82
|
[](LICENSE)
|
|
83
83
|
[](https://modelcontextprotocol.io/)
|
|
84
|
-
[](CHANGELOG.md)
|
|
85
85
|
|
|
86
86
|
**[clearfront.sh](https://clearfront.sh)** · [Disclaimer](./DISCLAIMER.md)
|
|
87
87
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
mcp>=1.28.1
|
|
2
|
+
anthropic>=0.116.0
|
|
3
|
+
requests>=2.34.2
|
|
4
|
+
python-whois>=0.9.6
|
|
5
|
+
rich>=15.0.0
|
|
6
|
+
prompt_toolkit>=3.0.52
|
|
7
|
+
python-dotenv>=1.2.2
|
|
8
|
+
aiohttp>=3.14.1
|
|
9
|
+
dnspython>=2.8.0
|
|
10
|
+
ddgs>=9.14.4
|
|
11
|
+
fastapi>=0.139.0
|
|
12
|
+
uvicorn[standard]>=0.51.0
|
|
13
|
+
httpx>=0.28.1
|
|
14
|
+
|
|
15
|
+
[all]
|
|
16
|
+
shodan>=1.31.0
|
|
17
|
+
ollama>=0.6.2
|
|
18
|
+
openai>=2.44.0
|
|
19
|
+
reportlab>=5.0.0
|
|
20
|
+
censys-platform>=0.15.1
|
|
21
|
+
fastapi>=0.139.0
|
|
22
|
+
uvicorn[standard]>=0.51.0
|
|
23
|
+
sse-starlette>=3.4.5
|
|
24
|
+
|
|
25
|
+
[censys]
|
|
26
|
+
censys-platform>=0.15.1
|
|
27
|
+
|
|
28
|
+
[dev]
|
|
29
|
+
pytest>=9.1.1
|
|
30
|
+
pytest-asyncio>=1.4.0
|
|
31
|
+
pytest-cov>=7.1.0
|
|
32
|
+
pytest-freezegun>=0.4.2
|
|
33
|
+
ruff>=0.15.20
|
|
34
|
+
|
|
35
|
+
[ollama]
|
|
36
|
+
ollama>=0.6.2
|
|
37
|
+
|
|
38
|
+
[openai]
|
|
39
|
+
openai>=2.44.0
|
|
40
|
+
|
|
41
|
+
[pdf]
|
|
42
|
+
reportlab>=5.0.0
|
|
43
|
+
|
|
44
|
+
[shodan]
|
|
45
|
+
shodan>=1.31.0
|
|
46
|
+
|
|
47
|
+
[web]
|
|
48
|
+
fastapi>=0.139.0
|
|
49
|
+
uvicorn[standard]>=0.51.0
|
|
50
|
+
sse-starlette>=3.4.5
|
|
51
|
+
python-dotenv>=1.2.2
|
|
52
|
+
anthropic>=0.116.0
|
|
53
|
+
playwright>=1.61.0
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=83.0.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "clearfront"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6.0"
|
|
8
8
|
description = "AI-powered OSINT agent, MCP server, and CLI. Interactive REPL + 30 tools. Anthropic Claude or local Ollama. For authorized security research use only."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -31,18 +31,18 @@ classifiers = [
|
|
|
31
31
|
]
|
|
32
32
|
dependencies = [
|
|
33
33
|
"mcp>=1.28.1",
|
|
34
|
-
"anthropic>=0.
|
|
35
|
-
"requests>=2.
|
|
34
|
+
"anthropic>=0.116.0",
|
|
35
|
+
"requests>=2.34.2",
|
|
36
36
|
"python-whois>=0.9.6",
|
|
37
|
-
"rich>=
|
|
38
|
-
"prompt_toolkit>=3.0.
|
|
39
|
-
"python-dotenv>=1.
|
|
37
|
+
"rich>=15.0.0",
|
|
38
|
+
"prompt_toolkit>=3.0.52",
|
|
39
|
+
"python-dotenv>=1.2.2",
|
|
40
40
|
"aiohttp>=3.14.1",
|
|
41
41
|
"dnspython>=2.8.0",
|
|
42
|
-
"ddgs>=9.
|
|
42
|
+
"ddgs>=9.14.4",
|
|
43
43
|
"fastapi>=0.139.0",
|
|
44
|
-
"uvicorn[standard]>=0.
|
|
45
|
-
"httpx>=0.
|
|
44
|
+
"uvicorn[standard]>=0.51.0",
|
|
45
|
+
"httpx>=0.28.1",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
48
|
[project.urls]
|
|
@@ -56,44 +56,44 @@ clearfront-mcp = "clearfront.mcp_server:main"
|
|
|
56
56
|
|
|
57
57
|
[project.optional-dependencies]
|
|
58
58
|
dev = [
|
|
59
|
-
"pytest>=
|
|
60
|
-
"pytest-asyncio>=0
|
|
59
|
+
"pytest>=9.1.1",
|
|
60
|
+
"pytest-asyncio>=1.4.0",
|
|
61
61
|
"pytest-cov>=7.1.0",
|
|
62
62
|
"pytest-freezegun>=0.4.2",
|
|
63
|
-
"ruff>=0.
|
|
63
|
+
"ruff>=0.15.20",
|
|
64
64
|
]
|
|
65
65
|
shodan = [
|
|
66
66
|
"shodan>=1.31.0",
|
|
67
67
|
]
|
|
68
68
|
ollama = [
|
|
69
|
-
"ollama>=0.
|
|
69
|
+
"ollama>=0.6.2",
|
|
70
70
|
]
|
|
71
71
|
openai = [
|
|
72
|
-
"openai>=
|
|
72
|
+
"openai>=2.44.0",
|
|
73
73
|
]
|
|
74
74
|
pdf = [
|
|
75
|
-
"reportlab>=
|
|
75
|
+
"reportlab>=5.0.0",
|
|
76
76
|
]
|
|
77
77
|
censys = [
|
|
78
|
-
"censys-platform>=0.
|
|
78
|
+
"censys-platform>=0.15.1",
|
|
79
79
|
]
|
|
80
80
|
web = [
|
|
81
81
|
"fastapi>=0.139.0",
|
|
82
|
-
"uvicorn[standard]>=0.
|
|
83
|
-
"sse-starlette>=
|
|
84
|
-
"python-dotenv>=1.
|
|
85
|
-
"anthropic>=0.
|
|
86
|
-
"playwright>=1.
|
|
82
|
+
"uvicorn[standard]>=0.51.0",
|
|
83
|
+
"sse-starlette>=3.4.5",
|
|
84
|
+
"python-dotenv>=1.2.2",
|
|
85
|
+
"anthropic>=0.116.0",
|
|
86
|
+
"playwright>=1.61.0",
|
|
87
87
|
]
|
|
88
88
|
all = [
|
|
89
89
|
"shodan>=1.31.0",
|
|
90
|
-
"ollama>=0.
|
|
91
|
-
"openai>=
|
|
92
|
-
"reportlab>=
|
|
93
|
-
"censys-platform>=0.
|
|
90
|
+
"ollama>=0.6.2",
|
|
91
|
+
"openai>=2.44.0",
|
|
92
|
+
"reportlab>=5.0.0",
|
|
93
|
+
"censys-platform>=0.15.1",
|
|
94
94
|
"fastapi>=0.139.0",
|
|
95
|
-
"uvicorn[standard]>=0.
|
|
96
|
-
"sse-starlette>=
|
|
95
|
+
"uvicorn[standard]>=0.51.0",
|
|
96
|
+
"sse-starlette>=3.4.5",
|
|
97
97
|
]
|
|
98
98
|
|
|
99
99
|
[tool.setuptools.packages.find]
|
|
@@ -493,3 +493,54 @@ class TestRunToolSelfLookup:
|
|
|
493
493
|
assert by_name["search_ip"]["input_schema"]["required"] == []
|
|
494
494
|
assert by_name["search_exposure"]["input_schema"]["required"] == []
|
|
495
495
|
assert by_name["search_shodan"]["input_schema"]["required"] == ["input"]
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
# ---------------------------------------------------------------------------
|
|
499
|
+
# Sweep-depth control
|
|
500
|
+
# ---------------------------------------------------------------------------
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
class TestSweepDepth:
|
|
504
|
+
def test_round_caps_per_level(self):
|
|
505
|
+
from clearfront.web_server import _depth_rounds
|
|
506
|
+
|
|
507
|
+
assert _depth_rounds("faster") == 4
|
|
508
|
+
assert _depth_rounds("balanced") == 8
|
|
509
|
+
assert _depth_rounds("deeper") == 12
|
|
510
|
+
|
|
511
|
+
def test_unknown_depth_falls_back_to_deeper(self):
|
|
512
|
+
from clearfront.web_server import _depth_rounds
|
|
513
|
+
|
|
514
|
+
assert _depth_rounds("bogus") == _depth_rounds("deeper")
|
|
515
|
+
|
|
516
|
+
def test_env_override_sets_deeper_ceiling_and_caps_lighter_levels(self, monkeypatch):
|
|
517
|
+
from clearfront.web_server import _depth_rounds
|
|
518
|
+
|
|
519
|
+
monkeypatch.setenv("OIS_MAX_TOOL_ROUNDS", "6")
|
|
520
|
+
assert _depth_rounds("deeper") == 6
|
|
521
|
+
# lighter levels are capped below the ceiling, never above it
|
|
522
|
+
assert _depth_rounds("balanced") == 6
|
|
523
|
+
assert _depth_rounds("faster") == 4
|
|
524
|
+
|
|
525
|
+
def test_deeper_prompt_unchanged(self):
|
|
526
|
+
# Deeper must stay byte-identical to the historical prompt: no mode preamble,
|
|
527
|
+
# and its enrichment clause plus the graph suffix reproduce the original paragraph.
|
|
528
|
+
from clearfront.web_server import _DEPTH_ENRICH, _DEPTH_MODE_LINE, _GRAPH_SUFFIX
|
|
529
|
+
|
|
530
|
+
assert "deeper" not in _DEPTH_MODE_LINE
|
|
531
|
+
original = (
|
|
532
|
+
"- Enrich aggressively before finalising. The goal is a complete map of how the "
|
|
533
|
+
"target's footprint connects, not a minimal answer. Every time a pivot surfaces "
|
|
534
|
+
"(an email, domain, company, real name, phone, or an additional handle), expand "
|
|
535
|
+
"it with the applicable tools, then expand the new entities those reveal, chaining "
|
|
536
|
+
"outward until the tool budget is reached. Do not stop at one or two pivots. The "
|
|
537
|
+
"more real, connected entities you surface, the stronger the report and the denser "
|
|
538
|
+
"the evidence graph. Do not emit any graph block yourself; the evidence graph is "
|
|
539
|
+
"generated separately after your report."
|
|
540
|
+
)
|
|
541
|
+
assert _DEPTH_ENRICH["deeper"] + _GRAPH_SUFFIX == original
|
|
542
|
+
|
|
543
|
+
def test_chat_request_defaults_to_deeper(self):
|
|
544
|
+
from clearfront.web_server import ChatRequest
|
|
545
|
+
|
|
546
|
+
assert ChatRequest(message="hi").depth == "deeper"
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
mcp>=1.28.1
|
|
2
|
-
anthropic>=0.40.0
|
|
3
|
-
requests>=2.31.0
|
|
4
|
-
python-whois>=0.9.6
|
|
5
|
-
rich>=13.0.0
|
|
6
|
-
prompt_toolkit>=3.0.0
|
|
7
|
-
python-dotenv>=1.0.0
|
|
8
|
-
aiohttp>=3.14.1
|
|
9
|
-
dnspython>=2.8.0
|
|
10
|
-
ddgs>=9.0.0
|
|
11
|
-
fastapi>=0.139.0
|
|
12
|
-
uvicorn[standard]>=0.29.0
|
|
13
|
-
httpx>=0.27.0
|
|
14
|
-
|
|
15
|
-
[all]
|
|
16
|
-
shodan>=1.31.0
|
|
17
|
-
ollama>=0.3.0
|
|
18
|
-
openai>=1.40.0
|
|
19
|
-
reportlab>=4.0.0
|
|
20
|
-
censys-platform>=0.14.0
|
|
21
|
-
fastapi>=0.139.0
|
|
22
|
-
uvicorn[standard]>=0.29.0
|
|
23
|
-
sse-starlette>=1.8.0
|
|
24
|
-
|
|
25
|
-
[censys]
|
|
26
|
-
censys-platform>=0.14.0
|
|
27
|
-
|
|
28
|
-
[dev]
|
|
29
|
-
pytest>=7.0
|
|
30
|
-
pytest-asyncio>=0.21
|
|
31
|
-
pytest-cov>=7.1.0
|
|
32
|
-
pytest-freezegun>=0.4.2
|
|
33
|
-
ruff>=0.1.0
|
|
34
|
-
|
|
35
|
-
[ollama]
|
|
36
|
-
ollama>=0.3.0
|
|
37
|
-
|
|
38
|
-
[openai]
|
|
39
|
-
openai>=1.40.0
|
|
40
|
-
|
|
41
|
-
[pdf]
|
|
42
|
-
reportlab>=4.0.0
|
|
43
|
-
|
|
44
|
-
[shodan]
|
|
45
|
-
shodan>=1.31.0
|
|
46
|
-
|
|
47
|
-
[web]
|
|
48
|
-
fastapi>=0.139.0
|
|
49
|
-
uvicorn[standard]>=0.29.0
|
|
50
|
-
sse-starlette>=1.8.0
|
|
51
|
-
python-dotenv>=1.0.0
|
|
52
|
-
anthropic>=0.25.0
|
|
53
|
-
playwright>=1.40.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|