@sherwoodagent/cli 0.42.0 → 0.47.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.
- package/dist/{addresses-DCQMJJFI.js → addresses-DOWLGXGE.js} +4 -4
- package/dist/{agent-5YEJ6W6W.js → agent-4HKHLQEJ.js} +3035 -2029
- package/dist/agent-4HKHLQEJ.js.map +1 -0
- package/dist/autoresearch-IA7HGSNW.js +267 -0
- package/dist/autoresearch-IA7HGSNW.js.map +1 -0
- package/dist/{chat-UWJ6JBFE.js → chat-MRTPUZ3X.js} +8 -8
- package/dist/{chunk-FOOZC4AM.js → chunk-2LRC7I33.js} +2 -2
- package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
- package/dist/chunk-2W4KGGPU.js.map +1 -0
- package/dist/chunk-7HJXZ4NM.js +189 -0
- package/dist/chunk-7HJXZ4NM.js.map +1 -0
- package/dist/{chunk-JNDEZFXW.js → chunk-E6LH2FJ6.js} +4 -4
- package/dist/{chunk-XAUMJZU2.js → chunk-FWC57PT7.js} +2 -2
- package/dist/{chunk-4OAZ7TP5.js → chunk-IS2CLLUT.js} +4 -4
- package/dist/{chunk-T2IA6CCW.js → chunk-KJ35EF7N.js} +4 -4
- package/dist/{chunk-GMVDHSCF.js → chunk-OHQOTSFG.js} +39 -3
- package/dist/chunk-OHQOTSFG.js.map +1 -0
- package/dist/chunk-R2P4C2I7.js +560 -0
- package/dist/chunk-R2P4C2I7.js.map +1 -0
- package/dist/{chunk-5Q3VT6NQ.js → chunk-R7X33D2S.js} +5 -5
- package/dist/{chunk-6U3A6ZJ2.js → chunk-WMZEOR5W.js} +6 -127
- package/dist/chunk-WMZEOR5W.js.map +1 -0
- package/dist/{chunk-SWKHSKDH.js → chunk-XDBRKW7I.js} +3 -3
- package/dist/chunk-YTYXBX7B.js +495 -0
- package/dist/chunk-YTYXBX7B.js.map +1 -0
- package/dist/{client-DRVUILER.js → client-UHHCB6EZ.js} +4 -4
- package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
- package/dist/{eas-KBU2BAOD.js → eas-PEEWGSAO.js} +6 -6
- package/dist/{governor-KH7RNH2E.js → governor-MG5VVSU4.js} +6 -6
- package/dist/index.js +1012 -265
- package/dist/index.js.map +1 -1
- package/dist/{network-3HB46JMQ.js → network-3ZU7UBDU.js} +3 -3
- package/dist/{price-TTQWI672.js → price-WCCXWPDM.js} +5 -5
- package/dist/{research-J3HYU3N6.js → research-QBKISW2M.js} +9 -9
- package/dist/research-YJWSQS2A.js +14 -0
- package/dist/serve-6MYBOAX6.js +222 -0
- package/dist/serve-6MYBOAX6.js.map +1 -0
- package/dist/{session-NUPHC6ZN.js → session-FPCPCVQX.js} +10 -10
- package/dist/summary-formatter-JIGWN4QS.js +180 -0
- package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
- package/dist/{trade-VGECC4QZ.js → trade-NO6CNWCA.js} +19 -17
- package/dist/{trade-VGECC4QZ.js.map → trade-NO6CNWCA.js.map} +1 -1
- package/dist/{xmtp-JO7W6RFK.js → xmtp-ACB2W573.js} +6 -6
- package/package.json +5 -2
- package/dist/agent-5YEJ6W6W.js.map +0 -1
- package/dist/chunk-4EHI4SKM.js.map +0 -1
- package/dist/chunk-6U3A6ZJ2.js.map +0 -1
- package/dist/chunk-GMVDHSCF.js.map +0 -1
- package/dist/research-NR4VC5LN.js +0 -14
- /package/dist/{addresses-DCQMJJFI.js.map → addresses-DOWLGXGE.js.map} +0 -0
- /package/dist/{chat-UWJ6JBFE.js.map → chat-MRTPUZ3X.js.map} +0 -0
- /package/dist/{chunk-FOOZC4AM.js.map → chunk-2LRC7I33.js.map} +0 -0
- /package/dist/{chunk-JNDEZFXW.js.map → chunk-E6LH2FJ6.js.map} +0 -0
- /package/dist/{chunk-XAUMJZU2.js.map → chunk-FWC57PT7.js.map} +0 -0
- /package/dist/{chunk-4OAZ7TP5.js.map → chunk-IS2CLLUT.js.map} +0 -0
- /package/dist/{chunk-T2IA6CCW.js.map → chunk-KJ35EF7N.js.map} +0 -0
- /package/dist/{chunk-5Q3VT6NQ.js.map → chunk-R7X33D2S.js.map} +0 -0
- /package/dist/{chunk-SWKHSKDH.js.map → chunk-XDBRKW7I.js.map} +0 -0
- /package/dist/{client-DRVUILER.js.map → client-UHHCB6EZ.js.map} +0 -0
- /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
- /package/dist/{eas-KBU2BAOD.js.map → eas-PEEWGSAO.js.map} +0 -0
- /package/dist/{governor-KH7RNH2E.js.map → governor-MG5VVSU4.js.map} +0 -0
- /package/dist/{network-3HB46JMQ.js.map → network-3ZU7UBDU.js.map} +0 -0
- /package/dist/{price-TTQWI672.js.map → price-WCCXWPDM.js.map} +0 -0
- /package/dist/{research-J3HYU3N6.js.map → research-QBKISW2M.js.map} +0 -0
- /package/dist/{research-NR4VC5LN.js.map → research-YJWSQS2A.js.map} +0 -0
- /package/dist/{session-NUPHC6ZN.js.map → session-FPCPCVQX.js.map} +0 -0
- /package/dist/{xmtp-JO7W6RFK.js.map → xmtp-ACB2W573.js.map} +0 -0
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
robinhoodTestnet,
|
|
14
14
|
setNetwork,
|
|
15
15
|
withNetwork
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-2LRC7I33.js";
|
|
17
|
+
import "./chunk-2W4KGGPU.js";
|
|
18
18
|
export {
|
|
19
19
|
CHAIN_REGISTRY,
|
|
20
20
|
VALID_NETWORKS,
|
|
@@ -31,4 +31,4 @@ export {
|
|
|
31
31
|
setNetwork,
|
|
32
32
|
withNetwork
|
|
33
33
|
};
|
|
34
|
-
//# sourceMappingURL=network-
|
|
34
|
+
//# sourceMappingURL=network-3ZU7UBDU.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SYNTHRA,
|
|
3
3
|
UNISWAP
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FWC57PT7.js";
|
|
5
5
|
import {
|
|
6
6
|
getPublicClient
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XDBRKW7I.js";
|
|
8
8
|
import {
|
|
9
9
|
getNetwork
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-2LRC7I33.js";
|
|
11
|
+
import "./chunk-2W4KGGPU.js";
|
|
12
12
|
import {
|
|
13
13
|
UNISWAP_QUOTER_V2_ABI
|
|
14
14
|
} from "./chunk-D77JGXIV.js";
|
|
@@ -155,4 +155,4 @@ export {
|
|
|
155
155
|
getTokenPriceInAsset,
|
|
156
156
|
getTokenPricesInAsset
|
|
157
157
|
};
|
|
158
|
-
//# sourceMappingURL=price-
|
|
158
|
+
//# sourceMappingURL=price-WCCXWPDM.js.map
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
MESSARI_COST_ESTIMATE,
|
|
3
3
|
NANSEN_COST_ESTIMATE,
|
|
4
4
|
getResearchProvider
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OHQOTSFG.js";
|
|
6
6
|
import {
|
|
7
7
|
formatContractError,
|
|
8
8
|
getAccount
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-XDBRKW7I.js";
|
|
10
|
+
import "./chunk-2LRC7I33.js";
|
|
11
|
+
import "./chunk-2W4KGGPU.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/research.ts
|
|
14
14
|
import chalk from "chalk";
|
|
@@ -16,7 +16,7 @@ import ora from "ora";
|
|
|
16
16
|
import { confirm } from "@inquirer/prompts";
|
|
17
17
|
import { isAddress } from "viem";
|
|
18
18
|
async function loadXmtp() {
|
|
19
|
-
return import("./xmtp-
|
|
19
|
+
return import("./xmtp-ACB2W573.js");
|
|
20
20
|
}
|
|
21
21
|
var COST_ESTIMATES = {
|
|
22
22
|
messari: MESSARI_COST_ESTIMATE,
|
|
@@ -83,7 +83,7 @@ function formatValue(value) {
|
|
|
83
83
|
}
|
|
84
84
|
async function postResearch(syndicateName, result, prompt) {
|
|
85
85
|
const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
|
|
86
|
-
const { createResearchAttestation, getEasScanUrl } = await import("./eas-
|
|
86
|
+
const { createResearchAttestation, getEasScanUrl } = await import("./eas-PEEWGSAO.js");
|
|
87
87
|
const xmtp = await loadXmtp();
|
|
88
88
|
const pinSpinner = ora("Pinning research result to IPFS...").start();
|
|
89
89
|
let resultUri;
|
|
@@ -112,8 +112,8 @@ async function postResearch(syndicateName, result, prompt) {
|
|
|
112
112
|
const easSpinner = ora("Creating EAS attestation...").start();
|
|
113
113
|
let attestationUid;
|
|
114
114
|
try {
|
|
115
|
-
const { getChainContracts } = await import("./config-
|
|
116
|
-
const { getChain } = await import("./network-
|
|
115
|
+
const { getChainContracts } = await import("./config-REASKDIK.js");
|
|
116
|
+
const { getChain } = await import("./network-3ZU7UBDU.js");
|
|
117
117
|
const vaultRecipient = getChainContracts(getChain().id).vault;
|
|
118
118
|
const { uid } = await createResearchAttestation(
|
|
119
119
|
result.provider,
|
|
@@ -312,4 +312,4 @@ function registerResearchCommands(program) {
|
|
|
312
312
|
export {
|
|
313
313
|
registerResearchCommands
|
|
314
314
|
};
|
|
315
|
-
//# sourceMappingURL=research-
|
|
315
|
+
//# sourceMappingURL=research-QBKISW2M.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MessariProvider,
|
|
3
|
+
NansenProvider,
|
|
4
|
+
getResearchProvider
|
|
5
|
+
} from "./chunk-OHQOTSFG.js";
|
|
6
|
+
import "./chunk-XDBRKW7I.js";
|
|
7
|
+
import "./chunk-2LRC7I33.js";
|
|
8
|
+
import "./chunk-2W4KGGPU.js";
|
|
9
|
+
export {
|
|
10
|
+
MessariProvider,
|
|
11
|
+
NansenProvider,
|
|
12
|
+
getResearchProvider
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=research-YJWSQS2A.js.map
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// src/agent/serve.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
import { readFile, stat } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { watchFile, unwatchFile } from "fs";
|
|
7
|
+
var AGENT_DIR = join(homedir(), ".sherwood", "agent");
|
|
8
|
+
async function readJsonFile(path) {
|
|
9
|
+
try {
|
|
10
|
+
const data = await readFile(path, "utf-8");
|
|
11
|
+
return JSON.parse(data);
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function readJsonlTail(path, count) {
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = await readFile(path, "utf-8");
|
|
20
|
+
} catch {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
24
|
+
const tail = lines.slice(-count);
|
|
25
|
+
const results = [];
|
|
26
|
+
for (const line of tail) {
|
|
27
|
+
try {
|
|
28
|
+
results.push(JSON.parse(line));
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
async function handleRequest(req, res, token) {
|
|
35
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
36
|
+
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
|
|
37
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
38
|
+
if (req.method === "OPTIONS") {
|
|
39
|
+
res.writeHead(204);
|
|
40
|
+
res.end();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (token) {
|
|
44
|
+
const auth = req.headers.authorization;
|
|
45
|
+
if (!auth || auth !== `Bearer ${token}`) {
|
|
46
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
47
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
52
|
+
const path = url.pathname;
|
|
53
|
+
const json = (data, status = 200) => {
|
|
54
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
55
|
+
res.end(JSON.stringify(data));
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
if (path === "/api/portfolio") {
|
|
59
|
+
const data = await readJsonFile(join(AGENT_DIR, "portfolio.json"));
|
|
60
|
+
json(data ?? { error: "No portfolio data" });
|
|
61
|
+
} else if (path === "/api/trades") {
|
|
62
|
+
const days = parseInt(url.searchParams.get("days") ?? "0", 10);
|
|
63
|
+
const trades = await readJsonFile(join(AGENT_DIR, "trades.json")) ?? [];
|
|
64
|
+
if (days > 0) {
|
|
65
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
66
|
+
json(trades.filter((t) => t.exitTimestamp >= cutoff));
|
|
67
|
+
} else {
|
|
68
|
+
json(trades);
|
|
69
|
+
}
|
|
70
|
+
} else if (path === "/api/cycles") {
|
|
71
|
+
const last = parseInt(url.searchParams.get("last") ?? "100", 10);
|
|
72
|
+
json(await readJsonlTail(join(AGENT_DIR, "cycles.jsonl"), last));
|
|
73
|
+
} else if (path === "/api/signals") {
|
|
74
|
+
const last = parseInt(url.searchParams.get("last") ?? "50", 10);
|
|
75
|
+
json(await readJsonlTail(join(AGENT_DIR, "signal-history.jsonl"), last));
|
|
76
|
+
} else if (path === "/api/grid") {
|
|
77
|
+
const data = await readJsonFile(join(AGENT_DIR, "grid-portfolio.json"));
|
|
78
|
+
json(data ?? { error: "No grid data" });
|
|
79
|
+
} else if (path === "/api/health") {
|
|
80
|
+
const cycles = await readJsonlTail(join(AGENT_DIR, "cycles.jsonl"), 1);
|
|
81
|
+
const lastCycle = cycles[0] ?? null;
|
|
82
|
+
const errors = lastCycle?.errors ?? [];
|
|
83
|
+
json({
|
|
84
|
+
agentDir: AGENT_DIR,
|
|
85
|
+
lastCycleTimestamp: lastCycle?.timestamp ?? null,
|
|
86
|
+
lastCycleNumber: lastCycle?.cycleNumber ?? null,
|
|
87
|
+
lastCycleErrors: errors.length,
|
|
88
|
+
recentErrors: errors
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
json({ error: "Not found" }, 404);
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
json({ error: err.message }, 500);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var OPEN = 1;
|
|
98
|
+
function broadcast(clients, msg) {
|
|
99
|
+
const payload = JSON.stringify(msg);
|
|
100
|
+
for (const client of clients) {
|
|
101
|
+
if (client.readyState === OPEN) {
|
|
102
|
+
try {
|
|
103
|
+
client.send(payload);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function startFileWatchers(clients) {
|
|
110
|
+
const DEBOUNCE_MS = 2e3;
|
|
111
|
+
const watchers = [
|
|
112
|
+
{
|
|
113
|
+
path: join(AGENT_DIR, "portfolio.json"),
|
|
114
|
+
type: "portfolio",
|
|
115
|
+
reader: () => readJsonFile(join(AGENT_DIR, "portfolio.json")),
|
|
116
|
+
lastSize: 0,
|
|
117
|
+
debounceTimer: null
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
path: join(AGENT_DIR, "cycles.jsonl"),
|
|
121
|
+
type: "cycle",
|
|
122
|
+
reader: async () => {
|
|
123
|
+
const c = await readJsonlTail(join(AGENT_DIR, "cycles.jsonl"), 1);
|
|
124
|
+
return c[0] ?? null;
|
|
125
|
+
},
|
|
126
|
+
lastSize: 0,
|
|
127
|
+
debounceTimer: null
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
path: join(AGENT_DIR, "trades.json"),
|
|
131
|
+
type: "trade",
|
|
132
|
+
reader: async () => {
|
|
133
|
+
const t = await readJsonFile(join(AGENT_DIR, "trades.json")) ?? [];
|
|
134
|
+
return t[t.length - 1] ?? null;
|
|
135
|
+
},
|
|
136
|
+
lastSize: 0,
|
|
137
|
+
debounceTimer: null
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
path: join(AGENT_DIR, "grid-portfolio.json"),
|
|
141
|
+
type: "grid",
|
|
142
|
+
reader: () => readJsonFile(join(AGENT_DIR, "grid-portfolio.json")),
|
|
143
|
+
lastSize: 0,
|
|
144
|
+
debounceTimer: null
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
for (const w of watchers) {
|
|
148
|
+
stat(w.path).then((s) => {
|
|
149
|
+
w.lastSize = s.size;
|
|
150
|
+
}).catch(() => {
|
|
151
|
+
});
|
|
152
|
+
watchFile(w.path, { interval: 2e3 }, async (curr, prev) => {
|
|
153
|
+
if (curr.mtimeMs === prev.mtimeMs) return;
|
|
154
|
+
if (w.debounceTimer) clearTimeout(w.debounceTimer);
|
|
155
|
+
w.debounceTimer = setTimeout(async () => {
|
|
156
|
+
const data = await w.reader();
|
|
157
|
+
if (data !== null) {
|
|
158
|
+
broadcast(clients, { type: w.type, data });
|
|
159
|
+
if (w.type === "cycle" && data.errors?.length > 0) {
|
|
160
|
+
for (const errMsg of data.errors) {
|
|
161
|
+
broadcast(clients, { type: "error", data: { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message: errMsg } });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}, DEBOUNCE_MS);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return () => {
|
|
169
|
+
for (const w of watchers) {
|
|
170
|
+
unwatchFile(w.path);
|
|
171
|
+
if (w.debounceTimer) clearTimeout(w.debounceTimer);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function startServe(options) {
|
|
176
|
+
const { port, host, token } = options;
|
|
177
|
+
const { WebSocketServer } = await import("ws");
|
|
178
|
+
const server = createServer((req, res) => handleRequest(req, res, token));
|
|
179
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
180
|
+
const clients = /* @__PURE__ */ new Set();
|
|
181
|
+
server.on("upgrade", (req, socket, head) => {
|
|
182
|
+
if (token) {
|
|
183
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
184
|
+
if (url.searchParams.get("token") !== token) {
|
|
185
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
186
|
+
socket.destroy();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
191
|
+
wss.emit("connection", ws, req);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
wss.on("connection", (ws) => {
|
|
195
|
+
clients.add(ws);
|
|
196
|
+
ws.on("close", () => clients.delete(ws));
|
|
197
|
+
ws.on("error", () => clients.delete(ws));
|
|
198
|
+
});
|
|
199
|
+
const stopWatchers = startFileWatchers(clients);
|
|
200
|
+
server.listen(port, host, () => {
|
|
201
|
+
console.log(`Sherwood dashboard API running on http://${host}:${port}`);
|
|
202
|
+
console.log(` REST: http://${host}:${port}/api/health`);
|
|
203
|
+
console.log(` WebSocket: ws://${host}:${port}/ws`);
|
|
204
|
+
if (token) console.log(` Auth: Bearer token required`);
|
|
205
|
+
else console.log(` Auth: None (localhost only)`);
|
|
206
|
+
});
|
|
207
|
+
const shutdown = () => {
|
|
208
|
+
console.log("\nShutting down dashboard API...");
|
|
209
|
+
stopWatchers();
|
|
210
|
+
wss.close();
|
|
211
|
+
server.close();
|
|
212
|
+
process.exit(0);
|
|
213
|
+
};
|
|
214
|
+
process.on("SIGINT", shutdown);
|
|
215
|
+
process.on("SIGTERM", shutdown);
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
readJsonFile,
|
|
219
|
+
readJsonlTail,
|
|
220
|
+
startServe
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=serve-6MYBOAX6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent/serve.ts"],"sourcesContent":["/**\n * Dashboard API server — serves agent state over REST + WebSocket.\n * Started via `sherwood agent serve`.\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { watchFile, unwatchFile } from \"node:fs\";\n\nconst AGENT_DIR = join(homedir(), \".sherwood\", \"agent\");\n\n// ── File readers ──\n\nexport async function readJsonFile(path: string): Promise<any | null> {\n try {\n const data = await readFile(path, \"utf-8\");\n return JSON.parse(data);\n } catch {\n return null;\n }\n}\n\nexport async function readJsonlTail(path: string, count: number): Promise<any[]> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf-8\");\n } catch {\n return [];\n }\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n const tail = lines.slice(-count);\n const results: any[] = [];\n for (const line of tail) {\n try {\n results.push(JSON.parse(line));\n } catch {\n // skip malformed\n }\n }\n return results;\n}\n\n// ── REST route handler ──\n\nasync function handleRequest(req: IncomingMessage, res: ServerResponse, token?: string): Promise<void> {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Authorization, Content-Type\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\");\n if (req.method === \"OPTIONS\") { res.writeHead(204); res.end(); return; }\n\n if (token) {\n const auth = req.headers.authorization;\n if (!auth || auth !== `Bearer ${token}`) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n return;\n }\n }\n\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host}`);\n const path = url.pathname;\n const json = (data: any, status = 200) => {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n };\n\n try {\n if (path === \"/api/portfolio\") {\n const data = await readJsonFile(join(AGENT_DIR, \"portfolio.json\"));\n json(data ?? { error: \"No portfolio data\" });\n } else if (path === \"/api/trades\") {\n const days = parseInt(url.searchParams.get(\"days\") ?? \"0\", 10);\n const trades = (await readJsonFile(join(AGENT_DIR, \"trades.json\"))) as any[] ?? [];\n if (days > 0) {\n const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n json(trades.filter((t: any) => t.exitTimestamp >= cutoff));\n } else {\n json(trades);\n }\n } else if (path === \"/api/cycles\") {\n const last = parseInt(url.searchParams.get(\"last\") ?? \"100\", 10);\n json(await readJsonlTail(join(AGENT_DIR, \"cycles.jsonl\"), last));\n } else if (path === \"/api/signals\") {\n const last = parseInt(url.searchParams.get(\"last\") ?? \"50\", 10);\n json(await readJsonlTail(join(AGENT_DIR, \"signal-history.jsonl\"), last));\n } else if (path === \"/api/grid\") {\n const data = await readJsonFile(join(AGENT_DIR, \"grid-portfolio.json\"));\n json(data ?? { error: \"No grid data\" });\n } else if (path === \"/api/health\") {\n const cycles = await readJsonlTail(join(AGENT_DIR, \"cycles.jsonl\"), 1);\n const lastCycle = cycles[0] ?? null;\n const errors = lastCycle?.errors ?? [];\n json({\n agentDir: AGENT_DIR,\n lastCycleTimestamp: lastCycle?.timestamp ?? null,\n lastCycleNumber: lastCycle?.cycleNumber ?? null,\n lastCycleErrors: errors.length,\n recentErrors: errors,\n });\n } else {\n json({ error: \"Not found\" }, 404);\n }\n } catch (err) {\n json({ error: (err as Error).message }, 500);\n }\n}\n\n// ── WebSocket relay ──\n\ninterface WsClient { send: (data: string) => void; readyState: number; }\nconst OPEN = 1;\n\nfunction broadcast(clients: Set<WsClient>, msg: object): void {\n const payload = JSON.stringify(msg);\n for (const client of clients) {\n if (client.readyState === OPEN) {\n try { client.send(payload); } catch { /* client gone */ }\n }\n }\n}\n\n// ── File watcher with debounce ──\n\ntype FileWatcherEntry = {\n path: string; type: string; reader: () => Promise<any>;\n lastSize: number; debounceTimer: ReturnType<typeof setTimeout> | null;\n};\n\nfunction startFileWatchers(clients: Set<WsClient>): () => void {\n const DEBOUNCE_MS = 2000;\n const watchers: FileWatcherEntry[] = [\n { path: join(AGENT_DIR, \"portfolio.json\"), type: \"portfolio\",\n reader: () => readJsonFile(join(AGENT_DIR, \"portfolio.json\")), lastSize: 0, debounceTimer: null },\n { path: join(AGENT_DIR, \"cycles.jsonl\"), type: \"cycle\",\n reader: async () => { const c = await readJsonlTail(join(AGENT_DIR, \"cycles.jsonl\"), 1); return c[0] ?? null; },\n lastSize: 0, debounceTimer: null },\n { path: join(AGENT_DIR, \"trades.json\"), type: \"trade\",\n reader: async () => { const t = (await readJsonFile(join(AGENT_DIR, \"trades.json\"))) as any[] ?? []; return t[t.length - 1] ?? null; },\n lastSize: 0, debounceTimer: null },\n { path: join(AGENT_DIR, \"grid-portfolio.json\"), type: \"grid\",\n reader: () => readJsonFile(join(AGENT_DIR, \"grid-portfolio.json\")), lastSize: 0, debounceTimer: null },\n ];\n\n for (const w of watchers) {\n stat(w.path).then((s) => { w.lastSize = s.size; }).catch(() => {});\n watchFile(w.path, { interval: 2000 }, async (curr, prev) => {\n if (curr.mtimeMs === prev.mtimeMs) return;\n if (w.debounceTimer) clearTimeout(w.debounceTimer);\n w.debounceTimer = setTimeout(async () => {\n const data = await w.reader();\n if (data !== null) {\n broadcast(clients, { type: w.type, data });\n if (w.type === \"cycle\" && data.errors?.length > 0) {\n for (const errMsg of data.errors) {\n broadcast(clients, { type: \"error\", data: { timestamp: new Date().toISOString(), message: errMsg } });\n }\n }\n }\n }, DEBOUNCE_MS);\n });\n }\n return () => { for (const w of watchers) { unwatchFile(w.path); if (w.debounceTimer) clearTimeout(w.debounceTimer); } };\n}\n\n// ── Public API ──\n\nexport interface ServeOptions { port: number; host: string; token?: string; }\n\nexport async function startServe(options: ServeOptions): Promise<void> {\n const { port, host, token } = options;\n const { WebSocketServer } = await import(\"ws\");\n const server = createServer((req, res) => handleRequest(req, res, token));\n const wss = new WebSocketServer({ noServer: true });\n const clients = new Set<WsClient>();\n\n server.on(\"upgrade\", (req, socket, head) => {\n if (token) {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host}`);\n if (url.searchParams.get(\"token\") !== token) {\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n wss.handleUpgrade(req, socket, head, (ws) => { wss.emit(\"connection\", ws, req); });\n });\n\n wss.on(\"connection\", (ws) => {\n clients.add(ws as unknown as WsClient);\n ws.on(\"close\", () => clients.delete(ws as unknown as WsClient));\n ws.on(\"error\", () => clients.delete(ws as unknown as WsClient));\n });\n\n const stopWatchers = startFileWatchers(clients);\n server.listen(port, host, () => {\n console.log(`Sherwood dashboard API running on http://${host}:${port}`);\n console.log(` REST: http://${host}:${port}/api/health`);\n console.log(` WebSocket: ws://${host}:${port}/ws`);\n if (token) console.log(` Auth: Bearer token required`);\n else console.log(` Auth: None (localhost only)`);\n });\n\n const shutdown = () => { console.log(\"\\nShutting down dashboard API...\"); stopWatchers(); wss.close(); server.close(); process.exit(0); };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -6,21 +6,21 @@ import {
|
|
|
6
6
|
} from "./chunk-W2ZUPR3W.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveSyndicate
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-R7X33D2S.js";
|
|
10
10
|
import {
|
|
11
11
|
PROPOSAL_STATES,
|
|
12
12
|
getProposal
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KJ35EF7N.js";
|
|
14
14
|
import {
|
|
15
15
|
SHERWOOD
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-FWC57PT7.js";
|
|
17
17
|
import {
|
|
18
18
|
getPublicClient
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-XDBRKW7I.js";
|
|
20
|
+
import "./chunk-2LRC7I33.js";
|
|
21
21
|
import {
|
|
22
22
|
loadConfig
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-2W4KGGPU.js";
|
|
24
24
|
import {
|
|
25
25
|
SYNDICATE_VAULT_ABI
|
|
26
26
|
} from "./chunk-D77JGXIV.js";
|
|
@@ -187,7 +187,7 @@ async function enrichProposalEvents(events, metadataCache) {
|
|
|
187
187
|
|
|
188
188
|
// src/commands/session.ts
|
|
189
189
|
async function loadXmtp() {
|
|
190
|
-
return import("./xmtp-
|
|
190
|
+
return import("./xmtp-ACB2W573.js");
|
|
191
191
|
}
|
|
192
192
|
async function resolveGovernor(vaultAddress) {
|
|
193
193
|
const client = getPublicClient();
|
|
@@ -435,8 +435,8 @@ function registerSessionCommands(program) {
|
|
|
435
435
|
);
|
|
436
436
|
session.command("cron <name>").description("Manage participation crons (OpenClaw agents)").option("--remove", "Remove participation crons", false).option("--status", "Show cron status", false).action(async (name, opts) => {
|
|
437
437
|
const { isOpenClaw, registerSyndicateCrons, unregisterSyndicateCrons, getSyndicateCronStatus } = await import("./cron-EOULYO3R.js");
|
|
438
|
-
const { isTestnet } = await import("./network-
|
|
439
|
-
const { getNotifyTo } = await import("./config-
|
|
438
|
+
const { isTestnet } = await import("./network-3ZU7UBDU.js");
|
|
439
|
+
const { getNotifyTo } = await import("./config-REASKDIK.js");
|
|
440
440
|
if (!isOpenClaw()) {
|
|
441
441
|
console.log(chalk.yellow("Not running on OpenClaw \u2014 cron commands unavailable"));
|
|
442
442
|
console.log(chalk.dim(` Set up your own scheduler: sherwood session check ${name} --stream`));
|
|
@@ -477,4 +477,4 @@ function registerSessionCommands(program) {
|
|
|
477
477
|
export {
|
|
478
478
|
registerSessionCommands
|
|
479
479
|
};
|
|
480
|
-
//# sourceMappingURL=session-
|
|
480
|
+
//# sourceMappingURL=session-FPCPCVQX.js.map
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// src/agent/summary-formatter.ts
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
var TICKER = {
|
|
6
|
+
bitcoin: "BTC",
|
|
7
|
+
ethereum: "ETH",
|
|
8
|
+
solana: "SOL",
|
|
9
|
+
hyperliquid: "HYPE",
|
|
10
|
+
ethena: "ENA",
|
|
11
|
+
aave: "AAVE",
|
|
12
|
+
dogecoin: "DOGE",
|
|
13
|
+
near: "NEAR",
|
|
14
|
+
ripple: "XRP",
|
|
15
|
+
sui: "SUI",
|
|
16
|
+
fartcoin: "FARTCOIN",
|
|
17
|
+
bittensor: "TAO",
|
|
18
|
+
zcash: "ZEC",
|
|
19
|
+
arbitrum: "ARB",
|
|
20
|
+
"avalanche-2": "AVAX",
|
|
21
|
+
chainlink: "LINK",
|
|
22
|
+
"worldcoin-wld": "WLD",
|
|
23
|
+
"pudgy-penguins": "PENGU",
|
|
24
|
+
binancecoin: "BNB",
|
|
25
|
+
blur: "BLUR",
|
|
26
|
+
"fetch-ai": "FET",
|
|
27
|
+
cardano: "ADA"
|
|
28
|
+
};
|
|
29
|
+
function ticker(cgId) {
|
|
30
|
+
return TICKER[cgId] ?? cgId.slice(0, 6).toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
function fmtUsd(v) {
|
|
33
|
+
const sign = v >= 0 ? "+" : "-";
|
|
34
|
+
return `${sign}$${Math.abs(v).toFixed(2)}`;
|
|
35
|
+
}
|
|
36
|
+
function fmtPct(v) {
|
|
37
|
+
return `${v >= 0 ? "+" : ""}${(v * 100).toFixed(2)}%`;
|
|
38
|
+
}
|
|
39
|
+
function fmtPrice(p) {
|
|
40
|
+
return p >= 1 ? `$${p.toFixed(2)}` : `$${p.toFixed(4)}`;
|
|
41
|
+
}
|
|
42
|
+
function arrow(action) {
|
|
43
|
+
if (action.includes("BUY")) return "\u25B2";
|
|
44
|
+
if (action.includes("SELL")) return "\u25BC";
|
|
45
|
+
return "\u2014\u2014";
|
|
46
|
+
}
|
|
47
|
+
function formatSummary(input) {
|
|
48
|
+
const { cycle, portfolio } = input;
|
|
49
|
+
const lines = [];
|
|
50
|
+
const hasEntries = cycle.tradesExecuted > 0;
|
|
51
|
+
const hasExits = cycle.exitsProcessed > 0;
|
|
52
|
+
if (hasEntries) {
|
|
53
|
+
lines.push("\u{1F916} SHERWOOD \u2014 Trade Executed");
|
|
54
|
+
} else if (hasExits) {
|
|
55
|
+
lines.push("\u{1F916} SHERWOOD \u2014 Position Closed");
|
|
56
|
+
} else {
|
|
57
|
+
lines.push("\u{1F916} SHERWOOD \u2014 Scan Complete");
|
|
58
|
+
}
|
|
59
|
+
lines.push("\u2501".repeat(28));
|
|
60
|
+
if (hasEntries) {
|
|
61
|
+
for (const pos of portfolio.positions) {
|
|
62
|
+
const age = Date.now() - (pos.entryTimestamp ?? 0);
|
|
63
|
+
if (age > 12e4) continue;
|
|
64
|
+
const side = (pos.side ?? "long").toUpperCase();
|
|
65
|
+
const stopPct = Math.abs(pos.entryPrice - pos.stopLoss) / pos.entryPrice;
|
|
66
|
+
const tpPct = Math.abs(pos.takeProfit - pos.entryPrice) / pos.entryPrice;
|
|
67
|
+
const sizeUsd = pos.quantity * pos.entryPrice;
|
|
68
|
+
const portPct = portfolio.totalValue > 0 ? sizeUsd / portfolio.totalValue * 100 : 0;
|
|
69
|
+
lines.push("");
|
|
70
|
+
lines.push(`\u{1F3AF} ${side} ${ticker(pos.tokenId)} @ ${fmtPrice(pos.entryPrice)}`);
|
|
71
|
+
lines.push(` Stop ${fmtPrice(pos.stopLoss)} (${fmtPct(-stopPct)}) | TP ${fmtPrice(pos.takeProfit)} (${fmtPct(tpPct)})`);
|
|
72
|
+
lines.push(` Size $${sizeUsd.toFixed(0)} (${portPct.toFixed(1)}% of port)`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (hasExits) {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
const recentExits = input.recentTrades.filter((t) => now - (t.exitTimestamp ?? 0) < 12e4);
|
|
78
|
+
for (const t of recentExits.slice(0, 3)) {
|
|
79
|
+
const pctMove = t.pnlPercent;
|
|
80
|
+
const reason = t.exitReason.replace(/\s*\(price:.*\)/, "");
|
|
81
|
+
lines.push("");
|
|
82
|
+
lines.push(`\u2705 CLOSED ${ticker(t.tokenId)} ${t.side}`);
|
|
83
|
+
lines.push(` ${fmtPrice(t.entryPrice)} \u2192 ${fmtPrice(t.exitPrice)} (${fmtPct(pctMove)})`);
|
|
84
|
+
lines.push(` P&L: ${fmtUsd(t.pnlUsd)} | ${reason}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const regime = cycle.signals.find((s) => s.regime)?.regime ?? "unknown";
|
|
88
|
+
lines.push("");
|
|
89
|
+
lines.push(`\u{1F4CA} Regime: ${regime.charAt(0).toUpperCase() + regime.slice(1)}`);
|
|
90
|
+
const realizedPnl = input.allTrades.reduce((sum, t) => sum + t.pnlUsd, 0);
|
|
91
|
+
const unrealized = cycle.unrealizedPnl;
|
|
92
|
+
const initVal = portfolio.initialValue ?? 1e4;
|
|
93
|
+
const positionValue = cycle.portfolioValue - portfolio.cash;
|
|
94
|
+
const gs = input.gridStats;
|
|
95
|
+
const gridAlloc = gs && !gs.paused ? gs.allocation : 0;
|
|
96
|
+
const gridPnl = gs && !gs.paused ? gs.totalPnlUsd : 0;
|
|
97
|
+
const totalCapital = cycle.portfolioValue + gridAlloc;
|
|
98
|
+
const totalRealizedAll = realizedPnl + gridPnl;
|
|
99
|
+
const totalPnlPct = initVal > 0 ? (totalCapital - initVal) / initVal : 0;
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push(`\u{1F4B0} $${totalCapital.toFixed(2)} (${fmtPct(totalPnlPct)})`);
|
|
102
|
+
lines.push(` Realized: ${fmtUsd(totalRealizedAll)} from ${input.allTrades.length} trades + grid`);
|
|
103
|
+
lines.push(` Directional: $${portfolio.cash.toFixed(0)} cash | $${positionValue.toFixed(0)} positions | ${fmtUsd(unrealized)} open`);
|
|
104
|
+
lines.push(` Today: ${fmtUsd(cycle.dailyRealizedPnl)} realized | ${fmtUsd(unrealized)} open`);
|
|
105
|
+
if (gs && !gs.paused) {
|
|
106
|
+
const todayStr = gs.todayFills > 0 ? `${gs.todayFills} fills ${fmtUsd(gs.todayPnlUsd)} today` : "no fills today";
|
|
107
|
+
lines.push(` Grid: ${fmtUsd(gs.totalPnlUsd)} total (${gs.totalRoundTrips} trips) | ${todayStr} | $${gs.allocation.toFixed(0)} alloc`);
|
|
108
|
+
} else if (gs?.paused) {
|
|
109
|
+
lines.push(` Grid: PAUSED`);
|
|
110
|
+
}
|
|
111
|
+
const sorted = [...cycle.signals].sort((a, b) => Math.abs(b.score) - Math.abs(a.score));
|
|
112
|
+
const actionable = sorted.filter((s) => s.action !== "HOLD");
|
|
113
|
+
const topHolds = sorted.filter((s) => s.action === "HOLD").slice(0, 4 - actionable.length);
|
|
114
|
+
const display = [...actionable, ...topHolds].slice(0, 5);
|
|
115
|
+
lines.push("");
|
|
116
|
+
lines.push(`\u{1F50E} Signals (${cycle.tokensAnalyzed} scanned):`);
|
|
117
|
+
for (const s of display) {
|
|
118
|
+
const t = ticker(s.token).padEnd(10);
|
|
119
|
+
const sc = (s.score >= 0 ? "+" : "") + s.score.toFixed(3);
|
|
120
|
+
lines.push(` ${t} ${sc} ${arrow(s.action)}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push("");
|
|
123
|
+
const parts = [];
|
|
124
|
+
if (hasEntries || hasExits) {
|
|
125
|
+
parts.push(`${cycle.tradesExecuted} entr${cycle.tradesExecuted === 1 ? "y" : "ies"} | ${cycle.exitsProcessed} exit${cycle.exitsProcessed === 1 ? "" : "s"}`);
|
|
126
|
+
}
|
|
127
|
+
if (gs && gs.todayFills > 0) {
|
|
128
|
+
parts.push(`Grid: ${gs.todayFills} fills`);
|
|
129
|
+
}
|
|
130
|
+
if (parts.length === 0) {
|
|
131
|
+
lines.push("\u26A1 No entries. No exits. Watching.");
|
|
132
|
+
} else {
|
|
133
|
+
lines.push(`\u26A1 ${parts.join(" | ")}`);
|
|
134
|
+
}
|
|
135
|
+
return lines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
async function printSummary() {
|
|
138
|
+
const base = join(homedir(), ".sherwood", "agent");
|
|
139
|
+
const cyclesRaw = await readFile(join(base, "cycles.jsonl"), "utf-8");
|
|
140
|
+
const cycleLines = cyclesRaw.trim().split("\n").filter(Boolean);
|
|
141
|
+
if (cycleLines.length === 0) {
|
|
142
|
+
console.error("No cycles found in cycles.jsonl");
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const cycle = JSON.parse(cycleLines[cycleLines.length - 1]);
|
|
147
|
+
let portfolio;
|
|
148
|
+
try {
|
|
149
|
+
portfolio = JSON.parse(await readFile(join(base, "portfolio.json"), "utf-8"));
|
|
150
|
+
} catch {
|
|
151
|
+
portfolio = { totalValue: 0, positions: [], cash: 0, dailyPnl: 0, weeklyPnl: 0, monthlyPnl: 0 };
|
|
152
|
+
}
|
|
153
|
+
let trades = [];
|
|
154
|
+
try {
|
|
155
|
+
trades = JSON.parse(await readFile(join(base, "trades.json"), "utf-8"));
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
let gridStats;
|
|
159
|
+
try {
|
|
160
|
+
const gridBase = join(homedir(), ".sherwood", "grid");
|
|
161
|
+
const gridRaw = JSON.parse(await readFile(join(gridBase, "portfolio.json"), "utf-8"));
|
|
162
|
+
const grids = gridRaw.grids;
|
|
163
|
+
gridStats = {
|
|
164
|
+
totalPnlUsd: grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),
|
|
165
|
+
todayPnlUsd: grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),
|
|
166
|
+
todayFills: grids.reduce((s, g) => s + g.stats.todayFills, 0),
|
|
167
|
+
totalRoundTrips: grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0),
|
|
168
|
+
allocation: grids.reduce((s, g) => s + g.allocation, 0),
|
|
169
|
+
paused: gridRaw.paused ?? false
|
|
170
|
+
};
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
const msg = formatSummary({ cycle, portfolio, recentTrades: trades.slice(-5), allTrades: trades, gridStats });
|
|
174
|
+
process.stdout.write(msg + "\n");
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
formatSummary,
|
|
178
|
+
printSummary
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=summary-formatter-JIGWN4QS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent/summary-formatter.ts"],"sourcesContent":["/**\n * Deterministic XMTP/Telegram summary formatter.\n *\n * `sherwood agent summary` pipes this output directly to chat send --stdin.\n * No LLM interpretation — the format is code, not a prompt.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { CycleResult } from \"./loop.js\";\nimport type { PortfolioState } from \"./risk.js\";\nimport type { TradeRecord } from \"./portfolio.js\";\n\n// ── Ticker mapping (CoinGecko ID → short symbol) ──\n\nconst TICKER: Record<string, string> = {\n bitcoin: \"BTC\", ethereum: \"ETH\", solana: \"SOL\", hyperliquid: \"HYPE\",\n ethena: \"ENA\", aave: \"AAVE\", dogecoin: \"DOGE\", near: \"NEAR\",\n ripple: \"XRP\", sui: \"SUI\", fartcoin: \"FARTCOIN\", bittensor: \"TAO\",\n zcash: \"ZEC\", arbitrum: \"ARB\", \"avalanche-2\": \"AVAX\", chainlink: \"LINK\",\n \"worldcoin-wld\": \"WLD\", \"pudgy-penguins\": \"PENGU\", binancecoin: \"BNB\",\n blur: \"BLUR\", \"fetch-ai\": \"FET\", cardano: \"ADA\",\n};\n\nfunction ticker(cgId: string): string {\n return TICKER[cgId] ?? cgId.slice(0, 6).toUpperCase();\n}\n\n// ── Formatting helpers ──\n\nfunction fmtUsd(v: number): string {\n const sign = v >= 0 ? \"+\" : \"-\";\n return `${sign}$${Math.abs(v).toFixed(2)}`;\n}\n\nfunction fmtPct(v: number): string {\n return `${v >= 0 ? \"+\" : \"\"}${(v * 100).toFixed(2)}%`;\n}\n\nfunction fmtPrice(p: number): string {\n return p >= 1 ? `$${p.toFixed(2)}` : `$${p.toFixed(4)}`;\n}\n\nfunction arrow(action: string): string {\n if (action.includes(\"BUY\")) return \"\\u25B2\"; // ▲\n if (action.includes(\"SELL\")) return \"\\u25BC\"; // ▼\n return \"\\u2014\\u2014\"; // ——\n}\n\n// ── Main formatter ──\n\nexport interface GridSummaryStats {\n totalPnlUsd: number;\n todayPnlUsd: number;\n todayFills: number;\n totalRoundTrips: number;\n allocation: number;\n paused: boolean;\n}\n\nexport interface SummaryInput {\n cycle: CycleResult;\n portfolio: PortfolioState;\n /** Most recent closed trades (last 3, for exit callouts). */\n recentTrades: TradeRecord[];\n /** All closed trades — used to compute total realized PnL. */\n allTrades: TradeRecord[];\n /** Cumulative grid stats from grid-portfolio.json (optional — omitted if grid disabled). */\n gridStats?: GridSummaryStats;\n}\n\nexport function formatSummary(input: SummaryInput): string {\n const { cycle, portfolio } = input;\n const lines: string[] = [];\n\n const hasEntries = cycle.tradesExecuted > 0;\n const hasExits = cycle.exitsProcessed > 0;\n\n // Header\n if (hasEntries) {\n lines.push(\"\\uD83E\\uDD16 SHERWOOD \\u2014 Trade Executed\");\n } else if (hasExits) {\n lines.push(\"\\uD83E\\uDD16 SHERWOOD \\u2014 Position Closed\");\n } else {\n lines.push(\"\\uD83E\\uDD16 SHERWOOD \\u2014 Scan Complete\");\n }\n lines.push(\"\\u2501\".repeat(28));\n\n // Entry callout — show open positions that were just opened this cycle\n if (hasEntries) {\n for (const pos of portfolio.positions) {\n // Heuristic: position opened in the last 2 minutes = this cycle\n const age = Date.now() - (pos.entryTimestamp ?? 0);\n if (age > 120_000) continue;\n const side = (pos.side ?? \"long\").toUpperCase();\n const stopPct = Math.abs(pos.entryPrice - pos.stopLoss) / pos.entryPrice;\n const tpPct = Math.abs(pos.takeProfit - pos.entryPrice) / pos.entryPrice;\n const sizeUsd = pos.quantity * pos.entryPrice;\n const portPct = portfolio.totalValue > 0 ? (sizeUsd / portfolio.totalValue) * 100 : 0;\n lines.push(\"\");\n lines.push(`\\uD83C\\uDFAF ${side} ${ticker(pos.tokenId)} @ ${fmtPrice(pos.entryPrice)}`);\n lines.push(` Stop ${fmtPrice(pos.stopLoss)} (${fmtPct(-stopPct)}) | TP ${fmtPrice(pos.takeProfit)} (${fmtPct(tpPct)})`);\n lines.push(` Size $${sizeUsd.toFixed(0)} (${portPct.toFixed(1)}% of port)`);\n }\n }\n\n // Exit callout — show trades closed this cycle\n if (hasExits) {\n // Find trades with exitTimestamp in the last 2 minutes\n const now = Date.now();\n const recentExits = input.recentTrades.filter(t => now - (t.exitTimestamp ?? 0) < 120_000);\n for (const t of recentExits.slice(0, 3)) {\n const pctMove = t.pnlPercent;\n const reason = t.exitReason.replace(/\\s*\\(price:.*\\)/, \"\"); // strip raw price\n lines.push(\"\");\n lines.push(`\\u2705 CLOSED ${ticker(t.tokenId)} ${t.side}`);\n lines.push(` ${fmtPrice(t.entryPrice)} \\u2192 ${fmtPrice(t.exitPrice)} (${fmtPct(pctMove)})`);\n lines.push(` P&L: ${fmtUsd(t.pnlUsd)} | ${reason}`);\n }\n }\n\n // Regime\n const regime = cycle.signals.find(s => s.regime)?.regime ?? \"unknown\";\n lines.push(\"\");\n lines.push(`\\uD83D\\uDCCA Regime: ${regime.charAt(0).toUpperCase() + regime.slice(1)}`);\n\n // Portfolio — headline includes grid allocation so the total reflects\n // ALL capital, not just the directional portfolio.\n const realizedPnl = input.allTrades.reduce((sum, t) => sum + t.pnlUsd, 0);\n const unrealized = cycle.unrealizedPnl;\n const initVal = portfolio.initialValue ?? 10_000;\n const positionValue = cycle.portfolioValue - portfolio.cash;\n\n // Grid stats (read from grid-portfolio.json, independent of agent loop)\n const gs = input.gridStats;\n const gridAlloc = gs && !gs.paused ? gs.allocation : 0;\n const gridPnl = gs && !gs.paused ? gs.totalPnlUsd : 0;\n\n // Headline: directional portfolio + grid allocation = total capital\n const totalCapital = cycle.portfolioValue + gridAlloc;\n const totalRealizedAll = realizedPnl + gridPnl;\n const totalPnlPct = initVal > 0 ? (totalCapital - initVal) / initVal : 0;\n\n lines.push(\"\");\n lines.push(`\\uD83D\\uDCB0 $${totalCapital.toFixed(2)} (${fmtPct(totalPnlPct)})`);\n lines.push(` Realized: ${fmtUsd(totalRealizedAll)} from ${input.allTrades.length} trades + grid`);\n lines.push(` Directional: $${portfolio.cash.toFixed(0)} cash | $${positionValue.toFixed(0)} positions | ${fmtUsd(unrealized)} open`);\n lines.push(` Today: ${fmtUsd(cycle.dailyRealizedPnl)} realized | ${fmtUsd(unrealized)} open`);\n\n // Grid line\n if (gs && !gs.paused) {\n const todayStr = gs.todayFills > 0\n ? `${gs.todayFills} fills ${fmtUsd(gs.todayPnlUsd)} today`\n : \"no fills today\";\n lines.push(` Grid: ${fmtUsd(gs.totalPnlUsd)} total (${gs.totalRoundTrips} trips) | ${todayStr} | $${gs.allocation.toFixed(0)} alloc`);\n } else if (gs?.paused) {\n lines.push(` Grid: PAUSED`);\n }\n\n // Signals — top 4 by |score|, always include any BUY/SELL\n const sorted = [...cycle.signals].sort((a, b) => Math.abs(b.score) - Math.abs(a.score));\n const actionable = sorted.filter(s => s.action !== \"HOLD\");\n const topHolds = sorted.filter(s => s.action === \"HOLD\").slice(0, 4 - actionable.length);\n const display = [...actionable, ...topHolds].slice(0, 5);\n\n lines.push(\"\");\n lines.push(`\\uD83D\\uDD0E Signals (${cycle.tokensAnalyzed} scanned):`);\n for (const s of display) {\n const t = ticker(s.token).padEnd(10);\n const sc = (s.score >= 0 ? \"+\" : \"\") + s.score.toFixed(3);\n lines.push(` ${t} ${sc} ${arrow(s.action)}`);\n }\n\n // Footer\n lines.push(\"\");\n const parts: string[] = [];\n if (hasEntries || hasExits) {\n parts.push(`${cycle.tradesExecuted} entr${cycle.tradesExecuted === 1 ? \"y\" : \"ies\"} | ${cycle.exitsProcessed} exit${cycle.exitsProcessed === 1 ? \"\" : \"s\"}`);\n }\n if (gs && gs.todayFills > 0) {\n parts.push(`Grid: ${gs.todayFills} fills`);\n }\n if (parts.length === 0) {\n lines.push(\"\\u26A1 No entries. No exits. Watching.\");\n } else {\n lines.push(`\\u26A1 ${parts.join(\" | \")}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n// ── CLI entry point ──\n\nexport async function printSummary(): Promise<void> {\n const base = join(homedir(), \".sherwood\", \"agent\");\n\n // Read latest cycle\n const cyclesRaw = await readFile(join(base, \"cycles.jsonl\"), \"utf-8\");\n const cycleLines = cyclesRaw.trim().split(\"\\n\").filter(Boolean);\n if (cycleLines.length === 0) {\n console.error(\"No cycles found in cycles.jsonl\");\n process.exitCode = 1;\n return;\n }\n const cycle = JSON.parse(cycleLines[cycleLines.length - 1]!) as CycleResult;\n\n // Read portfolio\n let portfolio: PortfolioState;\n try {\n portfolio = JSON.parse(await readFile(join(base, \"portfolio.json\"), \"utf-8\")) as PortfolioState;\n } catch {\n portfolio = { totalValue: 0, positions: [], cash: 0, dailyPnl: 0, weeklyPnl: 0, monthlyPnl: 0 };\n }\n\n // Read recent trades\n let trades: TradeRecord[] = [];\n try {\n trades = JSON.parse(await readFile(join(base, \"trades.json\"), \"utf-8\")) as TradeRecord[];\n } catch {\n // No trades file\n }\n\n // Read grid stats (optional — absent if grid never initialized)\n let gridStats: GridSummaryStats | undefined;\n try {\n const gridBase = join(homedir(), \".sherwood\", \"grid\");\n const gridRaw = JSON.parse(await readFile(join(gridBase, \"portfolio.json\"), \"utf-8\"));\n const grids = gridRaw.grids as Array<{ allocation: number; stats: { totalPnlUsd: number; todayPnlUsd: number; todayFills: number; totalRoundTrips: number } }>;\n gridStats = {\n totalPnlUsd: grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),\n todayPnlUsd: grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),\n todayFills: grids.reduce((s, g) => s + g.stats.todayFills, 0),\n totalRoundTrips: grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0),\n allocation: grids.reduce((s, g) => s + g.allocation, 0),\n paused: gridRaw.paused ?? false,\n };\n } catch {\n // Grid not initialized — omit from summary\n }\n\n const msg = formatSummary({ cycle, portfolio, recentTrades: trades.slice(-5), allTrades: trades, gridStats });\n process.stdout.write(msg + \"\\n\");\n}\n"],"mappings":"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","names":[]}
|