agentvisa-fastapi 0.1.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.
@@ -0,0 +1,42 @@
1
+ """
2
+ agentvisa-fastapi — AgentVisa verification middleware for FastAPI.
3
+
4
+ Quick start:
5
+
6
+ from agentvisa_fastapi import AgentVisaConfig, AgentVisaMiddleware, require_agentvisa
7
+
8
+ config = AgentVisaConfig(
9
+ api_key="your_widget_api_key", # server-side only
10
+ widget_id="your_widget_id", # from your AgentVisa dashboard
11
+ )
12
+
13
+ # Option A: middleware (enriches all requests, doesn't block)
14
+ app.add_middleware(AgentVisaMiddleware, config=config)
15
+
16
+ # Option B: per-route dependency (blocks unverified agents with 401)
17
+ @app.get("/protected")
18
+ async def handler(av=Depends(require_agentvisa(config))):
19
+ return {"verified": True, "plan": av.plan}
20
+
21
+ # Option C: both (recommended — middleware caches result, dependency enforces)
22
+ app.add_middleware(AgentVisaMiddleware, config=config)
23
+
24
+ @app.get("/protected")
25
+ async def handler(av=Depends(require_agentvisa(config))):
26
+ return {"plan": av.plan, "human_name": av.human_name}
27
+ """
28
+
29
+ from .config import AgentVisaConfig
30
+ from .middleware import AgentVisaMiddleware
31
+ from .dependencies import get_agentvisa, require_agentvisa
32
+ from .models import AgentVisaResult
33
+
34
+ __all__ = [
35
+ "AgentVisaConfig",
36
+ "AgentVisaMiddleware",
37
+ "get_agentvisa",
38
+ "require_agentvisa",
39
+ "AgentVisaResult",
40
+ ]
41
+
42
+ __version__ = "0.1.0"
@@ -0,0 +1,44 @@
1
+ """
2
+ Async HTTP client for POST /v1/verify.
3
+ Uses httpx with a shared client for connection pooling.
4
+ """
5
+ from __future__ import annotations
6
+ import httpx
7
+ from .config import AgentVisaConfig
8
+ from .models import AgentVisaResult
9
+
10
+
11
+ async def verify_token(
12
+ token: str,
13
+ config: AgentVisaConfig,
14
+ human_assertion: str | None = None,
15
+ ) -> AgentVisaResult:
16
+ """
17
+ Call POST /v1/verify and return a parsed AgentVisaResult.
18
+ Never raises — returns AgentVisaResult.error() on any network/API failure.
19
+ """
20
+ url = f"{config.api_base}/v1/verify"
21
+ params: dict = {"token": token, "widget_id": config.widget_id}
22
+ if human_assertion is not None:
23
+ params["human_assertion"] = human_assertion
24
+
25
+ try:
26
+ async with httpx.AsyncClient(timeout=config.timeout) as client:
27
+ response = await client.post(
28
+ url,
29
+ params=params,
30
+ headers={"X-Widget-Api-Key": config.api_key},
31
+ )
32
+ if response.status_code == 401:
33
+ return AgentVisaResult.error("invalid_api_key")
34
+ if response.status_code == 404:
35
+ return AgentVisaResult.error("invalid_widget")
36
+ if not response.is_success:
37
+ return AgentVisaResult.error(f"api_error_{response.status_code}")
38
+
39
+ return AgentVisaResult.from_api(response.json())
40
+
41
+ except httpx.TimeoutException:
42
+ return AgentVisaResult.error("verification_timeout")
43
+ except Exception:
44
+ return AgentVisaResult.error("verification_error")
@@ -0,0 +1,30 @@
1
+ """
2
+ AgentVisa configuration — holds the Widget Holder's credentials.
3
+ """
4
+ from __future__ import annotations
5
+ from dataclasses import dataclass, field
6
+
7
+
8
+ @dataclass
9
+ class AgentVisaConfig:
10
+ """
11
+ Configuration for the AgentVisa middleware and dependencies.
12
+
13
+ Args:
14
+ api_key: Your Widget Holder API key (X-Widget-Api-Key).
15
+ Keep this server-side only — never expose in client code.
16
+ widget_id: Your widget ID (public, from your AgentVisa dashboard).
17
+ api_base: Override the AgentVisa API base URL (default: https://api.agentvisa.ai).
18
+ timeout: HTTP timeout in seconds for /v1/verify calls (default: 5).
19
+ """
20
+ api_key: str
21
+ widget_id: str
22
+ api_base: str = "https://api.agentvisa.ai"
23
+ timeout: float = 5.0
24
+
25
+ def __post_init__(self):
26
+ if not self.api_key:
27
+ raise ValueError("AgentVisaConfig: api_key is required")
28
+ if not self.widget_id:
29
+ raise ValueError("AgentVisaConfig: widget_id is required")
30
+ self.api_base = self.api_base.rstrip("/")
@@ -0,0 +1,92 @@
1
+ """
2
+ FastAPI dependencies for AgentVisa verification.
3
+
4
+ Two options:
5
+
6
+ 1. get_agentvisa(config) — returns the result, never blocks.
7
+ Use when you want to check verification status yourself.
8
+
9
+ 2. require_agentvisa(config) — returns the result OR raises HTTP 401.
10
+ Use when the route must be verified to proceed.
11
+
12
+ Both work with or without AgentVisaMiddleware installed.
13
+ If the middleware is present, the cached result is reused (no second API call).
14
+ If not, the dependency calls /v1/verify itself.
15
+ """
16
+ from __future__ import annotations
17
+ from fastapi import Depends, HTTPException, Request
18
+ from .config import AgentVisaConfig
19
+ from .client import verify_token
20
+ from .models import AgentVisaResult
21
+
22
+
23
+ def get_agentvisa(config: AgentVisaConfig):
24
+ """
25
+ FastAPI dependency — returns AgentVisaResult, never raises.
26
+
27
+ Usage:
28
+ @app.get("/my-route")
29
+ async def handler(av: AgentVisaResult = Depends(get_agentvisa(config))):
30
+ if av.valid:
31
+ ...
32
+ """
33
+ async def _dependency(request: Request) -> AgentVisaResult:
34
+ # If middleware already ran, reuse its result
35
+ existing = getattr(request.state, "agentvisa", None)
36
+ if existing is not None:
37
+ return existing
38
+
39
+ token = request.headers.get("X-AgentVisa-Token")
40
+ if not token:
41
+ return AgentVisaResult.not_present()
42
+
43
+ return await verify_token(token, config)
44
+
45
+ return _dependency
46
+
47
+
48
+ def require_agentvisa(config: AgentVisaConfig):
49
+ """
50
+ FastAPI dependency — returns AgentVisaResult or raises HTTP 401.
51
+
52
+ Usage:
53
+ @app.get("/protected")
54
+ async def handler(av: AgentVisaResult = Depends(require_agentvisa(config))):
55
+ # Only reaches here if av.valid is True
56
+ return {"human_name": av.human_name}
57
+ """
58
+ async def _dependency(
59
+ result: AgentVisaResult = Depends(get_agentvisa(config)),
60
+ ) -> AgentVisaResult:
61
+ if not result.valid:
62
+ reason = result.reason
63
+
64
+ # Tell the agent what it needs to do next
65
+ if reason == "token_not_present":
66
+ raise HTTPException(
67
+ status_code=401,
68
+ detail="AgentVisa token required",
69
+ headers={"X-AgentVisa-Required": config.widget_id},
70
+ )
71
+ if reason == "reverification_required":
72
+ raise HTTPException(
73
+ status_code=401,
74
+ detail="AgentVisa reverification required — check email",
75
+ headers={"X-AgentVisa-Required": config.widget_id},
76
+ )
77
+ if reason in ("expired", "revoked"):
78
+ raise HTTPException(
79
+ status_code=401,
80
+ detail=f"AgentVisa token {reason}",
81
+ headers={"X-AgentVisa-Required": config.widget_id},
82
+ )
83
+
84
+ raise HTTPException(
85
+ status_code=401,
86
+ detail=f"AgentVisa verification failed: {reason}",
87
+ headers={"X-AgentVisa-Required": config.widget_id},
88
+ )
89
+
90
+ return result
91
+
92
+ return _dependency
@@ -0,0 +1,47 @@
1
+ """
2
+ AgentVisa Starlette middleware.
3
+
4
+ Reads X-AgentVisa-Token from the request, calls /v1/verify,
5
+ and injects the result into request.state.agentvisa.
6
+
7
+ Does NOT block requests — use the require_agentvisa dependency for that.
8
+ This lets you enrich all requests and make per-route decisions.
9
+ """
10
+ from __future__ import annotations
11
+ from starlette.middleware.base import BaseHTTPMiddleware
12
+ from starlette.requests import Request
13
+ from starlette.responses import Response
14
+ from .config import AgentVisaConfig
15
+ from .client import verify_token
16
+ from .models import AgentVisaResult
17
+
18
+
19
+ class AgentVisaMiddleware(BaseHTTPMiddleware):
20
+ """
21
+ Drop-in Starlette/FastAPI middleware.
22
+
23
+ Usage:
24
+ app.add_middleware(
25
+ AgentVisaMiddleware,
26
+ config=AgentVisaConfig(api_key="...", widget_id="..."),
27
+ )
28
+
29
+ After adding, every request will have request.state.agentvisa set
30
+ to an AgentVisaResult. No requests are blocked — use require_agentvisa
31
+ as a dependency on individual routes to enforce verification.
32
+ """
33
+
34
+ def __init__(self, app, config: AgentVisaConfig):
35
+ super().__init__(app)
36
+ self.config = config
37
+
38
+ async def dispatch(self, request: Request, call_next) -> Response:
39
+ token = request.headers.get("X-AgentVisa-Token")
40
+
41
+ if token:
42
+ result = await verify_token(token, self.config)
43
+ else:
44
+ result = AgentVisaResult.not_present()
45
+
46
+ request.state.agentvisa = result
47
+ return await call_next(request)
@@ -0,0 +1,71 @@
1
+ """
2
+ AgentVisa result models — mirroring the /v1/verify response schema.
3
+ """
4
+ from __future__ import annotations
5
+ from dataclasses import dataclass, field
6
+ from datetime import datetime
7
+ from typing import Literal, Optional
8
+
9
+
10
+ @dataclass
11
+ class AgentVisaResult:
12
+ """
13
+ The parsed response from POST /v1/verify.
14
+ Injected into request.state.agentvisa by the middleware.
15
+ """
16
+ # Always present
17
+ valid: bool
18
+ reason: str
19
+ plan: str
20
+ widget_id: str
21
+
22
+ # Present on valid tokens
23
+ verified_at: Optional[datetime] = None
24
+ expires_at: Optional[datetime] = None
25
+
26
+ # Pro plan metadata
27
+ human_name: Optional[str] = None
28
+ five_factor: Optional[Literal["y", "n"]] = None
29
+ age_over_18: Optional[Literal["y", "n", "null"]] = None
30
+ age_over_21: Optional[Literal["y", "n", "null"]] = None
31
+ multiple_agents_authorized: Optional[Literal["y", "n"]] = None
32
+ verifications_today: Optional[int] = None
33
+
34
+ # Set when no token was present in the request at all
35
+ skipped: bool = False
36
+
37
+ @classmethod
38
+ def from_api(cls, data: dict) -> "AgentVisaResult":
39
+ """Build from the raw /v1/verify JSON response."""
40
+ def parse_dt(val: Optional[str]) -> Optional[datetime]:
41
+ if not val:
42
+ return None
43
+ try:
44
+ return datetime.fromisoformat(val.replace("Z", "+00:00"))
45
+ except Exception:
46
+ return None
47
+
48
+ return cls(
49
+ valid=data.get("valid", False),
50
+ reason=data.get("reason", "unknown"),
51
+ plan=data.get("plan", "basic"),
52
+ widget_id=data.get("widget_id", ""),
53
+ verified_at=parse_dt(data.get("verified_at")),
54
+ expires_at=parse_dt(data.get("expires_at")),
55
+ human_name=data.get("human_name"),
56
+ five_factor=data.get("five_factor"),
57
+ age_over_18=data.get("age_over_18"),
58
+ age_over_21=data.get("age_over_21"),
59
+ multiple_agents_authorized=data.get("multiple_agents_authorized"),
60
+ verifications_today=data.get("verifications_today"),
61
+ )
62
+
63
+ @classmethod
64
+ def not_present(cls) -> "AgentVisaResult":
65
+ """Returned when no X-AgentVisa-Token header was in the request."""
66
+ return cls(valid=False, reason="token_not_present", plan="basic", widget_id="", skipped=True)
67
+
68
+ @classmethod
69
+ def error(cls, reason: str = "verification_error") -> "AgentVisaResult":
70
+ """Returned when the /v1/verify call itself failed."""
71
+ return cls(valid=False, reason=reason, plan="basic", widget_id="")
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentvisa-fastapi
3
+ Version: 0.1.0
4
+ Summary: FastAPI middleware for AgentVisa AI agent verification
5
+ Author-email: AgentVisa <info@agentvisa.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 AgentVisa
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://agentvisa.ai
29
+ Project-URL: Repository, https://github.com/AgentVisa-ai/agentvisa-fastapi
30
+ Project-URL: Documentation, https://agentvisa.ai/docs
31
+ Project-URL: Bug Tracker, https://github.com/AgentVisa-ai/agentvisa-fastapi/issues
32
+ Keywords: agentvisa,fastapi,middleware,ai-agent,verification
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.9
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Framework :: FastAPI
42
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
43
+ Classifier: Topic :: Security
44
+ Requires-Python: >=3.9
45
+ Description-Content-Type: text/markdown
46
+ License-File: LICENSE
47
+ Requires-Dist: fastapi>=0.100.0
48
+ Requires-Dist: httpx>=0.24.0
49
+ Requires-Dist: starlette>=0.27.0
50
+ Provides-Extra: dev
51
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
52
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
53
+ Requires-Dist: httpx>=0.24.0; extra == "dev"
54
+ Dynamic: license-file
55
+
56
+ # agentvisa-fastapi
57
+
58
+ FastAPI middleware for [AgentVisa](https://agentvisa.ai) — verify that incoming AI agents were authorized by a real, 5-factor biometric-verified human before granting access.
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install agentvisa-fastapi
64
+ ```
65
+
66
+ ## Quick start
67
+
68
+ ```python
69
+ from fastapi import FastAPI, Depends
70
+ from agentvisa_fastapi import AgentVisaConfig, AgentVisaMiddleware, require_agentvisa
71
+
72
+ app = FastAPI()
73
+
74
+ config = AgentVisaConfig(
75
+ api_key=os.environ["AGENTVISA_API_KEY"], # your Widget Holder API key — server-side only
76
+ widget_id=os.environ["AGENTVISA_WIDGET_ID"], # from your AgentVisa dashboard
77
+ )
78
+
79
+ # Add middleware — enriches every request with request.state.agentvisa
80
+ app.add_middleware(AgentVisaMiddleware, config=config)
81
+
82
+ # Protected route — agents without a valid AgentVisa token get 401
83
+ @app.get("/api/orders")
84
+ async def get_orders(av=Depends(require_agentvisa(config))):
85
+ return {"orders": [...], "verified_human": av.human_name}
86
+
87
+ # Unprotected route — check manually if you want soft enforcement
88
+ @app.get("/api/public")
89
+ async def public(request: Request):
90
+ av = request.state.agentvisa
91
+ if av.valid:
92
+ return {"message": "Hello, verified agent!", "plan": av.plan}
93
+ return {"message": "Hello — consider getting an AgentVisa for full access."}
94
+ ```
95
+
96
+ ## How it works
97
+
98
+ When an AI agent hits your site, AgentVisa requires a two-step handshake:
99
+
100
+ 1. **Your server returns 401** with `X-AgentVisa-Required: your_widget_id` — this middleware does that automatically via `require_agentvisa`.
101
+ 2. **The agent calls AgentVisa** to get a short-lived TemporaryToken scoped to your site.
102
+ 3. **The agent retries** with `X-AgentVisa-Token: tmp_...` in the header.
103
+ 4. **This middleware calls `/v1/verify`** with your API key and returns the result.
104
+
105
+ The human behind the agent completed 5-factor biometric verification (email, phone, cross-verification, Face ID / Touch ID, and a human assertion) when they got their AgentVisa.
106
+
107
+ ## Configuration
108
+
109
+ ```python
110
+ AgentVisaConfig(
111
+ api_key="wk_...", # required — your Widget Holder API key
112
+ widget_id="wgt_...", # required — your widget ID
113
+ api_base="https://api.agentvisa.ai", # optional — override for self-hosted
114
+ timeout=5.0, # optional — /v1/verify timeout in seconds
115
+ )
116
+ ```
117
+
118
+ **Security note:** Never expose `api_key` in client-side code, environment variables that get bundled, or logs. Treat it like a database password.
119
+
120
+ ## AgentVisaResult fields
121
+
122
+ | Field | Type | Description |
123
+ |-------|------|-------------|
124
+ | `valid` | `bool` | Whether the agent is verified |
125
+ | `reason` | `str` | `"ok"`, `"invalid"`, `"expired"`, `"revoked"`, `"reverification_required"`, `"token_not_present"` |
126
+ | `plan` | `str` | `"basic"` or `"pro"` |
127
+ | `widget_id` | `str` | Your widget ID |
128
+ | `verified_at` | `datetime \| None` | When the token was issued |
129
+ | `expires_at` | `datetime \| None` | When the token expires |
130
+ | `human_name` | `str \| None` | Pro plan only |
131
+ | `five_factor` | `"y" \| "n" \| None` | Pro plan only |
132
+ | `age_over_18` | `"y" \| "n" \| "null" \| None` | Pro plan only |
133
+ | `age_over_21` | `"y" \| "n" \| "null" \| None` | Pro plan only |
134
+ | `multiple_agents_authorized` | `"y" \| "n" \| None` | Pro plan only |
135
+ | `verifications_today` | `int \| None` | Pro plan only |
136
+ | `skipped` | `bool` | True when no token was in the request |
137
+
138
+ ## Using without the middleware
139
+
140
+ If you don't want to add middleware, use the dependency standalone — it will call `/v1/verify` directly:
141
+
142
+ ```python
143
+ from agentvisa_fastapi import AgentVisaConfig, require_agentvisa
144
+
145
+ config = AgentVisaConfig(api_key="...", widget_id="...")
146
+
147
+ @app.post("/checkout")
148
+ async def checkout(av=Depends(require_agentvisa(config))):
149
+ # verified agents only
150
+ ...
151
+ ```
152
+
153
+ ## Using get_agentvisa for soft enforcement
154
+
155
+ ```python
156
+ from agentvisa_fastapi import AgentVisaConfig, get_agentvisa, AgentVisaResult
157
+
158
+ config = AgentVisaConfig(api_key="...", widget_id="...")
159
+
160
+ @app.get("/search")
161
+ async def search(av: AgentVisaResult = Depends(get_agentvisa(config))):
162
+ results = perform_search()
163
+ if not av.valid:
164
+ # Return limited results for unverified agents
165
+ return {"results": results[:3], "note": "Get an AgentVisa for full results"}
166
+ return {"results": results}
167
+ ```
168
+
169
+ ## Reverification
170
+
171
+ When an agent's daily verification limit is hit, `/v1/verify` returns `reason: "reverification_required"`. This middleware propagates that reason in the 401 response so your users know to check their email.
172
+
173
+ The `require_agentvisa` dependency returns:
174
+ ```
175
+ HTTP 401
176
+ X-AgentVisa-Required: your_widget_id
177
+ {"detail": "AgentVisa reverification required — check email"}
178
+ ```
179
+
180
+ The AgentVisa MCP server handles this automatically — it calls `POST /v1/holder/reverify` to trigger the re-verification email without the human needing to intervene.
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,11 @@
1
+ agentvisa_fastapi/__init__.py,sha256=-IYxB2Q4cjIZeTCLAwbjDlcyoU8uRhdJyx2ttLpNE7c,1323
2
+ agentvisa_fastapi/client.py,sha256=HBv5nmXajTCTn7jj9MKY4ywUsppZy8EHhYJud8C51OI,1540
3
+ agentvisa_fastapi/config.py,sha256=N9g-fD-WGPZzc1V9ljnuSdwFwUTlIfrbQp-AcTCPnGo,1062
4
+ agentvisa_fastapi/dependencies.py,sha256=7mT265uhfloIIREA2uXtp9JHLH2OlKIMAEWwFYZ4htI,3133
5
+ agentvisa_fastapi/middleware.py,sha256=4YcDovZ4g-Li-KR30mOoaGvQxWtI3WQcPypZeeKAuxA,1512
6
+ agentvisa_fastapi/models.py,sha256=8DyFugHdQEIOrX2qnx0XTieAoHN3SllucqG7VOs47mA,2560
7
+ agentvisa_fastapi-0.1.0.dist-info/licenses/LICENSE,sha256=2ZWgZFz10QZvvgGbDGi58YRqYwa7fClyV4ryle_u3fs,1066
8
+ agentvisa_fastapi-0.1.0.dist-info/METADATA,sha256=YB4bSB4HKwllNxWDIdgkJfWSuEgPoJimfUu5KU0vldY,7476
9
+ agentvisa_fastapi-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ agentvisa_fastapi-0.1.0.dist-info/top_level.txt,sha256=QblYKfRccBIqklfxQ7rqeolfg3XL5U3r8-th0-QCj0c,18
11
+ agentvisa_fastapi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentVisa
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 @@
1
+ agentvisa_fastapi