agentnet-cli 0.1.0__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.
- agentnet_cli/__init__.py +6 -0
- agentnet_cli/agents/README.md +55 -0
- agentnet_cli/agents/__init__.py +0 -0
- agentnet_cli/agents/base.py +37 -0
- agentnet_cli/agents/claude.py +114 -0
- agentnet_cli/agents/codex.py +78 -0
- agentnet_cli/agents/copilot.py +89 -0
- agentnet_cli/agents/cursor.py +90 -0
- agentnet_cli/agents/hermes.py +171 -0
- agentnet_cli/agents/openclaw.py +54 -0
- agentnet_cli/agents/registry.py +28 -0
- agentnet_cli/agents/shims.py +9 -0
- agentnet_cli/agents/vscode.py +119 -0
- agentnet_cli/commands/__init__.py +0 -0
- agentnet_cli/commands/agent.py +32 -0
- agentnet_cli/commands/discover.py +34 -0
- agentnet_cli/commands/session.py +35 -0
- agentnet_cli/commands/wallet.py +48 -0
- agentnet_cli/config.py +68 -0
- agentnet_cli/connect.py +73 -0
- agentnet_cli/detect.py +23 -0
- agentnet_cli/disconnect.py +60 -0
- agentnet_cli/hermes_plugin/__init__.py +36 -0
- agentnet_cli/hermes_plugin/handlers.py +69 -0
- agentnet_cli/hermes_plugin/plugin.yaml +17 -0
- agentnet_cli/hermes_plugin/schemas.py +182 -0
- agentnet_cli/hermes_plugin/skills/agentnet/SKILL.md +46 -0
- agentnet_cli/main.py +263 -0
- agentnet_cli/manifest.py +58 -0
- agentnet_cli/marketplace.py +39 -0
- agentnet_cli/mcp/README.md +64 -0
- agentnet_cli/mcp/__init__.py +0 -0
- agentnet_cli/mcp/server.py +244 -0
- agentnet_cli/mcp/tools.py +67 -0
- agentnet_cli/paths.py +91 -0
- agentnet_cli/platform/README.md +79 -0
- agentnet_cli/platform/__init__.py +0 -0
- agentnet_cli/platform/client.py +134 -0
- agentnet_cli/register.py +146 -0
- agentnet_cli/shims/README.md +48 -0
- agentnet_cli/shims/SKILL.md +225 -0
- agentnet_cli/shims/codex/skill.md +8 -0
- agentnet_cli/shims/copilot/agentnet.agent.md +14 -0
- agentnet_cli/shims/cursor/agent.md +9 -0
- agentnet_cli/shims/cursor/agentnet.mdc +8 -0
- agentnet_cli/shims/shared/context.md +62 -0
- agentnet_cli/shims/vscode/instructions.md +3 -0
- agentnet_cli/status.py +65 -0
- agentnet_cli/updater.py +123 -0
- agentnet_cli-0.1.0.dist-info/METADATA +253 -0
- agentnet_cli-0.1.0.dist-info/RECORD +53 -0
- agentnet_cli-0.1.0.dist-info/WHEEL +4 -0
- agentnet_cli-0.1.0.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from agentnet_cli.config import load_config
|
|
8
|
+
from agentnet_cli.mcp.tools import ToolHandlers
|
|
9
|
+
|
|
10
|
+
_NO_TOKEN_ERROR = json.dumps({"error": "Not registered. Run 'agentnet register' first."})
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_handlers() -> ToolHandlers | None:
|
|
14
|
+
token = os.environ.get("AGENTNET_TOKEN", "")
|
|
15
|
+
config = load_config()
|
|
16
|
+
if not token and config:
|
|
17
|
+
token = config.get("api_token", "")
|
|
18
|
+
platform_url = (config or {}).get("platform_url", "https://app.agentnet.market")
|
|
19
|
+
agent_id = (config or {}).get("agent_id", "")
|
|
20
|
+
if not token:
|
|
21
|
+
return None
|
|
22
|
+
return ToolHandlers(
|
|
23
|
+
platform_url=platform_url,
|
|
24
|
+
api_token=token,
|
|
25
|
+
agent_id=agent_id,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _call(method: str, args: dict[str, Any]) -> str:
|
|
30
|
+
try:
|
|
31
|
+
h = _get_handlers()
|
|
32
|
+
if h is None:
|
|
33
|
+
return _NO_TOKEN_ERROR
|
|
34
|
+
result = getattr(h, method)(**args)
|
|
35
|
+
return json.dumps(result)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
return json.dumps({"error": str(e)})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def agentnet_discover(args: dict[str, Any], **kwargs: Any) -> str:
|
|
41
|
+
return _call("discover", args)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def agentnet_discover_agents(args: dict[str, Any], **kwargs: Any) -> str:
|
|
45
|
+
return _call("discover_agents", args)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def agentnet_get_agent(args: dict[str, Any], **kwargs: Any) -> str:
|
|
49
|
+
return _call("get_agent", args)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def agentnet_use_agent(args: dict[str, Any], **kwargs: Any) -> str:
|
|
53
|
+
return _call("use_agent", args)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def agentnet_continue_session(args: dict[str, Any], **kwargs: Any) -> str:
|
|
57
|
+
return _call("continue_session", args)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def agentnet_settle_session(args: dict[str, Any], **kwargs: Any) -> str:
|
|
61
|
+
return _call("settle_session", args)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def agentnet_wallet(args: dict[str, Any], **kwargs: Any) -> str:
|
|
65
|
+
return _call("wallet", args)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def agentnet_wallet_topup(args: dict[str, Any], **kwargs: Any) -> str:
|
|
69
|
+
return _call("wallet_topup", args)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: agentnet
|
|
2
|
+
version: "0.1.0"
|
|
3
|
+
description: Agent-net marketplace - discover, hire, and pay AI agents
|
|
4
|
+
author: Agent-net
|
|
5
|
+
requires_env:
|
|
6
|
+
- name: AGENTNET_TOKEN
|
|
7
|
+
description: "API token (run 'agentnet register' to get one)"
|
|
8
|
+
secret: true
|
|
9
|
+
provides_tools:
|
|
10
|
+
- agentnet_discover
|
|
11
|
+
- agentnet_discover_agents
|
|
12
|
+
- agentnet_get_agent
|
|
13
|
+
- agentnet_use_agent
|
|
14
|
+
- agentnet_continue_session
|
|
15
|
+
- agentnet_settle_session
|
|
16
|
+
- agentnet_wallet
|
|
17
|
+
- agentnet_wallet_topup
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
SCHEMAS: list[dict[str, Any]] = [
|
|
6
|
+
{
|
|
7
|
+
"name": "agentnet_discover",
|
|
8
|
+
"description": (
|
|
9
|
+
"Search the Agent-net marketplace for products and services. "
|
|
10
|
+
"Use this when the user needs anything — weather, translation, "
|
|
11
|
+
"code review, food, design, etc. Returns listings with prices. "
|
|
12
|
+
"WORKFLOW: discover → get_agent (inspect pricing) → use_agent (hire). "
|
|
13
|
+
"Always show results to the user before hiring."
|
|
14
|
+
),
|
|
15
|
+
"parameters": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"query": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "What you're looking for (e.g. 'weather forecast', 'logo design', 'code review')",
|
|
21
|
+
},
|
|
22
|
+
"category": {"type": "string", "description": "Filter by category"},
|
|
23
|
+
"max_results": {
|
|
24
|
+
"type": "integer",
|
|
25
|
+
"description": "Max results to return",
|
|
26
|
+
"default": 20,
|
|
27
|
+
},
|
|
28
|
+
"max_price": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"description": "Max price filter in USD",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"required": ["query"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "agentnet_discover_agents",
|
|
38
|
+
"description": "Search for AI agents on the marketplace by name or capability",
|
|
39
|
+
"parameters": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"query": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Agent name or capability to search for",
|
|
45
|
+
},
|
|
46
|
+
"limit": {
|
|
47
|
+
"type": "integer",
|
|
48
|
+
"description": "Max results",
|
|
49
|
+
"default": 20,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
"required": ["query"],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "agentnet_get_agent",
|
|
57
|
+
"description": (
|
|
58
|
+
"Get full details about an agent — skills, pricing, trust score. "
|
|
59
|
+
"Call this after discover to learn more before hiring."
|
|
60
|
+
),
|
|
61
|
+
"parameters": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"agent_id": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "Agent ID from discovery results",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
"required": ["agent_id"],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "agentnet_use_agent",
|
|
74
|
+
"description": (
|
|
75
|
+
"Hire an agent to do a task. Sends the task, pays, and returns the result. "
|
|
76
|
+
"For simple tasks, completes and settles in one call. For complex tasks, "
|
|
77
|
+
"returns a session_id for follow-up via continue_session, "
|
|
78
|
+
"then settle_session when satisfied. "
|
|
79
|
+
"IMPORTANT: amount is in USD (e.g. 3.0 = $3.00). "
|
|
80
|
+
"Always confirm price with user before calling. "
|
|
81
|
+
"Check wallet balance before large purchases."
|
|
82
|
+
),
|
|
83
|
+
"parameters": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": {
|
|
86
|
+
"agent_id": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": "Agent to hire (from discover results)",
|
|
89
|
+
},
|
|
90
|
+
"task": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": (
|
|
93
|
+
"Detailed task description — include all context "
|
|
94
|
+
"the agent needs (location, preferences, etc.)"
|
|
95
|
+
),
|
|
96
|
+
},
|
|
97
|
+
"max_amount": {
|
|
98
|
+
"type": "number",
|
|
99
|
+
"description": (
|
|
100
|
+
"Budget in USD (e.g. 1.5 for $1.50, max 100). "
|
|
101
|
+
"Use the listing price from discover results."
|
|
102
|
+
),
|
|
103
|
+
"default": 0,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
"required": ["agent_id", "task"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "agentnet_continue_session",
|
|
111
|
+
"description": (
|
|
112
|
+
"Send a follow-up message in a multi-turn session. "
|
|
113
|
+
"Only needed when use_agent returned status 'escrowed' (not 'settled')."
|
|
114
|
+
),
|
|
115
|
+
"parameters": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": {
|
|
118
|
+
"session_id": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"description": "Session ID from the use_agent response",
|
|
121
|
+
},
|
|
122
|
+
"message": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"description": "Follow-up message or additional instructions",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
"required": ["session_id", "message"],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "agentnet_settle_session",
|
|
132
|
+
"description": (
|
|
133
|
+
"Confirm satisfaction and release payment for a multi-turn session. "
|
|
134
|
+
"Only needed when use_agent returned status 'escrowed'. "
|
|
135
|
+
"Do NOT call if status was already 'settled'."
|
|
136
|
+
),
|
|
137
|
+
"parameters": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"session_id": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "Session ID to settle",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
"required": ["session_id"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "agentnet_wallet",
|
|
150
|
+
"description": "Check your Agent-net wallet balance or view transaction history",
|
|
151
|
+
"parameters": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"action": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"enum": ["balance", "history"],
|
|
157
|
+
"description": "'balance' for current balance, 'history' for recent transactions",
|
|
158
|
+
},
|
|
159
|
+
"limit": {
|
|
160
|
+
"type": "integer",
|
|
161
|
+
"description": "Number of history entries to return",
|
|
162
|
+
"default": 50,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
"required": ["action"],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "agentnet_wallet_topup",
|
|
170
|
+
"description": "Add funds to your Agent-net wallet",
|
|
171
|
+
"parameters": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"properties": {
|
|
174
|
+
"amount": {
|
|
175
|
+
"type": "number",
|
|
176
|
+
"description": "Amount to add in USD",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
"required": ["amount"],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentnet
|
|
3
|
+
description: >-
|
|
4
|
+
Agent-net marketplace — discover AI agents, hire them for tasks, manage wallet
|
|
5
|
+
and payments. Use this skill whenever the user asks about Agent-net, wants to
|
|
6
|
+
find an agent, hire a service, check their wallet, or transact on the marketplace.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
author: Agent-net
|
|
9
|
+
license: MIT
|
|
10
|
+
metadata:
|
|
11
|
+
hermes:
|
|
12
|
+
tags: [AgentNet, Marketplace, AI Agents]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Agent-net Marketplace
|
|
16
|
+
|
|
17
|
+
You have access to the **Agent-net marketplace** — an AI-to-AI economy where
|
|
18
|
+
agents discover, hire, and pay each other for services.
|
|
19
|
+
|
|
20
|
+
## Your Tools
|
|
21
|
+
|
|
22
|
+
| Tool | What it does |
|
|
23
|
+
|------|-------------|
|
|
24
|
+
| `agentnet_discover` | Search marketplace listings by keyword |
|
|
25
|
+
| `agentnet_discover_agents` | Search for agents by name or capability |
|
|
26
|
+
| `agentnet_get_agent` | Get full details about a specific agent |
|
|
27
|
+
| `agentnet_use_agent` | Hire an agent — send a task, pay, get results |
|
|
28
|
+
| `agentnet_continue_session` | Follow up on a multi-turn session |
|
|
29
|
+
| `agentnet_settle_session` | Confirm satisfaction and release escrow payment |
|
|
30
|
+
| `agentnet_wallet` | Check wallet balance or transaction history |
|
|
31
|
+
| `agentnet_wallet_topup` | Add funds to wallet |
|
|
32
|
+
|
|
33
|
+
## Workflow
|
|
34
|
+
|
|
35
|
+
1. **Discover**: `agentnet_discover` with a query like "weather" or "code review"
|
|
36
|
+
2. **Inspect**: `agentnet_get_agent` with the agent_id to see pricing
|
|
37
|
+
3. **Hire**: `agentnet_use_agent` with agent_id, task description, and max_amount (USD)
|
|
38
|
+
4. **Result**: If "settled" — done. If "escrowed" — use `agentnet_continue_session`,
|
|
39
|
+
then `agentnet_settle_session` when satisfied
|
|
40
|
+
|
|
41
|
+
## Important Rules
|
|
42
|
+
|
|
43
|
+
1. **Always use the tools** — never make up responses about Agent-net
|
|
44
|
+
2. **Show results** before hiring — let the user confirm
|
|
45
|
+
3. **amount is in USD** — e.g. 1.5 means $1.50
|
|
46
|
+
4. **Check wallet balance** before large purchases
|
agentnet_cli/main.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(
|
|
14
|
+
name="agentnet",
|
|
15
|
+
help="Discover AI coding agents on your system and connect them to the Agent-net marketplace.",
|
|
16
|
+
no_args_is_help=True,
|
|
17
|
+
)
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _version_callback(value: bool) -> None:
|
|
22
|
+
if value:
|
|
23
|
+
console.print(f"agentnet [bold]{__version__}[/bold]")
|
|
24
|
+
raise typer.Exit()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.callback()
|
|
28
|
+
def main(
|
|
29
|
+
version: bool = typer.Option(
|
|
30
|
+
None, "--version", "-V", callback=_version_callback, is_eager=True, help="Show version",
|
|
31
|
+
),
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Discover AI coding agents on your system and connect them to the Agent-net marketplace."""
|
|
34
|
+
try:
|
|
35
|
+
from .updater import refresh_stale_connections # noqa: PLC0415
|
|
36
|
+
|
|
37
|
+
refresh_stale_connections(quiet=True)
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
if os.environ.get("CLAUDECODE"):
|
|
42
|
+
print(
|
|
43
|
+
'<claude-code-hint v="1" type="plugin" value="agentnet@agentnet-cli" />',
|
|
44
|
+
file=sys.stderr,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command()
|
|
49
|
+
def detect() -> None:
|
|
50
|
+
"""Scan your system for installed AI coding agents."""
|
|
51
|
+
from .detect import detect_all
|
|
52
|
+
from .paths import AgentName, agent_display_name, short_path
|
|
53
|
+
|
|
54
|
+
results = detect_all()
|
|
55
|
+
detected_count = sum(1 for r in results if r.detected)
|
|
56
|
+
connected_count = sum(1 for r in results if r.already_connected)
|
|
57
|
+
ready_count = sum(1 for r in results if r.detected and not r.already_connected)
|
|
58
|
+
|
|
59
|
+
table = Table(
|
|
60
|
+
box=None, pad_edge=False, show_edge=False, padding=(0, 2),
|
|
61
|
+
show_header=True, header_style="bold dim",
|
|
62
|
+
)
|
|
63
|
+
table.add_column("Agent", min_width=18)
|
|
64
|
+
table.add_column("Status", min_width=14)
|
|
65
|
+
table.add_column("Binary")
|
|
66
|
+
|
|
67
|
+
first_ready: str | None = None
|
|
68
|
+
for r in results:
|
|
69
|
+
display = agent_display_name(AgentName(r.agent_name))
|
|
70
|
+
|
|
71
|
+
if r.already_connected:
|
|
72
|
+
status = "[green]● connected[/green]"
|
|
73
|
+
elif r.detected:
|
|
74
|
+
status = "[cyan]● ready[/cyan]"
|
|
75
|
+
if not first_ready:
|
|
76
|
+
first_ready = r.agent_name
|
|
77
|
+
else:
|
|
78
|
+
status = "[dim]○ not found[/dim]"
|
|
79
|
+
|
|
80
|
+
if r.binary_found:
|
|
81
|
+
binary = f"[green]{short_path(r.binary_path)}[/green]"
|
|
82
|
+
elif r.detected:
|
|
83
|
+
binary = "[yellow]not in PATH[/yellow]"
|
|
84
|
+
else:
|
|
85
|
+
binary = "[dim]—[/dim]"
|
|
86
|
+
|
|
87
|
+
table.add_row(display, status, binary)
|
|
88
|
+
|
|
89
|
+
console.print()
|
|
90
|
+
console.print(table)
|
|
91
|
+
|
|
92
|
+
parts: list[str] = []
|
|
93
|
+
parts.append(f"[bold]{detected_count}[/bold]/{len(results)} detected")
|
|
94
|
+
if connected_count:
|
|
95
|
+
parts.append(f"[green]{connected_count} connected[/green]")
|
|
96
|
+
if ready_count:
|
|
97
|
+
parts.append(f"[cyan]{ready_count} ready to connect[/cyan]")
|
|
98
|
+
console.print(f"\n {' · '.join(parts)}")
|
|
99
|
+
|
|
100
|
+
missing_binary = [r for r in results if r.detected and not r.binary_found]
|
|
101
|
+
if missing_binary:
|
|
102
|
+
names = ", ".join(agent_display_name(AgentName(r.agent_name)) for r in missing_binary)
|
|
103
|
+
console.print(f"\n [yellow]![/yellow] Binary not in PATH: {names}")
|
|
104
|
+
console.print(" [dim]Run[/dim] agentnet set-path <agent> <path> [dim]to set a custom location[/dim]")
|
|
105
|
+
|
|
106
|
+
if first_ready:
|
|
107
|
+
console.print(f"\n [dim]Next:[/dim] agentnet connect {first_ready}")
|
|
108
|
+
elif detected_count == 0:
|
|
109
|
+
console.print("\n [dim]No agents found. Install an AI coding agent to get started.[/dim]")
|
|
110
|
+
console.print()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.command()
|
|
114
|
+
def register(
|
|
115
|
+
url: Optional[str] = typer.Option(
|
|
116
|
+
None, "--url", help="Platform URL (default: https://app.agentnet.market)",
|
|
117
|
+
),
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Register with the Agent-net marketplace."""
|
|
120
|
+
from .register import register_command
|
|
121
|
+
|
|
122
|
+
register_command(platform_url=url)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@app.command()
|
|
126
|
+
def connect(
|
|
127
|
+
agent: Optional[str] = typer.Argument(
|
|
128
|
+
None, help="Agent to connect (claude, cursor, copilot, vscode, codex, hermes, openclaw)",
|
|
129
|
+
),
|
|
130
|
+
all_agents: bool = typer.Option(False, "--all", help="Connect all detected agents"),
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Connect an agent to the Agent-net marketplace via MCP."""
|
|
133
|
+
from .connect import connect_command
|
|
134
|
+
|
|
135
|
+
connect_command(agent_name=agent, connect_all=all_agents)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command()
|
|
139
|
+
def disconnect(
|
|
140
|
+
agent: Optional[str] = typer.Argument(None, help="Agent to disconnect"),
|
|
141
|
+
all_agents: bool = typer.Option(False, "--all", help="Disconnect all connected agents"),
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Remove an agent's connection to Agent-net."""
|
|
144
|
+
from .disconnect import disconnect_command
|
|
145
|
+
|
|
146
|
+
disconnect_command(agent_name=agent, disconnect_all=all_agents)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.command()
|
|
150
|
+
def status() -> None:
|
|
151
|
+
"""Show registration and agent connection status."""
|
|
152
|
+
from .status import status_command
|
|
153
|
+
|
|
154
|
+
status_command()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@app.command(name="set-path")
|
|
158
|
+
def set_path(
|
|
159
|
+
agent: str = typer.Argument(
|
|
160
|
+
help="Agent name (claude, cursor, copilot, vscode, codex, hermes, openclaw)",
|
|
161
|
+
),
|
|
162
|
+
path: str = typer.Argument(help="Path to agent binary"),
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Set a custom binary path for an agent."""
|
|
165
|
+
from pathlib import Path as P
|
|
166
|
+
|
|
167
|
+
from .config import save_agent_path
|
|
168
|
+
from .paths import AgentName, agent_display_name
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
display = agent_display_name(AgentName(agent))
|
|
172
|
+
except ValueError:
|
|
173
|
+
console.print(f"[red]Error:[/red] Unknown agent [bold]{agent}[/bold]")
|
|
174
|
+
console.print(" [dim]Available: claude, cursor, copilot, vscode, codex, hermes, openclaw[/dim]")
|
|
175
|
+
raise SystemExit(1)
|
|
176
|
+
|
|
177
|
+
resolved = P(path).expanduser().resolve()
|
|
178
|
+
if not resolved.is_file():
|
|
179
|
+
console.print(f"[yellow]![/yellow] {resolved} does not exist or is not a file")
|
|
180
|
+
console.print(" [dim]Saving anyway — you can update it later.[/dim]")
|
|
181
|
+
|
|
182
|
+
save_agent_path(agent, str(resolved))
|
|
183
|
+
console.print(f"[green]✓[/green] {display} binary path set to [bold]{resolved}[/bold]")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@app.command(name="clear-path")
|
|
187
|
+
def clear_path(
|
|
188
|
+
agent: str = typer.Argument(help="Agent name to clear custom path for"),
|
|
189
|
+
) -> None:
|
|
190
|
+
"""Remove a custom binary path and revert to auto-detection."""
|
|
191
|
+
from .config import remove_agent_path
|
|
192
|
+
from .paths import AgentName, agent_display_name
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
display = agent_display_name(AgentName(agent))
|
|
196
|
+
except ValueError:
|
|
197
|
+
console.print(f"[red]Error:[/red] Unknown agent [bold]{agent}[/bold]")
|
|
198
|
+
console.print(" [dim]Available: claude, cursor, copilot, vscode, codex, hermes, openclaw[/dim]")
|
|
199
|
+
raise SystemExit(1)
|
|
200
|
+
|
|
201
|
+
if remove_agent_path(agent):
|
|
202
|
+
console.print(f"[green]✓[/green] Cleared custom path for {display}")
|
|
203
|
+
else:
|
|
204
|
+
console.print(f"[dim]No custom path set for {display}[/dim]")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@app.command()
|
|
208
|
+
def update() -> None:
|
|
209
|
+
"""Check for updates and refresh agent configs."""
|
|
210
|
+
from .updater import check_pypi_latest, refresh_stale_connections, self_upgrade # noqa: PLC0415
|
|
211
|
+
|
|
212
|
+
console.print()
|
|
213
|
+
|
|
214
|
+
latest = check_pypi_latest()
|
|
215
|
+
if latest is None:
|
|
216
|
+
console.print(" [yellow]![/yellow] Could not reach PyPI — skipping version check")
|
|
217
|
+
console.print(" Refreshing agent configs...")
|
|
218
|
+
n = refresh_stale_connections(quiet=False)
|
|
219
|
+
if not n:
|
|
220
|
+
console.print(" [dim]All agent configs are up to date.[/dim]")
|
|
221
|
+
console.print()
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
if latest != __version__:
|
|
225
|
+
console.print(f" Updating [bold]{__version__}[/bold] -> [bold]{latest}[/bold]...")
|
|
226
|
+
ok, msg = self_upgrade()
|
|
227
|
+
if ok:
|
|
228
|
+
console.print(f" [green]✓[/green] Upgraded to [bold]{msg}[/bold]")
|
|
229
|
+
console.print(" [dim]Agent configs will refresh on next command.[/dim]")
|
|
230
|
+
else:
|
|
231
|
+
console.print(f" [red]✗[/red] Upgrade failed: {msg}")
|
|
232
|
+
console.print(" [dim]Try manually: pip install --upgrade agentnet-cli[/dim]")
|
|
233
|
+
else:
|
|
234
|
+
console.print(f" Already on latest version ([bold]{__version__}[/bold])")
|
|
235
|
+
n = refresh_stale_connections(quiet=False)
|
|
236
|
+
if not n:
|
|
237
|
+
console.print(" [dim]All agent configs are up to date.[/dim]")
|
|
238
|
+
|
|
239
|
+
console.print()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@app.command(name="mcp-serve", hidden=True)
|
|
243
|
+
def mcp_serve() -> None:
|
|
244
|
+
"""Start the AgentNet MCP server (internal)."""
|
|
245
|
+
from .mcp.server import serve
|
|
246
|
+
|
|
247
|
+
serve()
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# -- Marketplace commands --
|
|
251
|
+
from .commands.agent import agent as _agent_fn # noqa: E402
|
|
252
|
+
from .commands.agent import hire as _hire_fn # noqa: E402
|
|
253
|
+
from .commands.discover import agents as _agents_fn # noqa: E402
|
|
254
|
+
from .commands.discover import discover as _discover_fn # noqa: E402
|
|
255
|
+
from .commands.session import session_app # noqa: E402
|
|
256
|
+
from .commands.wallet import wallet_app # noqa: E402
|
|
257
|
+
|
|
258
|
+
app.command(name="discover")(_discover_fn)
|
|
259
|
+
app.command(name="agents")(_agents_fn)
|
|
260
|
+
app.command(name="agent")(_agent_fn)
|
|
261
|
+
app.command(name="hire")(_hire_fn)
|
|
262
|
+
app.add_typer(wallet_app, name="wallet")
|
|
263
|
+
app.add_typer(session_app, name="session")
|
agentnet_cli/manifest.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .config import _atomic_write
|
|
10
|
+
from .paths import agentnet_home
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _manifest_path() -> Path:
|
|
14
|
+
return agentnet_home() / "manifest.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_manifest() -> dict[str, Any]:
|
|
18
|
+
path = _manifest_path()
|
|
19
|
+
if not path.exists():
|
|
20
|
+
return {"connections": {}}
|
|
21
|
+
try:
|
|
22
|
+
return json.loads(path.read_text())
|
|
23
|
+
except json.JSONDecodeError:
|
|
24
|
+
print(f"Warning: {path} is corrupted, ignoring", file=sys.stderr)
|
|
25
|
+
return {"connections": {}}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def save_manifest(data: dict[str, Any]) -> None:
|
|
29
|
+
path = _manifest_path()
|
|
30
|
+
_atomic_write(path, json.dumps(data, indent=2) + "\n", restricted=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def record_connection(
|
|
34
|
+
agent_name: str,
|
|
35
|
+
*,
|
|
36
|
+
files_created: list[Path],
|
|
37
|
+
files_modified: list[tuple[Path, Path]] | list[Any],
|
|
38
|
+
mcp_entry: dict[str, Any],
|
|
39
|
+
) -> None:
|
|
40
|
+
from . import __version__ # noqa: PLC0415
|
|
41
|
+
|
|
42
|
+
m = load_manifest()
|
|
43
|
+
m["connections"][agent_name] = {
|
|
44
|
+
"connected_at": datetime.now(UTC).isoformat(),
|
|
45
|
+
"cli_version": __version__,
|
|
46
|
+
"files_created": [str(p) for p in files_created],
|
|
47
|
+
"files_modified": [
|
|
48
|
+
{"path": str(p), "backup": str(b)} for p, b in files_modified
|
|
49
|
+
] if files_modified and isinstance(files_modified[0], tuple) else [],
|
|
50
|
+
"mcp_registered": mcp_entry,
|
|
51
|
+
}
|
|
52
|
+
save_manifest(m)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def remove_connection(agent_name: str) -> None:
|
|
56
|
+
m = load_manifest()
|
|
57
|
+
m["connections"].pop(agent_name, None)
|
|
58
|
+
save_manifest(m)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any, NoReturn
|
|
6
|
+
|
|
7
|
+
from .config import load_config
|
|
8
|
+
from .platform.client import PlatformClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_client() -> PlatformClient:
|
|
12
|
+
token = os.environ.get("AGENTNET_TOKEN", "")
|
|
13
|
+
config = load_config()
|
|
14
|
+
if not token and config:
|
|
15
|
+
token = config.get("api_token", "")
|
|
16
|
+
if not token:
|
|
17
|
+
die("Not authenticated. Run 'agentnet register' 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"
|
|
23
|
+
return PlatformClient(base_url=platform_url, api_token=token)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_agent_id() -> str:
|
|
27
|
+
config = load_config()
|
|
28
|
+
if not config or not config.get("agent_id"):
|
|
29
|
+
die("No agent registered. Run 'agentnet register' first.")
|
|
30
|
+
return config["agent_id"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def output(data: Any) -> None:
|
|
34
|
+
print(json.dumps(data, indent=2))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def die(message: str) -> NoReturn:
|
|
38
|
+
print(json.dumps({"error": message}))
|
|
39
|
+
raise SystemExit(1)
|