collab-runtime 0.6.1__tar.gz → 0.6.2__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 (32) hide show
  1. {collab_runtime-0.6.1/collab_runtime.egg-info → collab_runtime-0.6.2}/PKG-INFO +1 -1
  2. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/lock_client.py +39 -7
  3. {collab_runtime-0.6.1 → collab_runtime-0.6.2/collab_runtime.egg-info}/PKG-INFO +1 -1
  4. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/pyproject.toml +1 -1
  5. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/LICENSE +0 -0
  6. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/README.md +0 -0
  7. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/__init__.py +0 -0
  8. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/__main__.py +0 -0
  9. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/agent_hooks.py +0 -0
  10. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/agent_identity.py +0 -0
  11. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/dashboard/dashboard-format.js +0 -0
  12. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/dashboard/index.html +0 -0
  13. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/dashboard_server.py +0 -0
  14. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/errors.py +0 -0
  15. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/githooks.py +0 -0
  16. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/hook_templates/commit-msg +0 -0
  17. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/hook_templates/post-commit +0 -0
  18. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/hook_templates/pre-commit +0 -0
  19. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/hook_templates/pre-push +0 -0
  20. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/live_locks_watcher.py +0 -0
  21. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/logging_config.py +0 -0
  22. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/main.py +0 -0
  23. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/platform_probe.py +0 -0
  24. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/safe_subprocess.py +0 -0
  25. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab/subprocess_bridge.py +0 -0
  26. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab_runtime.egg-info/SOURCES.txt +0 -0
  27. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab_runtime.egg-info/dependency_links.txt +0 -0
  28. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab_runtime.egg-info/entry_points.txt +0 -0
  29. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab_runtime.egg-info/requires.txt +0 -0
  30. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/collab_runtime.egg-info/top_level.txt +0 -0
  31. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/docs/pypi/README.md +0 -0
  32. {collab_runtime-0.6.1 → collab_runtime-0.6.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: collab-runtime
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Collaborative file locking runtime
5
5
  Author-email: KirilMT <kiril.mt95@gmail.com>
6
6
  License-Expression: MIT
@@ -1118,6 +1118,11 @@ class LockClient:
1118
1118
  def release(self, file_path: str) -> Tuple[bool, str]:
1119
1119
  """Release a lock on file_path owned by this developer.
1120
1120
 
1121
+ When called by a **human** (``agent_id`` is ``None``), any lock owned by this
1122
+ ``developer_id`` is released regardless of which agent claimed it — the human is
1123
+ in charge of all their agents. When called by an **agent** (``agent_id`` is
1124
+ set), only locks claimed by that specific agent identity are released.
1125
+
1121
1126
  Returns (success: bool, message: str).
1122
1127
  """
1123
1128
  # If ephemeral, nothing was persisted so there's nothing to delete.
@@ -1127,16 +1132,45 @@ class LockClient:
1127
1132
  )
1128
1133
  return True, "ephemeral-released"
1129
1134
 
1135
+ norm = self._normalize_file_path(file_path)
1130
1136
  client = self._require_client()
1137
+
1138
+ # Pre-check: verify a lock row exists for this file *and* belongs to
1139
+ # this developer before attempting the DELETE. PostgREST returns
1140
+ # 204 No Content even when zero rows are deleted, so without this
1141
+ # guard the CLI would falsely report "✓ released" for locks that
1142
+ # belong to another developer or do not exist at all.
1143
+ try:
1144
+ check_res = _retry_on_network_error(
1145
+ lambda: client.table("file_locks")
1146
+ .select("developer_id")
1147
+ .eq("file_path", norm)
1148
+ .execute()
1149
+ )
1150
+ except Exception as e:
1151
+ return False, f"API Error: {e}"
1152
+ _st, rows, _err = self._parse_response(check_res)
1153
+ if not rows or not isinstance(rows, list) or len(rows) == 0:
1154
+ return False, f"No lock found for: {file_path}"
1155
+ lock_owner = rows[0].get("developer_id")
1156
+ if lock_owner != self.developer_id:
1157
+ return False, (
1158
+ f"Permission denied: {file_path} is locked by @{lock_owner or '?'}. "
1159
+ "Use `collab force-release` if you have admin credentials."
1160
+ )
1161
+
1131
1162
  try:
1132
- norm = self._normalize_file_path(file_path)
1133
1163
  delete_query = (
1134
1164
  client.table("file_locks")
1135
1165
  .delete()
1136
1166
  .eq("file_path", norm)
1137
1167
  .eq("developer_id", self.developer_id)
1138
1168
  )
1139
- delete_query = self._apply_agent_scope(delete_query)
1169
+ # Human (agent_id is None): developer-scoped — release any agent's
1170
+ # lock. Agent (agent_id is set): identity-scoped — only release
1171
+ # this specific agent's lock.
1172
+ if self.agent_id is not None:
1173
+ delete_query = delete_query.eq("agent_id", self.agent_id)
1140
1174
  res = _retry_on_network_error(lambda: delete_query.execute())
1141
1175
  except Exception as e:
1142
1176
  return False, f"API Error: {e}"
@@ -1145,9 +1179,7 @@ class LockClient:
1145
1179
  if error:
1146
1180
  return False, f"API Error: {error}"
1147
1181
  if status in (200, 204) or data is not None:
1148
- logger.info(
1149
- "🔓 [RELEASED] %s — lock released", self._normalize_file_path(file_path)
1150
- )
1182
+ logger.info("🔓 [RELEASED] %s — lock released", norm)
1151
1183
  return True, "released"
1152
1184
  return False, "No lock released (not owner or lock does not exist)"
1153
1185
 
@@ -2008,7 +2040,7 @@ class LockClient:
2008
2040
  try:
2009
2041
  pid = self._read_pid(strict=True)
2010
2042
  except PidParseError as exc:
2011
- print(f"Lock watcher status unavailable: {exc.message}")
2043
+ print(f"ℹ️ Lock watcher status unavailable: {exc.message}")
2012
2044
  return False
2013
2045
  local_only_mode = bool(getattr(self, "local_only", False))
2014
2046
  if pid and self._is_process_alive(pid):
@@ -2137,7 +2169,7 @@ class LockClient:
2137
2169
  return True
2138
2170
  except (ValueError, OSError):
2139
2171
  pass
2140
- print("Lock watcher is NOT running.")
2172
+ print("ℹ️ Lock watcher is not running.")
2141
2173
  return False
2142
2174
 
2143
2175
  def cleanup_orphaned_processes(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: collab-runtime
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Collaborative file locking runtime
5
5
  Author-email: KirilMT <kiril.mt95@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "collab-runtime"
7
- version = "0.6.1"
7
+ version = "0.6.2"
8
8
  description = "Collaborative file locking runtime"
9
9
  readme = "docs/pypi/README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes