devops-bot-sdk 1.4.14__tar.gz → 1.4.19__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.14 → devops_bot_sdk-1.4.19}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/handlers.py +35 -15
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/local_exec.py +17 -1
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/updater.py +72 -31
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/README.md +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/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.19"
|
|
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.19
|
|
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.19"
|
|
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.19"
|
|
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
|
|
@@ -149,6 +149,7 @@ def _build_local_prompt(
|
|
|
149
149
|
structured_plan: dict | None,
|
|
150
150
|
human_instructions: str | None = None,
|
|
151
151
|
agent_mode: bool = True,
|
|
152
|
+
git_managed: bool = True,
|
|
152
153
|
) -> str:
|
|
153
154
|
"""Compose the coding prompt for the local claude CLI from the server plan.
|
|
154
155
|
|
|
@@ -204,14 +205,29 @@ def _build_local_prompt(
|
|
|
204
205
|
"above are authoritative if anything conflicts) ===\n"
|
|
205
206
|
+ _clip_text(user_input.strip())
|
|
206
207
|
)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
if git_managed:
|
|
209
|
+
# The target is a git repo the SDK manages — it branches before and
|
|
210
|
+
# commits/pushes/opens the PR after, so the agent must not touch git here.
|
|
211
|
+
parts.append(
|
|
212
|
+
"Complete all of the above in the current project directory. "
|
|
213
|
+
"Create and edit every file needed for a complete, working result.\n"
|
|
214
|
+
"Git for THIS repo is handled for you: a feature branch is already checked "
|
|
215
|
+
"out, and the system will commit, push, and open the PR after you finish — "
|
|
216
|
+
"so do NOT create branches, commit, push, or open a PR for the current repo. "
|
|
217
|
+
"(You may still clone and open PRs for OTHER repositories the instructions name.)"
|
|
218
|
+
)
|
|
219
|
+
else:
|
|
220
|
+
# Plain local working folder (no GitHub repo / not under our git control) —
|
|
221
|
+
# e.g. a non-developer (BA/analyst) task that just produces files/docs.
|
|
222
|
+
parts.append(
|
|
223
|
+
"Complete all of the above in the current working directory by creating and "
|
|
224
|
+
"editing the necessary files (documents, .md files, spreadsheets, code — "
|
|
225
|
+
"whatever the task asks for). This is a plain local folder, NOT a git "
|
|
226
|
+
"repository under our control: do NOT initialise git, create branches, "
|
|
227
|
+
"commit, push, or open a PR for it unless the instructions explicitly say so. "
|
|
228
|
+
"Just produce the requested deliverables here. (You may still clone and open "
|
|
229
|
+
"PRs for OTHER repositories the instructions explicitly name.)"
|
|
230
|
+
)
|
|
215
231
|
# agentMode=true → require approval before crucial/irreversible actions.
|
|
216
232
|
if agent_mode:
|
|
217
233
|
from sdk import crucial
|
|
@@ -399,7 +415,6 @@ async def _code_one_repo(
|
|
|
399
415
|
repo_path: str,
|
|
400
416
|
repo_url: str | None,
|
|
401
417
|
pending: dict,
|
|
402
|
-
prompt: str,
|
|
403
418
|
gh_token: str | None,
|
|
404
419
|
agent_mode: bool,
|
|
405
420
|
approval_dir: str | None,
|
|
@@ -474,6 +489,14 @@ async def _code_one_repo(
|
|
|
474
489
|
data={"status": "running", "current_step": f"Executing locally in {name} (claude_code)"},
|
|
475
490
|
).model_dump())
|
|
476
491
|
|
|
492
|
+
# Build the prompt with framing that matches the target: a git repo we manage
|
|
493
|
+
# (branch + PR) vs a plain local folder (BA/analyst deliverables, no git/PR).
|
|
494
|
+
prompt = _build_local_prompt(
|
|
495
|
+
pending["user_input"], pending.get("structured_plan"),
|
|
496
|
+
pending.get("human_instructions"),
|
|
497
|
+
agent_mode=agent_mode, git_managed=bool(branch_info),
|
|
498
|
+
)
|
|
499
|
+
|
|
477
500
|
# 3. initial claude run
|
|
478
501
|
result = await local_exec.run_claude_local(
|
|
479
502
|
prompt, repo_path, on_event=_on_event,
|
|
@@ -636,11 +659,8 @@ async def handle_pipeline_approve(
|
|
|
636
659
|
return
|
|
637
660
|
|
|
638
661
|
# ── Approved: run the local claude CLI on EACH repo the ticket names ──
|
|
639
|
-
prompt
|
|
640
|
-
|
|
641
|
-
pending.get("human_instructions"),
|
|
642
|
-
agent_mode=bool(pending.get("agent_mode", True)),
|
|
643
|
-
)
|
|
662
|
+
# The prompt is built per-repo inside _code_one_repo so its framing matches
|
|
663
|
+
# whether that target is a git repo (branch + PR) or a plain local folder.
|
|
644
664
|
agent_mode = bool(pending.get("agent_mode", True))
|
|
645
665
|
|
|
646
666
|
# Fetch the user's GitHub token from the backend so the local agent can
|
|
@@ -675,7 +695,7 @@ async def handle_pipeline_approve(
|
|
|
675
695
|
runs.append(await _code_one_repo(
|
|
676
696
|
client, repo_path=tgt["path"], repo_url=tgt.get("url"),
|
|
677
697
|
base=tgt.get("base"),
|
|
678
|
-
pending=pending,
|
|
698
|
+
pending=pending, gh_token=gh_token,
|
|
679
699
|
agent_mode=agent_mode, approval_dir=approval_dir,
|
|
680
700
|
send=send, thread_id=thread_id, task_id=task_id,
|
|
681
701
|
))
|
|
@@ -222,8 +222,12 @@ async def run_claude_local(
|
|
|
222
222
|
path.mkdir(parents=True, exist_ok=True)
|
|
223
223
|
|
|
224
224
|
tools = allowed_tools or DEFAULT_ALLOWED_TOOLS
|
|
225
|
+
# The prompt is fed via STDIN (not as an argv), so a long plan/instructions
|
|
226
|
+
# can't blow past the OS command-line limit — on Windows that's ~32 KB and
|
|
227
|
+
# raised "The command line is too long." `claude -p` reads the prompt from a
|
|
228
|
+
# piped stdin.
|
|
225
229
|
cmd = [
|
|
226
|
-
cli, "-p",
|
|
230
|
+
cli, "-p",
|
|
227
231
|
"--output-format", "stream-json",
|
|
228
232
|
"--verbose", # required for stream-json
|
|
229
233
|
"--permission-mode", "acceptEdits", # apply file edits without prompting
|
|
@@ -270,12 +274,24 @@ async def run_claude_local(
|
|
|
270
274
|
proc = await asyncio.create_subprocess_exec(
|
|
271
275
|
*cmd,
|
|
272
276
|
cwd=str(path),
|
|
277
|
+
stdin=asyncio.subprocess.PIPE,
|
|
273
278
|
stdout=asyncio.subprocess.PIPE,
|
|
274
279
|
stderr=asyncio.subprocess.PIPE,
|
|
275
280
|
env=env,
|
|
276
281
|
limit=_STREAM_LIMIT_BYTES,
|
|
277
282
|
)
|
|
278
283
|
|
|
284
|
+
# Feed the prompt via stdin (claude -p reads it), then close stdin so claude
|
|
285
|
+
# starts. Bounded write before draining stdout — no deadlock (claude buffers
|
|
286
|
+
# the prompt before emitting any events).
|
|
287
|
+
if proc.stdin is not None:
|
|
288
|
+
try:
|
|
289
|
+
proc.stdin.write((prompt or "").encode("utf-8"))
|
|
290
|
+
await proc.stdin.drain()
|
|
291
|
+
proc.stdin.close()
|
|
292
|
+
except Exception: # noqa: BLE001 — broken pipe if claude already exited
|
|
293
|
+
pass
|
|
294
|
+
|
|
279
295
|
result_event: dict | None = None
|
|
280
296
|
files_changed: list[str] = []
|
|
281
297
|
session_id: str | None = resume_session_id
|
|
@@ -183,43 +183,65 @@ def _fetch_github_token() -> str | None:
|
|
|
183
183
|
|
|
184
184
|
# ── Core update logic ─────────────────────────────────────────────────────────
|
|
185
185
|
|
|
186
|
-
def
|
|
187
|
-
"""
|
|
186
|
+
def _install_candidates() -> list[str]:
|
|
187
|
+
"""Ordered pip targets: detected URL → SSH → token-HTTPS branch → PyPI dist."""
|
|
188
|
+
cands = list(_unique([_detect_install_url(), _GITHUB_SSH]))
|
|
189
|
+
gh_token = _fetch_github_token()
|
|
190
|
+
if gh_token:
|
|
191
|
+
cands.append(f"git+https://{gh_token}@github.com/{_GITHUB_REPO}.git@{_sdk_branch()}")
|
|
192
|
+
cands.append(_DIST)
|
|
193
|
+
return cands
|
|
188
194
|
|
|
189
|
-
Tries candidates in order:
|
|
190
|
-
1. Original install URL (from direct_url.json)
|
|
191
|
-
2. GitHub SSH URL
|
|
192
|
-
3. GitHub HTTPS URL using token from /ml-api/github/token
|
|
193
|
-
4. PyPI dist name
|
|
194
|
-
Stops at the first success.
|
|
195
|
-
"""
|
|
196
|
-
flags = _pip_flags()
|
|
197
195
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
196
|
+
def _pip_install(url: str, *, force: bool = False) -> bool:
|
|
197
|
+
"""pip install --upgrade <url>. force=True adds --force-reinstall (repair)."""
|
|
198
|
+
extra = ["--force-reinstall", "--no-deps"] if force else []
|
|
199
|
+
try:
|
|
200
|
+
subprocess.run(
|
|
201
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", "--quiet",
|
|
202
|
+
*extra, *_pip_flags(), url],
|
|
203
|
+
check=True, capture_output=True,
|
|
204
|
+
)
|
|
205
|
+
return True
|
|
206
|
+
except Exception: # noqa: BLE001
|
|
207
|
+
return False
|
|
208
208
|
|
|
209
|
-
# 1 + 2: detected URL and SSH fallback
|
|
210
|
-
for url in _unique([_detect_install_url(), _GITHUB_SSH]):
|
|
211
|
-
if _pip_upgrade(url):
|
|
212
|
-
return True
|
|
213
209
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
210
|
+
def _verify_install() -> bool:
|
|
211
|
+
"""True if the installed SDK imports cleanly in a FRESH process.
|
|
212
|
+
|
|
213
|
+
Guards against a partial/corrupt upgrade — on Windows the running
|
|
214
|
+
``agentos-auto.exe`` / loaded package files can't be replaced mid-pip, which
|
|
215
|
+
can leave ``sdk`` importable but missing submodules (e.g. local_exec)."""
|
|
216
|
+
try:
|
|
217
|
+
r = subprocess.run(
|
|
218
|
+
[sys.executable, "-c",
|
|
219
|
+
"import sdk, sdk.local_exec, sdk.ipc.handlers, sdk.run_auto"],
|
|
220
|
+
capture_output=True, timeout=90,
|
|
221
|
+
)
|
|
222
|
+
return r.returncode == 0
|
|
223
|
+
except Exception: # noqa: BLE001
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _repair_install() -> bool:
|
|
228
|
+
"""Force-reinstall to recover from a corrupt upgrade; verifies the result."""
|
|
229
|
+
for url in _install_candidates():
|
|
230
|
+
if _pip_install(url, force=True) and _verify_install():
|
|
219
231
|
return True
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def self_update() -> bool:
|
|
236
|
+
"""Upgrade the installed SDK via pip. Returns True on success.
|
|
220
237
|
|
|
221
|
-
|
|
222
|
-
|
|
238
|
+
Tries candidates in order (detected URL → SSH → token-HTTPS → PyPI),
|
|
239
|
+
stopping at the first success.
|
|
240
|
+
"""
|
|
241
|
+
for url in _install_candidates():
|
|
242
|
+
if _pip_install(url):
|
|
243
|
+
return True
|
|
244
|
+
return False
|
|
223
245
|
|
|
224
246
|
|
|
225
247
|
def auto_update(quiet: bool = False) -> tuple[bool, str, str | None]:
|
|
@@ -274,6 +296,25 @@ def auto_update_and_reexec(module: str, argv: list[str]) -> None:
|
|
|
274
296
|
if os.getenv("AGENTOS_NO_UPDATE") or os.getenv("_AGENTOS_REEXEC"):
|
|
275
297
|
return
|
|
276
298
|
updated, _cur, _latest = auto_update(quiet=False)
|
|
299
|
+
|
|
300
|
+
# A self-upgrade of a RUNNING package can land broken on Windows (the active
|
|
301
|
+
# agentos-auto.exe / loaded files can't be replaced mid-pip — pip uninstalls
|
|
302
|
+
# the old then can't write the new, leaving sdk partial or removed). NEVER run
|
|
303
|
+
# on a corrupt install: verify it imports, repair (force-reinstall) if not, and
|
|
304
|
+
# abort with manual instructions only if repair also fails.
|
|
305
|
+
if not _verify_install():
|
|
306
|
+
print("⚠ the SDK looks incomplete after update — repairing …")
|
|
307
|
+
if not _repair_install():
|
|
308
|
+
print(
|
|
309
|
+
"error: the SDK self-update left the install broken and auto-repair "
|
|
310
|
+
"failed.\n reinstall it manually, then re-run agentos-auto:\n"
|
|
311
|
+
f' "{sys.executable}" -m pip install --force-reinstall '
|
|
312
|
+
f'"git+https://github.com/{_GITHUB_REPO}.git@{_sdk_branch()}"'
|
|
313
|
+
)
|
|
314
|
+
sys.exit(1)
|
|
315
|
+
print("✓ repaired.")
|
|
316
|
+
updated = True # repaired → restart on the fixed install
|
|
317
|
+
|
|
277
318
|
if not updated:
|
|
278
319
|
return
|
|
279
320
|
print("restarting on the updated version …\n")
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/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
|