@sherwoodagent/cli 0.22.8 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{chat-JYAJMP4D.js → chat-52PWNH7Y.js} +7 -7
  2. package/dist/{chunk-4YIE7GHL.js → chunk-66VFSLPS.js} +2 -2
  3. package/dist/{chunk-VO6DCRWV.js → chunk-7PCI3WHQ.js} +4 -4
  4. package/dist/{chunk-LVPBJ7MI.js → chunk-GWIW6DDG.js} +2 -2
  5. package/dist/{chunk-4Y6Y4SVH.js → chunk-JEVBC7DV.js} +4 -4
  6. package/dist/{chunk-G7JQRLQE.js → chunk-QBNWBWD3.js} +5 -5
  7. package/dist/{chunk-B42ZCCZ7.js → chunk-RGOTBJIN.js} +28 -3
  8. package/dist/chunk-RGOTBJIN.js.map +1 -0
  9. package/dist/{chunk-ASJTFFZH.js → chunk-SBVHGUQJ.js} +3 -3
  10. package/dist/{chunk-Y7LS6DEF.js → chunk-VQIFAXUY.js} +5 -5
  11. package/dist/{chunk-JR5K5WVR.js → chunk-ZPLUJT4V.js} +169 -13
  12. package/dist/chunk-ZPLUJT4V.js.map +1 -0
  13. package/dist/{client-SCIY6SUP.js → client-CWQFYZ5Z.js} +4 -4
  14. package/dist/{config-BGS5MX4D.js → config-YXZKPSZB.js} +8 -2
  15. package/dist/{eas-X5UIEM6D.js → eas-YZIHYQAI.js} +6 -6
  16. package/dist/{governor-ZKRSAHG4.js → governor-YPDAOT3S.js} +6 -6
  17. package/dist/index.js +741 -74
  18. package/dist/index.js.map +1 -1
  19. package/dist/{network-UTSOP7EF.js → network-QEMP3DSP.js} +3 -3
  20. package/dist/price-SWNEGWO4.js +156 -0
  21. package/dist/price-SWNEGWO4.js.map +1 -0
  22. package/dist/{research-EWH43HFN.js → research-PH77VSE4.js} +9 -9
  23. package/dist/research-ZX7T2ZIJ.js +14 -0
  24. package/dist/{session-XZNKLK3I.js → session-NMA4RLKC.js} +12 -12
  25. package/dist/{trade-VKSXEHWW.js → trade-N2A74UPX.js} +16 -16
  26. package/dist/{xmtp-XKHXATPQ.js → xmtp-YNOGIZ4G.js} +6 -6
  27. package/package.json +1 -1
  28. package/dist/chunk-B42ZCCZ7.js.map +0 -1
  29. package/dist/chunk-JR5K5WVR.js.map +0 -1
  30. package/dist/research-W3M3VZBD.js +0 -14
  31. /package/dist/{chat-JYAJMP4D.js.map → chat-52PWNH7Y.js.map} +0 -0
  32. /package/dist/{chunk-4YIE7GHL.js.map → chunk-66VFSLPS.js.map} +0 -0
  33. /package/dist/{chunk-VO6DCRWV.js.map → chunk-7PCI3WHQ.js.map} +0 -0
  34. /package/dist/{chunk-LVPBJ7MI.js.map → chunk-GWIW6DDG.js.map} +0 -0
  35. /package/dist/{chunk-4Y6Y4SVH.js.map → chunk-JEVBC7DV.js.map} +0 -0
  36. /package/dist/{chunk-G7JQRLQE.js.map → chunk-QBNWBWD3.js.map} +0 -0
  37. /package/dist/{chunk-ASJTFFZH.js.map → chunk-SBVHGUQJ.js.map} +0 -0
  38. /package/dist/{chunk-Y7LS6DEF.js.map → chunk-VQIFAXUY.js.map} +0 -0
  39. /package/dist/{client-SCIY6SUP.js.map → client-CWQFYZ5Z.js.map} +0 -0
  40. /package/dist/{config-BGS5MX4D.js.map → config-YXZKPSZB.js.map} +0 -0
  41. /package/dist/{eas-X5UIEM6D.js.map → eas-YZIHYQAI.js.map} +0 -0
  42. /package/dist/{governor-ZKRSAHG4.js.map → governor-YPDAOT3S.js.map} +0 -0
  43. /package/dist/{network-UTSOP7EF.js.map → network-QEMP3DSP.js.map} +0 -0
  44. /package/dist/{research-EWH43HFN.js.map → research-PH77VSE4.js.map} +0 -0
  45. /package/dist/{research-W3M3VZBD.js.map → research-ZX7T2ZIJ.js.map} +0 -0
  46. /package/dist/{session-XZNKLK3I.js.map → session-NMA4RLKC.js.map} +0 -0
  47. /package/dist/{trade-VKSXEHWW.js.map → trade-N2A74UPX.js.map} +0 -0
  48. /package/dist/{xmtp-XKHXATPQ.js.map → xmtp-YNOGIZ4G.js.map} +0 -0
@@ -9,8 +9,8 @@ import {
9
9
  isTestnet,
10
10
  robinhoodTestnet,
11
11
  setNetwork
12
- } from "./chunk-LVPBJ7MI.js";
13
- import "./chunk-B42ZCCZ7.js";
12
+ } from "./chunk-GWIW6DDG.js";
13
+ import "./chunk-RGOTBJIN.js";
14
14
  export {
15
15
  CHAIN_REGISTRY,
16
16
  VALID_NETWORKS,
@@ -23,4 +23,4 @@ export {
23
23
  robinhoodTestnet,
24
24
  setNetwork
25
25
  };
26
- //# sourceMappingURL=network-UTSOP7EF.js.map
26
+ //# sourceMappingURL=network-QEMP3DSP.js.map
@@ -0,0 +1,156 @@
1
+ import {
2
+ SYNTHRA,
3
+ UNISWAP,
4
+ UNISWAP_QUOTER_V2_ABI
5
+ } from "./chunk-ZPLUJT4V.js";
6
+ import {
7
+ getPublicClient
8
+ } from "./chunk-SBVHGUQJ.js";
9
+ import {
10
+ getNetwork
11
+ } from "./chunk-GWIW6DDG.js";
12
+ import "./chunk-RGOTBJIN.js";
13
+
14
+ // src/lib/price.ts
15
+ import { encodeFunctionData, decodeFunctionResult, parseUnits, formatUnits } from "viem";
16
+ var ZERO = "0x0000000000000000000000000000000000000000";
17
+ async function getTokenPriceInAsset(params) {
18
+ const { token, tokenDecimals, asset, assetDecimals, feeTier } = params;
19
+ if (token.toLowerCase() === asset.toLowerCase()) {
20
+ return { price: 1, amountOut: parseUnits("1", assetDecimals), source: "uniswap" };
21
+ }
22
+ const network = getNetwork();
23
+ const client = getPublicClient();
24
+ if (network === "robinhood-testnet") {
25
+ return quoteSynthra(client, token, tokenDecimals, asset, assetDecimals, feeTier);
26
+ }
27
+ return quoteUniswap(client, token, tokenDecimals, asset, assetDecimals, feeTier);
28
+ }
29
+ async function getTokenPricesInAsset(params) {
30
+ const { tokens, asset, assetDecimals } = params;
31
+ const results = await Promise.allSettled(
32
+ tokens.map(
33
+ (t) => getTokenPriceInAsset({
34
+ token: t.token,
35
+ tokenDecimals: t.tokenDecimals,
36
+ asset,
37
+ assetDecimals,
38
+ feeTier: t.feeTier
39
+ })
40
+ )
41
+ );
42
+ return results.map((r) => r.status === "fulfilled" ? r.value : null);
43
+ }
44
+ var POOL_SLOT0_ABI = [
45
+ {
46
+ type: "function",
47
+ name: "slot0",
48
+ inputs: [],
49
+ outputs: [
50
+ { name: "sqrtPriceX96", type: "uint160" },
51
+ { name: "tick", type: "int24" },
52
+ { name: "observationIndex", type: "uint16" },
53
+ { name: "observationCardinality", type: "uint16" },
54
+ { name: "observationCardinalityNext", type: "uint16" },
55
+ { name: "feeProtocol", type: "uint8" },
56
+ { name: "unlocked", type: "bool" }
57
+ ],
58
+ stateMutability: "view"
59
+ }
60
+ ];
61
+ var POOL_TOKEN0_ABI = [
62
+ {
63
+ type: "function",
64
+ name: "token0",
65
+ inputs: [],
66
+ outputs: [{ name: "", type: "address" }],
67
+ stateMutability: "view"
68
+ }
69
+ ];
70
+ var FACTORY_GET_POOL_ABI = [
71
+ {
72
+ type: "function",
73
+ name: "getPool",
74
+ inputs: [
75
+ { name: "tokenA", type: "address" },
76
+ { name: "tokenB", type: "address" },
77
+ { name: "fee", type: "uint24" }
78
+ ],
79
+ outputs: [{ name: "", type: "address" }],
80
+ stateMutability: "view"
81
+ }
82
+ ];
83
+ function priceFromSqrtPriceX96(sqrtPriceX96, token0Decimals, token1Decimals, tokenInIsToken0) {
84
+ const Q96 = 2 ** 96;
85
+ const ratio = Number(sqrtPriceX96) / Q96;
86
+ const rawPrice = ratio * ratio;
87
+ const decimalAdjust = 10 ** (token0Decimals - token1Decimals);
88
+ const token1PerToken0 = rawPrice * decimalAdjust;
89
+ return tokenInIsToken0 ? token1PerToken0 : 1 / token1PerToken0;
90
+ }
91
+ async function quoteSynthra(client, tokenIn, tokenInDecimals, tokenOut, tokenOutDecimals, fee) {
92
+ const factoryAddr = SYNTHRA().FACTORY;
93
+ if (factoryAddr === ZERO) {
94
+ throw new Error("Synthra Factory not deployed on this network");
95
+ }
96
+ const pool = await client.readContract({
97
+ address: factoryAddr,
98
+ abi: FACTORY_GET_POOL_ABI,
99
+ functionName: "getPool",
100
+ args: [tokenIn, tokenOut, fee]
101
+ });
102
+ if (!pool || pool === ZERO) {
103
+ throw new Error(`No Synthra pool for ${tokenIn}\u2194${tokenOut} fee=${fee}`);
104
+ }
105
+ const [slot0, token0] = await Promise.all([
106
+ client.readContract({ address: pool, abi: POOL_SLOT0_ABI, functionName: "slot0" }),
107
+ client.readContract({ address: pool, abi: POOL_TOKEN0_ABI, functionName: "token0" })
108
+ ]);
109
+ const sqrtPriceX96 = slot0[0];
110
+ if (sqrtPriceX96 === 0n) {
111
+ throw new Error(`Pool ${pool} not initialized (sqrtPriceX96 = 0)`);
112
+ }
113
+ const tokenInIsToken0 = tokenIn.toLowerCase() === token0.toLowerCase();
114
+ const t0Decimals = tokenInIsToken0 ? tokenInDecimals : tokenOutDecimals;
115
+ const t1Decimals = tokenInIsToken0 ? tokenOutDecimals : tokenInDecimals;
116
+ const price = priceFromSqrtPriceX96(sqrtPriceX96, t0Decimals, t1Decimals, tokenInIsToken0);
117
+ const oneToken = parseUnits("1", tokenInDecimals);
118
+ const amountOut = BigInt(Math.round(price * Number(parseUnits("1", tokenOutDecimals))));
119
+ return { price, amountOut, source: "synthra" };
120
+ }
121
+ async function quoteUniswap(client, tokenIn, tokenInDecimals, tokenOut, tokenOutDecimals, fee) {
122
+ const quoterAddr = UNISWAP().QUOTER_V2;
123
+ if (quoterAddr === ZERO) {
124
+ throw new Error("Uniswap QuoterV2 not deployed on this network");
125
+ }
126
+ const oneToken = parseUnits("1", tokenInDecimals);
127
+ const calldata = encodeFunctionData({
128
+ abi: UNISWAP_QUOTER_V2_ABI,
129
+ functionName: "quoteExactInputSingle",
130
+ args: [
131
+ {
132
+ tokenIn,
133
+ tokenOut,
134
+ amountIn: oneToken,
135
+ fee,
136
+ sqrtPriceLimitX96: 0n
137
+ }
138
+ ]
139
+ });
140
+ const { data } = await client.call({ to: quoterAddr, data: calldata });
141
+ if (!data) {
142
+ throw new Error(`Uniswap quoter returned no data for ${tokenIn}\u2192${tokenOut} fee=${fee}`);
143
+ }
144
+ const [amountOut] = decodeFunctionResult({
145
+ abi: UNISWAP_QUOTER_V2_ABI,
146
+ functionName: "quoteExactInputSingle",
147
+ data
148
+ });
149
+ const price = Number(formatUnits(amountOut, tokenOutDecimals));
150
+ return { price, amountOut, source: "uniswap" };
151
+ }
152
+ export {
153
+ getTokenPriceInAsset,
154
+ getTokenPricesInAsset
155
+ };
156
+ //# sourceMappingURL=price-SWNEGWO4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/price.ts"],"sourcesContent":["/**\n * Network-aware price quoting for portfolio valuation.\n *\n * Dispatches to the right DEX quoter based on network:\n * - Base / Base Sepolia → Uniswap QuoterV2 (struct params, 4-tuple return)\n * - Robinhood testnet → Direct pool slot0 (Synthra QuoterV2 can't resolve\n * proxy-deployed pools via CREATE2, so we read sqrtPriceX96 from the pool)\n *\n * Uniswap quoter uses eth_call (not view — it reverts internally).\n */\n\nimport type { Address } from \"viem\";\nimport { encodeFunctionData, decodeFunctionResult, parseUnits, formatUnits } from \"viem\";\nimport { getPublicClient } from \"./client.js\";\nimport { getNetwork } from \"./network.js\";\nimport { UNISWAP, SYNTHRA } from \"./addresses.js\";\nimport { UNISWAP_QUOTER_V2_ABI } from \"./abis.js\";\n\nexport interface TokenPrice {\n price: number; // 1 token = X asset units (human-readable)\n amountOut: bigint; // raw quoter output\n source: \"uniswap\" | \"synthra\";\n}\n\nconst ZERO: Address = \"0x0000000000000000000000000000000000000000\";\n\n/**\n * Get the price of one token denominated in the strategy's asset.\n * Uses the appropriate DEX quoter for the current network.\n */\nexport async function getTokenPriceInAsset(params: {\n token: Address;\n tokenDecimals: number;\n asset: Address;\n assetDecimals: number;\n feeTier: number;\n}): Promise<TokenPrice> {\n const { token, tokenDecimals, asset, assetDecimals, feeTier } = params;\n\n // Short-circuit: token IS the asset\n if (token.toLowerCase() === asset.toLowerCase()) {\n return { price: 1.0, amountOut: parseUnits(\"1\", assetDecimals), source: \"uniswap\" };\n }\n\n const network = getNetwork();\n const client = getPublicClient();\n\n if (network === \"robinhood-testnet\") {\n return quoteSynthra(client, token, tokenDecimals, asset, assetDecimals, feeTier);\n }\n\n return quoteUniswap(client, token, tokenDecimals, asset, assetDecimals, feeTier);\n}\n\n/**\n * Batch price lookup — parallel, graceful failure (null per failed quote).\n */\nexport async function getTokenPricesInAsset(params: {\n tokens: { token: Address; tokenDecimals: number; feeTier: number }[];\n asset: Address;\n assetDecimals: number;\n}): Promise<(TokenPrice | null)[]> {\n const { tokens, asset, assetDecimals } = params;\n\n const results = await Promise.allSettled(\n tokens.map((t) =>\n getTokenPriceInAsset({\n token: t.token,\n tokenDecimals: t.tokenDecimals,\n asset,\n assetDecimals,\n feeTier: t.feeTier,\n }),\n ),\n );\n\n return results.map((r) => (r.status === \"fulfilled\" ? r.value : null));\n}\n\n// ── Synthra direct pool pricing (Robinhood testnet) ──\n//\n// The Synthra QuoterV2 can't resolve proxy-deployed pools via CREATE2 (same\n// issue that led to SynthraDirectAdapter). Instead we look up the pool via\n// factory.getPool(), read slot0.sqrtPriceX96, and derive the spot price.\n\nconst POOL_SLOT0_ABI = [\n {\n type: \"function\" as const,\n name: \"slot0\",\n inputs: [],\n outputs: [\n { name: \"sqrtPriceX96\", type: \"uint160\" as const },\n { name: \"tick\", type: \"int24\" as const },\n { name: \"observationIndex\", type: \"uint16\" as const },\n { name: \"observationCardinality\", type: \"uint16\" as const },\n { name: \"observationCardinalityNext\", type: \"uint16\" as const },\n { name: \"feeProtocol\", type: \"uint8\" as const },\n { name: \"unlocked\", type: \"bool\" as const },\n ],\n stateMutability: \"view\" as const,\n },\n] as const;\n\nconst POOL_TOKEN0_ABI = [\n {\n type: \"function\" as const,\n name: \"token0\",\n inputs: [],\n outputs: [{ name: \"\", type: \"address\" as const }],\n stateMutability: \"view\" as const,\n },\n] as const;\n\nconst FACTORY_GET_POOL_ABI = [\n {\n type: \"function\" as const,\n name: \"getPool\",\n inputs: [\n { name: \"tokenA\", type: \"address\" as const },\n { name: \"tokenB\", type: \"address\" as const },\n { name: \"fee\", type: \"uint24\" as const },\n ],\n outputs: [{ name: \"\", type: \"address\" as const }],\n stateMutability: \"view\" as const,\n },\n] as const;\n\n/**\n * Derive a spot price from a Uniswap V3 / Synthra V3 pool's sqrtPriceX96.\n *\n * sqrtPriceX96 = sqrt(token1/token0) × 2^96\n * price (token1 per token0) = (sqrtPriceX96 / 2^96)^2\n *\n * Returns the price of `tokenIn` denominated in `tokenOut`.\n */\nfunction priceFromSqrtPriceX96(\n sqrtPriceX96: bigint,\n token0Decimals: number,\n token1Decimals: number,\n tokenInIsToken0: boolean,\n): number {\n const Q96 = 2 ** 96;\n const ratio = Number(sqrtPriceX96) / Q96;\n // raw price = token1 per token0 (adjusted for same-decimal tokens only)\n const rawPrice = ratio * ratio;\n // decimal adjustment: token0Decimals - token1Decimals\n const decimalAdjust = 10 ** (token0Decimals - token1Decimals);\n const token1PerToken0 = rawPrice * decimalAdjust;\n\n // If tokenIn is token0, price = how many token1 per token0 → token1PerToken0\n // If tokenIn is token1, price = how many token0 per token1 → 1/token1PerToken0\n return tokenInIsToken0 ? token1PerToken0 : 1 / token1PerToken0;\n}\n\nasync function quoteSynthra(\n client: ReturnType<typeof getPublicClient>,\n tokenIn: Address,\n tokenInDecimals: number,\n tokenOut: Address,\n tokenOutDecimals: number,\n fee: number,\n): Promise<TokenPrice> {\n const factoryAddr = SYNTHRA().FACTORY;\n if (factoryAddr === ZERO) {\n throw new Error(\"Synthra Factory not deployed on this network\");\n }\n\n // 1. Resolve pool via factory.getPool()\n const pool = await client.readContract({\n address: factoryAddr,\n abi: FACTORY_GET_POOL_ABI,\n functionName: \"getPool\",\n args: [tokenIn, tokenOut, fee],\n });\n\n if (!pool || pool === ZERO) {\n throw new Error(`No Synthra pool for ${tokenIn}↔${tokenOut} fee=${fee}`);\n }\n\n // 2. Read slot0 and token0 from the pool\n const [slot0, token0] = await Promise.all([\n client.readContract({ address: pool, abi: POOL_SLOT0_ABI, functionName: \"slot0\" }),\n client.readContract({ address: pool, abi: POOL_TOKEN0_ABI, functionName: \"token0\" }),\n ]);\n\n const sqrtPriceX96 = slot0[0];\n if (sqrtPriceX96 === 0n) {\n throw new Error(`Pool ${pool} not initialized (sqrtPriceX96 = 0)`);\n }\n\n // 3. Determine token ordering\n const tokenInIsToken0 = tokenIn.toLowerCase() === token0.toLowerCase();\n const t0Decimals = tokenInIsToken0 ? tokenInDecimals : tokenOutDecimals;\n const t1Decimals = tokenInIsToken0 ? tokenOutDecimals : tokenInDecimals;\n\n const price = priceFromSqrtPriceX96(sqrtPriceX96, t0Decimals, t1Decimals, tokenInIsToken0);\n\n // Compute amountOut for 1 tokenIn\n const oneToken = parseUnits(\"1\", tokenInDecimals);\n const amountOut = BigInt(Math.round(price * Number(parseUnits(\"1\", tokenOutDecimals))));\n\n return { price, amountOut, source: \"synthra\" };\n}\n\n// ── Uniswap QuoterV2 (Base / Base Sepolia) ──\n\nasync function quoteUniswap(\n client: ReturnType<typeof getPublicClient>,\n tokenIn: Address,\n tokenInDecimals: number,\n tokenOut: Address,\n tokenOutDecimals: number,\n fee: number,\n): Promise<TokenPrice> {\n const quoterAddr = UNISWAP().QUOTER_V2;\n if (quoterAddr === ZERO) {\n throw new Error(\"Uniswap QuoterV2 not deployed on this network\");\n }\n\n const oneToken = parseUnits(\"1\", tokenInDecimals);\n\n const calldata = encodeFunctionData({\n abi: UNISWAP_QUOTER_V2_ABI,\n functionName: \"quoteExactInputSingle\",\n args: [\n {\n tokenIn,\n tokenOut,\n amountIn: oneToken,\n fee,\n sqrtPriceLimitX96: 0n,\n },\n ],\n });\n\n const { data } = await client.call({ to: quoterAddr, data: calldata });\n\n if (!data) {\n throw new Error(`Uniswap quoter returned no data for ${tokenIn}→${tokenOut} fee=${fee}`);\n }\n\n const [amountOut] = decodeFunctionResult({\n abi: UNISWAP_QUOTER_V2_ABI,\n functionName: \"quoteExactInputSingle\",\n data,\n }) as [bigint, bigint, number, bigint];\n\n const price = Number(formatUnits(amountOut, tokenOutDecimals));\n return { price, amountOut, source: \"uniswap\" };\n}\n"],"mappings":"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","names":[]}
@@ -2,13 +2,13 @@ import {
2
2
  MESSARI_COST_ESTIMATE,
3
3
  NANSEN_COST_ESTIMATE,
4
4
  getResearchProvider
5
- } from "./chunk-4YIE7GHL.js";
5
+ } from "./chunk-66VFSLPS.js";
6
6
  import {
7
7
  formatContractError,
8
8
  getAccount
9
- } from "./chunk-ASJTFFZH.js";
10
- import "./chunk-LVPBJ7MI.js";
11
- import "./chunk-B42ZCCZ7.js";
9
+ } from "./chunk-SBVHGUQJ.js";
10
+ import "./chunk-GWIW6DDG.js";
11
+ import "./chunk-RGOTBJIN.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-XKHXATPQ.js");
19
+ return import("./xmtp-YNOGIZ4G.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-22YLNQ2C.js");
86
- const { createResearchAttestation, getEasScanUrl } = await import("./eas-X5UIEM6D.js");
86
+ const { createResearchAttestation, getEasScanUrl } = await import("./eas-YZIHYQAI.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-BGS5MX4D.js");
116
- const { getChain } = await import("./network-UTSOP7EF.js");
115
+ const { getChainContracts } = await import("./config-YXZKPSZB.js");
116
+ const { getChain } = await import("./network-QEMP3DSP.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-EWH43HFN.js.map
315
+ //# sourceMappingURL=research-PH77VSE4.js.map
@@ -0,0 +1,14 @@
1
+ import {
2
+ MessariProvider,
3
+ NansenProvider,
4
+ getResearchProvider
5
+ } from "./chunk-66VFSLPS.js";
6
+ import "./chunk-SBVHGUQJ.js";
7
+ import "./chunk-GWIW6DDG.js";
8
+ import "./chunk-RGOTBJIN.js";
9
+ export {
10
+ MessariProvider,
11
+ NansenProvider,
12
+ getResearchProvider
13
+ };
14
+ //# sourceMappingURL=research-ZX7T2ZIJ.js.map
@@ -4,27 +4,27 @@ import {
4
4
  resetSession,
5
5
  updateSession
6
6
  } from "./chunk-W2ZUPR3W.js";
7
+ import {
8
+ resolveSyndicate
9
+ } from "./chunk-QBNWBWD3.js";
7
10
  import {
8
11
  PROPOSAL_STATES,
9
12
  getProposal
10
- } from "./chunk-VO6DCRWV.js";
13
+ } from "./chunk-7PCI3WHQ.js";
11
14
  import {
12
15
  resolveMetadataSummary
13
16
  } from "./chunk-KEUEO4UE.js";
14
- import {
15
- resolveSyndicate
16
- } from "./chunk-G7JQRLQE.js";
17
17
  import {
18
18
  SHERWOOD,
19
19
  SYNDICATE_VAULT_ABI
20
- } from "./chunk-JR5K5WVR.js";
20
+ } from "./chunk-ZPLUJT4V.js";
21
21
  import {
22
22
  getPublicClient
23
- } from "./chunk-ASJTFFZH.js";
24
- import "./chunk-LVPBJ7MI.js";
23
+ } from "./chunk-SBVHGUQJ.js";
24
+ import "./chunk-GWIW6DDG.js";
25
25
  import {
26
26
  loadConfig
27
- } from "./chunk-B42ZCCZ7.js";
27
+ } from "./chunk-RGOTBJIN.js";
28
28
 
29
29
  // src/commands/session.ts
30
30
  import chalk from "chalk";
@@ -185,7 +185,7 @@ async function enrichProposalEvents(events, metadataCache) {
185
185
 
186
186
  // src/commands/session.ts
187
187
  async function loadXmtp() {
188
- return import("./xmtp-XKHXATPQ.js");
188
+ return import("./xmtp-YNOGIZ4G.js");
189
189
  }
190
190
  async function resolveGovernor(vaultAddress) {
191
191
  const client = getPublicClient();
@@ -422,8 +422,8 @@ function registerSessionCommands(program) {
422
422
  );
423
423
  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) => {
424
424
  const { isOpenClaw, registerSyndicateCrons, unregisterSyndicateCrons, getSyndicateCronStatus } = await import("./cron-EOULYO3R.js");
425
- const { isTestnet } = await import("./network-UTSOP7EF.js");
426
- const { getNotifyTo } = await import("./config-BGS5MX4D.js");
425
+ const { isTestnet } = await import("./network-QEMP3DSP.js");
426
+ const { getNotifyTo } = await import("./config-YXZKPSZB.js");
427
427
  if (!isOpenClaw()) {
428
428
  console.log(chalk.yellow("Not running on OpenClaw \u2014 cron commands unavailable"));
429
429
  console.log(chalk.dim(` Set up your own scheduler: sherwood session check ${name} --stream`));
@@ -464,4 +464,4 @@ function registerSessionCommands(program) {
464
464
  export {
465
465
  registerSessionCommands
466
466
  };
467
- //# sourceMappingURL=session-XZNKLK3I.js.map
467
+ //# sourceMappingURL=session-NMA4RLKC.js.map
@@ -1,27 +1,27 @@
1
1
  import {
2
2
  getResearchProvider
3
- } from "./chunk-4YIE7GHL.js";
3
+ } from "./chunk-66VFSLPS.js";
4
4
  import {
5
5
  UniswapProvider,
6
6
  chatCompletion,
7
7
  getQuote
8
- } from "./chunk-Y7LS6DEF.js";
8
+ } from "./chunk-VQIFAXUY.js";
9
9
  import {
10
10
  ERC20_ABI,
11
11
  TOKENS
12
- } from "./chunk-JR5K5WVR.js";
12
+ } from "./chunk-ZPLUJT4V.js";
13
13
  import {
14
14
  formatContractError,
15
15
  getAccount,
16
16
  getPublicClient
17
- } from "./chunk-ASJTFFZH.js";
17
+ } from "./chunk-SBVHGUQJ.js";
18
18
  import {
19
19
  getExplorerUrl
20
- } from "./chunk-LVPBJ7MI.js";
20
+ } from "./chunk-GWIW6DDG.js";
21
21
  import {
22
22
  loadConfig,
23
23
  saveConfig
24
- } from "./chunk-B42ZCCZ7.js";
24
+ } from "./chunk-RGOTBJIN.js";
25
25
 
26
26
  // src/commands/trade.ts
27
27
  import chalk from "chalk";
@@ -330,7 +330,7 @@ async function getCurrentPrice(tokenAddress, tokenDecimals, feeTier) {
330
330
  // src/commands/trade.ts
331
331
  var uniswap = new UniswapProvider();
332
332
  async function loadXmtp() {
333
- return import("./xmtp-XKHXATPQ.js");
333
+ return import("./xmtp-YNOGIZ4G.js");
334
334
  }
335
335
  var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
336
336
  var KNOWN_MEMECOINS = {
@@ -514,7 +514,7 @@ function registerTradeCommands(program) {
514
514
  }
515
515
  const wrapSpinner = ora("Wrapping ETH \u2192 WETH...").start();
516
516
  try {
517
- const { writeContractWithRetry, waitForReceipt: waitForReceiptFn } = await import("./client-SCIY6SUP.js");
517
+ const { writeContractWithRetry, waitForReceipt: waitForReceiptFn } = await import("./client-CWQFYZ5Z.js");
518
518
  const wethAddr = TOKENS().WETH;
519
519
  const wrapHash = await writeContractWithRetry({
520
520
  address: wethAddr,
@@ -523,7 +523,7 @@ function registerTradeCommands(program) {
523
523
  args: [],
524
524
  value: inputAmount,
525
525
  account,
526
- chain: (await import("./network-UTSOP7EF.js")).getChain()
526
+ chain: (await import("./network-QEMP3DSP.js")).getChain()
527
527
  });
528
528
  await waitForReceiptFn(wrapHash);
529
529
  wrapSpinner.succeed(`Wrapped ${opts.amount} ETH \u2192 WETH`);
@@ -637,9 +637,9 @@ function registerTradeCommands(program) {
637
637
  }
638
638
  console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash))}`);
639
639
  try {
640
- const { createTradeAttestation, getEasScanUrl } = await import("./eas-X5UIEM6D.js");
641
- const { getChainContracts } = await import("./config-BGS5MX4D.js");
642
- const { getChain: getActiveChain } = await import("./network-UTSOP7EF.js");
640
+ const { createTradeAttestation, getEasScanUrl } = await import("./eas-YZIHYQAI.js");
641
+ const { getChainContracts } = await import("./config-YXZKPSZB.js");
642
+ const { getChain: getActiveChain } = await import("./network-QEMP3DSP.js");
643
643
  const vaultRecipient = getChainContracts(getActiveChain().id).vault;
644
644
  const { uid } = await createTradeAttestation(
645
645
  inputAddr,
@@ -765,9 +765,9 @@ function registerTradeCommands(program) {
765
765
  }
766
766
  console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash))}`);
767
767
  try {
768
- const { createTradeAttestation, getEasScanUrl } = await import("./eas-X5UIEM6D.js");
769
- const { getChainContracts } = await import("./config-BGS5MX4D.js");
770
- const { getChain: getActiveChain } = await import("./network-UTSOP7EF.js");
768
+ const { createTradeAttestation, getEasScanUrl } = await import("./eas-YZIHYQAI.js");
769
+ const { getChainContracts } = await import("./config-YXZKPSZB.js");
770
+ const { getChain: getActiveChain } = await import("./network-QEMP3DSP.js");
771
771
  const vaultRecipient = getChainContracts(getActiveChain().id).vault;
772
772
  const { uid } = await createTradeAttestation(
773
773
  tokenAddr,
@@ -942,4 +942,4 @@ function sleep(ms) {
942
942
  export {
943
943
  registerTradeCommands
944
944
  };
945
- //# sourceMappingURL=trade-VKSXEHWW.js.map
945
+ //# sourceMappingURL=trade-N2A74UPX.js.map
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  getTextRecord
3
- } from "./chunk-G7JQRLQE.js";
4
- import "./chunk-JR5K5WVR.js";
3
+ } from "./chunk-QBNWBWD3.js";
4
+ import "./chunk-ZPLUJT4V.js";
5
5
  import {
6
6
  getAccount
7
- } from "./chunk-ASJTFFZH.js";
7
+ } from "./chunk-SBVHGUQJ.js";
8
8
  import {
9
9
  getChainConfig
10
- } from "./chunk-LVPBJ7MI.js";
10
+ } from "./chunk-GWIW6DDG.js";
11
11
  import {
12
12
  cacheGroupId,
13
13
  getCachedGroupId,
14
14
  loadConfig,
15
15
  saveConfig
16
- } from "./chunk-B42ZCCZ7.js";
16
+ } from "./chunk-RGOTBJIN.js";
17
17
 
18
18
  // src/lib/xmtp.ts
19
19
  import fs from "fs";
@@ -275,4 +275,4 @@ export {
275
275
  sendReaction,
276
276
  streamMessages
277
277
  };
278
- //# sourceMappingURL=xmtp-XKHXATPQ.js.map
278
+ //# sourceMappingURL=xmtp-YNOGIZ4G.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sherwoodagent/cli",
3
- "version": "0.22.8",
3
+ "version": "0.25.1",
4
4
  "description": "CLI for agent-managed investment syndicates — onchain DeFi syndicates with XMTP chat",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/config.ts"],"sourcesContent":["/**\n * Local config management — ~/.sherwood/config.json\n *\n * Stores group ID cache, per-chain contract addresses, and wallet config.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst CONFIG_DIR = path.join(os.homedir(), \".sherwood\");\nconst CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/** Per-chain user-specific addresses (stored by chainId). */\nexport interface ChainContracts {\n vault?: string; // user's default vault address\n}\n\n/** A syndicate the agent belongs to (creator or member). */\nexport interface SyndicateMembership {\n subdomain: string;\n vault: string;\n role: \"creator\" | \"agent\";\n}\n\nexport interface SherwoodConfig {\n dbEncryptionKey?: string; // legacy — no longer used, XMTP CLI manages its own DB\n privateKey?: string; // wallet private key (0x-prefixed)\n xmtpInboxId?: string;\n groupCache: Record<string, string>; // subdomain → XMTP group ID\n veniceApiKey?: string; // Venice AI inference API key\n agentId?: number; // ERC-8004 identity token ID\n contracts?: Record<string, ChainContracts>; // chainId → user addresses\n syndicates?: Record<string, SyndicateMembership[]>; // chainId → syndicate memberships\n primarySyndicate?: Record<string, string>; // chainId → subdomain of the active syndicate\n rpc?: Record<string, string>; // network name → custom RPC URL\n notifyTo?: string; // destination for cron summaries (Telegram chat ID, phone, etc.)\n uniswapApiKey?: string; // Uniswap Trading API key (from developers.uniswap.org)\n positions?: unknown[]; // open trade positions (typed in positions.ts)\n closedPositions?: unknown[]; // historical closed positions\n _xmtpMigrated?: boolean; // one-time flag: revoked stale installations from ~/.xmtp/ era\n}\n\nexport function loadConfig(): SherwoodConfig {\n if (fs.existsSync(CONFIG_PATH)) {\n return JSON.parse(fs.readFileSync(CONFIG_PATH, \"utf-8\"));\n }\n\n return { groupCache: {} };\n}\n\nexport function saveConfig(config: SherwoodConfig): void {\n fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });\n}\n\nexport function cacheGroupId(subdomain: string, groupId: string): void {\n const config = loadConfig();\n config.groupCache[subdomain] = groupId;\n saveConfig(config);\n}\n\nexport function getCachedGroupId(subdomain: string): string | undefined {\n const config = loadConfig();\n return config.groupCache[subdomain];\n}\n\nexport function setVeniceApiKey(apiKey: string): void {\n const config = loadConfig();\n config.veniceApiKey = apiKey;\n saveConfig(config);\n}\n\nexport function getVeniceApiKey(): string | undefined {\n return loadConfig().veniceApiKey ?? process.env.VENICE_API_KEY;\n}\n\nexport function setAgentId(agentId: number): void {\n const config = loadConfig();\n config.agentId = agentId;\n saveConfig(config);\n}\n\nexport function getAgentId(): number | undefined {\n return loadConfig().agentId;\n}\n\nexport function setPrivateKey(key: string): void {\n const config = loadConfig();\n config.privateKey = key.startsWith(\"0x\") ? key : `0x${key}`;\n saveConfig(config);\n}\n\nexport function getPrivateKey(): string | undefined {\n return loadConfig().privateKey;\n}\n\n// ── Uniswap Trading API Key ──\n\nexport function setUniswapApiKey(apiKey: string): void {\n const config = loadConfig();\n config.uniswapApiKey = apiKey;\n saveConfig(config);\n}\n\nexport function getUniswapApiKey(): string | undefined {\n return loadConfig().uniswapApiKey ?? process.env.UNISWAP_API_KEY;\n}\n\n// ── Per-network RPC URLs ──\n\nexport function getConfigRpcUrl(network: string): string | undefined {\n return loadConfig().rpc?.[network];\n}\n\nexport function setConfigRpcUrl(network: string, url: string): void {\n const config = loadConfig();\n if (!config.rpc) config.rpc = {};\n config.rpc[network] = url;\n saveConfig(config);\n}\n\n// ── Per-chain contract addresses ──\n\nexport function getChainContracts(chainId: number): ChainContracts {\n const config = loadConfig();\n return config.contracts?.[String(chainId)] ?? {};\n}\n\nexport function getNotifyTo(): string | undefined {\n return loadConfig().notifyTo;\n}\n\nexport function setNotifyTo(id: string): void {\n const config = loadConfig();\n config.notifyTo = id;\n saveConfig(config);\n}\n\nexport function setChainContract(\n chainId: number,\n key: keyof ChainContracts,\n value: string,\n): void {\n const config = loadConfig();\n if (!config.contracts) config.contracts = {};\n const cid = String(chainId);\n if (!config.contracts[cid]) config.contracts[cid] = {};\n config.contracts[cid][key] = value;\n saveConfig(config);\n}\n\n// ── Syndicate Membership ──\n\n/**\n * Add (or update) a syndicate membership. Deduplicates by subdomain.\n * Automatically sets it as primary if it's the first syndicate on this chain.\n */\nexport function addSyndicate(\n chainId: number,\n membership: SyndicateMembership,\n): void {\n const config = loadConfig();\n const cid = String(chainId);\n if (!config.syndicates) config.syndicates = {};\n if (!config.syndicates[cid]) config.syndicates[cid] = [];\n\n // Upsert — replace existing entry with same subdomain\n const idx = config.syndicates[cid].findIndex(\n (s) => s.subdomain === membership.subdomain,\n );\n if (idx >= 0) {\n config.syndicates[cid][idx] = membership;\n } else {\n config.syndicates[cid].push(membership);\n }\n\n // Auto-set as primary if it's the first syndicate (or no primary set)\n if (!config.primarySyndicate) config.primarySyndicate = {};\n if (!config.primarySyndicate[cid]) {\n config.primarySyndicate[cid] = membership.subdomain;\n }\n\n saveConfig(config);\n}\n\n/** Get all syndicate memberships for a chain. */\nexport function getSyndicates(chainId: number): SyndicateMembership[] {\n const config = loadConfig();\n return config.syndicates?.[String(chainId)] ?? [];\n}\n\n/** Set which syndicate is the active/primary one for CLI commands. */\nexport function setPrimarySyndicate(\n chainId: number,\n subdomain: string,\n): void {\n const config = loadConfig();\n if (!config.primarySyndicate) config.primarySyndicate = {};\n config.primarySyndicate[String(chainId)] = subdomain;\n saveConfig(config);\n}\n\n/**\n * Get the primary syndicate for this chain.\n * Falls back to ChainContracts.vault for backwards compat with older configs.\n */\nexport function getPrimarySyndicate(\n chainId: number,\n): SyndicateMembership | undefined {\n const config = loadConfig();\n const cid = String(chainId);\n\n // Check new syndicates store first\n const primarySubdomain = config.primarySyndicate?.[cid];\n if (primarySubdomain && config.syndicates?.[cid]) {\n const found = config.syndicates[cid].find(\n (s) => s.subdomain === primarySubdomain,\n );\n if (found) return found;\n }\n\n // Backwards compat: if syndicates list has entries but no primary, use the first one\n if (config.syndicates?.[cid]?.length) {\n return config.syndicates[cid][0];\n }\n\n // Legacy fallback: config only has a vault address, no subdomain\n const vault = config.contracts?.[cid]?.vault;\n if (vault) {\n return { subdomain: \"\", vault, role: \"creator\" };\n }\n\n return undefined;\n}\n"],"mappings":"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","names":[]}