devops-bot-sdk 1.4.90__tar.gz → 1.4.94__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.90 → devops_bot_sdk-1.4.94}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/git_ops.py +40 -12
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/ipc/handlers.py +49 -2
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/local_exec.py +15 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/README.md +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/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.94"
|
|
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.94
|
|
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.94"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -72,6 +72,19 @@ async def _is_git_repo(path: str, env: dict) -> bool:
|
|
|
72
72
|
return rc == 0 and out == "true"
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
# A branch named exactly like a Jira ticket id (e.g. "LDA-6", "OCR-103"). The
|
|
76
|
+
# "name the branch exactly the ticket id" convention produces these, so a
|
|
77
|
+
# leftover checkout from a PREVIOUS ticket's run must be treated as a stale
|
|
78
|
+
# feature branch — never as a base to cut the next ticket's branch from.
|
|
79
|
+
_TICKET_BRANCH_RE = re.compile(r"^[A-Za-z][A-Za-z0-9]{1,9}-\d+$")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _is_work_branch(name: str) -> bool:
|
|
83
|
+
"""True when `name` is one of OUR generated work branches (feature/* or a
|
|
84
|
+
bare ticket id) — i.e. never a legitimate base."""
|
|
85
|
+
return name.startswith("feature/") or bool(_TICKET_BRANCH_RE.match(name))
|
|
86
|
+
|
|
87
|
+
|
|
75
88
|
async def detect_default_branch(path: str, env: dict) -> str:
|
|
76
89
|
"""Repo default branch — the PR base.
|
|
77
90
|
|
|
@@ -100,9 +113,10 @@ async def detect_default_branch(path: str, env: dict) -> str:
|
|
|
100
113
|
)
|
|
101
114
|
if rc == 0:
|
|
102
115
|
return cand
|
|
103
|
-
# 4. Last resort: the current branch, but never
|
|
116
|
+
# 4. Last resort: the current branch, but never one of our work branches
|
|
117
|
+
# (feature/* or a bare ticket id like "LDA-6").
|
|
104
118
|
rc, out, _ = await _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], path, env)
|
|
105
|
-
if rc == 0 and out and not out
|
|
119
|
+
if rc == 0 and out and not _is_work_branch(out):
|
|
106
120
|
return out
|
|
107
121
|
return "main"
|
|
108
122
|
|
|
@@ -167,9 +181,10 @@ async def start_branch(
|
|
|
167
181
|
- `base` omitted → the repo's CURRENT checked-out branch (the branch the
|
|
168
182
|
user is actually working on); the feature branch is cut from local HEAD
|
|
169
183
|
so the user's working state is preserved. If HEAD is detached or already
|
|
170
|
-
one of OUR
|
|
171
|
-
a
|
|
172
|
-
|
|
184
|
+
one of OUR work branches — `feature/*` or a bare ticket-id branch like
|
|
185
|
+
"LDA-6" (a stale checkout from a prior ticket's run in a shared working
|
|
186
|
+
tree) — fall back to `detect_default_branch` so we never chain one
|
|
187
|
+
ticket's branch onto another's.
|
|
173
188
|
|
|
174
189
|
Returns {"branch", "base"} or None when the path isn't a git repo (e.g. a
|
|
175
190
|
fresh scratch dir) or the checkout failed. The returned `base` is the branch
|
|
@@ -210,7 +225,7 @@ async def start_branch(
|
|
|
210
225
|
# Default: base = the CURRENT checked-out branch (the user's working branch).
|
|
211
226
|
rc, cur, _ = await _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], path, env)
|
|
212
227
|
cur = cur.strip() if rc == 0 else ""
|
|
213
|
-
if cur and cur != "HEAD" and not cur
|
|
228
|
+
if cur and cur != "HEAD" and not _is_work_branch(cur):
|
|
214
229
|
# Cut from local HEAD — preserve the user's working state, do not pull.
|
|
215
230
|
rc, _, _ = await _run(["git", "checkout", "-B", branch], path, env)
|
|
216
231
|
if rc != 0:
|
|
@@ -218,7 +233,8 @@ async def start_branch(
|
|
|
218
233
|
logger.info("git_ops.branch | %s ← current branch %s", branch, cur)
|
|
219
234
|
return {"branch": branch, "base": cur}
|
|
220
235
|
|
|
221
|
-
# HEAD detached or a stale
|
|
236
|
+
# HEAD detached or a stale work-branch checkout (feature/* or a previous
|
|
237
|
+
# ticket's id-named branch) → fall back to the repo default.
|
|
222
238
|
resolved = await detect_default_branch(path, env)
|
|
223
239
|
if resolved == branch:
|
|
224
240
|
resolved = "main"
|
|
@@ -300,7 +316,10 @@ async def finish_pr(
|
|
|
300
316
|
`gh auth login`). SSH alone covers git transport, not the PR.
|
|
301
317
|
|
|
302
318
|
Every failure path is logged so a missing PR is diagnosable instead of silent.
|
|
303
|
-
Returns {"pushed": bool, "pr_url": str | None, "pr_error": str | None
|
|
319
|
+
Returns {"pushed": bool, "pr_url": str | None, "pr_error": str | None,
|
|
320
|
+
"committed": bool, "pr_unavailable": bool} — `pr_unavailable` is True when
|
|
321
|
+
the box simply has no way to open a PR (no token AND no `gh`), which callers
|
|
322
|
+
treat as a warning rather than a failed PR attempt.
|
|
304
323
|
"""
|
|
305
324
|
path = str(Path(project_path).expanduser())
|
|
306
325
|
env = _git_env(github_token)
|
|
@@ -353,18 +372,27 @@ async def finish_pr(
|
|
|
353
372
|
pr_error = f"gh pr create failed: {gh_err[:200]}"
|
|
354
373
|
logger.warning("git_ops.gh_pr_failed branch=%s error=%s", branch, gh_err[:300])
|
|
355
374
|
|
|
375
|
+
pr_unavailable = False
|
|
356
376
|
if not pr_url and pr_error is None:
|
|
357
377
|
# Pushed, but no PR and no specific error captured above.
|
|
358
378
|
if github_token and owner_repo:
|
|
359
379
|
pr_error = "no PR created — REST API returned no URL"
|
|
360
380
|
elif not github_token and not shutil.which("gh"):
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
# This box CANNOT open a PR at all (no token, no gh) — SSH covers
|
|
382
|
+
# git transport only. Not a failed attempt: on SSH-only setups the
|
|
383
|
+
# PR is opened externally (platform/human), so flag it as
|
|
384
|
+
# `pr_unavailable` so the caller treats it as a warning, not a
|
|
385
|
+
# compliance failure.
|
|
386
|
+
pr_unavailable = True
|
|
387
|
+
pr_error = ("PR not opened from this machine — no GitHub token and "
|
|
388
|
+
"`gh` not installed (SSH covers push only). The PR must "
|
|
389
|
+
"be opened externally, or install gh + `gh auth login` / "
|
|
390
|
+
"provide a token.")
|
|
364
391
|
elif not shutil.which("gh"):
|
|
365
392
|
pr_error = "no PR created — missing GitHub token or unrecognized origin remote"
|
|
366
393
|
else:
|
|
367
394
|
pr_error = "no PR created"
|
|
368
395
|
logger.warning("git_ops.no_pr branch=%s reason=%s", branch, pr_error)
|
|
369
396
|
|
|
370
|
-
return {"pushed": pushed, "pr_url": pr_url, "pr_error": pr_error,
|
|
397
|
+
return {"pushed": pushed, "pr_url": pr_url, "pr_error": pr_error,
|
|
398
|
+
"committed": committed, "pr_unavailable": pr_unavailable}
|
|
@@ -416,7 +416,10 @@ async def _run_with_heartbeat(coro, *, send: Callable, thread_id: str, label: st
|
|
|
416
416
|
beat.cancel()
|
|
417
417
|
try:
|
|
418
418
|
await beat
|
|
419
|
-
except Exception: # noqa: BLE001
|
|
419
|
+
except (asyncio.CancelledError, Exception): # noqa: BLE001
|
|
420
|
+
# CancelledError is a BaseException: raised here when the beat task
|
|
421
|
+
# was cancelled before it ever ran (coro finished immediately) — it
|
|
422
|
+
# must not propagate and sink the caller's result.
|
|
420
423
|
pass
|
|
421
424
|
|
|
422
425
|
|
|
@@ -441,6 +444,7 @@ async def _run_generation(
|
|
|
441
444
|
send=send, thread_id=thread_id, label=label, name=name,
|
|
442
445
|
)
|
|
443
446
|
combined = res.get("result") or ""
|
|
447
|
+
longest = res.get("longest_text") or ""
|
|
444
448
|
sid = res.get("session_id")
|
|
445
449
|
chunks = 0
|
|
446
450
|
while res.get("output_cap_exceeded") and sid and chunks < _MAX_OUTPUT_CAP_RESUMES:
|
|
@@ -463,9 +467,24 @@ async def _run_generation(
|
|
|
463
467
|
send=send, thread_id=thread_id, label=f"{label} (cont.)", name=name,
|
|
464
468
|
)
|
|
465
469
|
combined += res.get("result") or ""
|
|
470
|
+
if len(res.get("longest_text") or "") > len(longest):
|
|
471
|
+
longest = res.get("longest_text") or ""
|
|
466
472
|
sid = res.get("session_id") or sid
|
|
467
473
|
out = dict(res)
|
|
468
474
|
out["result"] = combined # the full, reassembled output
|
|
475
|
+
# Salvage a lost artifact: the CLI's `result` is only the LAST assistant
|
|
476
|
+
# message. If the model wrote the artifact and was then woken once more by a
|
|
477
|
+
# stray late event (e.g. a leftover background timer) for a short closing
|
|
478
|
+
# remark ("nothing further is pending"), `combined` is that remark and the
|
|
479
|
+
# real output is an EARLIER, much longer message. When an earlier message
|
|
480
|
+
# dwarfs the final one, it is the artifact — use it.
|
|
481
|
+
if (not out.get("limit_exceeded") and len(longest) > 1000
|
|
482
|
+
and len(longest) > 2 * len(combined.strip())):
|
|
483
|
+
logger.warning(
|
|
484
|
+
"generation.salvaged_longest_message final_len=%s longest_len=%s",
|
|
485
|
+
len(combined.strip()), len(longest),
|
|
486
|
+
)
|
|
487
|
+
out["result"] = longest.strip()
|
|
469
488
|
out["session_id"] = sid
|
|
470
489
|
return out
|
|
471
490
|
|
|
@@ -1382,6 +1401,13 @@ async def _handle_story(
|
|
|
1382
1401
|
# Use the output regardless of an is_error flag (a stray read-only tool denial
|
|
1383
1402
|
# shouldn't discard a PRD the model actually produced); only a usage limit is fatal.
|
|
1384
1403
|
prd = "" if gen.get("limit_exceeded") else (gen.get("result") or "").strip()
|
|
1404
|
+
if prd and len(prd) < 400:
|
|
1405
|
+
# A real PRD (numbered sections per _PRD_FORMAT) is never this short —
|
|
1406
|
+
# this is a stub (e.g. a closing remark captured as the run's result).
|
|
1407
|
+
# Treat it as a failed generation rather than filing garbage in the ticket.
|
|
1408
|
+
if not gen.get("error"):
|
|
1409
|
+
gen["error"] = f"PRD too short ({len(prd)} chars) — not a document"
|
|
1410
|
+
prd = ""
|
|
1385
1411
|
if prd:
|
|
1386
1412
|
# PRD lives alongside the user story in the (now Task) description.
|
|
1387
1413
|
new_description = f"{user_story}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"[:20000]
|
|
@@ -2078,8 +2104,19 @@ async def _code_one_repo(
|
|
|
2078
2104
|
issues.append("in scope but produced 0 committed changes")
|
|
2079
2105
|
elif not pr.get("pushed"):
|
|
2080
2106
|
issues.append("branch was not pushed")
|
|
2081
|
-
elif not pr.get("pr_url"):
|
|
2107
|
+
elif not pr.get("pr_url") and not pr.get("pr_unavailable"):
|
|
2082
2108
|
issues.append(f"no PR opened ({(pr or {}).get('pr_error') or 'unknown'})")
|
|
2109
|
+
elif pr.get("pr_unavailable"):
|
|
2110
|
+
# Committed + pushed, but this box has NO way to open a PR (no
|
|
2111
|
+
# token, no gh — SSH transport only). The PR is opened
|
|
2112
|
+
# externally on such setups, so this is a WARNING, not a
|
|
2113
|
+
# compliance failure: the ticket still completes (Done).
|
|
2114
|
+
await send(Envelope(
|
|
2115
|
+
type="step_update", thread_id=thread_id,
|
|
2116
|
+
data={"status": "running",
|
|
2117
|
+
"current_step": f"[{name}] Pushed OK — PR to be opened "
|
|
2118
|
+
"externally (no token/gh on this machine)"},
|
|
2119
|
+
).model_dump())
|
|
2083
2120
|
else:
|
|
2084
2121
|
# Plain working folder (documentation / analyst deliverables, non-git):
|
|
2085
2122
|
# no branch/PR to verify, but it must still have PRODUCED something.
|
|
@@ -2878,6 +2915,7 @@ import re as _re # noqa: E402
|
|
|
2878
2915
|
_BASE_NOISE = {
|
|
2879
2916
|
"the", "a", "an", "this", "that", "new", "feature", "features",
|
|
2880
2917
|
"your", "same", "current", "default", "its", "please", "create",
|
|
2918
|
+
"origin", "latest", "upstream", "branch",
|
|
2881
2919
|
}
|
|
2882
2920
|
_BASE_DIRECTIVE_RES = [
|
|
2883
2921
|
# "branch from/off <base>" — most explicit, tried first. Greedy so a full
|
|
@@ -2885,6 +2923,15 @@ _BASE_DIRECTIVE_RES = [
|
|
|
2885
2923
|
_re.compile(r"branch(?:es|ed|ing)?\s+(?:from|off(?:\s+of)?)\s+(?:the\s+)?[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?(?:\s+branch)?\b", _re.I),
|
|
2886
2924
|
# "base branch: <base>" / "base is <base>"
|
|
2887
2925
|
_re.compile(r"base(?:\s+branch)?\s*(?:[:=]|\bis\b|\bshould\s+be\b)\s*[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?\b", _re.I),
|
|
2926
|
+
# Literal git-workflow steps: "git checkout <base>" (NOT -b/-B — that's the
|
|
2927
|
+
# NEW branch) and "git pull origin <base>". This is how the standard
|
|
2928
|
+
# Human Instructions template names the base ("git checkout main && git
|
|
2929
|
+
# pull origin main").
|
|
2930
|
+
_re.compile(r"git\s+checkout\s+(?!-)[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?", _re.I),
|
|
2931
|
+
_re.compile(r"git\s+pull\s+(?:origin\s+)?[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?", _re.I),
|
|
2932
|
+
# "open a PR into <base>" / "merge to <base>" / "PR Requirements (target: <base>"
|
|
2933
|
+
_re.compile(r"(?:PR|pull\s+request|merge[sd]?)\s+(?:in)?to\s+(?:the\s+)?[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?", _re.I),
|
|
2934
|
+
_re.compile(r"target\s*:\s*[\"'`]?([A-Za-z][A-Za-z0-9._\-/]*)[\"'`]?", _re.I),
|
|
2888
2935
|
# "from the <base> branch"
|
|
2889
2936
|
_re.compile(r"from\s+(?:the\s+)?[\"'`]?([A-Za-z0-9._\-/]+)[\"'`]?\s+branch\b", _re.I),
|
|
2890
2937
|
# loose "<base> branch" (e.g. "main branch") — last, most permissive.
|
|
@@ -386,6 +386,12 @@ async def run_claude_local(
|
|
|
386
386
|
|
|
387
387
|
result_event: dict | None = None
|
|
388
388
|
files_changed: list[str] = []
|
|
389
|
+
# Longest single assistant TEXT message seen in the stream. The CLI's final
|
|
390
|
+
# `result` field is only the LAST assistant message — if the model outputs a
|
|
391
|
+
# large artifact (PRD/decomposition) and is then woken once more by a stray
|
|
392
|
+
# late event (e.g. a leftover background timer) for a short closing remark,
|
|
393
|
+
# the artifact would be lost. Callers use this to salvage it.
|
|
394
|
+
longest_text = {"text": ""}
|
|
389
395
|
session_id: str | None = resume_session_id
|
|
390
396
|
import time as _time
|
|
391
397
|
started = _time.monotonic()
|
|
@@ -452,6 +458,14 @@ async def run_claude_local(
|
|
|
452
458
|
if event.get("session_id"):
|
|
453
459
|
session_id = str(event["session_id"])
|
|
454
460
|
|
|
461
|
+
# Track the longest assistant text message (see `longest_text` above).
|
|
462
|
+
if event.get("type") == "assistant":
|
|
463
|
+
for c in event.get("message", {}).get("content", []) or []:
|
|
464
|
+
if isinstance(c, dict) and c.get("type") == "text":
|
|
465
|
+
txt = str(c.get("text") or "")
|
|
466
|
+
if len(txt) > len(longest_text["text"]):
|
|
467
|
+
longest_text["text"] = txt
|
|
468
|
+
|
|
455
469
|
# Track files the agent created/edited for the result summary.
|
|
456
470
|
if event.get("type") == "user":
|
|
457
471
|
tr = event.get("tool_use_result")
|
|
@@ -526,6 +540,7 @@ async def run_claude_local(
|
|
|
526
540
|
return _finish({
|
|
527
541
|
"ok": (not is_error) and (not limit_hit),
|
|
528
542
|
"result": result_text,
|
|
543
|
+
"longest_text": longest_text["text"],
|
|
529
544
|
"input_tokens": in_tok,
|
|
530
545
|
"output_tokens": out_tok,
|
|
531
546
|
"total_tokens": in_tok + out_tok,
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.90 → devops_bot_sdk-1.4.94}/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
|