authyouragent 0.3.2__tar.gz → 0.3.4__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.
- {authyouragent-0.3.2 → authyouragent-0.3.4}/PKG-INFO +7 -4
- {authyouragent-0.3.2 → authyouragent-0.3.4}/README.md +6 -3
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/__init__.py +1 -1
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/mcp_server.py +62 -5
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/vault_cli.py +42 -16
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/PKG-INFO +7 -4
- {authyouragent-0.3.2 → authyouragent-0.3.4}/pyproject.toml +1 -1
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/__main__.py +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/agent.py +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/site.py +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/takeover.py +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent/vault_seccomp.json +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/SOURCES.txt +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/dependency_links.txt +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/entry_points.txt +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/requires.txt +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/authyouragent.egg-info/top_level.txt +0 -0
- {authyouragent-0.3.2 → authyouragent-0.3.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authyouragent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Auth Your Agent SDK: let AI agents act for a user on websites, with the user's approval on their phone.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://authyouragent.com
|
|
@@ -40,7 +40,9 @@ authyouragent vault up --agent-id ag_xxxxx --key /path/to/agent-key.pem
|
|
|
40
40
|
|
|
41
41
|
`vault up` starts the browser vault: the browser your agent uses, in a
|
|
42
42
|
container on your machine. The agent drives it through the vault and never
|
|
43
|
-
gets its cookies. It prints the `env` block for your MCP client.
|
|
43
|
+
gets its cookies. It prints the `env` block for your MCP client. If you skip
|
|
44
|
+
it, the MCP server starts the vault itself the first time the agent needs the
|
|
45
|
+
browser (`AYA_VAULT_AUTOSTART=0` turns that off). See
|
|
44
46
|
[vault/README.md](https://github.com/kjames2001/authyouragent/blob/main/vault/README.md)
|
|
45
47
|
for what protects what, and the known limits.
|
|
46
48
|
|
|
@@ -50,8 +52,9 @@ For just the SDK (no browser, no MCP): `pip install authyouragent`.
|
|
|
50
52
|
|
|
51
53
|
The `authyouragent-mcp` command starts an MCP server with eleven tools:
|
|
52
54
|
|
|
53
|
-
- **Browser:** `navigate`, `click`, `type_text`, `read_page`.
|
|
54
|
-
|
|
55
|
+
- **Browser:** `navigate`, `click`, `type_text`, `read_page`. Only public
|
|
56
|
+
websites open. Clicks that submit a form, or whose button says create, send,
|
|
57
|
+
save, delete, pay and the like, wait for your approval on your phone.
|
|
55
58
|
- **`check_login_wall`**: is the page asking for a password, a code or a
|
|
56
59
|
sign-in approval?
|
|
57
60
|
- **`request_takeover`** / **`wait_for_takeover`**: you take over the browser
|
|
@@ -22,7 +22,9 @@ authyouragent vault up --agent-id ag_xxxxx --key /path/to/agent-key.pem
|
|
|
22
22
|
|
|
23
23
|
`vault up` starts the browser vault: the browser your agent uses, in a
|
|
24
24
|
container on your machine. The agent drives it through the vault and never
|
|
25
|
-
gets its cookies. It prints the `env` block for your MCP client.
|
|
25
|
+
gets its cookies. It prints the `env` block for your MCP client. If you skip
|
|
26
|
+
it, the MCP server starts the vault itself the first time the agent needs the
|
|
27
|
+
browser (`AYA_VAULT_AUTOSTART=0` turns that off). See
|
|
26
28
|
[vault/README.md](https://github.com/kjames2001/authyouragent/blob/main/vault/README.md)
|
|
27
29
|
for what protects what, and the known limits.
|
|
28
30
|
|
|
@@ -32,8 +34,9 @@ For just the SDK (no browser, no MCP): `pip install authyouragent`.
|
|
|
32
34
|
|
|
33
35
|
The `authyouragent-mcp` command starts an MCP server with eleven tools:
|
|
34
36
|
|
|
35
|
-
- **Browser:** `navigate`, `click`, `type_text`, `read_page`.
|
|
36
|
-
|
|
37
|
+
- **Browser:** `navigate`, `click`, `type_text`, `read_page`. Only public
|
|
38
|
+
websites open. Clicks that submit a form, or whose button says create, send,
|
|
39
|
+
save, delete, pay and the like, wait for your approval on your phone.
|
|
37
40
|
- **`check_login_wall`**: is the page asking for a password, a code or a
|
|
38
41
|
sign-in approval?
|
|
39
42
|
- **`request_takeover`** / **`wait_for_takeover`**: you take over the browser
|
|
@@ -17,6 +17,8 @@ Environment:
|
|
|
17
17
|
AYA_CLOUD default https://authyouragent.com
|
|
18
18
|
AYA_VAULT_URL the vault broker (default http://127.0.0.1:7801)
|
|
19
19
|
AYA_VAULT_TOKEN_FILE file holding the vault's bearer token (or AYA_VAULT_TOKEN)
|
|
20
|
+
AYA_VAULT_AUTOSTART default 1: start the local vault (Docker) on first use if
|
|
21
|
+
it is not running. Set 0 to manage it with `authyouragent vault`.
|
|
20
22
|
|
|
21
23
|
Tools: navigate, click, type_text, read_page, check_login_wall, request_takeover,
|
|
22
24
|
wait_for_takeover, request_approval, end_session, check_agent_status,
|
|
@@ -30,6 +32,8 @@ import os
|
|
|
30
32
|
import re
|
|
31
33
|
import sys
|
|
32
34
|
import time
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from urllib.parse import urlparse
|
|
33
37
|
|
|
34
38
|
import httpx
|
|
35
39
|
|
|
@@ -44,11 +48,13 @@ DEFAULT_CLOUD = "https://authyouragent.com"
|
|
|
44
48
|
WAIT_DEFAULT = 240 # stay under common MCP client call timeouts (300s)
|
|
45
49
|
WAIT_MAX = 290
|
|
46
50
|
HEARTBEAT_S = 20
|
|
51
|
+
AUTOSTART_WAIT = 120 # the first start downloads the image (~500 MB)
|
|
47
52
|
|
|
48
53
|
mcp = FastMCP("authyouragent")
|
|
49
54
|
_agent = None
|
|
50
55
|
_vault = None
|
|
51
56
|
_hb = None
|
|
57
|
+
_starting = None # the background task starting the vault, if any
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
def _log(*a):
|
|
@@ -67,6 +73,46 @@ def _get_agent():
|
|
|
67
73
|
return _agent
|
|
68
74
|
|
|
69
75
|
|
|
76
|
+
class NoVaultToken(RuntimeError):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _autostart_ok():
|
|
81
|
+
"""Start the vault ourselves only when it is the local one `vault up` would
|
|
82
|
+
create: default address, and the token file it writes (or none set)."""
|
|
83
|
+
if os.environ.get("AYA_VAULT_AUTOSTART", "1").strip().lower() in ("0", "false", "no", "off"):
|
|
84
|
+
return False
|
|
85
|
+
if os.environ.get("AYA_VAULT_TOKEN"):
|
|
86
|
+
return False
|
|
87
|
+
u = urlparse(os.environ.get("AYA_VAULT_URL", "http://127.0.0.1:7801"))
|
|
88
|
+
if u.hostname not in ("127.0.0.1", "localhost") or u.port != 7801:
|
|
89
|
+
return False
|
|
90
|
+
from . import vault_cli
|
|
91
|
+
tf = os.environ.get("AYA_VAULT_TOKEN_FILE")
|
|
92
|
+
return not tf or Path(tf).expanduser().resolve() == (vault_cli.HOME / "token").resolve()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def _ensure_vault():
|
|
96
|
+
"""Start the local vault in the background and wait for it, up to
|
|
97
|
+
AUTOSTART_WAIT. If it is still starting, say so; the next call waits again."""
|
|
98
|
+
global _starting, _vault
|
|
99
|
+
from . import vault_cli
|
|
100
|
+
if _starting is None or (_starting.done() and (_starting.cancelled() or _starting.exception())):
|
|
101
|
+
_log("the browser vault is not running: starting it")
|
|
102
|
+
_starting = asyncio.create_task(asyncio.to_thread(
|
|
103
|
+
vault_cli.start, os.environ.get("AYA_AGENT_ID"), os.environ.get("AYA_KEY_FILE"),
|
|
104
|
+
cloud=os.environ.get("AYA_CLOUD", DEFAULT_CLOUD), log=_log))
|
|
105
|
+
done, _ = await asyncio.wait({_starting}, timeout=AUTOSTART_WAIT)
|
|
106
|
+
if not done:
|
|
107
|
+
raise RuntimeError("the browser vault is starting (the first start downloads about 500 MB). "
|
|
108
|
+
"Call the tool again in a minute.")
|
|
109
|
+
if _starting.exception():
|
|
110
|
+
raise RuntimeError(f"could not start the browser vault: {_starting.exception()}")
|
|
111
|
+
if _vault is not None: # the token may be new: connect again
|
|
112
|
+
await _vault.aclose()
|
|
113
|
+
_vault = None
|
|
114
|
+
|
|
115
|
+
|
|
70
116
|
def _vault_client():
|
|
71
117
|
global _vault
|
|
72
118
|
if _vault is None:
|
|
@@ -79,7 +125,7 @@ def _vault_client():
|
|
|
79
125
|
if os.path.exists(default):
|
|
80
126
|
token = open(default).read().strip()
|
|
81
127
|
if not token:
|
|
82
|
-
raise
|
|
128
|
+
raise NoVaultToken("no vault token: run `authyouragent vault up`, or set AYA_VAULT_TOKEN_FILE")
|
|
83
129
|
_vault = httpx.AsyncClient(base_url=os.environ.get("AYA_VAULT_URL", "http://127.0.0.1:7801"),
|
|
84
130
|
headers={"Authorization": f"Bearer {token}"},
|
|
85
131
|
timeout=httpx.Timeout(60, read=WAIT_MAX + 30))
|
|
@@ -101,10 +147,21 @@ async def _call(method, path, **kw):
|
|
|
101
147
|
global _hb
|
|
102
148
|
if _hb is None or _hb.done():
|
|
103
149
|
_hb = asyncio.create_task(_heartbeat())
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
150
|
+
if _starting is not None and not _starting.done():
|
|
151
|
+
await _ensure_vault() # a start is under way: wait for it, never race it
|
|
152
|
+
for attempt in (1, 2):
|
|
153
|
+
try:
|
|
154
|
+
r = await _vault_client().request(method, path, **kw)
|
|
155
|
+
break
|
|
156
|
+
except (httpx.ConnectError, NoVaultToken) as e:
|
|
157
|
+
if attempt == 2 or not _autostart_ok():
|
|
158
|
+
if isinstance(e, NoVaultToken):
|
|
159
|
+
raise
|
|
160
|
+
raise RuntimeError("cannot reach the browser vault (not running). "
|
|
161
|
+
"Start it with `authyouragent vault up`.")
|
|
162
|
+
await _ensure_vault()
|
|
163
|
+
except httpx.HTTPError as e:
|
|
164
|
+
raise RuntimeError(f"cannot reach the browser vault ({type(e).__name__}); is it running?")
|
|
108
165
|
data = r.json()
|
|
109
166
|
if r.status_code == 409:
|
|
110
167
|
raise RuntimeError(data.get("detail", "busy"))
|
|
@@ -34,9 +34,12 @@ PORT = 7801
|
|
|
34
34
|
VAULT_UID = 10001
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
class VaultError(RuntimeError):
|
|
38
|
+
"""Starting or reaching the vault failed; the message says why."""
|
|
39
|
+
|
|
40
|
+
|
|
37
41
|
def _die(msg) -> NoReturn:
|
|
38
|
-
|
|
39
|
-
sys.exit(1)
|
|
42
|
+
raise VaultError(msg)
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
def _docker(*args, check=True, capture=True):
|
|
@@ -118,37 +121,57 @@ def _wait_ready(token, seconds=60):
|
|
|
118
121
|
return False
|
|
119
122
|
|
|
120
123
|
|
|
121
|
-
def
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
def start(agent_id, key, cloud="https://authyouragent.com", image=IMAGE, gvisor="auto",
|
|
125
|
+
size="412x860", scale="2", pull=True, log=None):
|
|
126
|
+
"""Start the vault, or wait for it if its container is already running.
|
|
127
|
+
Returns "started" or "running". Raises VaultError. Used by `vault up` and
|
|
128
|
+
by the MCP server when the vault is needed and not running; `log` gets
|
|
129
|
+
progress lines (the MCP server sends them to stderr, never stdout)."""
|
|
130
|
+
log = log or (lambda *a: None)
|
|
124
131
|
if not agent_id or not key:
|
|
125
132
|
_die("need --agent-id and --key (or AYA_AGENT_ID / AYA_KEY_FILE)")
|
|
126
133
|
if _running():
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
# e.g. Docker is restarting it after a reboot: give it time to come up
|
|
135
|
+
if not _wait_ready(_token()):
|
|
136
|
+
_die(f"the vault container is running but not answering; see `docker logs {NAME}`")
|
|
137
|
+
return "running"
|
|
129
138
|
_docker("rm", "-f", NAME, check=False)
|
|
130
139
|
token = _token()
|
|
131
|
-
|
|
132
|
-
if
|
|
140
|
+
use_gvisor = {"auto": _has_gvisor(), "on": True, "off": False}[gvisor]
|
|
141
|
+
if gvisor == "on" and not _has_gvisor():
|
|
133
142
|
_die("--gvisor on, but Docker has no 'runsc' runtime. See https://gvisor.dev/docs/user_guide/install/")
|
|
134
|
-
if
|
|
135
|
-
|
|
136
|
-
_docker("pull", "-q",
|
|
143
|
+
if pull:
|
|
144
|
+
log(f"pulling {image} ...")
|
|
145
|
+
r = _docker("pull", "-q", image, check=False)
|
|
146
|
+
if r.returncode != 0:
|
|
147
|
+
if _docker("image", "inspect", image, check=False).returncode != 0:
|
|
148
|
+
_die(f"docker pull failed: {(r.stderr or r.stdout).strip()[:400]}")
|
|
149
|
+
log("pull failed; using the copy already on this machine")
|
|
137
150
|
args = ["run", "-d", "--name", NAME, "--restart", "unless-stopped",
|
|
138
151
|
"--shm-size=1g", "--stop-timeout", "120",
|
|
139
152
|
"--security-opt", f"seccomp={_seccomp()}",
|
|
140
153
|
"--security-opt", "no-new-privileges", "--cap-drop", "ALL",
|
|
141
154
|
"-e", f"VAULT_TOKEN={token}", "-e", f"AYA_AGENT_ID={agent_id}",
|
|
142
|
-
"-e", f"AYA_CLOUD={
|
|
155
|
+
"-e", f"AYA_CLOUD={cloud}", "-e", f"VAULT_SIZE={size}", "-e", f"VAULT_SCALE={scale}",
|
|
143
156
|
"-v", f"{_key_copy(key)}:/run/secrets/agent.pem:ro",
|
|
144
157
|
"-v", f"{_state_dir()}:/var/lib/vault",
|
|
145
158
|
"-p", f"127.0.0.1:{PORT}:{PORT}"]
|
|
146
|
-
if
|
|
159
|
+
if use_gvisor:
|
|
147
160
|
args += ["--runtime", "runsc"]
|
|
148
|
-
_docker(*args,
|
|
161
|
+
_docker(*args, image)
|
|
149
162
|
if not _wait_ready(token):
|
|
150
163
|
_die(f"started but not answering; see `docker logs {NAME}`")
|
|
151
|
-
|
|
164
|
+
log(f"vault running on 127.0.0.1:{PORT} (browser sandbox: on, gVisor: {'on' if use_gvisor else 'off'})")
|
|
165
|
+
return "started"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def up(a):
|
|
169
|
+
if _running():
|
|
170
|
+
print(f"already running ({NAME}). `authyouragent vault down` first to restart.")
|
|
171
|
+
return
|
|
172
|
+
start(a.agent_id or os.environ.get("AYA_AGENT_ID"), a.key or os.environ.get("AYA_KEY_FILE"),
|
|
173
|
+
cloud=a.cloud, image=a.image, gvisor=a.gvisor, size=a.size, scale=a.scale,
|
|
174
|
+
pull=not a.no_pull, log=print)
|
|
152
175
|
print("MCP server env:")
|
|
153
176
|
env(a, quiet=True)
|
|
154
177
|
|
|
@@ -210,5 +233,8 @@ def main(argv=None):
|
|
|
210
233
|
a = ap.parse_args(argv)
|
|
211
234
|
try:
|
|
212
235
|
a.fn(a)
|
|
236
|
+
except VaultError as e:
|
|
237
|
+
print(f"authyouragent vault: {e}", file=sys.stderr)
|
|
238
|
+
sys.exit(1)
|
|
213
239
|
except BrokenPipeError: # output piped into e.g. `head`
|
|
214
240
|
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authyouragent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Auth Your Agent SDK: let AI agents act for a user on websites, with the user's approval on their phone.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://authyouragent.com
|
|
@@ -40,7 +40,9 @@ authyouragent vault up --agent-id ag_xxxxx --key /path/to/agent-key.pem
|
|
|
40
40
|
|
|
41
41
|
`vault up` starts the browser vault: the browser your agent uses, in a
|
|
42
42
|
container on your machine. The agent drives it through the vault and never
|
|
43
|
-
gets its cookies. It prints the `env` block for your MCP client.
|
|
43
|
+
gets its cookies. It prints the `env` block for your MCP client. If you skip
|
|
44
|
+
it, the MCP server starts the vault itself the first time the agent needs the
|
|
45
|
+
browser (`AYA_VAULT_AUTOSTART=0` turns that off). See
|
|
44
46
|
[vault/README.md](https://github.com/kjames2001/authyouragent/blob/main/vault/README.md)
|
|
45
47
|
for what protects what, and the known limits.
|
|
46
48
|
|
|
@@ -50,8 +52,9 @@ For just the SDK (no browser, no MCP): `pip install authyouragent`.
|
|
|
50
52
|
|
|
51
53
|
The `authyouragent-mcp` command starts an MCP server with eleven tools:
|
|
52
54
|
|
|
53
|
-
- **Browser:** `navigate`, `click`, `type_text`, `read_page`.
|
|
54
|
-
|
|
55
|
+
- **Browser:** `navigate`, `click`, `type_text`, `read_page`. Only public
|
|
56
|
+
websites open. Clicks that submit a form, or whose button says create, send,
|
|
57
|
+
save, delete, pay and the like, wait for your approval on your phone.
|
|
55
58
|
- **`check_login_wall`**: is the page asking for a password, a code or a
|
|
56
59
|
sign-in approval?
|
|
57
60
|
- **`request_takeover`** / **`wait_for_takeover`**: you take over the browser
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "authyouragent"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.4"
|
|
8
8
|
description = "Auth Your Agent SDK: let AI agents act for a user on websites, with the user's approval on their phone."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|