forgexa-cli 1.27.2__tar.gz → 1.27.3__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 (27) hide show
  1. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/PKG-INFO +1 -1
  2. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/daemon.py +9 -8
  4. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/main.py +13 -0
  5. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/PKG-INFO +1 -1
  6. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/pyproject.toml +1 -1
  7. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_local_bind_commands.py +38 -0
  8. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/README.md +0 -0
  9. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/_build_config.py +0 -0
  10. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/_local_bind.py +0 -0
  11. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/agent_core.py +0 -0
  12. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/autoupgrade.py +0 -0
  13. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli/py.typed +0 -0
  14. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  15. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  16. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/entry_points.txt +0 -0
  17. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/requires.txt +0 -0
  18. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/forgexa_cli.egg-info/top_level.txt +0 -0
  19. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/setup.cfg +0 -0
  20. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_auth_and_runtime_commands.py +0 -0
  21. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_autoupgrade.py +0 -0
  22. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_check_command.py +0 -0
  23. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_expiry_warnings_and_revoke.py +0 -0
  24. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_runtime_credentials.py +0 -0
  25. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_session_credentials.py +0 -0
  26. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_silent_install.py +0 -0
  27. {forgexa_cli-1.27.2 → forgexa_cli-1.27.3}/tests/test_upgrade_observability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.27.2
3
+ Version: 1.27.3
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.27.2"
2
+ __version__ = "1.27.3"
@@ -909,7 +909,7 @@ except (ImportError, ModuleNotFoundError):
909
909
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
910
910
  # Kept in sync with pyproject.toml version via bump-version.sh.
911
911
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
912
- DAEMON_VERSION = "1.27.2"
912
+ DAEMON_VERSION = "1.27.3"
913
913
 
914
914
 
915
915
  def _detect_client_type() -> str:
@@ -1925,13 +1925,14 @@ async def _validate_local_workspace_binding(binding: dict[str, Any], runtime_id:
1925
1925
  entry = _load_local_workspaces_mapping().get(binding_id)
1926
1926
  if not binding_id or not isinstance(entry, dict):
1927
1927
  return "mapping_missing"
1928
- if (
1929
- str(entry.get("project_id") or "") != str(binding.get("project_id") or "")
1930
- or str(entry.get("user_id") or "") != str(binding.get("user_id") or "")
1931
- or str(entry.get("runtime_id") or "") != str(runtime_id)
1932
- or str(entry.get("repo_identity") or "") != str(binding.get("repo_identity") or "")
1933
- ):
1934
- return "mapping_mismatch"
1928
+ if str(entry.get("project_id") or "") != str(binding.get("project_id") or ""):
1929
+ return "mapping_project_mismatch"
1930
+ if str(entry.get("user_id") or "") != str(binding.get("user_id") or ""):
1931
+ return "mapping_user_mismatch"
1932
+ if str(entry.get("runtime_id") or "") != str(runtime_id):
1933
+ return "mapping_runtime_mismatch"
1934
+ if str(entry.get("repo_identity") or "") != str(binding.get("repo_identity") or ""):
1935
+ return "mapping_repository_mismatch"
1935
1936
 
1936
1937
  local_path = Path(str(entry.get("local_path") or "")).expanduser()
1937
1938
  try:
@@ -2857,6 +2857,19 @@ def cmd_project_bind_local(args: argparse.Namespace) -> None:
2857
2857
  if not binding_id:
2858
2858
  print("Error: failed to create local workspace binding", file=sys.stderr)
2859
2859
  sys.exit(1)
2860
+ expected_identity = binding.get("repo_identity")
2861
+ if isinstance(expected_identity, str) and expected_identity != identity:
2862
+ try:
2863
+ _delete(f"/projects/{args.project}/local-workspace-bindings/{binding_id}")
2864
+ except Exception:
2865
+ pass
2866
+ print(
2867
+ "Error: this folder's Git remote does not match the selected Forgexa project's repository "
2868
+ f"(project: {expected_identity}, this folder: {identity}). "
2869
+ "Open the matching folder or update the project's repository URL.",
2870
+ file=sys.stderr,
2871
+ )
2872
+ sys.exit(1)
2860
2873
 
2861
2874
  mapping = load_local_workspaces_mapping()
2862
2875
  local_path_str = str(local_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.27.2
3
+ Version: 1.27.3
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.27.2"
3
+ version = "1.27.3"
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"
@@ -152,6 +152,44 @@ def test_bind_local_rejects_unrecognized_remote_url(tmp_path: Path, monkeypatch:
152
152
  main.cmd_project_bind_local(args)
153
153
 
154
154
 
155
+ def test_bind_local_rejects_project_repository_mismatch_before_writing_mapping(
156
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
157
+ ) -> None:
158
+ monkeypatch.setenv("HOME", str(tmp_path))
159
+ repo = tmp_path / "other-project"
160
+ _init_git_repo_with_remote(repo, "https://github.com/acme/other.git")
161
+
162
+ monkeypatch.setattr(main, "_get_runtimes", lambda: [
163
+ {"id": "runtime-1", "pool_type": "personal", "daemon_id": "d1", "device_name": "laptop"},
164
+ ])
165
+ monkeypatch.setattr(main, "_get", lambda path: {"id": "user-1"} if path == "/auth/me" else None)
166
+
167
+ posts: list[tuple[str, dict]] = []
168
+ deletes: list[str] = []
169
+
170
+ def fake_post(path: str, data=None):
171
+ posts.append((path, data or {}))
172
+ assert path == "/projects/project-1/local-workspace-bindings"
173
+ return {
174
+ "id": "binding-1",
175
+ "status": "pending",
176
+ "repo_identity": "github.com/acme/widgets",
177
+ }
178
+
179
+ monkeypatch.setattr(main, "_post", fake_post)
180
+ monkeypatch.setattr(main, "_delete", lambda path: deletes.append(path))
181
+
182
+ args = SimpleNamespace(project="project-1", path=str(repo), runtime=None)
183
+ with pytest.raises(SystemExit):
184
+ main.cmd_project_bind_local(args)
185
+
186
+ assert posts == [
187
+ ("/projects/project-1/local-workspace-bindings", {"runtime_id": "runtime-1"}),
188
+ ]
189
+ assert deletes == ["/projects/project-1/local-workspace-bindings/binding-1"]
190
+ assert not (tmp_path / ".forgexa" / "local-workspaces.json").exists()
191
+
192
+
155
193
  def test_bind_local_requires_disambiguation_with_multiple_personal_runtimes(
156
194
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
157
195
  ) -> None:
File without changes
File without changes