aaw-core 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.
Files changed (79) hide show
  1. aaw_core-0.1.0/.dockerignore +10 -0
  2. aaw_core-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  3. aaw_core-0.1.0/.github/workflows/ci.yml +46 -0
  4. aaw_core-0.1.0/.gitignore +33 -0
  5. aaw_core-0.1.0/CODE_OF_CONDUCT.md +9 -0
  6. aaw_core-0.1.0/CONTRIBUTING.md +44 -0
  7. aaw_core-0.1.0/LICENSE +21 -0
  8. aaw_core-0.1.0/PKG-INFO +138 -0
  9. aaw_core-0.1.0/README.md +101 -0
  10. aaw_core-0.1.0/SECURITY.md +18 -0
  11. aaw_core-0.1.0/aaw_core/__init__.py +7 -0
  12. aaw_core-0.1.0/aaw_core/agents/__init__.py +0 -0
  13. aaw_core-0.1.0/aaw_core/config.py +151 -0
  14. aaw_core-0.1.0/aaw_core/daemon.py +712 -0
  15. aaw_core-0.1.0/aaw_core/encryption.py +96 -0
  16. aaw_core-0.1.0/aaw_core/hooks/__init__.py +0 -0
  17. aaw_core-0.1.0/aaw_core/hooks/common.py +201 -0
  18. aaw_core-0.1.0/aaw_core/hooks/on_notification.py +39 -0
  19. aaw_core-0.1.0/aaw_core/hooks/on_post_tool.py +57 -0
  20. aaw_core-0.1.0/aaw_core/hooks/on_pre_tool.py +195 -0
  21. aaw_core-0.1.0/aaw_core/hooks/on_session_start.py +65 -0
  22. aaw_core-0.1.0/aaw_core/hooks/on_stop.py +114 -0
  23. aaw_core-0.1.0/aaw_core/hooks/on_stop_claude.py +287 -0
  24. aaw_core-0.1.0/aaw_core/hooks/on_stop_cursor.py +51 -0
  25. aaw_core-0.1.0/aaw_core/hooks/on_stop_gemini.py +14 -0
  26. aaw_core-0.1.0/aaw_core/hooks/on_stop_grok.py +206 -0
  27. aaw_core-0.1.0/aaw_core/hooks/on_user_prompt.py +43 -0
  28. aaw_core-0.1.0/aaw_core/host/__init__.py +0 -0
  29. aaw_core-0.1.0/aaw_core/host/cli.py +846 -0
  30. aaw_core-0.1.0/aaw_core/host/detect.py +449 -0
  31. aaw_core-0.1.0/aaw_core/host/fs.py +162 -0
  32. aaw_core-0.1.0/aaw_core/host/hooks_installer.py +376 -0
  33. aaw_core-0.1.0/aaw_core/host/identity.py +54 -0
  34. aaw_core-0.1.0/aaw_core/host/scripts/shell_integration.sh +58 -0
  35. aaw_core-0.1.0/aaw_core/host/service.py +209 -0
  36. aaw_core-0.1.0/aaw_core/host/session_id.py +75 -0
  37. aaw_core-0.1.0/aaw_core/host/sessions.py +388 -0
  38. aaw_core-0.1.0/aaw_core/host/state.py +97 -0
  39. aaw_core-0.1.0/aaw_core/host/supervisor.py +405 -0
  40. aaw_core-0.1.0/aaw_core/host/tmux.py +201 -0
  41. aaw_core-0.1.0/aaw_core/host/usage.py +283 -0
  42. aaw_core-0.1.0/aaw_core/relay/__init__.py +8 -0
  43. aaw_core-0.1.0/aaw_core/relay/__main__.py +44 -0
  44. aaw_core-0.1.0/aaw_core/relay/backup.py +56 -0
  45. aaw_core-0.1.0/aaw_core/relay/push_fcm.py +164 -0
  46. aaw_core-0.1.0/aaw_core/relay/server.py +646 -0
  47. aaw_core-0.1.0/aaw_core/transport/__init__.py +0 -0
  48. aaw_core-0.1.0/aaw_core/transport/base.py +225 -0
  49. aaw_core-0.1.0/aaw_core/transport/relay.py +470 -0
  50. aaw_core-0.1.0/aaw_core/transport/utils.py +51 -0
  51. aaw_core-0.1.0/docker/README.md +40 -0
  52. aaw_core-0.1.0/docker/linux-host.Dockerfile +20 -0
  53. aaw_core-0.1.0/docs/.gitkeep +0 -0
  54. aaw_core-0.1.0/docs/architecture.md +85 -0
  55. aaw_core-0.1.0/docs/protocol.contract.md +127 -0
  56. aaw_core-0.1.0/pyproject.toml +58 -0
  57. aaw_core-0.1.0/relay/Dockerfile +16 -0
  58. aaw_core-0.1.0/relay/README.md +55 -0
  59. aaw_core-0.1.0/relay/docker-compose.yml +22 -0
  60. aaw_core-0.1.0/scripts/install.sh +143 -0
  61. aaw_core-0.1.0/tests/test_cli.py +184 -0
  62. aaw_core-0.1.0/tests/test_config.py +60 -0
  63. aaw_core-0.1.0/tests/test_detect.py +240 -0
  64. aaw_core-0.1.0/tests/test_e2e_host.py +116 -0
  65. aaw_core-0.1.0/tests/test_encryption.py +98 -0
  66. aaw_core-0.1.0/tests/test_fs.py +67 -0
  67. aaw_core-0.1.0/tests/test_hooks.py +319 -0
  68. aaw_core-0.1.0/tests/test_hooks_installer.py +144 -0
  69. aaw_core-0.1.0/tests/test_push_fcm.py +153 -0
  70. aaw_core-0.1.0/tests/test_relay.py +618 -0
  71. aaw_core-0.1.0/tests/test_relay_backup.py +25 -0
  72. aaw_core-0.1.0/tests/test_service.py +65 -0
  73. aaw_core-0.1.0/tests/test_session_id.py +55 -0
  74. aaw_core-0.1.0/tests/test_sessions.py +99 -0
  75. aaw_core-0.1.0/tests/test_smoke.py +37 -0
  76. aaw_core-0.1.0/tests/test_state.py +60 -0
  77. aaw_core-0.1.0/tests/test_supervisor.py +201 -0
  78. aaw_core-0.1.0/tests/test_transport_base.py +114 -0
  79. aaw_core-0.1.0/tests/test_utils.py +36 -0
@@ -0,0 +1,10 @@
1
+ .git
2
+ .venv
3
+ .github
4
+ .pytest_cache
5
+ .ruff_cache
6
+ **/__pycache__
7
+ tests
8
+ docs
9
+ docker
10
+ *.sqlite
@@ -0,0 +1,10 @@
1
+ ## What this changes
2
+
3
+ <!-- One or two sentences. Link the issue or discussion if there is one. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] Every commit is signed off (`git commit -s`), per the DCO in CONTRIBUTING.md.
8
+ - [ ] No secrets, tokens, or hosted-project defaults were added; config comes from env or `~/.aaw/config.json`.
9
+ - [ ] Tests pass locally (`pytest`) and lint is clean (`ruff check .`).
10
+ - [ ] If this touches encryption, pairing, `aaw_core/transport/`, or `relay/`: the event/command shapes and the encryption envelope stay compatible with the phone apps (see `docs/protocol.contract.md`).
@@ -0,0 +1,46 @@
1
+ # Build, lint, test, and scan for secrets. Never deploys anything.
2
+ # Fork pull requests get no repository secrets.
3
+ name: ci
4
+
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ os: [ubuntu-latest, macos-latest]
20
+ python: ["3.11", "3.12", "3.13"]
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python }}
26
+ - name: tmux (the end-to-end host test needs it)
27
+ run: |
28
+ if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get update -q && sudo apt-get install -y -q tmux; fi
29
+ if [ "$RUNNER_OS" = "macOS" ]; then brew list tmux >/dev/null 2>&1 || brew install -q tmux; fi
30
+ tmux -V
31
+ - run: python -m pip install --upgrade pip
32
+ - run: pip install -e ".[dev,relay]"
33
+ - run: ruff check .
34
+ - run: pytest -q
35
+ - run: aaw --version
36
+
37
+ secrets:
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ with:
42
+ fetch-depth: 0
43
+ - name: gitleaks
44
+ uses: gitleaks/gitleaks-action@v2
45
+ env:
46
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+
13
+ # Local config, state, logs
14
+ .env
15
+ .env.*
16
+ *.log
17
+ *.jsonl
18
+ .aaw/
19
+ locks/
20
+ *.pid
21
+
22
+ # OS / editors
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
26
+
27
+ # Belt and braces: secrets never belong here
28
+ *.p8
29
+ *.p12
30
+ *.cer
31
+ *.provisionprofile
32
+ *service-account*.json
33
+ *secrets*.env
@@ -0,0 +1,9 @@
1
+ # Code of Conduct
2
+
3
+ This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4
+
5
+ In short: be respectful and constructive, assume good faith, and keep discussions about the work. Harassment, personal attacks, and discriminatory language are not tolerated anywhere this project happens: issues, pull requests, discussions, and any linked channels.
6
+
7
+ ## Enforcement
8
+
9
+ Report unacceptable behavior to **hello@agentsatwork.app**. Reports are handled privately and confidentially. Maintainers may edit, remove, or reject contributions and comments that violate this code, and may temporarily or permanently ban contributors for repeated or severe violations.
@@ -0,0 +1,44 @@
1
+ # Contributing to Agents At Work Core
2
+
3
+ Thanks for helping. This is the open-source engine behind Agents At Work; the apps are closed, but everything that runs on your computer lives here.
4
+
5
+ ## Developer Certificate of Origin (DCO)
6
+
7
+ Every commit must be signed off:
8
+
9
+ ```bash
10
+ git commit -s -m "your message"
11
+ ```
12
+
13
+ That adds a `Signed-off-by: Your Name <you@example.com>` trailer, certifying you have the right to contribute the change under the MIT license (the [DCO 1.1](https://developercertificate.org/)). A DCO check runs on every pull request; unsigned commits fail it. There is no CLA.
14
+
15
+ ## Development setup
16
+
17
+ ```bash
18
+ git clone https://github.com/sergenes/aaw-core
19
+ cd aaw-core
20
+ python -m venv .venv && source .venv/bin/activate
21
+ pip install -e ".[dev,relay]"
22
+ pytest
23
+ ruff check .
24
+ ```
25
+
26
+ Python 3.11+. The host runs on Linux and macOS; the relay runs anywhere Python does.
27
+
28
+ ## What gets extra scrutiny
29
+
30
+ Changes to these paths are reviewed more slowly and carefully, because they affect security or compatibility with the closed phone apps:
31
+
32
+ - `aaw_core/encryption.py`: the AES-256-GCM envelope must stay byte-compatible with the apps.
33
+ - Pairing and the QR payload.
34
+ - `aaw_core/transport/` and `relay/`: event and command shapes are a contract with the apps (see `docs/protocol.contract.md`).
35
+
36
+ ## Before opening a pull request
37
+
38
+ - For anything non-trivial, open an issue or discussion first so you do not spend a weekend on something that conflicts with the roadmap.
39
+ - CI must be green: build, tests, lint, and the secret scanner.
40
+ - Never commit secrets, keys, tokens, or anything pointing at a specific hosted project. Configuration comes from environment variables and `~/.aaw/config.json`; the code has no hosted defaults.
41
+
42
+ ## How this project is developed
43
+
44
+ The engine is developed here, in the open. It was assembled by porting the host from the maintainer's product, Agents At Work, and it is now the engine that product runs: the Mac app bundles it, and the hosted relay is this relay. Good contributions here reach both.
aaw_core-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sergey Nes
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,138 @@
1
+ Metadata-Version: 2.5
2
+ Name: aaw-core
3
+ Version: 0.1.0
4
+ Summary: Agents At Work Core: the open-source engine behind Agents At Work. Run Claude Code, Codex, Gemini CLI, Grok, Cursor, and Scoot on your own Mac or Linux box and control them from your phone.
5
+ Project-URL: Homepage, https://agentsatwork.app
6
+ Project-URL: Repository, https://github.com/sergenes/aaw-core
7
+ Project-URL: Issues, https://github.com/sergenes/aaw-core/issues
8
+ Project-URL: Changelog, https://github.com/sergenes/aaw-core/releases
9
+ Author: Sergey Nes
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,claude-code,codex,gemini-cli,remote-control,tmux
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: cryptography>=42
26
+ Requires-Dist: qrcode>=7
27
+ Requires-Dist: websockets>=12
28
+ Provides-Extra: dev
29
+ Requires-Dist: httpx2; extra == 'dev'
30
+ Requires-Dist: pytest>=8; extra == 'dev'
31
+ Requires-Dist: ruff>=0.5; extra == 'dev'
32
+ Provides-Extra: relay
33
+ Requires-Dist: aiosqlite>=0.20; extra == 'relay'
34
+ Requires-Dist: starlette>=0.37; extra == 'relay'
35
+ Requires-Dist: uvicorn>=0.29; extra == 'relay'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # Agents At Work Core
39
+
40
+ [![PyPI](https://img.shields.io/pypi/v/aaw-core.svg?label=PyPI)](https://pypi.org/project/aaw-core/)
41
+ [![tests](https://github.com/sergenes/aaw-core/actions/workflows/ci.yml/badge.svg)](https://github.com/sergenes/aaw-core/actions/workflows/ci.yml)
42
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
43
+ [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey.svg)](#install)
44
+ [![Agents](https://img.shields.io/badge/agents-Claude%20Code%20%7C%20Codex%20%7C%20Gemini%20%7C%20Grok%20%7C%20Cursor%20%7C%20scoot-orange.svg)](#supported-agents)
45
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
46
+
47
+ **The open-source engine behind [Agents At Work](https://agentsatwork.app).**
48
+ Run Claude Code, Codex, Gemini CLI, Grok, Cursor, and scoot on your own Mac or Linux box, and follow them from your phone: approve tool calls, answer questions, send prompts, and schedule prompts for later, end-to-end encrypted.
49
+
50
+ > **Status: alpha.**
51
+ > This is the engine the Agents At Work Mac app bundles and the hosted relay runs, so it is in daily use, but the command line and the config keys can still change between 0.x releases.
52
+
53
+ ## What is open and what is not
54
+
55
+ `aaw-core` is everything that runs on *your* computer: the daemon that watches your agents in `tmux`, the hooks that report what they do, and the `aaw` command line.
56
+ It also contains the relay server that connects your computer to your phone.
57
+ All of it is MIT licensed.
58
+
59
+ The phone apps (iPhone, iPad, Android) and the Mac menu-bar app are not in this repository.
60
+ The Mac app is a free download that runs this same engine.
61
+ The phone apps are free for one computer on our hosted relay; the Personal and Pro plans raise that to 3 and 15 computers.
62
+ Computers on a relay you host yourself never count toward a plan.
63
+
64
+ ## How it works
65
+
66
+ ```
67
+ your computer your phone
68
+ agent in tmux <-> aaw daemon <-> local store store app
69
+ | ^
70
+ | one outbound WebSocket |
71
+ v |
72
+ the relay (tiny; ciphertext + routing only) -> push (FCM/APNs)
73
+ ```
74
+
75
+ - Your **local store** on the computer is the source of truth for the conversation history.
76
+ - The daemon keeps **one outbound WebSocket** to a relay, so it works behind any home router or cellular NAT with **no ports, no tunnel, no VPN, no Firebase project**.
77
+ - The relay buffers what is in flight while the phone is asleep and triggers a wake-up push. It only ever sees **ciphertext and routing metadata**: prompts, answers, and content are AES-256-GCM encrypted with a key that only your computer and your phone hold, exchanged once by QR.
78
+ - We host a relay for free users; self-hosters run the same server with `docker compose`.
79
+
80
+ ## Install
81
+
82
+ On Linux, one command installs or updates everything: its own virtualenv, the `aaw` command, the shell integration, the link with your phone (a QR code), and the always-on service.
83
+ It also removes the earlier Agents At Work Linux host if it finds one.
84
+
85
+ ```bash
86
+ curl -fsSL https://agentsatwork.app/install-linux.sh | bash # the same script as scripts/install.sh
87
+ ```
88
+
89
+ Or by hand, on Linux or macOS (on a Mac, the free Agents At Work app does all of this for you):
90
+
91
+ ```bash
92
+ pipx install aaw-core # Python 3.11+, and tmux
93
+ aaw link # asks which relay (ours, or your own), prints a QR code to scan
94
+ aaw service install # the always-on part: runs in the background and starts at every login
95
+ aaw shell-integration on # typing claude, codex, ... in a folder starts a bridged session
96
+ ```
97
+
98
+ Then start an agent the way you always do (`cd ~/my-project && claude`), or start one from the phone.
99
+
100
+ ## Everyday commands
101
+
102
+ ```bash
103
+ aaw status # the computer, the relay, and every session
104
+ aaw start ~/my-project # start a session (--agent codex, gemini, grok, cursor, scoot)
105
+ aaw stop my-project # stop one session
106
+ aaw feed my-project # the conversation, paged; -f follows it live
107
+ aaw send my-project "run the tests" # send a prompt
108
+ aaw schedule my-project --at 2am "continue with the refactor" # a prompt for later
109
+ aaw mobile-mode on # permission prompts go to the phone
110
+ aaw quit # stop everything; the next login (or aaw service start) brings it back
111
+ aaw uninstall # remove the service, the hooks, the shell line, and ~/.aaw
112
+ ```
113
+
114
+ `aaw --help` lists every command, and `aaw <command> --help` its options.
115
+
116
+ ## Hosting the relay yourself
117
+
118
+ The relay is a small server in this repository (`aaw_core/relay/`): run it with `docker compose` from `relay/`, or with `pip install "aaw-core[relay]"` and `python -m aaw_core.relay`, behind a TLS proxy.
119
+ Point your computer at it with `aaw link --relay wss://your.host/v1/ws`.
120
+ It sees only ciphertext and routing metadata, the same as ours.
121
+ One difference: waking the phone with a push notification needs the app's own push credentials, so on your own relay the phone gets updates while the app is open, not in the background.
122
+ `relay/README.md` has the details.
123
+
124
+ ## Desktop notifications
125
+
126
+ The phone is the primary channel, and the computer itself also gets a banner when a turn finishes, an error happens, or the agent waits for input: `osascript` on macOS, `notify-send` on a Linux desktop (GNOME, KDE, and the rest). A server or a box without a desktop simply has no `notify-send` and shows nothing. On macOS, while any Focus is on (Do Not Disturb included), banners from scripts go to Notification Center silently unless Agents At Work is allowed under System Settings > Focus > Allowed Apps; `aaw status` reminds you. Two keys in `~/.aaw/config.json`: `local_notifications` (default `true`; a GUI that shows its own banners turns it off) and `waiting_alert_seconds` (default `0`, off; the seconds a question may sit unanswered before a "waiting for your answer" banner, for when the push is muted while you are at the desk).
127
+
128
+ ## Supported agents
129
+
130
+ Claude Code, Codex CLI, Gemini CLI, Grok CLI, Cursor, and [Scoot](https://github.com/sergenes/scootcli) (local models with Ollama, plus cloud models). Not affiliated with Anthropic, OpenAI, Google, xAI, Anysphere, or Ollama.
131
+
132
+ ## Contributing
133
+
134
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Contributions use the DCO (`git commit -s`). Security reports: see [SECURITY.md](SECURITY.md).
135
+
136
+ ## License
137
+
138
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,101 @@
1
+ # Agents At Work Core
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/aaw-core.svg?label=PyPI)](https://pypi.org/project/aaw-core/)
4
+ [![tests](https://github.com/sergenes/aaw-core/actions/workflows/ci.yml/badge.svg)](https://github.com/sergenes/aaw-core/actions/workflows/ci.yml)
5
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
6
+ [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey.svg)](#install)
7
+ [![Agents](https://img.shields.io/badge/agents-Claude%20Code%20%7C%20Codex%20%7C%20Gemini%20%7C%20Grok%20%7C%20Cursor%20%7C%20scoot-orange.svg)](#supported-agents)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
9
+
10
+ **The open-source engine behind [Agents At Work](https://agentsatwork.app).**
11
+ Run Claude Code, Codex, Gemini CLI, Grok, Cursor, and scoot on your own Mac or Linux box, and follow them from your phone: approve tool calls, answer questions, send prompts, and schedule prompts for later, end-to-end encrypted.
12
+
13
+ > **Status: alpha.**
14
+ > This is the engine the Agents At Work Mac app bundles and the hosted relay runs, so it is in daily use, but the command line and the config keys can still change between 0.x releases.
15
+
16
+ ## What is open and what is not
17
+
18
+ `aaw-core` is everything that runs on *your* computer: the daemon that watches your agents in `tmux`, the hooks that report what they do, and the `aaw` command line.
19
+ It also contains the relay server that connects your computer to your phone.
20
+ All of it is MIT licensed.
21
+
22
+ The phone apps (iPhone, iPad, Android) and the Mac menu-bar app are not in this repository.
23
+ The Mac app is a free download that runs this same engine.
24
+ The phone apps are free for one computer on our hosted relay; the Personal and Pro plans raise that to 3 and 15 computers.
25
+ Computers on a relay you host yourself never count toward a plan.
26
+
27
+ ## How it works
28
+
29
+ ```
30
+ your computer your phone
31
+ agent in tmux <-> aaw daemon <-> local store store app
32
+ | ^
33
+ | one outbound WebSocket |
34
+ v |
35
+ the relay (tiny; ciphertext + routing only) -> push (FCM/APNs)
36
+ ```
37
+
38
+ - Your **local store** on the computer is the source of truth for the conversation history.
39
+ - The daemon keeps **one outbound WebSocket** to a relay, so it works behind any home router or cellular NAT with **no ports, no tunnel, no VPN, no Firebase project**.
40
+ - The relay buffers what is in flight while the phone is asleep and triggers a wake-up push. It only ever sees **ciphertext and routing metadata**: prompts, answers, and content are AES-256-GCM encrypted with a key that only your computer and your phone hold, exchanged once by QR.
41
+ - We host a relay for free users; self-hosters run the same server with `docker compose`.
42
+
43
+ ## Install
44
+
45
+ On Linux, one command installs or updates everything: its own virtualenv, the `aaw` command, the shell integration, the link with your phone (a QR code), and the always-on service.
46
+ It also removes the earlier Agents At Work Linux host if it finds one.
47
+
48
+ ```bash
49
+ curl -fsSL https://agentsatwork.app/install-linux.sh | bash # the same script as scripts/install.sh
50
+ ```
51
+
52
+ Or by hand, on Linux or macOS (on a Mac, the free Agents At Work app does all of this for you):
53
+
54
+ ```bash
55
+ pipx install aaw-core # Python 3.11+, and tmux
56
+ aaw link # asks which relay (ours, or your own), prints a QR code to scan
57
+ aaw service install # the always-on part: runs in the background and starts at every login
58
+ aaw shell-integration on # typing claude, codex, ... in a folder starts a bridged session
59
+ ```
60
+
61
+ Then start an agent the way you always do (`cd ~/my-project && claude`), or start one from the phone.
62
+
63
+ ## Everyday commands
64
+
65
+ ```bash
66
+ aaw status # the computer, the relay, and every session
67
+ aaw start ~/my-project # start a session (--agent codex, gemini, grok, cursor, scoot)
68
+ aaw stop my-project # stop one session
69
+ aaw feed my-project # the conversation, paged; -f follows it live
70
+ aaw send my-project "run the tests" # send a prompt
71
+ aaw schedule my-project --at 2am "continue with the refactor" # a prompt for later
72
+ aaw mobile-mode on # permission prompts go to the phone
73
+ aaw quit # stop everything; the next login (or aaw service start) brings it back
74
+ aaw uninstall # remove the service, the hooks, the shell line, and ~/.aaw
75
+ ```
76
+
77
+ `aaw --help` lists every command, and `aaw <command> --help` its options.
78
+
79
+ ## Hosting the relay yourself
80
+
81
+ The relay is a small server in this repository (`aaw_core/relay/`): run it with `docker compose` from `relay/`, or with `pip install "aaw-core[relay]"` and `python -m aaw_core.relay`, behind a TLS proxy.
82
+ Point your computer at it with `aaw link --relay wss://your.host/v1/ws`.
83
+ It sees only ciphertext and routing metadata, the same as ours.
84
+ One difference: waking the phone with a push notification needs the app's own push credentials, so on your own relay the phone gets updates while the app is open, not in the background.
85
+ `relay/README.md` has the details.
86
+
87
+ ## Desktop notifications
88
+
89
+ The phone is the primary channel, and the computer itself also gets a banner when a turn finishes, an error happens, or the agent waits for input: `osascript` on macOS, `notify-send` on a Linux desktop (GNOME, KDE, and the rest). A server or a box without a desktop simply has no `notify-send` and shows nothing. On macOS, while any Focus is on (Do Not Disturb included), banners from scripts go to Notification Center silently unless Agents At Work is allowed under System Settings > Focus > Allowed Apps; `aaw status` reminds you. Two keys in `~/.aaw/config.json`: `local_notifications` (default `true`; a GUI that shows its own banners turns it off) and `waiting_alert_seconds` (default `0`, off; the seconds a question may sit unanswered before a "waiting for your answer" banner, for when the push is muted while you are at the desk).
90
+
91
+ ## Supported agents
92
+
93
+ Claude Code, Codex CLI, Gemini CLI, Grok CLI, Cursor, and [Scoot](https://github.com/sergenes/scootcli) (local models with Ollama, plus cloud models). Not affiliated with Anthropic, OpenAI, Google, xAI, Anysphere, or Ollama.
94
+
95
+ ## Contributing
96
+
97
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Contributions use the DCO (`git commit -s`). Security reports: see [SECURITY.md](SECURITY.md).
98
+
99
+ ## License
100
+
101
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,18 @@
1
+ # Security policy
2
+
3
+ This project handles end-to-end encryption and remote control of coding agents. Please report vulnerabilities privately, not in the public issue tracker.
4
+
5
+ ## Reporting
6
+
7
+ - Preferred: open a private report through **GitHub Security Advisories** on this repository ("Report a vulnerability").
8
+ - Or email **hello@agentsatwork.app** with the details.
9
+
10
+ Include what you found, how to reproduce it, and the impact you believe it has. You will get an acknowledgement, and a fix or a mitigation plan, before any public disclosure.
11
+
12
+ ## Scope
13
+
14
+ - The host (`aaw_core/`): the daemon, hooks, pairing, and the `aaw` command.
15
+ - The relay (`relay/`).
16
+ - The encryption envelope and key handling.
17
+
18
+ The closed phone and macOS apps are out of scope here; report those to the same email.
@@ -0,0 +1,7 @@
1
+ """Agents At Work Core: the open-source engine behind Agents At Work.
2
+
3
+ The headless host (daemon, hooks, ``aaw`` CLI) that watches coding agents in tmux
4
+ on your own computer, plus the relay transport that connects it to your phone.
5
+ """
6
+
7
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,151 @@
1
+ """Configuration for the aaw-core host.
2
+
3
+ Config-first by construction: everything comes from environment variables or
4
+ ``~/.aaw/config.json``. There are no defaults that point at any hosted project,
5
+ which is what makes this repository safe to publish without a secrets audit.
6
+
7
+ Precedence: environment variable > config file > built-in default.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import subprocess
15
+ import sys
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+
19
+ # A distinct state directory so aaw-core can coexist on one machine with the
20
+ # commercial Agents At Work host, which uses ~/.agent-bridge.
21
+ DEFAULT_STATE_DIR = Path.home() / ".aaw"
22
+
23
+ # The relay we host for free users. A public endpoint, not a secret, and never a
24
+ # silent default: `aaw link` offers it and writes the choice to config.json.
25
+ HOSTED_RELAY_URL = "wss://relay.agentsatwork.app/v1/ws"
26
+
27
+
28
+ def default_computer_name() -> str:
29
+ """The name a person knows this computer by.
30
+
31
+ On macOS that is the Computer Name from System Settings ("Sergey's MacBook Pro"),
32
+ not the network hostname ("Sergeys-MacBook-Pro.local"). Elsewhere the hostname,
33
+ without a trailing ".local".
34
+ """
35
+ if sys.platform == "darwin":
36
+ try:
37
+ out = subprocess.run(["scutil", "--get", "ComputerName"], capture_output=True, text=True,
38
+ timeout=2, check=False).stdout.strip()
39
+ if out:
40
+ return out
41
+ except (OSError, subprocess.SubprocessError):
42
+ pass
43
+ name = os.uname().nodename
44
+ return name.removesuffix(".local")
45
+
46
+
47
+ def _config_file(state_dir: Path) -> dict:
48
+ path = state_dir / "config.json"
49
+ try:
50
+ return json.loads(path.read_text()) if path.exists() else {}
51
+ except (OSError, ValueError):
52
+ return {}
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class Settings:
57
+ """Resolved settings for one host process."""
58
+
59
+ state_dir: Path
60
+ relay_url: str | None # wss://... ; required to bridge to a phone, None = local only
61
+ computer_name: str
62
+ keep_awake: bool
63
+ browse_roots: tuple[str, ...] = ("~",) # folders the phone may browse and start sessions in
64
+ local_notifications: bool = True # desktop banners (osascript / notify-send)
65
+ waiting_alert_seconds: int = 0 # 0 = no "waiting for your answer" desktop alert
66
+
67
+ @property
68
+ def sessions_dir(self) -> Path:
69
+ return self.state_dir / "sessions"
70
+
71
+ @property
72
+ def run_dir(self) -> Path:
73
+ """Pid files of the daemons and the keep-awake helper."""
74
+ return self.state_dir / "run"
75
+
76
+ def daemon_pid_file(self, project_id: str) -> Path:
77
+ return self.run_dir / f"daemon.{project_id}.pid"
78
+
79
+ def daemon_log_file(self, project_id: str) -> Path:
80
+ return self.logs_dir / f"daemon.{project_id}.log"
81
+
82
+ @property
83
+ def keep_awake_pid_file(self) -> Path:
84
+ return self.run_dir / "keep-awake.pid"
85
+
86
+ @property
87
+ def supervisor_log_file(self) -> Path:
88
+ return self.logs_dir / "supervisor.log"
89
+
90
+ @property
91
+ def session_key_file(self) -> Path:
92
+ return self.state_dir / "session.key"
93
+
94
+ @property
95
+ def enabled_flag(self) -> Path:
96
+ return self.state_dir / "enabled"
97
+
98
+ @property
99
+ def mobile_mode_file(self) -> Path:
100
+ """Holds "manual" or a timestamp while permission prompts should go to the phone."""
101
+ return self.state_dir / "mobile_mode"
102
+
103
+ @property
104
+ def host_file(self) -> Path:
105
+ """The persisted host identity (computer id + relay routing token)."""
106
+ return self.state_dir / "host.json"
107
+
108
+ @property
109
+ def logs_dir(self) -> Path:
110
+ return self.state_dir / "logs"
111
+
112
+
113
+ def save_config(state_dir: Path, **fields) -> Path:
114
+ """Merge fields into <state dir>/config.json (created if missing)."""
115
+ path = state_dir / "config.json"
116
+ current = _config_file(state_dir)
117
+ current.update(fields)
118
+ path.parent.mkdir(parents=True, exist_ok=True)
119
+ path.write_text(json.dumps(current, indent=2) + "\n")
120
+ return path
121
+
122
+
123
+ def load_settings() -> Settings:
124
+ state_dir = Path(os.environ.get("AAW_STATE_DIR") or DEFAULT_STATE_DIR).expanduser()
125
+ cfg = _config_file(state_dir)
126
+
127
+ def pick(env: str, key: str, default):
128
+ value = os.environ.get(env)
129
+ if value is not None and value != "":
130
+ return value
131
+ return cfg.get(key, default)
132
+
133
+ def flag(env: str, key: str, default: str) -> bool:
134
+ return str(pick(env, key, default)).lower() in ("1", "true", "yes")
135
+
136
+ roots = pick("AAW_BROWSE_ROOTS", "browse_roots", "~")
137
+ if isinstance(roots, str):
138
+ roots = [r for r in roots.split(os.pathsep) if r]
139
+ try:
140
+ waiting = int(pick("AAW_WAITING_ALERT_SECONDS", "waiting_alert_seconds", 0))
141
+ except (TypeError, ValueError):
142
+ waiting = 0
143
+ return Settings(
144
+ state_dir=state_dir,
145
+ relay_url=pick("AAW_RELAY_URL", "relay_url", None),
146
+ computer_name=pick("AAW_COMPUTER_NAME", "computer_name", None) or default_computer_name(),
147
+ keep_awake=flag("AAW_KEEP_AWAKE", "keep_awake", "true"),
148
+ browse_roots=tuple(str(r) for r in roots) or ("~",),
149
+ local_notifications=flag("AAW_LOCAL_NOTIFICATIONS", "local_notifications", "true"),
150
+ waiting_alert_seconds=max(0, waiting),
151
+ )