patent-watch 0.1.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.
- patent_watch-0.1.0/.gitignore +4 -0
- patent_watch-0.1.0/LICENSE +21 -0
- patent_watch-0.1.0/PKG-INFO +67 -0
- patent_watch-0.1.0/README.md +46 -0
- patent_watch-0.1.0/patent_watch.py +166 -0
- patent_watch-0.1.0/pyproject.toml +33 -0
- patent_watch-0.1.0/uv.lock +104 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Calidran
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: patent-watch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Watch a company's US patent applications: office actions, allowances, abandonments, grants, assignments, PTAB petitions — to Slack, email, or a webhook.
|
|
5
|
+
Project-URL: Homepage, https://patentdata.ai
|
|
6
|
+
Project-URL: Source, https://github.com/calidran/patent-watch
|
|
7
|
+
Project-URL: API docs, https://api.patentdata.ai/docs
|
|
8
|
+
Author-email: Calidran <rish@patentdata.ai>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,mcp,office-action,patent-status,patents,uspto
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Legal Industry
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Office/Business
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: click>=8.1
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# patent-watch
|
|
23
|
+
|
|
24
|
+
Watch a company's US patent portfolio and get told when something happens: an office action, a notice of
|
|
25
|
+
allowance, an abandonment, a grant, an assignment, a PTAB petition. Built on the
|
|
26
|
+
[Patent Data API](https://patentdata.ai) (USPTO bulk data, refreshed daily; per-request pricing,
|
|
27
|
+
no seats, redistribution allowed).
|
|
28
|
+
|
|
29
|
+
## If you are an agent
|
|
30
|
+
|
|
31
|
+
You can set yourself up without a human, except for paying:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install patent-watch
|
|
35
|
+
patent-watch signup your-owner@example.com # mints a key, saves it to ~/.patent-watch.json, prints a Stripe link
|
|
36
|
+
# hand the link to your human; $20 = 1,000 requests, credits land in seconds
|
|
37
|
+
patent-watch resolve "Impossible Foods" # → ent_... Impossible Foods Inc. apps=412 patents=180 pending=95
|
|
38
|
+
patent-watch pending ent_...
|
|
39
|
+
patent-watch poll ent_... --slack https://hooks.slack.com/services/... # run daily from cron
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or skip the polling and let the API push to you:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
patent-watch monitor ent_... --webhook https://your-agent.example.com/hooks/patents
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Deliveries carry `X-Signature: sha256=<hmac-sha256(secret, body)>`.
|
|
49
|
+
|
|
50
|
+
## If you use Claude Code
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude mcp add --transport http patent-data https://api.patentdata.ai/mcp --header "Authorization: Bearer $PATENT_API_KEY"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then: *"What's the status of application 17942951 and who owns it?"*
|
|
57
|
+
|
|
58
|
+
## Cost
|
|
59
|
+
|
|
60
|
+
`poll` costs 1 request per pending application per run plus 1 for the list (per 100 rows). A portfolio of 100
|
|
61
|
+
pending applications polled daily is ~3,000 requests/month ≈ $60 in credits, or use `monitor` (1 request per
|
|
62
|
+
delivered event).
|
|
63
|
+
|
|
64
|
+
## Data
|
|
65
|
+
|
|
66
|
+
Every answer carries the USPTO product and file date it came from (`source`) and the API's ingest date (`as_of`).
|
|
67
|
+
USPTO is the source; this tool and the API are derivations.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# patent-watch
|
|
2
|
+
|
|
3
|
+
Watch a company's US patent portfolio and get told when something happens: an office action, a notice of
|
|
4
|
+
allowance, an abandonment, a grant, an assignment, a PTAB petition. Built on the
|
|
5
|
+
[Patent Data API](https://patentdata.ai) (USPTO bulk data, refreshed daily; per-request pricing,
|
|
6
|
+
no seats, redistribution allowed).
|
|
7
|
+
|
|
8
|
+
## If you are an agent
|
|
9
|
+
|
|
10
|
+
You can set yourself up without a human, except for paying:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install patent-watch
|
|
14
|
+
patent-watch signup your-owner@example.com # mints a key, saves it to ~/.patent-watch.json, prints a Stripe link
|
|
15
|
+
# hand the link to your human; $20 = 1,000 requests, credits land in seconds
|
|
16
|
+
patent-watch resolve "Impossible Foods" # → ent_... Impossible Foods Inc. apps=412 patents=180 pending=95
|
|
17
|
+
patent-watch pending ent_...
|
|
18
|
+
patent-watch poll ent_... --slack https://hooks.slack.com/services/... # run daily from cron
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or skip the polling and let the API push to you:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
patent-watch monitor ent_... --webhook https://your-agent.example.com/hooks/patents
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Deliveries carry `X-Signature: sha256=<hmac-sha256(secret, body)>`.
|
|
28
|
+
|
|
29
|
+
## If you use Claude Code
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add --transport http patent-data https://api.patentdata.ai/mcp --header "Authorization: Bearer $PATENT_API_KEY"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then: *"What's the status of application 17942951 and who owns it?"*
|
|
36
|
+
|
|
37
|
+
## Cost
|
|
38
|
+
|
|
39
|
+
`poll` costs 1 request per pending application per run plus 1 for the list (per 100 rows). A portfolio of 100
|
|
40
|
+
pending applications polled daily is ~3,000 requests/month ≈ $60 in credits, or use `monitor` (1 request per
|
|
41
|
+
delivered event).
|
|
42
|
+
|
|
43
|
+
## Data
|
|
44
|
+
|
|
45
|
+
Every answer carries the USPTO product and file date it came from (`source`) and the API's ingest date (`as_of`).
|
|
46
|
+
USPTO is the source; this tool and the API are derivations.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
patent-watch — a small agent over the patent-status API (https://github.com/calidran/patent-api).
|
|
3
|
+
|
|
4
|
+
patent-watch signup you@example.com # gets a key; prints the Stripe link to fund it ($20 = 1,000 requests)
|
|
5
|
+
patent-watch resolve "Impossible Foods" # company name -> entity id + counts
|
|
6
|
+
patent-watch pending ent_... # the entity's pending applications
|
|
7
|
+
patent-watch poll ent_... --slack $WEBHOOK # diff against last run; notify on new events (run from cron)
|
|
8
|
+
patent-watch monitor ent_... --webhook URL # server-side monitor: the API pushes signed events to your URL
|
|
9
|
+
|
|
10
|
+
Config: PATENT_API_KEY (or ~/.patent-watch.json written by `signup`), PATENT_API_URL (default https://api.patentdata.ai).
|
|
11
|
+
State for `poll` lives in ~/.patent-watch-state.json (last seen transaction per application).
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
from datetime import date
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import click
|
|
22
|
+
import httpx
|
|
23
|
+
|
|
24
|
+
CONFIG = Path.home() / ".patent-watch.json"
|
|
25
|
+
STATE = Path.home() / ".patent-watch-state.json"
|
|
26
|
+
EVENTS = ("office_action", "allowance", "abandonment", "grant", "publication", "assignment", "ptab_petition")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def base_url() -> str:
|
|
30
|
+
return os.environ.get("PATENT_API_URL", "").strip() or _config().get("url") or "https://api.patentdata.ai"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _config() -> dict:
|
|
34
|
+
return json.loads(CONFIG.read_text()) if CONFIG.exists() else {}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def api_key() -> str:
|
|
38
|
+
key = os.environ.get("PATENT_API_KEY", "").strip() or _config().get("api_key")
|
|
39
|
+
if not key:
|
|
40
|
+
raise click.ClickException("no API key: run `patent-watch signup <owner-email>` or set PATENT_API_KEY")
|
|
41
|
+
return key
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def client() -> httpx.Client:
|
|
45
|
+
return httpx.Client(base_url=base_url(), headers={"X-API-KEY": api_key()}, timeout=30)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get(c: httpx.Client, path: str, **params) -> dict:
|
|
49
|
+
r = c.get(path, params={k: v for k, v in params.items() if v is not None})
|
|
50
|
+
if r.status_code == 402:
|
|
51
|
+
body = r.json()
|
|
52
|
+
raise click.ClickException(f"key not funded: {body['checkout']} (min ${body['minimum_usd']}); run `patent-watch fund`")
|
|
53
|
+
if r.status_code >= 400:
|
|
54
|
+
raise click.ClickException(f"{r.status_code} {r.text[:300]}")
|
|
55
|
+
return r.json()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@click.group(help=__doc__)
|
|
59
|
+
def cli() -> None:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@cli.command(help="Create an API key for this agent under your (human) email. Prints the funding link.")
|
|
64
|
+
@click.argument("owner_email")
|
|
65
|
+
@click.option("--agent-name", default="patent-watch")
|
|
66
|
+
@click.option("--url", default=None, help="API base URL")
|
|
67
|
+
def signup(owner_email: str, agent_name: str, url: str | None) -> None:
|
|
68
|
+
u = url or base_url()
|
|
69
|
+
r = httpx.post(f"{u}/agent/signup", json={"owner_email": owner_email, "agent_name": agent_name}, timeout=30)
|
|
70
|
+
if r.status_code >= 400:
|
|
71
|
+
raise click.ClickException(f"{r.status_code} {r.text[:300]}")
|
|
72
|
+
data = r.json()
|
|
73
|
+
CONFIG.write_text(json.dumps({"api_key": data["api_key"], "org_id": data["org_id"], "url": u}, indent=1))
|
|
74
|
+
CONFIG.chmod(0o600)
|
|
75
|
+
click.echo(f"saved key to {CONFIG} (org {data['org_id']})")
|
|
76
|
+
fund_link(u, data["api_key"])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def fund_link(u: str, key: str, amount: int = 20) -> None:
|
|
80
|
+
r = httpx.post(f"{u}/agent/checkout-link", json={"amount_usd": amount}, headers={"X-API-KEY": key}, timeout=30)
|
|
81
|
+
if r.status_code == 503:
|
|
82
|
+
click.echo("billing is not configured on this server yet; ask the operator for credits")
|
|
83
|
+
return
|
|
84
|
+
r.raise_for_status()
|
|
85
|
+
click.echo(f"fund it here (${amount} = {amount * 50} requests, no expiry): {r.json()['url']}")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@cli.command(help="Print a Stripe link to add credits.")
|
|
89
|
+
@click.option("--amount", default=20, type=int)
|
|
90
|
+
def fund(amount: int) -> None:
|
|
91
|
+
fund_link(base_url(), api_key(), amount)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@cli.command(help="Resolve a company or inventor name to entity ids.")
|
|
95
|
+
@click.argument("name")
|
|
96
|
+
@click.option("--type", "type_", type=click.Choice(["assignee", "inventor"]), default=None)
|
|
97
|
+
def resolve(name: str, type_: str | None) -> None:
|
|
98
|
+
with client() as c:
|
|
99
|
+
for e in get(c, "/v1/entities/search", q=name, type=type_, limit=10)["entities"]:
|
|
100
|
+
cnt = e["counts"]
|
|
101
|
+
click.echo(f"{e['entity_id']} {e['canonical_name']:50s} apps={cnt['applications']} patents={cnt['patents']} pending={cnt['pending']} ({e['source']})")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@cli.command(help="List an entity's pending applications.")
|
|
105
|
+
@click.argument("entity_id")
|
|
106
|
+
def pending(entity_id: str) -> None:
|
|
107
|
+
with client() as c:
|
|
108
|
+
for a in _all_pending(c, entity_id):
|
|
109
|
+
click.echo(f"{a['application_number']} {a['filing_date']} {a['status']['description'][:45]:45s} {a['title'][:60]}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _all_pending(c: httpx.Client, entity_id: str) -> list[dict]:
|
|
113
|
+
out, cursor = [], None
|
|
114
|
+
while True:
|
|
115
|
+
page = get(c, f"/v1/entities/{entity_id}/applications", status="pending", limit=100, cursor=cursor)
|
|
116
|
+
out += page["applications"]
|
|
117
|
+
cursor = page.get("next_cursor")
|
|
118
|
+
if not cursor:
|
|
119
|
+
return out
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@cli.command(help="Diff each pending application's timeline against the last run and notify on new events. Cron-friendly.")
|
|
123
|
+
@click.argument("entity_id")
|
|
124
|
+
@click.option("--slack", "slack_webhook", default=None, help="Slack incoming-webhook URL")
|
|
125
|
+
@click.option("--since-days", default=30, help="On first run, look back this many days")
|
|
126
|
+
def poll(entity_id: str, slack_webhook: str | None, since_days: int) -> None:
|
|
127
|
+
state = json.loads(STATE.read_text()) if STATE.exists() else {}
|
|
128
|
+
seen: dict[str, str] = state.setdefault(entity_id, {})
|
|
129
|
+
lines = []
|
|
130
|
+
with client() as c:
|
|
131
|
+
for a in _all_pending(c, entity_id):
|
|
132
|
+
n = a["application_number"]
|
|
133
|
+
since = seen.get(n) or (date.fromordinal(date.today().toordinal() - since_days)).isoformat()
|
|
134
|
+
tl = get(c, f"/v1/applications/{n}/transactions", since=since)
|
|
135
|
+
new = [t for t in tl["transactions"] if t["event_type"] in EVENTS and (t["date"] > since or n not in seen)]
|
|
136
|
+
for t in sorted(new, key=lambda t: t["date"]):
|
|
137
|
+
lines.append(f"{t['date']} {t['event_type']:14s} {n} {a['title'][:50]} ({t['code']}: {t['description']})")
|
|
138
|
+
if tl["transactions"]:
|
|
139
|
+
seen[n] = max(t["date"] for t in tl["transactions"])
|
|
140
|
+
elif n not in seen:
|
|
141
|
+
seen[n] = since
|
|
142
|
+
STATE.write_text(json.dumps(state, indent=1))
|
|
143
|
+
if not lines:
|
|
144
|
+
click.echo("no new events")
|
|
145
|
+
return
|
|
146
|
+
text = "\n".join(lines)
|
|
147
|
+
click.echo(text)
|
|
148
|
+
if slack_webhook:
|
|
149
|
+
httpx.post(slack_webhook, json={"text": f"patent-watch {entity_id}:\n```{text}```"}, timeout=30).raise_for_status()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@cli.command(help="Create a server-side monitor: the API POSTs signed events to your webhook the night they appear.")
|
|
153
|
+
@click.argument("entity_id")
|
|
154
|
+
@click.option("--webhook", required=True)
|
|
155
|
+
@click.option("--events", default=",".join(EVENTS), show_default=True)
|
|
156
|
+
def monitor(entity_id: str, webhook: str, events: str) -> None:
|
|
157
|
+
with client() as c:
|
|
158
|
+
r = c.post("/v1/monitors", json={"targets": {"entity_ids": [entity_id]}, "events": events.split(","), "webhook_url": webhook})
|
|
159
|
+
if r.status_code >= 400:
|
|
160
|
+
raise click.ClickException(f"{r.status_code} {r.text[:300]}")
|
|
161
|
+
m = r.json()
|
|
162
|
+
click.echo(f"monitor {m['monitor_id']} created; verify X-Signature with secret: {m['secret']}")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
cli()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "patent-watch"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Watch a company's US patent applications: office actions, allowances, abandonments, grants, assignments, PTAB petitions — to Slack, email, or a webhook."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Calidran", email = "rish@patentdata.ai" }]
|
|
9
|
+
keywords = ["patents", "uspto", "patent-status", "office-action", "mcp", "agents"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Intended Audience :: Legal Industry",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Office/Business",
|
|
16
|
+
]
|
|
17
|
+
requires-python = ">=3.11"
|
|
18
|
+
dependencies = ["httpx>=0.27", "click>=8.1"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://patentdata.ai"
|
|
22
|
+
Source = "https://github.com/calidran/patent-watch"
|
|
23
|
+
"API docs" = "https://api.patentdata.ai/docs"
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
patent-watch = "patent_watch:cli"
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["hatchling"]
|
|
30
|
+
build-backend = "hatchling.build"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["patent_watch.py"]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.11"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "anyio"
|
|
7
|
+
version = "4.15.1"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
dependencies = [
|
|
10
|
+
{ name = "idna" },
|
|
11
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.15'" },
|
|
12
|
+
]
|
|
13
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" }
|
|
14
|
+
wheels = [
|
|
15
|
+
{ url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[[package]]
|
|
19
|
+
name = "certifi"
|
|
20
|
+
version = "2026.7.22"
|
|
21
|
+
source = { registry = "https://pypi.org/simple" }
|
|
22
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" }
|
|
23
|
+
wheels = [
|
|
24
|
+
{ url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[[package]]
|
|
28
|
+
name = "click"
|
|
29
|
+
version = "8.5.0"
|
|
30
|
+
source = { registry = "https://pypi.org/simple" }
|
|
31
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" }
|
|
32
|
+
wheels = [
|
|
33
|
+
{ url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[[package]]
|
|
37
|
+
name = "h11"
|
|
38
|
+
version = "0.16.0"
|
|
39
|
+
source = { registry = "https://pypi.org/simple" }
|
|
40
|
+
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
|
41
|
+
wheels = [
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[package]]
|
|
46
|
+
name = "httpcore"
|
|
47
|
+
version = "1.0.9"
|
|
48
|
+
source = { registry = "https://pypi.org/simple" }
|
|
49
|
+
dependencies = [
|
|
50
|
+
{ name = "certifi" },
|
|
51
|
+
{ name = "h11" },
|
|
52
|
+
]
|
|
53
|
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
|
54
|
+
wheels = [
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[[package]]
|
|
59
|
+
name = "httpx"
|
|
60
|
+
version = "0.28.1"
|
|
61
|
+
source = { registry = "https://pypi.org/simple" }
|
|
62
|
+
dependencies = [
|
|
63
|
+
{ name = "anyio" },
|
|
64
|
+
{ name = "certifi" },
|
|
65
|
+
{ name = "httpcore" },
|
|
66
|
+
{ name = "idna" },
|
|
67
|
+
]
|
|
68
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
|
69
|
+
wheels = [
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[[package]]
|
|
74
|
+
name = "idna"
|
|
75
|
+
version = "3.19"
|
|
76
|
+
source = { registry = "https://pypi.org/simple" }
|
|
77
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" }
|
|
78
|
+
wheels = [
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" },
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[[package]]
|
|
83
|
+
name = "patent-watch"
|
|
84
|
+
version = "0.1.0"
|
|
85
|
+
source = { editable = "." }
|
|
86
|
+
dependencies = [
|
|
87
|
+
{ name = "click" },
|
|
88
|
+
{ name = "httpx" },
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[package.metadata]
|
|
92
|
+
requires-dist = [
|
|
93
|
+
{ name = "click", specifier = ">=8.1" },
|
|
94
|
+
{ name = "httpx", specifier = ">=0.27" },
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "typing-extensions"
|
|
99
|
+
version = "4.16.0"
|
|
100
|
+
source = { registry = "https://pypi.org/simple" }
|
|
101
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
|
102
|
+
wheels = [
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
|
104
|
+
]
|