forgexa-cli 1.18.9__tar.gz → 1.18.10__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.18.9
3
+ Version: 1.18.10
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.18.9"
2
+ __version__ = "1.18.10"
@@ -555,7 +555,7 @@ except (ImportError, ModuleNotFoundError):
555
555
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
556
556
  # Kept in sync with pyproject.toml version via bump-version.sh.
557
557
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
558
- DAEMON_VERSION = "1.18.9"
558
+ DAEMON_VERSION = "1.18.10"
559
559
 
560
560
 
561
561
  def _detect_client_type() -> str:
@@ -1716,6 +1716,13 @@ class WorkspaceManager:
1716
1716
  """Register an SSH private key for a project."""
1717
1717
  self._ssh_keys[project_key] = ssh_private_key
1718
1718
 
1719
+ @staticmethod
1720
+ def _summarize_git_error(error: Exception) -> str:
1721
+ """Return a compact Git error without exposing embedded HTTPS credentials."""
1722
+ detail = " ".join(str(error).split())
1723
+ detail = re.sub(r"(https?://)[^/@\s]+@", r"\1***@", detail)
1724
+ return detail[:500]
1725
+
1719
1726
  def _migrate_legacy_project_dirs(self) -> None:
1720
1727
  """Move project directories from root/ to root/projects/ (one-time migration).
1721
1728
 
@@ -3042,6 +3049,7 @@ class WorkspaceManager:
3042
3049
  # Retry up to 2 times with short delay for cross-runtime timing issues
3043
3050
  # (analysis daemon may have just pushed the branch).
3044
3051
  branch_exists_remote = False
3052
+ last_branch_fetch_error: str | None = None
3045
3053
  for _check_attempt in range(3 if not fresh_start else 1):
3046
3054
  try:
3047
3055
  check_proc = await asyncio.create_subprocess_exec(
@@ -3070,8 +3078,8 @@ class WorkspaceManager:
3070
3078
  timeout=settings.GIT_FETCH_TIMEOUT,
3071
3079
  project_key=project_key,
3072
3080
  )
3073
- except RuntimeError:
3074
- pass
3081
+ except RuntimeError as exc:
3082
+ last_branch_fetch_error = self._summarize_git_error(exc)
3075
3083
 
3076
3084
  # Fresh analysis with an existing remote branch that has implementation commits:
3077
3085
  # delete the remote branch so the agent starts from a clean slate.
@@ -3138,10 +3146,16 @@ class WorkspaceManager:
3138
3146
 
3139
3147
  if expect_branch and not fresh_start and not branch_exists_remote:
3140
3148
  self._safe_rmtree(ws_path)
3149
+ fetch_detail = (
3150
+ f" Last branch fetch error: {last_branch_fetch_error}"
3151
+ if last_branch_fetch_error
3152
+ else ""
3153
+ )
3141
3154
  raise RuntimeError(
3142
3155
  f"Expected remote branch '{branch_name}' to exist, but it could not be "
3143
3156
  "fetched or resolved. Refusing to create a continuation workspace from "
3144
3157
  f"origin/{default_branch} because that risks overwriting requirement-scoped history."
3158
+ f"{fetch_detail}"
3145
3159
  )
3146
3160
 
3147
3161
  if branch_exists_remote and not fresh_start:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.18.9
3
+ Version: 1.18.10
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.18.9"
3
+ version = "1.18.10"
4
4
  description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
5
5
  requires-python = ">=3.9"
6
6
  license = "MIT"
@@ -5,7 +5,7 @@ import io
5
5
  import json
6
6
  from pathlib import Path
7
7
  from types import SimpleNamespace
8
- from unittest.mock import MagicMock
8
+ from unittest.mock import AsyncMock, MagicMock
9
9
 
10
10
  import httpx
11
11
  import pytest
@@ -196,6 +196,49 @@ def test_httpx_recovery_hints_use_platform_specific_upgrade_commands() -> None:
196
196
  assert linux_hints[0] == "python3 -m pip install --upgrade forgexa-cli"
197
197
 
198
198
 
199
+ @pytest.mark.asyncio
200
+ async def test_workspace_reports_last_expected_branch_fetch_error(
201
+ tmp_path: Path,
202
+ monkeypatch: pytest.MonkeyPatch,
203
+ ) -> None:
204
+ manager = daemon.WorkspaceManager(str(tmp_path / "daemon-root"))
205
+ project_dir = tmp_path / "project"
206
+ (project_dir / "_main").mkdir(parents=True)
207
+
208
+ class DummyProc:
209
+ returncode = 1
210
+
211
+ async def communicate(self):
212
+ return b"", b""
213
+
214
+ async def fake_git(*args, **kwargs):
215
+ if args[:2] == ("fetch", "origin") and args[2].startswith("feature/SI-647:"):
216
+ raise RuntimeError("Permission denied (publickey).")
217
+ return ""
218
+
219
+ manager._git = AsyncMock(side_effect=fake_git)
220
+ monkeypatch.setattr(
221
+ daemon.asyncio,
222
+ "create_subprocess_exec",
223
+ AsyncMock(return_value=DummyProc()),
224
+ )
225
+ monkeypatch.setattr(daemon.asyncio, "sleep", AsyncMock())
226
+
227
+ with pytest.raises(
228
+ RuntimeError,
229
+ match="Last branch fetch error: Permission denied \\(publickey\\)\\.",
230
+ ):
231
+ await manager._create_worktree_impl(
232
+ project_dir,
233
+ "ssh://git@example.com/repo.git",
234
+ "develop",
235
+ "workflow-keep-branch",
236
+ "feature/SI-647",
237
+ project_key="SI",
238
+ expect_branch=True,
239
+ )
240
+
241
+
199
242
  def test_normalize_target_version_accepts_plain_and_v_prefixed_tags() -> None:
200
243
  assert main._normalize_target_version("1.12.3") == "1.12.3"
201
244
  assert main._normalize_target_version("v1.12.3") == "1.12.3"
File without changes
File without changes