fere-cli 0.5.0.dev53__py3-none-any.whl → 1.0.0.dev55__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.
- fere_cli/__init__.py +1 -1
- fere_cli/banner.py +1 -1
- fere_cli/main.py +2 -8
- fere_cli/output.py +0 -7
- {fere_cli-0.5.0.dev53.dist-info → fere_cli-1.0.0.dev55.dist-info}/METADATA +12 -23
- {fere_cli-0.5.0.dev53.dist-info → fere_cli-1.0.0.dev55.dist-info}/RECORD +8 -9
- {fere_cli-0.5.0.dev53.dist-info → fere_cli-1.0.0.dev55.dist-info}/WHEEL +1 -1
- fere_cli/commands/chat.py +0 -188
- {fere_cli-0.5.0.dev53.dist-info → fere_cli-1.0.0.dev55.dist-info}/entry_points.txt +0 -0
fere_cli/__init__.py
CHANGED
fere_cli/banner.py
CHANGED
fere_cli/main.py
CHANGED
|
@@ -6,12 +6,11 @@ import click
|
|
|
6
6
|
|
|
7
7
|
from . import __version__
|
|
8
8
|
from .commands.auth import auth, credits, whoami
|
|
9
|
-
from .commands.chat import chat, threads
|
|
10
9
|
from .commands.earn import earn
|
|
11
10
|
from .commands.hooks import hooks
|
|
12
11
|
from .commands.limit_order import limit_order
|
|
13
12
|
from .commands.perp import perp
|
|
14
|
-
from .commands.portfolio import holdings,
|
|
13
|
+
from .commands.portfolio import holdings, wallets
|
|
15
14
|
from .commands.spot_hl import spot_hl
|
|
16
15
|
from .commands.swap import swap
|
|
17
16
|
from .commands.utility import chains, config, status
|
|
@@ -47,7 +46,7 @@ from .update import maybe_check_for_updates
|
|
|
47
46
|
@click.version_option(version=__version__, prog_name="fere")
|
|
48
47
|
@click.pass_context
|
|
49
48
|
def cli(ctx, output_json, quiet, agent, base_url):
|
|
50
|
-
"""FereAI CLI — crypto trading
|
|
49
|
+
"""FereAI CLI — crypto trading from your terminal."""
|
|
51
50
|
ctx.ensure_object(dict)
|
|
52
51
|
ctx.obj["output_json"] = output_json
|
|
53
52
|
ctx.obj["quiet"] = quiet
|
|
@@ -75,10 +74,6 @@ cli.add_command(auth)
|
|
|
75
74
|
cli.add_command(whoami)
|
|
76
75
|
cli.add_command(credits)
|
|
77
76
|
|
|
78
|
-
# Chat
|
|
79
|
-
cli.add_command(chat)
|
|
80
|
-
cli.add_command(threads)
|
|
81
|
-
|
|
82
77
|
# Trading
|
|
83
78
|
cli.add_command(swap)
|
|
84
79
|
cli.add_command(limit_order, name="limit-order")
|
|
@@ -87,7 +82,6 @@ cli.add_command(hooks)
|
|
|
87
82
|
# Portfolio
|
|
88
83
|
cli.add_command(wallets)
|
|
89
84
|
cli.add_command(holdings)
|
|
90
|
-
cli.add_command(notifications)
|
|
91
85
|
|
|
92
86
|
# Earn
|
|
93
87
|
cli.add_command(earn)
|
fere_cli/output.py
CHANGED
|
@@ -9,7 +9,6 @@ Supports three modes:
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import json as json_lib
|
|
12
|
-
import sys
|
|
13
12
|
from typing import Any
|
|
14
13
|
|
|
15
14
|
import click
|
|
@@ -126,9 +125,3 @@ def print_table_from_dicts(
|
|
|
126
125
|
for row in data:
|
|
127
126
|
table.add_row(*[str(row.get(c, "")) for c in cols])
|
|
128
127
|
console.print(table)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def print_streaming_text(text: str) -> None:
|
|
132
|
-
"""Print streaming text chunk without newline."""
|
|
133
|
-
sys.stdout.write(text)
|
|
134
|
-
sys.stdout.flush()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fere-cli
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Terminal CLI for FereAI crypto trading
|
|
3
|
+
Version: 1.0.0.dev55
|
|
4
|
+
Summary: Terminal CLI for FereAI crypto trading
|
|
5
5
|
Author-email: Fere AI <hello@fereai.xyz>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
8
|
Requires-Dist: click>=8.1
|
|
9
|
-
Requires-Dist: fere-sdk>=0.
|
|
9
|
+
Requires-Dist: fere-sdk>=1.0.0.dev55
|
|
10
10
|
Requires-Dist: httpx>=0.27
|
|
11
11
|
Requires-Dist: rich>=13.0
|
|
12
12
|
Provides-Extra: dev
|
|
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
|
|
17
17
|
# FereAI CLI
|
|
18
18
|
|
|
19
|
-
Terminal-first interface for FereAI crypto trading
|
|
19
|
+
Terminal-first interface for FereAI crypto trading. Wraps the [FereAI Gateway API](https://api.fereai.xyz) — same capabilities as the Python and TypeScript SDKs.
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
@@ -38,15 +38,6 @@ pip install fere-cli
|
|
|
38
38
|
# Authenticate (first-run — creates agent, generates keys)
|
|
39
39
|
fere auth
|
|
40
40
|
|
|
41
|
-
# Chat with the AI agent
|
|
42
|
-
fere chat "what is the price of ETH?"
|
|
43
|
-
|
|
44
|
-
# Interactive chat REPL
|
|
45
|
-
fere chat
|
|
46
|
-
|
|
47
|
-
# Stream responses in real-time
|
|
48
|
-
fere chat --stream "top 5 memecoins on Base"
|
|
49
|
-
|
|
50
41
|
# Check your wallets and holdings
|
|
51
42
|
fere wallets
|
|
52
43
|
fere holdings
|
|
@@ -77,12 +68,6 @@ fere --json holdings
|
|
|
77
68
|
# {"ok": true, "data": {...}}
|
|
78
69
|
```
|
|
79
70
|
|
|
80
|
-
Streaming chat in JSON mode outputs NDJSON (one event per line):
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
fere --json chat --stream "price of SOL"
|
|
84
|
-
```
|
|
85
|
-
|
|
86
71
|
## Commands
|
|
87
72
|
|
|
88
73
|
| Command | Description |
|
|
@@ -90,8 +75,6 @@ fere --json chat --stream "price of SOL"
|
|
|
90
75
|
| `fere auth` | Authenticate and register agent |
|
|
91
76
|
| `fere whoami` | Show agent identity and wallets |
|
|
92
77
|
| `fere credits` | Check credit balance |
|
|
93
|
-
| `fere chat [QUERY]` | Chat with AI (one-shot or REPL) |
|
|
94
|
-
| `fere threads` | List chat threads |
|
|
95
78
|
| `fere swap` | Execute token swap |
|
|
96
79
|
| `fere limit-order create` | Create limit order |
|
|
97
80
|
| `fere limit-order list` | List limit orders |
|
|
@@ -99,7 +82,6 @@ fere --json chat --stream "price of SOL"
|
|
|
99
82
|
| `fere hooks set` | Set stop-loss / take-profit |
|
|
100
83
|
| `fere wallets` | Show wallet addresses |
|
|
101
84
|
| `fere holdings` | Show token holdings |
|
|
102
|
-
| `fere notifications` | Show notifications |
|
|
103
85
|
| `fere earn info` | Show APY and vault info |
|
|
104
86
|
| `fere earn deposit` | Deposit USDC |
|
|
105
87
|
| `fere earn withdraw` | Withdraw USDC |
|
|
@@ -108,6 +90,14 @@ fere --json chat --stream "price of SOL"
|
|
|
108
90
|
| `fere status` | Check API connectivity |
|
|
109
91
|
| `fere config` | Show/edit CLI config |
|
|
110
92
|
|
|
93
|
+
## 1.0.0 — breaking changes
|
|
94
|
+
|
|
95
|
+
- Removed `fere chat` and `fere threads`. Chat, chat threads and scheduled
|
|
96
|
+
chats are no longer part of the public FereAI API.
|
|
97
|
+
- `fere notifications` is no longer registered (notifications are not a
|
|
98
|
+
released public feature).
|
|
99
|
+
- Requires `fere-sdk>=1.0.0`.
|
|
100
|
+
|
|
111
101
|
## Global Flags
|
|
112
102
|
|
|
113
103
|
| Flag | Env Var | Description |
|
|
@@ -132,5 +122,4 @@ fere config set base_url http://localhost:8003 # Point to local gateway
|
|
|
132
122
|
- Exit codes: 0 success, 1 error
|
|
133
123
|
- Non-interactive when stdin is not a TTY (all prompts skipped)
|
|
134
124
|
- `--json` output: `{"ok": true, "data": {...}}` or `{"ok": false, "error": "..."}`
|
|
135
|
-
- Streaming JSON: NDJSON format
|
|
136
125
|
- No ANSI colors when piped
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
fere_cli/__init__.py,sha256=
|
|
1
|
+
fere_cli/__init__.py,sha256=Ms4hFy5GMInGDkYDgSUnHFSbCO3lYyDj-C1_g2B8fto,328
|
|
2
2
|
fere_cli/async_util.py,sha256=Ai832fD956lcDdYrTSbe59iU1Dl59rRZRODFz3KA9lU,1562
|
|
3
|
-
fere_cli/banner.py,sha256=
|
|
3
|
+
fere_cli/banner.py,sha256=KWpsAoGeTj59yTIf_IpHASpt-C1IqOyngBZVeMAytHY,812
|
|
4
4
|
fere_cli/config.py,sha256=BIVr6ViLCWCEloWbxF0aekS4uSrvbUVzbSN1q9dHh_o,1574
|
|
5
|
-
fere_cli/main.py,sha256=
|
|
6
|
-
fere_cli/output.py,sha256=
|
|
5
|
+
fere_cli/main.py,sha256=KGpiK95vtBp1CIFgVnCuZkAE2_XibpTiSuZaDTKDGVw,2491
|
|
6
|
+
fere_cli/output.py,sha256=pQtQj6fDnhRmn0lK47tHzC7REgOglTzela-iwvfyXfY,3485
|
|
7
7
|
fere_cli/update.py,sha256=vsdQTC_ue6xLpwvJGVmNt89XT64vfYV_S-vtumJEDiM,9731
|
|
8
8
|
fere_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
fere_cli/commands/auth.py,sha256=0tyoQZuGRNm53ZB88kLbG4cqfay_HR8Ju5A63e08J-s,2550
|
|
10
|
-
fere_cli/commands/chat.py,sha256=YF_bI3HBY6vOrPMYjRrvPWP74kfqwIBIcAfb9ZI47_c,5106
|
|
11
10
|
fere_cli/commands/earn.py,sha256=g5UMvzztRZUSbIAO0P-G4yqjQl30c12ceagzxzEcrws,3575
|
|
12
11
|
fere_cli/commands/hooks.py,sha256=e3A1TD_qBt-I3zbQ_Wyf3oWhSceCQwFx0Wvn17Z74R4,2007
|
|
13
12
|
fere_cli/commands/limit_order.py,sha256=VLddA7wEMVOALVO9RrD4W2bAZ2C611zqAJ6MPxEikhU,4259
|
|
@@ -16,7 +15,7 @@ fere_cli/commands/portfolio.py,sha256=7JJOIolmlud6yWStw7iv9GCOH1igOilKUJzPr7ixx2
|
|
|
16
15
|
fere_cli/commands/spot_hl.py,sha256=qQ6e8EyQK4bSrrm57cq0d3RUS-PC71cMjy2Rcn293k0,6983
|
|
17
16
|
fere_cli/commands/swap.py,sha256=Em96kUjBU2IB_FRNmIrGrWEjrQ0vcdbqi59aU0yNfdA,2609
|
|
18
17
|
fere_cli/commands/utility.py,sha256=0icyDsouq-NchWg_CY2WdrH9VoQjg1WBybkkmZgknAg,2198
|
|
19
|
-
fere_cli-0.
|
|
20
|
-
fere_cli-0.
|
|
21
|
-
fere_cli-0.
|
|
22
|
-
fere_cli-0.
|
|
18
|
+
fere_cli-1.0.0.dev55.dist-info/METADATA,sha256=7VGYojYZhpb6GU3qHUUeMnWIhp5XyJqpvSlNvGlv168,3542
|
|
19
|
+
fere_cli-1.0.0.dev55.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
20
|
+
fere_cli-1.0.0.dev55.dist-info/entry_points.txt,sha256=vOltZxMv_RJwS1AqO3UstqpRp74xPd4UMlf62Ma-fWM,43
|
|
21
|
+
fere_cli-1.0.0.dev55.dist-info/RECORD,,
|
fere_cli/commands/chat.py
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
"""Chat commands: fere chat, fere threads."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
|
|
7
|
-
import click
|
|
8
|
-
from rich.console import Console
|
|
9
|
-
|
|
10
|
-
from ..async_util import async_command, get_client, is_tty
|
|
11
|
-
from ..output import (
|
|
12
|
-
is_json_mode,
|
|
13
|
-
print_error,
|
|
14
|
-
print_streaming_text,
|
|
15
|
-
print_success,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
console = Console()
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@click.command()
|
|
22
|
-
@click.argument("query", required=False, default=None)
|
|
23
|
-
@click.option(
|
|
24
|
-
"--stream",
|
|
25
|
-
"stream_mode",
|
|
26
|
-
is_flag=True,
|
|
27
|
-
default=False,
|
|
28
|
-
help="Stream response events as they arrive.",
|
|
29
|
-
)
|
|
30
|
-
@click.option(
|
|
31
|
-
"--thread",
|
|
32
|
-
"thread_id",
|
|
33
|
-
default=None,
|
|
34
|
-
help="Continue an existing chat thread.",
|
|
35
|
-
)
|
|
36
|
-
@click.option(
|
|
37
|
-
"--agent",
|
|
38
|
-
"agent_type",
|
|
39
|
-
default="ProAgent",
|
|
40
|
-
help="Agent type to use (default: ProAgent).",
|
|
41
|
-
)
|
|
42
|
-
@click.pass_context
|
|
43
|
-
@async_command
|
|
44
|
-
async def chat(ctx, query, stream_mode, thread_id, agent_type):
|
|
45
|
-
"""Chat with FereAI's crypto AI agent.
|
|
46
|
-
|
|
47
|
-
Pass a query as an argument for one-shot mode, or run without
|
|
48
|
-
arguments to enter interactive REPL mode.
|
|
49
|
-
"""
|
|
50
|
-
try:
|
|
51
|
-
client = await get_client(ctx)
|
|
52
|
-
except click.ClickException:
|
|
53
|
-
raise
|
|
54
|
-
except Exception as e:
|
|
55
|
-
print_error(str(e))
|
|
56
|
-
raise SystemExit(1)
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
if query:
|
|
60
|
-
# One-shot mode
|
|
61
|
-
if stream_mode:
|
|
62
|
-
await _stream_chat(client, query, thread_id, agent_type)
|
|
63
|
-
else:
|
|
64
|
-
await _oneshot_chat(client, query, thread_id, agent_type)
|
|
65
|
-
elif is_tty():
|
|
66
|
-
# Interactive REPL
|
|
67
|
-
await _repl_chat(client, thread_id, agent_type)
|
|
68
|
-
else:
|
|
69
|
-
raise click.ClickException(
|
|
70
|
-
"Query required in non-interactive mode. "
|
|
71
|
-
'Usage: fere chat "your question"'
|
|
72
|
-
)
|
|
73
|
-
except click.ClickException:
|
|
74
|
-
raise
|
|
75
|
-
except KeyboardInterrupt:
|
|
76
|
-
pass
|
|
77
|
-
except Exception as e:
|
|
78
|
-
print_error(str(e))
|
|
79
|
-
raise SystemExit(1)
|
|
80
|
-
finally:
|
|
81
|
-
await client.close()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
async def _oneshot_chat(client, query, thread_id, agent_type):
|
|
85
|
-
"""Send a query and print the final answer."""
|
|
86
|
-
result = await client.chat(query, thread_id=thread_id, agent=agent_type)
|
|
87
|
-
print_success(
|
|
88
|
-
result,
|
|
89
|
-
quiet_value=result.get("answer", ""),
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
async def _stream_chat(client, query, thread_id, agent_type):
|
|
94
|
-
"""Stream chat events, printing each as it arrives."""
|
|
95
|
-
json_mode = is_json_mode()
|
|
96
|
-
|
|
97
|
-
async for event in client.chat_stream(
|
|
98
|
-
query, thread_id=thread_id, agent=agent_type
|
|
99
|
-
):
|
|
100
|
-
if json_mode:
|
|
101
|
-
# NDJSON: one JSON object per line
|
|
102
|
-
click.echo(
|
|
103
|
-
json.dumps(
|
|
104
|
-
{"event": event.event, "data": event.data},
|
|
105
|
-
default=str,
|
|
106
|
-
)
|
|
107
|
-
)
|
|
108
|
-
else:
|
|
109
|
-
if event.event == "chunk":
|
|
110
|
-
text = event.data.get("text", "")
|
|
111
|
-
print_streaming_text(text)
|
|
112
|
-
elif event.event == "answer":
|
|
113
|
-
text = event.data.get("text", "")
|
|
114
|
-
if text:
|
|
115
|
-
click.echo() # newline after chunks
|
|
116
|
-
console.print(f"\n[bold]Answer:[/bold] {text}")
|
|
117
|
-
elif event.event == "tool_response":
|
|
118
|
-
console.print(
|
|
119
|
-
f"[dim]Tool: {json.dumps(event.data, default=str)}[/dim]"
|
|
120
|
-
)
|
|
121
|
-
elif event.event == "error":
|
|
122
|
-
msg = event.data.get("message", str(event.data))
|
|
123
|
-
print_error(msg)
|
|
124
|
-
elif event.event == "meta":
|
|
125
|
-
chat_id = event.data.get("chat_id", "")
|
|
126
|
-
console.print(f"[dim]Chat: {chat_id}[/dim]")
|
|
127
|
-
|
|
128
|
-
if not json_mode:
|
|
129
|
-
click.echo() # final newline
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
async def _repl_chat(client, thread_id, agent_type):
|
|
133
|
-
"""Interactive REPL loop."""
|
|
134
|
-
console.print(
|
|
135
|
-
"[bold]FereAI Chat[/bold] "
|
|
136
|
-
"[dim](type /exit or Ctrl+D to quit)[/dim]\n"
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
current_thread = thread_id
|
|
140
|
-
|
|
141
|
-
while True:
|
|
142
|
-
try:
|
|
143
|
-
query = input(">>> ")
|
|
144
|
-
except EOFError:
|
|
145
|
-
click.echo()
|
|
146
|
-
break
|
|
147
|
-
|
|
148
|
-
query = query.strip()
|
|
149
|
-
if not query:
|
|
150
|
-
continue
|
|
151
|
-
if query in ("/exit", "/quit"):
|
|
152
|
-
break
|
|
153
|
-
|
|
154
|
-
result = await client.chat(
|
|
155
|
-
query, thread_id=current_thread, agent=agent_type
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Track thread for continuity
|
|
159
|
-
if "chat_id" in result and not current_thread:
|
|
160
|
-
current_thread = result["chat_id"]
|
|
161
|
-
|
|
162
|
-
answer = result.get("answer", "")
|
|
163
|
-
if answer:
|
|
164
|
-
console.print(f"\n{answer}\n")
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
@click.command()
|
|
168
|
-
@click.option(
|
|
169
|
-
"--limit",
|
|
170
|
-
default=10,
|
|
171
|
-
help="Number of threads to show.",
|
|
172
|
-
)
|
|
173
|
-
@click.option("--skip", default=0, help="Offset.")
|
|
174
|
-
@click.pass_context
|
|
175
|
-
@async_command
|
|
176
|
-
async def threads(ctx, limit, skip):
|
|
177
|
-
"""List recent chat threads."""
|
|
178
|
-
try:
|
|
179
|
-
client = await get_client(ctx)
|
|
180
|
-
data = await client.get_threads(skip=skip, limit=limit)
|
|
181
|
-
await client.close()
|
|
182
|
-
|
|
183
|
-
print_success(data)
|
|
184
|
-
except click.ClickException:
|
|
185
|
-
raise
|
|
186
|
-
except Exception as e:
|
|
187
|
-
print_error(str(e))
|
|
188
|
-
raise SystemExit(1)
|
|
File without changes
|