onako 0.1.2__tar.gz → 0.2.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.
- {onako-0.1.2 → onako-0.2.0}/PKG-INFO +9 -7
- {onako-0.1.2 → onako-0.2.0}/README.md +8 -6
- {onako-0.1.2 → onako-0.2.0}/pyproject.toml +1 -1
- onako-0.2.0/src/onako/__init__.py +1 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako/cli.py +72 -63
- {onako-0.1.2 → onako-0.2.0}/src/onako/templates/com.onako.server.plist.tpl +4 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako/templates/onako.service.tpl +2 -1
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/PKG-INFO +9 -7
- {onako-0.1.2 → onako-0.2.0}/tests/test_cli.py +3 -1
- {onako-0.1.2 → onako-0.2.0}/tests/test_cli_service.py +3 -3
- onako-0.1.2/src/onako/__init__.py +0 -1
- {onako-0.1.2 → onako-0.2.0}/setup.cfg +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako/server.py +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako/static/index.html +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako/tmux_orchestrator.py +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/SOURCES.txt +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/dependency_links.txt +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/entry_points.txt +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/requires.txt +0 -0
- {onako-0.1.2 → onako-0.2.0}/src/onako.egg-info/top_level.txt +0 -0
- {onako-0.1.2 → onako-0.2.0}/tests/test_api.py +0 -0
- {onako-0.1.2 → onako-0.2.0}/tests/test_tmux_orchestrator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onako
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Dispatch and monitor Claude Code tasks from your phone
|
|
5
5
|
Author: Amir
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,23 +32,25 @@ Requires [tmux](https://github.com/tmux/tmux) and [Claude Code](https://docs.ant
|
|
|
32
32
|
## Usage
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
cd ~/projects
|
|
35
36
|
onako serve
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
Open http://localhost:8787 on your phone (same network) or set up [Tailscale](https://tailscale.com) for access from anywhere.
|
|
39
40
|
|
|
40
|
-
###
|
|
41
|
+
### Run in the background
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
|
-
onako
|
|
44
|
+
onako serve --background
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
This installs a system service (launchd on macOS, systemd on Linux) that starts on boot. Tasks run in the directory where you ran the command.
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
onako
|
|
50
|
-
onako
|
|
51
|
-
onako
|
|
50
|
+
onako serve --background --dir ~/projects # override working directory
|
|
51
|
+
onako status # check if running
|
|
52
|
+
onako stop # stop the background service
|
|
53
|
+
onako version # print version
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## How it works
|
|
@@ -15,23 +15,25 @@ Requires [tmux](https://github.com/tmux/tmux) and [Claude Code](https://docs.ant
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
+
cd ~/projects
|
|
18
19
|
onako serve
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
Open http://localhost:8787 on your phone (same network) or set up [Tailscale](https://tailscale.com) for access from anywhere.
|
|
22
23
|
|
|
23
|
-
###
|
|
24
|
+
### Run in the background
|
|
24
25
|
|
|
25
26
|
```bash
|
|
26
|
-
onako
|
|
27
|
+
onako serve --background
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
This installs a system service (launchd on macOS, systemd on Linux) that starts on boot. Tasks run in the directory where you ran the command.
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
|
-
onako
|
|
33
|
-
onako
|
|
34
|
-
onako
|
|
33
|
+
onako serve --background --dir ~/projects # override working directory
|
|
34
|
+
onako status # check if running
|
|
35
|
+
onako stop # stop the background service
|
|
36
|
+
onako version # print version
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
## How it works
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -27,14 +27,24 @@ def version():
|
|
|
27
27
|
@main.command()
|
|
28
28
|
@click.option("--host", default="0.0.0.0", help="Host to bind to.")
|
|
29
29
|
@click.option("--port", default=8787, type=int, help="Port to bind to.")
|
|
30
|
-
|
|
30
|
+
@click.option("--dir", "working_dir", default=None, type=click.Path(exists=True), help="Working directory for tasks (default: current directory).")
|
|
31
|
+
@click.option("--background", is_flag=True, help="Run as a background service (launchd/systemd).")
|
|
32
|
+
def serve(host, port, working_dir, background):
|
|
31
33
|
"""Start the Onako server."""
|
|
34
|
+
working_dir = str(Path(working_dir).resolve()) if working_dir else os.getcwd()
|
|
35
|
+
|
|
36
|
+
if background:
|
|
37
|
+
_start_background(host, port, working_dir)
|
|
38
|
+
return
|
|
39
|
+
|
|
32
40
|
_check_prerequisites()
|
|
33
41
|
|
|
42
|
+
os.environ["ONAKO_WORKING_DIR"] = working_dir
|
|
43
|
+
|
|
34
44
|
from onako import __version__
|
|
35
45
|
click.echo(f"Onako v{__version__}")
|
|
36
46
|
click.echo(f"Starting server at http://{host}:{port}")
|
|
37
|
-
click.echo(f"
|
|
47
|
+
click.echo(f"Working directory: {working_dir}")
|
|
38
48
|
click.echo()
|
|
39
49
|
|
|
40
50
|
import uvicorn
|
|
@@ -43,10 +53,54 @@ def serve(host, port):
|
|
|
43
53
|
|
|
44
54
|
|
|
45
55
|
@main.command()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
def stop():
|
|
57
|
+
"""Stop the background Onako service."""
|
|
58
|
+
system = platform.system()
|
|
59
|
+
if system == "Darwin":
|
|
60
|
+
plist_path = Path.home() / "Library" / "LaunchAgents" / "com.onako.server.plist"
|
|
61
|
+
if plist_path.exists():
|
|
62
|
+
uid = os.getuid()
|
|
63
|
+
result = subprocess.run(
|
|
64
|
+
["launchctl", "bootout", f"gui/{uid}", str(plist_path)],
|
|
65
|
+
capture_output=True,
|
|
66
|
+
)
|
|
67
|
+
if result.returncode != 0:
|
|
68
|
+
subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True)
|
|
69
|
+
plist_path.unlink()
|
|
70
|
+
click.echo("Onako service stopped and removed.")
|
|
71
|
+
else:
|
|
72
|
+
click.echo("Onako service is not running.")
|
|
73
|
+
elif system == "Linux":
|
|
74
|
+
unit_path = Path.home() / ".config" / "systemd" / "user" / "onako.service"
|
|
75
|
+
if unit_path.exists():
|
|
76
|
+
subprocess.run(["systemctl", "--user", "disable", "--now", "onako"])
|
|
77
|
+
unit_path.unlink()
|
|
78
|
+
subprocess.run(["systemctl", "--user", "daemon-reload"])
|
|
79
|
+
click.echo("Onako service stopped and removed.")
|
|
80
|
+
else:
|
|
81
|
+
click.echo("Onako service is not running.")
|
|
82
|
+
else:
|
|
83
|
+
click.echo(f"Not supported on {system}.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@main.command()
|
|
87
|
+
def status():
|
|
88
|
+
"""Check if Onako is running."""
|
|
89
|
+
import urllib.request
|
|
90
|
+
try:
|
|
91
|
+
r = urllib.request.urlopen("http://127.0.0.1:8787/health", timeout=2)
|
|
92
|
+
data = r.read().decode()
|
|
93
|
+
if '"ok"' in data:
|
|
94
|
+
click.echo("Onako server: running")
|
|
95
|
+
click.echo(" URL: http://127.0.0.1:8787")
|
|
96
|
+
else:
|
|
97
|
+
click.echo("Onako server: not responding correctly")
|
|
98
|
+
except Exception:
|
|
99
|
+
click.echo("Onako server: not running")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _start_background(host, port, working_dir):
|
|
103
|
+
"""Install and start Onako as a background service."""
|
|
50
104
|
system = platform.system()
|
|
51
105
|
onako_bin = shutil.which("onako")
|
|
52
106
|
if not onako_bin:
|
|
@@ -65,21 +119,22 @@ def install(host, port):
|
|
|
65
119
|
path_value = ":".join(sorted(path_dirs))
|
|
66
120
|
|
|
67
121
|
if system == "Darwin":
|
|
68
|
-
_install_launchd(onako_bin, host, port, path_value)
|
|
122
|
+
_install_launchd(onako_bin, host, port, working_dir, path_value)
|
|
69
123
|
elif system == "Linux":
|
|
70
|
-
_install_systemd(onako_bin, host, port, path_value)
|
|
124
|
+
_install_systemd(onako_bin, host, port, working_dir, path_value)
|
|
71
125
|
else:
|
|
72
|
-
click.echo(f"
|
|
126
|
+
click.echo(f"Background mode is not supported on {system}. Run 'onako serve' manually.", err=True)
|
|
73
127
|
sys.exit(1)
|
|
74
128
|
|
|
75
129
|
|
|
76
|
-
def _install_launchd(onako_bin, host, port, path_value):
|
|
130
|
+
def _install_launchd(onako_bin, host, port, working_dir, path_value):
|
|
77
131
|
from importlib.resources import files
|
|
78
132
|
tpl = files("onako").joinpath("templates", "com.onako.server.plist.tpl").read_text()
|
|
79
133
|
plist = tpl.format(
|
|
80
134
|
onako_bin=onako_bin,
|
|
81
135
|
host=host,
|
|
82
136
|
port=port,
|
|
137
|
+
working_dir=working_dir,
|
|
83
138
|
log_dir=LOG_DIR,
|
|
84
139
|
path_value=path_value,
|
|
85
140
|
)
|
|
@@ -109,21 +164,22 @@ def _install_launchd(onako_bin, host, port, path_value):
|
|
|
109
164
|
capture_output=True,
|
|
110
165
|
)
|
|
111
166
|
|
|
112
|
-
click.echo(f"
|
|
113
|
-
click.echo(f"
|
|
114
|
-
click.echo(f"
|
|
167
|
+
click.echo(f"Onako running in background at http://{host}:{port}")
|
|
168
|
+
click.echo(f" Working directory: {working_dir}")
|
|
169
|
+
click.echo(f" Logs: {LOG_DIR}")
|
|
115
170
|
click.echo()
|
|
116
171
|
click.echo("If macOS blocks this service, allow it in:")
|
|
117
172
|
click.echo(" System Settings > General > Login Items & Extensions")
|
|
118
173
|
|
|
119
174
|
|
|
120
|
-
def _install_systemd(onako_bin, host, port, path_value):
|
|
175
|
+
def _install_systemd(onako_bin, host, port, working_dir, path_value):
|
|
121
176
|
from importlib.resources import files
|
|
122
177
|
tpl = files("onako").joinpath("templates", "onako.service.tpl").read_text()
|
|
123
178
|
unit = tpl.format(
|
|
124
179
|
onako_bin=onako_bin,
|
|
125
180
|
host=host,
|
|
126
181
|
port=port,
|
|
182
|
+
working_dir=working_dir,
|
|
127
183
|
path_value=path_value,
|
|
128
184
|
)
|
|
129
185
|
unit_path = Path.home() / ".config" / "systemd" / "user" / "onako.service"
|
|
@@ -131,55 +187,8 @@ def _install_systemd(onako_bin, host, port, path_value):
|
|
|
131
187
|
unit_path.write_text(unit)
|
|
132
188
|
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
|
|
133
189
|
subprocess.run(["systemctl", "--user", "enable", "--now", "onako"], check=True)
|
|
134
|
-
click.echo(f"
|
|
135
|
-
click.echo(f"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
@main.command()
|
|
139
|
-
def uninstall():
|
|
140
|
-
"""Remove the Onako background service."""
|
|
141
|
-
system = platform.system()
|
|
142
|
-
if system == "Darwin":
|
|
143
|
-
plist_path = Path.home() / "Library" / "LaunchAgents" / "com.onako.server.plist"
|
|
144
|
-
if plist_path.exists():
|
|
145
|
-
uid = os.getuid()
|
|
146
|
-
result = subprocess.run(
|
|
147
|
-
["launchctl", "bootout", f"gui/{uid}", str(plist_path)],
|
|
148
|
-
capture_output=True,
|
|
149
|
-
)
|
|
150
|
-
if result.returncode != 0:
|
|
151
|
-
subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True)
|
|
152
|
-
plist_path.unlink()
|
|
153
|
-
click.echo("Onako service removed.")
|
|
154
|
-
else:
|
|
155
|
-
click.echo("Onako service is not installed.")
|
|
156
|
-
elif system == "Linux":
|
|
157
|
-
unit_path = Path.home() / ".config" / "systemd" / "user" / "onako.service"
|
|
158
|
-
if unit_path.exists():
|
|
159
|
-
subprocess.run(["systemctl", "--user", "disable", "--now", "onako"])
|
|
160
|
-
unit_path.unlink()
|
|
161
|
-
subprocess.run(["systemctl", "--user", "daemon-reload"])
|
|
162
|
-
click.echo("Onako service removed.")
|
|
163
|
-
else:
|
|
164
|
-
click.echo("Onako service is not installed.")
|
|
165
|
-
else:
|
|
166
|
-
click.echo(f"Not supported on {system}.")
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@main.command()
|
|
170
|
-
def status():
|
|
171
|
-
"""Check if Onako is running."""
|
|
172
|
-
import urllib.request
|
|
173
|
-
try:
|
|
174
|
-
r = urllib.request.urlopen("http://127.0.0.1:8787/health", timeout=2)
|
|
175
|
-
data = r.read().decode()
|
|
176
|
-
if '"ok"' in data:
|
|
177
|
-
click.echo("Onako server: running")
|
|
178
|
-
click.echo(" URL: http://127.0.0.1:8787")
|
|
179
|
-
else:
|
|
180
|
-
click.echo("Onako server: not responding correctly")
|
|
181
|
-
except Exception:
|
|
182
|
-
click.echo("Onako server: not running")
|
|
190
|
+
click.echo(f"Onako running in background at http://{host}:{port}")
|
|
191
|
+
click.echo(f" Working directory: {working_dir}")
|
|
183
192
|
|
|
184
193
|
|
|
185
194
|
def _check_prerequisites():
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
<string>{host}</string>
|
|
13
13
|
<string>--port</string>
|
|
14
14
|
<string>{port}</string>
|
|
15
|
+
<string>--dir</string>
|
|
16
|
+
<string>{working_dir}</string>
|
|
15
17
|
</array>
|
|
18
|
+
<key>WorkingDirectory</key>
|
|
19
|
+
<string>{working_dir}</string>
|
|
16
20
|
<key>RunAtLoad</key>
|
|
17
21
|
<true/>
|
|
18
22
|
<key>KeepAlive</key>
|
|
@@ -3,7 +3,8 @@ Description=Onako - Claude Code Task Orchestrator
|
|
|
3
3
|
After=network.target
|
|
4
4
|
|
|
5
5
|
[Service]
|
|
6
|
-
ExecStart={onako_bin} serve --host {host} --port {port}
|
|
6
|
+
ExecStart={onako_bin} serve --host {host} --port {port} --dir {working_dir}
|
|
7
|
+
WorkingDirectory={working_dir}
|
|
7
8
|
Restart=on-failure
|
|
8
9
|
Environment=PATH={path_value}
|
|
9
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onako
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Dispatch and monitor Claude Code tasks from your phone
|
|
5
5
|
Author: Amir
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,23 +32,25 @@ Requires [tmux](https://github.com/tmux/tmux) and [Claude Code](https://docs.ant
|
|
|
32
32
|
## Usage
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
cd ~/projects
|
|
35
36
|
onako serve
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
Open http://localhost:8787 on your phone (same network) or set up [Tailscale](https://tailscale.com) for access from anywhere.
|
|
39
40
|
|
|
40
|
-
###
|
|
41
|
+
### Run in the background
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
|
-
onako
|
|
44
|
+
onako serve --background
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
This installs a system service (launchd on macOS, systemd on Linux) that starts on boot. Tasks run in the directory where you ran the command.
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
onako
|
|
50
|
-
onako
|
|
51
|
-
onako
|
|
50
|
+
onako serve --background --dir ~/projects # override working directory
|
|
51
|
+
onako status # check if running
|
|
52
|
+
onako stop # stop the background service
|
|
53
|
+
onako version # print version
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## How it works
|
|
@@ -6,7 +6,7 @@ def test_version():
|
|
|
6
6
|
runner = CliRunner()
|
|
7
7
|
result = runner.invoke(main, ["version"])
|
|
8
8
|
assert result.exit_code == 0
|
|
9
|
-
assert "0.
|
|
9
|
+
assert "0.2.0" in result.output
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def test_serve_help():
|
|
@@ -15,3 +15,5 @@ def test_serve_help():
|
|
|
15
15
|
assert result.exit_code == 0
|
|
16
16
|
assert "--host" in result.output
|
|
17
17
|
assert "--port" in result.output
|
|
18
|
+
assert "--background" in result.output
|
|
19
|
+
assert "--dir" in result.output
|
|
@@ -9,8 +9,8 @@ def test_status_reports_something():
|
|
|
9
9
|
assert "Onako server:" in result.output
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def
|
|
12
|
+
def test_stop_completes():
|
|
13
13
|
runner = CliRunner()
|
|
14
|
-
result = runner.invoke(main, ["
|
|
14
|
+
result = runner.invoke(main, ["stop"])
|
|
15
15
|
assert result.exit_code == 0
|
|
16
|
-
assert "not
|
|
16
|
+
assert "not running" in result.output or "stopped" in result.output
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.2"
|
|
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
|