Skip to content

feat(plugin): add marketplace management commands#415

Draft
timon0305 wants to merge 2 commits intoOpenHands:mainfrom
timon0305:feat/plugin-marketplace-management
Draft

feat(plugin): add marketplace management commands#415
timon0305 wants to merge 2 commits intoOpenHands:mainfrom
timon0305:feat/plugin-marketplace-management

Conversation

@timon0305
Copy link

@timon0305 timon0305 commented Jan 26, 2026

PR: Add Plugin Marketplace Management (#411)

Summary

This PR implements the Plugin Marketplace Management feature as specified in issue #411. It allows users to configure plugin marketplaces (registries) that can be used to discover and install plugins.

Changes

New Files Created

File Description
openhands_cli/plugins/__init__.py Plugin module initialization
openhands_cli/plugins/marketplace_storage.py JSON persistence for marketplace configurations with fetch/cache support
openhands_cli/plugins/marketplace_commands.py CLI command handlers for marketplace operations
openhands_cli/argparsers/plugin_parser.py Argument parser for plugin subcommands

Modified Files

File Changes
openhands_cli/locations.py Added MARKETPLACES_FILE constant
openhands_cli/argparsers/main_parser.py Wired plugin parser into main parser
openhands_cli/entrypoint.py Added plugin command handler

Commands Implemented

# Add a plugin marketplace (supports multiple source formats)
openhands plugin marketplace add <source> [--name NAME]

# Supported source formats:
#   - GitHub shorthand: company/plugins
#   - Explicit GitHub: github:owner/repo
#   - Git URL: https://gitlab.com/org/plugins.git
#   - Direct URL: https://example.com/marketplace.json

# List configured marketplaces
openhands plugin marketplace list

# Remove a marketplace by name
openhands plugin marketplace remove <name>

# Update marketplace indexes (fetch and cache)
openhands plugin marketplace update [<name>]

Key Features

1. Name-Based Identification

  • Marketplaces are identified by name (not URL)
  • Default names are auto-generated from source:
    • company/pluginscompany-plugins
    • github:owner/repoowner-repo
    • https://gitlab.com/org/plugins.gitplugins
    • https://example.com/marketplace.jsonmarketplace

2. GitHub Shorthand Parsing

  • Supports owner/repo format that expands to GitHub raw content URL
  • Also supports explicit github:owner/repo prefix

3. Marketplace Index Fetching

  • update command fetches the marketplace index from source
  • Indexes are cached locally in ~/.openhands/marketplace_cache/
  • Supports URL and GitHub sources (Git requires cloning, not yet supported)

4. Plugin Count Display

  • list command shows plugin count from cached indexes
  • Total plugin count displayed at bottom

Storage Format

Marketplaces (~/.openhands/marketplaces.json)

{
  "marketplaces": {
    "company-plugins": {
      "source": {
        "source": "github",
        "repo": "company/plugins"
      },
      "added_at": "2026-01-26T12:00:00",
      "last_updated": "2026-01-26T12:30:00",
      "auto_update": true
    }
  }
}

Cached Index (~/.openhands/marketplace_cache/<name>.json)

The fetched marketplace index JSON is cached locally for offline access.

CLI Help Output

$ openhands plugin marketplace --help
usage: openhands plugin marketplace [-h] {add,remove,list,update} ...

Manage plugin marketplaces (registries).

Marketplaces provide an index of available plugins. Supported source formats:
  - GitHub shorthand: owner/repo
  - Explicit GitHub: github:owner/repo
  - Git URL: https://gitlab.com/org/plugins.git
  - Direct URL: https://example.com/marketplace.json

Examples:

  # Add a marketplace using GitHub shorthand
  openhands plugin marketplace add company/plugins

  # Add with a custom name
  openhands plugin marketplace add company/plugins --name my-plugins

  # List all marketplaces
  openhands plugin marketplace list

  # Remove a marketplace by name
  openhands plugin marketplace remove company-plugins

  # Update all marketplace indexes
  openhands plugin marketplace update

  # Update a specific marketplace
  openhands plugin marketplace update company-plugins

positional arguments:
  {add,remove,list,update}
                        Marketplace commands
    add                 Add a plugin marketplace
    remove              Remove a plugin marketplace
    list                List all configured marketplaces
    update              Update marketplace indexes

options:
  -h, --help            show this help message and exit

Manual Testing Results

Source Parsing Tests

$ uv run python -c "
from openhands_cli.plugins.marketplace_storage import parse_source

tests = [
    'company/plugins',
    'github:owner/repo',
    'https://gitlab.com/org/plugins.git',
    'https://example.com/marketplace.json',
]

for source in tests:
    name, src = parse_source(source)
    print(f'{source}')
    print(f'  → name: \"{name}\", type: {src.source_type}')
"

company/plugins
  → name: "company-plugins", type: github
github:owner/repo
  → name: "owner-repo", type: github
https://gitlab.com/org/plugins.git
  → name: "plugins", type: git
https://example.com/marketplace.json
  → name: "marketplace", type: url

Add Marketplace

$ openhands plugin marketplace add company/plugins
Added marketplace 'company-plugins'
  Source: github:company/plugins

$ openhands plugin marketplace add https://httpbin.org/json --name test-httpbin
Added marketplace 'test-httpbin'
  Source: https://httpbin.org/json

List Marketplaces

$ openhands plugin marketplace list
                         Configured Plugin Marketplaces
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Name            ┃ Source           ┃ Plugins ┃ Auto Update ┃ Added      ┃ Last Updated ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ company-plugins │ github:company/… │       - │ Yes         │ 2026-01-26 │ -            │
│ test-httpbin    │ https://httpbin… │       - │ Yes         │ 2026-01-26 │ -            │
└─────────────────┴──────────────────┴─────────┴─────────────┴────────────┴──────────────┘

2 marketplace(s) configured. Plugins available: 0

Update Marketplace (Fetch Index)

$ openhands plugin marketplace update test-httpbin
Updating marketplace 'test-httpbin'...
  Updated: test-httpbin (0 plugins)
Successfully updated 1 marketplace(s).

$ openhands plugin marketplace list
                         Configured Plugin Marketplaces
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Name         ┃ Source      ┃ Plugins ┃ Auto Update ┃ Added      ┃ Last Updated ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ test-httpbin │ https://ht… │       0 │ Yes         │ 2026-01-26 │ 2026-01-26   │
└──────────────┴─────────────┴─────────┴─────────────┴────────────┴──────────────┘

1 marketplace(s) configured. Plugins available: 0

Update with Failed Fetch (404 Error)

$ openhands plugin marketplace update company-plugins
Updating marketplace 'company-plugins'...
  Failed: Failed to fetch marketplace index: HTTP 404 - Not Found
Failed to update 1 marketplace(s).

Remove Marketplace

$ openhands plugin marketplace remove company-plugins
Removed marketplace 'company-plugins'

Cached Index Verification

$ ls -la ~/.openhands/marketplace_cache/
total 12
drwxrwxr-x 2 odmin odmin 4096 Jan 26 20:11 .
drwxrwxr-x 7 odmin odmin 4096 Jan 26 20:11 ..
-rw-rw-r-- 1 odmin odmin  420 Jan 26 20:11 test-httpbin.json

$ cat ~/.openhands/marketplace_cache/test-httpbin.json
{
  "slideshow": {
    "author": "Yours Truly",
    ...
  }
}

Automated Test Results

$ uv run pytest tests/ -v --tb=short

--------------------------- snapshot report summary ----------------------------
13 snapshots passed.
================= 1144 passed, 3 warnings in 116.60s (0:01:56) =================

Test Plan

Marketplace Commands (#411)

  • openhands plugin marketplace add <source> adds marketplace with auto-generated name
  • openhands plugin marketplace add <source> --name <name> adds with custom name
  • openhands plugin marketplace list shows marketplaces with plugin counts
  • openhands plugin marketplace update fetches and caches marketplace indexes
  • openhands plugin marketplace update <name> updates specific marketplace
  • openhands plugin marketplace remove <name> removes marketplace by name
  • GitHub shorthand parsing (company/pluginsgithub:company/plugins)
  • Name-based identification (not URL-based)
  • Default name generation from source URL
  • All 1144 existing tests pass

Related Issues

Add  subcommands to manage plugin
marketplaces (registries) for discovering and installing plugins:

-  - Add a marketplace
-  - List configured marketplaces
-  - Remove a marketplace
-  - Update marketplace indexes

Marketplaces are stored in ~/.openhands/marketplaces.json

Implements OpenHands#411
- Use name as primary identifier instead of URL for all operations
- Add MarketplaceSource dataclass with support for github, git, url types
- Add GitHub shorthand parsing (owner/repo → github:owner/repo)
- Auto-generate default names from source URLs
- Implement marketplace index fetching with HTTP support
- Add local caching of fetched indexes
- Show plugin count from cached indexes in list command
- Update parser to use 'source' for add, 'name' for remove/update
@jpshackelford
Copy link
Contributor

@timon0305 Thanks for the contribution!

@malhotra5
Copy link
Collaborator

Whenever you're comfortable, feel free mark this PR as ready for review and request me + john mason as reviewers

Thanks for the contribution!

Copy link
Collaborator

enyst commented Mar 4, 2026

Status update: SDK PR OpenHands/software-agent-sdk#2031 adds installed plugin management utilities (install/uninstall/list/get/load/update) under ~/.openhands/plugins/installed/ with .installed.json metadata. That provides the SDK backend for plugin install/list/uninstall flows here.

The SDK still has no enable/disable state; CLI should keep that state and pass enabled plugins into Conversation/AgentContext. AgentSkills management is tracked in OpenHands/software-agent-sdk#2301.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants