@varsity-arena/agent 0.1.1

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 (57) hide show
  1. package/README.md +127 -0
  2. package/bin/arena-mcp.js +2 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +661 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.js +77 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/python-bridge.d.ts +10 -0
  10. package/dist/python-bridge.js +57 -0
  11. package/dist/python-bridge.js.map +1 -0
  12. package/dist/setup/backend-probe.d.ts +10 -0
  13. package/dist/setup/backend-probe.js +219 -0
  14. package/dist/setup/backend-probe.js.map +1 -0
  15. package/dist/setup/bootstrap-python.d.ts +18 -0
  16. package/dist/setup/bootstrap-python.js +175 -0
  17. package/dist/setup/bootstrap-python.js.map +1 -0
  18. package/dist/setup/client-configs.d.ts +4 -0
  19. package/dist/setup/client-configs.js +61 -0
  20. package/dist/setup/client-configs.js.map +1 -0
  21. package/dist/setup/detect-python.d.ts +8 -0
  22. package/dist/setup/detect-python.js +58 -0
  23. package/dist/setup/detect-python.js.map +1 -0
  24. package/dist/setup/openclaw-agent.d.ts +1 -0
  25. package/dist/setup/openclaw-agent.js +51 -0
  26. package/dist/setup/openclaw-agent.js.map +1 -0
  27. package/dist/tools/competition-info.d.ts +34 -0
  28. package/dist/tools/competition-info.js +15 -0
  29. package/dist/tools/competition-info.js.map +1 -0
  30. package/dist/tools/last-transition.d.ts +11 -0
  31. package/dist/tools/last-transition.js +8 -0
  32. package/dist/tools/last-transition.js.map +1 -0
  33. package/dist/tools/market-state.d.ts +34 -0
  34. package/dist/tools/market-state.js +19 -0
  35. package/dist/tools/market-state.js.map +1 -0
  36. package/dist/tools/runtime-start.d.ts +29 -0
  37. package/dist/tools/runtime-start.js +107 -0
  38. package/dist/tools/runtime-start.js.map +1 -0
  39. package/dist/tools/runtime-stop.d.ts +6 -0
  40. package/dist/tools/runtime-stop.js +7 -0
  41. package/dist/tools/runtime-stop.js.map +1 -0
  42. package/dist/tools/trade-action.d.ts +49 -0
  43. package/dist/tools/trade-action.js +26 -0
  44. package/dist/tools/trade-action.js.map +1 -0
  45. package/dist/util/env.d.ts +8 -0
  46. package/dist/util/env.js +43 -0
  47. package/dist/util/env.js.map +1 -0
  48. package/dist/util/home.d.ts +43 -0
  49. package/dist/util/home.js +263 -0
  50. package/dist/util/home.js.map +1 -0
  51. package/dist/util/paths.d.ts +8 -0
  52. package/dist/util/paths.js +51 -0
  53. package/dist/util/paths.js.map +1 -0
  54. package/dist/util/runtime-state.d.ts +15 -0
  55. package/dist/util/runtime-state.js +55 -0
  56. package/dist/util/runtime-state.js.map +1 -0
  57. package/package.json +59 -0
package/dist/cli.js ADDED
@@ -0,0 +1,661 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * arena-mcp / arena-agent CLI
4
+ *
5
+ * MCP usage:
6
+ * arena-mcp serve
7
+ * arena-mcp setup --client <name>
8
+ * arena-mcp check
9
+ *
10
+ * User workflow:
11
+ * arena-agent init
12
+ * arena-agent doctor
13
+ * arena-agent up --agent gemini
14
+ * arena-agent monitor
15
+ */
16
+ import { spawn, spawnSync } from "node:child_process";
17
+ import { closeSync, existsSync, openSync } from "node:fs";
18
+ import { basename, isAbsolute, resolve } from "node:path";
19
+ import { stdin as input, stdout as output } from "node:process";
20
+ import { setTimeout as sleep } from "node:timers/promises";
21
+ import { createInterface } from "node:readline/promises";
22
+ import { serve } from "./index.js";
23
+ import { findArenaRoot, findPython } from "./util/paths.js";
24
+ import { checkPythonEnvironment } from "./setup/detect-python.js";
25
+ import { CLIENT_SETUP } from "./setup/client-configs.js";
26
+ import { ensureOpenClawTradingAgent } from "./setup/openclaw-agent.js";
27
+ import { probeBackend } from "./setup/backend-probe.js";
28
+ import { bootstrapPythonRuntime, commandAvailable, } from "./setup/bootstrap-python.js";
29
+ 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";
31
+ import { clearRuntimeState, isProcessRunning, latestRuntimeLogPath, readRuntimeState, tailLines, writeRuntimeState, } from "./util/runtime-state.js";
32
+ const argv = process.argv.slice(2);
33
+ const invokedAs = basename(process.argv[1] ?? "arena-agent");
34
+ const command = argv[0] ?? defaultCommand(invokedAs);
35
+ async function main() {
36
+ if (command === "serve") {
37
+ const root = resolveHomeOrRoot(optionValue("--arena-root") ?? optionValue("--home"));
38
+ await serve(root);
39
+ return;
40
+ }
41
+ if (command === "check") {
42
+ const root = resolveConfiguredHome(optionValue("--home"));
43
+ const result = checkPythonEnvironment(root);
44
+ console.log(`Arena home: ${root}`);
45
+ console.log(`Python: ${result.python ?? "not found"}`);
46
+ console.log(`Venv: ${result.venv ? "ok" : "missing"}`);
47
+ console.log(`Deps: ${result.deps ? "ok" : "missing"}`);
48
+ if (result.errors.length > 0) {
49
+ console.log("\nIssues:");
50
+ for (const err of result.errors) {
51
+ console.log(` - ${err}`);
52
+ }
53
+ process.exit(1);
54
+ }
55
+ console.log("\nAll checks passed.");
56
+ return;
57
+ }
58
+ if (command === "setup") {
59
+ const clientName = requireOption("--client", `Usage: ${invokedAs} setup --client <${Object.keys(CLIENT_SETUP).join("|")}>`);
60
+ const setupFn = CLIENT_SETUP[clientName];
61
+ if (!setupFn) {
62
+ throw new Error(`Unknown client: ${clientName}. Supported: ${Object.keys(CLIENT_SETUP).join(", ")}`);
63
+ }
64
+ const root = resolveConfiguredHome(optionValue("--home"));
65
+ console.log("Checking Python environment...");
66
+ const check = checkPythonEnvironment(root);
67
+ if (check.errors.length > 0) {
68
+ console.log("\nIssues found:");
69
+ for (const err of check.errors) {
70
+ console.log(` - ${err}`);
71
+ }
72
+ console.log("\nFix the issues above, then re-run setup.");
73
+ process.exit(1);
74
+ }
75
+ const configPath = setupFn(root);
76
+ console.log(`\nConfigured ${clientName} at: ${configPath}`);
77
+ console.log(`Arena home: ${root}`);
78
+ console.log("\nTools available:");
79
+ console.log(" arena.market_state Get market/account/position state");
80
+ console.log(" arena.competition_info Competition metadata");
81
+ console.log(" arena.trade_action Submit a trade");
82
+ console.log(" arena.last_transition Last trade event");
83
+ console.log(" arena.runtime_start Start autonomous agent");
84
+ console.log(" arena.runtime_stop Stop autonomous agent");
85
+ return;
86
+ }
87
+ if (command === "init") {
88
+ await initManagedHome();
89
+ return;
90
+ }
91
+ if (command === "doctor") {
92
+ runDoctor();
93
+ return;
94
+ }
95
+ if (command === "up") {
96
+ const code = await runUp();
97
+ process.exit(code);
98
+ }
99
+ if (command === "monitor") {
100
+ const code = await runMonitorOnly();
101
+ process.exit(code);
102
+ }
103
+ if (command === "upgrade") {
104
+ await runUpgrade();
105
+ return;
106
+ }
107
+ if (command === "status") {
108
+ runStatus();
109
+ return;
110
+ }
111
+ if (command === "down") {
112
+ runDown();
113
+ return;
114
+ }
115
+ if (command === "logs") {
116
+ runLogs();
117
+ return;
118
+ }
119
+ printUsage(invokedAs);
120
+ process.exit(command ? 1 : 0);
121
+ }
122
+ async function initManagedHome() {
123
+ const home = resolveArenaHome(optionValue("--home"));
124
+ const existingState = readArenaHomeState(home);
125
+ const existingEnv = loadEnvFile(home);
126
+ const availableCliBackends = detectInstalledCliBackends();
127
+ let apiKey = optionValue("--api-key") ?? existingEnv.VARSITY_API_KEY ?? "";
128
+ let agent = (optionValue("--agent") ??
129
+ existingState?.defaultAgent ??
130
+ (availableCliBackends.length > 0 ? "auto" : "rule"));
131
+ let model = optionValue("--model") ?? existingState?.defaultModel ?? "";
132
+ let liveTrading = parseTradingMode(optionValue("--mode")) ??
133
+ existingState?.liveTrading ??
134
+ false;
135
+ const pythonInstallSource = optionValue("--python-source") ??
136
+ existingState?.pythonInstallSource ??
137
+ DEFAULT_PYTHON_INSTALL_SOURCE;
138
+ if (!hasFlag("--non-interactive")) {
139
+ const rl = createInterface({ input, output });
140
+ try {
141
+ if (!apiKey) {
142
+ apiKey = await promptRequired(rl, "Varsity API key");
143
+ }
144
+ else {
145
+ apiKey = await promptValue(rl, "Varsity API key", apiKey);
146
+ }
147
+ agent = (await promptValue(rl, `Default agent backend [auto/rule/claude/gemini/openclaw/codex]`, agent));
148
+ model = await promptValue(rl, "Default model override (leave blank for backend default)", model);
149
+ const modeAnswer = await promptValue(rl, "Trading mode [live/dry-run]", liveTrading ? "live" : "dry-run");
150
+ liveTrading = modeAnswer.trim().toLowerCase() !== "dry-run";
151
+ if (liveTrading) {
152
+ await confirmLiveTrading(rl);
153
+ }
154
+ }
155
+ finally {
156
+ rl.close();
157
+ }
158
+ }
159
+ else if (liveTrading && !hasFlag("--yes-live")) {
160
+ throw new Error("Live trading requires explicit confirmation. Re-run init with --yes-live or choose --mode dry-run.");
161
+ }
162
+ if (!apiKey.trim()) {
163
+ throw new Error("VARSITY_API_KEY is required.");
164
+ }
165
+ if (!isManagedAgent(agent)) {
166
+ throw new Error("Invalid agent backend. Use one of: auto, rule, claude, gemini, openclaw, codex.");
167
+ }
168
+ validateAgentAvailability(agent, availableCliBackends);
169
+ console.log(`Preparing Arena home at ${home}`);
170
+ const state = createArenaHomeState(home, {
171
+ defaultAgent: agent,
172
+ defaultModel: model.trim() || null,
173
+ liveTrading,
174
+ pythonInstallSource,
175
+ });
176
+ writeArenaEnvFile(home, apiKey);
177
+ writeManagedConfigs(home, state, { overwrite: true });
178
+ writeArenaHomeState(home, state);
179
+ console.log("\nBootstrapping Python runtime...");
180
+ bootstrapPythonRuntime({
181
+ home,
182
+ pythonInstallSource,
183
+ reinstall: hasFlag("--reinstall"),
184
+ installMonitor: true,
185
+ installMcp: true,
186
+ });
187
+ if (agent === "openclaw") {
188
+ console.log("Provisioning dedicated OpenClaw trading agent...");
189
+ ensureOpenClawTradingAgent(home);
190
+ }
191
+ console.log("\nArena agent is ready.");
192
+ console.log(`Home: ${home}`);
193
+ console.log(`API key file: ${envFilePath(home)}`);
194
+ console.log(`Config dir: ${configDirPath(home)}`);
195
+ console.log(`Artifacts dir: ${artifactsDirPath(home)}`);
196
+ console.log("\nNext steps:");
197
+ console.log(` arena-agent doctor --home ${home}`);
198
+ console.log(` arena-agent up --home ${home}`);
199
+ }
200
+ function runDoctor() {
201
+ const home = resolveConfiguredHome(optionValue("--home"));
202
+ const state = readArenaHomeState(home);
203
+ const env = loadEnvFile(home);
204
+ const pythonCheck = checkPythonEnvironment(home);
205
+ const errors = [...pythonCheck.errors];
206
+ console.log(`Arena home: ${home}`);
207
+ console.log(`Managed home: ${isManagedArenaHome(home) ? "yes" : "no"}`);
208
+ console.log(`Python: ${pythonCheck.python ?? "not found"}`);
209
+ console.log(`Venv: ${pythonCheck.venv ? "ok" : "missing"}`);
210
+ console.log(`Runtime deps: ${pythonCheck.deps ? "ok" : "missing"}`);
211
+ console.log(`Env file: ${existsSync(envFilePath(home)) ? "ok" : "missing"}`);
212
+ console.log(`API key: ${env.VARSITY_API_KEY ? "set" : "missing"}`);
213
+ if (state) {
214
+ console.log(`Default agent: ${state.defaultAgent}`);
215
+ console.log(`Default model: ${state.defaultModel ?? "default"}`);
216
+ console.log(`Mode: ${state.liveTrading ? "live" : "dry-run"}`);
217
+ console.log(`Monitor port: ${state.monitorPort}`);
218
+ console.log(`Agent config: ${state.profiles.agentExec}`);
219
+ console.log(`Rule config: ${state.profiles.rule}`);
220
+ if (!existsSync(state.profiles.agentExec)) {
221
+ errors.push(`Missing managed config: ${state.profiles.agentExec}`);
222
+ }
223
+ if (!existsSync(state.profiles.rule)) {
224
+ errors.push(`Missing managed config: ${state.profiles.rule}`);
225
+ }
226
+ const backendProbe = probeBackend(state.defaultAgent);
227
+ console.log(`Backend CLI: ${backendProbe.summary}`);
228
+ if (!backendProbe.ready) {
229
+ errors.push(backendProbe.details);
230
+ }
231
+ }
232
+ else if (isManagedArenaHome(home)) {
233
+ errors.push(`Arena home marker exists but is invalid: ${home}`);
234
+ }
235
+ else {
236
+ errors.push(`Arena home is not initialized. Run: arena-agent init --home ${home}`);
237
+ }
238
+ if (pythonCheck.python) {
239
+ const monitorDepsOk = pythonImportsOk(pythonCheck.python, home, ["textual", "rich"]);
240
+ console.log(`Monitor deps: ${monitorDepsOk ? "ok" : "missing"}`);
241
+ if (!monitorDepsOk) {
242
+ errors.push("Monitor dependencies are missing.");
243
+ }
244
+ }
245
+ if (errors.length > 0) {
246
+ console.log("\nIssues:");
247
+ for (const err of errors) {
248
+ console.log(` - ${err}`);
249
+ }
250
+ process.exit(1);
251
+ }
252
+ console.log("\nAll checks passed.");
253
+ }
254
+ async function runUp() {
255
+ const home = resolveConfiguredHome(optionValue("--home"));
256
+ const state = readArenaHomeState(home);
257
+ if (!state) {
258
+ throw new Error(`Arena home is not initialized at ${home}. Run \`arena-agent init\` first.`);
259
+ }
260
+ const agent = (optionValue("--agent") ?? state.defaultAgent);
261
+ const model = optionValue("--model") ?? state.defaultModel ?? undefined;
262
+ validateAgentAvailability(agent, detectInstalledCliBackends());
263
+ if (state.liveTrading) {
264
+ console.log("LIVE trading is enabled for this Arena home.");
265
+ }
266
+ else {
267
+ console.log("Dry-run mode is enabled for this Arena home.");
268
+ }
269
+ if (agent === "openclaw") {
270
+ ensureOpenClawTradingAgent(home);
271
+ }
272
+ const configPath = resolveUserConfigPath(home, state, optionValue("--config"), agent);
273
+ const python = findPython(home);
274
+ const env = buildChildEnv(home);
275
+ const runtimeArgs = [
276
+ "-m",
277
+ "arena_agent",
278
+ "run",
279
+ "--agent",
280
+ agent,
281
+ "--config",
282
+ configPath,
283
+ ];
284
+ if (model) {
285
+ runtimeArgs.push("--model", model);
286
+ }
287
+ const iterations = optionValue("--iterations");
288
+ if (iterations) {
289
+ runtimeArgs.push("--iterations", iterations);
290
+ }
291
+ const monitorPort = Number(optionValue("--port") ?? String(state.monitorPort ?? DEFAULT_MONITOR_PORT));
292
+ const daemonMode = hasFlag("--daemon");
293
+ if (daemonMode && !hasFlag("--no-monitor")) {
294
+ throw new Error("Use --daemon together with --no-monitor. Attach later with `arena-agent monitor`.");
295
+ }
296
+ if (hasFlag("--no-monitor")) {
297
+ const logPath = resolve(logsDirPath(home), `runtime-${Date.now()}.log`);
298
+ if (daemonMode) {
299
+ const logFd = openSync(logPath, "a");
300
+ const child = spawn(python, runtimeArgs, {
301
+ cwd: home,
302
+ env,
303
+ stdio: ["ignore", logFd, logFd],
304
+ detached: true,
305
+ });
306
+ closeSync(logFd);
307
+ child.unref();
308
+ if (child.pid) {
309
+ writeRuntimeState(home, {
310
+ pid: child.pid,
311
+ agent,
312
+ configPath,
313
+ logPath,
314
+ startedAt: new Date().toISOString(),
315
+ monitorPort,
316
+ });
317
+ }
318
+ console.log(`Runtime started in background with pid ${child.pid ?? "unknown"}`);
319
+ console.log(`Logs: ${logPath}`);
320
+ console.log(`Monitor: arena-agent monitor --home ${home}`);
321
+ return 0;
322
+ }
323
+ const child = spawn(python, runtimeArgs, {
324
+ cwd: home,
325
+ env,
326
+ stdio: "inherit",
327
+ });
328
+ return waitForExit(child);
329
+ }
330
+ const logPath = resolve(logsDirPath(home), `runtime-${Date.now()}.log`);
331
+ const logFd = openSync(logPath, "a");
332
+ const runtimeChild = spawn(python, runtimeArgs, {
333
+ cwd: home,
334
+ env,
335
+ stdio: ["ignore", logFd, logFd],
336
+ });
337
+ closeSync(logFd);
338
+ if (runtimeChild.pid) {
339
+ writeRuntimeState(home, {
340
+ pid: runtimeChild.pid,
341
+ agent,
342
+ configPath,
343
+ logPath,
344
+ startedAt: new Date().toISOString(),
345
+ monitorPort,
346
+ });
347
+ }
348
+ console.log(`Runtime started with pid ${runtimeChild.pid ?? "unknown"}`);
349
+ console.log(`Runtime logs: ${logPath}`);
350
+ await sleep(800);
351
+ const monitorArgs = [
352
+ "-m",
353
+ "arena_agent",
354
+ "monitor",
355
+ "--host",
356
+ "127.0.0.1",
357
+ "--port",
358
+ String(monitorPort),
359
+ ];
360
+ const monitorChild = spawn(python, monitorArgs, {
361
+ cwd: home,
362
+ env,
363
+ stdio: "inherit",
364
+ });
365
+ const shutdown = () => {
366
+ if (!monitorChild.killed) {
367
+ monitorChild.kill("SIGINT");
368
+ }
369
+ if (!runtimeChild.killed) {
370
+ runtimeChild.kill("SIGTERM");
371
+ }
372
+ };
373
+ const onSignal = () => shutdown();
374
+ process.once("SIGINT", onSignal);
375
+ process.once("SIGTERM", onSignal);
376
+ runtimeChild.once("exit", (code) => {
377
+ if (code && !monitorChild.killed) {
378
+ console.error(`Runtime exited with code ${code}. Check ${logPath}`);
379
+ monitorChild.kill("SIGINT");
380
+ }
381
+ });
382
+ const monitorCode = await waitForExit(monitorChild);
383
+ if (!runtimeChild.killed) {
384
+ runtimeChild.kill("SIGTERM");
385
+ await waitForExit(runtimeChild);
386
+ }
387
+ clearRuntimeState(home);
388
+ return monitorCode;
389
+ }
390
+ async function runMonitorOnly() {
391
+ const home = resolveConfiguredHome(optionValue("--home"));
392
+ const state = readArenaHomeState(home);
393
+ const monitorPort = Number(optionValue("--port") ?? String(state?.monitorPort ?? DEFAULT_MONITOR_PORT));
394
+ const python = findPython(home);
395
+ const env = buildChildEnv(home);
396
+ const child = spawn(python, [
397
+ "-m",
398
+ "arena_agent",
399
+ "monitor",
400
+ "--host",
401
+ "127.0.0.1",
402
+ "--port",
403
+ String(monitorPort),
404
+ ], {
405
+ cwd: home,
406
+ env,
407
+ stdio: "inherit",
408
+ });
409
+ return waitForExit(child);
410
+ }
411
+ function resolveConfiguredHome(explicitHome) {
412
+ if (explicitHome) {
413
+ return resolveArenaHome(explicitHome);
414
+ }
415
+ return findArenaRoot();
416
+ }
417
+ function resolveHomeOrRoot(explicitHome) {
418
+ if (explicitHome) {
419
+ return resolveArenaHome(explicitHome);
420
+ }
421
+ return findArenaRoot();
422
+ }
423
+ function resolveUserConfigPath(home, state, rawConfig, agent) {
424
+ const candidates = [];
425
+ if (rawConfig) {
426
+ if (isAbsolute(rawConfig)) {
427
+ return rawConfig;
428
+ }
429
+ candidates.push(resolve(home, rawConfig));
430
+ candidates.push(resolve(home, "arena_agent", "config", rawConfig));
431
+ }
432
+ else if (isManagedArenaHome(home) && state) {
433
+ if (agent === "rule") {
434
+ candidates.push(state.profiles.rule);
435
+ }
436
+ else {
437
+ candidates.push(state.profiles.agentExec);
438
+ }
439
+ }
440
+ else if (agent === "rule") {
441
+ candidates.push(resolve(home, "arena_agent", "config", "agent_config.yaml"));
442
+ }
443
+ else {
444
+ candidates.push(resolve(home, "arena_agent", "config", "codex_agent_config.yaml"));
445
+ }
446
+ for (const candidate of candidates) {
447
+ if (existsSync(candidate)) {
448
+ return candidate;
449
+ }
450
+ }
451
+ return candidates[0];
452
+ }
453
+ function detectInstalledCliBackends() {
454
+ const installed = [];
455
+ if (commandAvailable("claude")) {
456
+ installed.push("claude");
457
+ }
458
+ if (commandAvailable("gemini")) {
459
+ installed.push("gemini");
460
+ }
461
+ if (commandAvailable("openclaw")) {
462
+ installed.push("openclaw");
463
+ }
464
+ if (commandAvailable("codex")) {
465
+ installed.push("codex");
466
+ }
467
+ return installed;
468
+ }
469
+ function describeBackendStatus(agent) {
470
+ return probeBackend(agent).summary;
471
+ }
472
+ function validateAgentAvailability(agent, availableCliBackends) {
473
+ if (agent === "rule") {
474
+ return;
475
+ }
476
+ if (agent === "auto" && availableCliBackends.length > 0) {
477
+ const autoProbe = probeBackend("auto");
478
+ if (!autoProbe.ready) {
479
+ throw new Error(autoProbe.details);
480
+ }
481
+ return;
482
+ }
483
+ if (agent === "auto") {
484
+ throw new Error("No CLI backend found in PATH for auto mode. Install claude, gemini, openclaw, or codex, or use --agent rule.");
485
+ }
486
+ const probe = probeBackend(agent);
487
+ if (!probe.available || !probe.ready) {
488
+ throw new Error(probe.details);
489
+ }
490
+ }
491
+ function pythonImportsOk(python, cwd, modules) {
492
+ const script = modules.map((name) => `import ${name}`).join("; ");
493
+ const result = spawnSync(python, ["-c", script], {
494
+ cwd,
495
+ stdio: "ignore",
496
+ });
497
+ return result.status === 0;
498
+ }
499
+ function defaultCommand(invocation) {
500
+ if (invocation === "arena-mcp") {
501
+ return "serve";
502
+ }
503
+ return "help";
504
+ }
505
+ function printUsage(invocation) {
506
+ console.log(`Usage: ${invocation} <command> [options]`);
507
+ console.log("");
508
+ console.log("Commands:");
509
+ console.log(" serve Start MCP server on stdio");
510
+ console.log(" setup --client <name> Configure an MCP client");
511
+ console.log(" check Validate Python environment");
512
+ console.log(" init Bootstrap a managed Arena home");
513
+ console.log(" doctor Check managed home, Python, deps, and backend CLI");
514
+ console.log(" up Start trading runtime and open the TUI monitor");
515
+ console.log(" monitor Attach to the TUI monitor only");
516
+ console.log(" upgrade Reinstall or refresh the managed Python runtime");
517
+ console.log(" status Show runtime pid, config, and monitor port");
518
+ console.log(" down Stop the background runtime");
519
+ console.log(" logs Print recent runtime logs");
520
+ console.log("");
521
+ console.log("Examples:");
522
+ console.log(" arena-agent init");
523
+ console.log(" arena-agent init --agent openclaw --mode dry-run");
524
+ console.log(" arena-agent up --agent gemini");
525
+ console.log(" arena-agent up --no-monitor --daemon");
526
+ console.log(" arena-agent upgrade");
527
+ console.log(" arena-mcp setup --client claude-code");
528
+ }
529
+ function optionValue(name) {
530
+ const idx = argv.indexOf(name);
531
+ if (idx < 0 || idx + 1 >= argv.length) {
532
+ return undefined;
533
+ }
534
+ return argv[idx + 1];
535
+ }
536
+ function requireOption(name, usage) {
537
+ const value = optionValue(name);
538
+ if (!value) {
539
+ throw new Error(usage);
540
+ }
541
+ return value;
542
+ }
543
+ function hasFlag(name) {
544
+ return argv.includes(name);
545
+ }
546
+ function parseTradingMode(value) {
547
+ if (!value) {
548
+ return undefined;
549
+ }
550
+ const normalized = value.trim().toLowerCase();
551
+ if (normalized === "live") {
552
+ return true;
553
+ }
554
+ if (normalized === "dry-run" || normalized === "dryrun") {
555
+ return false;
556
+ }
557
+ throw new Error("Trading mode must be `live` or `dry-run`.");
558
+ }
559
+ function isManagedAgent(value) {
560
+ return ["auto", "rule", "claude", "gemini", "openclaw", "codex"].includes(value);
561
+ }
562
+ async function promptValue(rl, label, defaultValue = "") {
563
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
564
+ const answer = (await rl.question(`${label}${suffix}: `)).trim();
565
+ return answer || defaultValue;
566
+ }
567
+ async function promptRequired(rl, label) {
568
+ while (true) {
569
+ const answer = (await rl.question(`${label}: `)).trim();
570
+ if (answer) {
571
+ return answer;
572
+ }
573
+ }
574
+ }
575
+ function waitForExit(child) {
576
+ return new Promise((resolvePromise) => {
577
+ child.once("exit", (code) => {
578
+ resolvePromise(code ?? 0);
579
+ });
580
+ });
581
+ }
582
+ async function runUpgrade() {
583
+ const home = resolveConfiguredHome(optionValue("--home"));
584
+ const state = readArenaHomeState(home);
585
+ if (!state) {
586
+ throw new Error(`Arena home is not initialized at ${home}. Run \`arena-agent init\` first.`);
587
+ }
588
+ const pythonInstallSource = optionValue("--python-source") ?? state.pythonInstallSource;
589
+ console.log(`Upgrading managed runtime in ${home}`);
590
+ bootstrapPythonRuntime({
591
+ home,
592
+ pythonInstallSource,
593
+ reinstall: true,
594
+ installMonitor: true,
595
+ installMcp: true,
596
+ });
597
+ if (state.defaultAgent === "openclaw") {
598
+ ensureOpenClawTradingAgent(home);
599
+ }
600
+ console.log("Managed runtime upgraded.");
601
+ }
602
+ async function confirmLiveTrading(rl) {
603
+ console.log("");
604
+ console.log("Live trading will send real orders from this machine.");
605
+ const answer = (await rl.question("Type LIVE to confirm: ")).trim();
606
+ if (answer !== "LIVE") {
607
+ throw new Error("Live trading confirmation was not provided.");
608
+ }
609
+ }
610
+ function runStatus() {
611
+ const home = resolveConfiguredHome(optionValue("--home"));
612
+ const runtimeState = readRuntimeState(home);
613
+ if (!runtimeState) {
614
+ console.log(`Arena home: ${home}`);
615
+ console.log("Runtime: stopped");
616
+ return;
617
+ }
618
+ const running = isProcessRunning(runtimeState.pid);
619
+ console.log(`Arena home: ${home}`);
620
+ console.log(`Runtime: ${running ? "running" : "stopped"}`);
621
+ console.log(`PID: ${runtimeState.pid}`);
622
+ console.log(`Agent: ${runtimeState.agent}`);
623
+ console.log(`Config: ${runtimeState.configPath}`);
624
+ console.log(`Logs: ${runtimeState.logPath}`);
625
+ console.log(`Monitor port: ${runtimeState.monitorPort}`);
626
+ console.log(`Started at: ${runtimeState.startedAt}`);
627
+ if (!running) {
628
+ clearRuntimeState(home);
629
+ }
630
+ }
631
+ function runDown() {
632
+ const home = resolveConfiguredHome(optionValue("--home"));
633
+ const runtimeState = readRuntimeState(home);
634
+ if (!runtimeState) {
635
+ console.log("Runtime is not running.");
636
+ return;
637
+ }
638
+ if (!isProcessRunning(runtimeState.pid)) {
639
+ clearRuntimeState(home);
640
+ console.log("Runtime is already stopped.");
641
+ return;
642
+ }
643
+ process.kill(runtimeState.pid, "SIGTERM");
644
+ clearRuntimeState(home);
645
+ console.log(`Stopped runtime pid ${runtimeState.pid}.`);
646
+ }
647
+ function runLogs() {
648
+ const home = resolveConfiguredHome(optionValue("--home"));
649
+ const runtimeState = readRuntimeState(home);
650
+ const logPath = runtimeState?.logPath ?? latestRuntimeLogPath(home);
651
+ if (!logPath || !existsSync(logPath)) {
652
+ throw new Error("No runtime log file found.");
653
+ }
654
+ const lines = Number(optionValue("--lines") ?? "50");
655
+ console.log(tailLines(logPath, lines));
656
+ }
657
+ main().catch((err) => {
658
+ console.error(err instanceof Error ? err.message : err);
659
+ process.exit(1);
660
+ });
661
+ //# sourceMappingURL=cli.js.map