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.
Files changed (38) hide show
  1. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/handlers.py +35 -15
  7. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/local_exec.py +17 -1
  8. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/updater.py +72 -31
  9. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/README.md +0 -0
  10. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  11. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  12. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  13. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/requires.txt +0 -0
  14. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  15. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/__init__.py +0 -0
  16. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/files.py +0 -0
  17. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/process.py +0 -0
  18. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/collectors/screenshot.py +0 -0
  19. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/config.py +0 -0
  20. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/crucial.py +0 -0
  21. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/exceptions.py +0 -0
  22. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/git_ops.py +0 -0
  23. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/graphify.py +0 -0
  24. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/hooks/__init__.py +0 -0
  25. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/hooks/crucial_guard.py +0 -0
  26. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/__init__.py +0 -0
  27. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/ipc/electron_bridge.py +0 -0
  28. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/__init__.py +0 -0
  29. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/envelope.py +0 -0
  30. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/requests.py +0 -0
  31. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/responses.py +0 -0
  32. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/models/snapshots.py +0 -0
  33. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/py.typed +0 -0
  34. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/run_auto.py +0 -0
  35. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/sse.py +0 -0
  36. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/test.py +0 -0
  37. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/sdk/test_pipeline.py +0 -0
  38. {devops_bot_sdk-1.4.14 → devops_bot_sdk-1.4.19}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.14
3
+ Version: 1.4.19
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.14
3
+ Version: 1.4.19
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devops-bot-sdk"
7
- version = "1.4.14"
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.14
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.14"
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.14"
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
- parts.append(
208
- "Implement all of the above in the current project directory. "
209
- "Create and edit every file needed for a complete, working result.\n"
210
- "Git for THIS repo is handled for you: a feature branch is already checked "
211
- "out, and the system will commit, push, and open the PR after you finish — "
212
- "so do NOT create branches, commit, push, or open a PR for the current repo. "
213
- "(You may still clone and open PRs for OTHER repositories the instructions name.)"
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 = _build_local_prompt(
640
- pending["user_input"], pending.get("structured_plan"),
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, prompt=prompt, gh_token=gh_token,
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", prompt,
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 self_update() -> bool:
187
- """Upgrade the installed SDK via pip. Returns True on success.
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
- def _pip_upgrade(url: str) -> bool:
199
- try:
200
- subprocess.run(
201
- [sys.executable, "-m", "pip", "install",
202
- "--upgrade", "--quiet", *flags, url],
203
- check=True, capture_output=True,
204
- )
205
- return True
206
- except Exception: # noqa: BLE001
207
- return False
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
- # 3: token (env first, then backend) HTTPS upgrade from the target branch
215
- gh_token = _fetch_github_token()
216
- if gh_token:
217
- https_url = f"git+https://{gh_token}@github.com/{_GITHUB_REPO}.git@{_sdk_branch()}"
218
- if _pip_upgrade(https_url):
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
- # 4: bare PyPI name (last resort)
222
- return _pip_upgrade(_DIST)
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")