devops-bot-sdk 1.4.29__tar.gz → 1.4.33__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.29 → devops_bot_sdk-1.4.33}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/client.py +53 -1
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/run_auto.py +191 -21
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/README.md +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/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.33"
|
|
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.33
|
|
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.33"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -36,7 +36,29 @@ from sdk.sse import _check_status, stream_with_reconnect
|
|
|
36
36
|
|
|
37
37
|
logger = logging.getLogger(__name__)
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
# ── Ticket status-change observers ────────────────────────────────────────
|
|
40
|
+
# Best-effort, synchronous hooks fired when the client changes a ticket's backend
|
|
41
|
+
# status (In Progress / Backlog / To Do). The CLI loop registers one to track
|
|
42
|
+
# which tickets the run has parked in Backlog, so `agentos-auto stop` can restore
|
|
43
|
+
# exactly those to To Do. Kept generic here — the client never touches CLI files.
|
|
44
|
+
_status_observers: list = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def register_status_observer(cb) -> None:
|
|
48
|
+
"""Register cb(task_id: str, status: str) — status ∈ {in_progress, backlog, to_do}."""
|
|
49
|
+
if cb not in _status_observers:
|
|
50
|
+
_status_observers.append(cb)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _notify_status(task_id: str, status: str) -> None:
|
|
54
|
+
for cb in list(_status_observers):
|
|
55
|
+
try:
|
|
56
|
+
cb(task_id, status)
|
|
57
|
+
except Exception: # noqa: BLE001 — observers must never break a request
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
SDK_VERSION = "1.4.33"
|
|
40
62
|
_POLL_INTERVAL = 3.0
|
|
41
63
|
_POLL_TIMEOUT = 600.0
|
|
42
64
|
_ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS execution time
|
|
@@ -686,11 +708,40 @@ class BackendClient:
|
|
|
686
708
|
task_id, resp.status_code, resp.text[:200])
|
|
687
709
|
return False
|
|
688
710
|
await self.log_activity(f"Task {task_id}: status → Backlog")
|
|
711
|
+
_notify_status(task_id, "backlog")
|
|
689
712
|
return True
|
|
690
713
|
except Exception as exc: # noqa: BLE001
|
|
691
714
|
logger.warning("client.move_to_backlog error task=%s err=%s", task_id, exc)
|
|
692
715
|
return False
|
|
693
716
|
|
|
717
|
+
async def move_to_todo(self, task_id: str) -> bool:
|
|
718
|
+
"""PUT {BE}/tasks-ml/{id} status="To Do" (best-effort).
|
|
719
|
+
|
|
720
|
+
Used by `agentos-auto stop` to return the in-flight ticket to the unstarted
|
|
721
|
+
queue so a later run re-picks it up from scratch (rather than leaving it
|
|
722
|
+
In Progress). Returns True on success; logs and returns False otherwise.
|
|
723
|
+
"""
|
|
724
|
+
from sdk.config import BE_DEFAULT_URL
|
|
725
|
+
|
|
726
|
+
url = f"{BE_DEFAULT_URL.rstrip('/')}/tasks-ml/{task_id}"
|
|
727
|
+
headers = {
|
|
728
|
+
k: v for k, v in self._headers.items() if k.lower() != "content-type"
|
|
729
|
+
}
|
|
730
|
+
headers.update({"accept": "*/*", "developer-name": "null", "product": ""})
|
|
731
|
+
try:
|
|
732
|
+
async with httpx.AsyncClient(timeout=20.0) as client:
|
|
733
|
+
resp = await client.put(url, files={"status": (None, "To Do")}, headers=headers)
|
|
734
|
+
if resp.status_code >= 400:
|
|
735
|
+
logger.warning("client.move_to_todo failed task=%s status=%s body=%s",
|
|
736
|
+
task_id, resp.status_code, resp.text[:200])
|
|
737
|
+
return False
|
|
738
|
+
await self.log_activity(f"Task {task_id}: status → To Do")
|
|
739
|
+
_notify_status(task_id, "to_do")
|
|
740
|
+
return True
|
|
741
|
+
except Exception as exc: # noqa: BLE001
|
|
742
|
+
logger.warning("client.move_to_todo error task=%s err=%s", task_id, exc)
|
|
743
|
+
return False
|
|
744
|
+
|
|
694
745
|
async def mark_in_progress(self, task_id: str) -> bool:
|
|
695
746
|
"""PUT {BE}/tasks-ml/{id} status=In Progress (best-effort).
|
|
696
747
|
|
|
@@ -712,6 +763,7 @@ class BackendClient:
|
|
|
712
763
|
task_id, resp.status_code, resp.text[:200])
|
|
713
764
|
return False
|
|
714
765
|
await self.log_activity(f"Task {task_id}: status → In Progress")
|
|
766
|
+
_notify_status(task_id, "in_progress")
|
|
715
767
|
return True
|
|
716
768
|
except Exception as exc: # noqa: BLE001
|
|
717
769
|
logger.warning("client.mark_in_progress error task=%s err=%s", task_id, exc)
|
|
@@ -28,11 +28,13 @@ Auto-update: on `run`/`resume`, the SDK checks for a newer version and upgrades
|
|
|
28
28
|
itself (then re-runs on the new code) BEFORE detaching, so non-technical users
|
|
29
29
|
never reinstall.
|
|
30
30
|
|
|
31
|
-
Stop
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
Stop model: `stop` is immediate — it kills the loop's whole process group (the
|
|
32
|
+
daemon and any child `claude`), then returns to "To Do" both the in-flight ticket
|
|
33
|
+
AND every ticket this run parked in Backlog (e.g. 15-min approval timeouts), so a
|
|
34
|
+
later run re-attempts them; human-parked Backlog tickets are left untouched. The
|
|
35
|
+
in-flight id is kept in ~/.agentos/auto.current and the run's Backlog set in
|
|
36
|
+
~/.agentos/auto.backlogged (maintained via a client status observer). `resume`
|
|
37
|
+
(or just re-running) starts fresh over whatever is still To Do.
|
|
36
38
|
"""
|
|
37
39
|
from __future__ import annotations
|
|
38
40
|
|
|
@@ -46,6 +48,13 @@ _AGENTOS_DIR = Path.home() / ".agentos"
|
|
|
46
48
|
_STOP_FILE = _AGENTOS_DIR / "auto.stop"
|
|
47
49
|
_PID_FILE = _AGENTOS_DIR / "auto.pid"
|
|
48
50
|
_LOG_FILE = _AGENTOS_DIR / "auto.log"
|
|
51
|
+
# Backend id (UUID) of the ticket currently being processed. `stop` reads it to
|
|
52
|
+
# return the in-flight ticket to "To Do" before killing the loop.
|
|
53
|
+
_CURRENT_FILE = _AGENTOS_DIR / "auto.current"
|
|
54
|
+
# Backend ids the CURRENT run has moved to Backlog (e.g. 15-min approval
|
|
55
|
+
# timeouts). `stop` restores exactly these to "To Do" too — not human-parked
|
|
56
|
+
# Backlog tickets. Maintained live via a client status observer.
|
|
57
|
+
_BACKLOGGED_FILE = _AGENTOS_DIR / "auto.backlogged"
|
|
49
58
|
|
|
50
59
|
|
|
51
60
|
def _stop_requested() -> bool:
|
|
@@ -64,6 +73,64 @@ def _clear_stop() -> None:
|
|
|
64
73
|
pass
|
|
65
74
|
|
|
66
75
|
|
|
76
|
+
def _write_current(task_id: str) -> None:
|
|
77
|
+
try:
|
|
78
|
+
_AGENTOS_DIR.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
_CURRENT_FILE.write_text(task_id)
|
|
80
|
+
except Exception: # noqa: BLE001
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _read_current() -> str | None:
|
|
85
|
+
try:
|
|
86
|
+
val = _CURRENT_FILE.read_text().strip()
|
|
87
|
+
return val or None
|
|
88
|
+
except FileNotFoundError:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _clear_current() -> None:
|
|
93
|
+
try:
|
|
94
|
+
_CURRENT_FILE.unlink()
|
|
95
|
+
except FileNotFoundError:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _read_backlogged() -> set[str]:
|
|
100
|
+
try:
|
|
101
|
+
return set(json.loads(_BACKLOGGED_FILE.read_text()))
|
|
102
|
+
except (FileNotFoundError, ValueError, TypeError):
|
|
103
|
+
return set()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _write_backlogged(ids: set[str]) -> None:
|
|
107
|
+
try:
|
|
108
|
+
_AGENTOS_DIR.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
_BACKLOGGED_FILE.write_text(json.dumps(sorted(ids)))
|
|
110
|
+
except Exception: # noqa: BLE001
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _clear_backlogged() -> None:
|
|
115
|
+
try:
|
|
116
|
+
_BACKLOGGED_FILE.unlink()
|
|
117
|
+
except FileNotFoundError:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _track_status(task_id: str, status: str) -> None:
|
|
122
|
+
"""Client status observer (runs in the daemon): keep the set of tickets THIS
|
|
123
|
+
run has parked in Backlog current, so `stop` can restore them to To Do."""
|
|
124
|
+
if not task_id:
|
|
125
|
+
return
|
|
126
|
+
ids = _read_backlogged()
|
|
127
|
+
if status == "backlog":
|
|
128
|
+
ids.add(task_id)
|
|
129
|
+
else: # in_progress / to_do → no longer backlogged by us
|
|
130
|
+
ids.discard(task_id)
|
|
131
|
+
_write_backlogged(ids)
|
|
132
|
+
|
|
133
|
+
|
|
67
134
|
# ── Background-daemon support ─────────────────────────────────────────────
|
|
68
135
|
# `agentos-auto` must keep running after the terminal is closed and stop ONLY on
|
|
69
136
|
# `agentos-auto stop`. We fully detach from the controlling terminal (double-fork
|
|
@@ -157,9 +224,24 @@ def _parse_workspace_root(argv: list[str]) -> str | None:
|
|
|
157
224
|
return None
|
|
158
225
|
|
|
159
226
|
|
|
227
|
+
_current_task: str | None = None
|
|
228
|
+
|
|
229
|
+
|
|
160
230
|
async def _send(env: dict) -> None:
|
|
161
231
|
etype = env.get("type", "")
|
|
162
232
|
data = env.get("data", {}) or {}
|
|
233
|
+
|
|
234
|
+
# Track the in-flight ticket (backend UUID) so `stop` can return it to To Do.
|
|
235
|
+
# The handler tags per-task envelopes with jira_task_id / task_id (both = the
|
|
236
|
+
# backend UUID here). Persist only on change to avoid churning the file.
|
|
237
|
+
global _current_task
|
|
238
|
+
tid = data.get("jira_task_id") or data.get("task_id")
|
|
239
|
+
if tid and tid != _current_task:
|
|
240
|
+
_current_task = tid
|
|
241
|
+
_write_current(tid)
|
|
242
|
+
if etype == "done":
|
|
243
|
+
_clear_current()
|
|
244
|
+
|
|
163
245
|
if etype == "pipeline_started":
|
|
164
246
|
print(f"\n▶ orchestrate-auto (local) — {data.get('task_count', 0)} task(s)\n")
|
|
165
247
|
elif etype == "step_update":
|
|
@@ -183,7 +265,7 @@ async def _send(env: dict) -> None:
|
|
|
183
265
|
|
|
184
266
|
def _run_loop(workspace_root: str | None) -> None:
|
|
185
267
|
from sdk import local_exec
|
|
186
|
-
from sdk.client import BackendClient
|
|
268
|
+
from sdk.client import BackendClient, register_status_observer
|
|
187
269
|
from sdk.ipc import handlers
|
|
188
270
|
|
|
189
271
|
if not local_exec.is_available():
|
|
@@ -202,6 +284,8 @@ def _run_loop(workspace_root: str | None) -> None:
|
|
|
202
284
|
payload["workspace_root"] = workspace_root
|
|
203
285
|
|
|
204
286
|
_clear_stop() # a fresh run/resume clears any stale stop flag
|
|
287
|
+
_clear_backlogged() # session-scoped: only track Backlog moves from THIS run
|
|
288
|
+
register_status_observer(_track_status) # keep the run's Backlog set current
|
|
205
289
|
try:
|
|
206
290
|
asyncio.run(
|
|
207
291
|
handlers.handle_orchestrate_auto_local_run(
|
|
@@ -213,12 +297,15 @@ def _run_loop(workspace_root: str | None) -> None:
|
|
|
213
297
|
print("\n⏸ interrupted — stop flag set. run `agentos-auto resume` to continue.")
|
|
214
298
|
finally:
|
|
215
299
|
# Whether the loop finished, paused, or crashed, drop our PID file so
|
|
216
|
-
# `status` doesn't report a dead daemon as running
|
|
300
|
+
# `status` doesn't report a dead daemon as running, and clear the in-flight
|
|
301
|
+
# + backlog markers (a clean finish leaves nothing for `stop` to restore).
|
|
217
302
|
if _read_pid() == os.getpid():
|
|
218
303
|
try:
|
|
219
304
|
_PID_FILE.unlink()
|
|
220
305
|
except FileNotFoundError:
|
|
221
306
|
pass
|
|
307
|
+
_clear_current()
|
|
308
|
+
_clear_backlogged()
|
|
222
309
|
|
|
223
310
|
|
|
224
311
|
def main() -> None:
|
|
@@ -226,20 +313,98 @@ def main() -> None:
|
|
|
226
313
|
cmd = argv[0] if argv and not argv[0].startswith("-") else "run"
|
|
227
314
|
|
|
228
315
|
if cmd == "update":
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
316
|
+
# Update-only: upgrade the installed SDK package to the latest version and
|
|
317
|
+
# report before → after. Does NOT start, stop, or touch the running loop —
|
|
318
|
+
# a running daemon keeps its current code until it is restarted. Honours
|
|
319
|
+
# AGENTOS_NO_UPDATE=1 (auto_update no-ops).
|
|
320
|
+
from sdk.updater import auto_update, current_version
|
|
321
|
+
before = current_version()
|
|
322
|
+
print("checking for SDK updates …")
|
|
323
|
+
auto_update(quiet=True)
|
|
324
|
+
after = current_version()
|
|
325
|
+
if after != before:
|
|
326
|
+
print(f"✓ updated {before} → {after}")
|
|
327
|
+
pid = _daemon_running()
|
|
328
|
+
if pid:
|
|
329
|
+
print("note: a background loop is running the OLD version — "
|
|
330
|
+
"`agentos-auto stop` then `agentos-auto` to run the new one.")
|
|
331
|
+
else:
|
|
332
|
+
print(f"already up to date (v{after})")
|
|
233
333
|
return
|
|
234
334
|
|
|
235
335
|
if cmd == "stop":
|
|
336
|
+
# Hard stop: halt the current task immediately, return it to To Do so it
|
|
337
|
+
# restarts cleanly next run, and kill the whole loop (not a cooperative
|
|
338
|
+
# pause after the current task).
|
|
236
339
|
_set_stop()
|
|
237
340
|
pid = _daemon_running()
|
|
341
|
+
cur = _read_current()
|
|
342
|
+
backlogged = _read_backlogged() # tickets THIS run parked in Backlog
|
|
343
|
+
|
|
344
|
+
# 1. Kill the loop's ENTIRE process group first (daemon + any child
|
|
345
|
+
# `claude` it spawned) so nothing keeps mutating the ticket. The daemon
|
|
346
|
+
# is a session leader (setsid), so its PGID == its PID.
|
|
238
347
|
if pid:
|
|
239
|
-
|
|
240
|
-
|
|
348
|
+
import signal
|
|
349
|
+
import time as _t
|
|
350
|
+
# Kill the whole process group ONLY when pid is its own group leader
|
|
351
|
+
# (the daemon, via setsid) — that also reaps the child `claude`. For a
|
|
352
|
+
# foreground run (shares the shell's group) fall back to a single kill
|
|
353
|
+
# so we don't take down the user's terminal.
|
|
354
|
+
try:
|
|
355
|
+
is_group_leader = os.getpgid(pid) == pid
|
|
356
|
+
except ProcessLookupError:
|
|
357
|
+
is_group_leader = False
|
|
358
|
+
killed = False
|
|
359
|
+
for sig in (signal.SIGTERM, signal.SIGKILL):
|
|
360
|
+
try:
|
|
361
|
+
if is_group_leader:
|
|
362
|
+
os.killpg(pid, sig)
|
|
363
|
+
else:
|
|
364
|
+
os.kill(pid, sig)
|
|
365
|
+
except ProcessLookupError:
|
|
366
|
+
killed = True
|
|
367
|
+
break
|
|
368
|
+
except Exception: # noqa: BLE001
|
|
369
|
+
pass
|
|
370
|
+
_t.sleep(1.5)
|
|
371
|
+
if not _pid_alive(pid):
|
|
372
|
+
killed = True
|
|
373
|
+
break
|
|
374
|
+
print(f"stopped background loop (pid {pid})" if killed
|
|
375
|
+
else f"sent stop signals to pid {pid}")
|
|
241
376
|
else:
|
|
242
|
-
print("
|
|
377
|
+
print("no background loop running — stop flag set for any foreground run.")
|
|
378
|
+
|
|
379
|
+
# 2. Now that the loop is dead, move the in-flight ticket AND every ticket
|
|
380
|
+
# this run parked in Backlog (e.g. 15-min approval timeouts) back to
|
|
381
|
+
# To Do, so a later run re-attempts them. Human-parked Backlog tickets
|
|
382
|
+
# are untouched — only ids this run recorded are restored.
|
|
383
|
+
to_restore = [t for t in ({cur} | backlogged) if t]
|
|
384
|
+
if to_restore:
|
|
385
|
+
async def _restore(ids: list[str]) -> int:
|
|
386
|
+
from sdk.client import BackendClient
|
|
387
|
+
client = BackendClient.from_config()
|
|
388
|
+
done = 0
|
|
389
|
+
for t in ids:
|
|
390
|
+
try:
|
|
391
|
+
if await client.move_to_todo(t):
|
|
392
|
+
done += 1
|
|
393
|
+
except Exception: # noqa: BLE001
|
|
394
|
+
pass
|
|
395
|
+
return done
|
|
396
|
+
try:
|
|
397
|
+
n = asyncio.run(_restore(to_restore))
|
|
398
|
+
print(f"restored {n}/{len(to_restore)} ticket(s) → To Do")
|
|
399
|
+
except Exception as exc: # noqa: BLE001
|
|
400
|
+
print(f"warning: could not restore tickets to To Do ({exc})")
|
|
401
|
+
|
|
402
|
+
# 3. Clean up state files.
|
|
403
|
+
for f in (_PID_FILE, _CURRENT_FILE, _BACKLOGGED_FILE):
|
|
404
|
+
try:
|
|
405
|
+
f.unlink()
|
|
406
|
+
except FileNotFoundError:
|
|
407
|
+
pass
|
|
243
408
|
return
|
|
244
409
|
|
|
245
410
|
if cmd == "status":
|
|
@@ -247,12 +412,24 @@ def main() -> None:
|
|
|
247
412
|
if pid:
|
|
248
413
|
print(f"background loop: running (pid {pid})")
|
|
249
414
|
print(f"logs: {_LOG_FILE}")
|
|
415
|
+
cur = _read_current()
|
|
416
|
+
if cur:
|
|
417
|
+
print(f"current ticket: {cur}")
|
|
250
418
|
else:
|
|
251
419
|
print("background loop: not running")
|
|
252
420
|
print("stop pending: yes" if _stop_requested() else "stop pending: no")
|
|
253
421
|
return
|
|
254
422
|
|
|
255
423
|
if cmd in ("run", "resume"):
|
|
424
|
+
# Self-update FIRST (before the already-running check) so the installed
|
|
425
|
+
# package is refreshed even when a daemon is already running — otherwise a
|
|
426
|
+
# long-lived old daemon would pin `stop`/`status` to a stale version. Runs
|
|
427
|
+
# in the FOREGROUND so the user sees update output; re-execs on the new
|
|
428
|
+
# code. Skipped with --no-update or AGENTOS_NO_UPDATE=1.
|
|
429
|
+
if "--no-update" not in argv:
|
|
430
|
+
from sdk.updater import auto_update_and_reexec
|
|
431
|
+
auto_update_and_reexec("sdk.run_auto", sys.argv[1:])
|
|
432
|
+
|
|
256
433
|
# Refuse to start a second loop — one background daemon owns the To Do queue.
|
|
257
434
|
pid = _daemon_running()
|
|
258
435
|
if pid:
|
|
@@ -261,13 +438,6 @@ def main() -> None:
|
|
|
261
438
|
print(" stop: agentos-auto stop")
|
|
262
439
|
return
|
|
263
440
|
|
|
264
|
-
# Keep non-technical users current: self-update, then re-run on the new
|
|
265
|
-
# code. Runs in the FOREGROUND (before detaching) so the user sees update
|
|
266
|
-
# output. Skipped with --no-update or AGENTOS_NO_UPDATE=1.
|
|
267
|
-
if "--no-update" not in argv:
|
|
268
|
-
from sdk.updater import auto_update_and_reexec
|
|
269
|
-
auto_update_and_reexec("sdk.run_auto", sys.argv[1:])
|
|
270
|
-
|
|
271
441
|
workspace_root = _parse_workspace_root(argv)
|
|
272
442
|
|
|
273
443
|
# Background by default: detach so closing the terminal (SIGHUP) can't kill
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.29 → devops_bot_sdk-1.4.33}/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
|
|
File without changes
|