devops-bot-sdk 1.4.23__tar.gz → 1.4.24__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.
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/ipc/handlers.py +14 -4
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/local_exec.py +49 -3
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/README.md +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devops-bot-sdk"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.24"
|
|
8
8
|
description = "DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "LicenseRef-Proprietary"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.24
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -30,7 +30,7 @@ Rules:
|
|
|
30
30
|
- All data egress through submit_webhook only
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "1.4.
|
|
33
|
+
__version__ = "1.4.24"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -36,7 +36,7 @@ from sdk.sse import _check_status, stream_with_reconnect
|
|
|
36
36
|
|
|
37
37
|
logger = logging.getLogger(__name__)
|
|
38
38
|
|
|
39
|
-
SDK_VERSION = "1.4.
|
|
39
|
+
SDK_VERSION = "1.4.24"
|
|
40
40
|
_POLL_INTERVAL = 3.0
|
|
41
41
|
_POLL_TIMEOUT = 600.0
|
|
42
42
|
_ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS execution time
|
|
@@ -1871,13 +1871,23 @@ async def handle_orchestrate_auto_local_run(
|
|
|
1871
1871
|
).model_dump())
|
|
1872
1872
|
return
|
|
1873
1873
|
continue # re-run this ticket from phase 1
|
|
1874
|
-
# Out of retries
|
|
1874
|
+
# Out of retries. The usage/session limit is ACCOUNT-WIDE, so
|
|
1875
|
+
# every remaining ticket would hit the same wall and burn its
|
|
1876
|
+
# own retry budget (up to 6h each). Pause the whole run instead
|
|
1877
|
+
# of marching on — this ticket and the rest stay in To Do/
|
|
1878
|
+
# Backlog, and `agentos-auto resume` picks them up after reset.
|
|
1875
1879
|
await send(Envelope(
|
|
1876
1880
|
type="step_update", thread_id=thread_id,
|
|
1877
|
-
data={"current_step": f"{jira_key}: usage limit — max retries reached
|
|
1878
|
-
f"
|
|
1881
|
+
data={"current_step": f"{jira_key}: usage limit — max retries reached; "
|
|
1882
|
+
f"pausing run (resume after the window resets)"},
|
|
1879
1883
|
).model_dump())
|
|
1880
|
-
|
|
1884
|
+
await send(Envelope(
|
|
1885
|
+
type=Envelope.TYPE_DONE, thread_id=thread_id,
|
|
1886
|
+
data={"mode": "orchestrate-auto-local", "status": "paused",
|
|
1887
|
+
"reason": "usage_limit", "processed": processed,
|
|
1888
|
+
"task_count": total},
|
|
1889
|
+
).model_dump())
|
|
1890
|
+
return
|
|
1881
1891
|
|
|
1882
1892
|
processed += 1
|
|
1883
1893
|
break
|
|
@@ -23,7 +23,7 @@ import os
|
|
|
23
23
|
import re
|
|
24
24
|
import shutil
|
|
25
25
|
import sys
|
|
26
|
-
from datetime import datetime
|
|
26
|
+
from datetime import datetime, timedelta
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
from typing import Any, Awaitable, Callable
|
|
29
29
|
|
|
@@ -86,9 +86,21 @@ _LIMIT_MARKERS = (
|
|
|
86
86
|
"usage limit reached", "usage limit", "rate limit", "rate_limit",
|
|
87
87
|
"too many requests", "limit will reset", "limit reached", "overloaded_error",
|
|
88
88
|
"resets at", "429",
|
|
89
|
+
# Newer Claude Code CLI wording: "You've hit your session limit · resets 10:20pm
|
|
90
|
+
# (Asia/Karachi)" and "You've hit your usage limit". Without these the message
|
|
91
|
+
# fell through to the generic-failure path and the auto-loop never waited for
|
|
92
|
+
# the window to reset — every following ticket hit the same limit and cascaded
|
|
93
|
+
# to Backlog.
|
|
94
|
+
"session limit", "you've hit your", "youve hit your",
|
|
89
95
|
)
|
|
90
96
|
# A unix-seconds reset timestamp claude sometimes appends (e.g. "...reached|1719500400").
|
|
91
97
|
_RESET_EPOCH_RE = re.compile(r"\b(1[6-9]\d{8}|20\d{8})\b")
|
|
98
|
+
# A wall-clock reset the CLI prints instead, e.g. "resets 10:20pm (Asia/Karachi)"
|
|
99
|
+
# or "resets at 10pm". Captures hour, optional minutes, am/pm, optional IANA tz.
|
|
100
|
+
_RESET_CLOCK_RE = re.compile(
|
|
101
|
+
r"reset[s]?\s+(?:at\s+)?(\d{1,2})(?::(\d{2}))?\s*([ap]m)\b(?:\s*\(([^)]+)\))?",
|
|
102
|
+
re.IGNORECASE,
|
|
103
|
+
)
|
|
92
104
|
# Fallback wait when the limit message carries no reset time (env-tunable).
|
|
93
105
|
_LIMIT_DEFAULT_WAIT_S = _f("AGENTOS_LIMIT_DEFAULT_WAIT_S", 60 * 60.0) # 1 h
|
|
94
106
|
|
|
@@ -99,7 +111,15 @@ def _is_limit(text: str | None) -> bool:
|
|
|
99
111
|
|
|
100
112
|
|
|
101
113
|
def _parse_reset_epoch(text: str | None) -> int | None:
|
|
102
|
-
"""A unix-seconds reset time embedded in a limit message, if present.
|
|
114
|
+
"""A unix-seconds reset time embedded in a limit message, if present.
|
|
115
|
+
|
|
116
|
+
Handles two shapes the CLI uses:
|
|
117
|
+
1. A literal unix timestamp (e.g. "...reached|1719500400").
|
|
118
|
+
2. A wall-clock time (e.g. "resets 10:20pm (Asia/Karachi)") — resolved to
|
|
119
|
+
the next future occurrence of that time in the named tz (local tz when
|
|
120
|
+
none/unknown). Returns None when neither is present, so callers fall
|
|
121
|
+
back to the default wait.
|
|
122
|
+
"""
|
|
103
123
|
if not text:
|
|
104
124
|
return None
|
|
105
125
|
m = _RESET_EPOCH_RE.search(text)
|
|
@@ -108,7 +128,33 @@ def _parse_reset_epoch(text: str | None) -> int | None:
|
|
|
108
128
|
return int(m.group(1))
|
|
109
129
|
except ValueError:
|
|
110
130
|
return None
|
|
111
|
-
return
|
|
131
|
+
return _parse_reset_clock(text)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _parse_reset_clock(text: str) -> int | None:
|
|
135
|
+
"""Resolve a 'resets 10:20pm (Asia/Karachi)'-style reset to a unix epoch."""
|
|
136
|
+
m = _RESET_CLOCK_RE.search(text)
|
|
137
|
+
if not m:
|
|
138
|
+
return None
|
|
139
|
+
try:
|
|
140
|
+
hour = int(m.group(1)) % 12 # 12am → 0, 12pm → 0 (+12 below)
|
|
141
|
+
minute = int(m.group(2) or 0)
|
|
142
|
+
if m.group(3).lower() == "pm":
|
|
143
|
+
hour += 12
|
|
144
|
+
tz = None
|
|
145
|
+
if m.group(4):
|
|
146
|
+
try:
|
|
147
|
+
from zoneinfo import ZoneInfo
|
|
148
|
+
tz = ZoneInfo(m.group(4).strip())
|
|
149
|
+
except Exception: # noqa: BLE001 — unknown tz → local time
|
|
150
|
+
tz = None
|
|
151
|
+
now = datetime.now(tz)
|
|
152
|
+
target = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
|
|
153
|
+
if target <= now: # already passed today → next day
|
|
154
|
+
target += timedelta(days=1)
|
|
155
|
+
return int(target.timestamp())
|
|
156
|
+
except Exception: # noqa: BLE001 — never let reset parsing break limit handling
|
|
157
|
+
return None
|
|
112
158
|
|
|
113
159
|
|
|
114
160
|
# ── Per-run claude log file (on the user's machine, local-timezone stamps) ──
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.23 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/dependency_links.txt
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|