@sherwoodagent/cli 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-HHXJOCKN.js → chat-E5AXULN3.js} +16 -6
- package/dist/chat-E5AXULN3.js.map +1 -0
- package/dist/chunk-DVWORPEY.js +75 -0
- package/dist/chunk-DVWORPEY.js.map +1 -0
- package/dist/{chunk-3WZLP6BH.js → chunk-FGSXRXCZ.js} +55 -480
- package/dist/chunk-FGSXRXCZ.js.map +1 -0
- package/dist/chunk-JY24QVXN.js +277 -0
- package/dist/chunk-JY24QVXN.js.map +1 -0
- package/dist/chunk-L5Y7E7LI.js +413 -0
- package/dist/chunk-L5Y7E7LI.js.map +1 -0
- package/dist/chunk-P4OHGJ3Z.js +350 -0
- package/dist/chunk-P4OHGJ3Z.js.map +1 -0
- package/dist/chunk-QMWMT6EH.js +149 -0
- package/dist/chunk-QMWMT6EH.js.map +1 -0
- package/dist/eas-Y2XAG3C6.js +21 -0
- package/dist/eas-Y2XAG3C6.js.map +1 -0
- package/dist/index.js +71 -270
- package/dist/index.js.map +1 -1
- package/dist/ipfs-P3NVJQCF.js +11 -0
- package/dist/ipfs-P3NVJQCF.js.map +1 -0
- package/dist/research-4RV3AQGW.js +308 -0
- package/dist/research-4RV3AQGW.js.map +1 -0
- package/dist/research-K6MRGQFD.js +12 -0
- package/dist/research-K6MRGQFD.js.map +1 -0
- package/dist/session-BBNSJYAQ.js +390 -0
- package/dist/session-BBNSJYAQ.js.map +1 -0
- package/dist/{xmtp-PICTODCB.js → xmtp-PQ4VJWTW.js} +6 -3
- package/dist/{xmtp-PICTODCB.js.map → xmtp-PQ4VJWTW.js.map} +1 -1
- package/package.json +3 -1
- package/dist/chat-HHXJOCKN.js.map +0 -1
- package/dist/chunk-3WZLP6BH.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MESSARI_COST_ESTIMATE,
|
|
3
|
+
NANSEN_COST_ESTIMATE,
|
|
4
|
+
getResearchProvider
|
|
5
|
+
} from "./chunk-L5Y7E7LI.js";
|
|
6
|
+
import {
|
|
7
|
+
getAccount
|
|
8
|
+
} from "./chunk-QMWMT6EH.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/research.ts
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import ora from "ora";
|
|
13
|
+
import { confirm } from "@inquirer/prompts";
|
|
14
|
+
import { isAddress } from "viem";
|
|
15
|
+
async function loadXmtp() {
|
|
16
|
+
return import("./xmtp-PQ4VJWTW.js");
|
|
17
|
+
}
|
|
18
|
+
var COST_ESTIMATES = {
|
|
19
|
+
messari: MESSARI_COST_ESTIMATE,
|
|
20
|
+
nansen: NANSEN_COST_ESTIMATE
|
|
21
|
+
};
|
|
22
|
+
function getEstimatedCost(provider, queryType) {
|
|
23
|
+
return COST_ESTIMATES[provider]?.[queryType] ?? "unknown";
|
|
24
|
+
}
|
|
25
|
+
async function confirmCost(provider, queryType, target, skipConfirm) {
|
|
26
|
+
const estimate = getEstimatedCost(provider, queryType);
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(chalk.bold(`Research Query`));
|
|
29
|
+
console.log(chalk.dim("\u2500".repeat(40)));
|
|
30
|
+
console.log(` Provider: ${provider}`);
|
|
31
|
+
console.log(` Type: ${queryType}`);
|
|
32
|
+
console.log(` Target: ${target}`);
|
|
33
|
+
console.log(` Est. cost: ${chalk.yellow(estimate + " USDC")} (x402 micropayment)`);
|
|
34
|
+
console.log();
|
|
35
|
+
if (skipConfirm) return true;
|
|
36
|
+
return confirm({
|
|
37
|
+
message: `Pay ${estimate} USDC to ${provider} for this query?`,
|
|
38
|
+
default: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function formatResearchResult(result) {
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(
|
|
44
|
+
chalk.bold(`Research: ${result.queryType} ${result.target} (${result.provider})`)
|
|
45
|
+
);
|
|
46
|
+
console.log(chalk.dim("\u2500".repeat(50)));
|
|
47
|
+
const data = result.data;
|
|
48
|
+
for (const [key, value] of Object.entries(data)) {
|
|
49
|
+
if (value === null || value === void 0) continue;
|
|
50
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
51
|
+
console.log(chalk.bold(`
|
|
52
|
+
${key}`));
|
|
53
|
+
for (const [subKey, subValue] of Object.entries(
|
|
54
|
+
value
|
|
55
|
+
)) {
|
|
56
|
+
console.log(` ${subKey}: ${formatValue(subValue)}`);
|
|
57
|
+
}
|
|
58
|
+
} else if (Array.isArray(value)) {
|
|
59
|
+
console.log(` ${key}: ${chalk.dim(`[${value.length} items]`)}`);
|
|
60
|
+
} else {
|
|
61
|
+
console.log(` ${key}: ${formatValue(value)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(
|
|
66
|
+
chalk.dim(
|
|
67
|
+
` Cost: $${result.costUsdc} USDC \u2022 Provider: ${result.provider}`
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
72
|
+
function formatValue(value) {
|
|
73
|
+
if (typeof value === "number") {
|
|
74
|
+
return value.toLocaleString("en-US", { maximumFractionDigits: 4 });
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === "bigint") {
|
|
77
|
+
return value.toLocaleString("en-US");
|
|
78
|
+
}
|
|
79
|
+
return String(value);
|
|
80
|
+
}
|
|
81
|
+
async function postResearch(syndicateName, result, prompt) {
|
|
82
|
+
const { pinJSON } = await import("./ipfs-P3NVJQCF.js");
|
|
83
|
+
const { createResearchAttestation, getEasScanUrl } = await import("./eas-Y2XAG3C6.js");
|
|
84
|
+
const xmtp = await loadXmtp();
|
|
85
|
+
const pinSpinner = ora("Pinning research result to IPFS...").start();
|
|
86
|
+
let resultUri;
|
|
87
|
+
try {
|
|
88
|
+
resultUri = await pinJSON(
|
|
89
|
+
{
|
|
90
|
+
schema: "sherwood/research/v1",
|
|
91
|
+
provider: result.provider,
|
|
92
|
+
queryType: result.queryType,
|
|
93
|
+
target: result.target,
|
|
94
|
+
prompt,
|
|
95
|
+
costUsdc: result.costUsdc,
|
|
96
|
+
data: result.data,
|
|
97
|
+
timestamp: result.timestamp
|
|
98
|
+
},
|
|
99
|
+
`sherwood-research-${result.provider}-${result.queryType}-${Date.now()}`
|
|
100
|
+
);
|
|
101
|
+
pinSpinner.succeed(`Pinned to IPFS: ${chalk.dim(resultUri)}`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
pinSpinner.fail("Failed to pin to IPFS");
|
|
104
|
+
console.error(
|
|
105
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
106
|
+
);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const easSpinner = ora("Creating EAS attestation...").start();
|
|
110
|
+
let attestationUid;
|
|
111
|
+
try {
|
|
112
|
+
const { uid } = await createResearchAttestation(
|
|
113
|
+
result.provider,
|
|
114
|
+
result.queryType,
|
|
115
|
+
prompt,
|
|
116
|
+
result.costUsdc,
|
|
117
|
+
resultUri
|
|
118
|
+
);
|
|
119
|
+
attestationUid = uid;
|
|
120
|
+
easSpinner.succeed(
|
|
121
|
+
`Attested: ${chalk.dim(getEasScanUrl(uid))}`
|
|
122
|
+
);
|
|
123
|
+
} catch (err) {
|
|
124
|
+
easSpinner.fail("Failed to create EAS attestation");
|
|
125
|
+
console.error(
|
|
126
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const chatSpinner = ora("Posting to syndicate chat...").start();
|
|
131
|
+
try {
|
|
132
|
+
const group = await xmtp.getGroup("", syndicateName);
|
|
133
|
+
await xmtp.sendEnvelope(group, {
|
|
134
|
+
type: "X402_RESEARCH",
|
|
135
|
+
from: getAccount().address,
|
|
136
|
+
text: `Ran ${result.provider} ${result.queryType} on ${result.target} ($${result.costUsdc} USDC)`,
|
|
137
|
+
data: {
|
|
138
|
+
provider: result.provider,
|
|
139
|
+
queryType: result.queryType,
|
|
140
|
+
target: result.target,
|
|
141
|
+
costUsdc: result.costUsdc,
|
|
142
|
+
resultUri,
|
|
143
|
+
attestationUid
|
|
144
|
+
},
|
|
145
|
+
timestamp: result.timestamp
|
|
146
|
+
});
|
|
147
|
+
chatSpinner.succeed("Posted to syndicate chat");
|
|
148
|
+
} catch (err) {
|
|
149
|
+
chatSpinner.fail("Failed to post to chat");
|
|
150
|
+
console.error(
|
|
151
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function registerResearchCommands(program) {
|
|
156
|
+
const research = program.command("research").description(
|
|
157
|
+
"Query DeFi research providers via x402 micropayments (USDC on Base)"
|
|
158
|
+
);
|
|
159
|
+
research.command("token <target>").description("Token report \u2014 profile, market data, on-chain metrics").requiredOption(
|
|
160
|
+
"--provider <name>",
|
|
161
|
+
"Research provider (messari, nansen)"
|
|
162
|
+
).option(
|
|
163
|
+
"--post <syndicate>",
|
|
164
|
+
"Post result to syndicate chat (pin to IPFS + attest to EAS)"
|
|
165
|
+
).option("--yes", "Skip cost confirmation prompt", false).action(async (target, opts) => {
|
|
166
|
+
const ok = await confirmCost(opts.provider, "token", target, opts.yes);
|
|
167
|
+
if (!ok) {
|
|
168
|
+
console.log(chalk.dim("Cancelled."));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const spinner = ora(
|
|
172
|
+
`Querying ${opts.provider} for token ${target}...`
|
|
173
|
+
).start();
|
|
174
|
+
try {
|
|
175
|
+
const provider = getResearchProvider(opts.provider);
|
|
176
|
+
const result = await provider.query({ type: "token", target });
|
|
177
|
+
spinner.succeed(`Token data received from ${opts.provider}`);
|
|
178
|
+
formatResearchResult(result);
|
|
179
|
+
if (opts.post) {
|
|
180
|
+
await postResearch(opts.post, result, `token ${target}`);
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
spinner.fail(`Token query failed`);
|
|
184
|
+
console.error(
|
|
185
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
186
|
+
);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
research.command("market <asset>").description("Market overview \u2014 price, volume, market cap, ROI, ATH").requiredOption(
|
|
191
|
+
"--provider <name>",
|
|
192
|
+
"Research provider (messari, nansen)"
|
|
193
|
+
).option(
|
|
194
|
+
"--post <syndicate>",
|
|
195
|
+
"Post result to syndicate chat (pin to IPFS + attest to EAS)"
|
|
196
|
+
).option("--yes", "Skip cost confirmation prompt", false).action(async (asset, opts) => {
|
|
197
|
+
const ok = await confirmCost(opts.provider, "market", asset, opts.yes);
|
|
198
|
+
if (!ok) {
|
|
199
|
+
console.log(chalk.dim("Cancelled."));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const spinner = ora(
|
|
203
|
+
`Querying ${opts.provider} for ${asset} market data...`
|
|
204
|
+
).start();
|
|
205
|
+
try {
|
|
206
|
+
const provider = getResearchProvider(opts.provider);
|
|
207
|
+
const result = await provider.query({ type: "market", target: asset });
|
|
208
|
+
spinner.succeed(`Market data received from ${opts.provider}`);
|
|
209
|
+
formatResearchResult(result);
|
|
210
|
+
if (opts.post) {
|
|
211
|
+
await postResearch(opts.post, result, `market ${asset}`);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
spinner.fail(`Market query failed`);
|
|
215
|
+
console.error(
|
|
216
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
217
|
+
);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
research.command("smart-money").description(
|
|
222
|
+
"Smart money flows \u2014 net flow, DEX trades, holdings from labeled wallets"
|
|
223
|
+
).requiredOption("--token <symbol>", "Token symbol to analyze (e.g. WETH)").requiredOption(
|
|
224
|
+
"--provider <name>",
|
|
225
|
+
"Research provider (messari, nansen)"
|
|
226
|
+
).option(
|
|
227
|
+
"--post <syndicate>",
|
|
228
|
+
"Post result to syndicate chat (pin to IPFS + attest to EAS)"
|
|
229
|
+
).option("--yes", "Skip cost confirmation prompt", false).action(async (opts) => {
|
|
230
|
+
const ok = await confirmCost(opts.provider, "smart-money", opts.token, opts.yes);
|
|
231
|
+
if (!ok) {
|
|
232
|
+
console.log(chalk.dim("Cancelled."));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const spinner = ora(
|
|
236
|
+
`Querying ${opts.provider} for ${opts.token} smart money flows...`
|
|
237
|
+
).start();
|
|
238
|
+
try {
|
|
239
|
+
const provider = getResearchProvider(opts.provider);
|
|
240
|
+
const result = await provider.query({
|
|
241
|
+
type: "smart-money",
|
|
242
|
+
target: opts.token,
|
|
243
|
+
options: { token: opts.token }
|
|
244
|
+
});
|
|
245
|
+
spinner.succeed(
|
|
246
|
+
`Smart money data received from ${opts.provider}`
|
|
247
|
+
);
|
|
248
|
+
formatResearchResult(result);
|
|
249
|
+
if (opts.post) {
|
|
250
|
+
await postResearch(
|
|
251
|
+
opts.post,
|
|
252
|
+
result,
|
|
253
|
+
`smart-money --token ${opts.token}`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
} catch (err) {
|
|
257
|
+
spinner.fail(`Smart money query failed`);
|
|
258
|
+
console.error(
|
|
259
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
260
|
+
);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
research.command("wallet <address>").description(
|
|
265
|
+
"Wallet due diligence \u2014 PnL history, transaction patterns, counterparties"
|
|
266
|
+
).requiredOption(
|
|
267
|
+
"--provider <name>",
|
|
268
|
+
"Research provider (messari, nansen)"
|
|
269
|
+
).option(
|
|
270
|
+
"--post <syndicate>",
|
|
271
|
+
"Post result to syndicate chat (pin to IPFS + attest to EAS)"
|
|
272
|
+
).option("--yes", "Skip cost confirmation prompt", false).action(async (address, opts) => {
|
|
273
|
+
if (!isAddress(address)) {
|
|
274
|
+
console.error(chalk.red(`Invalid wallet address: ${address}`));
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
const ok = await confirmCost(opts.provider, "wallet", address, opts.yes);
|
|
278
|
+
if (!ok) {
|
|
279
|
+
console.log(chalk.dim("Cancelled."));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const spinner = ora(
|
|
283
|
+
`Querying ${opts.provider} for wallet ${address.slice(0, 8)}...`
|
|
284
|
+
).start();
|
|
285
|
+
try {
|
|
286
|
+
const provider = getResearchProvider(opts.provider);
|
|
287
|
+
const result = await provider.query({
|
|
288
|
+
type: "wallet",
|
|
289
|
+
target: address
|
|
290
|
+
});
|
|
291
|
+
spinner.succeed(`Wallet data received from ${opts.provider}`);
|
|
292
|
+
formatResearchResult(result);
|
|
293
|
+
if (opts.post) {
|
|
294
|
+
await postResearch(opts.post, result, `wallet ${address}`);
|
|
295
|
+
}
|
|
296
|
+
} catch (err) {
|
|
297
|
+
spinner.fail(`Wallet query failed`);
|
|
298
|
+
console.error(
|
|
299
|
+
chalk.red(err instanceof Error ? err.message : String(err))
|
|
300
|
+
);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
export {
|
|
306
|
+
registerResearchCommands
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=research-4RV3AQGW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/research.ts"],"sourcesContent":["/**\n * Research commands — sherwood research <subcommand>\n *\n * Query DeFi research providers (Messari, Nansen) using x402 micropayments.\n * Agent pays per-query with USDC from its own wallet — no vault interaction needed.\n *\n * --post <syndicate>: pins result to IPFS, creates EAS attestation, posts\n * lightweight notification to syndicate XMTP chat (not the full result).\n *\n * --yes: skip the cost confirmation prompt (for non-interactive / agent use).\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { isAddress } from \"viem\";\nimport { getAccount } from \"../lib/client.js\";\nimport type { MessageType } from \"../lib/types.js\";\nimport { getResearchProvider } from \"../providers/research/index.js\";\nimport type { ResearchResult } from \"../providers/research/index.js\";\nimport { MESSARI_COST_ESTIMATE } from \"../providers/research/messari.js\";\nimport { NANSEN_COST_ESTIMATE } from \"../providers/research/nansen.js\";\n\n// Lazy-load XMTP to avoid breaking non-chat commands when @xmtp/cli is missing\nasync function loadXmtp() {\n return import(\"../lib/xmtp.js\");\n}\n\n// ── Cost estimates ──\n\nconst COST_ESTIMATES: Record<string, Record<string, string>> = {\n messari: MESSARI_COST_ESTIMATE,\n nansen: NANSEN_COST_ESTIMATE,\n};\n\nfunction getEstimatedCost(provider: string, queryType: string): string {\n return COST_ESTIMATES[provider]?.[queryType] ?? \"unknown\";\n}\n\n/**\n * Confirm cost with the user before executing the x402 query.\n * Skipped when --yes is passed (non-interactive / agent mode).\n */\nasync function confirmCost(\n provider: string,\n queryType: string,\n target: string,\n skipConfirm: boolean,\n): Promise<boolean> {\n const estimate = getEstimatedCost(provider, queryType);\n\n console.log();\n console.log(chalk.bold(`Research Query`));\n console.log(chalk.dim(\"─\".repeat(40)));\n console.log(` Provider: ${provider}`);\n console.log(` Type: ${queryType}`);\n console.log(` Target: ${target}`);\n console.log(` Est. cost: ${chalk.yellow(estimate + \" USDC\")} (x402 micropayment)`);\n console.log();\n\n if (skipConfirm) return true;\n\n return confirm({\n message: `Pay ${estimate} USDC to ${provider} for this query?`,\n default: true,\n });\n}\n\n// ── Output formatting ──\n\nfunction formatResearchResult(result: ResearchResult): void {\n console.log();\n console.log(\n chalk.bold(`Research: ${result.queryType} ${result.target} (${result.provider})`),\n );\n console.log(chalk.dim(\"─\".repeat(50)));\n\n // Flatten the top-level data keys into a readable summary\n const data = result.data;\n for (const [key, value] of Object.entries(data)) {\n if (value === null || value === undefined) continue;\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n // Nested object — show as sub-section\n console.log(chalk.bold(`\\n ${key}`));\n for (const [subKey, subValue] of Object.entries(\n value as Record<string, unknown>,\n )) {\n console.log(` ${subKey}: ${formatValue(subValue)}`);\n }\n } else if (Array.isArray(value)) {\n console.log(` ${key}: ${chalk.dim(`[${value.length} items]`)}`);\n } else {\n console.log(` ${key}: ${formatValue(value)}`);\n }\n }\n\n console.log();\n console.log(\n chalk.dim(\n ` Cost: $${result.costUsdc} USDC • Provider: ${result.provider}`,\n ),\n );\n console.log();\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"number\") {\n // Format large numbers with commas\n return value.toLocaleString(\"en-US\", { maximumFractionDigits: 4 });\n }\n if (typeof value === \"bigint\") {\n return value.toLocaleString(\"en-US\");\n }\n return String(value);\n}\n\n// ── Post flow: IPFS → EAS → XMTP ──\n\nasync function postResearch(\n syndicateName: string,\n result: ResearchResult,\n prompt: string,\n): Promise<void> {\n const { pinJSON } = await import(\"../lib/ipfs.js\");\n const { createResearchAttestation, getEasScanUrl } = await import(\n \"../lib/eas.js\"\n );\n const xmtp = await loadXmtp();\n\n // 1. Pin full research result to IPFS\n const pinSpinner = ora(\"Pinning research result to IPFS...\").start();\n let resultUri: string;\n try {\n resultUri = await pinJSON(\n {\n schema: \"sherwood/research/v1\",\n provider: result.provider,\n queryType: result.queryType,\n target: result.target,\n prompt,\n costUsdc: result.costUsdc,\n data: result.data,\n timestamp: result.timestamp,\n } as Record<string, unknown>,\n `sherwood-research-${result.provider}-${result.queryType}-${Date.now()}`,\n );\n pinSpinner.succeed(`Pinned to IPFS: ${chalk.dim(resultUri)}`);\n } catch (err) {\n pinSpinner.fail(\"Failed to pin to IPFS\");\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n return;\n }\n\n // 2. Create EAS attestation with prompt, cost, provider, and IPFS URI\n const easSpinner = ora(\"Creating EAS attestation...\").start();\n let attestationUid: string;\n try {\n const { uid } = await createResearchAttestation(\n result.provider,\n result.queryType,\n prompt,\n result.costUsdc,\n resultUri,\n );\n attestationUid = uid;\n easSpinner.succeed(\n `Attested: ${chalk.dim(getEasScanUrl(uid))}`,\n );\n } catch (err) {\n easSpinner.fail(\"Failed to create EAS attestation\");\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n return;\n }\n\n // 3. Post lightweight notification to XMTP chat\n const chatSpinner = ora(\"Posting to syndicate chat...\").start();\n try {\n const group = await xmtp.getGroup(\"\", syndicateName);\n await xmtp.sendEnvelope(group, {\n type: \"X402_RESEARCH\" as MessageType,\n from: getAccount().address,\n text: `Ran ${result.provider} ${result.queryType} on ${result.target} ($${result.costUsdc} USDC)`,\n data: {\n provider: result.provider,\n queryType: result.queryType,\n target: result.target,\n costUsdc: result.costUsdc,\n resultUri,\n attestationUid,\n },\n timestamp: result.timestamp,\n });\n chatSpinner.succeed(\"Posted to syndicate chat\");\n } catch (err) {\n chatSpinner.fail(\"Failed to post to chat\");\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n }\n}\n\n// ── Command registration ──\n\nexport function registerResearchCommands(program: Command): void {\n const research = program\n .command(\"research\")\n .description(\n \"Query DeFi research providers via x402 micropayments (USDC on Base)\",\n );\n\n // ── research token <target> ──\n\n research\n .command(\"token <target>\")\n .description(\"Token report — profile, market data, on-chain metrics\")\n .requiredOption(\n \"--provider <name>\",\n \"Research provider (messari, nansen)\",\n )\n .option(\n \"--post <syndicate>\",\n \"Post result to syndicate chat (pin to IPFS + attest to EAS)\",\n )\n .option(\"--yes\", \"Skip cost confirmation prompt\", false)\n .action(async (target: string, opts: { provider: string; post?: string; yes: boolean }) => {\n const ok = await confirmCost(opts.provider, \"token\", target, opts.yes);\n if (!ok) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n const spinner = ora(\n `Querying ${opts.provider} for token ${target}...`,\n ).start();\n\n try {\n const provider = getResearchProvider(opts.provider);\n const result = await provider.query({ type: \"token\", target });\n spinner.succeed(`Token data received from ${opts.provider}`);\n formatResearchResult(result);\n\n if (opts.post) {\n await postResearch(opts.post, result, `token ${target}`);\n }\n } catch (err) {\n spinner.fail(`Token query failed`);\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\n // ── research market <asset> ──\n\n research\n .command(\"market <asset>\")\n .description(\"Market overview — price, volume, market cap, ROI, ATH\")\n .requiredOption(\n \"--provider <name>\",\n \"Research provider (messari, nansen)\",\n )\n .option(\n \"--post <syndicate>\",\n \"Post result to syndicate chat (pin to IPFS + attest to EAS)\",\n )\n .option(\"--yes\", \"Skip cost confirmation prompt\", false)\n .action(async (asset: string, opts: { provider: string; post?: string; yes: boolean }) => {\n const ok = await confirmCost(opts.provider, \"market\", asset, opts.yes);\n if (!ok) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n const spinner = ora(\n `Querying ${opts.provider} for ${asset} market data...`,\n ).start();\n\n try {\n const provider = getResearchProvider(opts.provider);\n const result = await provider.query({ type: \"market\", target: asset });\n spinner.succeed(`Market data received from ${opts.provider}`);\n formatResearchResult(result);\n\n if (opts.post) {\n await postResearch(opts.post, result, `market ${asset}`);\n }\n } catch (err) {\n spinner.fail(`Market query failed`);\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\n // ── research smart-money ──\n\n research\n .command(\"smart-money\")\n .description(\n \"Smart money flows — net flow, DEX trades, holdings from labeled wallets\",\n )\n .requiredOption(\"--token <symbol>\", \"Token symbol to analyze (e.g. WETH)\")\n .requiredOption(\n \"--provider <name>\",\n \"Research provider (messari, nansen)\",\n )\n .option(\n \"--post <syndicate>\",\n \"Post result to syndicate chat (pin to IPFS + attest to EAS)\",\n )\n .option(\"--yes\", \"Skip cost confirmation prompt\", false)\n .action(async (opts: { token: string; provider: string; post?: string; yes: boolean }) => {\n const ok = await confirmCost(opts.provider, \"smart-money\", opts.token, opts.yes);\n if (!ok) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n const spinner = ora(\n `Querying ${opts.provider} for ${opts.token} smart money flows...`,\n ).start();\n\n try {\n const provider = getResearchProvider(opts.provider);\n const result = await provider.query({\n type: \"smart-money\",\n target: opts.token,\n options: { token: opts.token },\n });\n spinner.succeed(\n `Smart money data received from ${opts.provider}`,\n );\n formatResearchResult(result);\n\n if (opts.post) {\n await postResearch(\n opts.post,\n result,\n `smart-money --token ${opts.token}`,\n );\n }\n } catch (err) {\n spinner.fail(`Smart money query failed`);\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\n // ── research wallet <address> ──\n\n research\n .command(\"wallet <address>\")\n .description(\n \"Wallet due diligence — PnL history, transaction patterns, counterparties\",\n )\n .requiredOption(\n \"--provider <name>\",\n \"Research provider (messari, nansen)\",\n )\n .option(\n \"--post <syndicate>\",\n \"Post result to syndicate chat (pin to IPFS + attest to EAS)\",\n )\n .option(\"--yes\", \"Skip cost confirmation prompt\", false)\n .action(async (address: string, opts: { provider: string; post?: string; yes: boolean }) => {\n if (!isAddress(address)) {\n console.error(chalk.red(`Invalid wallet address: ${address}`));\n process.exit(1);\n }\n\n const ok = await confirmCost(opts.provider, \"wallet\", address, opts.yes);\n if (!ok) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n const spinner = ora(\n `Querying ${opts.provider} for wallet ${address.slice(0, 8)}...`,\n ).start();\n\n try {\n const provider = getResearchProvider(opts.provider);\n const result = await provider.query({\n type: \"wallet\",\n target: address,\n });\n spinner.succeed(`Wallet data received from ${opts.provider}`);\n formatResearchResult(result);\n\n if (opts.post) {\n await postResearch(opts.post, result, `wallet ${address}`);\n }\n } catch (err) {\n spinner.fail(`Wallet query failed`);\n console.error(\n chalk.red(err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MessariProvider,
|
|
3
|
+
NansenProvider,
|
|
4
|
+
getResearchProvider
|
|
5
|
+
} from "./chunk-L5Y7E7LI.js";
|
|
6
|
+
import "./chunk-QMWMT6EH.js";
|
|
7
|
+
export {
|
|
8
|
+
MessariProvider,
|
|
9
|
+
NansenProvider,
|
|
10
|
+
getResearchProvider
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=research-K6MRGQFD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|