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.
Files changed (53) hide show
  1. agentnet_cli/__init__.py +6 -0
  2. agentnet_cli/agents/README.md +55 -0
  3. agentnet_cli/agents/__init__.py +0 -0
  4. agentnet_cli/agents/base.py +37 -0
  5. agentnet_cli/agents/claude.py +114 -0
  6. agentnet_cli/agents/codex.py +78 -0
  7. agentnet_cli/agents/copilot.py +89 -0
  8. agentnet_cli/agents/cursor.py +90 -0
  9. agentnet_cli/agents/hermes.py +171 -0
  10. agentnet_cli/agents/openclaw.py +54 -0
  11. agentnet_cli/agents/registry.py +28 -0
  12. agentnet_cli/agents/shims.py +9 -0
  13. agentnet_cli/agents/vscode.py +119 -0
  14. agentnet_cli/commands/__init__.py +0 -0
  15. agentnet_cli/commands/agent.py +32 -0
  16. agentnet_cli/commands/discover.py +34 -0
  17. agentnet_cli/commands/session.py +35 -0
  18. agentnet_cli/commands/wallet.py +48 -0
  19. agentnet_cli/config.py +68 -0
  20. agentnet_cli/connect.py +73 -0
  21. agentnet_cli/detect.py +23 -0
  22. agentnet_cli/disconnect.py +60 -0
  23. agentnet_cli/hermes_plugin/__init__.py +36 -0
  24. agentnet_cli/hermes_plugin/handlers.py +69 -0
  25. agentnet_cli/hermes_plugin/plugin.yaml +17 -0
  26. agentnet_cli/hermes_plugin/schemas.py +182 -0
  27. agentnet_cli/hermes_plugin/skills/agentnet/SKILL.md +46 -0
  28. agentnet_cli/main.py +263 -0
  29. agentnet_cli/manifest.py +58 -0
  30. agentnet_cli/marketplace.py +39 -0
  31. agentnet_cli/mcp/README.md +64 -0
  32. agentnet_cli/mcp/__init__.py +0 -0
  33. agentnet_cli/mcp/server.py +244 -0
  34. agentnet_cli/mcp/tools.py +67 -0
  35. agentnet_cli/paths.py +91 -0
  36. agentnet_cli/platform/README.md +79 -0
  37. agentnet_cli/platform/__init__.py +0 -0
  38. agentnet_cli/platform/client.py +134 -0
  39. agentnet_cli/register.py +146 -0
  40. agentnet_cli/shims/README.md +48 -0
  41. agentnet_cli/shims/SKILL.md +225 -0
  42. agentnet_cli/shims/codex/skill.md +8 -0
  43. agentnet_cli/shims/copilot/agentnet.agent.md +14 -0
  44. agentnet_cli/shims/cursor/agent.md +9 -0
  45. agentnet_cli/shims/cursor/agentnet.mdc +8 -0
  46. agentnet_cli/shims/shared/context.md +62 -0
  47. agentnet_cli/shims/vscode/instructions.md +3 -0
  48. agentnet_cli/status.py +65 -0
  49. agentnet_cli/updater.py +123 -0
  50. agentnet_cli-0.1.0.dist-info/METADATA +253 -0
  51. agentnet_cli-0.1.0.dist-info/RECORD +53 -0
  52. agentnet_cli-0.1.0.dist-info/WHEEL +4 -0
  53. 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")
@@ -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)