cloudagent-daemon 0.2.3__tar.gz → 0.2.5__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 (33) hide show
  1. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/PKG-INFO +1 -1
  2. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/pyproject.toml +1 -1
  3. cloudagent_daemon-0.2.5/src/cloudagent_daemon/__init__.py +1 -0
  4. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/service.py +72 -9
  5. cloudagent_daemon-0.2.3/src/cloudagent_daemon/__init__.py +0 -1
  6. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/.gitignore +0 -0
  7. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/LICENSE +0 -0
  8. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/README.md +0 -0
  9. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/__main__.py +0 -0
  10. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/claude_runner.py +0 -0
  11. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/cli.py +0 -0
  12. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/config.py +0 -0
  13. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/doctor.py +0 -0
  14. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/frame_dispatch.py +0 -0
  15. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/http_outbound.py +0 -0
  16. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/jsonl_logging.py +0 -0
  17. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/login.py +0 -0
  18. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/main_loop.py +0 -0
  19. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/mcp_bridge.py +0 -0
  20. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/metrics.py +0 -0
  21. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/metrics_server.py +0 -0
  22. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/outbound_queue.py +0 -0
  23. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/paths.py +0 -0
  24. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/protocol.py +0 -0
  25. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/runtime_detect.py +0 -0
  26. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/spawn_outbox.py +0 -0
  27. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/spawn_queue.py +0 -0
  28. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/sse_client.py +0 -0
  29. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/transport.py +0 -0
  30. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/unix_socket.py +0 -0
  31. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/workspace.py +0 -0
  32. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/src/cloudagent_daemon/ws_client.py +0 -0
  33. {cloudagent_daemon-0.2.3 → cloudagent_daemon-0.2.5}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudagent-daemon
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Local daemon for cloudagent — bridges Claude Code subprocesses to the cloudagent backend over WebSocket.
5
5
  Project-URL: Homepage, https://cloudagent.chat
6
6
  Project-URL: Repository, https://codeup.aliyun.com/69bd0a4d29ad98af4065ce71/cloudagent
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cloudagent-daemon"
7
- version = "0.2.3"
7
+ version = "0.2.5"
8
8
  description = "Local daemon for cloudagent — bridges Claude Code subprocesses to the cloudagent backend over WebSocket."
9
9
  authors = [{name = "chaoyin", email = "tudoucyx@gmail.com"}]
10
10
  readme = "README.md"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.5"
@@ -17,6 +17,7 @@ import os
17
17
  import platform
18
18
  import shutil
19
19
  import subprocess
20
+ import time
20
21
  from pathlib import Path
21
22
 
22
23
  from .paths import (
@@ -200,18 +201,69 @@ def install_macos(
200
201
  plist_path.chmod(0o600)
201
202
 
202
203
  uid = os.getuid()
203
- # Bootstrap into launchd (modern API). ``check=False`` because the
204
- # service may already be loaded — bootout/kickstart below will recover.
205
- subprocess.run(
206
- ["launchctl", "bootstrap", f"gui/{uid}", str(plist_path)],
207
- check=False,
208
- )
204
+ domain = f"gui/{uid}"
205
+ service_target = f"{domain}/{label}"
206
+ _launchd_reinstall(domain, service_target, plist_path)
207
+ return plist_path
208
+
209
+
210
+ def _launchd_reinstall(
211
+ domain: str, service_target: str, plist_path: Path
212
+ ) -> None:
213
+ """Reinstall a launchd job: bootout → wait → bootstrap → kickstart.
214
+
215
+ Reinstall-safe + race-safe. Two problems this handles:
216
+
217
+ 1. A plain ``bootstrap`` is a no-op when the service is already
218
+ loaded — launchd keeps the OLD in-memory plist and never re-reads
219
+ the file. So we ``bootout`` first to unload any loaded instance.
220
+
221
+ 2. ``bootout`` is *asynchronous*: launchctl returns before launchd
222
+ has finished tearing the job down. A ``bootstrap`` fired
223
+ immediately races the still-unloading job and fails with
224
+ ``Input/output error`` (5). So after bootout we POLL
225
+ ``launchctl print`` until the job is genuinely gone, then
226
+ bootstrap — with a few retries for slow teardown.
227
+
228
+ Raises RuntimeError if bootstrap never succeeds (cli surfaces it as
229
+ a clean error instead of the old silent "✓ Installed" lie).
230
+ """
231
+ subprocess.run(["launchctl", "bootout", service_target], check=False)
232
+
233
+ # Poll until the job is no longer registered (``print`` exits
234
+ # non-zero). ~5s budget — teardown is normally well under 1s.
235
+ for _ in range(50):
236
+ probe = subprocess.run(
237
+ ["launchctl", "print", service_target],
238
+ capture_output=True,
239
+ check=False,
240
+ )
241
+ if probe.returncode != 0:
242
+ break
243
+ time.sleep(0.1)
244
+
245
+ last_err = ""
246
+ for _ in range(5):
247
+ result = subprocess.run(
248
+ ["launchctl", "bootstrap", domain, str(plist_path)],
249
+ capture_output=True,
250
+ text=True,
251
+ check=False,
252
+ )
253
+ if result.returncode == 0:
254
+ break
255
+ last_err = (result.stderr or result.stdout or "").strip()
256
+ time.sleep(0.3)
257
+ else:
258
+ raise RuntimeError(
259
+ f"launchctl bootstrap failed after retries: {last_err or 'unknown'}"
260
+ )
261
+
209
262
  # Kickstart -k forces an immediate (re)start.
210
263
  subprocess.run(
211
- ["launchctl", "kickstart", "-k", f"gui/{uid}/{label}"],
264
+ ["launchctl", "kickstart", "-k", service_target],
212
265
  check=False,
213
266
  )
214
- return plist_path
215
267
 
216
268
 
217
269
  def uninstall_macos(profile: str | None = None) -> bool:
@@ -324,7 +376,18 @@ def install_linux(
324
376
  )
325
377
  subprocess.run(["systemctl", "--user", "daemon-reload"], check=False)
326
378
  subprocess.run(
327
- ["systemctl", "--user", "enable", "--now", unit_name],
379
+ ["systemctl", "--user", "enable", unit_name],
380
+ check=False,
381
+ )
382
+ # Reinstall-safe (2026-05-19): ``restart`` (not ``enable --now`` /
383
+ # ``start``) so a re-run of ``install-service`` genuinely applies
384
+ # the freshly written unit file — ``start`` is a no-op on an
385
+ # already-running service, leaving the daemon on the stale config.
386
+ # ``daemon-reload`` above re-read the unit; ``restart`` makes the
387
+ # running process pick it up. ``restart`` also starts a stopped
388
+ # service, so this covers first-install too.
389
+ subprocess.run(
390
+ ["systemctl", "--user", "restart", unit_name],
328
391
  check=False,
329
392
  )
330
393
  return unit_path
@@ -1 +0,0 @@
1
- __version__ = "0.2.3"