stackresolve 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.
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: stackresolve
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the StackResolve API (AgentReady + CompanyData).
|
|
5
|
+
Project-URL: Homepage, https://stackresolve.dev
|
|
6
|
+
Author: StackResolve
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: agent,agentready,companydata,mcp,sdk,stackresolve
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Requires-Dist: httpx>=0.24
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# stackresolve
|
|
14
|
+
|
|
15
|
+
Official Python SDK for the [StackResolve](https://stackresolve.dev) API.
|
|
16
|
+
|
|
17
|
+
StackResolve helps agents discover, evaluate, select, install, and use software
|
|
18
|
+
(AgentReady), and compresses web research into structured company data calls
|
|
19
|
+
(CompanyData). This SDK is a thin wrapper over the live REST API.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install stackresolve
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requires Python 3.9+. Depends on `httpx`.
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from stackresolve import StackResolve
|
|
33
|
+
|
|
34
|
+
sr = StackResolve(api_key="ar_...") # or set STACKRESOLVE_API_KEY
|
|
35
|
+
|
|
36
|
+
# 1. Audit a domain: AgentReady scores, facts, and issues.
|
|
37
|
+
report = sr.audit("stripe.com")
|
|
38
|
+
print(report["scores"]["agentready"], report["issues"])
|
|
39
|
+
|
|
40
|
+
# 2. Structured company data.
|
|
41
|
+
company = sr.get_company("vercel.com")
|
|
42
|
+
print(company["name"], company.get("description"))
|
|
43
|
+
|
|
44
|
+
# 3. Task -> ranked, agent-ready tools.
|
|
45
|
+
tools = sr.find_tools("send transactional email from a Python service")
|
|
46
|
+
print(tools["results"])
|
|
47
|
+
|
|
48
|
+
# 4. Run an Agent Discovery check (needs an API key).
|
|
49
|
+
discovery = sr.run_discovery("firecrawl")
|
|
50
|
+
print(discovery)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
Get an API key at [stackresolve.dev](https://stackresolve.dev). Pass it to the
|
|
56
|
+
constructor, or set the `STACKRESOLVE_API_KEY` environment variable:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
sr = StackResolve(api_key="ar_...")
|
|
60
|
+
# or, reading STACKRESOLVE_API_KEY from the environment:
|
|
61
|
+
sr = StackResolve()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The key is sent as the `x-api-key` header on every request. Public endpoints
|
|
65
|
+
(audit, search, profiles, company data, ...) work without a key, subject to an
|
|
66
|
+
anonymous rate limit. Gated endpoints (monitors, usage, discovery runs) need one.
|
|
67
|
+
|
|
68
|
+
Point the client at a different host with `base_url` or the
|
|
69
|
+
`STACKRESOLVE_BASE_URL` environment variable:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
sr = StackResolve(api_key="ar_...", base_url="https://api.stackresolve.dev")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Errors
|
|
76
|
+
|
|
77
|
+
Any non-2xx response raises a `StackResolveError` carrying the HTTP `status` and
|
|
78
|
+
the parsed `body`:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from stackresolve import StackResolve, StackResolveError
|
|
82
|
+
|
|
83
|
+
sr = StackResolve()
|
|
84
|
+
try:
|
|
85
|
+
sr.get_usage()
|
|
86
|
+
except StackResolveError as err:
|
|
87
|
+
print(err.status, err.body)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Methods
|
|
91
|
+
|
|
92
|
+
AgentReady:
|
|
93
|
+
`audit(domain)`, `find_tools(task)`, `search(query, requirements=None)`,
|
|
94
|
+
`compare(slugs)`, `get_profile(slug)`, `list_registry(...)`, `get_categories()`,
|
|
95
|
+
`get_category(slug)`, `get_discovery(slug)`, `run_discovery(slug)`,
|
|
96
|
+
`get_score_history(slug)`, `generate(domain, openapi_url=None)`,
|
|
97
|
+
`deploy(domain, openapi_url=None)`.
|
|
98
|
+
|
|
99
|
+
CompanyData:
|
|
100
|
+
`get_company(domain)`, `get_pricing(domain)`, `get_competitors(domain)`,
|
|
101
|
+
`research(domain, question=None)`, `compare_companies(domains)`.
|
|
102
|
+
|
|
103
|
+
Monitors + account (API key required):
|
|
104
|
+
`list_monitors()`, `add_monitor(slug, ...)`, `run_monitor_now(slug)`,
|
|
105
|
+
`get_usage()`, `get_my_profile()`.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# stackresolve
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [StackResolve](https://stackresolve.dev) API.
|
|
4
|
+
|
|
5
|
+
StackResolve helps agents discover, evaluate, select, install, and use software
|
|
6
|
+
(AgentReady), and compresses web research into structured company data calls
|
|
7
|
+
(CompanyData). This SDK is a thin wrapper over the live REST API.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install stackresolve
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Python 3.9+. Depends on `httpx`.
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from stackresolve import StackResolve
|
|
21
|
+
|
|
22
|
+
sr = StackResolve(api_key="ar_...") # or set STACKRESOLVE_API_KEY
|
|
23
|
+
|
|
24
|
+
# 1. Audit a domain: AgentReady scores, facts, and issues.
|
|
25
|
+
report = sr.audit("stripe.com")
|
|
26
|
+
print(report["scores"]["agentready"], report["issues"])
|
|
27
|
+
|
|
28
|
+
# 2. Structured company data.
|
|
29
|
+
company = sr.get_company("vercel.com")
|
|
30
|
+
print(company["name"], company.get("description"))
|
|
31
|
+
|
|
32
|
+
# 3. Task -> ranked, agent-ready tools.
|
|
33
|
+
tools = sr.find_tools("send transactional email from a Python service")
|
|
34
|
+
print(tools["results"])
|
|
35
|
+
|
|
36
|
+
# 4. Run an Agent Discovery check (needs an API key).
|
|
37
|
+
discovery = sr.run_discovery("firecrawl")
|
|
38
|
+
print(discovery)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Authentication
|
|
42
|
+
|
|
43
|
+
Get an API key at [stackresolve.dev](https://stackresolve.dev). Pass it to the
|
|
44
|
+
constructor, or set the `STACKRESOLVE_API_KEY` environment variable:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
sr = StackResolve(api_key="ar_...")
|
|
48
|
+
# or, reading STACKRESOLVE_API_KEY from the environment:
|
|
49
|
+
sr = StackResolve()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The key is sent as the `x-api-key` header on every request. Public endpoints
|
|
53
|
+
(audit, search, profiles, company data, ...) work without a key, subject to an
|
|
54
|
+
anonymous rate limit. Gated endpoints (monitors, usage, discovery runs) need one.
|
|
55
|
+
|
|
56
|
+
Point the client at a different host with `base_url` or the
|
|
57
|
+
`STACKRESOLVE_BASE_URL` environment variable:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
sr = StackResolve(api_key="ar_...", base_url="https://api.stackresolve.dev")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Errors
|
|
64
|
+
|
|
65
|
+
Any non-2xx response raises a `StackResolveError` carrying the HTTP `status` and
|
|
66
|
+
the parsed `body`:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from stackresolve import StackResolve, StackResolveError
|
|
70
|
+
|
|
71
|
+
sr = StackResolve()
|
|
72
|
+
try:
|
|
73
|
+
sr.get_usage()
|
|
74
|
+
except StackResolveError as err:
|
|
75
|
+
print(err.status, err.body)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Methods
|
|
79
|
+
|
|
80
|
+
AgentReady:
|
|
81
|
+
`audit(domain)`, `find_tools(task)`, `search(query, requirements=None)`,
|
|
82
|
+
`compare(slugs)`, `get_profile(slug)`, `list_registry(...)`, `get_categories()`,
|
|
83
|
+
`get_category(slug)`, `get_discovery(slug)`, `run_discovery(slug)`,
|
|
84
|
+
`get_score_history(slug)`, `generate(domain, openapi_url=None)`,
|
|
85
|
+
`deploy(domain, openapi_url=None)`.
|
|
86
|
+
|
|
87
|
+
CompanyData:
|
|
88
|
+
`get_company(domain)`, `get_pricing(domain)`, `get_competitors(domain)`,
|
|
89
|
+
`research(domain, question=None)`, `compare_companies(domains)`.
|
|
90
|
+
|
|
91
|
+
Monitors + account (API key required):
|
|
92
|
+
`list_monitors()`, `add_monitor(slug, ...)`, `run_monitor_now(slug)`,
|
|
93
|
+
`get_usage()`, `get_my_profile()`.
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stackresolve"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the StackResolve API (AgentReady + CompanyData)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "StackResolve" }]
|
|
13
|
+
keywords = ["stackresolve", "agentready", "companydata", "agent", "mcp", "sdk"]
|
|
14
|
+
dependencies = ["httpx>=0.24"]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://stackresolve.dev"
|
|
18
|
+
|
|
19
|
+
[tool.hatch.build.targets.wheel]
|
|
20
|
+
packages = ["stackresolve"]
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""StackResolve official Python SDK.
|
|
2
|
+
|
|
3
|
+
A thin wrapper over the StackResolve REST API. It gives agents and developers
|
|
4
|
+
Firecrawl/Exa-style ergonomics over AgentReady (discover, evaluate, select,
|
|
5
|
+
install, and use software) and CompanyData (structured company research).
|
|
6
|
+
|
|
7
|
+
from stackresolve import StackResolve
|
|
8
|
+
|
|
9
|
+
sr = StackResolve(api_key="ar_...")
|
|
10
|
+
report = sr.audit("stripe.com")
|
|
11
|
+
print(report["scores"]["agentready"])
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
import httpx
|
|
20
|
+
|
|
21
|
+
DEFAULT_BASE_URL = "https://api.stackresolve.dev"
|
|
22
|
+
|
|
23
|
+
JsonDict = Dict[str, Any]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class StackResolveError(Exception):
|
|
27
|
+
"""Raised on any non-2xx API response. Carries the HTTP status and parsed body."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, status: int, body: Any, message: Optional[str] = None) -> None:
|
|
30
|
+
self.status = status
|
|
31
|
+
self.body = body
|
|
32
|
+
super().__init__(message or f"StackResolve API error {status}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class StackResolve:
|
|
36
|
+
"""Client for the StackResolve API.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
api_key: API key. Falls back to the STACKRESOLVE_API_KEY env var.
|
|
40
|
+
Optional for public endpoints, required for gated ones.
|
|
41
|
+
base_url: Base URL override. Falls back to STACKRESOLVE_BASE_URL,
|
|
42
|
+
then https://api.stackresolve.dev .
|
|
43
|
+
timeout: Per-request timeout in seconds (default 60).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
base_url: Optional[str] = None,
|
|
50
|
+
timeout: float = 60.0,
|
|
51
|
+
) -> None:
|
|
52
|
+
self.api_key = api_key or os.environ.get("STACKRESOLVE_API_KEY")
|
|
53
|
+
base = base_url or os.environ.get("STACKRESOLVE_BASE_URL") or DEFAULT_BASE_URL
|
|
54
|
+
self.base_url = base.rstrip("/")
|
|
55
|
+
headers = {"accept": "application/json"}
|
|
56
|
+
if self.api_key:
|
|
57
|
+
headers["x-api-key"] = self.api_key
|
|
58
|
+
self._client = httpx.Client(base_url=self.base_url, headers=headers, timeout=timeout)
|
|
59
|
+
|
|
60
|
+
# ---- context manager + cleanup ----
|
|
61
|
+
|
|
62
|
+
def close(self) -> None:
|
|
63
|
+
self._client.close()
|
|
64
|
+
|
|
65
|
+
def __enter__(self) -> "StackResolve":
|
|
66
|
+
return self
|
|
67
|
+
|
|
68
|
+
def __exit__(self, *exc: Any) -> None:
|
|
69
|
+
self.close()
|
|
70
|
+
|
|
71
|
+
# ---- core transport ----
|
|
72
|
+
|
|
73
|
+
def _request(
|
|
74
|
+
self,
|
|
75
|
+
method: str,
|
|
76
|
+
path: str,
|
|
77
|
+
json: Optional[JsonDict] = None,
|
|
78
|
+
params: Optional[Dict[str, Any]] = None,
|
|
79
|
+
) -> Any:
|
|
80
|
+
clean_params = None
|
|
81
|
+
if params:
|
|
82
|
+
clean_params = {k: v for k, v in params.items() if v is not None and v != ""}
|
|
83
|
+
resp = self._client.request(method, path, json=json, params=clean_params)
|
|
84
|
+
text = resp.text
|
|
85
|
+
parsed: Any = None
|
|
86
|
+
if text:
|
|
87
|
+
try:
|
|
88
|
+
parsed = resp.json()
|
|
89
|
+
except ValueError:
|
|
90
|
+
parsed = text
|
|
91
|
+
if resp.status_code < 200 or resp.status_code >= 300:
|
|
92
|
+
message = None
|
|
93
|
+
if isinstance(parsed, dict) and "error" in parsed:
|
|
94
|
+
message = str(parsed["error"])
|
|
95
|
+
raise StackResolveError(resp.status_code, parsed, message)
|
|
96
|
+
return parsed
|
|
97
|
+
|
|
98
|
+
# ---- AgentReady: discovery, evaluation, profiles ----
|
|
99
|
+
|
|
100
|
+
def audit(self, domain: str) -> JsonDict:
|
|
101
|
+
"""Audit a domain: AgentReady scores, facts, and issues."""
|
|
102
|
+
return self._request("POST", "/v1/audit", json={"domain": domain})
|
|
103
|
+
|
|
104
|
+
def find_tools(self, task: str) -> JsonDict:
|
|
105
|
+
"""Turn a natural-language task into ranked, agent-ready tool candidates."""
|
|
106
|
+
return self._request("POST", "/v1/find-tools", json={"task": task})
|
|
107
|
+
|
|
108
|
+
def search(self, query: str, requirements: Optional[JsonDict] = None) -> List[JsonDict]:
|
|
109
|
+
"""Keyword search over the registry, optionally filtered by requirements."""
|
|
110
|
+
body: JsonDict = {"query": query}
|
|
111
|
+
if requirements:
|
|
112
|
+
body["requirements"] = requirements
|
|
113
|
+
return self._request("POST", "/v1/search", json=body)
|
|
114
|
+
|
|
115
|
+
def compare(self, slugs: List[str]) -> JsonDict:
|
|
116
|
+
"""Compare products side by side by slug."""
|
|
117
|
+
return self._request("POST", "/v1/compare", json={"slugs": slugs})
|
|
118
|
+
|
|
119
|
+
def get_profile(self, slug: str) -> Optional[JsonDict]:
|
|
120
|
+
"""Fetch a single product profile (scores + facts) by slug."""
|
|
121
|
+
return self._request("GET", f"/v1/profile/{slug}")
|
|
122
|
+
|
|
123
|
+
def list_registry(
|
|
124
|
+
self,
|
|
125
|
+
category: Optional[str] = None,
|
|
126
|
+
min_score: Optional[float] = None,
|
|
127
|
+
limit: Optional[int] = None,
|
|
128
|
+
) -> List[JsonDict]:
|
|
129
|
+
"""List registry rows, optionally filtered by category, minimum score, and limit."""
|
|
130
|
+
return self._request(
|
|
131
|
+
"GET",
|
|
132
|
+
"/v1/registry",
|
|
133
|
+
params={"category": category, "minScore": min_score, "limit": limit},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def get_categories(self) -> List[JsonDict]:
|
|
137
|
+
"""List all categories."""
|
|
138
|
+
r = self._request("GET", "/v1/categories")
|
|
139
|
+
return r.get("categories", []) if isinstance(r, dict) else r
|
|
140
|
+
|
|
141
|
+
def get_category(self, slug: str) -> Optional[JsonDict]:
|
|
142
|
+
"""Fetch one category and its products by slug."""
|
|
143
|
+
return self._request("GET", f"/v1/categories/{slug}")
|
|
144
|
+
|
|
145
|
+
# ---- CompanyData: structured company research ----
|
|
146
|
+
|
|
147
|
+
def get_company(self, domain: str) -> JsonDict:
|
|
148
|
+
"""Structured company profile for a domain."""
|
|
149
|
+
return self._request("POST", "/v1/company", json={"domain": domain})
|
|
150
|
+
|
|
151
|
+
def get_pricing(self, domain: str) -> JsonDict:
|
|
152
|
+
"""Structured pricing for a domain.
|
|
153
|
+
|
|
154
|
+
Note: the API reads ``domain`` from the query string for this endpoint,
|
|
155
|
+
not the body.
|
|
156
|
+
"""
|
|
157
|
+
return self._request("POST", "/v1/pricing", params={"domain": domain})
|
|
158
|
+
|
|
159
|
+
def get_competitors(self, domain: str) -> JsonDict:
|
|
160
|
+
"""Competitors and alternatives for a domain.
|
|
161
|
+
|
|
162
|
+
Note: the API reads ``domain`` from the query string for this endpoint,
|
|
163
|
+
not the body.
|
|
164
|
+
"""
|
|
165
|
+
return self._request("POST", "/v1/competitors", params={"domain": domain})
|
|
166
|
+
|
|
167
|
+
def research(self, domain: str, question: Optional[str] = None) -> JsonDict:
|
|
168
|
+
"""Grounded research answer about a domain, optionally scoped to a question."""
|
|
169
|
+
body: JsonDict = {"domain": domain}
|
|
170
|
+
if question:
|
|
171
|
+
body["question"] = question
|
|
172
|
+
return self._request("POST", "/v1/research", json=body)
|
|
173
|
+
|
|
174
|
+
def compare_companies(self, domains: List[str]) -> JsonDict:
|
|
175
|
+
"""Compare multiple companies by domain."""
|
|
176
|
+
return self._request("POST", "/v1/compare-companies", json={"domains": domains})
|
|
177
|
+
|
|
178
|
+
# ---- Agent-native interface generation + hosting ----
|
|
179
|
+
|
|
180
|
+
def generate(self, domain: str, openapi_url: Optional[str] = None) -> JsonDict:
|
|
181
|
+
"""Generate agent-native interfaces (MCP server, CLI, llms.txt, snippets)."""
|
|
182
|
+
body: JsonDict = {"domain": domain}
|
|
183
|
+
if openapi_url:
|
|
184
|
+
body["openapiUrl"] = openapi_url
|
|
185
|
+
return self._request("POST", "/v1/generate", json=body)
|
|
186
|
+
|
|
187
|
+
def deploy(self, domain: str, openapi_url: Optional[str] = None) -> JsonDict:
|
|
188
|
+
"""Deploy a hosted MCP server for a domain."""
|
|
189
|
+
body: JsonDict = {"domain": domain}
|
|
190
|
+
if openapi_url:
|
|
191
|
+
body["openapiUrl"] = openapi_url
|
|
192
|
+
return self._request("POST", "/v1/deploy", json=body)
|
|
193
|
+
|
|
194
|
+
# ---- Agent Discovery + Score History ----
|
|
195
|
+
|
|
196
|
+
def get_discovery(self, slug: str) -> JsonDict:
|
|
197
|
+
"""Read the latest Agent Discovery view for a slug."""
|
|
198
|
+
return self._request("GET", "/v1/discovery", params={"slug": slug})
|
|
199
|
+
|
|
200
|
+
def run_discovery(self, slug: str) -> JsonDict:
|
|
201
|
+
"""Run an Agent Discovery check for a slug (requires an API key; spends credits)."""
|
|
202
|
+
return self._request("POST", "/v1/discovery/run", json={"slug": slug})
|
|
203
|
+
|
|
204
|
+
def get_score_history(self, slug: str) -> JsonDict:
|
|
205
|
+
"""Read the score-history timeseries for a slug."""
|
|
206
|
+
return self._request("GET", "/v1/score-history", params={"slug": slug})
|
|
207
|
+
|
|
208
|
+
# ---- Monitors (require an API key) ----
|
|
209
|
+
|
|
210
|
+
def list_monitors(self) -> JsonDict:
|
|
211
|
+
"""List monitors for the authenticated workspace."""
|
|
212
|
+
return self._request("GET", "/v1/monitors")
|
|
213
|
+
|
|
214
|
+
def add_monitor(
|
|
215
|
+
self,
|
|
216
|
+
slug: str,
|
|
217
|
+
cadence: Optional[str] = None,
|
|
218
|
+
kinds: Optional[List[str]] = None,
|
|
219
|
+
enabled: Optional[bool] = None,
|
|
220
|
+
) -> JsonDict:
|
|
221
|
+
"""Create or update a monitor for a slug."""
|
|
222
|
+
body: JsonDict = {"slug": slug}
|
|
223
|
+
if cadence is not None:
|
|
224
|
+
body["cadence"] = cadence
|
|
225
|
+
if kinds is not None:
|
|
226
|
+
body["kinds"] = kinds
|
|
227
|
+
if enabled is not None:
|
|
228
|
+
body["enabled"] = enabled
|
|
229
|
+
return self._request("POST", "/v1/monitors", json=body)
|
|
230
|
+
|
|
231
|
+
def run_monitor_now(self, slug: str) -> JsonDict:
|
|
232
|
+
"""Run a monitor immediately for a slug."""
|
|
233
|
+
return self._request("POST", "/v1/monitors/run-now", json={"slug": slug})
|
|
234
|
+
|
|
235
|
+
# ---- Account (require an API key) ----
|
|
236
|
+
|
|
237
|
+
def get_usage(self) -> JsonDict:
|
|
238
|
+
"""Usage for the authenticated workspace."""
|
|
239
|
+
return self._request("GET", "/v1/usage")
|
|
240
|
+
|
|
241
|
+
def get_my_profile(self) -> JsonDict:
|
|
242
|
+
"""The authenticated workspace's own claimed profile (or null)."""
|
|
243
|
+
return self._request("GET", "/v1/me/profile")
|