agentdom 3.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.
- agentdom-3.6.0/.gitignore +89 -0
- agentdom-3.6.0/PKG-INFO +241 -0
- agentdom-3.6.0/README.md +195 -0
- agentdom-3.6.0/agentdom/__init__.py +45 -0
- agentdom-3.6.0/agentdom/core.py +206 -0
- agentdom-3.6.0/agentdom/dispatch.py +103 -0
- agentdom-3.6.0/pyproject.toml +68 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ── Dependencies ─────────────────────────────────────────────────────────────
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# ── Build outputs ─────────────────────────────────────────────────────────────
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.next/
|
|
10
|
+
out/
|
|
11
|
+
*.tsbuildinfo
|
|
12
|
+
|
|
13
|
+
# ── Python ────────────────────────────────────────────────────────────────────
|
|
14
|
+
__pycache__/
|
|
15
|
+
*.py[cod]
|
|
16
|
+
*$py.class
|
|
17
|
+
*.egg-info/
|
|
18
|
+
*.egg
|
|
19
|
+
.eggs/
|
|
20
|
+
python/dist/
|
|
21
|
+
python/build/
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
env/
|
|
25
|
+
.Python
|
|
26
|
+
pip-wheel-metadata/
|
|
27
|
+
|
|
28
|
+
# ── Secrets & env ─────────────────────────────────────────────────────────────
|
|
29
|
+
.env
|
|
30
|
+
.env.local
|
|
31
|
+
.env.*.local
|
|
32
|
+
*.pem
|
|
33
|
+
*.key
|
|
34
|
+
|
|
35
|
+
# ── OS artifacts ─────────────────────────────────────────────────────────────
|
|
36
|
+
.DS_Store
|
|
37
|
+
.DS_Store?
|
|
38
|
+
Thumbs.db
|
|
39
|
+
desktop.ini
|
|
40
|
+
|
|
41
|
+
# ── Logs ─────────────────────────────────────────────────────────────────────
|
|
42
|
+
*.log
|
|
43
|
+
npm-debug.log*
|
|
44
|
+
yarn-debug.log*
|
|
45
|
+
yarn-error.log*
|
|
46
|
+
|
|
47
|
+
# ── Test screenshots & debug images ─────────────────────────────────────────
|
|
48
|
+
# Keep: website/public/*.png (brand assets)
|
|
49
|
+
# Remove: root-level screenshots from dev/testing
|
|
50
|
+
/*.png
|
|
51
|
+
/*.jpg
|
|
52
|
+
/*.jpeg
|
|
53
|
+
/*.gif
|
|
54
|
+
!/website/public/logo.png
|
|
55
|
+
!/website/public/og-image.png
|
|
56
|
+
|
|
57
|
+
# ── AWS CDK artifacts ─────────────────────────────────────────────────────────
|
|
58
|
+
cdk.out/
|
|
59
|
+
aws/cdk-outputs.json
|
|
60
|
+
|
|
61
|
+
# ── Generated reports (not source code) ───────────────────────────────────────
|
|
62
|
+
gtm-strategy-*.md
|
|
63
|
+
*-report-*.md
|
|
64
|
+
tools/demo-*.js
|
|
65
|
+
|
|
66
|
+
# ── Personal / session files (never commit these) ─────────────────────────────
|
|
67
|
+
yc-coding-session.md
|
|
68
|
+
*-coding-session.md
|
|
69
|
+
*-session.md
|
|
70
|
+
|
|
71
|
+
# ── macOS finder aliases / fileloc ───────────────────────────────────────────
|
|
72
|
+
*.fileloc
|
|
73
|
+
*.alias
|
|
74
|
+
|
|
75
|
+
# ── Infrastructure config with real data ──────────────────────────────────────
|
|
76
|
+
website/dns-change.json
|
|
77
|
+
dns-change.json
|
|
78
|
+
**/dns-change*.json
|
|
79
|
+
aws/cdk-outputs.json
|
|
80
|
+
|
|
81
|
+
# ── AI agent internal instruction files (keep root AGENTS.md / CLAUDE.md) ────
|
|
82
|
+
website/AGENTS.md
|
|
83
|
+
website/CLAUDE.md
|
|
84
|
+
|
|
85
|
+
# ── Misc ──────────────────────────────────────────────────────────────────────
|
|
86
|
+
.turbo/
|
|
87
|
+
.vercel/
|
|
88
|
+
coverage/
|
|
89
|
+
.nyc_output/
|
agentdom-3.6.0/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentdom
|
|
3
|
+
Version: 3.6.0
|
|
4
|
+
Summary: The universal runtime that makes every website, desktop app, and API accessible to AI agents — zero human in the loop.
|
|
5
|
+
Project-URL: Homepage, https://getagentdom.com
|
|
6
|
+
Project-URL: Documentation, https://getagentdom.com/docs/python
|
|
7
|
+
Project-URL: Repository, https://github.com/RagavRida/agentdom
|
|
8
|
+
Project-URL: Changelog, https://github.com/RagavRida/agentdom/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/RagavRida/agentdom/issues
|
|
10
|
+
Author-email: Ragavendhra Machikatla <hi@getagentdom.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
Keywords: agentdom,agents,ai,automation,browser,dispatch,llm,mcp,protocol,rpa
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: fastapi>=0.100; extra == 'all'
|
|
28
|
+
Requires-Dist: flask>=2.0; extra == 'all'
|
|
29
|
+
Requires-Dist: httpx>=0.25; extra == 'all'
|
|
30
|
+
Requires-Dist: uvicorn>=0.20; extra == 'all'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: build; extra == 'dev'
|
|
33
|
+
Requires-Dist: flake8>=7; extra == 'dev'
|
|
34
|
+
Requires-Dist: httpx>=0.25; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
37
|
+
Requires-Dist: twine; extra == 'dev'
|
|
38
|
+
Provides-Extra: dispatch
|
|
39
|
+
Requires-Dist: httpx>=0.25; extra == 'dispatch'
|
|
40
|
+
Provides-Extra: fastapi
|
|
41
|
+
Requires-Dist: fastapi>=0.100; extra == 'fastapi'
|
|
42
|
+
Requires-Dist: uvicorn>=0.20; extra == 'fastapi'
|
|
43
|
+
Provides-Extra: flask
|
|
44
|
+
Requires-Dist: flask>=2.0; extra == 'flask'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# agentdom
|
|
48
|
+
|
|
49
|
+
**The universal runtime that makes every website, desktop app, and API accessible to AI agents — zero human in the loop.**
|
|
50
|
+
|
|
51
|
+
[](https://pypi.org/project/agentdom/)
|
|
52
|
+
[](https://pypi.org/project/agentdom/)
|
|
53
|
+
[](https://github.com/RagavRida/agentdom/blob/main/LICENSE)
|
|
54
|
+
[](https://www.npmjs.com/package/agentdom)
|
|
55
|
+
|
|
56
|
+
[Website](https://getagentdom.com) · [Docs](https://getagentdom.com/docs/python) · [GitHub](https://github.com/RagavRida/agentdom) · [npm package](https://www.npmjs.com/package/agentdom)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install agentdom
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
With framework support:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install "agentdom[flask]" # Flask integration
|
|
70
|
+
pip install "agentdom[fastapi]" # FastAPI integration
|
|
71
|
+
pip install "agentdom[dispatch]" # dispatch_intent() support (requires httpx)
|
|
72
|
+
pip install "agentdom[all]" # everything
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick start — dispatch an intent
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import asyncio
|
|
79
|
+
from agentdom import dispatch_intent
|
|
80
|
+
|
|
81
|
+
async def main():
|
|
82
|
+
# Create a GitHub issue — routes via REST API, no browser needed
|
|
83
|
+
result = await dispatch_intent(
|
|
84
|
+
"issues.create",
|
|
85
|
+
{
|
|
86
|
+
"title": "Login crash on iOS 17",
|
|
87
|
+
"body": "Reproducible on iPhone 15 Pro",
|
|
88
|
+
"labels": ["bug", "mobile"],
|
|
89
|
+
},
|
|
90
|
+
host="github.com",
|
|
91
|
+
)
|
|
92
|
+
print(result) # → {"id": 42, "url": "https://github.com/..."}
|
|
93
|
+
|
|
94
|
+
asyncio.run(main())
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or synchronously:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from agentdom import dispatch_intent_sync
|
|
101
|
+
|
|
102
|
+
result = dispatch_intent_sync("repos.list", {"username": "octocat"}, host="github.com")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Embed AgentDOM in your app
|
|
106
|
+
|
|
107
|
+
### Flask
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from flask import Flask
|
|
111
|
+
from agentdom import AgentDOM
|
|
112
|
+
|
|
113
|
+
app = Flask(__name__)
|
|
114
|
+
agent = AgentDOM(
|
|
115
|
+
host="api.myapp.com",
|
|
116
|
+
auth={"method": "api_key", "key_header": "Authorization"},
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@agent.capability("todos.create", description="Create a new todo item",
|
|
120
|
+
args={"title": {"type": "string", "required": True}})
|
|
121
|
+
def create_todo(args):
|
|
122
|
+
return {"id": 1, "title": args["title"]}
|
|
123
|
+
|
|
124
|
+
@agent.capability("todos.list", description="List all todo items")
|
|
125
|
+
def list_todos(args):
|
|
126
|
+
return [{"id": 1, "title": "Buy milk"}]
|
|
127
|
+
|
|
128
|
+
agent.register(app)
|
|
129
|
+
|
|
130
|
+
# Now your app automatically serves:
|
|
131
|
+
# GET /.well-known/agentdom.json → manifest
|
|
132
|
+
# POST /api/agentdom/todos.create → your handler
|
|
133
|
+
# POST /api/agentdom/todos.list → your handler
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### FastAPI
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from fastapi import FastAPI
|
|
140
|
+
from agentdom import AgentDOM
|
|
141
|
+
|
|
142
|
+
app = FastAPI()
|
|
143
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
144
|
+
|
|
145
|
+
@agent.capability("orders.ship", description="Ship a pending order",
|
|
146
|
+
args={"order_id": {"type": "string", "required": True}})
|
|
147
|
+
async def ship_order(args):
|
|
148
|
+
# async handlers are fully supported
|
|
149
|
+
return await shipping_service.ship(args["order_id"])
|
|
150
|
+
|
|
151
|
+
agent.register(app)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Standalone manifest
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from agentdom import AgentDOM
|
|
158
|
+
|
|
159
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
160
|
+
|
|
161
|
+
@agent.capability("items.create", description="Create an item")
|
|
162
|
+
def create_item(args):
|
|
163
|
+
return {"created": True}
|
|
164
|
+
|
|
165
|
+
# Get the manifest dict to serve however you like
|
|
166
|
+
manifest = agent.manifest()
|
|
167
|
+
print(manifest)
|
|
168
|
+
# → {"version": "1.0", "host": "api.myapp.com", "capabilities": [...], ...}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Auth configuration
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# API key
|
|
175
|
+
agent = AgentDOM(
|
|
176
|
+
host="api.myapp.com",
|
|
177
|
+
auth={
|
|
178
|
+
"method": "api_key",
|
|
179
|
+
"key_header": "Authorization",
|
|
180
|
+
"key_format": "Bearer {token}",
|
|
181
|
+
},
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# OAuth 2.0
|
|
185
|
+
agent = AgentDOM(
|
|
186
|
+
host="api.myapp.com",
|
|
187
|
+
auth={
|
|
188
|
+
"method": "oauth2",
|
|
189
|
+
"auth_url": "https://api.myapp.com/oauth/authorize",
|
|
190
|
+
"token_url": "https://api.myapp.com/oauth/token",
|
|
191
|
+
"scopes": ["read", "write"],
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# No auth (public API)
|
|
196
|
+
agent = AgentDOM(host="api.myapp.com", auth={"method": "none"})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Environment variables
|
|
200
|
+
|
|
201
|
+
| Variable | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `AGENTDOM_API_URL` | Override the AgentDOM API server URL |
|
|
204
|
+
| `AGENTDOM_TOKEN` | Default auth token for dispatch_intent |
|
|
205
|
+
| `<HOST>_TOKEN` | Per-host token (e.g. `GITHUB_COM_TOKEN`) |
|
|
206
|
+
|
|
207
|
+
## Agent Token Protocol
|
|
208
|
+
|
|
209
|
+
Publishers can declare an `agent_tokens` endpoint so agents provision their own scoped tokens automatically:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
agent = AgentDOM(
|
|
213
|
+
host="api.myapp.com",
|
|
214
|
+
auth={
|
|
215
|
+
"method": "api_key",
|
|
216
|
+
"agent_tokens": {
|
|
217
|
+
"issue": "POST https://api.myapp.com/agent-tokens",
|
|
218
|
+
"revoke": "DELETE https://api.myapp.com/agent-tokens/{id}",
|
|
219
|
+
"scopes": ["read", "write"],
|
|
220
|
+
"max_ttl_seconds": 86400,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Supported providers
|
|
227
|
+
|
|
228
|
+
| Provider | Auth | Capabilities |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `github.com` | Device Flow | repos, issues, PRs, actions (811) |
|
|
231
|
+
| `linear.app` | OAuth 2.0 | issues, teams, cycles (8) |
|
|
232
|
+
| `slack.com` | OAuth 2.0 | messages, channels (6) |
|
|
233
|
+
| `notion.so` | OAuth 2.0 | pages, databases (6) |
|
|
234
|
+
| `stripe.com` | API Key | payments, customers (442) |
|
|
235
|
+
| `resend.com` | API Key | emails, domains (5) |
|
|
236
|
+
|
|
237
|
+
More providers at [getagentdom.com/providers](https://getagentdom.com/providers).
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT © [Ragavendhra Machikatla](https://github.com/RagavRida)
|
agentdom-3.6.0/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# agentdom
|
|
2
|
+
|
|
3
|
+
**The universal runtime that makes every website, desktop app, and API accessible to AI agents — zero human in the loop.**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/agentdom/)
|
|
6
|
+
[](https://pypi.org/project/agentdom/)
|
|
7
|
+
[](https://github.com/RagavRida/agentdom/blob/main/LICENSE)
|
|
8
|
+
[](https://www.npmjs.com/package/agentdom)
|
|
9
|
+
|
|
10
|
+
[Website](https://getagentdom.com) · [Docs](https://getagentdom.com/docs/python) · [GitHub](https://github.com/RagavRida/agentdom) · [npm package](https://www.npmjs.com/package/agentdom)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install agentdom
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
With framework support:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install "agentdom[flask]" # Flask integration
|
|
24
|
+
pip install "agentdom[fastapi]" # FastAPI integration
|
|
25
|
+
pip install "agentdom[dispatch]" # dispatch_intent() support (requires httpx)
|
|
26
|
+
pip install "agentdom[all]" # everything
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick start — dispatch an intent
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import asyncio
|
|
33
|
+
from agentdom import dispatch_intent
|
|
34
|
+
|
|
35
|
+
async def main():
|
|
36
|
+
# Create a GitHub issue — routes via REST API, no browser needed
|
|
37
|
+
result = await dispatch_intent(
|
|
38
|
+
"issues.create",
|
|
39
|
+
{
|
|
40
|
+
"title": "Login crash on iOS 17",
|
|
41
|
+
"body": "Reproducible on iPhone 15 Pro",
|
|
42
|
+
"labels": ["bug", "mobile"],
|
|
43
|
+
},
|
|
44
|
+
host="github.com",
|
|
45
|
+
)
|
|
46
|
+
print(result) # → {"id": 42, "url": "https://github.com/..."}
|
|
47
|
+
|
|
48
|
+
asyncio.run(main())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or synchronously:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from agentdom import dispatch_intent_sync
|
|
55
|
+
|
|
56
|
+
result = dispatch_intent_sync("repos.list", {"username": "octocat"}, host="github.com")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Embed AgentDOM in your app
|
|
60
|
+
|
|
61
|
+
### Flask
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from flask import Flask
|
|
65
|
+
from agentdom import AgentDOM
|
|
66
|
+
|
|
67
|
+
app = Flask(__name__)
|
|
68
|
+
agent = AgentDOM(
|
|
69
|
+
host="api.myapp.com",
|
|
70
|
+
auth={"method": "api_key", "key_header": "Authorization"},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@agent.capability("todos.create", description="Create a new todo item",
|
|
74
|
+
args={"title": {"type": "string", "required": True}})
|
|
75
|
+
def create_todo(args):
|
|
76
|
+
return {"id": 1, "title": args["title"]}
|
|
77
|
+
|
|
78
|
+
@agent.capability("todos.list", description="List all todo items")
|
|
79
|
+
def list_todos(args):
|
|
80
|
+
return [{"id": 1, "title": "Buy milk"}]
|
|
81
|
+
|
|
82
|
+
agent.register(app)
|
|
83
|
+
|
|
84
|
+
# Now your app automatically serves:
|
|
85
|
+
# GET /.well-known/agentdom.json → manifest
|
|
86
|
+
# POST /api/agentdom/todos.create → your handler
|
|
87
|
+
# POST /api/agentdom/todos.list → your handler
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### FastAPI
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from fastapi import FastAPI
|
|
94
|
+
from agentdom import AgentDOM
|
|
95
|
+
|
|
96
|
+
app = FastAPI()
|
|
97
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
98
|
+
|
|
99
|
+
@agent.capability("orders.ship", description="Ship a pending order",
|
|
100
|
+
args={"order_id": {"type": "string", "required": True}})
|
|
101
|
+
async def ship_order(args):
|
|
102
|
+
# async handlers are fully supported
|
|
103
|
+
return await shipping_service.ship(args["order_id"])
|
|
104
|
+
|
|
105
|
+
agent.register(app)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Standalone manifest
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from agentdom import AgentDOM
|
|
112
|
+
|
|
113
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
114
|
+
|
|
115
|
+
@agent.capability("items.create", description="Create an item")
|
|
116
|
+
def create_item(args):
|
|
117
|
+
return {"created": True}
|
|
118
|
+
|
|
119
|
+
# Get the manifest dict to serve however you like
|
|
120
|
+
manifest = agent.manifest()
|
|
121
|
+
print(manifest)
|
|
122
|
+
# → {"version": "1.0", "host": "api.myapp.com", "capabilities": [...], ...}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Auth configuration
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# API key
|
|
129
|
+
agent = AgentDOM(
|
|
130
|
+
host="api.myapp.com",
|
|
131
|
+
auth={
|
|
132
|
+
"method": "api_key",
|
|
133
|
+
"key_header": "Authorization",
|
|
134
|
+
"key_format": "Bearer {token}",
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# OAuth 2.0
|
|
139
|
+
agent = AgentDOM(
|
|
140
|
+
host="api.myapp.com",
|
|
141
|
+
auth={
|
|
142
|
+
"method": "oauth2",
|
|
143
|
+
"auth_url": "https://api.myapp.com/oauth/authorize",
|
|
144
|
+
"token_url": "https://api.myapp.com/oauth/token",
|
|
145
|
+
"scopes": ["read", "write"],
|
|
146
|
+
},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# No auth (public API)
|
|
150
|
+
agent = AgentDOM(host="api.myapp.com", auth={"method": "none"})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Environment variables
|
|
154
|
+
|
|
155
|
+
| Variable | Description |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `AGENTDOM_API_URL` | Override the AgentDOM API server URL |
|
|
158
|
+
| `AGENTDOM_TOKEN` | Default auth token for dispatch_intent |
|
|
159
|
+
| `<HOST>_TOKEN` | Per-host token (e.g. `GITHUB_COM_TOKEN`) |
|
|
160
|
+
|
|
161
|
+
## Agent Token Protocol
|
|
162
|
+
|
|
163
|
+
Publishers can declare an `agent_tokens` endpoint so agents provision their own scoped tokens automatically:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
agent = AgentDOM(
|
|
167
|
+
host="api.myapp.com",
|
|
168
|
+
auth={
|
|
169
|
+
"method": "api_key",
|
|
170
|
+
"agent_tokens": {
|
|
171
|
+
"issue": "POST https://api.myapp.com/agent-tokens",
|
|
172
|
+
"revoke": "DELETE https://api.myapp.com/agent-tokens/{id}",
|
|
173
|
+
"scopes": ["read", "write"],
|
|
174
|
+
"max_ttl_seconds": 86400,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Supported providers
|
|
181
|
+
|
|
182
|
+
| Provider | Auth | Capabilities |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `github.com` | Device Flow | repos, issues, PRs, actions (811) |
|
|
185
|
+
| `linear.app` | OAuth 2.0 | issues, teams, cycles (8) |
|
|
186
|
+
| `slack.com` | OAuth 2.0 | messages, channels (6) |
|
|
187
|
+
| `notion.so` | OAuth 2.0 | pages, databases (6) |
|
|
188
|
+
| `stripe.com` | API Key | payments, customers (442) |
|
|
189
|
+
| `resend.com` | API Key | emails, domains (5) |
|
|
190
|
+
|
|
191
|
+
More providers at [getagentdom.com/providers](https://getagentdom.com/providers).
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT © [Ragavendhra Machikatla](https://github.com/RagavRida)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentDOM Python SDK
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
The universal runtime that makes every website, desktop app,
|
|
6
|
+
and API accessible to AI agents — zero human in the loop.
|
|
7
|
+
|
|
8
|
+
pip install agentdom
|
|
9
|
+
|
|
10
|
+
Usage (Flask):
|
|
11
|
+
from agentdom import AgentDOM
|
|
12
|
+
|
|
13
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
14
|
+
|
|
15
|
+
@agent.capability("todos.create", description="Create a todo")
|
|
16
|
+
def create_todo(args):
|
|
17
|
+
return {"id": 1, "title": args["title"]}
|
|
18
|
+
|
|
19
|
+
agent.register(app)
|
|
20
|
+
|
|
21
|
+
Usage (FastAPI):
|
|
22
|
+
from agentdom import AgentDOM
|
|
23
|
+
|
|
24
|
+
agent = AgentDOM(host="api.myapp.com")
|
|
25
|
+
|
|
26
|
+
@agent.capability("items.list", description="List items")
|
|
27
|
+
async def list_items(args):
|
|
28
|
+
return [{"id": 1, "name": "Widget"}]
|
|
29
|
+
|
|
30
|
+
agent.register(fastapi_app)
|
|
31
|
+
|
|
32
|
+
Usage (standalone dispatch):
|
|
33
|
+
from agentdom import dispatch_intent
|
|
34
|
+
|
|
35
|
+
result = await dispatch_intent("issues.create", {
|
|
36
|
+
"title": "Bug report",
|
|
37
|
+
"body": "Reproducible on iOS 17"
|
|
38
|
+
}, host="github.com")
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from agentdom.core import AgentDOM, AgentCapability
|
|
42
|
+
from agentdom.dispatch import dispatch_intent
|
|
43
|
+
|
|
44
|
+
__version__ = "1.0.0"
|
|
45
|
+
__all__ = ["AgentDOM", "AgentCapability", "dispatch_intent", "__version__"]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agentdom.core — AgentDOM class and capability registration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
from typing import Callable, List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentCapability:
|
|
10
|
+
"""A single declared capability (intent + handler)."""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
intent: str,
|
|
15
|
+
description: str,
|
|
16
|
+
handler: Callable,
|
|
17
|
+
args: dict = None,
|
|
18
|
+
side_effects: list = None,
|
|
19
|
+
method: str = "POST",
|
|
20
|
+
):
|
|
21
|
+
self.intent = intent
|
|
22
|
+
self.description = description
|
|
23
|
+
self.handler = handler
|
|
24
|
+
self.args = args or {}
|
|
25
|
+
self.side_effects = side_effects or ["write_local"]
|
|
26
|
+
self.method = method
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AgentDOM:
|
|
30
|
+
"""
|
|
31
|
+
Embed AgentDOM into any Python web app.
|
|
32
|
+
|
|
33
|
+
Serves /.well-known/agentdom.json automatically and routes
|
|
34
|
+
incoming agent intents to your handler functions.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
host: Your public domain (e.g. "api.myapp.com")
|
|
38
|
+
name: Human-readable name (defaults to host)
|
|
39
|
+
description: Short description of your service
|
|
40
|
+
auth: Auth config dict, e.g. {"method": "api_key", ...}
|
|
41
|
+
api_base_path: Route prefix for intent endpoints (default: /api/agentdom)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
host: str,
|
|
47
|
+
name: Optional[str] = None,
|
|
48
|
+
description: Optional[str] = None,
|
|
49
|
+
auth: Optional[dict] = None,
|
|
50
|
+
api_base_path: str = "/api/agentdom",
|
|
51
|
+
):
|
|
52
|
+
if not host:
|
|
53
|
+
raise ValueError("AgentDOM: host is required")
|
|
54
|
+
self.host = host
|
|
55
|
+
self.name = name or host
|
|
56
|
+
self.description = description or f"AgentDOM manifest for {host}"
|
|
57
|
+
self.auth = auth or {"method": "none"}
|
|
58
|
+
self.api_base_path = api_base_path
|
|
59
|
+
self._capabilities: List[AgentCapability] = []
|
|
60
|
+
|
|
61
|
+
def capability(
|
|
62
|
+
self,
|
|
63
|
+
intent: str,
|
|
64
|
+
description: str = "",
|
|
65
|
+
args: dict = None,
|
|
66
|
+
side_effects: list = None,
|
|
67
|
+
method: str = "POST",
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
Decorator to register a capability handler.
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
@agent.capability("todos.create", description="Create a todo")
|
|
74
|
+
def create_todo(args):
|
|
75
|
+
return db.todos.insert(args["title"])
|
|
76
|
+
"""
|
|
77
|
+
def decorator(fn: Callable):
|
|
78
|
+
cap = AgentCapability(
|
|
79
|
+
intent=intent,
|
|
80
|
+
description=description,
|
|
81
|
+
handler=fn,
|
|
82
|
+
args=args or {},
|
|
83
|
+
side_effects=side_effects or ["write_local"],
|
|
84
|
+
method=method,
|
|
85
|
+
)
|
|
86
|
+
self._capabilities.append(cap)
|
|
87
|
+
return fn
|
|
88
|
+
return decorator
|
|
89
|
+
|
|
90
|
+
def manifest(self) -> dict:
|
|
91
|
+
"""
|
|
92
|
+
Build and return the agentdom.json manifest as a Python dict.
|
|
93
|
+
Serve this at GET /.well-known/agentdom.json.
|
|
94
|
+
"""
|
|
95
|
+
return {
|
|
96
|
+
"version": "1.0",
|
|
97
|
+
"host": self.host,
|
|
98
|
+
"name": self.name,
|
|
99
|
+
"description": self.description,
|
|
100
|
+
"generated_at": datetime.datetime.utcnow().isoformat() + "Z",
|
|
101
|
+
"generated_by": "agentdom-python/1.0.0",
|
|
102
|
+
"auth": self.auth,
|
|
103
|
+
"capabilities": [
|
|
104
|
+
{
|
|
105
|
+
"intent": cap.intent,
|
|
106
|
+
"description": cap.description,
|
|
107
|
+
"transport": "api",
|
|
108
|
+
"method": cap.method,
|
|
109
|
+
"endpoint": f"https://{self.host}{self.api_base_path}/{cap.intent}",
|
|
110
|
+
"args": {
|
|
111
|
+
k: {
|
|
112
|
+
"type": v if isinstance(v, str) else v.get("type", "string"),
|
|
113
|
+
"required": v.get("required", False) if isinstance(v, dict) else True,
|
|
114
|
+
"description": v.get("description", "") if isinstance(v, dict) else "",
|
|
115
|
+
}
|
|
116
|
+
for k, v in cap.args.items()
|
|
117
|
+
},
|
|
118
|
+
"side_effects": cap.side_effects,
|
|
119
|
+
}
|
|
120
|
+
for cap in self._capabilities
|
|
121
|
+
],
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
def register(self, app):
|
|
125
|
+
"""
|
|
126
|
+
Auto-detect Flask or FastAPI and register all routes.
|
|
127
|
+
|
|
128
|
+
Registers:
|
|
129
|
+
GET /.well-known/agentdom.json → serves the manifest
|
|
130
|
+
POST /api/agentdom/<intent> → dispatches to your handler
|
|
131
|
+
"""
|
|
132
|
+
app_module = type(app).__module__
|
|
133
|
+
|
|
134
|
+
if "flask" in app_module:
|
|
135
|
+
self._register_flask(app)
|
|
136
|
+
elif "fastapi" in app_module:
|
|
137
|
+
self._register_fastapi(app)
|
|
138
|
+
else:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"AgentDOM.register() expects a Flask or FastAPI app instance. "
|
|
141
|
+
"For other frameworks, use .manifest() and route manually."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def _register_flask(self, app):
|
|
145
|
+
"""Attach routes to a Flask app."""
|
|
146
|
+
from flask import request, jsonify
|
|
147
|
+
|
|
148
|
+
manifest = self.manifest()
|
|
149
|
+
cap_map = {cap.intent: cap for cap in self._capabilities}
|
|
150
|
+
|
|
151
|
+
@app.route("/.well-known/agentdom.json", methods=["GET"])
|
|
152
|
+
def agentdom_manifest():
|
|
153
|
+
return jsonify(manifest)
|
|
154
|
+
|
|
155
|
+
@app.route(f"{self.api_base_path}/<path:intent>", methods=["POST"])
|
|
156
|
+
def agentdom_intent(intent):
|
|
157
|
+
cap = cap_map.get(intent)
|
|
158
|
+
if not cap:
|
|
159
|
+
return jsonify({
|
|
160
|
+
"error": f"Intent '{intent}' not found",
|
|
161
|
+
"available": list(cap_map.keys()),
|
|
162
|
+
}), 404
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
body = request.get_json(silent=True) or {}
|
|
166
|
+
result = cap.handler(body)
|
|
167
|
+
return jsonify({"success": True, "result": result})
|
|
168
|
+
except Exception as exc:
|
|
169
|
+
return jsonify({"success": False, "error": str(exc)}), 500
|
|
170
|
+
|
|
171
|
+
def _register_fastapi(self, app):
|
|
172
|
+
"""Attach routes to a FastAPI app."""
|
|
173
|
+
from fastapi import Request
|
|
174
|
+
from fastapi.responses import JSONResponse
|
|
175
|
+
|
|
176
|
+
manifest = self.manifest()
|
|
177
|
+
cap_map = {cap.intent: cap for cap in self._capabilities}
|
|
178
|
+
|
|
179
|
+
@app.get("/.well-known/agentdom.json")
|
|
180
|
+
async def agentdom_manifest():
|
|
181
|
+
return JSONResponse(
|
|
182
|
+
manifest,
|
|
183
|
+
headers={"Cache-Control": "public, max-age=3600", "Access-Control-Allow-Origin": "*"},
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@app.post(f"{self.api_base_path}/{{intent:path}}")
|
|
187
|
+
async def agentdom_intent(intent: str, request: Request):
|
|
188
|
+
import asyncio
|
|
189
|
+
cap = cap_map.get(intent)
|
|
190
|
+
if not cap:
|
|
191
|
+
return JSONResponse(
|
|
192
|
+
{"error": f"Intent '{intent}' not found", "available": list(cap_map.keys())},
|
|
193
|
+
status_code=404,
|
|
194
|
+
)
|
|
195
|
+
try:
|
|
196
|
+
body = await request.json()
|
|
197
|
+
except Exception:
|
|
198
|
+
body = {}
|
|
199
|
+
try:
|
|
200
|
+
if asyncio.iscoroutinefunction(cap.handler):
|
|
201
|
+
result = await cap.handler(body)
|
|
202
|
+
else:
|
|
203
|
+
result = cap.handler(body)
|
|
204
|
+
return JSONResponse({"success": True, "result": result})
|
|
205
|
+
except Exception as exc:
|
|
206
|
+
return JSONResponse({"success": False, "error": str(exc)}, status_code=500)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agentdom.dispatch — dispatch_intent() for calling any AgentDOM-compatible service.
|
|
3
|
+
|
|
4
|
+
This is the consumer-side function. It fetches the manifest from a host,
|
|
5
|
+
finds the capability matching the intent, and executes it via the declared transport.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def dispatch_intent(
|
|
14
|
+
intent: str,
|
|
15
|
+
args: dict,
|
|
16
|
+
host: str,
|
|
17
|
+
token: Optional[str] = None,
|
|
18
|
+
agentdom_api: Optional[str] = None,
|
|
19
|
+
) -> Any:
|
|
20
|
+
"""
|
|
21
|
+
Dispatch an intent to an AgentDOM-compatible host.
|
|
22
|
+
|
|
23
|
+
This is the main consumer-side function. It:
|
|
24
|
+
1. Fetches the /.well-known/agentdom.json manifest from the host
|
|
25
|
+
2. Finds the capability matching `intent`
|
|
26
|
+
3. Executes the call via the declared transport (api, browser, desktop)
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
intent: Intent name, e.g. "issues.create"
|
|
30
|
+
args: Intent arguments dict
|
|
31
|
+
host: Target host, e.g. "github.com"
|
|
32
|
+
token: Auth token (overrides env var lookup)
|
|
33
|
+
agentdom_api: AgentDOM API server URL (default: https://api.getagentdom.com)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The result from the remote capability.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
ValueError: If the intent is not found in the manifest.
|
|
40
|
+
httpx.HTTPStatusError: If the HTTP request fails.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
from agentdom import dispatch_intent
|
|
44
|
+
|
|
45
|
+
result = await dispatch_intent(
|
|
46
|
+
"issues.create",
|
|
47
|
+
{"title": "Bug in login", "body": "Steps to reproduce..."},
|
|
48
|
+
host="github.com",
|
|
49
|
+
)
|
|
50
|
+
print(result) # → {"id": 42, "url": "https://github.com/..."}
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
import httpx
|
|
54
|
+
except ImportError:
|
|
55
|
+
raise ImportError(
|
|
56
|
+
"agentdom dispatch requires httpx: pip install agentdom[dispatch]"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
api_base = agentdom_api or os.environ.get("AGENTDOM_API_URL", "https://api.getagentdom.com")
|
|
60
|
+
|
|
61
|
+
# Resolve auth token from env if not provided
|
|
62
|
+
if not token:
|
|
63
|
+
env_key = host.upper().replace(".", "_") + "_TOKEN"
|
|
64
|
+
token = os.environ.get(env_key) or os.environ.get("AGENTDOM_TOKEN")
|
|
65
|
+
|
|
66
|
+
headers = {"Content-Type": "application/json"}
|
|
67
|
+
if token:
|
|
68
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
69
|
+
|
|
70
|
+
payload = {"intent": intent, "host": host, "args": args}
|
|
71
|
+
|
|
72
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
73
|
+
res = await client.post(
|
|
74
|
+
f"{api_base}/api/agentdom/dispatch",
|
|
75
|
+
headers=headers,
|
|
76
|
+
json=payload,
|
|
77
|
+
)
|
|
78
|
+
res.raise_for_status()
|
|
79
|
+
data = res.json()
|
|
80
|
+
return data.get("result", data)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def dispatch_intent_sync(
|
|
84
|
+
intent: str,
|
|
85
|
+
args: dict,
|
|
86
|
+
host: str,
|
|
87
|
+
token: Optional[str] = None,
|
|
88
|
+
agentdom_api: Optional[str] = None,
|
|
89
|
+
) -> Any:
|
|
90
|
+
"""
|
|
91
|
+
Synchronous version of dispatch_intent for non-async code.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
from agentdom import dispatch_intent_sync
|
|
95
|
+
|
|
96
|
+
result = dispatch_intent_sync(
|
|
97
|
+
"repos.list",
|
|
98
|
+
{"username": "octocat"},
|
|
99
|
+
host="github.com",
|
|
100
|
+
)
|
|
101
|
+
"""
|
|
102
|
+
import asyncio
|
|
103
|
+
return asyncio.run(dispatch_intent(intent, args, host, token, agentdom_api))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agentdom"
|
|
7
|
+
version = "3.6.0"
|
|
8
|
+
description = "The universal runtime that makes every website, desktop app, and API accessible to AI agents — zero human in the loop."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Ragavendhra Machikatla", email = "hi@getagentdom.com" }
|
|
13
|
+
]
|
|
14
|
+
keywords = [
|
|
15
|
+
"ai", "agents", "mcp", "automation", "browser", "rpa",
|
|
16
|
+
"llm", "agentdom", "dispatch", "protocol"
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
29
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
31
|
+
]
|
|
32
|
+
requires-python = ">=3.9"
|
|
33
|
+
|
|
34
|
+
# Core has no required dependencies — web framework support is optional
|
|
35
|
+
dependencies = []
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
flask = ["flask>=2.0"]
|
|
39
|
+
fastapi = ["fastapi>=0.100", "uvicorn>=0.20"]
|
|
40
|
+
dispatch = ["httpx>=0.25"]
|
|
41
|
+
all = ["flask>=2.0", "fastapi>=0.100", "uvicorn>=0.20", "httpx>=0.25"]
|
|
42
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.23", "httpx>=0.25", "flake8>=7", "build", "twine"]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://getagentdom.com"
|
|
46
|
+
Documentation = "https://getagentdom.com/docs/python"
|
|
47
|
+
Repository = "https://github.com/RagavRida/agentdom"
|
|
48
|
+
Changelog = "https://github.com/RagavRida/agentdom/blob/main/CHANGELOG.md"
|
|
49
|
+
"Bug Tracker" = "https://github.com/RagavRida/agentdom/issues"
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = ["agentdom"]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.sdist]
|
|
55
|
+
include = [
|
|
56
|
+
"agentdom/",
|
|
57
|
+
"README.md",
|
|
58
|
+
"LICENSE",
|
|
59
|
+
"pyproject.toml",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
asyncio_mode = "auto"
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
|
|
66
|
+
[tool.flake8]
|
|
67
|
+
max-line-length = 120
|
|
68
|
+
extend-ignore = ["E501"]
|