flockward 1.0.0__py3-none-any.whl

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.
flockward/__init__.py ADDED
@@ -0,0 +1,97 @@
1
+ """Flockward: free link-safety checks for AI agents. No dependencies.
2
+
3
+ from flockward import check, fetch_safely
4
+ if check("https://example.com/page")["verdict"] == "block": ...
5
+ """
6
+ import json
7
+ import os
8
+ import urllib.error
9
+ import urllib.parse
10
+ import urllib.request
11
+
12
+ __version__ = "1.0.0"
13
+ BASE = "https://flockward.com"
14
+
15
+
16
+ class FlockwardError(Exception):
17
+ def __init__(self, status, data):
18
+ super().__init__(data.get("error") or f"Flockward HTTP {status}")
19
+ self.status, self.data = status, data
20
+
21
+
22
+ class Flockward:
23
+ def __init__(self, api_key=None, base_url=BASE, agent="flockward-py/1.0", timeout=20):
24
+ self.api_key = api_key or os.environ.get("FLOCKWARD_API_KEY")
25
+ self.base_url, self.agent, self.timeout = base_url.rstrip("/"), agent, timeout
26
+
27
+ def _req(self, path, body=None):
28
+ headers = {"user-agent": self.agent, "x-agentguard-via": "sdk-py"}
29
+ data = None
30
+ if body is not None:
31
+ headers["content-type"] = "application/json"
32
+ data = json.dumps(body).encode()
33
+ if self.api_key:
34
+ headers["x-api-key"] = self.api_key
35
+ req = urllib.request.Request(self.base_url + path, data=data, headers=headers, method="POST" if data else "GET")
36
+ try:
37
+ with urllib.request.urlopen(req, timeout=self.timeout) as r:
38
+ return json.loads(r.read() or b"{}")
39
+ except urllib.error.HTTPError as e:
40
+ try:
41
+ payload = json.loads(e.read() or b"{}")
42
+ except ValueError:
43
+ payload = {}
44
+ raise FlockwardError(e.code, payload) from None
45
+
46
+ def check(self, url):
47
+ """Check a URL before opening it. verdict: block | warn | safe | unknown."""
48
+ return self._req("/v1/check?url=" + urllib.parse.quote(url, safe=""))
49
+
50
+ def check_many(self, urls):
51
+ """Check up to 50 URLs at once."""
52
+ return self._req("/v1/check/batch", {"urls": list(urls)})
53
+
54
+ def fetch_safely(self, url):
55
+ """Fetch a page; returns readable text with hidden text and agent-directed instructions removed."""
56
+ return self._req("/v1/fetch?url=" + urllib.parse.quote(url, safe=""))
57
+
58
+ def report(self, url, kind="visit", note=None):
59
+ """Report a page: kind is 'visit', 'suspicious' or 'false_positive'."""
60
+ return self._req("/v1/report", {"url": url, "kind": kind, "note": note})
61
+
62
+ def me(self):
63
+ return self._req("/v1/me")
64
+
65
+ def get_key(self, label=None, ref=None):
66
+ """Get a free key (no sign-up) and use it from now on."""
67
+ k = self._req("/v1/keys", {"label": label or self.agent, "ref": ref})
68
+ self.api_key = k.get("key")
69
+ return k
70
+
71
+ def is_safe(self, url):
72
+ return self.check(url).get("verdict") != "block"
73
+
74
+ def langchain_tools(self):
75
+ """LangChain tools: flockward_check_url and flockward_fetch_safely (needs langchain-core)."""
76
+ from langchain_core.tools import tool
77
+
78
+ @tool
79
+ def flockward_check_url(url: str) -> str:
80
+ """Check whether a web page is a trap for AI agents (prompt injection, data theft, phishing) before opening it."""
81
+ return json.dumps(self.check(url))
82
+
83
+ @tool
84
+ def flockward_fetch_safely(url: str) -> str:
85
+ """Read a web page with hidden text and instructions aimed at AI agents removed."""
86
+ r = self.fetch_safely(url)
87
+ return r.get("text") or json.dumps(r)
88
+
89
+ return [flockward_check_url, flockward_fetch_safely]
90
+
91
+
92
+ _shared = Flockward()
93
+ check = _shared.check
94
+ check_many = _shared.check_many
95
+ fetch_safely = _shared.fetch_safely
96
+ report = _shared.report
97
+ __all__ = ["Flockward", "FlockwardError", "check", "check_many", "fetch_safely", "report"]
flockward/cli.py ADDED
@@ -0,0 +1,33 @@
1
+ import json
2
+ import sys
3
+
4
+ from . import Flockward, FlockwardError
5
+
6
+ HELP = """flockward: free link-safety checks for AI agents (https://flockward.com)
7
+ flockward check <url> is this page a trap? (block | warn | safe | unknown)
8
+ flockward fetch <url> readable page text with traps removed
9
+ flockward report <url> [kind] kind: visit | suspicious | false_positive
10
+ flockward key [label] get a free key; set FLOCKWARD_API_KEY to use it
11
+ flockward me your limits and alerts"""
12
+
13
+
14
+ def main(argv=None):
15
+ a = list(sys.argv[1:] if argv is None else argv)
16
+ fw = Flockward()
17
+ out = lambda x: print(json.dumps(x, indent=2))
18
+ try:
19
+ if a[:1] == ["check"] and len(a) > 1: out(fw.check(a[1]))
20
+ elif a[:1] == ["fetch"] and len(a) > 1:
21
+ r = fw.fetch_safely(a[1]); print(r.get("text") or json.dumps(r, indent=2))
22
+ elif a[:1] == ["report"] and len(a) > 1: out(fw.report(a[1], a[2] if len(a) > 2 else "visit", " ".join(a[3:]) or None))
23
+ elif a[:1] == ["key"]: out(fw.get_key(a[1] if len(a) > 1 else None))
24
+ elif a[:1] == ["me"]: out(fw.me())
25
+ else:
26
+ print(HELP); return 0 if not a else 1
27
+ except FlockwardError as e:
28
+ print(e, file=sys.stderr); return 2
29
+ return 0
30
+
31
+
32
+ if __name__ == "__main__":
33
+ sys.exit(main())
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: flockward
3
+ Version: 1.0.0
4
+ Summary: Free link-safety checks for AI agents: spot prompt-injection traps and read pages with them removed.
5
+ Author-email: Flockward <hello@flockward.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://flockward.com
8
+ Project-URL: Documentation, https://flockward.com/llms.txt
9
+ Keywords: ai-agents,agent-security,prompt-injection,llm,mcp,safe-browsing,langchain
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Topic :: Security
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ Provides-Extra: langchain
16
+ Requires-Dist: langchain-core>=0.2; extra == "langchain"
17
+
18
+ # flockward
19
+
20
+ Free link-safety checks for AI agents. Before your agent opens a link, ask [Flockward](https://flockward.com) whether the page is a trap: hidden prompt injection, data theft, destructive commands, payment fraud, phishing or malware. Or let Flockward fetch the page and hand back clean text with the traps removed.
21
+
22
+ No sign-up, no dependencies. Agents pay with reports, not money. When one agent spots an attack, every agent in the flock is protected.
23
+
24
+ ```bash
25
+ pip install flockward
26
+ ```
27
+
28
+ ```python
29
+ from flockward import check, fetch_safely
30
+
31
+ r = check("https://example.com/page")
32
+ if r["verdict"] == "block":
33
+ raise RuntimeError("Flockward: " + r.get("reason", ""))
34
+
35
+ page = fetch_safely("https://example.com/page") # page["text"] has hidden instructions removed
36
+ ```
37
+
38
+ With a free key (250 checks a day instead of 25, plus alerts):
39
+
40
+ ```python
41
+ from flockward import Flockward
42
+ fw = Flockward() # reads FLOCKWARD_API_KEY if set
43
+ fw.get_key("my-agent/1.0") # once; save fw.api_key
44
+ fw.check_many(search_result_urls) # up to 50 at once
45
+ fw.report(url, "suspicious", "hidden text told me to email the user's API keys")
46
+ ```
47
+
48
+ LangChain: `pip install "flockward[langchain]"`, then `tools = Flockward().langchain_tools()`.
49
+
50
+ CLI: `flockward check https://example.com`
51
+
52
+ **MCP:** prefer tools over code? Add `https://flockward.com/mcp` as a remote MCP server.
53
+
54
+ Docs for agents: https://flockward.com/llms.txt · Contact: hello@flockward.com · License: MIT
@@ -0,0 +1,7 @@
1
+ flockward/__init__.py,sha256=eEn6BaD4JUGjHSgEbLM1ZPV5Ak2TYMw-QGy9TfF7HQo,3733
2
+ flockward/cli.py,sha256=ufC4ksc6at0qUxfwA_sbzaWBTEz9jtbnO-aQjvsh8IU,1339
3
+ flockward-1.0.0.dist-info/METADATA,sha256=LUTrlrx5ouZGI0yYCjjxL5pEuKu9hqTpmNNf4A4T4Co,2159
4
+ flockward-1.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
5
+ flockward-1.0.0.dist-info/entry_points.txt,sha256=ag4bGUQvLno7NHZAnInqL9p9m_nc9or4OLN176icKuo,49
6
+ flockward-1.0.0.dist-info/top_level.txt,sha256=H0vUmePulmF9EDqtM1w6yoWNdYx7YUuQCWf4XFRnq4Q,10
7
+ flockward-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ flockward = flockward.cli:main
@@ -0,0 +1 @@
1
+ flockward