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,146 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ import urllib.request
7
+ from typing import Any
8
+
9
+ from agentpool.models import CapacitySnapshot, Confidence, UsageStatus, UsageWindow, UsageWindowKind
10
+ from agentpool.usage._common import (
11
+ ProbeError,
12
+ _clamp_percent,
13
+ _clean_optional_string,
14
+ _number,
15
+ _parse_datetime,
16
+ _request_json,
17
+ _status_from_windows,
18
+ unknown,
19
+ )
20
+
21
+
22
+ def copilot_cli_usage_snapshot(provider_id: str, binary: str | None = None) -> CapacitySnapshot:
23
+ _ = binary
24
+ token_result = _copilot_token()
25
+ if not token_result:
26
+ return CapacitySnapshot(
27
+ provider_id=provider_id,
28
+ status=UsageStatus.UNAUTHENTICATED,
29
+ confidence=Confidence.UNKNOWN,
30
+ warnings=[
31
+ "No Copilot API token found. Set AGENTPOOL_COPILOT_TOKEN/GITHUB_TOKEN/GH_TOKEN, "
32
+ "or authenticate gh so `gh auth token` can provide a token."
33
+ ],
34
+ raw={"source": "github_copilot_internal_api"},
35
+ )
36
+ token, token_source = token_result
37
+ try:
38
+ request = urllib.request.Request(
39
+ "https://api.github.com/copilot_internal/user",
40
+ headers={
41
+ "Authorization": f"token {token}",
42
+ "Accept": "application/json",
43
+ # GitHub Copilot usage API client id; not an MCP host or VS Code IDE config.
44
+ "Editor-Version": "vscode/1.96.2",
45
+ "Editor-Plugin-Version": "copilot-chat/0.26.7",
46
+ "User-Agent": "GitHubCopilotChat/0.26.7",
47
+ "X-Github-Api-Version": "2025-04-01",
48
+ },
49
+ )
50
+ payload = _request_json(request)
51
+ snapshot = parse_copilot_usage_response(provider_id, payload)
52
+ snapshot.raw["source"] = "github_copilot_internal_api"
53
+ snapshot.raw["token_source"] = token_source
54
+ return snapshot
55
+ except ProbeError as exc:
56
+ return unknown(
57
+ provider_id,
58
+ f"Copilot internal API probe failed: {exc}",
59
+ source="github_copilot_internal_api",
60
+ )
61
+
62
+
63
+ def parse_copilot_usage_response(provider_id: str, payload: dict[str, Any]) -> CapacitySnapshot:
64
+ snapshots = payload.get("quota_snapshots")
65
+ if not isinstance(snapshots, dict):
66
+ snapshots = {}
67
+ windows = []
68
+ for name, item in (("premium_interactions", snapshots.get("premium_interactions")), ("chat", snapshots.get("chat"))):
69
+ window = _copilot_window(name, item)
70
+ if window:
71
+ windows.append(window)
72
+ if not windows:
73
+ windows = _copilot_windows_from_legacy_counts(payload)
74
+ if not windows:
75
+ raise ProbeError("Copilot response did not include usable quota snapshots.")
76
+ reset_at = _parse_datetime(payload.get("quota_reset_date"))
77
+ if reset_at:
78
+ windows = [window.model_copy(update={"reset_at": reset_at}) for window in windows]
79
+ return CapacitySnapshot(
80
+ provider_id=provider_id,
81
+ status=_status_from_windows(windows),
82
+ confidence=Confidence.OFFICIAL,
83
+ windows=windows,
84
+ reset_at=reset_at,
85
+ raw={"plan": _clean_optional_string(payload.get("copilot_plan"))},
86
+ )
87
+
88
+
89
+ def _copilot_token() -> tuple[str, str] | None:
90
+ for key in ("AGENTPOOL_COPILOT_TOKEN", "GITHUB_TOKEN", "GH_TOKEN"):
91
+ value = os.environ.get(key)
92
+ if value:
93
+ return value, key
94
+ gh = shutil.which("gh")
95
+ if not gh:
96
+ return None
97
+ proc = subprocess.run([gh, "auth", "token"], text=True, capture_output=True, timeout=5, check=False)
98
+ token = proc.stdout.strip()
99
+ if proc.returncode == 0 and token:
100
+ return token, "gh auth token"
101
+ return None
102
+
103
+
104
+ def _copilot_window(name: str, item: object) -> UsageWindow | None:
105
+ if not isinstance(item, dict):
106
+ return None
107
+ percent_remaining = _number(item.get("percent_remaining"))
108
+ entitlement = _number(item.get("entitlement"))
109
+ remaining = _number(item.get("remaining"))
110
+ if percent_remaining is None and entitlement and remaining is not None:
111
+ percent_remaining = (remaining / entitlement) * 100.0
112
+ if percent_remaining is None or (entitlement == 0 and remaining == 0 and percent_remaining == 0):
113
+ return None
114
+ return UsageWindow(
115
+ name=name,
116
+ kind=UsageWindowKind.MONTHLY,
117
+ used_percent=_clamp_percent(100.0 - percent_remaining),
118
+ remaining_percent=_clamp_percent(percent_remaining),
119
+ remaining_units=remaining,
120
+ confidence=Confidence.OFFICIAL,
121
+ )
122
+
123
+
124
+ def _copilot_windows_from_legacy_counts(payload: dict[str, Any]) -> list[UsageWindow]:
125
+ monthly = payload.get("monthly_quotas")
126
+ limited = payload.get("limited_user_quotas")
127
+ if not isinstance(monthly, dict) or not isinstance(limited, dict):
128
+ return []
129
+ windows: list[UsageWindow] = []
130
+ for name, key in (("premium_interactions", "completions"), ("chat", "chat")):
131
+ total = _number(monthly.get(key))
132
+ remaining = _number(limited.get(key))
133
+ if not total or remaining is None:
134
+ continue
135
+ percent_remaining = (remaining / total) * 100.0
136
+ windows.append(
137
+ UsageWindow(
138
+ name=name,
139
+ kind=UsageWindowKind.MONTHLY,
140
+ used_percent=_clamp_percent(100.0 - percent_remaining),
141
+ remaining_percent=_clamp_percent(percent_remaining),
142
+ remaining_units=remaining,
143
+ confidence=Confidence.OFFICIAL,
144
+ )
145
+ )
146
+ return windows
@@ -0,0 +1,265 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ import tomllib
5
+ import urllib.error
6
+ import urllib.request
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from agentpool.models import CapacitySnapshot, Confidence, UsageWindow, UsageWindowKind
11
+ from agentpool.usage._common import (
12
+ ProbeError,
13
+ _clamp_percent,
14
+ _epoch_seconds,
15
+ _number,
16
+ _tmux_slash_usage_probe,
17
+ _clean_optional_string,
18
+ _status_from_windows,
19
+ unavailable,
20
+ )
21
+ from agentpool.usage.provider_parsers import parse_devin_usage
22
+
23
+
24
+ def devin_cli_usage_snapshot(provider_id: str, binary: str | None = None) -> CapacitySnapshot:
25
+ executable = binary or shutil.which("devin")
26
+ if not executable:
27
+ return unavailable(provider_id, "Devin CLI is not installed.")
28
+ try:
29
+ return _devin_plan_status_usage_snapshot(provider_id)
30
+ except ProbeError as exc:
31
+ fallback_warning = f"Devin plan-status API probe failed; fell back to CLI /usage: {exc}"
32
+ snapshot = _tmux_slash_usage_probe(
33
+ provider_id=provider_id,
34
+ command=[executable, "--permission-mode", "auto"],
35
+ slash_command="/usage",
36
+ parser=parse_devin_usage,
37
+ source="devin_pty_usage",
38
+ startup_delay=1.0,
39
+ timeout=18.0,
40
+ pre_keys=[["Enter"]],
41
+ prefer_text="Quota used:",
42
+ )
43
+ snapshot.warnings.append(fallback_warning)
44
+ return snapshot
45
+
46
+
47
+ def _devin_plan_status_usage_snapshot(provider_id: str) -> CapacitySnapshot:
48
+ creds = _load_devin_cli_credentials()
49
+ token = _clean_optional_string(creds.get("windsurf_api_key"))
50
+ if not token:
51
+ raise ProbeError("Devin CLI credentials do not contain windsurf_api_key.")
52
+ api_server_url = _clean_optional_string(creds.get("api_server_url")) or "https://server.codeium.com"
53
+ endpoint = api_server_url.rstrip("/") + "/exa.seat_management_pb.SeatManagementService/GetPlanStatus"
54
+ request = urllib.request.Request(
55
+ endpoint,
56
+ data=_encode_devin_plan_status_request(token),
57
+ headers={
58
+ "Content-Type": "application/proto",
59
+ "Connect-Protocol-Version": "1",
60
+ "Origin": "https://windsurf.com",
61
+ "Referer": "https://windsurf.com/profile",
62
+ "User-Agent": "agentpool-devin-probe/0.1",
63
+ "x-auth-token": token,
64
+ "x-devin-session-token": token,
65
+ },
66
+ method="POST",
67
+ )
68
+ try:
69
+ with urllib.request.urlopen(request, timeout=10) as response:
70
+ data = response.read()
71
+ except urllib.error.HTTPError as exc:
72
+ body = exc.read().decode("utf-8", errors="replace")[:500].replace(token, "<redacted>")
73
+ raise ProbeError(f"HTTP {exc.code}: {body}") from exc
74
+ except urllib.error.URLError as exc:
75
+ raise ProbeError(str(exc.reason)) from exc
76
+ payload = decode_devin_plan_status_response(data)
77
+ snapshot = parse_devin_plan_status_response(provider_id, payload)
78
+ snapshot.raw["source"] = "devin_plan_status_api"
79
+ snapshot.raw["credential_source"] = "devin_cli_credentials"
80
+ return snapshot
81
+
82
+
83
+ def _load_devin_cli_credentials() -> dict[str, Any]:
84
+ path = Path("~/.local/share/devin/credentials.toml").expanduser()
85
+ if not path.exists():
86
+ raise ProbeError("Devin CLI credentials were not found. Run `devin auth login` first.")
87
+ try:
88
+ raw = tomllib.loads(path.read_text(encoding="utf-8"))
89
+ except (OSError, tomllib.TOMLDecodeError) as exc:
90
+ raise ProbeError(f"Could not read Devin CLI credentials: {exc}") from exc
91
+ if not isinstance(raw, dict):
92
+ raise ProbeError("Devin CLI credentials did not parse as a TOML table.")
93
+ return raw
94
+
95
+
96
+ def parse_devin_plan_status_response(provider_id: str, payload: dict[str, Any]) -> CapacitySnapshot:
97
+ plan_status = payload.get("plan_status")
98
+ if not isinstance(plan_status, dict):
99
+ raise ProbeError("Devin plan-status response did not include plan_status.")
100
+ windows: list[UsageWindow] = []
101
+ for name, remaining_key, reset_key in (
102
+ ("daily", "daily_remaining_percent", "daily_reset_at_unix"),
103
+ ("weekly", "weekly_remaining_percent", "weekly_reset_at_unix"),
104
+ ):
105
+ remaining = _number(plan_status.get(remaining_key))
106
+ if remaining is None:
107
+ continue
108
+ windows.append(
109
+ UsageWindow(
110
+ name=name,
111
+ kind=UsageWindowKind(name),
112
+ status=name,
113
+ used_percent=_clamp_percent(100.0 - remaining),
114
+ remaining_percent=_clamp_percent(remaining),
115
+ reset_at=_epoch_seconds(plan_status.get(reset_key)),
116
+ confidence=Confidence.OFFICIAL,
117
+ )
118
+ )
119
+ overage_balance_micros = _number(plan_status.get("overage_balance_micros"))
120
+ credits_remaining = overage_balance_micros / 1_000_000 if overage_balance_micros is not None else None
121
+ if not windows and credits_remaining is None:
122
+ raise ProbeError("Devin plan-status response did not include quota windows or overage balance.")
123
+ plan_info = plan_status.get("plan_info") if isinstance(plan_status.get("plan_info"), dict) else {}
124
+ raw: dict[str, Any] = {
125
+ "plan_name": _clean_optional_string(plan_info.get("plan_name")),
126
+ "teams_tier": plan_info.get("teams_tier"),
127
+ "plan_start": _epoch_seconds(plan_status.get("plan_start_unix")).isoformat()
128
+ if _epoch_seconds(plan_status.get("plan_start_unix"))
129
+ else None,
130
+ "plan_end": _epoch_seconds(plan_status.get("plan_end_unix")).isoformat()
131
+ if _epoch_seconds(plan_status.get("plan_end_unix"))
132
+ else None,
133
+ "has_overage_balance": overage_balance_micros is not None,
134
+ }
135
+ return CapacitySnapshot(
136
+ provider_id=provider_id,
137
+ status=_status_from_windows(windows),
138
+ confidence=Confidence.OFFICIAL,
139
+ windows=windows,
140
+ credits_remaining=credits_remaining,
141
+ raw={key: value for key, value in raw.items() if value is not None},
142
+ )
143
+
144
+
145
+ def _encode_devin_plan_status_request(auth_token: str) -> bytes:
146
+ data = bytearray()
147
+ _proto_append_key(data, 1, 2)
148
+ _proto_append_bytes(data, auth_token.encode("utf-8"))
149
+ _proto_append_key(data, 2, 0)
150
+ _proto_append_varint(data, 1)
151
+ return bytes(data)
152
+
153
+
154
+ def decode_devin_plan_status_response(data: bytes) -> dict[str, Any]:
155
+ plan_status: dict[str, Any] | None = None
156
+ for field_number, wire_type, value in _proto_fields(data):
157
+ if field_number == 1 and wire_type == 2 and isinstance(value, bytes):
158
+ plan_status = _decode_devin_plan_status(value)
159
+ return {"plan_status": plan_status} if plan_status is not None else {}
160
+
161
+
162
+ def _decode_devin_plan_status(data: bytes) -> dict[str, Any]:
163
+ plan_status: dict[str, Any] = {}
164
+ for field_number, wire_type, value in _proto_fields(data):
165
+ if field_number == 1 and wire_type == 2 and isinstance(value, bytes):
166
+ plan_status["plan_info"] = _decode_devin_plan_info(value)
167
+ elif field_number == 2 and wire_type == 2 and isinstance(value, bytes):
168
+ plan_status["plan_start_unix"] = _decode_timestamp_seconds(value)
169
+ elif field_number == 3 and wire_type == 2 and isinstance(value, bytes):
170
+ plan_status["plan_end_unix"] = _decode_timestamp_seconds(value)
171
+ elif field_number == 14 and wire_type == 0:
172
+ plan_status["daily_remaining_percent"] = value
173
+ elif field_number == 15 and wire_type == 0:
174
+ plan_status["weekly_remaining_percent"] = value
175
+ elif field_number == 16 and wire_type == 0:
176
+ plan_status["overage_balance_micros"] = value
177
+ elif field_number == 17 and wire_type == 0:
178
+ plan_status["daily_reset_at_unix"] = value
179
+ elif field_number == 18 and wire_type == 0:
180
+ plan_status["weekly_reset_at_unix"] = value
181
+ return plan_status
182
+
183
+
184
+ def _decode_devin_plan_info(data: bytes) -> dict[str, Any]:
185
+ plan_info: dict[str, Any] = {}
186
+ for field_number, wire_type, value in _proto_fields(data):
187
+ if field_number == 1 and wire_type == 0:
188
+ plan_info["teams_tier"] = value
189
+ elif field_number == 2 and wire_type == 2 and isinstance(value, bytes):
190
+ plan_info["plan_name"] = value.decode("utf-8", errors="replace")
191
+ return plan_info
192
+
193
+
194
+ def _decode_timestamp_seconds(data: bytes) -> int | None:
195
+ for field_number, wire_type, value in _proto_fields(data):
196
+ if field_number == 1 and wire_type == 0:
197
+ return int(value)
198
+ return None
199
+
200
+
201
+ def _proto_fields(data: bytes) -> list[tuple[int, int, int | bytes]]:
202
+ fields: list[tuple[int, int, int | bytes]] = []
203
+ index = 0
204
+ while index < len(data):
205
+ key, index = _proto_read_varint(data, index)
206
+ field_number = key >> 3
207
+ wire_type = key & 0x07
208
+ if wire_type == 0:
209
+ value, index = _proto_read_varint(data, index)
210
+ fields.append((field_number, wire_type, value))
211
+ elif wire_type == 1:
212
+ if index + 8 > len(data):
213
+ raise ProbeError("Truncated fixed64 protobuf field.")
214
+ fields.append((field_number, wire_type, data[index : index + 8]))
215
+ index += 8
216
+ elif wire_type == 2:
217
+ length, index = _proto_read_varint(data, index)
218
+ end = index + length
219
+ if end > len(data):
220
+ raise ProbeError("Truncated length-delimited protobuf field.")
221
+ fields.append((field_number, wire_type, data[index:end]))
222
+ index = end
223
+ elif wire_type == 5:
224
+ if index + 4 > len(data):
225
+ raise ProbeError("Truncated fixed32 protobuf field.")
226
+ fields.append((field_number, wire_type, data[index : index + 4]))
227
+ index += 4
228
+ else:
229
+ raise ProbeError(f"Unsupported protobuf wire type {wire_type}.")
230
+ return fields
231
+
232
+
233
+ def _proto_read_varint(data: bytes, index: int) -> tuple[int, int]:
234
+ shift = 0
235
+ value = 0
236
+ while index < len(data):
237
+ byte = data[index]
238
+ index += 1
239
+ value |= (byte & 0x7F) << shift
240
+ if not byte & 0x80:
241
+ return value, index
242
+ shift += 7
243
+ if shift > 70:
244
+ raise ProbeError("Malformed protobuf varint.")
245
+ raise ProbeError("Truncated protobuf varint.")
246
+
247
+
248
+ def _proto_append_key(data: bytearray, field_number: int, wire_type: int) -> None:
249
+ _proto_append_varint(data, (field_number << 3) | wire_type)
250
+
251
+
252
+ def _proto_append_bytes(data: bytearray, value: bytes) -> None:
253
+ _proto_append_varint(data, len(value))
254
+ data.extend(value)
255
+
256
+
257
+ def _proto_append_varint(data: bytearray, value: int) -> None:
258
+ while True:
259
+ byte = value & 0x7F
260
+ value >>= 7
261
+ if value:
262
+ data.append(byte | 0x80)
263
+ else:
264
+ data.append(byte)
265
+ return
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from agentpool.models import CapacitySnapshot, Confidence, UsageStatus, UsageWindow, UsageWindowKind
6
+
7
+
8
+ def parse_usage_warning(provider_id: str, text: str) -> CapacitySnapshot | None:
9
+ if re.search(r"limit reached", text, re.I):
10
+ return CapacitySnapshot(
11
+ provider_id=provider_id,
12
+ status=UsageStatus.LIMIT_REACHED,
13
+ confidence=Confidence.PROVIDER_WARNING,
14
+ windows=[
15
+ UsageWindow(
16
+ name="unknown",
17
+ kind=UsageWindowKind.UNKNOWN,
18
+ status="limit_reached",
19
+ confidence=Confidence.PROVIDER_WARNING,
20
+ raw_text=text,
21
+ )
22
+ ],
23
+ warnings=[text.strip()],
24
+ )
25
+ if re.search(r"approaching .*limit", text, re.I):
26
+ return CapacitySnapshot(
27
+ provider_id=provider_id,
28
+ status=UsageStatus.NEAR_LIMIT,
29
+ confidence=Confidence.PROVIDER_WARNING,
30
+ windows=[
31
+ UsageWindow(
32
+ name="unknown",
33
+ kind=UsageWindowKind.UNKNOWN,
34
+ status="near_limit",
35
+ confidence=Confidence.PROVIDER_WARNING,
36
+ raw_text=text,
37
+ )
38
+ ],
39
+ warnings=[text.strip()],
40
+ )
41
+ return None
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from agentpool.usage._common import ProbeError, _extract_json_payload, unavailable, unknown
4
+ from agentpool.usage.ccusage import ccusage_usage_snapshot, detect_ccusage, parse_ccusage_blocks
5
+ from agentpool.usage.claude import claude_code_usage_snapshot
6
+ from agentpool.usage.codex import codex_cli_usage_snapshot, parse_codex_rate_limits
7
+ from agentpool.usage.codexbar import (
8
+ CODEXBAR_PROVIDER_MAP,
9
+ CODEXBAR_SAFE_SOURCE_MAP,
10
+ codexbar_usage_snapshot,
11
+ detect_codexbar,
12
+ parse_codexbar_usage,
13
+ )
14
+ from agentpool.usage.combine import combine_usage_snapshots
15
+ from agentpool.usage.copilot import copilot_cli_usage_snapshot, parse_copilot_usage_response
16
+ from agentpool.usage.devin import (
17
+ _encode_devin_plan_status_request,
18
+ _proto_append_bytes,
19
+ _proto_append_key,
20
+ _proto_append_varint,
21
+ decode_devin_plan_status_response,
22
+ devin_cli_usage_snapshot,
23
+ parse_devin_plan_status_response,
24
+ )
25
+
26
+ __all__ = [
27
+ "CODEXBAR_PROVIDER_MAP",
28
+ "CODEXBAR_SAFE_SOURCE_MAP",
29
+ "ProbeError",
30
+ "_encode_devin_plan_status_request",
31
+ "_extract_json_payload",
32
+ "_proto_append_bytes",
33
+ "_proto_append_key",
34
+ "_proto_append_varint",
35
+ "unavailable",
36
+ "unknown",
37
+ "ccusage_usage_snapshot",
38
+ "claude_code_usage_snapshot",
39
+ "codex_cli_usage_snapshot",
40
+ "codexbar_usage_snapshot",
41
+ "combine_usage_snapshots",
42
+ "copilot_cli_usage_snapshot",
43
+ "decode_devin_plan_status_response",
44
+ "detect_ccusage",
45
+ "detect_codexbar",
46
+ "devin_cli_usage_snapshot",
47
+ "parse_ccusage_blocks",
48
+ "parse_codex_rate_limits",
49
+ "parse_codexbar_usage",
50
+ "parse_copilot_usage_response",
51
+ "parse_devin_plan_status_response",
52
+ ]