@jellyos/agent 0.1.3 → 0.1.5
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.
- package/README.md +9 -9
- package/README.npm.md +212 -0
- package/bin/jellyos-mcp +26 -0
- package/dist/api/ExtensionAPI.d.ts +6 -0
- package/dist/api/Registry.js +3 -1
- package/dist/cli.js +117 -42
- package/dist/index.d.ts +24 -1
- package/dist/index.js +19 -2
- package/dist/mcp/entry.d.ts +2 -0
- package/dist/mcp/entry.js +71 -0
- package/dist/mcp/server.d.ts +31 -0
- package/dist/mcp/server.js +128 -0
- package/dist/models/CostTracker.d.ts +66 -0
- package/dist/models/CostTracker.js +148 -0
- package/dist/models/ModelRegistry.d.ts +157 -0
- package/dist/models/ModelRegistry.js +496 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/models/index.js +3 -0
- package/dist/runner/AgentRunner.d.ts +23 -2
- package/dist/runner/AgentRunner.js +264 -24
- package/dist/runner/ModelClient.d.ts +26 -6
- package/dist/runner/ModelClient.js +147 -28
- package/dist/runner/SwarmRouter.d.ts +10 -7
- package/dist/runner/SwarmRouter.js +85 -28
- package/dist/runner/ToolDispatcher.d.ts +10 -0
- package/dist/runner/ToolDispatcher.js +106 -2
- package/dist/scheduler/AgentScheduler.d.ts +118 -0
- package/dist/scheduler/AgentScheduler.js +253 -0
- package/dist/session/ContextStore.d.ts +96 -0
- package/dist/session/ContextStore.js +207 -0
- package/dist/session/GoalManager.d.ts +101 -0
- package/dist/session/GoalManager.js +167 -0
- package/dist/session/MemoryStore.d.ts +48 -0
- package/dist/session/MemoryStore.js +166 -0
- package/dist/session/SessionManager.d.ts +45 -4
- package/dist/session/SessionManager.js +151 -8
- package/dist/telemetry/Tracer.d.ts +48 -0
- package/dist/telemetry/Tracer.js +102 -0
- package/dist/tests/ContextStore.test.d.ts +2 -0
- package/dist/tests/ContextStore.test.js +74 -0
- package/dist/tests/ModelRegistry.test.d.ts +2 -0
- package/dist/tests/ModelRegistry.test.js +69 -0
- package/dist/tests/SessionManager.test.d.ts +2 -0
- package/dist/tests/SessionManager.test.js +108 -0
- package/dist/tests/TechnicalAnalysis.test.d.ts +2 -0
- package/dist/tests/TechnicalAnalysis.test.js +109 -0
- package/dist/tools/MarketSentiment.d.ts +166 -0
- package/dist/tools/MarketSentiment.js +209 -0
- package/dist/tools/NewsSentiment.d.ts +67 -0
- package/dist/tools/NewsSentiment.js +226 -0
- package/dist/tools/PriceFeed.d.ts +105 -0
- package/dist/tools/PriceFeed.js +282 -0
- package/dist/tools/TechnicalAnalysis.d.ts +110 -0
- package/dist/tools/TechnicalAnalysis.js +357 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +4 -0
- package/dist/tui/App.d.ts +7 -5
- package/dist/tui/App.js +350 -65
- package/dist/tui/REPL.d.ts +2 -1
- package/dist/tui/REPL.js +11 -6
- package/dist/tui/StatusBar.js +1 -1
- package/package.json +9 -4
- package/dist/api/ExtensionAPI.d.ts.map +0 -1
- package/dist/api/ExtensionAPI.js.map +0 -1
- package/dist/api/Registry.d.ts.map +0 -1
- package/dist/api/Registry.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js.map +0 -1
- package/dist/runner/AgentRunner.d.ts.map +0 -1
- package/dist/runner/AgentRunner.js.map +0 -1
- package/dist/runner/ModelClient.d.ts.map +0 -1
- package/dist/runner/ModelClient.js.map +0 -1
- package/dist/runner/SwarmRouter.d.ts.map +0 -1
- package/dist/runner/SwarmRouter.js.map +0 -1
- package/dist/runner/ToolDispatcher.d.ts.map +0 -1
- package/dist/runner/ToolDispatcher.js.map +0 -1
- package/dist/session/SessionManager.d.ts.map +0 -1
- package/dist/session/SessionManager.js.map +0 -1
- package/dist/tui/App.d.ts.map +0 -1
- package/dist/tui/App.js.map +0 -1
- package/dist/tui/REPL.d.ts.map +0 -1
- package/dist/tui/REPL.js.map +0 -1
- package/dist/tui/StatusBar.d.ts.map +0 -1
- package/dist/tui/StatusBar.js.map +0 -1
- package/dist/tui/theme.d.ts.map +0 -1
- package/dist/tui/theme.js.map +0 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Your machine
|
|
|
39
39
|
│ ├── VaultManager ← AES-256-GCM encrypted profit vault
|
|
40
40
|
│ └── DashboardServer ← optional local WebSocket dashboard
|
|
41
41
|
│
|
|
42
|
-
├── ~/.
|
|
42
|
+
├── ~/.jelly/
|
|
43
43
|
│ ├── .env ← your API keys
|
|
44
44
|
│ ├── wallets/ ← local keypairs (never synced)
|
|
45
45
|
│ ├── vault/ ← encrypted vault file
|
|
@@ -68,8 +68,8 @@ npm install -g @jellyos/agent
|
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
# 1. Create config directory and set your API key
|
|
71
|
-
mkdir -p ~/.
|
|
72
|
-
echo "OPENROUTER_API_KEY=sk-or-..." > ~/.
|
|
71
|
+
mkdir -p ~/.jelly
|
|
72
|
+
echo "OPENROUTER_API_KEY=sk-or-..." > ~/.jelly/.env
|
|
73
73
|
|
|
74
74
|
# 2. Launch
|
|
75
75
|
jellyos
|
|
@@ -91,7 +91,7 @@ cd JellyOS
|
|
|
91
91
|
# Install dependencies
|
|
92
92
|
npm install
|
|
93
93
|
|
|
94
|
-
# Run one-command setup (generates wallets, vault ceremony, writes ~/.
|
|
94
|
+
# Run one-command setup (generates wallets, vault ceremony, writes ~/.jelly/.env)
|
|
95
95
|
bash setup.sh # macOS / Linux
|
|
96
96
|
# or
|
|
97
97
|
powershell -ExecutionPolicy Bypass -File setup.ps1 # Windows
|
|
@@ -104,11 +104,11 @@ jellyos
|
|
|
104
104
|
|
|
105
105
|
## Configuration
|
|
106
106
|
|
|
107
|
-
All config lives in `~/.
|
|
107
|
+
All config lives in `~/.jelly/.env`. The setup wizard creates this for you, or create it manually:
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
|
-
cp .env.example ~/.
|
|
111
|
-
nano ~/.
|
|
110
|
+
cp .env.example ~/.jelly/.env
|
|
111
|
+
nano ~/.jelly/.env
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
### AI Model Provider (pick one)
|
|
@@ -362,11 +362,11 @@ jellyos setup # run the setup wizard
|
|
|
362
362
|
|
|
363
363
|
## Security
|
|
364
364
|
|
|
365
|
-
- **Keys stay local** — API keys are read from `~/.
|
|
365
|
+
- **Keys stay local** — API keys are read from `~/.jelly/.env` at startup and never logged or transmitted beyond outbound API calls
|
|
366
366
|
- **Private keys never leave the process** — signing happens in memory; only the resulting signature is returned
|
|
367
367
|
- **Vault encryption** — AES-256-GCM with a key derived from your passphrase using scrypt (memory-hard KDF) + random per-vault salt; the key itself is never persisted
|
|
368
368
|
- **Auto-lock** — vault locks automatically on `/panic` and on process exit
|
|
369
|
-
- **Wallet storage** — keypairs are written to `~/.
|
|
369
|
+
- **Wallet storage** — keypairs are written to `~/.jelly/wallets/` which is in `.gitignore` and never included in any sync or backup by the agent
|
|
370
370
|
|
|
371
371
|
---
|
|
372
372
|
|
package/README.npm.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @jellyos/agent
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<pre>
|
|
6
|
+
██╗███████╗██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗
|
|
7
|
+
██║██╔════╝██║ ██║ ╚██╗ ██╔╝ ██╔═══██╗██╔════╝
|
|
8
|
+
██║█████╗ ██║ ██║ ╚████╔╝ ██║ ██║███████╗
|
|
9
|
+
██ ██║██╔══╝ ██║ ██║ ╚██╔╝ ██║ ██║╚════██║
|
|
10
|
+
╚█████╔╝███████╗███████╗██║ ██║ ╚██████╔╝███████║
|
|
11
|
+
╚════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
12
|
+
</pre>
|
|
13
|
+
|
|
14
|
+
**Autonomous AI trading agent. Runs 100% locally. No server. No inbound ports.**
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@jellyos/agent)
|
|
17
|
+
[](https://nodejs.org)
|
|
18
|
+
[](LICENSE)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## What's in this package
|
|
25
|
+
|
|
26
|
+
This NPM package contains the **core JellyOS agent engine**:
|
|
27
|
+
|
|
28
|
+
| Component | Description |
|
|
29
|
+
|-----------|-------------|
|
|
30
|
+
| `AgentRunner` | Multi-model agentic loop with tool dispatch, swarm routing, reflection |
|
|
31
|
+
| `ModelClient` | Streaming OpenAI-compatible client with fallback rotation + thinking mode |
|
|
32
|
+
| `ModelRegistry` | Dynamic model discovery, tier classification, cost tracking (356 models) |
|
|
33
|
+
| `SwarmRouter` | LLM-based task decomposition + parallel sub-agent execution |
|
|
34
|
+
| `SessionManager` | Conversation history with 3-tier smart compaction + turbo headroom tracking |
|
|
35
|
+
| `MemoryStore` | SQLite long-term memory (cross-session, persistent) |
|
|
36
|
+
| `GoalManager` | Persistent cross-session goals injected into every turn |
|
|
37
|
+
| `ContextStore` | Ephemeral task context folders (auto-deleted on completion) |
|
|
38
|
+
| `AgentScheduler` | Cron + price-trigger autonomous task scheduling |
|
|
39
|
+
| `MCPServer` | Model Context Protocol server (Claude Desktop / Cursor compatible) |
|
|
40
|
+
| **18 built-in tools** | Prices, candles+TA, news, Fear&Greed, funding rates, BTC mempool, DeFi TVL, Solana TPS, model management, goals, tasks, scheduling |
|
|
41
|
+
| Ink TUI | Terminal UI with live ticker, context pressure monitor, approval gates |
|
|
42
|
+
|
|
43
|
+
For wallets, vault, on-chain signing, and the full 28-tool trading suite → [JellyOS full project](https://github.com/jelly-chain/JellyOS)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g @jellyos/agent
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mkdir -p ~/.jelly
|
|
57
|
+
echo "OPENROUTER_API_KEY=sk-or-..." > ~/.jelly/.env
|
|
58
|
+
jellyos
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Headless Mode
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Single turn, print to stdout, exit
|
|
65
|
+
jellyos --headless "what is the current ETH price and RSI on the 1h chart?"
|
|
66
|
+
|
|
67
|
+
# Use in scripts
|
|
68
|
+
RESULT=$(jellyos --headless "summarize BTC market conditions")
|
|
69
|
+
echo "$RESULT"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## MCP Server (Claude Desktop / Cursor)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Start MCP server — exposes all 18 tools to any MCP client
|
|
76
|
+
jellyos-mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"jellyos": {
|
|
84
|
+
"command": "jellyos-mcp"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Configuration (`~/.jelly/.env`)
|
|
93
|
+
|
|
94
|
+
```env
|
|
95
|
+
# AI Provider (pick one)
|
|
96
|
+
OPENROUTER_API_KEY=sk-or-... # recommended — 356 models
|
|
97
|
+
ANTHROPIC_API_KEY=sk-ant-... # direct Anthropic
|
|
98
|
+
OPENAI_API_KEY=sk-... # direct OpenAI
|
|
99
|
+
OPENAI_BASE_URL=http://localhost:11434/v1 # Ollama / local
|
|
100
|
+
|
|
101
|
+
# Optional model pool (up to 5, rotated on rate-limit)
|
|
102
|
+
JELLY_MODEL_1=anthropic/claude-opus-4.7
|
|
103
|
+
JELLY_MODEL_2=openai/gpt-5.5
|
|
104
|
+
JELLY_MODEL_3=google/gemini-3.5-flash
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Built-in Tools
|
|
110
|
+
|
|
111
|
+
| Tool | Description | Source |
|
|
112
|
+
|------|-------------|--------|
|
|
113
|
+
| `get_prices` | Live prices + 24h change | Binance + CoinGecko |
|
|
114
|
+
| `get_candles` | OHLCV data + RSI/MACD/Bollinger analysis | Binance |
|
|
115
|
+
| `get_top_movers` | Largest 24h movers | Binance |
|
|
116
|
+
| `get_market_overview` | Aggregated market summary | Multi-source |
|
|
117
|
+
| `get_news` | Headlines + sentiment scoring | CoinDesk/CoinTelegraph/TheBlock |
|
|
118
|
+
| `get_fear_greed` | Fear & Greed Index (7-day history) | alternative.me |
|
|
119
|
+
| `get_funding_rates` | Perp funding rates (long/short bias) | Binance |
|
|
120
|
+
| `get_btc_mempool` | BTC pending txs + fee rates | mempool.space |
|
|
121
|
+
| `get_defi_tvl` | DeFi TVL by chain | DeFiLlama |
|
|
122
|
+
| `get_solana_stats` | Solana TPS + network health | Solana RPC |
|
|
123
|
+
| `analyze_ta` | RSI, MACD, Bollinger, EMA, ATR on price arrays | Local |
|
|
124
|
+
| `list_models` | Search 356 available AI models | OpenRouter |
|
|
125
|
+
| `pick_model` | Find cheapest model for requirements | OpenRouter |
|
|
126
|
+
| `set_goal` / `list_goals` / `complete_goal` | Persistent cross-session goals | Local |
|
|
127
|
+
| `schedule_task` / `list_schedule` | Cron + price-trigger scheduling | Local |
|
|
128
|
+
| `read_task_context` / `list_tasks` | Ephemeral task context folders | Local |
|
|
129
|
+
| `cost_report` | Session + lifetime token usage | Local |
|
|
130
|
+
|
|
131
|
+
All data tools are free — no API keys required.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Effect Levels
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
/effect eco # minimal tool calls, cheapest models
|
|
139
|
+
/effect normal # balanced (default)
|
|
140
|
+
/effect turbo # 2 parallel sub-agents for complex tasks
|
|
141
|
+
/effect max # 5 agents + thinking models for deep research
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Extension API
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { Type } from "@jellyos/agent";
|
|
150
|
+
import type { ExtensionAPI } from "@jellyos/agent";
|
|
151
|
+
|
|
152
|
+
export default function (agent: ExtensionAPI) {
|
|
153
|
+
agent.setSystemPrompt("You are a DeFi yield optimizer.");
|
|
154
|
+
|
|
155
|
+
agent.registerTool({
|
|
156
|
+
name: "get_apy",
|
|
157
|
+
label: "Get APY",
|
|
158
|
+
description: "Fetch current APY for a DeFi protocol",
|
|
159
|
+
requiresApproval: false, // set true for money-moving tools
|
|
160
|
+
parameters: Type.Object({
|
|
161
|
+
protocol: Type.String({ description: "Protocol name e.g. aave" }),
|
|
162
|
+
}),
|
|
163
|
+
async execute(_id, { protocol }) {
|
|
164
|
+
const res = await fetch(`https://api.llama.fi/protocol/${protocol}`);
|
|
165
|
+
const data = await res.json() as Record<string, unknown>;
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: `${protocol}: ${JSON.stringify(data)}` }],
|
|
168
|
+
details: {},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
agent.on("session_start", async (ctx) => {
|
|
174
|
+
ctx.ui.setStatus("yields", "ready");
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
jellyos --extension ./my-extension.ts
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## REPL Commands
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
/help List commands
|
|
189
|
+
/effect [level] eco | normal | turbo | max
|
|
190
|
+
/prices [symbols] Quick price check
|
|
191
|
+
/news Latest headlines + sentiment
|
|
192
|
+
/goals List active goals
|
|
193
|
+
/goal add <text> Add a persistent goal
|
|
194
|
+
/goal done <id> Mark goal complete
|
|
195
|
+
/schedule List scheduled tasks
|
|
196
|
+
/tasks List active context folders
|
|
197
|
+
/traces Show last 5 agent traces with timing
|
|
198
|
+
/memory <query> Search long-term memory
|
|
199
|
+
/models [query] Search available AI models
|
|
200
|
+
/cost Session + lifetime cost report
|
|
201
|
+
/approve | /deny Respond to tool approval gates
|
|
202
|
+
/palette Full command + tool list
|
|
203
|
+
/clear Clear conversation history
|
|
204
|
+
Escape Abort in-flight model stream
|
|
205
|
+
Ctrl-C Exit
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
package/bin/jellyos-mcp
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* jellyos-mcp — JellyOS MCP server entry point.
|
|
4
|
+
* Exposes all JellyOS tools via the Model Context Protocol (stdio transport).
|
|
5
|
+
* Compatible with Claude Desktop, Cursor, Continue, and any MCP client.
|
|
6
|
+
*/
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
const distEntry = join(__dirname, "..", "dist", "mcp", "entry.js");
|
|
16
|
+
const srcEntry = join(__dirname, "..", "src", "mcp", "entry.ts");
|
|
17
|
+
|
|
18
|
+
if (existsSync(distEntry)) {
|
|
19
|
+
await import(distEntry);
|
|
20
|
+
} else if (existsSync(srcEntry)) {
|
|
21
|
+
const { register } = await import("tsx/esm");
|
|
22
|
+
await import(srcEntry);
|
|
23
|
+
} else {
|
|
24
|
+
process.stderr.write("JellyOS MCP: run 'npm run build' first\n");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
@@ -24,6 +24,12 @@ export interface ToolDef<P extends TSchema = TSchema> {
|
|
|
24
24
|
label: string;
|
|
25
25
|
description: string;
|
|
26
26
|
parameters: P;
|
|
27
|
+
/**
|
|
28
|
+
* #10: If true, the agent pauses before executing this tool and asks the
|
|
29
|
+
* user for confirmation. Use for any tool that moves money or signs txs.
|
|
30
|
+
* The REPL renders an [APPROVE] prompt — user types y/n within 60 seconds.
|
|
31
|
+
*/
|
|
32
|
+
requiresApproval?: boolean;
|
|
27
33
|
execute(id: string, params: Static<P>): Promise<ToolContent>;
|
|
28
34
|
}
|
|
29
35
|
export interface TuiHeader {
|
package/dist/api/Registry.js
CHANGED
|
@@ -83,7 +83,9 @@ export class Registry {
|
|
|
83
83
|
await h(undefined, ctx); // (_event, ctx) => {...} — Pi convention
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
catch {
|
|
86
|
+
catch (e) {
|
|
87
|
+
console.error(`[Registry] hook "${event}" threw:`, e);
|
|
88
|
+
}
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
// ── OpenAI tool schema ───────────────────────────────────────────────────
|
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,11 @@ import { Registry } from "./api/Registry.js";
|
|
|
12
12
|
import { loadExtension } from "./loader.js";
|
|
13
13
|
import { App } from "./tui/App.js";
|
|
14
14
|
import { T } from "./tui/theme.js";
|
|
15
|
+
import { modelRegistry } from "./models/ModelRegistry.js";
|
|
16
|
+
import { CostTracker } from "./models/CostTracker.js";
|
|
17
|
+
import { AgentRunner } from "./runner/AgentRunner.js";
|
|
18
|
+
import { SessionManager } from "./session/SessionManager.js";
|
|
19
|
+
import { makeTheme } from "./tui/theme.js";
|
|
15
20
|
// ── Load env vars from ~/.jelly/.env ─────────────────────────────────────────
|
|
16
21
|
const JELLY_HOME = process.env.JELLYOS_HOME ?? join(homedir(), ".jelly");
|
|
17
22
|
const envPath = join(JELLY_HOME, ".env");
|
|
@@ -33,7 +38,7 @@ if (subcmd === "config") {
|
|
|
33
38
|
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
34
39
|
if (!m)
|
|
35
40
|
continue;
|
|
36
|
-
const masked = m[2].length >
|
|
41
|
+
const masked = m[2].length > 12 ? m[2].slice(0, 6) + "********" : "********";
|
|
37
42
|
console.log(` ${m[1].padEnd(28)} ${masked}`);
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -81,10 +86,72 @@ function loadContext() {
|
|
|
81
86
|
return { effectLevel: "normal", chain: "ethereum" };
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
|
-
// ──
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
// ── Headless mode (#26) ─────────────────────────────────────────────────────
|
|
90
|
+
const headlessIdx = args.indexOf("--headless");
|
|
91
|
+
const headlessMsg = headlessIdx >= 0 ? args[headlessIdx + 1] : null;
|
|
92
|
+
if (headlessMsg) {
|
|
93
|
+
(async () => {
|
|
94
|
+
if (existsSync(envPath))
|
|
95
|
+
loadDotenv({ path: envPath, override: false });
|
|
96
|
+
const registry = new Registry();
|
|
97
|
+
if (extensionPath) {
|
|
98
|
+
try {
|
|
99
|
+
await loadExtension(extensionPath, registry);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
103
|
+
process.stderr.write(`Extension load failed: ${msg}\n`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
await modelRegistry.initialise();
|
|
108
|
+
const session = new SessionManager();
|
|
109
|
+
const prompt = systemPrompt || registry.getSystemPrompt() || "You are JellyOS, an autonomous AI trading agent.";
|
|
110
|
+
session.setSystemPrompt(prompt);
|
|
111
|
+
const theme = makeTheme();
|
|
112
|
+
const nullUi = {
|
|
113
|
+
notify: () => { },
|
|
114
|
+
setStatus: () => { },
|
|
115
|
+
setTheme: () => { },
|
|
116
|
+
setHeader: () => { },
|
|
117
|
+
theme,
|
|
118
|
+
};
|
|
119
|
+
const sessionCtx = {
|
|
120
|
+
ui: nullUi,
|
|
121
|
+
hasUI: false,
|
|
122
|
+
config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY },
|
|
123
|
+
};
|
|
124
|
+
// Fire session_start hooks
|
|
125
|
+
await registry.fireHook("session_start", sessionCtx);
|
|
126
|
+
session.setSystemPrompt(registry.getSystemPrompt() || prompt);
|
|
127
|
+
let exitCode = 0;
|
|
128
|
+
const runner = new AgentRunner(registry, session, (event) => {
|
|
129
|
+
if (event.type === "text_delta")
|
|
130
|
+
process.stdout.write(event.text);
|
|
131
|
+
if (event.type === "turn_done")
|
|
132
|
+
process.stdout.write("\n");
|
|
133
|
+
if (event.type === "error") {
|
|
134
|
+
process.stderr.write(`\nError: ${event.message}\n`);
|
|
135
|
+
exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
}, sessionCtx, "normal", modelRegistry);
|
|
138
|
+
try {
|
|
139
|
+
await runner.run(headlessMsg);
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
143
|
+
process.stderr.write(`Runner error: ${msg}\n`);
|
|
144
|
+
exitCode = 1;
|
|
145
|
+
}
|
|
146
|
+
await registry.fireHook("session_end", sessionCtx);
|
|
147
|
+
process.exit(exitCode);
|
|
148
|
+
})();
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// ── Boot (interactive TUI) ────────────────────────────────────────────────────
|
|
152
|
+
(async () => {
|
|
153
|
+
console.clear();
|
|
154
|
+
console.log(T.accent(`
|
|
88
155
|
██╗███████╗██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗
|
|
89
156
|
██║██╔════╝██║ ██║ ╚██╗ ██╔╝ ██╔═══██╗██╔════╝
|
|
90
157
|
██║█████╗ ██║ ██║ ╚████╔╝ ██║ ██║███████╗
|
|
@@ -92,43 +159,51 @@ function loadContext() {
|
|
|
92
159
|
╚█████╔╝███████╗███████╗██║ ██║ ╚██████╔╝███████║
|
|
93
160
|
╚════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
94
161
|
`));
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
162
|
+
console.log(T.muted(" Standalone AI trading agent — all local, zero exposure\n"));
|
|
163
|
+
const registry = new Registry();
|
|
164
|
+
const { effectLevel, chain } = loadContext();
|
|
165
|
+
// These callbacks are forwarded into the extension API so ui.setStatus
|
|
166
|
+
// and ui.notify work even during the session_start hook (before Ink mounts).
|
|
167
|
+
let _notifyFn = null;
|
|
168
|
+
let _setStatusFn = null;
|
|
169
|
+
if (extensionPath) {
|
|
170
|
+
try {
|
|
171
|
+
console.log(T.muted(` Loading: ${extensionPath}`));
|
|
172
|
+
await loadExtension(extensionPath, registry, {
|
|
173
|
+
onNotify: (msg) => { _notifyFn?.(msg); },
|
|
174
|
+
onStatusUpdate: (k, v) => { _setStatusFn?.(k, v); },
|
|
175
|
+
});
|
|
176
|
+
console.log(T.success(` ✓ ${registry.listTools().length} tools · ${registry.listCommands().length} commands`));
|
|
177
|
+
}
|
|
178
|
+
catch (e) {
|
|
179
|
+
console.error(T.error(` ✗ Extension load failed: ${e.message}`));
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
110
182
|
}
|
|
111
|
-
|
|
112
|
-
console.
|
|
113
|
-
|
|
183
|
+
else {
|
|
184
|
+
console.log(T.warn(" No extension found — base agent only."));
|
|
185
|
+
console.log(T.muted(" Run from jellyos project root or pass --extension path\n"));
|
|
114
186
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
187
|
+
if (!process.env.OPENROUTER_API_KEY && !process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) {
|
|
188
|
+
console.log(T.warn(" No API key found. Set OPENROUTER_API_KEY in ~/.jelly/.env\n"));
|
|
189
|
+
}
|
|
190
|
+
// ── Initialise model registry & cost tracker ─────────────────────────────
|
|
191
|
+
const costTracker = new CostTracker(modelRegistry);
|
|
192
|
+
console.log(T.muted(" Discovering available models via OpenRouter…"));
|
|
193
|
+
await modelRegistry.initialise();
|
|
194
|
+
console.log(T.success(` ✓ ${modelRegistry.modelCount} models available \n`));
|
|
195
|
+
await new Promise(r => setTimeout(r, 500));
|
|
196
|
+
console.clear();
|
|
197
|
+
render(React.createElement(App, {
|
|
198
|
+
registry,
|
|
199
|
+
systemPrompt,
|
|
200
|
+
effectLevel,
|
|
201
|
+
chain,
|
|
202
|
+
modelReg: modelRegistry,
|
|
203
|
+
costTracker,
|
|
204
|
+
onNotifyReady: (fn) => { _notifyFn = fn; },
|
|
205
|
+
onStatusReady: (fn) => { _setStatusFn = fn; },
|
|
206
|
+
}), { exitOnCtrlC: false });
|
|
207
|
+
})();
|
|
208
|
+
} // end headless else
|
|
134
209
|
//# sourceMappingURL=cli.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,30 @@ export { Registry } from "./api/Registry.js";
|
|
|
12
12
|
export { AgentRunner } from "./runner/AgentRunner.js";
|
|
13
13
|
export { ModelClient, resolveModelConfig } from "./runner/ModelClient.js";
|
|
14
14
|
export { ToolDispatcher } from "./runner/ToolDispatcher.js";
|
|
15
|
-
export { SessionManager } from "./session/SessionManager.js";
|
|
16
15
|
export { loadExtension } from "./loader.js";
|
|
17
16
|
export { makeTheme, T, JELLY_COLORS } from "./tui/theme.js";
|
|
17
|
+
export { ModelRegistry, modelRegistry, classifyModel } from "./models/ModelRegistry.js";
|
|
18
|
+
export { CostTracker } from "./models/CostTracker.js";
|
|
19
|
+
export type { OpenRouterModel, TieredModel, TieredPool, ModelTier } from "./models/ModelRegistry.js";
|
|
20
|
+
export type { UsageEntry, SessionUsage, LifetimeUsage } from "./models/CostTracker.js";
|
|
21
|
+
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr, getCandlesTool, getCandlesParams } from "./tools/TechnicalAnalysis.js";
|
|
22
|
+
export { PriceFeed, priceFeed, getPricesTool, topMoversTool, marketOverviewTool } from "./tools/PriceFeed.js";
|
|
23
|
+
export { NewsFeed, newsFeed, getNewsTool, scoreSentiment } from "./tools/NewsSentiment.js";
|
|
24
|
+
export { getFearGreedTool, fundingRatesParams, getFundingRatesTool, getBtcMempoolTool, getDefiTvlTool, getSolanaStatsTool, } from "./tools/MarketSentiment.js";
|
|
25
|
+
export type { OHLCV, AnalysisResult } from "./tools/TechnicalAnalysis.js";
|
|
26
|
+
export type { PriceTick } from "./tools/PriceFeed.js";
|
|
27
|
+
export type { NewsItem, SentimentReport } from "./tools/NewsSentiment.js";
|
|
28
|
+
export { SessionManager } from "./session/SessionManager.js";
|
|
29
|
+
export { MemoryStore, memoryStore } from "./session/MemoryStore.js";
|
|
30
|
+
export { GoalManager, goalManager } from "./session/GoalManager.js";
|
|
31
|
+
export { ContextStore, contextStore } from "./session/ContextStore.js";
|
|
32
|
+
export type { MemoryEntry, RecentSession } from "./session/MemoryStore.js";
|
|
33
|
+
export type { Goal } from "./session/GoalManager.js";
|
|
34
|
+
export type { TaskContext } from "./session/ContextStore.js";
|
|
35
|
+
export type { ContextPressure } from "./session/SessionManager.js";
|
|
36
|
+
export { MCPServer } from "./mcp/server.js";
|
|
37
|
+
export { AgentScheduler, agentScheduler } from "./scheduler/AgentScheduler.js";
|
|
38
|
+
export type { ScheduledTask, PriceTrigger } from "./scheduler/AgentScheduler.js";
|
|
39
|
+
export { Tracer } from "./telemetry/Tracer.js";
|
|
40
|
+
export type { Span, Trace } from "./telemetry/Tracer.js";
|
|
18
41
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15,10 +15,27 @@ export { Registry } from "./api/Registry.js";
|
|
|
15
15
|
export { AgentRunner } from "./runner/AgentRunner.js";
|
|
16
16
|
export { ModelClient, resolveModelConfig } from "./runner/ModelClient.js";
|
|
17
17
|
export { ToolDispatcher } from "./runner/ToolDispatcher.js";
|
|
18
|
-
// Session
|
|
19
|
-
export { SessionManager } from "./session/SessionManager.js";
|
|
20
18
|
// Loader (for embedding the agent in other tools)
|
|
21
19
|
export { loadExtension } from "./loader.js";
|
|
22
20
|
// Theme
|
|
23
21
|
export { makeTheme, T, JELLY_COLORS } from "./tui/theme.js";
|
|
22
|
+
// Model intelligence
|
|
23
|
+
export { ModelRegistry, modelRegistry, classifyModel } from "./models/ModelRegistry.js";
|
|
24
|
+
export { CostTracker } from "./models/CostTracker.js";
|
|
25
|
+
// Tools
|
|
26
|
+
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr, getCandlesTool, getCandlesParams } from "./tools/TechnicalAnalysis.js";
|
|
27
|
+
export { PriceFeed, priceFeed, getPricesTool, topMoversTool, marketOverviewTool } from "./tools/PriceFeed.js";
|
|
28
|
+
export { NewsFeed, newsFeed, getNewsTool, scoreSentiment } from "./tools/NewsSentiment.js";
|
|
29
|
+
export { getFearGreedTool, fundingRatesParams, getFundingRatesTool, getBtcMempoolTool, getDefiTvlTool, getSolanaStatsTool, } from "./tools/MarketSentiment.js";
|
|
30
|
+
// Session / Memory / Goals / Context
|
|
31
|
+
export { SessionManager } from "./session/SessionManager.js";
|
|
32
|
+
export { MemoryStore, memoryStore } from "./session/MemoryStore.js";
|
|
33
|
+
export { GoalManager, goalManager } from "./session/GoalManager.js";
|
|
34
|
+
export { ContextStore, contextStore } from "./session/ContextStore.js";
|
|
35
|
+
// MCP server
|
|
36
|
+
export { MCPServer } from "./mcp/server.js";
|
|
37
|
+
// Scheduler
|
|
38
|
+
export { AgentScheduler, agentScheduler } from "./scheduler/AgentScheduler.js";
|
|
39
|
+
// Telemetry
|
|
40
|
+
export { Tracer } from "./telemetry/Tracer.js";
|
|
24
41
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server entry — bootstraps the Registry with built-in tools
|
|
3
|
+
* and starts the MCP stdio server.
|
|
4
|
+
*/
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { config as loadDotenv } from "dotenv";
|
|
9
|
+
import { Registry } from "../api/Registry.js";
|
|
10
|
+
import { loadExtension } from "../loader.js";
|
|
11
|
+
import { MCPServer } from "./server.js";
|
|
12
|
+
import { modelRegistry } from "../models/ModelRegistry.js";
|
|
13
|
+
import { getPricesTool, topMoversTool, marketOverviewTool, getPricesParams, topMoversParams, marketOverviewParams, priceFeed, } from "../tools/PriceFeed.js";
|
|
14
|
+
import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
|
|
15
|
+
import { getCandlesParams, getCandlesTool, analyzeTAParams, fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
16
|
+
import { getFearGreedTool, fearGreedParams, getFundingRatesTool, fundingRatesParams, getBtcMempoolTool, btcMempoolParams, getDefiTvlTool, defiTvlParams, getSolanaStatsTool, solanaStatsParams, } from "../tools/MarketSentiment.js";
|
|
17
|
+
const JELLY_HOME = process.env.JELLYOS_HOME ?? join(homedir(), ".jelly");
|
|
18
|
+
const envPath = join(JELLY_HOME, ".env");
|
|
19
|
+
if (existsSync(envPath))
|
|
20
|
+
loadDotenv({ path: envPath, override: false });
|
|
21
|
+
const registry = new Registry();
|
|
22
|
+
// Load optional extension from --extension arg
|
|
23
|
+
const extIdx = process.argv.indexOf("--extension");
|
|
24
|
+
const extPath = extIdx >= 0 ? process.argv[extIdx + 1] : null;
|
|
25
|
+
if (extPath && existsSync(extPath)) {
|
|
26
|
+
try {
|
|
27
|
+
await loadExtension(extPath, registry);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
process.stderr.write(`[MCP] Extension load failed: ${e}\n`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Register all built-in tools
|
|
34
|
+
await modelRegistry.initialise();
|
|
35
|
+
const mk = modelRegistry;
|
|
36
|
+
registry.addTool({ name: "list_models", label: "List Models", description: "Search available AI models.", parameters: mk["listModelsParams"], execute: (id, p) => mk["listModelsTool"](id, p) });
|
|
37
|
+
registry.addTool({ name: "get_prices", label: "Get Prices", description: "Get live crypto prices.", parameters: getPricesParams, execute: (id, p) => getPricesTool(id, p) });
|
|
38
|
+
registry.addTool({ name: "get_top_movers", label: "Top Movers", description: "Top 24h price movers.", parameters: topMoversParams, execute: (id, p) => topMoversTool(id, p) });
|
|
39
|
+
registry.addTool({ name: "market_overview", label: "Market Overview", description: "Aggregated market data.", parameters: marketOverviewParams, execute: () => marketOverviewTool() });
|
|
40
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news + sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
41
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV + TA analysis from Binance.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
42
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto fear & greed index.", parameters: fearGreedParams, execute: (id, p) => getFearGreedTool(id, p) });
|
|
43
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Perp funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
44
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool + fees.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
45
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
46
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana TPS + health.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
47
|
+
registry.addTool({
|
|
48
|
+
name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger on price arrays.",
|
|
49
|
+
parameters: analyzeTAParams,
|
|
50
|
+
execute: async (_id, p) => {
|
|
51
|
+
const params = p;
|
|
52
|
+
const closes = params.prices;
|
|
53
|
+
const candles = closes.map((c, i) => ({
|
|
54
|
+
timestamp: i, open: c, high: (params.highs?.[i] ?? c), low: (params.lows?.[i] ?? c), close: c, volume: (params.volumes?.[i] ?? 0),
|
|
55
|
+
}));
|
|
56
|
+
const results = fullAnalysis(candles);
|
|
57
|
+
const text = results.map(r => {
|
|
58
|
+
const s = r.signal === "bullish" ? "🟢" : r.signal === "bearish" ? "🔴" : "⚪";
|
|
59
|
+
return `${s} ${r.indicator}: ${typeof r.value === "number" ? r.value.toFixed(2) : String(r.value)}`;
|
|
60
|
+
}).join("\n");
|
|
61
|
+
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: {} };
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// Start price + news feeds
|
|
65
|
+
priceFeed.track("btc", "eth", "sol", "bnb", "matic", "arb", "op", "avax");
|
|
66
|
+
priceFeed.start();
|
|
67
|
+
newsFeed.start();
|
|
68
|
+
// Start MCP server
|
|
69
|
+
const server = new MCPServer(registry);
|
|
70
|
+
await server.run();
|
|
71
|
+
//# sourceMappingURL=entry.js.map
|