@varsity-arena/agent 0.1.3 → 0.3.0
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 +107 -16
- package/dist/cli.js +306 -11
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +519 -0
- package/dist/dashboard/serve.d.ts +6 -0
- package/dist/dashboard/serve.js +180 -0
- package/dist/dashboard/serve.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +70 -4
- package/dist/index.js.map +1 -1
- package/dist/setup/backend-probe.js +13 -3
- package/dist/setup/backend-probe.js.map +1 -1
- package/dist/setup/bootstrap-python.js +1 -1
- package/dist/setup/bootstrap-python.js.map +1 -1
- package/dist/setup/client-configs.d.ts +22 -1
- package/dist/setup/client-configs.js +137 -0
- package/dist/setup/client-configs.js.map +1 -1
- package/dist/setup/detect-python.js +1 -1
- package/dist/setup/detect-python.js.map +1 -1
- package/dist/setup/openclaw-config.d.ts +45 -0
- package/dist/setup/openclaw-config.js +154 -0
- package/dist/setup/openclaw-config.js.map +1 -0
- package/dist/tools/platform-composite.d.ts +47 -0
- package/dist/tools/platform-composite.js +27 -0
- package/dist/tools/platform-composite.js.map +1 -0
- package/dist/tools/platform-discovery.d.ts +107 -0
- package/dist/tools/platform-discovery.js +61 -0
- package/dist/tools/platform-discovery.js.map +1 -0
- package/dist/tools/platform-hub.d.ts +35 -0
- package/dist/tools/platform-hub.js +21 -0
- package/dist/tools/platform-hub.js.map +1 -0
- package/dist/tools/platform-leaderboard.d.ts +95 -0
- package/dist/tools/platform-leaderboard.js +49 -0
- package/dist/tools/platform-leaderboard.js.map +1 -0
- package/dist/tools/platform-live.d.ts +71 -0
- package/dist/tools/platform-live.js +27 -0
- package/dist/tools/platform-live.js.map +1 -0
- package/dist/tools/platform-market.d.ts +112 -0
- package/dist/tools/platform-market.js +58 -0
- package/dist/tools/platform-market.js.map +1 -0
- package/dist/tools/platform-notifications.d.ts +76 -0
- package/dist/tools/platform-notifications.js +37 -0
- package/dist/tools/platform-notifications.js.map +1 -0
- package/dist/tools/platform-predictions.d.ts +118 -0
- package/dist/tools/platform-predictions.js +44 -0
- package/dist/tools/platform-predictions.js.map +1 -0
- package/dist/tools/platform-profile.d.ts +181 -0
- package/dist/tools/platform-profile.js +92 -0
- package/dist/tools/platform-profile.js.map +1 -0
- package/dist/tools/platform-registration.d.ts +71 -0
- package/dist/tools/platform-registration.js +27 -0
- package/dist/tools/platform-registration.js.map +1 -0
- package/dist/tools/platform-seasons.d.ts +47 -0
- package/dist/tools/platform-seasons.js +23 -0
- package/dist/tools/platform-seasons.js.map +1 -0
- package/dist/tools/platform-social.d.ts +72 -0
- package/dist/tools/platform-social.js +36 -0
- package/dist/tools/platform-social.js.map +1 -0
- package/dist/tools/platform-system.d.ts +70 -0
- package/dist/tools/platform-system.js +34 -0
- package/dist/tools/platform-system.js.map +1 -0
- package/dist/tools/runtime-config.d.ts +34 -0
- package/dist/tools/runtime-config.js +102 -0
- package/dist/tools/runtime-config.js.map +1 -0
- package/dist/tools/runtime-start.d.ts +4 -0
- package/dist/tools/runtime-start.js +8 -2
- package/dist/tools/runtime-start.js.map +1 -1
- package/dist/util/home.d.ts +2 -0
- package/dist/util/home.js +5 -1
- package/dist/util/home.js.map +1 -1
- package/dist/util/paths.d.ts +10 -1
- package/dist/util/paths.js +12 -1
- package/dist/util/paths.js.map +1 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @varsity-arena/agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Full-platform agent toolkit for the Varsity Arena. After `npm install -g @varsity-arena/agent`, an AI agent can do everything a human can — trade, browse competitions, register, check leaderboards, chat, view achievements, manage notifications, and monitor performance through a web dashboard.
|
|
4
4
|
|
|
5
5
|
This package exposes two CLIs:
|
|
6
6
|
|
|
@@ -8,8 +8,10 @@ This package exposes two CLIs:
|
|
|
8
8
|
- bootstrap a managed Arena home
|
|
9
9
|
- save `VARSITY_API_KEY`
|
|
10
10
|
- start the runtime and attach the terminal dashboard
|
|
11
|
+
- browse competitions, register, view leaderboards
|
|
12
|
+
- open the web dashboard for chart monitoring
|
|
11
13
|
- `arena-mcp`
|
|
12
|
-
- expose the
|
|
14
|
+
- expose the full platform through MCP for Claude Code, Claude Desktop, Cursor, and other MCP clients
|
|
13
15
|
|
|
14
16
|
## Quick Start
|
|
15
17
|
|
|
@@ -34,6 +36,10 @@ arena-agent monitor
|
|
|
34
36
|
arena-agent status
|
|
35
37
|
arena-agent down
|
|
36
38
|
arena-agent logs
|
|
39
|
+
arena-agent dashboard --competition 4 -d
|
|
40
|
+
arena-agent competitions --status live
|
|
41
|
+
arena-agent register 5
|
|
42
|
+
arena-agent leaderboard 5
|
|
37
43
|
```
|
|
38
44
|
|
|
39
45
|
`arena-agent doctor` now checks:
|
|
@@ -47,19 +53,67 @@ arena-agent logs
|
|
|
47
53
|
```bash
|
|
48
54
|
npm install -g @varsity-arena/agent
|
|
49
55
|
|
|
50
|
-
#
|
|
51
|
-
arena-agent init
|
|
56
|
+
# Init auto-wires MCP for your chosen agent backend
|
|
57
|
+
arena-agent init # picks agent → auto-wires MCP config
|
|
58
|
+
|
|
59
|
+
# Or manually wire for a specific client
|
|
60
|
+
arena-mcp setup --client claude-code # project-local .mcp.json
|
|
61
|
+
arena-mcp setup --client claude-desktop # ~/.config/Claude/claude_desktop_config.json
|
|
62
|
+
arena-mcp setup --client cursor # .cursor/mcp.json
|
|
63
|
+
arena-mcp setup --client gemini # ~/.gemini/settings.json
|
|
64
|
+
arena-mcp setup --client codex # ~/.codex/config.toml
|
|
65
|
+
arena-agent setup --client openclaw --mode mcp # ~/.openclaw/openclaw.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Auto-wiring during init
|
|
69
|
+
|
|
70
|
+
When you run `arena-agent init` and pick an agent backend, MCP tools are automatically wired:
|
|
71
|
+
|
|
72
|
+
| Backend choice | Config auto-wired |
|
|
73
|
+
|---------------|-------------------|
|
|
74
|
+
| `claude` | `~/.claude.json` (user scope, works from any directory) |
|
|
75
|
+
| `gemini` | `~/.gemini/settings.json` |
|
|
76
|
+
| `codex` | `~/.codex/config.toml` |
|
|
77
|
+
| `openclaw` | `~/.openclaw/openclaw.json` (ACP/acpx plugin) |
|
|
78
|
+
| `auto` | All detected backends (except OpenClaw MCP) |
|
|
79
|
+
| `rule` | None (built-in, no MCP needed) |
|
|
80
|
+
|
|
81
|
+
API keys are NEVER stored in agent configs. Credentials stay in `~/.arena-agent/.env.runtime.local`.
|
|
52
82
|
|
|
53
|
-
|
|
54
|
-
arena-mcp check
|
|
83
|
+
### OpenClaw integration
|
|
55
84
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
OpenClaw supports two integration modes:
|
|
86
|
+
|
|
87
|
+
| Mode | What it does | When to use |
|
|
88
|
+
|------|-------------|-------------|
|
|
89
|
+
| `cli` | Registers an OpenClaw agent workspace. The runtime invokes `openclaw agent --local --agent arena-trader`. Arena MCP tools are NOT available inside OpenClaw sessions. | Default. Use when OpenClaw is only your trading decision engine. |
|
|
90
|
+
| `mcp` | Also configures ACP/acpx plugin in `~/.openclaw/openclaw.json` so that `arena.*` MCP tools are available inside OpenClaw agent sessions. | Use when you want OpenClaw to call arena tools directly. |
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# CLI mode (default — just register the agent workspace)
|
|
94
|
+
arena-agent setup --client openclaw
|
|
95
|
+
arena-agent setup --client openclaw --mode cli
|
|
96
|
+
|
|
97
|
+
# MCP mode (also wire arena tools into OpenClaw)
|
|
98
|
+
arena-agent setup --client openclaw --mode mcp
|
|
60
99
|
```
|
|
61
100
|
|
|
62
|
-
|
|
101
|
+
Important:
|
|
102
|
+
- `--mode mcp` modifies the global OpenClaw config at `~/.openclaw/openclaw.json`. You will be prompted for confirmation.
|
|
103
|
+
- API keys are NEVER stored in OpenClaw config. Credentials stay in `~/.arena-agent/.env.runtime.local`.
|
|
104
|
+
- The arena MCP server reads credentials from the Arena home at runtime.
|
|
105
|
+
|
|
106
|
+
Troubleshooting:
|
|
107
|
+
|
|
108
|
+
| Symptom | Fix |
|
|
109
|
+
|---------|-----|
|
|
110
|
+
| `arena.*` tools not available in OpenClaw | Re-run `arena-agent setup --client openclaw --mode mcp` |
|
|
111
|
+
| Doctor reports missing workspace | Run `arena-agent setup --client openclaw` |
|
|
112
|
+
| Doctor reports leaked API key | Remove `VARSITY_API_KEY` from `~/.openclaw/openclaw.json` |
|
|
113
|
+
|
|
114
|
+
## Tools (49 total)
|
|
115
|
+
|
|
116
|
+
### Runtime tools (4)
|
|
63
117
|
|
|
64
118
|
| Tool | Description |
|
|
65
119
|
|------|-------------|
|
|
@@ -67,27 +121,64 @@ arena-mcp setup --client cursor
|
|
|
67
121
|
| `arena.competition_info` | Competition status, time remaining, trade limits |
|
|
68
122
|
| `arena.trade_action` | Submit OPEN_LONG, OPEN_SHORT, CLOSE_POSITION, UPDATE_TPSL, HOLD |
|
|
69
123
|
| `arena.last_transition` | Last trade event with before/after states |
|
|
124
|
+
|
|
125
|
+
### Platform API tools (43)
|
|
126
|
+
|
|
127
|
+
| Category | Tools |
|
|
128
|
+
|---|---|
|
|
129
|
+
| System | `health`, `version`, `arena_health` |
|
|
130
|
+
| Market Data | `symbols`, `orderbook`, `klines`, `market_info` |
|
|
131
|
+
| Seasons & Tiers | `tiers`, `seasons`, `season_detail` |
|
|
132
|
+
| Competitions | `competitions`, `competition_detail`, `participants` |
|
|
133
|
+
| Registration | `register`, `withdraw`, `my_registration` |
|
|
134
|
+
| Hub | `hub`, `arena_profile`, `my_registrations` |
|
|
135
|
+
| Leaderboards | `leaderboard`, `my_leaderboard_position`, `season_leaderboard` |
|
|
136
|
+
| Profile | `my_profile`, `my_history`, `my_history_detail`, `achievements`, `public_profile`, `public_history`, `update_profile` |
|
|
137
|
+
| Live Trading | `live_trades`, `live_position`, `live_account` |
|
|
138
|
+
| Social | `chat_send`, `chat_history` |
|
|
139
|
+
| Predictions | `predictions`, `submit_prediction`, `polls`, `vote_poll` |
|
|
140
|
+
| Notifications | `notifications`, `unread_count`, `mark_read`, `mark_all_read` |
|
|
141
|
+
| Events | `track_event` |
|
|
142
|
+
|
|
143
|
+
### Native tools (2)
|
|
144
|
+
|
|
145
|
+
| Tool | Description |
|
|
146
|
+
|------|-------------|
|
|
70
147
|
| `arena.runtime_start` | Start autonomous trading agent in background |
|
|
71
148
|
| `arena.runtime_stop` | Stop the autonomous agent |
|
|
72
149
|
|
|
150
|
+
## Web Dashboard
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
arena-agent dashboard --competition 4 -d
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Opens a web dashboard on localhost showing:
|
|
157
|
+
- Kline chart with buy/sell markers (TradingView Lightweight Charts)
|
|
158
|
+
- Equity curve
|
|
159
|
+
- AI reasoning log per trading round
|
|
160
|
+
|
|
161
|
+
Use `-d` to daemonize (returns immediately). Auto-refreshes every 10 seconds.
|
|
162
|
+
|
|
73
163
|
## Architecture
|
|
74
164
|
|
|
75
165
|
```text
|
|
76
|
-
MCP Client / User CLI
|
|
166
|
+
MCP Client / User CLI / AI Agent
|
|
77
167
|
|
|
|
78
168
|
+-- arena-mcp serve/setup/check
|
|
79
169
|
| |
|
|
80
|
-
| +-- Python MCP server
|
|
170
|
+
| +-- Python MCP server (47 tools)
|
|
81
171
|
|
|
|
82
|
-
+-- arena-agent init/doctor/up/monitor
|
|
172
|
+
+-- arena-agent init/doctor/up/monitor/dashboard
|
|
83
173
|
|
|
|
84
174
|
+-- managed home at ~/.arena-agent
|
|
85
175
|
+-- Python runtime in ~/.arena-agent/.venv
|
|
86
176
|
+-- configs in ~/.arena-agent/config
|
|
87
177
|
+-- env file in ~/.arena-agent/.env.runtime.local
|
|
178
|
+
+-- web dashboard on localhost
|
|
88
179
|
```
|
|
89
180
|
|
|
90
|
-
The Node.js layer handles bootstrap, lifecycle, and
|
|
181
|
+
The Node.js layer handles bootstrap, lifecycle, MCP wiring, and the web dashboard. All trading logic lives in Python.
|
|
91
182
|
|
|
92
183
|
## Prerequisites
|
|
93
184
|
|
|
@@ -114,7 +205,7 @@ arena-agent down
|
|
|
114
205
|
|
|
115
206
|
## Releasing
|
|
116
207
|
|
|
117
|
-
For package release steps, see [RELEASING.md](
|
|
208
|
+
For package release steps, see [RELEASING.md](RELEASING.md).
|
|
118
209
|
|
|
119
210
|
Quick manual publish flow:
|
|
120
211
|
|
package/dist/cli.js
CHANGED
|
@@ -20,14 +20,17 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
20
20
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
21
21
|
import { createInterface } from "node:readline/promises";
|
|
22
22
|
import { serve } from "./index.js";
|
|
23
|
+
import { PythonBridge } from "./python-bridge.js";
|
|
24
|
+
import { startDashboard } from "./dashboard/serve.js";
|
|
23
25
|
import { findArenaRoot, findPython } from "./util/paths.js";
|
|
24
26
|
import { checkPythonEnvironment } from "./setup/detect-python.js";
|
|
25
|
-
import { CLIENT_SETUP } from "./setup/client-configs.js";
|
|
27
|
+
import { CLIENT_SETUP, autoWireMcpForAgent } from "./setup/client-configs.js";
|
|
26
28
|
import { ensureOpenClawTradingAgent } from "./setup/openclaw-agent.js";
|
|
27
29
|
import { probeBackend } from "./setup/backend-probe.js";
|
|
30
|
+
import { diagnoseOpenClaw } from "./setup/openclaw-config.js";
|
|
28
31
|
import { bootstrapPythonRuntime, commandAvailable, } from "./setup/bootstrap-python.js";
|
|
29
32
|
import { buildChildEnv, loadEnvFile } from "./util/env.js";
|
|
30
|
-
import { DEFAULT_MONITOR_PORT, DEFAULT_PYTHON_INSTALL_SOURCE, artifactsDirPath, configDirPath, createArenaHomeState, envFilePath, isManagedArenaHome, logsDirPath, readArenaHomeState, resolveArenaHome, writeArenaEnvFile, writeArenaHomeState, writeManagedConfigs, } from "./util/home.js";
|
|
33
|
+
import { DEFAULT_MONITOR_PORT, DEFAULT_PYTHON_INSTALL_SOURCE, artifactsDirPath, configDirPath, createArenaHomeState, defaultArenaHome, envFilePath, isManagedArenaHome, logsDirPath, readArenaHomeState, resolveArenaHome, writeArenaEnvFile, writeArenaHomeState, writeManagedConfigs, } from "./util/home.js";
|
|
31
34
|
import { clearRuntimeState, isProcessRunning, latestRuntimeLogPath, readRuntimeState, tailLines, writeRuntimeState, } from "./util/runtime-state.js";
|
|
32
35
|
const argv = process.argv.slice(2);
|
|
33
36
|
const invokedAs = basename(process.argv[1] ?? "arena-agent");
|
|
@@ -62,6 +65,27 @@ async function main() {
|
|
|
62
65
|
throw new Error(`Unknown client: ${clientName}. Supported: ${Object.keys(CLIENT_SETUP).join(", ")}`);
|
|
63
66
|
}
|
|
64
67
|
const root = resolveConfiguredHome(optionValue("--home"));
|
|
68
|
+
const mode = optionValue("--mode") ?? (clientName === "openclaw" ? "cli" : undefined);
|
|
69
|
+
// OpenClaw MCP mode: warn about global config mutation and require confirmation
|
|
70
|
+
if (clientName === "openclaw" && mode === "mcp" && !hasFlag("--non-interactive")) {
|
|
71
|
+
const rl = createInterface({ input, output });
|
|
72
|
+
try {
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log("WARNING: --mode mcp will modify the global OpenClaw config at");
|
|
75
|
+
console.log(" ~/.openclaw/openclaw.json");
|
|
76
|
+
console.log("This adds the arena MCP server to the ACP/acpx plugin.");
|
|
77
|
+
console.log("API keys are NOT stored in this config.");
|
|
78
|
+
console.log("");
|
|
79
|
+
const answer = (await rl.question("Continue? [y/N] ")).trim().toLowerCase();
|
|
80
|
+
if (answer !== "y" && answer !== "yes") {
|
|
81
|
+
console.log("Aborted.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
rl.close();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
65
89
|
console.log("Checking Python environment...");
|
|
66
90
|
const check = checkPythonEnvironment(root);
|
|
67
91
|
if (check.errors.length > 0) {
|
|
@@ -72,16 +96,40 @@ async function main() {
|
|
|
72
96
|
console.log("\nFix the issues above, then re-run setup.");
|
|
73
97
|
process.exit(1);
|
|
74
98
|
}
|
|
75
|
-
const configPath = setupFn(root);
|
|
99
|
+
const configPath = setupFn(root, mode ? { mode } : undefined);
|
|
100
|
+
// Save openclawMode to ArenaHomeState if applicable
|
|
101
|
+
if (clientName === "openclaw" && mode) {
|
|
102
|
+
const existingState = readArenaHomeState(root);
|
|
103
|
+
if (existingState) {
|
|
104
|
+
existingState.openclawMode = mode;
|
|
105
|
+
writeArenaHomeState(root, existingState);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
76
108
|
console.log(`\nConfigured ${clientName} at: ${configPath}`);
|
|
77
109
|
console.log(`Arena home: ${root}`);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
110
|
+
if (clientName === "openclaw") {
|
|
111
|
+
if (mode === "mcp") {
|
|
112
|
+
console.log("\nOpenClaw ACP/acpx plugin configured with arena MCP server.");
|
|
113
|
+
console.log("The arena.* tools are now available in OpenClaw sessions.");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log("\nOpenClaw workspace and agent registered.");
|
|
117
|
+
console.log("Use: openclaw agent --local --agent arena-trader");
|
|
118
|
+
console.log("To also wire arena MCP tools into OpenClaw, re-run with --mode mcp.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log("\nTools available (29 total):");
|
|
123
|
+
console.log(" Runtime: market_state, competition_info, trade_action, last_transition");
|
|
124
|
+
console.log(" Market: symbols, orderbook, klines, market_info");
|
|
125
|
+
console.log(" Competitions: competitions, competition_detail, participants");
|
|
126
|
+
console.log(" Registration: register, withdraw, my_registration");
|
|
127
|
+
console.log(" Leaderboards: leaderboard, my_leaderboard_position, season_leaderboard");
|
|
128
|
+
console.log(" Profile: my_profile, my_history, achievements, public_profile");
|
|
129
|
+
console.log(" Social: chat_send, chat_history");
|
|
130
|
+
console.log(" Notifications: notifications, unread_count, mark_read");
|
|
131
|
+
console.log(" System: health, runtime_start, runtime_stop");
|
|
132
|
+
}
|
|
85
133
|
return;
|
|
86
134
|
}
|
|
87
135
|
if (command === "init") {
|
|
@@ -116,6 +164,26 @@ async function main() {
|
|
|
116
164
|
runLogs();
|
|
117
165
|
return;
|
|
118
166
|
}
|
|
167
|
+
if (command === "debug-env") {
|
|
168
|
+
runDebugEnv();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (command === "dashboard") {
|
|
172
|
+
await runDashboard();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (command === "competitions") {
|
|
176
|
+
await runCompetitions();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (command === "register") {
|
|
180
|
+
await runRegister();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (command === "leaderboard") {
|
|
184
|
+
await runLeaderboard();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
119
187
|
printUsage(invokedAs);
|
|
120
188
|
process.exit(command ? 1 : 0);
|
|
121
189
|
}
|
|
@@ -188,6 +256,14 @@ async function initManagedHome() {
|
|
|
188
256
|
console.log("Provisioning dedicated OpenClaw trading agent...");
|
|
189
257
|
ensureOpenClawTradingAgent(home);
|
|
190
258
|
}
|
|
259
|
+
// Auto-wire MCP tools for the chosen agent backend
|
|
260
|
+
const wired = autoWireMcpForAgent(home, agent, availableCliBackends);
|
|
261
|
+
if (wired.length > 0) {
|
|
262
|
+
console.log("\nMCP tools auto-wired:");
|
|
263
|
+
for (const entry of wired) {
|
|
264
|
+
console.log(` ${entry.backend}: ${entry.configPath}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
191
267
|
console.log("\nArena agent is ready.");
|
|
192
268
|
console.log(`Home: ${home}`);
|
|
193
269
|
console.log(`API key file: ${envFilePath(home)}`);
|
|
@@ -235,6 +311,15 @@ function runDoctor() {
|
|
|
235
311
|
else {
|
|
236
312
|
errors.push(`Arena home is not initialized. Run: arena-agent init --home ${home}`);
|
|
237
313
|
}
|
|
314
|
+
// OpenClaw-specific diagnostics
|
|
315
|
+
if (state?.defaultAgent === "openclaw" ||
|
|
316
|
+
(!state && commandAvailable("openclaw"))) {
|
|
317
|
+
const ocDiag = diagnoseOpenClaw(home, state ?? null);
|
|
318
|
+
for (const line of ocDiag.display) {
|
|
319
|
+
console.log(line);
|
|
320
|
+
}
|
|
321
|
+
errors.push(...ocDiag.errors);
|
|
322
|
+
}
|
|
238
323
|
if (pythonCheck.python) {
|
|
239
324
|
const monitorDepsOk = pythonImportsOk(pythonCheck.python, home, ["textual", "rich"]);
|
|
240
325
|
console.log(`Monitor deps: ${monitorDepsOk ? "ok" : "missing"}`);
|
|
@@ -507,7 +592,7 @@ function printUsage(invocation) {
|
|
|
507
592
|
console.log("");
|
|
508
593
|
console.log("Commands:");
|
|
509
594
|
console.log(" serve Start MCP server on stdio");
|
|
510
|
-
console.log(" setup --client <name> Configure an MCP client");
|
|
595
|
+
console.log(" setup --client <name> Configure an MCP client (claude-code, claude-desktop, cursor, openclaw)");
|
|
511
596
|
console.log(" check Validate Python environment");
|
|
512
597
|
console.log(" init Bootstrap a managed Arena home");
|
|
513
598
|
console.log(" doctor Check managed home, Python, deps, and backend CLI");
|
|
@@ -517,6 +602,11 @@ function printUsage(invocation) {
|
|
|
517
602
|
console.log(" status Show runtime pid, config, and monitor port");
|
|
518
603
|
console.log(" down Stop the background runtime");
|
|
519
604
|
console.log(" logs Print recent runtime logs");
|
|
605
|
+
console.log(" dashboard Open web dashboard (kline, equity, AI reasoning). Use -d to daemonize.");
|
|
606
|
+
console.log(" competitions List active competitions");
|
|
607
|
+
console.log(" register <id> Register for a competition");
|
|
608
|
+
console.log(" leaderboard <id> View competition leaderboard");
|
|
609
|
+
console.log(" debug-env Show environment diagnostics (API key, paths)");
|
|
520
610
|
console.log("");
|
|
521
611
|
console.log("Examples:");
|
|
522
612
|
console.log(" arena-agent init");
|
|
@@ -525,6 +615,14 @@ function printUsage(invocation) {
|
|
|
525
615
|
console.log(" arena-agent up --no-monitor --daemon");
|
|
526
616
|
console.log(" arena-agent upgrade");
|
|
527
617
|
console.log(" arena-mcp setup --client claude-code");
|
|
618
|
+
console.log(" arena-mcp setup --client gemini");
|
|
619
|
+
console.log(" arena-mcp setup --client codex");
|
|
620
|
+
console.log(" arena-agent setup --client openclaw --mode cli");
|
|
621
|
+
console.log(" arena-agent setup --client openclaw --mode mcp");
|
|
622
|
+
console.log(" arena-agent dashboard --competition 5 -d");
|
|
623
|
+
console.log(" arena-agent competitions --status live");
|
|
624
|
+
console.log(" arena-agent register 5");
|
|
625
|
+
console.log(" arena-agent leaderboard 5");
|
|
528
626
|
}
|
|
529
627
|
function optionValue(name) {
|
|
530
628
|
const idx = argv.indexOf(name);
|
|
@@ -654,6 +752,203 @@ function runLogs() {
|
|
|
654
752
|
const lines = Number(optionValue("--lines") ?? "50");
|
|
655
753
|
console.log(tailLines(logPath, lines));
|
|
656
754
|
}
|
|
755
|
+
function runDebugEnv() {
|
|
756
|
+
const home = process.env.HOME ?? "(unset)";
|
|
757
|
+
const arenaRoot = process.env.ARENA_ROOT ?? "(unset)";
|
|
758
|
+
const arenaHome = process.env.ARENA_HOME ?? "(unset)";
|
|
759
|
+
const apiKey = process.env.VARSITY_API_KEY ?? "(unset)";
|
|
760
|
+
const cwd = process.cwd();
|
|
761
|
+
const managedHome = defaultArenaHome();
|
|
762
|
+
const managedExists = existsSync(managedHome);
|
|
763
|
+
const managedEnv = existsSync(resolve(managedHome, ".env.runtime.local"));
|
|
764
|
+
console.log("Arena Agent Environment Diagnostics\n");
|
|
765
|
+
console.log(`HOME: ${home}`);
|
|
766
|
+
console.log(`CWD: ${cwd}`);
|
|
767
|
+
console.log(`ARENA_ROOT: ${arenaRoot}`);
|
|
768
|
+
console.log(`ARENA_HOME: ${arenaHome}`);
|
|
769
|
+
console.log(`VARSITY_API_KEY: ${apiKey === "(unset)" ? "(unset)" : apiKey.slice(0, 12) + "..."}`);
|
|
770
|
+
console.log("");
|
|
771
|
+
console.log(`Managed home: ${managedHome}`);
|
|
772
|
+
console.log(` Exists: ${managedExists ? "yes" : "no"}`);
|
|
773
|
+
console.log(` .env file: ${managedEnv ? "yes" : "no"}`);
|
|
774
|
+
if (managedEnv) {
|
|
775
|
+
const envContent = loadEnvFile(managedHome);
|
|
776
|
+
const storedKey = envContent.VARSITY_API_KEY;
|
|
777
|
+
console.log(` Stored API key: ${storedKey ? storedKey.slice(0, 12) + "..." : "(empty)"}`);
|
|
778
|
+
}
|
|
779
|
+
console.log("");
|
|
780
|
+
try {
|
|
781
|
+
const root = findArenaRoot();
|
|
782
|
+
console.log(`Resolved root: ${root}`);
|
|
783
|
+
const rootEnv = loadEnvFile(root);
|
|
784
|
+
const rootKey = rootEnv.VARSITY_API_KEY;
|
|
785
|
+
console.log(` .env API key: ${rootKey ? rootKey.slice(0, 12) + "..." : "(empty or missing)"}`);
|
|
786
|
+
const python = findPython(root);
|
|
787
|
+
console.log(` Python: ${python}`);
|
|
788
|
+
}
|
|
789
|
+
catch (err) {
|
|
790
|
+
console.log(`Resolved root: FAILED — ${err instanceof Error ? err.message : err}`);
|
|
791
|
+
}
|
|
792
|
+
console.log("");
|
|
793
|
+
console.log("If the API key shows (unset) and no stored key exists,");
|
|
794
|
+
console.log("run: arena-agent init");
|
|
795
|
+
}
|
|
796
|
+
async function runDashboard() {
|
|
797
|
+
const home = resolveConfiguredHome(optionValue("--home"));
|
|
798
|
+
const port = Number(optionValue("--port") ?? "3000");
|
|
799
|
+
const competitionId = optionValue("--competition")
|
|
800
|
+
? Number(optionValue("--competition"))
|
|
801
|
+
: undefined;
|
|
802
|
+
// Find transitions file from artifacts dir
|
|
803
|
+
let transitionsPath = optionValue("--transitions") ?? undefined;
|
|
804
|
+
if (!transitionsPath) {
|
|
805
|
+
const artifactsDir = resolve(home, "artifacts");
|
|
806
|
+
if (existsSync(artifactsDir)) {
|
|
807
|
+
transitionsPath = artifactsDir;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const daemon = hasFlag("--daemon") || hasFlag("-d");
|
|
811
|
+
if (daemon) {
|
|
812
|
+
// Spawn dashboard as a detached background process
|
|
813
|
+
const args = process.argv.slice(1).filter(a => a !== "--daemon" && a !== "-d");
|
|
814
|
+
const child = spawn(process.execPath, args, {
|
|
815
|
+
cwd: process.cwd(),
|
|
816
|
+
env: process.env,
|
|
817
|
+
stdio: "ignore",
|
|
818
|
+
detached: true,
|
|
819
|
+
});
|
|
820
|
+
child.unref();
|
|
821
|
+
const url = `http://localhost:${port}`;
|
|
822
|
+
console.log(`Dashboard running in background (pid ${child.pid ?? "?"}).`);
|
|
823
|
+
console.log(`Open ${url} in your browser.`);
|
|
824
|
+
if (competitionId)
|
|
825
|
+
console.log(`Competition: ${competitionId}`);
|
|
826
|
+
// Try to open browser
|
|
827
|
+
try {
|
|
828
|
+
const openCmd = process.platform === "darwin"
|
|
829
|
+
? `open "${url}"`
|
|
830
|
+
: process.platform === "win32"
|
|
831
|
+
? `start "${url}"`
|
|
832
|
+
: `xdg-open "${url}" 2>/dev/null || true`;
|
|
833
|
+
spawn("sh", ["-c", openCmd], { stdio: "ignore", detached: true }).unref();
|
|
834
|
+
}
|
|
835
|
+
catch { }
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
// Foreground mode — start server and keep running
|
|
839
|
+
startDashboard({
|
|
840
|
+
arenaRoot: home,
|
|
841
|
+
port,
|
|
842
|
+
competitionId,
|
|
843
|
+
transitionsPath,
|
|
844
|
+
});
|
|
845
|
+
// Try to open browser
|
|
846
|
+
const url = `http://localhost:${port}`;
|
|
847
|
+
try {
|
|
848
|
+
const openCmd = process.platform === "darwin"
|
|
849
|
+
? `open "${url}"`
|
|
850
|
+
: process.platform === "win32"
|
|
851
|
+
? `start "${url}"`
|
|
852
|
+
: `xdg-open "${url}" 2>/dev/null || true`;
|
|
853
|
+
spawn("sh", ["-c", openCmd], { stdio: "ignore", detached: true }).unref();
|
|
854
|
+
}
|
|
855
|
+
catch { }
|
|
856
|
+
}
|
|
857
|
+
async function runCompetitions() {
|
|
858
|
+
const home = resolveConfiguredHome(optionValue("--home"));
|
|
859
|
+
const bridge = new PythonBridge(home);
|
|
860
|
+
try {
|
|
861
|
+
const status = optionValue("--status") ?? undefined;
|
|
862
|
+
const args = { page: 1, size: 20 };
|
|
863
|
+
if (status)
|
|
864
|
+
args.status = status;
|
|
865
|
+
const result = await bridge.callTool("varsity.competitions", args);
|
|
866
|
+
if (result?.error) {
|
|
867
|
+
console.error(`Error: ${result.error}`);
|
|
868
|
+
process.exit(1);
|
|
869
|
+
}
|
|
870
|
+
const items = result?.items ?? result?.list ?? result;
|
|
871
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
872
|
+
console.log("No competitions found.");
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
console.log("Competitions:\n");
|
|
876
|
+
for (const c of items) {
|
|
877
|
+
const id = c.id ?? c.competitionId ?? "?";
|
|
878
|
+
const name = c.name ?? c.title ?? "Unnamed";
|
|
879
|
+
const st = c.status ?? "unknown";
|
|
880
|
+
const type = c.type ?? c.competitionType ?? "";
|
|
881
|
+
console.log(` #${id} ${name}`);
|
|
882
|
+
console.log(` Status: ${st} Type: ${type}`);
|
|
883
|
+
console.log("");
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
finally {
|
|
887
|
+
await bridge.disconnect();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async function runRegister() {
|
|
891
|
+
const home = resolveConfiguredHome(optionValue("--home"));
|
|
892
|
+
const idArg = argv[1];
|
|
893
|
+
if (!idArg || isNaN(Number(idArg))) {
|
|
894
|
+
console.error("Usage: arena-agent register <competition_id>");
|
|
895
|
+
process.exit(1);
|
|
896
|
+
}
|
|
897
|
+
const bridge = new PythonBridge(home);
|
|
898
|
+
try {
|
|
899
|
+
const result = await bridge.callTool("varsity.register", {
|
|
900
|
+
competition_id: Number(idArg),
|
|
901
|
+
});
|
|
902
|
+
if (result?.error) {
|
|
903
|
+
console.error(`Error: ${result.error ?? result.message}`);
|
|
904
|
+
process.exit(1);
|
|
905
|
+
}
|
|
906
|
+
console.log(`Registered for competition ${idArg}.`);
|
|
907
|
+
if (result?.status) {
|
|
908
|
+
console.log(`Status: ${result.status}`);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
finally {
|
|
912
|
+
await bridge.disconnect();
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
async function runLeaderboard() {
|
|
916
|
+
const home = resolveConfiguredHome(optionValue("--home"));
|
|
917
|
+
const idArg = argv[1];
|
|
918
|
+
if (!idArg) {
|
|
919
|
+
console.error("Usage: arena-agent leaderboard <competition_id>");
|
|
920
|
+
process.exit(1);
|
|
921
|
+
}
|
|
922
|
+
const bridge = new PythonBridge(home);
|
|
923
|
+
try {
|
|
924
|
+
const result = await bridge.callTool("varsity.leaderboard", {
|
|
925
|
+
identifier: idArg,
|
|
926
|
+
page: 1,
|
|
927
|
+
size: 20,
|
|
928
|
+
});
|
|
929
|
+
if (result?.error) {
|
|
930
|
+
console.error(`Error: ${result.error ?? result.message}`);
|
|
931
|
+
process.exit(1);
|
|
932
|
+
}
|
|
933
|
+
const items = result?.items ?? result?.list ?? result;
|
|
934
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
935
|
+
console.log("No leaderboard data.");
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
console.log(`Leaderboard for competition ${idArg}:\n`);
|
|
939
|
+
console.log(" Rank Username PnL");
|
|
940
|
+
console.log(" ---- ------------------- ----------");
|
|
941
|
+
for (const entry of items) {
|
|
942
|
+
const rank = String(entry.rank ?? entry.position ?? "?").padStart(4);
|
|
943
|
+
const user = (entry.username ?? entry.displayName ?? "unknown").padEnd(19);
|
|
944
|
+
const pnl = entry.pnl ?? entry.totalPnl ?? entry.returnPct ?? "?";
|
|
945
|
+
console.log(` ${rank} ${user} ${pnl}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
finally {
|
|
949
|
+
await bridge.disconnect();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
657
952
|
main().catch((err) => {
|
|
658
953
|
console.error(err instanceof Error ? err.message : err);
|
|
659
954
|
process.exit(1);
|