agentpool-cli 0.1.0__py3-none-any.whl

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 (60) hide show
  1. agentpool/__init__.py +3 -0
  2. agentpool/agent_io.py +134 -0
  3. agentpool/artifacts.py +151 -0
  4. agentpool/cli.py +1199 -0
  5. agentpool/config.py +373 -0
  6. agentpool/docs/agentpool-skill.md +85 -0
  7. agentpool/docs/onboarding.md +169 -0
  8. agentpool/event_detection.py +150 -0
  9. agentpool/fixtures/__init__.py +1 -0
  10. agentpool/fixtures/fake_agents/__init__.py +1 -0
  11. agentpool/fixtures/fake_agents/fake_approval_agent.py +16 -0
  12. agentpool/fixtures/fake_agents/fake_common.py +44 -0
  13. agentpool/fixtures/fake_agents/fake_completed_agent.py +13 -0
  14. agentpool/fixtures/fake_agents/fake_idle_agent.py +16 -0
  15. agentpool/fixtures/fake_agents/fake_limit_agent.py +14 -0
  16. agentpool/fixtures/fake_agents/fake_patch_agent.py +17 -0
  17. agentpool/fixtures/fake_agents/fake_question_agent.py +16 -0
  18. agentpool/git_worktree.py +144 -0
  19. agentpool/mcp/__init__.py +1 -0
  20. agentpool/mcp/resources.py +64 -0
  21. agentpool/mcp/tools.py +259 -0
  22. agentpool/mcp_server.py +487 -0
  23. agentpool/models.py +310 -0
  24. agentpool/onboarding.py +1279 -0
  25. agentpool/policy.py +63 -0
  26. agentpool/provider_model_catalog.json +997 -0
  27. agentpool/providers/__init__.py +3 -0
  28. agentpool/providers/base.py +411 -0
  29. agentpool/providers/registry.py +139 -0
  30. agentpool/redaction.py +30 -0
  31. agentpool/runtimes/__init__.py +3 -0
  32. agentpool/runtimes/base.py +36 -0
  33. agentpool/runtimes/tmux.py +133 -0
  34. agentpool/session_manager.py +1061 -0
  35. agentpool/stats/__init__.py +6 -0
  36. agentpool/stats/card.py +74 -0
  37. agentpool/stats/compute.py +496 -0
  38. agentpool/stats/queries.py +138 -0
  39. agentpool/stats/render.py +103 -0
  40. agentpool/stats/window.py +85 -0
  41. agentpool/store.py +478 -0
  42. agentpool/usage/__init__.py +1 -0
  43. agentpool/usage/_common.py +223 -0
  44. agentpool/usage/ccusage.py +130 -0
  45. agentpool/usage/claude.py +23 -0
  46. agentpool/usage/codex.py +210 -0
  47. agentpool/usage/codexbar.py +186 -0
  48. agentpool/usage/combine.py +71 -0
  49. agentpool/usage/copilot.py +146 -0
  50. agentpool/usage/devin.py +265 -0
  51. agentpool/usage/parsers.py +41 -0
  52. agentpool/usage/probes.py +52 -0
  53. agentpool/usage/provider_parsers.py +276 -0
  54. agentpool/usage/summary.py +166 -0
  55. agentpool/utils.py +59 -0
  56. agentpool_cli-0.1.0.dist-info/METADATA +292 -0
  57. agentpool_cli-0.1.0.dist-info/RECORD +60 -0
  58. agentpool_cli-0.1.0.dist-info/WHEEL +4 -0
  59. agentpool_cli-0.1.0.dist-info/entry_points.txt +2 -0
  60. agentpool_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import signal
5
+ import shutil
6
+ import subprocess
7
+ from pathlib import Path
8
+
9
+ from agentpool.models import RuntimeKind, TmuxSessionRef, ToolError
10
+
11
+
12
+ class TmuxRuntime:
13
+ kind = RuntimeKind.TMUX
14
+
15
+ def __init__(self, tmux_binary: str | None = None):
16
+ self.tmux_binary = tmux_binary or shutil.which("tmux")
17
+
18
+ def require_tmux(self) -> str:
19
+ if not self.tmux_binary:
20
+ raise ToolError("TMUX_NOT_FOUND", "tmux is required for AgentPool v0.1.")
21
+ return self.tmux_binary
22
+
23
+ def spawn(
24
+ self, command: list[str], cwd: Path, env: dict[str, str] | None, session_name: str
25
+ ) -> TmuxSessionRef:
26
+ tmux = self.require_tmux()
27
+ cwd.mkdir(parents=True, exist_ok=True)
28
+ merged_env = os.environ.copy()
29
+ if env:
30
+ merged_env.update(env)
31
+ try:
32
+ subprocess.run(
33
+ [tmux, "new-session", "-d", "-s", session_name, "-c", str(cwd), *command],
34
+ env=merged_env,
35
+ text=True,
36
+ capture_output=True,
37
+ check=True,
38
+ )
39
+ except subprocess.CalledProcessError as exc:
40
+ raise ToolError(
41
+ "SPAWN_FAILED",
42
+ f"Failed to create tmux session {session_name}.",
43
+ {"stderr": exc.stderr, "stdout": exc.stdout, "command": command},
44
+ ) from exc
45
+ return TmuxSessionRef(session_name=session_name)
46
+
47
+ def capture(self, ref: TmuxSessionRef, lines: int = 300) -> str:
48
+ tmux = self.require_tmux()
49
+ proc = subprocess.run(
50
+ [tmux, "capture-pane", "-p", "-J", "-t", ref.target, "-S", f"-{lines}"],
51
+ text=True,
52
+ capture_output=True,
53
+ check=False,
54
+ )
55
+ if proc.returncode != 0:
56
+ raise ToolError(
57
+ "TMUX_SESSION_NOT_FOUND",
58
+ f"Could not capture tmux pane {ref.target}.",
59
+ {"stderr": proc.stderr},
60
+ )
61
+ return proc.stdout
62
+
63
+ def send_message(self, ref: TmuxSessionRef, text: str, submit: bool = True) -> None:
64
+ if text == "" and submit:
65
+ self.send_keys(ref, ["Enter"])
66
+ return
67
+ tmux = self.require_tmux()
68
+ buffer_name = f"agentpool-{ref.session_name}"
69
+ subprocess.run([tmux, "load-buffer", "-b", buffer_name, "-"], input=text, text=True, check=True)
70
+ subprocess.run([tmux, "paste-buffer", "-b", buffer_name, "-t", ref.target], check=True)
71
+ if submit and not text.endswith("\n"):
72
+ self.send_keys(ref, ["Enter"])
73
+
74
+ def send_keys(self, ref: TmuxSessionRef, keys: list[str]) -> None:
75
+ tmux = self.require_tmux()
76
+ proc = subprocess.run(
77
+ [tmux, "send-keys", "-t", ref.target, *keys],
78
+ text=True,
79
+ capture_output=True,
80
+ check=False,
81
+ )
82
+ if proc.returncode != 0:
83
+ raise ToolError(
84
+ "TMUX_SESSION_NOT_FOUND",
85
+ f"Could not send keys to tmux pane {ref.target}.",
86
+ {"stderr": proc.stderr, "keys": keys},
87
+ )
88
+
89
+ def interrupt(self, ref: TmuxSessionRef) -> None:
90
+ self.send_keys(ref, ["C-c"])
91
+
92
+ def attach_command(self, ref: TmuxSessionRef) -> str:
93
+ return f"tmux attach -t {ref.session_name}"
94
+
95
+ def terminate(self, ref: TmuxSessionRef) -> None:
96
+ tmux = self.require_tmux()
97
+ pgid = self._pane_process_group(ref)
98
+ subprocess.run([tmux, "kill-session", "-t", ref.session_name], check=False)
99
+ if pgid is not None:
100
+ try:
101
+ os.killpg(pgid, signal.SIGTERM)
102
+ except ProcessLookupError:
103
+ pass
104
+ except PermissionError:
105
+ pass
106
+
107
+ def exists(self, ref: TmuxSessionRef) -> bool:
108
+ if not self.tmux_binary:
109
+ return False
110
+ proc = subprocess.run(
111
+ [self.tmux_binary, "has-session", "-t", ref.session_name],
112
+ text=True,
113
+ capture_output=True,
114
+ check=False,
115
+ )
116
+ return proc.returncode == 0
117
+
118
+ def _pane_process_group(self, ref: TmuxSessionRef) -> int | None:
119
+ if not self.tmux_binary:
120
+ return None
121
+ proc = subprocess.run(
122
+ [self.tmux_binary, "display-message", "-p", "-t", ref.target, "#{pane_pid}"],
123
+ text=True,
124
+ capture_output=True,
125
+ check=False,
126
+ )
127
+ if proc.returncode != 0:
128
+ return None
129
+ try:
130
+ pane_pid = int(proc.stdout.strip())
131
+ return os.getpgid(pane_pid)
132
+ except (ValueError, ProcessLookupError, PermissionError):
133
+ return None