ageri 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.
- ageri-0.1.0/PKG-INFO +61 -0
- ageri-0.1.0/README.md +33 -0
- ageri-0.1.0/ageri/__init__.py +2 -0
- ageri-0.1.0/ageri/__main__.py +161 -0
- ageri-0.1.0/ageri/biography.py +582 -0
- ageri-0.1.0/ageri/bootstrap.py +39 -0
- ageri-0.1.0/ageri/bundle.py +153 -0
- ageri-0.1.0/ageri/capability_proxy.py +382 -0
- ageri-0.1.0/ageri/config_loader.py +259 -0
- ageri-0.1.0/ageri/daily_life.py +247 -0
- ageri-0.1.0/ageri/device.py +14 -0
- ageri-0.1.0/ageri/main.py +230 -0
- ageri-0.1.0/ageri/memory.py +729 -0
- ageri-0.1.0/ageri/notifications.py +147 -0
- ageri-0.1.0/ageri/orchestrator.py +999 -0
- ageri-0.1.0/ageri/personas.py +334 -0
- ageri-0.1.0/ageri/proactive.py +337 -0
- ageri-0.1.0/ageri/profile_store.py +418 -0
- ageri-0.1.0/ageri/pronouns.py +29 -0
- ageri-0.1.0/ageri/setup_wizard.py +315 -0
- ageri-0.1.0/ageri/skill.py +41 -0
- ageri-0.1.0/ageri/skills/__init__.py +1 -0
- ageri-0.1.0/ageri/skills/core/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/core/skill.py +483 -0
- ageri-0.1.0/ageri/skills/devops/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/devops/boss.py +379 -0
- ageri-0.1.0/ageri/skills/httpapi/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/httpapi/boss.py +266 -0
- ageri-0.1.0/ageri/skills/knowledge/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/knowledge/boss.py +275 -0
- ageri-0.1.0/ageri/skills/messenger/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/messenger/boss.py +345 -0
- ageri-0.1.0/ageri/skills/personal/__init__.py +1 -0
- ageri-0.1.0/ageri/skills/personal/boss.py +703 -0
- ageri-0.1.0/ageri/skills/research/__init__.py +1 -0
- ageri-0.1.0/ageri/skills/research/boss.py +421 -0
- ageri-0.1.0/ageri/skills/sentinel/__init__.py +1 -0
- ageri-0.1.0/ageri/skills/sentinel/boss.py +61 -0
- ageri-0.1.0/ageri/skills/telegram/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/telegram/boss.py +401 -0
- ageri-0.1.0/ageri/skills/whatsapp/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/whatsapp/boss.py +346 -0
- ageri-0.1.0/ageri/skills/zalo_oa/__init__.py +0 -0
- ageri-0.1.0/ageri/skills/zalo_oa/boss.py +388 -0
- ageri-0.1.0/ageri/slack_interface.py +244 -0
- ageri-0.1.0/ageri/tools.py +606 -0
- ageri-0.1.0/ageri/tunnel.py +143 -0
- ageri-0.1.0/ageri/web/__init__.py +1 -0
- ageri-0.1.0/ageri/web/app.py +61 -0
- ageri-0.1.0/ageri/web/auth.py +99 -0
- ageri-0.1.0/ageri/web/routes/__init__.py +0 -0
- ageri-0.1.0/ageri/web/routes/chat.py +366 -0
- ageri-0.1.0/ageri/web/routes/get_started.py +224 -0
- ageri-0.1.0/ageri/web/routes/login.py +50 -0
- ageri-0.1.0/ageri/web/routes/onboarding.py +187 -0
- ageri-0.1.0/ageri/web/templates/base.html +38 -0
- ageri-0.1.0/ageri/web/templates/chat.html +475 -0
- ageri-0.1.0/ageri/web/templates/get_started.html +191 -0
- ageri-0.1.0/ageri/web/templates/login.html +39 -0
- ageri-0.1.0/ageri/web/templates/onboarding.html +85 -0
- ageri-0.1.0/ageri/webhook.py +355 -0
- ageri-0.1.0/ageri/wizard.py +1280 -0
- ageri-0.1.0/ageri.egg-info/PKG-INFO +61 -0
- ageri-0.1.0/ageri.egg-info/SOURCES.txt +94 -0
- ageri-0.1.0/ageri.egg-info/dependency_links.txt +1 -0
- ageri-0.1.0/ageri.egg-info/entry_points.txt +2 -0
- ageri-0.1.0/ageri.egg-info/requires.txt +21 -0
- ageri-0.1.0/ageri.egg-info/top_level.txt +1 -0
- ageri-0.1.0/pyproject.toml +49 -0
- ageri-0.1.0/setup.cfg +4 -0
- ageri-0.1.0/tests/test_app_contract.py +317 -0
- ageri-0.1.0/tests/test_app_devops.py +572 -0
- ageri-0.1.0/tests/test_app_httpapi.py +397 -0
- ageri-0.1.0/tests/test_app_knowledge.py +425 -0
- ageri-0.1.0/tests/test_app_messenger.py +578 -0
- ageri-0.1.0/tests/test_app_personal.py +389 -0
- ageri-0.1.0/tests/test_app_registry.py +103 -0
- ageri-0.1.0/tests/test_app_research.py +704 -0
- ageri-0.1.0/tests/test_app_telegram.py +740 -0
- ageri-0.1.0/tests/test_app_whatsapp.py +638 -0
- ageri-0.1.0/tests/test_app_zalo_oa.py +752 -0
- ageri-0.1.0/tests/test_bootstrap.py +75 -0
- ageri-0.1.0/tests/test_bundle.py +178 -0
- ageri-0.1.0/tests/test_capability_proxy.py +462 -0
- ageri-0.1.0/tests/test_config_loader.py +410 -0
- ageri-0.1.0/tests/test_core_skill.py +518 -0
- ageri-0.1.0/tests/test_knowledge_engine.py +294 -0
- ageri-0.1.0/tests/test_memory.py +207 -0
- ageri-0.1.0/tests/test_orchestrator.py +964 -0
- ageri-0.1.0/tests/test_profile_store.py +307 -0
- ageri-0.1.0/tests/test_pronouns.py +47 -0
- ageri-0.1.0/tests/test_slack_interface.py +207 -0
- ageri-0.1.0/tests/test_tools.py +591 -0
- ageri-0.1.0/tests/test_webhook_messenger.py +272 -0
- ageri-0.1.0/tests/test_webhook_whatsapp.py +357 -0
- ageri-0.1.0/tests/test_wizard.py +812 -0
ageri-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ageri
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Ageri — Personal AI Operating System
|
|
5
|
+
Author: Huy Do
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: anthropic>=0.25
|
|
10
|
+
Requires-Dist: httpx>=0.27
|
|
11
|
+
Requires-Dist: requests>=2.31
|
|
12
|
+
Requires-Dist: ddgs>=7.0
|
|
13
|
+
Requires-Dist: schedule>=1.2
|
|
14
|
+
Requires-Dist: python-dotenv>=1.0
|
|
15
|
+
Requires-Dist: jinja2>=3.1
|
|
16
|
+
Requires-Dist: fastapi>=0.110
|
|
17
|
+
Requires-Dist: uvicorn>=0.29
|
|
18
|
+
Requires-Dist: websockets>=12.0
|
|
19
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
20
|
+
Requires-Dist: itsdangerous>=2.1
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Provides-Extra: slack
|
|
23
|
+
Requires-Dist: slack-sdk>=3.27; extra == "slack"
|
|
24
|
+
Requires-Dist: slack-bolt>=1.18; extra == "slack"
|
|
25
|
+
Provides-Extra: docs
|
|
26
|
+
Requires-Dist: pypdf2>=3.0; extra == "docs"
|
|
27
|
+
Requires-Dist: python-docx>=1.0; extra == "docs"
|
|
28
|
+
|
|
29
|
+
# Ageri — Personal AI Operating System
|
|
30
|
+
|
|
31
|
+
Ageri is a self-hosted personal AI platform that runs 24/7, remembers everything across sessions, and coordinates specialist skills across every domain of your work and life.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -sSL https://get.ageri.ai/install.sh | bash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or on Windows (PowerShell):
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
irm https://get.ageri.ai/install.ps1 | iex
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ageri setup # first-time wizard (namespace → API key → password → tunnel)
|
|
49
|
+
ageri start # start the web server
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then open **http://localhost:7821** in your browser.
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.10+
|
|
57
|
+
- An [Anthropic API key](https://console.anthropic.com/) or Ageri hosted credits
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
See [ageri.ai](https://ageri.ai) for full documentation.
|
ageri-0.1.0/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Ageri — Personal AI Operating System
|
|
2
|
+
|
|
3
|
+
Ageri is a self-hosted personal AI platform that runs 24/7, remembers everything across sessions, and coordinates specialist skills across every domain of your work and life.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
curl -sSL https://get.ageri.ai/install.sh | bash
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or on Windows (PowerShell):
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
irm https://get.ageri.ai/install.ps1 | iex
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
ageri setup # first-time wizard (namespace → API key → password → tunnel)
|
|
21
|
+
ageri start # start the web server
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then open **http://localhost:7821** in your browser.
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Python 3.10+
|
|
29
|
+
- An [Anthropic API key](https://console.anthropic.com/) or Ageri hosted credits
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
See [ageri.ai](https://ageri.ai) for full documentation.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""ageri/__main__.py — Entry point for `python -m ageri [command]`.
|
|
2
|
+
|
|
3
|
+
Commands:
|
|
4
|
+
(none) — start the web server (default)
|
|
5
|
+
setup — interactive first-run wizard (namespace, key, password, tunnel)
|
|
6
|
+
tunnel — provision / restart the CF tunnel only
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import threading
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import uvicorn
|
|
18
|
+
|
|
19
|
+
from .config_loader import load_all
|
|
20
|
+
from .memory import MemoryStore
|
|
21
|
+
from .profile_store import ProfileStore
|
|
22
|
+
from .orchestrator import Orchestrator
|
|
23
|
+
from .bootstrap import ensure_ageri_profile
|
|
24
|
+
from .main import _build_registry
|
|
25
|
+
from .web.app import create_app
|
|
26
|
+
from .device import get_or_create_device_id
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def _setup_tunnel(config_dir: Path, namespace: str) -> None:
|
|
32
|
+
from . import tunnel
|
|
33
|
+
device_id = get_or_create_device_id(config_dir)
|
|
34
|
+
|
|
35
|
+
# Download cloudflared if not present
|
|
36
|
+
if not tunnel.is_installed(config_dir):
|
|
37
|
+
try:
|
|
38
|
+
tunnel.download_cloudflared(config_dir)
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
logger.warning("Could not download cloudflared: %s", exc)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# Provision tunnel (idempotent — registry returns existing if already done)
|
|
44
|
+
try:
|
|
45
|
+
info = await tunnel.provision(config_dir, namespace, device_id)
|
|
46
|
+
tunnel.setup(config_dir, info)
|
|
47
|
+
tunnel.start(config_dir)
|
|
48
|
+
logger.info("Tunnel active: https://%s.ageri.ai", namespace)
|
|
49
|
+
except Exception as exc:
|
|
50
|
+
logger.warning("Tunnel setup failed: %s", exc)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _run_tunnel_setup(config_dir: Path, namespace: str) -> None:
|
|
54
|
+
asyncio.run(_setup_tunnel(config_dir, namespace))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main() -> None:
|
|
58
|
+
logging.basicConfig(
|
|
59
|
+
level=logging.INFO,
|
|
60
|
+
format="%(asctime)s %(levelname)-8s %(name)s — %(message)s",
|
|
61
|
+
)
|
|
62
|
+
config_dir = Path(os.environ.get("AGERI_HOME", Path.home() / "ageri"))
|
|
63
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
|
|
65
|
+
# Load config — tolerate missing properties on first run
|
|
66
|
+
try:
|
|
67
|
+
ageri_config, _ = load_all(config_dir)
|
|
68
|
+
except Exception:
|
|
69
|
+
from .config_loader import AgeriConfig
|
|
70
|
+
ageri_config = AgeriConfig()
|
|
71
|
+
|
|
72
|
+
port = int(os.environ.get("AGERI_PORT", getattr(ageri_config, "port", 7821)))
|
|
73
|
+
|
|
74
|
+
# Bootstrap core services
|
|
75
|
+
memory = MemoryStore(config_dir / "state.db")
|
|
76
|
+
memory.expire_working_memory()
|
|
77
|
+
profile_store = ProfileStore(config_dir / "state.db")
|
|
78
|
+
ensure_ageri_profile(profile_store)
|
|
79
|
+
|
|
80
|
+
registry = _build_registry(ageri_config)
|
|
81
|
+
orchestrator = Orchestrator(ageri_config, memory, registry.all(), profile_store=profile_store)
|
|
82
|
+
|
|
83
|
+
# Push notification service — enabled whenever Anthropic key is present
|
|
84
|
+
# (ProactiveMessenger needs it to generate messages).
|
|
85
|
+
from .notifications import PushService
|
|
86
|
+
push_service = PushService(memory) if getattr(ageri_config, "anthropic_api_key", "") else None
|
|
87
|
+
|
|
88
|
+
app = create_app(config_dir, orchestrator, memory, push_service=push_service)
|
|
89
|
+
|
|
90
|
+
# Start tunnel in background if namespace is configured
|
|
91
|
+
namespace = ageri_config.namespace or None
|
|
92
|
+
if namespace:
|
|
93
|
+
t = threading.Thread(target=_run_tunnel_setup, args=(config_dir, namespace), daemon=True)
|
|
94
|
+
t.start()
|
|
95
|
+
|
|
96
|
+
logger.info("Ageri web UI starting on http://localhost:%d", port)
|
|
97
|
+
uvicorn.run(app, host="0.0.0.0", port=port, log_level="warning")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _cmd_setup() -> None:
|
|
101
|
+
"""python -m ageri setup — run the interactive first-run wizard."""
|
|
102
|
+
from .setup_wizard import run as wizard_run
|
|
103
|
+
config_dir = Path(os.environ.get("AGERI_HOME", Path.home() / "ageri"))
|
|
104
|
+
wizard_run(config_dir)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _cmd_tunnel() -> None:
|
|
108
|
+
"""python -m ageri tunnel — provision / restart tunnel only."""
|
|
109
|
+
from . import tunnel as _tunnel
|
|
110
|
+
from .device import get_or_create_device_id
|
|
111
|
+
from .setup_wizard import _read_props, _green, _red, _bold, _ok, _err
|
|
112
|
+
|
|
113
|
+
config_dir = Path(os.environ.get("AGERI_HOME", Path.home() / "ageri"))
|
|
114
|
+
props = _read_props(config_dir)
|
|
115
|
+
namespace = props.get("NAMESPACE")
|
|
116
|
+
if not namespace:
|
|
117
|
+
print(_red(" ✗ No namespace configured — run: ageri setup"))
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
|
|
120
|
+
print(f"\n {_bold('Tunnel setup')}")
|
|
121
|
+
if not _tunnel.is_installed(config_dir):
|
|
122
|
+
print(" Downloading cloudflared...", end=" ", flush=True)
|
|
123
|
+
try:
|
|
124
|
+
_tunnel.download_cloudflared(config_dir)
|
|
125
|
+
print(_green("done"))
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
print(_red("failed"))
|
|
128
|
+
print(f" {_red(str(exc))}")
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
|
|
131
|
+
print(" Provisioning...", end=" ", flush=True)
|
|
132
|
+
device_id = get_or_create_device_id(config_dir)
|
|
133
|
+
try:
|
|
134
|
+
info = asyncio.run(_tunnel.provision(config_dir, namespace, device_id))
|
|
135
|
+
_tunnel.setup(config_dir, info)
|
|
136
|
+
print(_green("done"))
|
|
137
|
+
_ok(f"https://{_bold(namespace)}.ageri.ai is live")
|
|
138
|
+
except Exception as exc:
|
|
139
|
+
print(_red("failed"))
|
|
140
|
+
print(f" {_red(str(exc))}")
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def cli() -> None:
|
|
145
|
+
"""Dispatch subcommands: setup | tunnel | (default: start web server)."""
|
|
146
|
+
cmd = sys.argv[1] if len(sys.argv) > 1 else "start"
|
|
147
|
+
if cmd == "setup":
|
|
148
|
+
_cmd_setup()
|
|
149
|
+
elif cmd == "tunnel":
|
|
150
|
+
_cmd_tunnel()
|
|
151
|
+
elif cmd in ("start", "run"):
|
|
152
|
+
main()
|
|
153
|
+
elif cmd in ("-h", "--help", "help"):
|
|
154
|
+
print(__doc__)
|
|
155
|
+
else:
|
|
156
|
+
# Unknown arg — pass through to main (could be uvicorn flags etc.)
|
|
157
|
+
main()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
cli()
|