devops-bot-sdk 1.4.39__tar.gz → 1.4.46__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.39 → devops_bot_sdk-1.4.46}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/ipc/handlers.py +184 -16
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/README.md +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/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.46"
|
|
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.46
|
|
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.46"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -393,12 +393,16 @@ def _build_epic_prompt(jira_key: str, summary: str, description: str | None,
|
|
|
393
393
|
' "humanInstructions": "Operator instructions to implement THIS story. MUST '
|
|
394
394
|
"repeat the same project location/path and constraints from the epic so the "
|
|
395
395
|
'coding run works in the right place.",\n'
|
|
396
|
+
' "assigneeEmail": "dev@company.com",\n'
|
|
396
397
|
' "points": 5\n'
|
|
397
398
|
" }\n"
|
|
398
399
|
" ]\n"
|
|
399
400
|
"}\n"
|
|
400
401
|
"Create as many stories as the epic genuinely needs (do not pad or truncate "
|
|
401
|
-
"artificially); each story must be independently implementable
|
|
402
|
+
"artificially); each story must be independently implementable.\n"
|
|
403
|
+
"assigneeEmail is OPTIONAL per story: set it ONLY when the epic explicitly "
|
|
404
|
+
"assigns that specific work to a named person/email (e.g. 'assign the API to "
|
|
405
|
+
"alice@x.com'); otherwise omit it."
|
|
402
406
|
)
|
|
403
407
|
return "\n\n".join(parts)
|
|
404
408
|
|
|
@@ -423,6 +427,17 @@ def _extract_json_block(text: str | None) -> dict | None:
|
|
|
423
427
|
return None
|
|
424
428
|
|
|
425
429
|
|
|
430
|
+
_EMAIL_RE = re.compile(r"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _extract_email(text: str | None) -> str | None:
|
|
434
|
+
"""First email address mentioned in `text`, lower-cased, or None."""
|
|
435
|
+
if not text:
|
|
436
|
+
return None
|
|
437
|
+
m = _EMAIL_RE.search(text)
|
|
438
|
+
return m.group(0).strip().lower() if m else None
|
|
439
|
+
|
|
440
|
+
|
|
426
441
|
def _epic_inherited_fields(task: dict) -> dict:
|
|
427
442
|
"""Parent-Epic fields a child Task should inherit (drops empties)."""
|
|
428
443
|
def _date(v: object) -> str | None:
|
|
@@ -502,24 +517,34 @@ async def _handle_epic(
|
|
|
502
517
|
await send(Envelope(
|
|
503
518
|
type="step_update", thread_id=thread_id,
|
|
504
519
|
data={"jira_task_id": tid, "current_step": f"Epic {jira_key}: marked Done — "
|
|
505
|
-
f"creating {min(len(stories), _MAX_EPIC_STORIES)}
|
|
520
|
+
f"creating {min(len(stories), _MAX_EPIC_STORIES)} user Story(ies)"},
|
|
506
521
|
).model_dump())
|
|
507
522
|
|
|
508
523
|
# 3. Create a To Do child Task per story, inheriting the Epic's fields.
|
|
509
524
|
inherited = _epic_inherited_fields(task)
|
|
525
|
+
# An assignee email named in the EPIC's description/instructions overrides the
|
|
526
|
+
# inherited assignee — but ONLY on the CHILD task, never on the parent Epic.
|
|
527
|
+
desc_email = _extract_email(description) or _extract_email(human)
|
|
510
528
|
created: list[str] = []
|
|
511
529
|
for st in stories[:_MAX_EPIC_STORIES]:
|
|
512
530
|
if not isinstance(st, dict) or not (st.get("summary") or "").strip():
|
|
513
531
|
continue
|
|
532
|
+
# Precedence: a per-story email the model extracted → an email in the
|
|
533
|
+
# epic description/instructions → the inherited parent assignee.
|
|
534
|
+
story_email = _extract_email(str(st.get("assigneeEmail") or ""))
|
|
535
|
+
child_email = story_email or desc_email or inherited.get("assigneeEmail")
|
|
514
536
|
fields = {
|
|
515
537
|
**inherited,
|
|
516
|
-
"type": "
|
|
538
|
+
"type": "Story", # Epic decomposes into user Stories; a Story later
|
|
539
|
+
# becomes a Task (see _handle_story) when it is run.
|
|
517
540
|
"status": "To Do",
|
|
518
541
|
"summary": str(st.get("summary", "")).strip()[:250],
|
|
519
542
|
"description": str(st.get("description", "") or "")[:20000],
|
|
520
543
|
"humanInstructions": str(st.get("humanInstructions") or human or "")[:20000] or None,
|
|
521
544
|
"parentTaskId": tid,
|
|
522
545
|
}
|
|
546
|
+
if child_email:
|
|
547
|
+
fields["assigneeEmail"] = child_email
|
|
523
548
|
if st.get("points"):
|
|
524
549
|
fields["points"] = st["points"]
|
|
525
550
|
new_id = await client.create_task_ml(fields, model_used=_EPIC_MODELS)
|
|
@@ -531,13 +556,148 @@ async def _handle_epic(
|
|
|
531
556
|
).model_dump())
|
|
532
557
|
|
|
533
558
|
await client.log_activity(
|
|
534
|
-
f"Epic {jira_key}: Done — created {len(created)}/{len(stories)}
|
|
559
|
+
f"Epic {jira_key}: Done — created {len(created)}/{len(stories)} To Do user Story(ies)"
|
|
535
560
|
)
|
|
536
561
|
await send(Envelope(
|
|
537
562
|
type="step_update", thread_id=thread_id,
|
|
538
563
|
data={"jira_task_id": tid,
|
|
539
|
-
"current_step": f"Epic {jira_key} complete — {len(created)}
|
|
540
|
-
"(run again
|
|
564
|
+
"current_step": f"Epic {jira_key} complete — {len(created)} Story(ies) queued "
|
|
565
|
+
"(run again: each Story → PRD + Task)"},
|
|
566
|
+
).model_dump())
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
# ── Story → Task (with optional PRD) ───────────────────────────────────────
|
|
570
|
+
# A Story (produced by decomposing an Epic) is not coded directly. When it is
|
|
571
|
+
# run: if a PRD is asked for in the instructions, generate one and fold it into
|
|
572
|
+
# the description alongside the user story; then convert the ticket to a Task
|
|
573
|
+
# (kept To Do) so the next run implements it through the normal pipeline.
|
|
574
|
+
_PRD_RE = re.compile(r"\bprd\b|product\s+requirements?\s+document", re.I)
|
|
575
|
+
# Section header the generated PRD is filed under in the description. Also the
|
|
576
|
+
# idempotency marker: if it's already present we neither regenerate nor re-write.
|
|
577
|
+
_PRD_MARKER = "# PRD — Product Requirements Document"
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def _wants_prd(text: str | None) -> bool:
|
|
581
|
+
"""True when the text asks for a PRD / product-requirements document."""
|
|
582
|
+
return bool(text and _PRD_RE.search(text))
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def _build_prd_prompt(jira_key: str, summary: str, user_story: str | None,
|
|
586
|
+
human_instructions: str | None) -> str:
|
|
587
|
+
"""Read-only prompt that produces a PRD (Markdown) for a user story."""
|
|
588
|
+
parts: list[str] = [
|
|
589
|
+
"Write a PRD (Product Requirements Document) for the user story below. This is "
|
|
590
|
+
"ANALYSIS ONLY — do NOT create, edit, or delete any file.",
|
|
591
|
+
f"=== USER STORY {jira_key} ===\nSummary: {summary}",
|
|
592
|
+
]
|
|
593
|
+
if user_story and user_story.strip():
|
|
594
|
+
parts.append("User story / description:\n" + _clip_text(user_story.strip()))
|
|
595
|
+
if human_instructions and human_instructions.strip():
|
|
596
|
+
parts.append("Operator instructions:\n" + human_instructions.strip())
|
|
597
|
+
parts.append(
|
|
598
|
+
"Explore the codebase (CLAUDE.md, graphify, Read/Glob/Grep) to ground the PRD in "
|
|
599
|
+
"the REAL system. Output ONLY the PRD as Markdown — no preamble, no code edits — "
|
|
600
|
+
"with sections: Overview, Goals & Success Metrics, User Stories, Functional "
|
|
601
|
+
"Requirements, Non-Functional Requirements, Acceptance Criteria, Out of Scope, "
|
|
602
|
+
"Dependencies & Risks."
|
|
603
|
+
)
|
|
604
|
+
return "\n\n".join(parts)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
async def _handle_story(
|
|
608
|
+
client: BackendClient, task: dict, payload: dict, send: Callable, thread_id: str,
|
|
609
|
+
) -> None:
|
|
610
|
+
"""Run a Story: optionally generate a PRD (when instructed) into the description,
|
|
611
|
+
then convert the ticket to a Task (To Do) for the coding run.
|
|
612
|
+
|
|
613
|
+
Best-effort. If a PRD was explicitly instructed but generation fails, the ticket
|
|
614
|
+
goes to Backlog for retry (so the PRD requirement isn't silently dropped); a
|
|
615
|
+
conversion failure likewise backlogs it.
|
|
616
|
+
"""
|
|
617
|
+
from sdk import local_exec, graphify
|
|
618
|
+
|
|
619
|
+
tid = str(task.get("id", ""))
|
|
620
|
+
jira_key = task.get("jiraIssueKey") or task.get("jiraKey", "") or "STORY"
|
|
621
|
+
summary = task.get("summary", "") or ""
|
|
622
|
+
user_story = task.get("description") or ""
|
|
623
|
+
human = task.get("humanInstructions")
|
|
624
|
+
wants_prd = _wants_prd(human) or _wants_prd(user_story)
|
|
625
|
+
|
|
626
|
+
await send(Envelope(
|
|
627
|
+
type="step_update", thread_id=thread_id,
|
|
628
|
+
data={"jira_task_id": tid, "task_id": tid,
|
|
629
|
+
"current_step": f"Story {jira_key}: "
|
|
630
|
+
+ ("generating PRD, then " if wants_prd else "")
|
|
631
|
+
+ "converting to Task"},
|
|
632
|
+
).model_dump())
|
|
633
|
+
|
|
634
|
+
# Idempotent: if a PRD is already in the description, don't regenerate it or
|
|
635
|
+
# re-write the description — only the type conversion (if still needed) runs.
|
|
636
|
+
already_has_prd = _PRD_MARKER in (user_story or "")
|
|
637
|
+
new_description: str | None = None
|
|
638
|
+
if wants_prd and already_has_prd:
|
|
639
|
+
await send(Envelope(
|
|
640
|
+
type="step_update", thread_id=thread_id,
|
|
641
|
+
data={"jira_task_id": tid,
|
|
642
|
+
"current_step": f"Story {jira_key}: PRD already present — skipping regeneration"},
|
|
643
|
+
).model_dump())
|
|
644
|
+
elif wants_prd:
|
|
645
|
+
project_path = _resolve_local_project_path(task, payload)
|
|
646
|
+
try:
|
|
647
|
+
await graphify.prepare(project_path)
|
|
648
|
+
except Exception: # noqa: BLE001
|
|
649
|
+
pass
|
|
650
|
+
gen = await local_exec.run_claude_local(
|
|
651
|
+
_build_prd_prompt(jira_key, summary, user_story, human),
|
|
652
|
+
project_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only analysis
|
|
653
|
+
github_token=await client.github_token(),
|
|
654
|
+
)
|
|
655
|
+
prd = (gen.get("result") or "").strip() if gen.get("ok") else ""
|
|
656
|
+
if prd:
|
|
657
|
+
# PRD lives alongside the user story in the (now Task) description.
|
|
658
|
+
new_description = f"{user_story}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"[:20000]
|
|
659
|
+
else:
|
|
660
|
+
# PRD generation failed — do NOT abandon the story: it must still become
|
|
661
|
+
# a Task (per "a Story once run should be changed into Task"). The PRD
|
|
662
|
+
# directive stays in humanInstructions, so the later coding run can still
|
|
663
|
+
# produce the PRD.md file.
|
|
664
|
+
reason = (gen.get("error") or "PRD generation produced no content")[:200]
|
|
665
|
+
await client.log_activity(
|
|
666
|
+
f"Story {jira_key}: PRD not pre-generated ({reason}) — converting to Task anyway"
|
|
667
|
+
)
|
|
668
|
+
await send(Envelope(
|
|
669
|
+
type="step_update", thread_id=thread_id,
|
|
670
|
+
data={"jira_task_id": tid,
|
|
671
|
+
"current_step": f"Story {jira_key}: PRD generation skipped ({reason[:60]}) "
|
|
672
|
+
"— converting to Task"},
|
|
673
|
+
).model_dump())
|
|
674
|
+
|
|
675
|
+
# Convert Story → Task on the SAME ticket; leave it To Do so the next run codes
|
|
676
|
+
# it. humanInstructions are untouched (they still carry the location + any PRD
|
|
677
|
+
# directive, so the coding run can also write the PRD.md file if asked).
|
|
678
|
+
update: dict = {"type": "Task"}
|
|
679
|
+
if new_description:
|
|
680
|
+
update["description"] = new_description
|
|
681
|
+
if not await client.update_task_ml(tid, update, model_used=_EPIC_MODELS):
|
|
682
|
+
# Leave the Story To Do (never In Progress/Done/Backlog) on failure.
|
|
683
|
+
await client.log_activity(f"Story {jira_key}: convert-to-Task failed")
|
|
684
|
+
await send(Envelope(
|
|
685
|
+
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
686
|
+
data={"code": "STORY_CONVERT_FAILED", "task_id": tid,
|
|
687
|
+
"message": "could not convert Story to Task"},
|
|
688
|
+
).model_dump())
|
|
689
|
+
return
|
|
690
|
+
|
|
691
|
+
prd_in_desc = bool(new_description) or already_has_prd
|
|
692
|
+
await client.log_activity(
|
|
693
|
+
f"Story {jira_key}: converted to Task{' with PRD' if prd_in_desc else ''} (To Do)"
|
|
694
|
+
)
|
|
695
|
+
await send(Envelope(
|
|
696
|
+
type="step_update", thread_id=thread_id,
|
|
697
|
+
data={"jira_task_id": tid,
|
|
698
|
+
"current_step": f"Story {jira_key} → Task (To Do)"
|
|
699
|
+
+ (" + PRD in description" if prd_in_desc else "")
|
|
700
|
+
+ " — run again to implement"},
|
|
541
701
|
).model_dump())
|
|
542
702
|
|
|
543
703
|
|
|
@@ -2372,6 +2532,7 @@ async def handle_orchestrate_auto_local_run(
|
|
|
2372
2532
|
jira_key = task.get("jiraIssueKey") or task.get("jiraKey", "")
|
|
2373
2533
|
agent_mode = bool(task.get("agentMode", True)) # default True (fail-safe)
|
|
2374
2534
|
project_path = _resolve_local_project_path(task, payload)
|
|
2535
|
+
ttype = str(task.get("type") or "").strip().lower()
|
|
2375
2536
|
|
|
2376
2537
|
await send(Envelope(
|
|
2377
2538
|
type="step_update", thread_id=thread_id,
|
|
@@ -2379,23 +2540,30 @@ async def handle_orchestrate_auto_local_run(
|
|
|
2379
2540
|
"project_path": project_path, "jira_task_id": tid, "agent_mode": agent_mode},
|
|
2380
2541
|
).model_dump())
|
|
2381
2542
|
|
|
2382
|
-
#
|
|
2383
|
-
#
|
|
2384
|
-
#
|
|
2385
|
-
|
|
2543
|
+
# Staged decomposition — an EPIC and a STORY are transformed, not coded:
|
|
2544
|
+
# Epic → decompose into user Stories (To Do), mark Epic Done
|
|
2545
|
+
# Story → (optional PRD) + ALWAYS convert to Task (To Do); a Story is
|
|
2546
|
+
# never marked In Progress/Done — only ever converted
|
|
2547
|
+
# Task → implemented by the normal pipeline below (In Progress → Done)
|
|
2548
|
+
# Each is isolated: a failure must not abort the remaining queue.
|
|
2549
|
+
if ttype in ("epic", "story"):
|
|
2550
|
+
handler = _handle_epic if ttype == "epic" else _handle_story
|
|
2551
|
+
code = "EPIC_DECOMPOSE_FAILED" if ttype == "epic" else "STORY_HANDLE_FAILED"
|
|
2386
2552
|
try:
|
|
2387
|
-
await
|
|
2553
|
+
await handler(client, task, payload, send, thread_id)
|
|
2388
2554
|
except Exception as exc: # noqa: BLE001
|
|
2389
|
-
logger.warning("auto_local
|
|
2555
|
+
logger.warning("auto_local.%s_failed task=%s err=%s", ttype, tid, exc)
|
|
2390
2556
|
try:
|
|
2391
|
-
await client.log_activity(f"Task {tid}:
|
|
2392
|
-
|
|
2557
|
+
await client.log_activity(f"Task {tid}: {ttype} handling errored — {str(exc)[:200]}")
|
|
2558
|
+
# Backlog only Epics for retry; a Story is left To Do (never
|
|
2559
|
+
# marked In Progress/Done/Backlog) so developers keep it.
|
|
2560
|
+
if ttype == "epic":
|
|
2561
|
+
await client.move_to_backlog(tid)
|
|
2393
2562
|
except Exception: # noqa: BLE001
|
|
2394
2563
|
pass
|
|
2395
2564
|
await send(Envelope(
|
|
2396
2565
|
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
2397
|
-
data={"code":
|
|
2398
|
-
"message": str(exc)[:200]},
|
|
2566
|
+
data={"code": code, "task_id": tid, "message": str(exc)[:200]},
|
|
2399
2567
|
).model_dump())
|
|
2400
2568
|
processed += 1
|
|
2401
2569
|
continue
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.39 → devops_bot_sdk-1.4.46}/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
|