mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-21 12:12:22 +02:00
[eric] split: carve out workspace git helpers
This commit is contained in:
@@ -42,6 +42,7 @@ from backend.apps.agents.session_store import (
|
||||
build_search_text,
|
||||
)
|
||||
from backend.apps.agents.cloud_sync import _sync_session_close
|
||||
from backend.apps.agents.workspace_git import _detect_git_identity, _ensure_cwd_git_repo
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -153,129 +154,6 @@ def get_all_tool_names() -> list[str]:
|
||||
return builtin_tools + mcp_names
|
||||
|
||||
|
||||
def _ensure_cwd_git_repo(cwd: str, home: str | None = None) -> None:
|
||||
"""Idempotently make `cwd` into a git repo with a valid HEAD.
|
||||
|
||||
The CLI's built-in Agent tool uses `isolation: "worktree"` to spawn
|
||||
subagents, which runs `git rev-parse HEAD` + `git worktree add`. If
|
||||
cwd isn't a git repo, or is a repo with no commits yet, that fails
|
||||
with "worktree/base-branch metadata is broken for isolation" or
|
||||
"repo doesn't have a valid HEAD yet". We silently init a minimal
|
||||
repo with one empty commit so worktree add always has something to
|
||||
anchor on.
|
||||
|
||||
Safe to call on every request, does nothing if cwd is already a
|
||||
valid repo (real project, previous init, or inside a parent repo).
|
||||
"""
|
||||
try:
|
||||
home = home or os.path.expanduser("~")
|
||||
cwd_abs = os.path.abspath(cwd)
|
||||
risky_roots = {
|
||||
os.path.abspath(home),
|
||||
"/",
|
||||
os.path.abspath(os.path.dirname(home)), # e.g. /Users
|
||||
}
|
||||
if cwd_abs in risky_roots:
|
||||
return
|
||||
if not os.path.isdir(cwd):
|
||||
return
|
||||
|
||||
import subprocess as _sp_git
|
||||
# Case A: cwd is inside some git repo (possibly parent). Verify
|
||||
# HEAD resolves. If the enclosing repo is broken (e.g. a stray
|
||||
# `.git` in $HOME with no commits, which makes workspaces
|
||||
# under ~/.openswarm/workspaces/ inherit a broken HEAD), we
|
||||
# need to init a fresh repo AT cwd so it shadows the parent.
|
||||
_inside = _sp_git.run(
|
||||
["git", "rev-parse", "--is-inside-work-tree"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.PIPE, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _inside.returncode == 0 and b"true" in _inside.stdout:
|
||||
# Check HEAD resolves (has at least one commit).
|
||||
_head = _sp_git.run(
|
||||
["git", "rev-parse", "--verify", "HEAD"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _head.returncode == 0:
|
||||
return # parent repo is healthy, leave it alone
|
||||
# Parent repo exists but HEAD is broken.
|
||||
if os.path.isdir(os.path.join(cwd, ".git")):
|
||||
# .git is directly here, commit to fix it.
|
||||
_sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
return
|
||||
# .git is in a parent dir (broken home-dir repo, etc.).
|
||||
# Init our own repo at cwd so it shadows the broken parent.
|
||||
# Fall through to Case B.
|
||||
|
||||
# Case B: cwd is not a git repo at all (or parent is broken):
|
||||
# init + empty commit here.
|
||||
_sp_git.run(
|
||||
["git", "init", "-q", "-b", "main"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
_sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
except Exception as _e:
|
||||
logger.info(f"[agent-cwd] git init skipped: {_e}")
|
||||
|
||||
|
||||
def _detect_git_identity(cwd: str) -> tuple[str | None, str | None]:
|
||||
"""Resolve the origin remote and current branch for `cwd`.
|
||||
|
||||
Used to label sessions in the session list ("Agent on owner/repo
|
||||
@ branch") and to keep a resumed session pinned to the same project
|
||||
even after the user `cd`'s elsewhere. Returns (None, None) for
|
||||
non-git cwds, detached HEADs, repos without an origin, or any
|
||||
subprocess failure. Credentials in the URL are stripped so a
|
||||
`https://user:token@host/...` remote becomes `https://host/...`.
|
||||
"""
|
||||
if not cwd or not os.path.isdir(cwd):
|
||||
return (None, None)
|
||||
try:
|
||||
import subprocess as _sp
|
||||
url_proc = _sp.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
)
|
||||
repo_url: str | None = None
|
||||
if url_proc.returncode == 0:
|
||||
raw = url_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw:
|
||||
if "://" in raw:
|
||||
scheme, _, rest = raw.partition("://")
|
||||
if "@" in rest:
|
||||
rest = rest.split("@", 1)[1]
|
||||
repo_url = f"{scheme}://{rest}"
|
||||
else:
|
||||
repo_url = raw
|
||||
branch_proc = _sp.run(
|
||||
["git", "branch", "--show-current"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
)
|
||||
branch_name: str | None = None
|
||||
if branch_proc.returncode == 0:
|
||||
raw_b = branch_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw_b:
|
||||
branch_name = raw_b
|
||||
return (repo_url, branch_name)
|
||||
except Exception:
|
||||
return (None, None)
|
||||
|
||||
|
||||
class AgentManager:
|
||||
def __init__(self):
|
||||
self.sessions: dict[str, AgentSession] = {}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _ensure_cwd_git_repo(cwd: str, home: str | None = None) -> None:
|
||||
"""Idempotently make `cwd` into a git repo with a valid HEAD.
|
||||
|
||||
The CLI's built-in Agent tool uses `isolation: "worktree"` to spawn
|
||||
subagents, which runs `git rev-parse HEAD` + `git worktree add`. If
|
||||
cwd isn't a git repo, or is a repo with no commits yet, that fails
|
||||
with "worktree/base-branch metadata is broken for isolation" or
|
||||
"repo doesn't have a valid HEAD yet". We silently init a minimal
|
||||
repo with one empty commit so worktree add always has something to
|
||||
anchor on.
|
||||
|
||||
Safe to call on every request, does nothing if cwd is already a
|
||||
valid repo (real project, previous init, or inside a parent repo).
|
||||
"""
|
||||
try:
|
||||
home = home or os.path.expanduser("~")
|
||||
cwd_abs = os.path.abspath(cwd)
|
||||
risky_roots = {
|
||||
os.path.abspath(home),
|
||||
"/",
|
||||
os.path.abspath(os.path.dirname(home)), # e.g. /Users
|
||||
}
|
||||
if cwd_abs in risky_roots:
|
||||
return
|
||||
if not os.path.isdir(cwd):
|
||||
return
|
||||
|
||||
import subprocess as _sp_git
|
||||
# Case A: cwd is inside some git repo (possibly parent). Verify
|
||||
# HEAD resolves. If the enclosing repo is broken (e.g. a stray
|
||||
# `.git` in $HOME with no commits, which makes workspaces
|
||||
# under ~/.openswarm/workspaces/ inherit a broken HEAD), we
|
||||
# need to init a fresh repo AT cwd so it shadows the parent.
|
||||
_inside = _sp_git.run(
|
||||
["git", "rev-parse", "--is-inside-work-tree"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.PIPE, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _inside.returncode == 0 and b"true" in _inside.stdout:
|
||||
# Check HEAD resolves (has at least one commit).
|
||||
_head = _sp_git.run(
|
||||
["git", "rev-parse", "--verify", "HEAD"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _head.returncode == 0:
|
||||
return # parent repo is healthy, leave it alone
|
||||
# Parent repo exists but HEAD is broken.
|
||||
if os.path.isdir(os.path.join(cwd, ".git")):
|
||||
# .git is directly here, commit to fix it.
|
||||
_sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
return
|
||||
# .git is in a parent dir (broken home-dir repo, etc.).
|
||||
# Init our own repo at cwd so it shadows the broken parent.
|
||||
# Fall through to Case B.
|
||||
|
||||
# Case B: cwd is not a git repo at all (or parent is broken):
|
||||
# init + empty commit here.
|
||||
_sp_git.run(
|
||||
["git", "init", "-q", "-b", "main"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
_sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
except Exception as _e:
|
||||
logger.info(f"[agent-cwd] git init skipped: {_e}")
|
||||
|
||||
|
||||
def _detect_git_identity(cwd: str) -> tuple[str | None, str | None]:
|
||||
"""Resolve the origin remote and current branch for `cwd`.
|
||||
|
||||
Used to label sessions in the session list ("Agent on owner/repo
|
||||
@ branch") and to keep a resumed session pinned to the same project
|
||||
even after the user `cd`'s elsewhere. Returns (None, None) for
|
||||
non-git cwds, detached HEADs, repos without an origin, or any
|
||||
subprocess failure. Credentials in the URL are stripped so a
|
||||
`https://user:token@host/...` remote becomes `https://host/...`.
|
||||
"""
|
||||
if not cwd or not os.path.isdir(cwd):
|
||||
return (None, None)
|
||||
try:
|
||||
import subprocess as _sp
|
||||
url_proc = _sp.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
)
|
||||
repo_url: str | None = None
|
||||
if url_proc.returncode == 0:
|
||||
raw = url_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw:
|
||||
if "://" in raw:
|
||||
scheme, _, rest = raw.partition("://")
|
||||
if "@" in rest:
|
||||
rest = rest.split("@", 1)[1]
|
||||
repo_url = f"{scheme}://{rest}"
|
||||
else:
|
||||
repo_url = raw
|
||||
branch_proc = _sp.run(
|
||||
["git", "branch", "--show-current"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
)
|
||||
branch_name: str | None = None
|
||||
if branch_proc.returncode == 0:
|
||||
raw_b = branch_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw_b:
|
||||
branch_name = raw_b
|
||||
return (repo_url, branch_name)
|
||||
except Exception:
|
||||
return (None, None)
|
||||
Reference in New Issue
Block a user