Summary
is_git_url() in openhands/sdk/git/utils.py does not recognize the ssh:// URL scheme, so a plugin/extension source like:
ssh://git@bitbucket.example.com:7999/team/repo.git
drops through parse_extension_source() and fails at parse time with the misleading error:
ExtensionFetchError: Unable to parse extension source: ssh://git@bitbucket.example.com:7999/team/repo.git
The SCP-style spelling of the same repo (git@bitbucket.example.com:team/repo.git) is recognized and proceeds to git clone. So today the two equivalent SSH spellings behave inconsistently: the scp-form is fetched, the ssh://-scheme form is rejected as "unparseable" before any clone is attempted.
⚠️ How narrow is this? (please read before prioritizing)
This is a deliberately small edge case. It only matters for a user who is doing all of the following:
- Using a private plugin/extension repo reachable only over SSH (not HTTPS), AND
- Spelling the source with the
ssh:// scheme specifically (the scp-style git@host:... form already works), AND
- Has manually provisioned an SSH key into the sandbox/runtime (e.g. started a sandbox, injected the SSH credential, then attached a conversation whose agent/plugins clone over SSH).
For the overwhelming majority of users, the supported and recommended path is an HTTPS URL with a token (which, combined with #3755, can reference a secret: https://x-token-auth:${TOKEN}@host/repo.git). This issue does not add SSH key provisioning, and it does not make SSH "just work" for hosted/SaaS users who have not injected a key. It only fixes the parse-level inconsistency so that an already-working SSH setup accepts the ssh:// spelling too.
Root cause
is_git_url() checks https://, http://, the scp regex ^[\w.-]+@[\w.-]+:, git://, and file:// — but never the ssh:// scheme. ssh://git@host:port/path matches none of those (the scp regex requires <user>@<host>: with no scheme prefix), so the source is classified as neither git nor local and raises Unable to parse.
normalize_git_url() and extract_repo_name() already handle the ssh:// string correctly; only the recognition check is missing.
Proposed fix
Add a single branch to is_git_url() recognizing the ssh:// scheme, so it is classified as SourceType.GIT and handed to the normal cached-clone path (which then succeeds iff the runtime has a usable SSH key — same as the scp-style form today). No behavior change for HTTPS/HTTPS-with-token, local paths, or hosted users without a key.
Acceptance criteria
is_git_url("ssh://git@host:7999/team/repo.git") returns True.
parse_extension_source("ssh://...") returns SourceType.GIT (no "Unable to parse").
- Regression test covering the
ssh:// scheme (incl. an explicit port).
This issue was created by an AI agent (OpenHands) on behalf of @jpshackelford.
Summary
is_git_url()inopenhands/sdk/git/utils.pydoes not recognize thessh://URL scheme, so a plugin/extensionsourcelike:drops through
parse_extension_source()and fails at parse time with the misleading error:The SCP-style spelling of the same repo (
git@bitbucket.example.com:team/repo.git) is recognized and proceeds togit clone. So today the two equivalent SSH spellings behave inconsistently: the scp-form is fetched, thessh://-scheme form is rejected as "unparseable" before any clone is attempted.This is a deliberately small edge case. It only matters for a user who is doing all of the following:
ssh://scheme specifically (the scp-stylegit@host:...form already works), ANDFor the overwhelming majority of users, the supported and recommended path is an HTTPS URL with a token (which, combined with #3755, can reference a secret:
https://x-token-auth:${TOKEN}@host/repo.git). This issue does not add SSH key provisioning, and it does not make SSH "just work" for hosted/SaaS users who have not injected a key. It only fixes the parse-level inconsistency so that an already-working SSH setup accepts thessh://spelling too.Root cause
is_git_url()checkshttps://,http://, the scp regex^[\w.-]+@[\w.-]+:,git://, andfile://— but never thessh://scheme.ssh://git@host:port/pathmatches none of those (the scp regex requires<user>@<host>:with no scheme prefix), so the source is classified as neither git nor local and raisesUnable to parse.normalize_git_url()andextract_repo_name()already handle thessh://string correctly; only the recognition check is missing.Proposed fix
Add a single branch to
is_git_url()recognizing thessh://scheme, so it is classified asSourceType.GITand handed to the normal cached-clone path (which then succeeds iff the runtime has a usable SSH key — same as the scp-style form today). No behavior change for HTTPS/HTTPS-with-token, local paths, or hosted users without a key.Acceptance criteria
is_git_url("ssh://git@host:7999/team/repo.git")returnsTrue.parse_extension_source("ssh://...")returnsSourceType.GIT(no "Unable to parse").ssh://scheme (incl. an explicit port).This issue was created by an AI agent (OpenHands) on behalf of @jpshackelford.