@trading-boy/cli 1.2.18 → 1.2.19
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/dist/cli.bundle.js +227 -11
- package/dist/commands/trader.js +283 -0
- package/package.json +1 -1
package/dist/cli.bundle.js
CHANGED
|
@@ -23934,7 +23934,7 @@ var init_RemoveFileError = __esm({
|
|
|
23934
23934
|
|
|
23935
23935
|
// ../../node_modules/.pnpm/@inquirer+external-editor@1.0.3_@types+node@25.2.3/node_modules/@inquirer/external-editor/dist/esm/index.js
|
|
23936
23936
|
import { spawn, spawnSync } from "child_process";
|
|
23937
|
-
import { readFileSync as
|
|
23937
|
+
import { readFileSync as readFileSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
23938
23938
|
import path2 from "node:path";
|
|
23939
23939
|
import os2 from "node:os";
|
|
23940
23940
|
import { randomUUID } from "node:crypto";
|
|
@@ -24051,14 +24051,14 @@ var init_esm5 = __esm({
|
|
|
24051
24051
|
if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
|
|
24052
24052
|
opt.mode = this.fileOptions.mode;
|
|
24053
24053
|
}
|
|
24054
|
-
|
|
24054
|
+
writeFileSync2(this.tempFile, this.text, opt);
|
|
24055
24055
|
} catch (createFileError) {
|
|
24056
24056
|
throw new CreateFileError(createFileError);
|
|
24057
24057
|
}
|
|
24058
24058
|
}
|
|
24059
24059
|
readTemporaryFile() {
|
|
24060
24060
|
try {
|
|
24061
|
-
const tempFileBuffer =
|
|
24061
|
+
const tempFileBuffer = readFileSync2(this.tempFile);
|
|
24062
24062
|
if (tempFileBuffer.length === 0) {
|
|
24063
24063
|
this.text = "";
|
|
24064
24064
|
} else {
|
|
@@ -48143,9 +48143,10 @@ function computeStats(decisions) {
|
|
|
48143
48143
|
}
|
|
48144
48144
|
|
|
48145
48145
|
// dist/commands/trader.js
|
|
48146
|
-
import { readFileSync as
|
|
48146
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
48147
48147
|
init_source();
|
|
48148
48148
|
init_utils();
|
|
48149
|
+
init_esm15();
|
|
48149
48150
|
var logger12 = createLogger("cli-trader");
|
|
48150
48151
|
function formatTraderOutput(trader, wallets) {
|
|
48151
48152
|
const lines = [];
|
|
@@ -48424,7 +48425,7 @@ function registerTraderCommand(program2) {
|
|
|
48424
48425
|
trader.command("soul").description("Show or upload a SOUL document").argument("<name-or-id>", "Trader name or ID").option("--file <path>", "Path to SOUL document file to upload").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (nameOrId, options) => {
|
|
48425
48426
|
try {
|
|
48426
48427
|
if (options.file) {
|
|
48427
|
-
const document =
|
|
48428
|
+
const document = readFileSync3(options.file, "utf-8");
|
|
48428
48429
|
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/soul`, { method: "PUT", body: { document } });
|
|
48429
48430
|
if (options.format === "json") {
|
|
48430
48431
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -48472,7 +48473,7 @@ function registerTraderCommand(program2) {
|
|
|
48472
48473
|
trader.command("purpose").description("Show or upload a PURPOSE document").argument("<name-or-id>", "Trader name or ID").option("--file <path>", "Path to PURPOSE document file to upload").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (nameOrId, options) => {
|
|
48473
48474
|
try {
|
|
48474
48475
|
if (options.file) {
|
|
48475
|
-
const document =
|
|
48476
|
+
const document = readFileSync3(options.file, "utf-8");
|
|
48476
48477
|
const result = await apiRequest(`/api/v1/traders/${encodeURIComponent(nameOrId)}/purpose`, { method: "PUT", body: { document } });
|
|
48477
48478
|
if (options.format === "json") {
|
|
48478
48479
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -48633,6 +48634,221 @@ function registerTraderCommand(program2) {
|
|
|
48633
48634
|
process.exitCode = error49 instanceof ApiError ? 2 : 1;
|
|
48634
48635
|
}
|
|
48635
48636
|
});
|
|
48637
|
+
const AUTONOMY_LEVELS = ["OBSERVE_ONLY", "SUGGEST", "AUTO_WITH_APPROVAL", "FULLY_AUTONOMOUS"];
|
|
48638
|
+
const agent = trader.command("agent").description("Manage autonomous trading agent");
|
|
48639
|
+
agent.command("status").description("Show agent runtime status").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
|
|
48640
|
+
const jsonMode = options.format === "json";
|
|
48641
|
+
try {
|
|
48642
|
+
const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/status`);
|
|
48643
|
+
if (jsonMode) {
|
|
48644
|
+
const safeOutput = {
|
|
48645
|
+
agentId,
|
|
48646
|
+
state: result.state,
|
|
48647
|
+
autonomyLevel: result.autonomyLevel,
|
|
48648
|
+
paused: result.paused,
|
|
48649
|
+
killed: result.killed,
|
|
48650
|
+
override: result.humanOverride,
|
|
48651
|
+
openPositions: result.openPositions ?? 0,
|
|
48652
|
+
dailyTradeCount: result.dailyTradeCount ?? 0,
|
|
48653
|
+
dailyPnlUsd: result.dailyPnlUsd ?? null,
|
|
48654
|
+
lastTransitionAt: result.lastTransitionAt ?? null,
|
|
48655
|
+
healthTimestamp: result.healthTimestamp ?? null
|
|
48656
|
+
};
|
|
48657
|
+
console.log(JSON.stringify(safeOutput, null, 2));
|
|
48658
|
+
} else {
|
|
48659
|
+
console.log("");
|
|
48660
|
+
console.log(source_default.bold.cyan(" Agent Status"));
|
|
48661
|
+
console.log(source_default.gray(" " + "\u2500".repeat(50)));
|
|
48662
|
+
console.log("");
|
|
48663
|
+
console.log(` ${source_default.gray("Agent ID:")} ${source_default.white(agentId)}`);
|
|
48664
|
+
console.log(` ${source_default.gray("State:")} ${formatAgentState(result.state)}`);
|
|
48665
|
+
console.log(` ${source_default.gray("Autonomy:")} ${source_default.white(result.autonomyLevel)}`);
|
|
48666
|
+
console.log(` ${source_default.gray("Paused:")} ${result.paused ? source_default.yellow("yes") : source_default.green("no")}`);
|
|
48667
|
+
console.log(` ${source_default.gray("Killed:")} ${result.killed ? source_default.red("yes") : source_default.green("no")}`);
|
|
48668
|
+
console.log(` ${source_default.gray("Override:")} ${result.humanOverride ? source_default.yellow("active") : source_default.dim("none")}`);
|
|
48669
|
+
console.log(` ${source_default.gray("Open positions:")} ${source_default.white(String(result.openPositions ?? 0))}`);
|
|
48670
|
+
console.log(` ${source_default.gray("Trades today:")} ${source_default.white(String(result.dailyTradeCount ?? 0))}`);
|
|
48671
|
+
console.log(` ${source_default.gray("Daily PnL:")} ${formatPnl2(result.dailyPnlUsd)}`);
|
|
48672
|
+
console.log("");
|
|
48673
|
+
}
|
|
48674
|
+
} catch (error49) {
|
|
48675
|
+
handleAgentError(error49, jsonMode, agentId, "get agent status");
|
|
48676
|
+
}
|
|
48677
|
+
});
|
|
48678
|
+
agent.command("pause").description("Pause the agent (stops all trading)").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
|
|
48679
|
+
const jsonMode = options.format === "json";
|
|
48680
|
+
try {
|
|
48681
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/pause`, { method: "POST" });
|
|
48682
|
+
if (jsonMode) {
|
|
48683
|
+
console.log(JSON.stringify({ agentId, paused: true }));
|
|
48684
|
+
} else {
|
|
48685
|
+
console.log("");
|
|
48686
|
+
console.log(source_default.yellow(` Agent ${agentId} paused.`));
|
|
48687
|
+
console.log(source_default.dim(" Resume with: trading-boy trader agent resume " + agentId));
|
|
48688
|
+
console.log("");
|
|
48689
|
+
}
|
|
48690
|
+
} catch (error49) {
|
|
48691
|
+
handleAgentError(error49, jsonMode, agentId, "pause agent");
|
|
48692
|
+
}
|
|
48693
|
+
});
|
|
48694
|
+
agent.command("resume").description("Resume a paused agent").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
|
|
48695
|
+
const jsonMode = options.format === "json";
|
|
48696
|
+
try {
|
|
48697
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/resume`, { method: "POST" });
|
|
48698
|
+
if (jsonMode) {
|
|
48699
|
+
console.log(JSON.stringify({ agentId, resumed: true }));
|
|
48700
|
+
} else {
|
|
48701
|
+
console.log("");
|
|
48702
|
+
console.log(source_default.green(` Agent ${agentId} resumed.`));
|
|
48703
|
+
console.log("");
|
|
48704
|
+
}
|
|
48705
|
+
} catch (error49) {
|
|
48706
|
+
handleAgentError(error49, jsonMode, agentId, "resume agent");
|
|
48707
|
+
}
|
|
48708
|
+
});
|
|
48709
|
+
agent.command("kill").description("Kill the agent permanently (until revived)").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).addOption(new Option("--force", "Skip confirmation prompt")).action(async (agentId, options) => {
|
|
48710
|
+
const jsonMode = options.format === "json";
|
|
48711
|
+
if (jsonMode && !options.force) {
|
|
48712
|
+
console.error(JSON.stringify({ error: "--force is required with --format json" }));
|
|
48713
|
+
process.exitCode = 1;
|
|
48714
|
+
return;
|
|
48715
|
+
}
|
|
48716
|
+
if (!options.force) {
|
|
48717
|
+
const proceed = await esm_default4({
|
|
48718
|
+
message: `Are you sure you want to kill agent ${agentId}? This is permanent until revived.`
|
|
48719
|
+
});
|
|
48720
|
+
if (!proceed) {
|
|
48721
|
+
console.log(source_default.dim(" Kill cancelled."));
|
|
48722
|
+
return;
|
|
48723
|
+
}
|
|
48724
|
+
}
|
|
48725
|
+
try {
|
|
48726
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/kill`, { method: "POST" });
|
|
48727
|
+
if (jsonMode) {
|
|
48728
|
+
console.log(JSON.stringify({ agentId, killed: true }));
|
|
48729
|
+
} else {
|
|
48730
|
+
console.log("");
|
|
48731
|
+
console.log(source_default.red(` Agent ${agentId} killed.`));
|
|
48732
|
+
console.log(source_default.dim(" The agent will not trade until manually revived."));
|
|
48733
|
+
console.log("");
|
|
48734
|
+
}
|
|
48735
|
+
} catch (error49) {
|
|
48736
|
+
handleAgentError(error49, jsonMode, agentId, "kill agent");
|
|
48737
|
+
}
|
|
48738
|
+
});
|
|
48739
|
+
agent.command("autonomy").description("Set agent autonomy level").argument("<agent-id>", "Agent ID").argument("<level>", `Autonomy level: ${AUTONOMY_LEVELS.join(" | ")}`).addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, level, options) => {
|
|
48740
|
+
const jsonMode = options.format === "json";
|
|
48741
|
+
const upperLevel = level.toUpperCase();
|
|
48742
|
+
if (!AUTONOMY_LEVELS.includes(upperLevel)) {
|
|
48743
|
+
const msg = `Invalid autonomy level. Must be one of: ${AUTONOMY_LEVELS.join(", ")}`;
|
|
48744
|
+
if (jsonMode) {
|
|
48745
|
+
console.error(JSON.stringify({ error: msg }));
|
|
48746
|
+
} else {
|
|
48747
|
+
console.error(source_default.red(` ${msg}`));
|
|
48748
|
+
}
|
|
48749
|
+
process.exitCode = 1;
|
|
48750
|
+
return;
|
|
48751
|
+
}
|
|
48752
|
+
try {
|
|
48753
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/autonomy`, {
|
|
48754
|
+
method: "PUT",
|
|
48755
|
+
body: { level: upperLevel }
|
|
48756
|
+
});
|
|
48757
|
+
if (jsonMode) {
|
|
48758
|
+
console.log(JSON.stringify({ agentId, autonomyLevel: upperLevel }));
|
|
48759
|
+
} else {
|
|
48760
|
+
console.log("");
|
|
48761
|
+
console.log(source_default.green(` Autonomy level set to: ${source_default.white(upperLevel)}`));
|
|
48762
|
+
console.log("");
|
|
48763
|
+
}
|
|
48764
|
+
} catch (error49) {
|
|
48765
|
+
handleAgentError(error49, jsonMode, agentId, "set autonomy");
|
|
48766
|
+
}
|
|
48767
|
+
});
|
|
48768
|
+
agent.command("override").description("Set human override (agent defers all decisions to human)").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).addOption(new Option("--force", "Skip confirmation prompt")).action(async (agentId, options) => {
|
|
48769
|
+
const jsonMode = options.format === "json";
|
|
48770
|
+
if (jsonMode && !options.force) {
|
|
48771
|
+
console.error(JSON.stringify({ error: "--force is required with --format json" }));
|
|
48772
|
+
process.exitCode = 1;
|
|
48773
|
+
return;
|
|
48774
|
+
}
|
|
48775
|
+
if (!options.force) {
|
|
48776
|
+
const proceed = await esm_default4({
|
|
48777
|
+
message: `Are you sure you want to set human override for agent ${agentId}?`
|
|
48778
|
+
});
|
|
48779
|
+
if (!proceed) {
|
|
48780
|
+
console.log(source_default.dim(" Override cancelled."));
|
|
48781
|
+
return;
|
|
48782
|
+
}
|
|
48783
|
+
}
|
|
48784
|
+
try {
|
|
48785
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/override`, { method: "POST" });
|
|
48786
|
+
if (jsonMode) {
|
|
48787
|
+
console.log(JSON.stringify({ agentId, humanOverride: true }));
|
|
48788
|
+
} else {
|
|
48789
|
+
console.log("");
|
|
48790
|
+
console.log(source_default.yellow(` Human override set for agent ${agentId}.`));
|
|
48791
|
+
console.log(source_default.dim(" The agent will defer all decisions to you."));
|
|
48792
|
+
console.log(source_default.dim(" Clear with: trading-boy trader agent clear-override " + agentId));
|
|
48793
|
+
console.log("");
|
|
48794
|
+
}
|
|
48795
|
+
} catch (error49) {
|
|
48796
|
+
handleAgentError(error49, jsonMode, agentId, "set override");
|
|
48797
|
+
}
|
|
48798
|
+
});
|
|
48799
|
+
agent.command("clear-override").description("Clear human override (return control to agent)").argument("<agent-id>", "Agent ID").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
|
|
48800
|
+
const jsonMode = options.format === "json";
|
|
48801
|
+
try {
|
|
48802
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/clear-override`, { method: "POST" });
|
|
48803
|
+
if (jsonMode) {
|
|
48804
|
+
console.log(JSON.stringify({ agentId, humanOverride: false }));
|
|
48805
|
+
} else {
|
|
48806
|
+
console.log("");
|
|
48807
|
+
console.log(source_default.green(` Human override cleared for agent ${agentId}.`));
|
|
48808
|
+
console.log(source_default.dim(" The agent will resume autonomous decision-making."));
|
|
48809
|
+
console.log("");
|
|
48810
|
+
}
|
|
48811
|
+
} catch (error49) {
|
|
48812
|
+
handleAgentError(error49, jsonMode, agentId, "clear override");
|
|
48813
|
+
}
|
|
48814
|
+
});
|
|
48815
|
+
}
|
|
48816
|
+
function formatAgentState(state) {
|
|
48817
|
+
const colors8 = {
|
|
48818
|
+
"IDLE": source_default.dim,
|
|
48819
|
+
"SCANNING": source_default.cyan,
|
|
48820
|
+
"ANALYZING": source_default.blue,
|
|
48821
|
+
"EXECUTING": source_default.yellow,
|
|
48822
|
+
"PAUSED": source_default.yellow,
|
|
48823
|
+
"KILLED": source_default.red
|
|
48824
|
+
};
|
|
48825
|
+
return (colors8[state] ?? source_default.white)(state);
|
|
48826
|
+
}
|
|
48827
|
+
function formatPnl2(pnl) {
|
|
48828
|
+
if (pnl === void 0 || pnl === null)
|
|
48829
|
+
return source_default.dim("n/a");
|
|
48830
|
+
const sign = pnl >= 0 ? "+" : "";
|
|
48831
|
+
const color = pnl >= 0 ? source_default.green : source_default.red;
|
|
48832
|
+
return color(`${sign}$${pnl.toFixed(2)}`);
|
|
48833
|
+
}
|
|
48834
|
+
function handleAgentError(error49, jsonMode, agentId, action) {
|
|
48835
|
+
if (error49 instanceof ApiError && error49.status === 404) {
|
|
48836
|
+
const msg = `Agent not found: "${agentId}"`;
|
|
48837
|
+
if (jsonMode) {
|
|
48838
|
+
console.error(JSON.stringify({ error: msg }));
|
|
48839
|
+
} else {
|
|
48840
|
+
console.error(source_default.yellow(msg));
|
|
48841
|
+
}
|
|
48842
|
+
} else {
|
|
48843
|
+
const message = error49 instanceof Error ? error49.message : String(error49);
|
|
48844
|
+
logger12.error({ error: message }, `Failed to ${action}`);
|
|
48845
|
+
if (jsonMode) {
|
|
48846
|
+
console.error(JSON.stringify({ error: message }));
|
|
48847
|
+
} else {
|
|
48848
|
+
console.error(source_default.red(`Error: ${message}`));
|
|
48849
|
+
}
|
|
48850
|
+
}
|
|
48851
|
+
process.exitCode = error49 instanceof ApiError ? 2 : 1;
|
|
48636
48852
|
}
|
|
48637
48853
|
function parseFloatOption(value) {
|
|
48638
48854
|
return parseFloat(value);
|
|
@@ -49762,7 +49978,7 @@ function registerAuditCommand(program2) {
|
|
|
49762
49978
|
|
|
49763
49979
|
// dist/commands/config-cmd.js
|
|
49764
49980
|
init_source();
|
|
49765
|
-
import { readFileSync as
|
|
49981
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync2 } from "node:fs";
|
|
49766
49982
|
import { resolve } from "node:path";
|
|
49767
49983
|
init_utils();
|
|
49768
49984
|
var logger17 = createLogger("cli-config");
|
|
@@ -49862,7 +50078,7 @@ function parseEnvFile(filePath) {
|
|
|
49862
50078
|
if (!existsSync2(filePath)) {
|
|
49863
50079
|
return { entries, lines };
|
|
49864
50080
|
}
|
|
49865
|
-
const content =
|
|
50081
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
49866
50082
|
const rawLines = content.split("\n");
|
|
49867
50083
|
for (const line of rawLines) {
|
|
49868
50084
|
lines.push(line);
|
|
@@ -49901,11 +50117,11 @@ function writeEnvValue(filePath, key, value) {
|
|
|
49901
50117
|
}
|
|
49902
50118
|
return line;
|
|
49903
50119
|
});
|
|
49904
|
-
|
|
50120
|
+
writeFileSync3(filePath, updatedLines.join("\n"));
|
|
49905
50121
|
} else {
|
|
49906
|
-
const existing = existsSync2(filePath) ?
|
|
50122
|
+
const existing = existsSync2(filePath) ? readFileSync4(filePath, "utf-8") : "";
|
|
49907
50123
|
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
49908
|
-
|
|
50124
|
+
writeFileSync3(filePath, existing + separator + `${key}=${value}
|
|
49909
50125
|
`);
|
|
49910
50126
|
}
|
|
49911
50127
|
}
|
package/dist/commands/trader.js
CHANGED
|
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import { createLogger } from '@trading-boy/core';
|
|
5
5
|
import { padRight } from '../utils.js';
|
|
6
6
|
import { apiRequest, ApiError } from '../api-client.js';
|
|
7
|
+
import { confirm } from '@inquirer/prompts';
|
|
7
8
|
// ─── Logger ───
|
|
8
9
|
const logger = createLogger('cli-trader');
|
|
9
10
|
// ─── Formatters ───
|
|
@@ -634,6 +635,288 @@ export function registerTraderCommand(program) {
|
|
|
634
635
|
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
635
636
|
}
|
|
636
637
|
});
|
|
638
|
+
// ─── agent (subcommand group) ───
|
|
639
|
+
const AUTONOMY_LEVELS = ['OBSERVE_ONLY', 'SUGGEST', 'AUTO_WITH_APPROVAL', 'FULLY_AUTONOMOUS'];
|
|
640
|
+
const agent = trader
|
|
641
|
+
.command('agent')
|
|
642
|
+
.description('Manage autonomous trading agent');
|
|
643
|
+
// ─── agent status ───
|
|
644
|
+
agent
|
|
645
|
+
.command('status')
|
|
646
|
+
.description('Show agent runtime status')
|
|
647
|
+
.argument('<agent-id>', 'Agent ID')
|
|
648
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
649
|
+
.action(async (agentId, options) => {
|
|
650
|
+
const jsonMode = options.format === 'json';
|
|
651
|
+
try {
|
|
652
|
+
const result = await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/status`);
|
|
653
|
+
if (jsonMode) {
|
|
654
|
+
const safeOutput = {
|
|
655
|
+
agentId,
|
|
656
|
+
state: result.state,
|
|
657
|
+
autonomyLevel: result.autonomyLevel,
|
|
658
|
+
paused: result.paused,
|
|
659
|
+
killed: result.killed,
|
|
660
|
+
override: result.humanOverride,
|
|
661
|
+
openPositions: result.openPositions ?? 0,
|
|
662
|
+
dailyTradeCount: result.dailyTradeCount ?? 0,
|
|
663
|
+
dailyPnlUsd: result.dailyPnlUsd ?? null,
|
|
664
|
+
lastTransitionAt: result.lastTransitionAt ?? null,
|
|
665
|
+
healthTimestamp: result.healthTimestamp ?? null,
|
|
666
|
+
};
|
|
667
|
+
console.log(JSON.stringify(safeOutput, null, 2));
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
console.log('');
|
|
671
|
+
console.log(chalk.bold.cyan(' Agent Status'));
|
|
672
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
673
|
+
console.log('');
|
|
674
|
+
console.log(` ${chalk.gray('Agent ID:')} ${chalk.white(agentId)}`);
|
|
675
|
+
console.log(` ${chalk.gray('State:')} ${formatAgentState(result.state)}`);
|
|
676
|
+
console.log(` ${chalk.gray('Autonomy:')} ${chalk.white(result.autonomyLevel)}`);
|
|
677
|
+
console.log(` ${chalk.gray('Paused:')} ${result.paused ? chalk.yellow('yes') : chalk.green('no')}`);
|
|
678
|
+
console.log(` ${chalk.gray('Killed:')} ${result.killed ? chalk.red('yes') : chalk.green('no')}`);
|
|
679
|
+
console.log(` ${chalk.gray('Override:')} ${result.humanOverride ? chalk.yellow('active') : chalk.dim('none')}`);
|
|
680
|
+
console.log(` ${chalk.gray('Open positions:')} ${chalk.white(String(result.openPositions ?? 0))}`);
|
|
681
|
+
console.log(` ${chalk.gray('Trades today:')} ${chalk.white(String(result.dailyTradeCount ?? 0))}`);
|
|
682
|
+
console.log(` ${chalk.gray('Daily PnL:')} ${formatPnl(result.dailyPnlUsd)}`);
|
|
683
|
+
console.log('');
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
catch (error) {
|
|
687
|
+
handleAgentError(error, jsonMode, agentId, 'get agent status');
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
// ─── agent pause ───
|
|
691
|
+
agent
|
|
692
|
+
.command('pause')
|
|
693
|
+
.description('Pause the agent (stops all trading)')
|
|
694
|
+
.argument('<agent-id>', 'Agent ID')
|
|
695
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
696
|
+
.action(async (agentId, options) => {
|
|
697
|
+
const jsonMode = options.format === 'json';
|
|
698
|
+
try {
|
|
699
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/pause`, { method: 'POST' });
|
|
700
|
+
if (jsonMode) {
|
|
701
|
+
console.log(JSON.stringify({ agentId, paused: true }));
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
console.log('');
|
|
705
|
+
console.log(chalk.yellow(` Agent ${agentId} paused.`));
|
|
706
|
+
console.log(chalk.dim(' Resume with: trading-boy trader agent resume ' + agentId));
|
|
707
|
+
console.log('');
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
handleAgentError(error, jsonMode, agentId, 'pause agent');
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
// ─── agent resume ───
|
|
715
|
+
agent
|
|
716
|
+
.command('resume')
|
|
717
|
+
.description('Resume a paused agent')
|
|
718
|
+
.argument('<agent-id>', 'Agent ID')
|
|
719
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
720
|
+
.action(async (agentId, options) => {
|
|
721
|
+
const jsonMode = options.format === 'json';
|
|
722
|
+
try {
|
|
723
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/resume`, { method: 'POST' });
|
|
724
|
+
if (jsonMode) {
|
|
725
|
+
console.log(JSON.stringify({ agentId, resumed: true }));
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
console.log('');
|
|
729
|
+
console.log(chalk.green(` Agent ${agentId} resumed.`));
|
|
730
|
+
console.log('');
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
handleAgentError(error, jsonMode, agentId, 'resume agent');
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
// ─── agent kill ───
|
|
738
|
+
agent
|
|
739
|
+
.command('kill')
|
|
740
|
+
.description('Kill the agent permanently (until revived)')
|
|
741
|
+
.argument('<agent-id>', 'Agent ID')
|
|
742
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
743
|
+
.addOption(new Option('--force', 'Skip confirmation prompt'))
|
|
744
|
+
.action(async (agentId, options) => {
|
|
745
|
+
const jsonMode = options.format === 'json';
|
|
746
|
+
if (jsonMode && !options.force) {
|
|
747
|
+
console.error(JSON.stringify({ error: '--force is required with --format json' }));
|
|
748
|
+
process.exitCode = 1;
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (!options.force) {
|
|
752
|
+
const proceed = await confirm({
|
|
753
|
+
message: `Are you sure you want to kill agent ${agentId}? This is permanent until revived.`,
|
|
754
|
+
});
|
|
755
|
+
if (!proceed) {
|
|
756
|
+
console.log(chalk.dim(' Kill cancelled.'));
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
try {
|
|
761
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/kill`, { method: 'POST' });
|
|
762
|
+
if (jsonMode) {
|
|
763
|
+
console.log(JSON.stringify({ agentId, killed: true }));
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
console.log('');
|
|
767
|
+
console.log(chalk.red(` Agent ${agentId} killed.`));
|
|
768
|
+
console.log(chalk.dim(' The agent will not trade until manually revived.'));
|
|
769
|
+
console.log('');
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
catch (error) {
|
|
773
|
+
handleAgentError(error, jsonMode, agentId, 'kill agent');
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
// ─── agent autonomy ───
|
|
777
|
+
agent
|
|
778
|
+
.command('autonomy')
|
|
779
|
+
.description('Set agent autonomy level')
|
|
780
|
+
.argument('<agent-id>', 'Agent ID')
|
|
781
|
+
.argument('<level>', `Autonomy level: ${AUTONOMY_LEVELS.join(' | ')}`)
|
|
782
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
783
|
+
.action(async (agentId, level, options) => {
|
|
784
|
+
const jsonMode = options.format === 'json';
|
|
785
|
+
const upperLevel = level.toUpperCase();
|
|
786
|
+
if (!AUTONOMY_LEVELS.includes(upperLevel)) {
|
|
787
|
+
const msg = `Invalid autonomy level. Must be one of: ${AUTONOMY_LEVELS.join(', ')}`;
|
|
788
|
+
if (jsonMode) {
|
|
789
|
+
console.error(JSON.stringify({ error: msg }));
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
console.error(chalk.red(` ${msg}`));
|
|
793
|
+
}
|
|
794
|
+
process.exitCode = 1;
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
try {
|
|
798
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/autonomy`, {
|
|
799
|
+
method: 'PUT',
|
|
800
|
+
body: { level: upperLevel },
|
|
801
|
+
});
|
|
802
|
+
if (jsonMode) {
|
|
803
|
+
console.log(JSON.stringify({ agentId, autonomyLevel: upperLevel }));
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
console.log('');
|
|
807
|
+
console.log(chalk.green(` Autonomy level set to: ${chalk.white(upperLevel)}`));
|
|
808
|
+
console.log('');
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
handleAgentError(error, jsonMode, agentId, 'set autonomy');
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
// ─── agent override ───
|
|
816
|
+
agent
|
|
817
|
+
.command('override')
|
|
818
|
+
.description('Set human override (agent defers all decisions to human)')
|
|
819
|
+
.argument('<agent-id>', 'Agent ID')
|
|
820
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
821
|
+
.addOption(new Option('--force', 'Skip confirmation prompt'))
|
|
822
|
+
.action(async (agentId, options) => {
|
|
823
|
+
const jsonMode = options.format === 'json';
|
|
824
|
+
if (jsonMode && !options.force) {
|
|
825
|
+
console.error(JSON.stringify({ error: '--force is required with --format json' }));
|
|
826
|
+
process.exitCode = 1;
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (!options.force) {
|
|
830
|
+
const proceed = await confirm({
|
|
831
|
+
message: `Are you sure you want to set human override for agent ${agentId}?`,
|
|
832
|
+
});
|
|
833
|
+
if (!proceed) {
|
|
834
|
+
console.log(chalk.dim(' Override cancelled.'));
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
try {
|
|
839
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/override`, { method: 'POST' });
|
|
840
|
+
if (jsonMode) {
|
|
841
|
+
console.log(JSON.stringify({ agentId, humanOverride: true }));
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
console.log('');
|
|
845
|
+
console.log(chalk.yellow(` Human override set for agent ${agentId}.`));
|
|
846
|
+
console.log(chalk.dim(' The agent will defer all decisions to you.'));
|
|
847
|
+
console.log(chalk.dim(' Clear with: trading-boy trader agent clear-override ' + agentId));
|
|
848
|
+
console.log('');
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
handleAgentError(error, jsonMode, agentId, 'set override');
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
// ─── agent clear-override ───
|
|
856
|
+
agent
|
|
857
|
+
.command('clear-override')
|
|
858
|
+
.description('Clear human override (return control to agent)')
|
|
859
|
+
.argument('<agent-id>', 'Agent ID')
|
|
860
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
861
|
+
.action(async (agentId, options) => {
|
|
862
|
+
const jsonMode = options.format === 'json';
|
|
863
|
+
try {
|
|
864
|
+
await apiRequest(`/api/v1/agents/${encodeURIComponent(agentId)}/clear-override`, { method: 'POST' });
|
|
865
|
+
if (jsonMode) {
|
|
866
|
+
console.log(JSON.stringify({ agentId, humanOverride: false }));
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
console.log('');
|
|
870
|
+
console.log(chalk.green(` Human override cleared for agent ${agentId}.`));
|
|
871
|
+
console.log(chalk.dim(' The agent will resume autonomous decision-making.'));
|
|
872
|
+
console.log('');
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
handleAgentError(error, jsonMode, agentId, 'clear override');
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
// ─── Agent Helpers ───
|
|
881
|
+
function formatAgentState(state) {
|
|
882
|
+
const colors = {
|
|
883
|
+
'IDLE': chalk.dim,
|
|
884
|
+
'SCANNING': chalk.cyan,
|
|
885
|
+
'ANALYZING': chalk.blue,
|
|
886
|
+
'EXECUTING': chalk.yellow,
|
|
887
|
+
'PAUSED': chalk.yellow,
|
|
888
|
+
'KILLED': chalk.red,
|
|
889
|
+
};
|
|
890
|
+
return (colors[state] ?? chalk.white)(state);
|
|
891
|
+
}
|
|
892
|
+
function formatPnl(pnl) {
|
|
893
|
+
if (pnl === undefined || pnl === null)
|
|
894
|
+
return chalk.dim('n/a');
|
|
895
|
+
const sign = pnl >= 0 ? '+' : '';
|
|
896
|
+
const color = pnl >= 0 ? chalk.green : chalk.red;
|
|
897
|
+
return color(`${sign}$${pnl.toFixed(2)}`);
|
|
898
|
+
}
|
|
899
|
+
function handleAgentError(error, jsonMode, agentId, action) {
|
|
900
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
901
|
+
const msg = `Agent not found: "${agentId}"`;
|
|
902
|
+
if (jsonMode) {
|
|
903
|
+
console.error(JSON.stringify({ error: msg }));
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
console.error(chalk.yellow(msg));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
911
|
+
logger.error({ error: message }, `Failed to ${action}`);
|
|
912
|
+
if (jsonMode) {
|
|
913
|
+
console.error(JSON.stringify({ error: message }));
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
console.error(chalk.red(`Error: ${message}`));
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
637
920
|
}
|
|
638
921
|
// ─── Helpers ───
|
|
639
922
|
function parseFloatOption(value) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trading-boy/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.19",
|
|
4
4
|
"description": "Trading Boy CLI — crypto context intelligence for traders and AI agents. Query real-time prices, funding rates, whale activity, and DeFi risk for 100+ Solana tokens and 229 Hyperliquid perpetuals.",
|
|
5
5
|
"homepage": "https://cabal.ventures",
|
|
6
6
|
"repository": {
|