devops-bot-sdk 1.4.22__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.22 → devops_bot_sdk-1.4.24}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/ipc/handlers.py +54 -5
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/local_exec.py +85 -3
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/README.md +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.22 → devops_bot_sdk-1.4.24}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.22 → 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
|
|
@@ -296,8 +296,14 @@ async def _run_local_pipeline(
|
|
|
296
296
|
body["user_id"] = derived
|
|
297
297
|
|
|
298
298
|
# 1. Kick off the background run (server plans, gates — does NOT execute).
|
|
299
|
+
# The kickoff sends background=True and should return a session_id quickly, but
|
|
300
|
+
# under load the backend can take well over 30s to ack — which previously raised
|
|
301
|
+
# a (blank-message) ReadTimeout that sent the whole ticket to Backlog. Connect
|
|
302
|
+
# fast, but allow a generous read window so a slow-but-working ack isn't killed.
|
|
299
303
|
import httpx
|
|
300
|
-
|
|
304
|
+
_kick_read_s = float(os.getenv("AGENTOS_ORCHESTRATE_KICKOFF_TIMEOUT_S", "") or 120.0)
|
|
305
|
+
_kick_timeout = httpx.Timeout(_kick_read_s, connect=15.0)
|
|
306
|
+
async with httpx.AsyncClient(timeout=_kick_timeout) as http:
|
|
301
307
|
resp = await http.post(client._url("/api/v1/orchestrate"), json=body, headers=client._headers)
|
|
302
308
|
if resp.status_code >= 400:
|
|
303
309
|
await send(_error_envelope(thread_id, RuntimeError(f"orchestrate failed: {resp.status_code} {resp.text[:200]}")).model_dump())
|
|
@@ -560,6 +566,36 @@ async def _code_one_repo(
|
|
|
560
566
|
)
|
|
561
567
|
session_id = result.get("session_id") or session_id
|
|
562
568
|
|
|
569
|
+
# 4b. output-token-cap recovery. A single assistant turn that would exceed the
|
|
570
|
+
# CLI's per-response output cap aborts the run ("response exceeded the N output
|
|
571
|
+
# token maximum"). Resume the session with a doubled cap and an instruction to
|
|
572
|
+
# emit the work in smaller chunks, so a large dump finishes across turns instead
|
|
573
|
+
# of failing the ticket.
|
|
574
|
+
cap_resumes = 0
|
|
575
|
+
next_cap = local_exec._MAX_OUTPUT_TOKENS
|
|
576
|
+
while (not result.get("ok")) and result.get("output_cap_exceeded") \
|
|
577
|
+
and session_id and cap_resumes < _MAX_OUTPUT_CAP_RESUMES:
|
|
578
|
+
cap_resumes += 1
|
|
579
|
+
next_cap = min(next_cap * 2, 128000) # CLI/model output ceiling
|
|
580
|
+
await send(Envelope(
|
|
581
|
+
type="step_update", thread_id=thread_id,
|
|
582
|
+
data={"status": "running",
|
|
583
|
+
"current_step": f"[{name}] Response exceeded output-token cap — "
|
|
584
|
+
f"resuming with cap {next_cap} and chunked output "
|
|
585
|
+
f"(attempt {cap_resumes}/{_MAX_OUTPUT_CAP_RESUMES})"},
|
|
586
|
+
).model_dump())
|
|
587
|
+
result = await local_exec.run_claude_local(
|
|
588
|
+
"Your previous response was cut off because it exceeded the output-token "
|
|
589
|
+
"limit for a single turn. Continue from where you left off and finish the "
|
|
590
|
+
"task. Keep each response within the limit: write results incrementally to "
|
|
591
|
+
"files in smaller steps rather than emitting one very large response, and "
|
|
592
|
+
"avoid dumping large content back as chat text.",
|
|
593
|
+
repo_path, on_event=_on_event,
|
|
594
|
+
github_token=gh_token, resume_session_id=session_id,
|
|
595
|
+
approval_dir=approval_dir, max_output_tokens=next_cap,
|
|
596
|
+
)
|
|
597
|
+
session_id = result.get("session_id") or session_id
|
|
598
|
+
|
|
563
599
|
# 5. mid-run crucial-decision gate
|
|
564
600
|
if agent_mode and session_id:
|
|
565
601
|
from sdk import crucial
|
|
@@ -1529,6 +1565,9 @@ _FIRST_GATE_TIMEOUT_S = 900.0
|
|
|
1529
1565
|
_APPROVAL_POLL_TIMEOUT_S = 1200.0
|
|
1530
1566
|
# Max times a single ticket's session is resumed after a context overflow.
|
|
1531
1567
|
_MAX_CONTEXT_RESUMES = 3
|
|
1568
|
+
# Max times a ticket's session is resumed after hitting the per-response output-token
|
|
1569
|
+
# cap, each time with a doubled cap, to let a large turn finish across chunks.
|
|
1570
|
+
_MAX_OUTPUT_CAP_RESUMES = 3
|
|
1532
1571
|
# Max mid-run crucial-decision approval gates per ticket (safety bound).
|
|
1533
1572
|
_MAX_CRUCIAL_GATES = 10
|
|
1534
1573
|
# Max times a ticket is retried after hitting the Claude usage limit.
|
|
@@ -1832,13 +1871,23 @@ async def handle_orchestrate_auto_local_run(
|
|
|
1832
1871
|
).model_dump())
|
|
1833
1872
|
return
|
|
1834
1873
|
continue # re-run this ticket from phase 1
|
|
1835
|
-
# 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.
|
|
1836
1879
|
await send(Envelope(
|
|
1837
1880
|
type="step_update", thread_id=thread_id,
|
|
1838
|
-
data={"current_step": f"{jira_key}: usage limit — max retries reached
|
|
1839
|
-
f"
|
|
1881
|
+
data={"current_step": f"{jira_key}: usage limit — max retries reached; "
|
|
1882
|
+
f"pausing run (resume after the window resets)"},
|
|
1840
1883
|
).model_dump())
|
|
1841
|
-
|
|
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
|
|
1842
1891
|
|
|
1843
1892
|
processed += 1
|
|
1844
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
|
|
|
@@ -62,6 +62,23 @@ _CONTEXT_MARKERS = (
|
|
|
62
62
|
"token limit", "prompt is too long", "exceeds the maximum", "context_length_exceeded",
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
+
# Markers that indicate the CLI's per-response OUTPUT-token cap was hit — distinct
|
|
66
|
+
# from a context overflow (input side) and a usage/rate limit (account side). The
|
|
67
|
+
# CLI aborts a single assistant turn that would exceed CLAUDE_CODE_MAX_OUTPUT_TOKENS
|
|
68
|
+
# (default 32000). We raise that cap below and, if it's still hit, resume the
|
|
69
|
+
# session to finish the turn in smaller chunks.
|
|
70
|
+
_OUTPUT_CAP_MARKERS = (
|
|
71
|
+
"output token maximum", "max_output_tokens", "claude_code_max_output_tokens",
|
|
72
|
+
"max output tokens", "output_tokens exceed",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Per-response output-token cap handed to the CLI via CLAUDE_CODE_MAX_OUTPUT_TOKENS.
|
|
76
|
+
# The CLI default is 32000, which a large single dump (e.g. extracting a whole
|
|
77
|
+
# taxonomy from a spreadsheet) can blow past, failing the whole ticket. 64000 is a
|
|
78
|
+
# safe headroom for the Opus/Sonnet families. Env-tunable; a value the user already
|
|
79
|
+
# exported in the environment is respected and never overridden.
|
|
80
|
+
_MAX_OUTPUT_TOKENS = int(_f("AGENTOS_MAX_OUTPUT_TOKENS", 64000.0))
|
|
81
|
+
|
|
65
82
|
# Markers that indicate the Claude USAGE/RATE limit (the N-hour window) was hit —
|
|
66
83
|
# distinct from a context overflow. The run didn't fail on the task; the account
|
|
67
84
|
# is throttled until the window resets.
|
|
@@ -69,9 +86,21 @@ _LIMIT_MARKERS = (
|
|
|
69
86
|
"usage limit reached", "usage limit", "rate limit", "rate_limit",
|
|
70
87
|
"too many requests", "limit will reset", "limit reached", "overloaded_error",
|
|
71
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",
|
|
72
95
|
)
|
|
73
96
|
# A unix-seconds reset timestamp claude sometimes appends (e.g. "...reached|1719500400").
|
|
74
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
|
+
)
|
|
75
104
|
# Fallback wait when the limit message carries no reset time (env-tunable).
|
|
76
105
|
_LIMIT_DEFAULT_WAIT_S = _f("AGENTOS_LIMIT_DEFAULT_WAIT_S", 60 * 60.0) # 1 h
|
|
77
106
|
|
|
@@ -82,7 +111,15 @@ def _is_limit(text: str | None) -> bool:
|
|
|
82
111
|
|
|
83
112
|
|
|
84
113
|
def _parse_reset_epoch(text: str | None) -> int | None:
|
|
85
|
-
"""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
|
+
"""
|
|
86
123
|
if not text:
|
|
87
124
|
return None
|
|
88
125
|
m = _RESET_EPOCH_RE.search(text)
|
|
@@ -91,7 +128,33 @@ def _parse_reset_epoch(text: str | None) -> int | None:
|
|
|
91
128
|
return int(m.group(1))
|
|
92
129
|
except ValueError:
|
|
93
130
|
return None
|
|
94
|
-
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
|
|
95
158
|
|
|
96
159
|
|
|
97
160
|
# ── Per-run claude log file (on the user's machine, local-timezone stamps) ──
|
|
@@ -162,6 +225,12 @@ def _ctx_exceeded(text: str | None) -> bool:
|
|
|
162
225
|
low = (text or "").lower()
|
|
163
226
|
return any(m in low for m in _CONTEXT_MARKERS)
|
|
164
227
|
|
|
228
|
+
|
|
229
|
+
def _output_cap_exceeded(text: str | None) -> bool:
|
|
230
|
+
"""True when the CLI aborted because the response hit the per-turn output cap."""
|
|
231
|
+
low = (text or "").lower()
|
|
232
|
+
return any(m in low for m in _OUTPUT_CAP_MARKERS)
|
|
233
|
+
|
|
165
234
|
EventHandler = Callable[[dict], Awaitable[None]]
|
|
166
235
|
|
|
167
236
|
|
|
@@ -186,6 +255,7 @@ async def run_claude_local(
|
|
|
186
255
|
resume_session_id: str | None = None,
|
|
187
256
|
approval_dir: str | None = None,
|
|
188
257
|
idle_timeout: float = _IDLE_TIMEOUT_S,
|
|
258
|
+
max_output_tokens: int | None = None,
|
|
189
259
|
) -> dict[str, Any]:
|
|
190
260
|
"""Run the local `claude` CLI non-interactively against ``project_path``.
|
|
191
261
|
|
|
@@ -258,6 +328,16 @@ async def run_claude_local(
|
|
|
258
328
|
# `git clone https://github.com/owner/repo` authenticates — without touching
|
|
259
329
|
# the user's global git config. Tokens stay in the subprocess env only.
|
|
260
330
|
env = dict(os.environ)
|
|
331
|
+
|
|
332
|
+
# Raise the CLI's per-response output-token cap. Without this the CLI errors
|
|
333
|
+
# out ("Claude's response exceeded the 32000 output token maximum") the moment
|
|
334
|
+
# a single assistant turn would exceed 32000 tokens — a large dump (extracting
|
|
335
|
+
# a whole taxonomy, generating many files at once) fails the entire ticket.
|
|
336
|
+
# A value the operator already exported wins; the `max_output_tokens` override
|
|
337
|
+
# lets a retry escalate the cap after a cap-exceeded run (see handlers.py).
|
|
338
|
+
if "CLAUDE_CODE_MAX_OUTPUT_TOKENS" not in env or max_output_tokens:
|
|
339
|
+
env["CLAUDE_CODE_MAX_OUTPUT_TOKENS"] = str(max_output_tokens or _MAX_OUTPUT_TOKENS)
|
|
340
|
+
|
|
261
341
|
if github_token:
|
|
262
342
|
env["GH_TOKEN"] = github_token
|
|
263
343
|
env["GITHUB_TOKEN"] = github_token
|
|
@@ -419,6 +499,7 @@ async def run_claude_local(
|
|
|
419
499
|
"cost_usd": 0.0, "files_changed": files_changed,
|
|
420
500
|
"session_id": session_id, "context_exceeded": _ctx_exceeded(err),
|
|
421
501
|
"limit_exceeded": _is_limit(err), "limit_reset_epoch": _parse_reset_epoch(err),
|
|
502
|
+
"output_cap_exceeded": _output_cap_exceeded(err),
|
|
422
503
|
})
|
|
423
504
|
|
|
424
505
|
usage = result_event.get("usage") or {}
|
|
@@ -453,4 +534,5 @@ async def run_claude_local(
|
|
|
453
534
|
"context_exceeded": is_error and _ctx_exceeded(err_text),
|
|
454
535
|
"limit_exceeded": limit_hit,
|
|
455
536
|
"limit_reset_epoch": reset_epoch,
|
|
537
|
+
"output_cap_exceeded": is_error and _output_cap_exceeded(err_text),
|
|
456
538
|
})
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.22 → 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
|