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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onako
3
- Version: 0.1.2
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
- ### Auto-start on boot
41
+ ### Run in the background
41
42
 
42
43
  ```bash
43
- onako install
44
+ onako serve --background
44
45
  ```
45
46
 
46
- ### Other commands
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 status # Check if server is running
50
- onako uninstall # Remove auto-start service
51
- onako version # Print version
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
- ### Auto-start on boot
24
+ ### Run in the background
24
25
 
25
26
  ```bash
26
- onako install
27
+ onako serve --background
27
28
  ```
28
29
 
29
- ### Other commands
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 status # Check if server is running
33
- onako uninstall # Remove auto-start service
34
- onako version # Print version
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "onako"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "Dispatch and monitor Claude Code tasks from your phone"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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
- def serve(host, port):
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"Dashboard: http://{host}:{port}")
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
- @click.option("--host", default="0.0.0.0", help="Host to bind to.")
47
- @click.option("--port", default=8787, type=int, help="Port to bind to.")
48
- def install(host, port):
49
- """Install Onako as a background service (launchd on macOS, systemd on Linux)."""
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"Auto-start is not supported on {system}. Run 'onako serve' manually.", err=True)
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"Installed launchd service: {plist_path}")
113
- click.echo(f"Logs: {LOG_DIR}")
114
- click.echo(f"Onako is running at http://{host}:{port}")
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"Installed systemd service: {unit_path}")
135
- click.echo(f"Onako is running at http://{host}:{port}")
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.1.2
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
- ### Auto-start on boot
41
+ ### Run in the background
41
42
 
42
43
  ```bash
43
- onako install
44
+ onako serve --background
44
45
  ```
45
46
 
46
- ### Other commands
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 status # Check if server is running
50
- onako uninstall # Remove auto-start service
51
- onako version # Print version
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.1.2" in result.output
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 test_uninstall_completes():
12
+ def test_stop_completes():
13
13
  runner = CliRunner()
14
- result = runner.invoke(main, ["uninstall"])
14
+ result = runner.invoke(main, ["stop"])
15
15
  assert result.exit_code == 0
16
- assert "not installed" in result.output or "removed" in result.output
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