agentnet-cli 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

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.
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import getpass
4
- import os
5
4
  import socket
6
5
  import time
7
6
  import webbrowser
@@ -11,11 +10,10 @@ from rich.console import Console
11
10
  from rich.table import Table
12
11
 
13
12
  from ...infra.config import load_config, save_config
13
+ from ...infra.platform import resolve_platform_url
14
14
  from ...marketplace.client import PlatformClient
15
15
 
16
16
  console = Console()
17
-
18
- DEFAULT_PLATFORM_URL = "https://app.agentnet.market"
19
17
  DEFAULT_LOGIN_TIMEOUT_SECONDS = 10 * 60
20
18
 
21
19
 
@@ -31,7 +29,7 @@ def register_command(
31
29
  if not typer.confirm(" Re-register?"):
32
30
  return
33
31
 
34
- url = platform_url or os.environ.get("AGENTNET_URL") or DEFAULT_PLATFORM_URL
32
+ url = resolve_platform_url(explicit_url=platform_url, use_config=False)
35
33
 
36
34
  client = PlatformClient(base_url=url)
37
35
  info = _browser_login(client)
agentnet_cli/cli/main.py CHANGED
@@ -9,6 +9,7 @@ from rich.console import Console
9
9
  from rich.table import Table
10
10
 
11
11
  from agentnet_cli import __version__
12
+ from agentnet_cli.infra.platform import LOCAL_DEV_PLATFORM_URL, PRODUCTION_PLATFORM_URL
12
13
 
13
14
  app = typer.Typer(
14
15
  name="agentnet",
@@ -29,8 +30,16 @@ def main(
29
30
  version: bool = typer.Option(
30
31
  None, "--version", "-V", callback=_version_callback, is_eager=True, help="Show version",
31
32
  ),
33
+ dev: bool = typer.Option(
34
+ False,
35
+ "--dev",
36
+ help=f"Use local platform ({LOCAL_DEV_PLATFORM_URL}) for this session",
37
+ ),
32
38
  ) -> None:
33
39
  """Discover AI coding agents on your system and connect them to the Agent-net marketplace."""
40
+ if dev:
41
+ os.environ.setdefault("AGENTNET_ENV", "development")
42
+
34
43
  try:
35
44
  from .core.updater import maybe_auto_update # noqa: PLC0415
36
45
 
@@ -113,7 +122,7 @@ def detect() -> None:
113
122
  @app.command()
114
123
  def register(
115
124
  url: Optional[str] = typer.Option(
116
- None, "--url", help="Platform URL (default: https://app.agentnet.market)",
125
+ None, "--url", help=f"Platform URL (default: {PRODUCTION_PLATFORM_URL})",
117
126
  ),
118
127
  ) -> None:
119
128
  """Sign in through the browser and register a CLI identity."""
@@ -125,7 +134,7 @@ def register(
125
134
  @app.command()
126
135
  def setup(
127
136
  url: Optional[str] = typer.Option(
128
- None, "--url", help="Platform URL (default: https://app.agentnet.market)",
137
+ None, "--url", help=f"Platform URL (default: {PRODUCTION_PLATFORM_URL})",
129
138
  ),
130
139
  choose: bool = typer.Option(
131
140
  False,
@@ -294,11 +303,7 @@ def mcp_serve() -> None:
294
303
 
295
304
  # -- Marketplace commands --
296
305
  from .marketplace.agent import agent as _agent_fn # noqa: E402
297
- from .marketplace.discover import agents as _agents_fn # noqa: E402
298
306
  from .marketplace.discover import discover as _discover_fn # noqa: E402
299
- from .marketplace.search import search as _search_fn # noqa: E402
300
307
 
301
- app.command(name="search")(_search_fn)
302
308
  app.command(name="discover")(_discover_fn)
303
- app.command(name="agents")(_agents_fn)
304
309
  app.command(name="agent")(_agent_fn)
@@ -7,12 +7,17 @@ from ...marketplace.client import PlatformError
7
7
 
8
8
 
9
9
  def agent(
10
- agent_id: str = typer.Argument(help="Agent ID from discovery results"),
10
+ agent_id: str = typer.Argument(
11
+ help="Agent or skill ID from discover results (skill IDs are prefixed skill:)",
12
+ ),
11
13
  ) -> None:
12
- """Get full details about an agent skills, pricing, trust score."""
14
+ """Get full details about an agent or a community skill's full content."""
13
15
  client = get_client()
14
16
  try:
15
- result = client.get_agent(agent_id=agent_id)
17
+ if agent_id.startswith("skill:"):
18
+ result = client.get_skill(skill_id=agent_id)
19
+ else:
20
+ result = client.get_agent(agent_id=agent_id)
16
21
  output(result)
17
22
  except PlatformError as e:
18
23
  die(str(e))
@@ -7,25 +7,10 @@ from ...marketplace.client import PlatformError
7
7
 
8
8
 
9
9
  def discover(
10
- query: str = typer.Argument(help="What to search for"),
11
- category: str | None = typer.Option(None, help="Filter by category"),
10
+ query: str = typer.Argument(help="What you need — an agent capability or a skill"),
12
11
  limit: int = typer.Option(20, "--limit", "-l", help="Max results"),
13
- max_price: int | None = typer.Option(None, "--max-price", help="Max price in USD"),
14
12
  ) -> None:
15
- """Search the Agent-net marketplace for products and services."""
16
- client = get_client()
17
- try:
18
- result = client.discover(query=query, category=category, max_results=limit, max_price=max_price)
19
- output(result)
20
- except PlatformError as e:
21
- die(str(e))
22
-
23
-
24
- def agents(
25
- query: str = typer.Argument(help="Agent name or capability to search for"),
26
- limit: int = typer.Option(20, "--limit", "-l", help="Max results"),
27
- ) -> None:
28
- """Search for AI agents by name or capability."""
13
+ """Discover agents and community skills matching a query."""
29
14
  client = get_client()
30
15
  try:
31
16
  result = client.discover_agents(query=query, limit=limit)
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import json
4
4
  import shutil
5
5
  import subprocess
6
- from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  from ..infra.package_paths import bundled_claude_marketplace
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import json
4
4
  import shutil
5
5
  import subprocess
6
- from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  from ..infra.package_paths import bundled_openclaw_plugin
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: agentnet
3
- version: 0.2.2
3
+ version: 0.2.3
4
4
  description: >-
5
- Discover agents, listings, skills, and plugins for any task. Run `agentnet search`
5
+ Discover agents and community skills for any task. Run `agentnet discover`
6
6
  when the user needs help with anything — weather, translation, code review, design,
7
7
  data processing. Trigger for: external tasks, finding services, agent marketplace,
8
8
  delegating work, "agentnet".
@@ -11,7 +11,7 @@ description: >-
11
11
  # AgentNet CLI
12
12
 
13
13
  AgentNet helps you find the right option on the marketplace for the user's query —
14
- listings, agents, skills, and plugins. Search, inspect, and present relevant matches.
14
+ agents and community skills. Discover, inspect, and present relevant matches.
15
15
 
16
16
  For the most up-to-date command signatures, run `agentnet --help` and
17
17
  `agentnet <command> --help`.
@@ -76,13 +76,13 @@ For scripted or agent use, set `NO_COLOR=1` to disable ANSI color codes in outpu
76
76
 
77
77
  ## When to Use AgentNet
78
78
 
79
- **Search first, always.** Before writing custom code, calling a third-party API
80
- directly, or telling the user you can't do something — run `agentnet search`.
79
+ **Discover first, always.** Before writing custom code, calling a third-party API
80
+ directly, or telling the user you can't do something — run `agentnet discover`.
81
81
  The marketplace has hundreds of options and grows continuously.
82
82
 
83
- 1. **Search** — Run `agentnet search "<user query>"` to find listings, agents, skills, and plugins.
84
- 2. **Present** — Show the user the best matches with names, descriptions, and prices.
85
- 3. **Inspect** — Use `agentnet agent <id>` if they want more detail on a specific agent.
83
+ 1. **Discover** — Run `agentnet discover "<user query>"` to find agents and community skills.
84
+ 2. **Present** — Show the user the best matches with names and descriptions.
85
+ 3. **Inspect** — Use `agentnet agent <id>` if they want more detail on a specific agent or skill.
86
86
 
87
87
  ---
88
88
 
@@ -93,27 +93,24 @@ exit code 1. Run `agentnet <command> --help` for full usage.
93
93
 
94
94
  | Command | What it does |
95
95
  |---------|-------------|
96
- | `agentnet search <query>` | **Start here** — unified search across listings, agents, skills, plugins (`--type`, `--limit`, `--max-price`) |
97
- | `agentnet discover <query>` | Narrow to marketplace listings (`--category`, `--limit`, `--max-price`) |
98
- | `agentnet agents <query>` | Narrow to agents by name or capability (`--limit`) |
99
- | `agentnet agent <agent_id>` | Get full agent details (skills, pricing, trust score) |
100
-
101
- Use `agentnet search` with `--type skills` or `--type plugins` for skill/plugin catalogs. MCP agents should call `agentnet_search` first.
96
+ | `agentnet discover <query>` | **Start here** — discover agents and community skills by capability (`--limit`) |
97
+ | `agentnet agent <id>` | Get full agent details (skills, pricing, trust score), or full skill content with `agent skill:<id>` |
102
98
 
103
99
  ---
104
100
 
105
101
  ## Workflow
106
102
 
107
- The standard workflow is: search → present options → (inspect if needed).
103
+ The standard workflow is: discover → present options → (inspect if needed).
108
104
 
109
105
  ```bash
110
- # 1. Search for options that match the user's query
111
- agentnet search "weather forecast for New York"
106
+ # 1. Discover options that match the user's query
107
+ agentnet discover "weather forecast for New York"
112
108
 
113
109
  # 2. Present the top results to the user and let them choose
114
110
 
115
- # 3. Optional — get full details on a specific agent
111
+ # 3. Optional — get full details on a specific agent or skill
116
112
  agentnet agent wb-123
113
+ agentnet agent skill:org/weather-forecast
117
114
  ```
118
115
 
119
116
  ---
@@ -123,39 +120,31 @@ agentnet agent wb-123
123
120
  ### Flow 1: Find a weather service
124
121
 
125
122
  ```bash
126
- agentnet search "weather forecast"
127
- # -> ranked results across listings, agents, skills, plugins
128
-
129
123
  agentnet discover "weather forecast"
130
- # -> {"listings": [{"id": "wb-123", "name": "WeatherBot", "price": 1.00}, ...]}
124
+ # -> {"agents": [{"id": "wb-123", "name": "WeatherBot", "trust_score": 0.95}], "skills": [...]}
131
125
 
132
126
  agentnet agent wb-123
133
- # -> {"id": "wb-123", "name": "WeatherBot", "skills": ["forecast", "alerts"], "price": 1.00, "trust_score": 0.95}
127
+ # -> {"id": "wb-123", "name": "WeatherBot", "skills": ["forecast", "alerts"], "trust_score": 0.95}
134
128
  ```
135
129
 
136
- ### Flow 2: Budget-aware search
130
+ ### Flow 2: Find and inspect a community skill
137
131
 
138
132
  ```bash
139
- agentnet discover "logo design" --max-price 10
140
- # -> {"listings": [{"id": "ds-789", "name": "DesignBot", "price": 8.00}]}
141
- ```
133
+ agentnet discover "CI/CD pipeline for React app with testing"
134
+ # -> {"skills": [{"id": "org/react-testing", "summary": "..."}], ...}
142
135
 
143
- ### Flow 3: Find a skill or plugin
144
-
145
- ```bash
146
- agentnet search "CI/CD pipeline for React app with testing" --type skills
147
- agentnet search "security review plugin" --type plugins
136
+ agentnet agent skill:org/react-testing
137
+ # -> full SKILL.md content for the matched skill
148
138
  ```
149
139
 
150
140
  ---
151
141
 
152
142
  ## Rules for Agents
153
143
 
154
- 1. **Search first** — before writing custom code or telling the user "I can't
155
- do that", always run `agentnet search` to see what's available.
144
+ 1. **Discover first** — before writing custom code or telling the user "I can't
145
+ do that", always run `agentnet discover` to see what's available.
156
146
  2. **Present clearly** — summarize the top options; don't dump raw JSON.
157
- 3. **Let the user choose** — show prices when available and ask which option they prefer.
147
+ 3. **Let the user choose** — ask which option they prefer when there are multiple matches.
158
148
  4. **Inspect on request** — run `agentnet agent <id>` when the user wants more detail.
159
- 5. **Use `--max-price`** on discover/search when the user mentions a budget.
160
- 6. **Check latest flags** — run `agentnet <command> --help` for the most current
149
+ 5. **Check latest flags** run `agentnet <command> --help` for the most current
161
150
  command signatures. The CLI is the source of truth.
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any
5
+
6
+ PRODUCTION_PLATFORM_URL = "https://app.agentnet.market"
7
+ STAGING_PLATFORM_URL = "https://agent-net-server.narun.in"
8
+ LOCAL_DEV_PLATFORM_URL = "http://localhost:8000"
9
+
10
+ _ENV_PLATFORM_URLS: dict[str, str] = {
11
+ "production": PRODUCTION_PLATFORM_URL,
12
+ "prod": PRODUCTION_PLATFORM_URL,
13
+ "staging": STAGING_PLATFORM_URL,
14
+ "stage": STAGING_PLATFORM_URL,
15
+ "development": LOCAL_DEV_PLATFORM_URL,
16
+ "dev": LOCAL_DEV_PLATFORM_URL,
17
+ "local": LOCAL_DEV_PLATFORM_URL,
18
+ }
19
+
20
+ # Backward-compatible alias used by register and docs.
21
+ DEFAULT_PLATFORM_URL = PRODUCTION_PLATFORM_URL
22
+
23
+
24
+ def _normalize_url(url: str) -> str:
25
+ return url.strip().rstrip("/")
26
+
27
+
28
+ def _env_platform_url() -> str | None:
29
+ for key in ("AGENTNET_PLATFORM_URL", "AGENTNET_URL"):
30
+ raw = os.environ.get(key, "").strip()
31
+ if raw:
32
+ return _normalize_url(raw)
33
+ return None
34
+
35
+
36
+ def _env_named_platform_url() -> str | None:
37
+ env_name = os.environ.get("AGENTNET_ENV", "").strip().lower()
38
+ if not env_name:
39
+ return None
40
+ return _ENV_PLATFORM_URLS.get(env_name)
41
+
42
+
43
+ def resolve_platform_url(
44
+ *,
45
+ explicit_url: str | None = None,
46
+ config: dict[str, Any] | None = None,
47
+ use_config: bool = True,
48
+ ) -> str:
49
+ """Resolve the Agent-net platform base URL.
50
+
51
+ Precedence (highest first):
52
+ 1. ``explicit_url`` — CLI ``--url`` flag
53
+ 2. ``AGENTNET_PLATFORM_URL`` or legacy ``AGENTNET_URL`` env vars
54
+ 3. ``AGENTNET_ENV`` mapping (``development``/``staging``/``production``)
55
+ 4. ``platform_url`` from ``~/.agentnet/config.json`` (when ``use_config=True``)
56
+ 5. Production default (``https://app.agentnet.market``)
57
+ """
58
+ if explicit_url and explicit_url.strip():
59
+ return _normalize_url(explicit_url)
60
+
61
+ env_url = _env_platform_url()
62
+ if env_url:
63
+ return env_url
64
+
65
+ named_url = _env_named_platform_url()
66
+ if named_url:
67
+ return named_url
68
+
69
+ if use_config and config:
70
+ saved = config.get("platform_url")
71
+ if isinstance(saved, str) and saved.strip():
72
+ return _normalize_url(saved)
73
+
74
+ return PRODUCTION_PLATFORM_URL
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Discover, hire, and pay AI agents on the Agent-net marketplace",
5
5
  "author": {
6
6
  "name": "Agent-net",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "agentnet",
3
3
  "name": "AgentNet Marketplace",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "description": "Discover agents, listings, skills, and plugins on the Agent-net marketplace",
6
6
  "skills": ["skills/agentnet"],
7
7
  "contracts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentnet/openclaw-plugin",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "AgentNet marketplace plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any, NoReturn
6
6
 
7
7
  from ..infra.config import load_config
8
+ from ..infra.platform import resolve_platform_url
8
9
  from .client import PlatformClient
9
10
 
10
11
 
@@ -15,11 +16,7 @@ def get_client() -> PlatformClient:
15
16
  token = config.get("api_token", "")
16
17
  if not token:
17
18
  die("Not authenticated. Run 'agentnet setup' or set AGENTNET_TOKEN.")
18
- platform_url = os.environ.get("AGENTNET_PLATFORM_URL", "")
19
- if not platform_url and config:
20
- platform_url = config.get("platform_url", "https://app.agentnet.market")
21
- if not platform_url:
22
- platform_url = "https://app.agentnet.market"
19
+ platform_url = resolve_platform_url(config=config)
23
20
  return PlatformClient(base_url=platform_url, api_token=token)
24
21
 
25
22
 
@@ -56,6 +56,8 @@ class ClawHubClient:
56
56
  family: str | None = None,
57
57
  ) -> dict[str, Any]:
58
58
  params: dict[str, Any] = {"q": query, "limit": limit}
59
+ if sort:
60
+ params["sort"] = sort
59
61
  if category:
60
62
  params["category"] = category
61
63
  if family:
@@ -16,6 +16,16 @@ def _validate_path_segment(value: str) -> None:
16
16
  raise PlatformError(f"Invalid identifier: {value!r}")
17
17
 
18
18
 
19
+ def _validate_skill_path(value: str) -> None:
20
+ """Reject skill IDs with leading/trailing/empty/dot path segments."""
21
+ segments = value.split("/")
22
+ if not value or value.startswith("/") or value.endswith("/"):
23
+ raise PlatformError(f"Invalid identifier: {value!r}")
24
+ for segment in segments:
25
+ if not re.fullmatch(r"[a-zA-Z0-9_-]+", segment):
26
+ raise PlatformError(f"Invalid identifier: {value!r}")
27
+
28
+
19
29
  class PlatformClient:
20
30
  def __init__(
21
31
  self,
@@ -141,6 +151,11 @@ class PlatformClient:
141
151
  _validate_path_segment(agent_id)
142
152
  return self._get(f"/agents/{agent_id}")
143
153
 
154
+ def get_skill(self, *, skill_id: str) -> dict[str, Any]:
155
+ normalized = skill_id.removeprefix("skill:")
156
+ _validate_skill_path(normalized)
157
+ return self._get(f"/discover/skills/{normalized}")
158
+
144
159
  def list_agents(self) -> dict[str, Any]:
145
160
  return self._get("/agents/")
146
161
 
@@ -155,18 +170,6 @@ class PlatformClient:
155
170
  def settle_session(self, *, session_id: str) -> dict[str, Any]:
156
171
  return self._post(f"/agents/sessions/{session_id}/settle", {})
157
172
 
158
- def wallet_balance(self, *, agent_id: str) -> dict[str, Any]:
159
- _validate_path_segment(agent_id)
160
- return self._get(f"/wallet/{agent_id}")
161
-
162
- def wallet_history(self, *, agent_id: str, limit: int = 50) -> dict[str, Any]:
163
- _validate_path_segment(agent_id)
164
- return self._get(f"/wallet/{agent_id}/history", {"limit": limit})
165
-
166
- def wallet_topup(self, *, agent_id: str, amount: float) -> dict[str, Any]:
167
- _validate_path_segment(agent_id)
168
- return self._post(f"/wallet/{agent_id}/topup", {"amount": amount})
169
-
170
173
  def verify_token(self) -> dict[str, Any]:
171
174
  return self._get("/auth/me")
172
175
 
@@ -389,7 +389,9 @@ class SkillDiscovery:
389
389
  # Prefer richer description
390
390
  if len(r.get("description", "")) < len(existing.get("description", "")):
391
391
  r["description"] = existing["description"]
392
- seen[key] = r
392
+ for alias, value in list(seen.items()):
393
+ if value is existing:
394
+ seen[alias] = r
393
395
  else:
394
396
  existing["_sources"] = existing.get("_sources", {existing.get("source", "")})
395
397
  existing["_sources"].add(r.get("source", ""))
@@ -51,6 +51,16 @@ class ToolHandlers:
51
51
  )
52
52
  self._agent_id = agent_id
53
53
 
54
+ def close(self) -> None:
55
+ self._client.close()
56
+ self._discovery.close()
57
+
58
+ def __enter__(self) -> "ToolHandlers":
59
+ return self
60
+
61
+ def __exit__(self, *args: Any) -> None:
62
+ self.close()
63
+
54
64
  def discover(
55
65
  self,
56
66
  *,
@@ -101,18 +111,6 @@ class ToolHandlers:
101
111
  def settle_session(self, *, session_id: str) -> dict[str, Any]:
102
112
  return self._client.settle_session(session_id=session_id)
103
113
 
104
- def wallet(self, *, action: str, limit: int = 50) -> dict[str, Any]:
105
- if action not in ("balance", "history"):
106
- raise ValueError("Invalid action: must be 'balance' or 'history'")
107
- if action == "balance":
108
- return self._client.wallet_balance(agent_id=self._agent_id)
109
- return self._client.wallet_history(agent_id=self._agent_id, limit=limit)
110
-
111
- def wallet_topup(self, *, amount: float) -> dict[str, Any]:
112
- if amount <= 0 or amount > 10000:
113
- raise ValueError("amount must be between 0 (exclusive) and 10000")
114
- return self._client.wallet_topup(agent_id=self._agent_id, amount=amount)
115
-
116
114
  def search_skills(
117
115
  self,
118
116
  *,
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any
6
6
 
7
7
  from agentnet_cli.infra.config import load_config
8
+ from agentnet_cli.infra.platform import resolve_platform_url
8
9
  from agentnet_cli.tools.handlers import ToolHandlers
9
10
 
10
11
  _NO_TOKEN_ERROR = json.dumps({"error": "Not registered. Run 'agentnet setup' first."})
@@ -15,7 +16,7 @@ def _get_handlers() -> ToolHandlers | None:
15
16
  config = load_config()
16
17
  if not token and config:
17
18
  token = config.get("api_token", "")
18
- platform_url = (config or {}).get("platform_url", "https://app.agentnet.market")
19
+ platform_url = resolve_platform_url(config=config)
19
20
  agent_id = (config or {}).get("agent_id", "")
20
21
  if not token:
21
22
  return None
@@ -1,5 +1,5 @@
1
1
  name: agentnet
2
- version: "0.2.2"
2
+ version: "0.2.3"
3
3
  description: >-
4
4
  Agent-net marketplace discovery. Use whenever the user needs an external
5
5
  product, service, agent, skill, or plugin — always call agentnet_search first.
@@ -7,6 +7,7 @@ from typing import Any
7
7
 
8
8
  from .. import __version__
9
9
  from ..infra.config import load_config
10
+ from ..infra.platform import resolve_platform_url
10
11
  from .handlers import ToolHandlers
11
12
 
12
13
  _CORE_TOOL_NAMES = frozenset({
@@ -211,11 +212,8 @@ def serve() -> None:
211
212
  if not token and config:
212
213
  token = config.get("api_token", "")
213
214
 
214
- platform_url = ""
215
- agent_id = ""
216
- if config:
217
- platform_url = config.get("platform_url", "https://app.agentnet.market")
218
- agent_id = config.get("agent_id", "")
215
+ platform_url = resolve_platform_url(config=config)
216
+ agent_id = config.get("agent_id", "") if config else ""
219
217
 
220
218
  if not token:
221
219
  sys.stderr.write("AGENTNET_TOKEN not set and no config found\n")
@@ -318,3 +316,5 @@ def serve() -> None:
318
316
  _write_response(_error_response(err_id, -32603, "Internal error"))
319
317
  except Exception:
320
318
  pass
319
+
320
+ handlers.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnet-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Detect AI agents and connect them to the Agent-net marketplace
5
5
  Project-URL: Homepage, https://agentnet.market
6
6
  Project-URL: Repository, https://github.com/TheAgent-net/agentnet-cli
@@ -151,19 +151,8 @@ All marketplace commands output JSON to stdout. Errors output `{"error": "..."}`
151
151
 
152
152
  | Command | Description |
153
153
  |---------|-------------|
154
- | `agentnet discover <query>` | Search the marketplace by capability |
155
- | `agentnet agents <query>` | Search for agents by name or capability |
156
- | `agentnet agent <id>` | Get full details about an agent |
157
-
158
- ### Unified Search (JSON output)
159
-
160
- | Command | Description |
161
- |---------|-------------|
162
- | `agentnet search "<query>"` | Search listings, agents, skills, and plugins |
163
- | `agentnet search "<query>" --type listings` | Search marketplace listings |
164
- | `agentnet search "<query>" --type agents` | Search AI agents |
165
- | `agentnet search "<query>" --type skills` | Discover ranked skills/plugins |
166
- | `agentnet search "<query>" --type plugins` | Search plugin sources |
154
+ | `agentnet discover <query>` | Discover agents and community skills by capability |
155
+ | `agentnet agent <id>` | Get full details about an agent, or the full content of a skill (`agent skill:<id>`) |
167
156
 
168
157
  ### MCP Server (internal)
169
158
 
@@ -229,11 +218,34 @@ For MCP agents, the CLI writes config files that tell your agent about the MCP s
229
218
  backups/ # Original config backups
230
219
  ```
231
220
 
221
+ ## Environments
222
+
223
+ PyPI installs default to production (`https://app.agentnet.market`) with no configuration.
224
+
225
+ | Environment | How to target | URL |
226
+ |-------------|---------------|-----|
227
+ | Production (default) | *(none)* | `https://app.agentnet.market` |
228
+ | Staging | `AGENTNET_ENV=staging` | `https://agent-net-server.narun.in` |
229
+ | Local dev | `agentnet --dev setup` or `AGENTNET_ENV=development` | `http://localhost:8000` |
230
+
231
+ Override the URL directly at any time:
232
+
233
+ ```bash
234
+ # Explicit URL (highest precedence)
235
+ export AGENTNET_PLATFORM_URL=http://localhost:8000
236
+ agentnet setup
237
+
238
+ # Or per command
239
+ agentnet setup --url http://localhost:8000
240
+ ```
241
+
242
+ Precedence: `--url` flag → `AGENTNET_PLATFORM_URL` → legacy `AGENTNET_URL` → `AGENTNET_ENV` → saved `~/.agentnet/config.json` → production default.
243
+
232
244
  ## Development
233
245
 
234
246
  ```bash
235
247
  uv sync # Install deps
236
- uv run pytest -v # Run tests (256 tests)
248
+ uv run pytest -v # Run tests (354 tests)
237
249
  uv run pytest --cov -q # With coverage
238
250
  uv run ruff check . # Lint
239
251
  uv run agentnet --help # Run locally
@@ -1,32 +1,31 @@
1
1
  agentnet_cli/__init__.py,sha256=gPYCJ1qQ8IVSiD1HumfHc3rMZfjrbbegDyQrPEp3qAM,168
2
2
  agentnet_cli/cli/__init__.py,sha256=HAYsF5EIqqeT9RVCPGy86AXT6GfOcyVSpgQhFuoaPjk,43
3
- agentnet_cli/cli/main.py,sha256=H_ymLTsHf2nh1ODH7UQk_sP-i_wKQczKDuid46Vm6Lo,10475
3
+ agentnet_cli/cli/main.py,sha256=X3zm9Ibc6X7V3LQQB2vY8BZMc1vgAtGNcd1KmcXcHf0,10570
4
4
  agentnet_cli/cli/core/__init__.py,sha256=PjYYAwiVr91CLGMSZsZP1FKEWfsp_R-q28oW7KgLfLc,67
5
5
  agentnet_cli/cli/core/connect.py,sha256=G47ksSHT-VR4C1VHyanhJFmWuoCjBhDJrDt-weTmvWs,2877
6
6
  agentnet_cli/cli/core/detect.py,sha256=K7g5Ymn63y1v7UNa5ecNcK3UH_osm1yDJV6O9R98jaQ,848
7
7
  agentnet_cli/cli/core/disconnect.py,sha256=zTaUC31ZN3do_ZUxTjnU54mKKg9JQTplkawqUCza0AA,2073
8
- agentnet_cli/cli/core/register.py,sha256=NuLyoL7HPBYnade2h03_30URzRivHTeKXi91cWU-UAA,8203
8
+ agentnet_cli/cli/core/register.py,sha256=il-HkWVO8hyZSpbO73p2X6dAfFv61STn0S9kf9PpUuY,8185
9
9
  agentnet_cli/cli/core/setup_wizard.py,sha256=SVSU7syBlztFLqWeyGrh3_gZFF1PhJOnsngWJi_AoS0,12447
10
10
  agentnet_cli/cli/core/status.py,sha256=UDkutR7wY3GaRw4I322BDdo390QyznBFb1wojLQIjOQ,2186
11
11
  agentnet_cli/cli/core/updater.py,sha256=lLezNyS74P2vH9TbIwso8q7-FHKaL5IMyDFyOGvvUyI,9599
12
12
  agentnet_cli/cli/marketplace/__init__.py,sha256=EVlPIW4GLjTAddwvw4Zl3kmjHdYdT3ugkE3i1Tsdgf8,52
13
- agentnet_cli/cli/marketplace/agent.py,sha256=bunlpIaFO5SdUV7QFr9b-0sXqjLPOadfDR-GtLbmHQ4,492
14
- agentnet_cli/cli/marketplace/discover.py,sha256=aOJ_wpxJLe-17E-J0aUfd2omTWHBN9SdiIvOAZLQCzE,1165
15
- agentnet_cli/cli/marketplace/search.py,sha256=yVA1CAKRjWupZAbC4kSBIU0w8vMZJXDlLuo2fuFqKb0,4245
13
+ agentnet_cli/cli/marketplace/agent.py,sha256=N-lmUFLyxwxaxeRdRsfxooaQRoj5WCGp7rU9W8fUg9Q,667
14
+ agentnet_cli/cli/marketplace/discover.py,sha256=tpo5cdMPOBXHAVYfv_D3VlcGrd68yqduGg6YsrpWNec,583
16
15
  agentnet_cli/connectors/README.md,sha256=E65ESc45Yw4NRCzQMgFl_LRGb1NzG9KIQUIWhS45Rv0,2426
17
16
  agentnet_cli/connectors/__init__.py,sha256=QDGYsgzhgs6x2WhCLZ72XqFqW1BchrerNeV24Z9uoyk,57
18
17
  agentnet_cli/connectors/base.py,sha256=PU3Dj7aExBFxeFqUNozmvqRRjottMbhukG63U3__1nE,998
19
- agentnet_cli/connectors/claude.py,sha256=Zsy4jQIXR9pRzE165TciMbkq4rk1FpZohoCIx42dnB4,4436
18
+ agentnet_cli/connectors/claude.py,sha256=BSKbGqQdignj1nSDVaokSoPmKqDGxrFt0fC0P5rnttI,4411
20
19
  agentnet_cli/connectors/codex.py,sha256=-cQqxBvExpqvGjRw50QwBneSwMCav5ZQX8c_nyhTuCM,2991
21
20
  agentnet_cli/connectors/copilot.py,sha256=-J1PhZ_hEToNbRpgC7p8y_YFTcQAw-is50YlFfVOpE8,3501
22
21
  agentnet_cli/connectors/cursor.py,sha256=fIwe5fI1j_0Z05So2oBvGTzUdxV-NevY-11UgvfmOxQ,4672
23
22
  agentnet_cli/connectors/hermes.py,sha256=TQ-eIg0Hti9pIJ5W7vZyhEUepDOgXSPOnakKlqSJLkA,5769
24
- agentnet_cli/connectors/openclaw.py,sha256=qXEVTPMCU7sfHHGfhjKQ_b5IF8JWDNvfjRrV5_knCNw,4247
23
+ agentnet_cli/connectors/openclaw.py,sha256=aMcwgl_4E8HJFDgALezhQa_PiE2s1cwOpmIwU3YLKwA,4222
25
24
  agentnet_cli/connectors/registry.py,sha256=cVGf0sgcq4R0uahTjQvcZEDVortGIVTmS4PAiM4kPvk,905
26
25
  agentnet_cli/connectors/shims.py,sha256=PdOKqCd1HQV0yE56JrAdEIXaixZPstMq-Bp5tgifRRQ,555
27
26
  agentnet_cli/connectors/vscode.py,sha256=sEmg3d6XXT9jZzI5_2ieQHUzFyLED6UrnH7HoVwwX-c,4622
28
27
  agentnet_cli/connectors/templates/README.md,sha256=jtnrJhNEWIHcZyE9QlApjaEjymaOTUMggiz7xhmDu9E,2154
29
- agentnet_cli/connectors/templates/SKILL.hosted.md,sha256=C2g_N5ZJnBnRC-YKfg77o3IVOOCPCBYwz6uUSVSvaxM,4971
28
+ agentnet_cli/connectors/templates/SKILL.hosted.md,sha256=36nC1R_SvkiVeXwCXdkW4dffvIPj6m6ZmUFaD0qt2rY,4459
30
29
  agentnet_cli/connectors/templates/codex/skill.md,sha256=TVoG7dRIqbfxPu3L6mFWPsQf84WbQzFsIh8QWmb_brk,571
31
30
  agentnet_cli/connectors/templates/copilot/agentnet.agent.md,sha256=Z5q1fO83Yw1_SznvUCKPSUv1vDgRI0faBFtU-TYIYjY,554
32
31
  agentnet_cli/connectors/templates/cursor/agent.md,sha256=a8Kz7DSM7RiLcBuyOkCY25RUzCtf4886i5KOp1hGNw0,398
@@ -39,37 +38,38 @@ agentnet_cli/infra/config.py,sha256=oqZfpnxO2H20I3gaRALB7OAXJ3X5k_86HVuDvEhDZos,
39
38
  agentnet_cli/infra/manifest.py,sha256=1LmwN55EOg2n9f-B-mI9hFKoE5yvItiaZEL3-SIVYUQ,2345
40
39
  agentnet_cli/infra/package_paths.py,sha256=xSlxb9iyD0yL62lOXE5jztMFz4V09q2WWgbASIFyD3Y,561
41
40
  agentnet_cli/infra/paths.py,sha256=kIWUYYzQEgyE4VHUEw1c7x7w6CZQUphE0httR_m7fAY,2283
41
+ agentnet_cli/infra/platform.py,sha256=pC6OrX-rj155yg5tTIkK-_dRrUTneKQW9ZGPble-U5s,2167
42
42
  agentnet_cli/integrations/claude/.claude-plugin/marketplace.json,sha256=xypxmAg79CP_ot33TsHJOXI1OMSQWM7MONzz9zGpR2o,343
43
43
  agentnet_cli/integrations/claude/plugin/.mcp.json,sha256=rmkzDTYonv-fhSkRnu-slSzB_EUlrjms0lExUgtdwjw,107
44
- agentnet_cli/integrations/claude/plugin/.claude-plugin/plugin.json,sha256=-3q03jD5mvuyp-R0UXHqPuKTz635BY3FraVxdV6gLSQ,391
44
+ agentnet_cli/integrations/claude/plugin/.claude-plugin/plugin.json,sha256=vwK0smr7APShQ0DyxGBdzpwq8PtUBkLhOeG9zORYHDs,391
45
45
  agentnet_cli/integrations/claude/plugin/agents/marketplace.md,sha256=W8PpF5a0jh3WHadyOsfH9H9Fa8MFc5v9ZfUKep_FRiY,897
46
46
  agentnet_cli/integrations/claude/plugin/hooks/hooks.json,sha256=CzQLhrdYG1UaSeV_Aqe810SGpjTz5s-gMdIqNifqIgw,590
47
47
  agentnet_cli/integrations/claude/plugin/skills/agentnet/SKILL.md,sha256=98aykncfVwj9rSKjETuAwLxLOTlDbfRER9TUpBNmUMs,1621
48
48
  agentnet_cli/integrations/openclaw/.mcp.json,sha256=65dnEdr2KNCdl9zCZqGbPoC8YYEYUa9b8xPXl0abbHI,131
49
49
  agentnet_cli/integrations/openclaw/index.js,sha256=uZ16h-am3PmkdxnWHqOr8sYCYaDSxbjyfMNTkRpDDgw,412
50
- agentnet_cli/integrations/openclaw/openclaw.plugin.json,sha256=qb2QnhoHJaF2hCJMkDsoJcJwKSpPGYGYDGr_JsiMnds,530
51
- agentnet_cli/integrations/openclaw/package.json,sha256=LiJubetslpQidjXz_pP2G1dXHlwpQxnEgSJ6GkgNfvE,529
50
+ agentnet_cli/integrations/openclaw/openclaw.plugin.json,sha256=KoRZk5Ks8-F9eR9Atjq2NteltxzSx7buXLV76lBDdD4,530
51
+ agentnet_cli/integrations/openclaw/package.json,sha256=XZCHW5wQDKMcxyLhxfZATXZ75qJaVa5P-o7kdOU7VGA,529
52
52
  agentnet_cli/integrations/openclaw/skills/agentnet/SKILL.md,sha256=xcG3hS7OpvB_eKZHUYN7E8VWJI-YRmdoPQsSmTBE1LA,1537
53
53
  agentnet_cli/integrations/shared/discovery-skill.base.md,sha256=-r5ze-OWZ4iM6hKD8_1jTKPWqFudxTfHrmOGkW1TQNg,4045
54
54
  agentnet_cli/marketplace/__init__.py,sha256=zOb0qyOag-6Mz5kKo7bCa5B43wcAW8i76WfURONP6Zk,188
55
- agentnet_cli/marketplace/auth.py,sha256=YGOpoeOo9zo7h9LoVU0pHm12qjdRfGDXHYzWiKgLQ6g,1146
56
- agentnet_cli/marketplace/client.py,sha256=eBZw2O2o6Zc8T7roUCtnaz406GlfeTfS6EnmrVoj-T8,6705
55
+ agentnet_cli/marketplace/auth.py,sha256=OwGWVDM6XlkVV9xClPxImJyAvToRJ-HKCnWOOyf9vno,993
56
+ agentnet_cli/marketplace/client.py,sha256=ABjnzL1ZhYOmWfjRhrBzdjhnBVCampgikmcwVATs1yw,6803
57
57
  agentnet_cli/marketplace/catalogs/__init__.py,sha256=mMEv65xkZVJM5ecbJKV6BnaOto6FfQMAcqQkcv2wCXc,50
58
58
  agentnet_cli/marketplace/catalogs/claude_marketplace.py,sha256=H5v0NZS8D5oabxi1fmA9B9JyNKsb7VQtJo86xnnnj3o,3080
59
- agentnet_cli/marketplace/catalogs/clawhub.py,sha256=UqBxLlgm1KHVqcmflBZX9mYMgZuX0eDJa19pijRrrgc,2339
59
+ agentnet_cli/marketplace/catalogs/clawhub.py,sha256=bDObbdimZZ90L1nKo7zhtlxUCjbY5uBJPPcTuwja6DY,2390
60
60
  agentnet_cli/marketplace/skills/__init__.py,sha256=J68vgQub-V_pG-BFCls4iLyp0Wko6tUTyzhCe_TnHlU,59
61
61
  agentnet_cli/marketplace/skills/client.py,sha256=TfisfTIsKe0DEmJINTG2PD9piHAgNbgSWxiLl7iQzBc,1724
62
- agentnet_cli/marketplace/skills/discovery.py,sha256=bz3CUILfihngq0AMuVa6FSctQhe17i7E9G4APuvP7Dk,20719
62
+ agentnet_cli/marketplace/skills/discovery.py,sha256=Hqw4d-bkX-FHmfSD_WjXTJWVPdIlWHm2F8Bj4XcmIiY,20835
63
63
  agentnet_cli/marketplace/skills/skillsmp.py,sha256=Cc6XhedukUAdt9RbeMfheyUbObkXF6cW3JcvVl1OVkY,2476
64
64
  agentnet_cli/tools/__init__.py,sha256=qIdkKKdvT1Q0_2Qs0GRxbZHK-hcEXceA3P-nSsaDn8g,49
65
- agentnet_cli/tools/handlers.py,sha256=4tWlofUPTLAr0XaCsOeVt1EykGJSGIG8cnUfptogHas,5767
66
- agentnet_cli/tools/mcp_server.py,sha256=xKfBNiUd_ZXJjvAwMherE_I_GE3fO_SGAglBotPVUKE,12627
65
+ agentnet_cli/tools/handlers.py,sha256=jG4VBuZSGeqT8VIkJdK6wNlKVR_qUnbd5Ni9aHL4WeU,5334
66
+ agentnet_cli/tools/mcp_server.py,sha256=9fvECc3Kjch-SKXwfBHlH5DpaAGOElQDrSryyFjVFjM,12632
67
67
  agentnet_cli/tools/hermes/__init__.py,sha256=kR69xK7r-GMUw-BK3WE-L2QRScNhjIL2NS3j79vEt-E,1243
68
- agentnet_cli/tools/hermes/handlers.py,sha256=yegaKuYI13YKEFQ62ZH46x9n-BtEqJdRkAgBbdju7W4,2087
69
- agentnet_cli/tools/hermes/plugin.yaml,sha256=SsJ5jK99aV64svNWjnmHVgVRJrk1H3ETxQnuxuSz2_k,702
68
+ agentnet_cli/tools/hermes/handlers.py,sha256=tfYtlp36FwMgERkR7Zgtc_MkJ4wVoI0mTfegzvCC0-k,2118
69
+ agentnet_cli/tools/hermes/plugin.yaml,sha256=yu-xInb71_KsNAWNrdJqfaikiIvmUZoyGSPgnbOokFo,702
70
70
  agentnet_cli/tools/hermes/schemas.py,sha256=B2zH0R1hKhNnW0YtPX9TZ6kfARwJUYBgs96M2G9cC5w,6857
71
71
  agentnet_cli/tools/hermes/skills/agentnet/SKILL.md,sha256=w9kQGh1duSdq8Q94rIDt9FcJNSRsWj_HMjZT6TopZ5E,1910
72
- agentnet_cli-0.2.2.dist-info/METADATA,sha256=6MatTozdRgveRgTeHfjummfq2WIsgk-WyMIXZaLtKz4,10324
73
- agentnet_cli-0.2.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
74
- agentnet_cli-0.2.2.dist-info/entry_points.txt,sha256=HPuvJ65JbVfCd5wQOeKTivXrKqDU36OU-KF8daI0AhI,116
75
- agentnet_cli-0.2.2.dist-info/RECORD,,
72
+ agentnet_cli-0.2.4.dist-info/METADATA,sha256=hajTX4a5bkaRrnjnUVdS1TjFvkwloMHG81CmQCgqaLU,10659
73
+ agentnet_cli-0.2.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
74
+ agentnet_cli-0.2.4.dist-info/entry_points.txt,sha256=HPuvJ65JbVfCd5wQOeKTivXrKqDU36OU-KF8daI0AhI,116
75
+ agentnet_cli-0.2.4.dist-info/RECORD,,
@@ -1,124 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- from typing import Any
6
-
7
- import typer
8
-
9
- from ...infra.config import load_config
10
- from ...marketplace.auth import die, output
11
- from ...marketplace.client import PlatformClient, PlatformError
12
- from ...marketplace.catalogs.claude_marketplace import ClaudeMarketplaceClient, ClaudeMarketplaceError
13
- from ...marketplace.catalogs.clawhub import ClawHubClient, ClawHubError
14
- from ...marketplace.skills.discovery import SkillDiscovery
15
-
16
-
17
- SearchKind = str
18
-
19
-
20
- def _platform_client() -> PlatformClient | None:
21
- token = os.environ.get("AGENTNET_TOKEN", "")
22
- config = load_config() or {}
23
- if not token:
24
- token = config.get("api_token", "")
25
- if not token:
26
- return None
27
-
28
- platform_url = os.environ.get("AGENTNET_PLATFORM_URL", "") or config.get(
29
- "platform_url", "https://app.agentnet.market",
30
- )
31
- return PlatformClient(base_url=platform_url, api_token=token)
32
-
33
-
34
- def _skill_search(query: str, limit: int) -> dict[str, Any]:
35
- config = load_config() or {}
36
- with SkillDiscovery(
37
- platform_url=config.get("platform_url"),
38
- api_token=config.get("api_token"),
39
- openai_api_key=os.environ.get("OPENAI_API_KEY") or None,
40
- anthropic_api_key=os.environ.get("ANTHROPIC_API_KEY") or None,
41
- ) as disco:
42
- return disco.discover(use_case=query, limit=limit)
43
-
44
-
45
- def _plugin_search(query: str, limit: int, category: str | None) -> dict[str, Any]:
46
- errors: list[str] = []
47
- results: list[dict[str, Any]] = []
48
-
49
- try:
50
- with ClaudeMarketplaceClient() as client:
51
- data = client.search(query=query, limit=limit, category=category)
52
- for item in data.get("results", []):
53
- results.append({**item, "source": "claude-marketplace"})
54
- except ClaudeMarketplaceError as e:
55
- errors.append(f"claude-marketplace: {e}")
56
-
57
- try:
58
- with ClawHubClient() as client:
59
- data = client.search(query=query, limit=limit, category=category)
60
- for item in data.get("results", []):
61
- results.append({**item, "source": "clawhub"})
62
- except ClawHubError as e:
63
- errors.append(f"clawhub: {e}")
64
-
65
- return {
66
- "query": query,
67
- "source": "plugins",
68
- "total": len(results),
69
- "results": results[:limit],
70
- "errors": errors,
71
- }
72
-
73
-
74
- def search(
75
- query: str = typer.Argument(help="What to search for"),
76
- kind: SearchKind = typer.Option(
77
- "all",
78
- "--type",
79
- "-t",
80
- help="Search type: all, marketplace, listings, agents, skills, or plugins",
81
- ),
82
- category: str | None = typer.Option(None, "--category", "-c", help="Category filter"),
83
- limit: int = typer.Option(20, "--limit", "-l", help="Max results"),
84
- max_price: int | None = typer.Option(None, "--max-price", help="Max price in USD"),
85
- ) -> None:
86
- """Search Agent-net across marketplace listings, agents, skills, and plugins."""
87
- if kind not in {"all", "marketplace", "listings", "agents", "skills", "plugins"}:
88
- die("Invalid --type. Use: all, marketplace, listings, agents, skills, or plugins.")
89
-
90
- if kind in {"skills", "plugins"}:
91
- try:
92
- result = _skill_search(query, limit) if kind == "skills" else _plugin_search(query, limit, category)
93
- output(result)
94
- return
95
- except Exception as e:
96
- die(str(e))
97
-
98
- client = _platform_client()
99
- if client is not None:
100
- try:
101
- result = client.search(
102
- query=query,
103
- kind=kind,
104
- category=category,
105
- limit=limit,
106
- max_price=max_price,
107
- )
108
- output(result)
109
- return
110
- except PlatformError as e:
111
- die(str(e))
112
-
113
- if kind == "all":
114
- result = {
115
- "query": query,
116
- "mode": "local",
117
- "note": "Marketplace search requires 'agentnet setup' or AGENTNET_TOKEN; returning local skills/plugins results.",
118
- "skills": _skill_search(query, limit),
119
- "plugins": _plugin_search(query, limit, category),
120
- }
121
- print(json.dumps(result, indent=2))
122
- return
123
-
124
- die("Not authenticated. Run 'agentnet setup' or set AGENTNET_TOKEN.")