@sherwoodagent/cli 0.65.2 → 0.72.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.
Files changed (93) hide show
  1. package/dist/{abis-4HWN2E3H.js → abis-KP7M4IPM.js} +4 -2
  2. package/dist/addresses-HJDYFLWZ.js +45 -0
  3. package/dist/{agent-AETROOEW.js → agent-6OJKQVEG.js} +10 -10
  4. package/dist/{agent-AETROOEW.js.map → agent-6OJKQVEG.js.map} +1 -1
  5. package/dist/{chat-JSSTSE4K.js → chat-2DKD3QPA.js} +37 -36
  6. package/dist/chat-2DKD3QPA.js.map +1 -0
  7. package/dist/{chunk-O4G3SBCI.js → chunk-3YUMQGJD.js} +109 -129
  8. package/dist/chunk-3YUMQGJD.js.map +1 -0
  9. package/dist/{chunk-W2ZUPR3W.js → chunk-5DWB5Y7G.js} +9 -9
  10. package/dist/chunk-5DWB5Y7G.js.map +1 -0
  11. package/dist/{chunk-IL43IMOC.js → chunk-6SRIVGAV.js} +23 -18
  12. package/dist/chunk-6SRIVGAV.js.map +1 -0
  13. package/dist/{chunk-UCQEEF3Z.js → chunk-ASF2WTSW.js} +6 -6
  14. package/dist/chunk-ASF2WTSW.js.map +1 -0
  15. package/dist/{chunk-YZHZSBES.js → chunk-BQMOE4FJ.js} +144 -37
  16. package/dist/chunk-BQMOE4FJ.js.map +1 -0
  17. package/dist/{chunk-2W4KGGPU.js → chunk-GSC7PVEO.js} +27 -27
  18. package/dist/chunk-GSC7PVEO.js.map +1 -0
  19. package/dist/chunk-GT3KCKF6.js +124 -0
  20. package/dist/chunk-GT3KCKF6.js.map +1 -0
  21. package/dist/{chunk-RO26TXXE.js → chunk-ITZT5HD4.js} +2 -2
  22. package/dist/{chunk-UBAX22KA.js → chunk-OZLX666C.js} +4 -4
  23. package/dist/chunk-OZLX666C.js.map +1 -0
  24. package/dist/{chunk-A6TOJEUD.js → chunk-PDUGL4EY.js} +106 -128
  25. package/dist/chunk-PDUGL4EY.js.map +1 -0
  26. package/dist/chunk-RNASYOQT.js +111 -0
  27. package/dist/chunk-RNASYOQT.js.map +1 -0
  28. package/dist/{chunk-VYF2TXPX.js → chunk-SVSJNZ5D.js} +6 -6
  29. package/dist/{chunk-YJSCIQUZ.js → chunk-UC5RMCB3.js} +6 -6
  30. package/dist/chunk-UC5RMCB3.js.map +1 -0
  31. package/dist/chunk-YLYANZMC.js +308 -0
  32. package/dist/chunk-YLYANZMC.js.map +1 -0
  33. package/dist/{chunk-RHRLYVKM.js → chunk-ZYXFKVYF.js} +25 -12
  34. package/dist/chunk-ZYXFKVYF.js.map +1 -0
  35. package/dist/{client-KBBFAMEI.js → client-K4JCNPVV.js} +4 -4
  36. package/dist/{config-REASKDIK.js → config-FAQO37S7.js} +10 -10
  37. package/dist/{cron-EOULYO3R.js → cron-QMLRWDJG.js} +19 -17
  38. package/dist/cron-QMLRWDJG.js.map +1 -0
  39. package/dist/{eas-K4ZBACYN.js → eas-ZXGUNYC5.js} +7 -7
  40. package/dist/{governor-2CDSMSG7.js → governor-CV5PITE3.js} +13 -13
  41. package/dist/index.js +1964 -1219
  42. package/dist/index.js.map +1 -1
  43. package/dist/{ipfs-FAYWUCS2.js → ipfs-VGWTUORE.js} +2 -2
  44. package/dist/{network-3ZU7UBDU.js → network-DHWEYXP2.js} +7 -7
  45. package/dist/{price-LCTECRQP.js → price-GOX6P4NT.js} +6 -6
  46. package/dist/{research-FUQOQWKK.js → research-6D7NNJX4.js} +22 -22
  47. package/dist/research-6D7NNJX4.js.map +1 -0
  48. package/dist/research-EFNVHU7G.js +14 -0
  49. package/dist/{session-FJB2RV2X.js → session-LDLW66K3.js} +28 -28
  50. package/dist/session-LDLW66K3.js.map +1 -0
  51. package/dist/{session-XAW5UIXP.js → session-XBQY7SBJ.js} +2 -2
  52. package/dist/{trade-U7H5IFH6.js → trade-7NNZXP5B.js} +34 -34
  53. package/dist/trade-7NNZXP5B.js.map +1 -0
  54. package/dist/vault-SIL4QL5U.js +54 -0
  55. package/dist/vault-SIL4QL5U.js.map +1 -0
  56. package/dist/{xmtp-6NJ4PW6L.js → xmtp-QNOVDLXX.js} +13 -12
  57. package/dist/xmtp-QNOVDLXX.js.map +1 -0
  58. package/package.json +4 -3
  59. package/dist/addresses-J2LMGUCT.js +0 -35
  60. package/dist/chat-JSSTSE4K.js.map +0 -1
  61. package/dist/chunk-2LRC7I33.js +0 -186
  62. package/dist/chunk-2LRC7I33.js.map +0 -1
  63. package/dist/chunk-2W4KGGPU.js.map +0 -1
  64. package/dist/chunk-A6TOJEUD.js.map +0 -1
  65. package/dist/chunk-GWMTAW6A.js +0 -494
  66. package/dist/chunk-GWMTAW6A.js.map +0 -1
  67. package/dist/chunk-IL43IMOC.js.map +0 -1
  68. package/dist/chunk-O4G3SBCI.js.map +0 -1
  69. package/dist/chunk-RHRLYVKM.js.map +0 -1
  70. package/dist/chunk-UBAX22KA.js.map +0 -1
  71. package/dist/chunk-UCQEEF3Z.js.map +0 -1
  72. package/dist/chunk-W2ZUPR3W.js.map +0 -1
  73. package/dist/chunk-YJSCIQUZ.js.map +0 -1
  74. package/dist/chunk-YZHZSBES.js.map +0 -1
  75. package/dist/cron-EOULYO3R.js.map +0 -1
  76. package/dist/research-FUQOQWKK.js.map +0 -1
  77. package/dist/research-XEXTHDT3.js +0 -14
  78. package/dist/session-FJB2RV2X.js.map +0 -1
  79. package/dist/trade-U7H5IFH6.js.map +0 -1
  80. package/dist/xmtp-6NJ4PW6L.js.map +0 -1
  81. /package/dist/{abis-4HWN2E3H.js.map → abis-KP7M4IPM.js.map} +0 -0
  82. /package/dist/{addresses-J2LMGUCT.js.map → addresses-HJDYFLWZ.js.map} +0 -0
  83. /package/dist/{chunk-RO26TXXE.js.map → chunk-ITZT5HD4.js.map} +0 -0
  84. /package/dist/{chunk-VYF2TXPX.js.map → chunk-SVSJNZ5D.js.map} +0 -0
  85. /package/dist/{client-KBBFAMEI.js.map → client-K4JCNPVV.js.map} +0 -0
  86. /package/dist/{config-REASKDIK.js.map → config-FAQO37S7.js.map} +0 -0
  87. /package/dist/{eas-K4ZBACYN.js.map → eas-ZXGUNYC5.js.map} +0 -0
  88. /package/dist/{governor-2CDSMSG7.js.map → governor-CV5PITE3.js.map} +0 -0
  89. /package/dist/{ipfs-FAYWUCS2.js.map → ipfs-VGWTUORE.js.map} +0 -0
  90. /package/dist/{network-3ZU7UBDU.js.map → network-DHWEYXP2.js.map} +0 -0
  91. /package/dist/{price-LCTECRQP.js.map → price-GOX6P4NT.js.map} +0 -0
  92. /package/dist/{research-XEXTHDT3.js.map → research-EFNVHU7G.js.map} +0 -0
  93. /package/dist/{session-XAW5UIXP.js.map → session-XBQY7SBJ.js.map} +0 -0
@@ -0,0 +1,308 @@
1
+ import {
2
+ getNetwork
3
+ } from "./chunk-RNASYOQT.js";
4
+
5
+ // src/lib/addresses.ts
6
+ import { isAddress } from "viem";
7
+
8
+ // src/lib/address-overrides.ts
9
+ import { readFileSync } from "fs";
10
+ var CHAINS_TO_CLI = {
11
+ SHERWOOD: {
12
+ SYNDICATE_FACTORY: "FACTORY",
13
+ SYNDICATE_GOVERNOR: "GOVERNOR",
14
+ GUARDIAN_REGISTRY: "GUARDIAN_REGISTRY",
15
+ STAKED_WOOD: "STAKED_WOOD",
16
+ WOOD: "WOOD_TOKEN",
17
+ WOOD_TOKEN: "WOOD_TOKEN",
18
+ STRATEGY_FACTORY: "STRATEGY_FACTORY"
19
+ },
20
+ STRATEGY_TEMPLATES: {
21
+ MOONWELL_SUPPLY_TEMPLATE: "MOONWELL_SUPPLY",
22
+ AERODROME_LP_TEMPLATE: "AERODROME_LP",
23
+ VENICE_INFERENCE_TEMPLATE: "VENICE_INFERENCE",
24
+ WSTETH_MOONWELL_TEMPLATE: "WSTETH_MOONWELL",
25
+ MAMO_YIELD_TEMPLATE: "MAMO_YIELD",
26
+ PORTFOLIO_TEMPLATE: "PORTFOLIO",
27
+ HYPERLIQUID_PERP_TEMPLATE: "HYPERLIQUID_PERP",
28
+ HYPERLIQUID_GRID_TEMPLATE: "HYPERLIQUID_GRID"
29
+ },
30
+ ENS: {
31
+ L2_REGISTRAR: "L2_REGISTRAR",
32
+ L2_REGISTRY: "L2_REGISTRY"
33
+ },
34
+ UNISWAP: {
35
+ UNISWAP_SWAP_ADAPTER: "SWAP_ADAPTER"
36
+ }
37
+ };
38
+ var cached;
39
+ function loadFile() {
40
+ if (cached !== void 0) return cached;
41
+ const file = process.env.SIM_ADDRESSES_FILE;
42
+ if (!file) {
43
+ cached = null;
44
+ return cached;
45
+ }
46
+ let raw;
47
+ try {
48
+ raw = JSON.parse(readFileSync(file, "utf8"));
49
+ } catch (err) {
50
+ throw new Error(
51
+ `SIM_ADDRESSES_FILE is set (${file}) but unreadable: ${err instanceof Error ? err.message : String(err)}`
52
+ );
53
+ }
54
+ const out = {};
55
+ for (const [k, v] of Object.entries(raw)) {
56
+ if (typeof v === "string" && v.startsWith("0x")) out[k] = v;
57
+ }
58
+ cached = out;
59
+ return cached;
60
+ }
61
+ function overridesFor(registry) {
62
+ const file = loadFile();
63
+ if (!file) return {};
64
+ const map = CHAINS_TO_CLI[registry];
65
+ const out = {};
66
+ for (const [chainsKey, cliKey] of Object.entries(map)) {
67
+ if (file[chainsKey]) out[cliKey] = file[chainsKey];
68
+ }
69
+ return out;
70
+ }
71
+
72
+ // src/lib/addresses.ts
73
+ var ZERO = "0x0000000000000000000000000000000000000000";
74
+ var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
75
+ var ROBINHOOD_TESTNET_TOKENS = {
76
+ USDC: ZERO,
77
+ // WETH resolves to Synthra's WETH9 (SYNTHRA_WETH): ALL stock DEX liquidity is
78
+ // denominated in it (e.g. the TSLA fee-3000 pool holds ~37.8 WETH here vs
79
+ // ~0.0003 in the canonical WETH pool). It is WETH9-style (payable deposit()),
80
+ // so --use-eth wrapping works against it. Canonical WETH kept as WETH_CANONICAL
81
+ // so the reference isn't lost.
82
+ WETH: "0x33e4191705c386532ba27cBF171Db86919200B94",
83
+ WETH_CANONICAL: "0x7943e237c7F95DA44E0301572D358911207852Fa",
84
+ cbETH: ZERO,
85
+ wstETH: ZERO,
86
+ cbBTC: ZERO,
87
+ DAI: ZERO,
88
+ AERO: ZERO,
89
+ TSLA: "0xC9f9c86933092BbbfFF3CCb4b105A4A94bf3Bd4E",
90
+ AMZN: "0x5884aD2f920c162CFBbACc88C9C51AA75eC09E02",
91
+ PLTR: "0x1FBE1a0e43594b3455993B5dE5Fd0A7A266298d0",
92
+ NFLX: "0x3b8262A63d25f0477c4DDE23F83cfe22Cb768C93",
93
+ AMD: "0x71178BAc73cBeb415514eB542a8995b82669778d"
94
+ };
95
+ var ROBINHOOD_TESTNET_MOONWELL = {
96
+ COMPTROLLER: ZERO,
97
+ mUSDC: ZERO,
98
+ mWETH: ZERO,
99
+ mCbETH: ZERO,
100
+ mWstETH: ZERO,
101
+ mCbBTC: ZERO,
102
+ mDAI: ZERO,
103
+ mAERO: ZERO
104
+ };
105
+ var ROBINHOOD_TESTNET_UNISWAP = {
106
+ SWAP_ROUTER: "0x3Ce954107b1A675826B33bF23060Dd655e3758fE",
107
+ QUOTER_V2: "0xb3C009aECAeDd5ccC62Ec12eDAAA55F19C4A1eFb",
108
+ SWAP_ADAPTER: "0x4fc3492117cC3bbcE0b210D22a8DC244f9d86490"
109
+ };
110
+ var ROBINHOOD_TESTNET_SYNTHRA = {
111
+ ROUTER: "0x3Ce954107b1A675826B33bF23060Dd655e3758fE",
112
+ QUOTER: "0x231606c321A99DE81e28fE48B07a93F1ba49e713",
113
+ FACTORY: "0x911b4000D3422F482F4062a913885f7b035382Df"
114
+ };
115
+ var ROBINHOOD_TESTNET_CHAINLINK = {
116
+ VERIFIER_PROXY: "0x72790f9eB82db492a7DDb6d2af22A270Dcc3Db64",
117
+ WSTETH_ETH_FEED: ZERO
118
+ };
119
+ var ROBINHOOD_STOCK_ROUTES = {};
120
+ var ROBINHOOD_NON_EQUITY_FEEDS = /* @__PURE__ */ new Set(["ETH", "WETH", "BTC", "LINK", "USDG", "USDC"]);
121
+ function isRobinhoodEquityFeed(symbol) {
122
+ return !ROBINHOOD_NON_EQUITY_FEEDS.has(symbol.toUpperCase());
123
+ }
124
+ function symbolForTokenAddress(address, tokens) {
125
+ if (!address || address.toLowerCase() === ZERO.toLowerCase()) return void 0;
126
+ const want = address.toLowerCase();
127
+ for (const [symbol, addr] of Object.entries(tokens)) {
128
+ if (addr && addr !== ZERO && addr.toLowerCase() === want) return symbol;
129
+ }
130
+ return void 0;
131
+ }
132
+ function robinhoodEquityMaxAge(tokenSpec, tokens, equityMaxAgeSeconds) {
133
+ let symbol;
134
+ if (isAddress(tokenSpec)) {
135
+ symbol = symbolForTokenAddress(tokenSpec, tokens);
136
+ if (!symbol) return { maxPriceAge: 0, unknownAddress: true };
137
+ } else {
138
+ symbol = tokenSpec;
139
+ }
140
+ return {
141
+ maxPriceAge: isRobinhoodEquityFeed(symbol) ? equityMaxAgeSeconds : 0,
142
+ unknownAddress: false
143
+ };
144
+ }
145
+ var ROBINHOOD_TESTNET_INFRA = {
146
+ MULTICALL3: "0xcA11bde05977b3631167028862bE2a173976CA11"
147
+ // Deterministic, same everywhere
148
+ };
149
+ var ROBINHOOD_TESTNET_ENS = {
150
+ L2_REGISTRAR: ZERO,
151
+ L2_REGISTRY: ZERO
152
+ };
153
+ var ROBINHOOD_TESTNET_AGENT_REGISTRY = {
154
+ IDENTITY_REGISTRY: ZERO,
155
+ REPUTATION_REGISTRY: ZERO
156
+ };
157
+ var ROBINHOOD_TESTNET_SHERWOOD = {
158
+ FACTORY: "0xB9E71Fb33075328d6e94eCFFf8a8629D6d057cce",
159
+ GOVERNOR: ZERO,
160
+ // zeroed since the post-#421 redeploy — always factory.governorOf(vault)
161
+ GUARDIAN_REGISTRY: "0x57f0fa384d0d7e2F234535d1235440312866872B",
162
+ STAKED_WOOD: "0x15F48A9f24c8ECaa8f03c28Ecd1a3b4784CdCb3c",
163
+ WOOD_TOKEN: "0xCCb4fB59cf40de1E23083037ee81Da1DD747D8d7",
164
+ STRATEGY_FACTORY: "0xE1D082ef1CE17f9C6a46e97928337267BFF0C309",
165
+ MERKL_DISTRIBUTOR: ZERO
166
+ };
167
+ var ROBINHOOD_TESTNET_VENICE = {
168
+ VVV: ZERO,
169
+ STAKING: ZERO,
170
+ DIEM: ZERO
171
+ };
172
+ var ROBINHOOD_TESTNET_AERODROME = {
173
+ ROUTER: ZERO,
174
+ FACTORY: ZERO
175
+ };
176
+ var ROBINHOOD_TESTNET_STRATEGY_TEMPLATES = {
177
+ MOONWELL_SUPPLY: ZERO,
178
+ AERODROME_LP: ZERO,
179
+ VENICE_INFERENCE: ZERO,
180
+ WSTETH_MOONWELL: ZERO,
181
+ MAMO_YIELD: ZERO,
182
+ HYPERLIQUID_PERP: ZERO,
183
+ HYPERLIQUID_GRID: ZERO,
184
+ // V2 redeploy PORTFOLIO_TEMPLATE (chain 46630).
185
+ PORTFOLIO: "0x67420Cc504d70a42Adfd8867d878afe0978C7d10"
186
+ };
187
+ var ROBINHOOD_TESTNET_EAS = {
188
+ EAS: ZERO,
189
+ SCHEMA_REGISTRY: ZERO
190
+ };
191
+ var ROBINHOOD_TESTNET_EAS_SCHEMAS = {
192
+ SYNDICATE_JOIN_REQUEST: ZERO_BYTES32,
193
+ AGENT_APPROVED: ZERO_BYTES32,
194
+ X402_RESEARCH: ZERO_BYTES32,
195
+ VENICE_PROVISION: ZERO_BYTES32,
196
+ VENICE_INFERENCE: ZERO_BYTES32,
197
+ TRADE_EXECUTED: ZERO_BYTES32
198
+ };
199
+ var TOKEN_REGISTRY = {
200
+ "robinhood-testnet": ROBINHOOD_TESTNET_TOKENS
201
+ };
202
+ var MOONWELL_REGISTRY = {
203
+ "robinhood-testnet": ROBINHOOD_TESTNET_MOONWELL
204
+ };
205
+ var UNISWAP_REGISTRY = {
206
+ "robinhood-testnet": ROBINHOOD_TESTNET_UNISWAP
207
+ };
208
+ var INFRA_REGISTRY = {
209
+ "robinhood-testnet": ROBINHOOD_TESTNET_INFRA
210
+ };
211
+ var ENS_REGISTRY = {
212
+ "robinhood-testnet": ROBINHOOD_TESTNET_ENS
213
+ };
214
+ var AGENT_REGISTRY_MAP = {
215
+ "robinhood-testnet": ROBINHOOD_TESTNET_AGENT_REGISTRY
216
+ };
217
+ var SHERWOOD_REGISTRY = {
218
+ "robinhood-testnet": ROBINHOOD_TESTNET_SHERWOOD
219
+ };
220
+ var VENICE_REGISTRY = {
221
+ "robinhood-testnet": ROBINHOOD_TESTNET_VENICE
222
+ };
223
+ var AERODROME_REGISTRY = {
224
+ "robinhood-testnet": ROBINHOOD_TESTNET_AERODROME
225
+ };
226
+ var STRATEGY_TEMPLATE_REGISTRY = {
227
+ "robinhood-testnet": ROBINHOOD_TESTNET_STRATEGY_TEMPLATES
228
+ };
229
+ var SYNTHRA_REGISTRY = {
230
+ "robinhood-testnet": ROBINHOOD_TESTNET_SYNTHRA
231
+ };
232
+ var CHAINLINK_REGISTRY = {
233
+ "robinhood-testnet": ROBINHOOD_TESTNET_CHAINLINK
234
+ };
235
+ var EAS_CONTRACT_REGISTRY = {
236
+ "robinhood-testnet": ROBINHOOD_TESTNET_EAS
237
+ };
238
+ var EAS_SCHEMA_REGISTRY = {
239
+ "robinhood-testnet": ROBINHOOD_TESTNET_EAS_SCHEMAS
240
+ };
241
+ function TOKENS() {
242
+ return TOKEN_REGISTRY[getNetwork()];
243
+ }
244
+ function MOONWELL() {
245
+ return MOONWELL_REGISTRY[getNetwork()];
246
+ }
247
+ function UNISWAP() {
248
+ return { ...UNISWAP_REGISTRY[getNetwork()], ...overridesFor("UNISWAP") };
249
+ }
250
+ function INFRA() {
251
+ return INFRA_REGISTRY[getNetwork()];
252
+ }
253
+ function ENS() {
254
+ return { ...ENS_REGISTRY[getNetwork()], ...overridesFor("ENS") };
255
+ }
256
+ function AGENT_REGISTRY() {
257
+ return AGENT_REGISTRY_MAP[getNetwork()];
258
+ }
259
+ function VENICE() {
260
+ return VENICE_REGISTRY[getNetwork()];
261
+ }
262
+ function SHERWOOD() {
263
+ return { ...SHERWOOD_REGISTRY[getNetwork()], ...overridesFor("SHERWOOD") };
264
+ }
265
+ function AERODROME() {
266
+ return AERODROME_REGISTRY[getNetwork()];
267
+ }
268
+ function STRATEGY_TEMPLATES() {
269
+ return {
270
+ ...STRATEGY_TEMPLATE_REGISTRY[getNetwork()],
271
+ ...overridesFor("STRATEGY_TEMPLATES")
272
+ };
273
+ }
274
+ function EAS_CONTRACTS() {
275
+ return EAS_CONTRACT_REGISTRY[getNetwork()];
276
+ }
277
+ function EAS_SCHEMAS() {
278
+ return EAS_SCHEMA_REGISTRY[getNetwork()];
279
+ }
280
+ function SYNTHRA() {
281
+ return SYNTHRA_REGISTRY[getNetwork()];
282
+ }
283
+ function CHAINLINK() {
284
+ return CHAINLINK_REGISTRY[getNetwork()];
285
+ }
286
+
287
+ export {
288
+ ROBINHOOD_STOCK_ROUTES,
289
+ ROBINHOOD_NON_EQUITY_FEEDS,
290
+ isRobinhoodEquityFeed,
291
+ symbolForTokenAddress,
292
+ robinhoodEquityMaxAge,
293
+ TOKENS,
294
+ MOONWELL,
295
+ UNISWAP,
296
+ INFRA,
297
+ ENS,
298
+ AGENT_REGISTRY,
299
+ VENICE,
300
+ SHERWOOD,
301
+ AERODROME,
302
+ STRATEGY_TEMPLATES,
303
+ EAS_CONTRACTS,
304
+ EAS_SCHEMAS,
305
+ SYNTHRA,
306
+ CHAINLINK
307
+ };
308
+ //# sourceMappingURL=chunk-YLYANZMC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/addresses.ts","../src/lib/address-overrides.ts"],"sourcesContent":["/**\n * Contract addresses by network.\n *\n * All exports are functions — they resolve at call time based on the\n * current network set via setNetwork(). This ensures --chain works\n * even when modules are imported before the flag is parsed.\n *\n * Current deployment target: Robinhood L2 testnet (chain 46630) only. Other\n * chains may be added back later; when they are, their address blocks + registry\n * entries re-enter here alongside the robinhood-testnet ones.\n *\n * Zero addresses = protocol not deployed on that chain. Strategies that\n * need them will fail at execution time with a clear allowlist error.\n */\n\nimport type { Address } from \"viem\";\nimport { isAddress } from \"viem\";\nimport { type Network, getNetwork } from \"./network.js\";\nimport { overridesFor } from \"./address-overrides.js\";\n\nconst ZERO: Address = \"0x0000000000000000000000000000000000000000\";\nconst ZERO_BYTES32 =\n \"0x0000000000000000000000000000000000000000000000000000000000000000\" as `0x${string}`;\n\n// ── Robinhood L2 Testnet (Arbitrum Orbit, chain 46630) ──\n// Synthra DEX for swaps. Stock tokens available.\n\nconst ROBINHOOD_TESTNET_TOKENS = {\n USDC: ZERO,\n // WETH resolves to Synthra's WETH9 (SYNTHRA_WETH): ALL stock DEX liquidity is\n // denominated in it (e.g. the TSLA fee-3000 pool holds ~37.8 WETH here vs\n // ~0.0003 in the canonical WETH pool). It is WETH9-style (payable deposit()),\n // so --use-eth wrapping works against it. Canonical WETH kept as WETH_CANONICAL\n // so the reference isn't lost.\n WETH: \"0x33e4191705c386532ba27cBF171Db86919200B94\" as Address,\n WETH_CANONICAL: \"0x7943e237c7F95DA44E0301572D358911207852Fa\" as Address,\n cbETH: ZERO,\n wstETH: ZERO,\n cbBTC: ZERO,\n DAI: ZERO,\n AERO: ZERO,\n TSLA: \"0xC9f9c86933092BbbfFF3CCb4b105A4A94bf3Bd4E\" as Address,\n AMZN: \"0x5884aD2f920c162CFBbACc88C9C51AA75eC09E02\" as Address,\n PLTR: \"0x1FBE1a0e43594b3455993B5dE5Fd0A7A266298d0\" as Address,\n NFLX: \"0x3b8262A63d25f0477c4DDE23F83cfe22Cb768C93\" as Address,\n AMD: \"0x71178BAc73cBeb415514eB542a8995b82669778d\" as Address,\n} as const;\n\nconst ROBINHOOD_TESTNET_MOONWELL = {\n COMPTROLLER: ZERO,\n mUSDC: ZERO,\n mWETH: ZERO,\n mCbETH: ZERO,\n mWstETH: ZERO,\n mCbBTC: ZERO,\n mDAI: ZERO,\n mAERO: ZERO,\n} as const;\n\n// Synthra is Uniswap-V3-compatible; the deployed UniswapSwapAdapter is backed by\n// Synthra's router + a QuoterV2 shim (SYNTHRA_QUOTER_V2_SHIM). SWAP_ROUTER /\n// QUOTER_V2 mirror the Synthra addresses so a direct v3 quote path also resolves.\nconst ROBINHOOD_TESTNET_UNISWAP = {\n SWAP_ROUTER: \"0x3Ce954107b1A675826B33bF23060Dd655e3758fE\" as Address,\n QUOTER_V2: \"0xb3C009aECAeDd5ccC62Ec12eDAAA55F19C4A1eFb\" as Address,\n SWAP_ADAPTER: \"0x4fc3492117cC3bbcE0b210D22a8DC244f9d86490\" as Address,\n} as const;\n\n// ── Synthra DEX (Robinhood L2 — Uniswap V3-compatible concentrated liquidity) ──\n\nconst ROBINHOOD_TESTNET_SYNTHRA = {\n ROUTER: \"0x3Ce954107b1A675826B33bF23060Dd655e3758fE\" as Address,\n QUOTER: \"0x231606c321A99DE81e28fE48B07a93F1ba49e713\" as Address,\n FACTORY: \"0x911b4000D3422F482F4062a913885f7b035382Df\" as Address,\n} as const;\n\n// ── Chainlink (Data Streams verifier proxy + AggregatorV3 feeds) ──\n\nconst ROBINHOOD_TESTNET_CHAINLINK = {\n VERIFIER_PROXY: \"0x72790f9eB82db492a7DDb6d2af22A270Dcc3Db64\" as Address,\n WSTETH_ETH_FEED: ZERO,\n} as const;\n\n// ── Robinhood portfolio-basket route + feed metadata ──\n//\n// Data-driven replacement for the hand-drifting \"known stock\" Set that used to\n// live in strategy-template.ts. Every entry is a VERIFIED-liquidity default\n// swap route for a basket token. Absence from this map means \"no verified\n// default route\" — the CLI fails fast and demands an explicit --swap-routes\n// rather than falling through to a v3 auto-detect that would be dead.\n//\n// `kind: \"v4-native\"` = a Uniswap v4 mode-3 multi-hop USDG → native ETH → stock,\n// consumed by portfolio-template.ts as\n// v4PathExtraData([{currency: ETH, ...native}, {currency: <token>, ...stock}]).\nexport interface RobinhoodStockRoute {\n kind: \"v4-native\";\n nativeFee: number;\n nativeTickSpacing: number;\n stockFee: number;\n stockTickSpacing: number;\n}\n\n// No verified default stock swap routes on the Robinhood testnet — the machinery\n// is retained (see robinhoodEquityMaxAge / portfolio-template.ts) but the map is\n// empty, so every basket token falls to the existing fail-fast route error until\n// a verified route is added.\n// LOCKSTEP: the app's quote-probe geometry (app/src/lib/price-quote.ts\n// V4_DIRECT_TIERS + via-native path) must stay consistent with any route added\n// here, or the dashboard displays a tier the strategy won't execute.\nexport const ROBINHOOD_STOCK_ROUTES: Record<string, RobinhoodStockRoute> = {};\n\n// Chainlink push-feed classification. Every `*_USD_FEED` is an EQUITY feed\n// (needs the extended 96h max-price-age over holiday weekends) EXCEPT these —\n// crypto/FX/stable feeds keep the contract default (26h). Encoded here (not an\n// inline list in the command) so route/feed metadata lives in one registry.\n//\n// WETH is listed alongside ETH because feed auto-resolve normalizes WETH →\n// ETH_USD_FEED (there is no WETH_USD_FEED — the two share the 24h crypto feed).\n// The classification helper below MUST agree with that normalization for every\n// symbol the feed-resolution path accepts, or a WETH allocation packs the 96h\n// equity max-age onto a 24h crypto feed and accepts ~4-day-stale ETH prices.\nexport const ROBINHOOD_NON_EQUITY_FEEDS = new Set([\"ETH\", \"WETH\", \"BTC\", \"LINK\", \"USDG\", \"USDC\"]);\n\n/** True if `symbol`'s Robinhood push feed is a tokenized-equity feed. */\nexport function isRobinhoodEquityFeed(symbol: string): boolean {\n return !ROBINHOOD_NON_EQUITY_FEEDS.has(symbol.toUpperCase());\n}\n\n/**\n * Reverse-lookup a token address against a `TOKENS()` registry to recover its\n * symbol (case-insensitive). Returns undefined for the zero address or an\n * address that isn't registered. Used so a raw-address token spec can still be\n * classified as an equity feed (→ 96h max-price-age) the same way a symbol spec\n * is — otherwise a stock token passed by address silently falls back to the\n * contract's 26h default and trips `StalePrice` over long weekends.\n */\nexport function symbolForTokenAddress(\n address: string,\n tokens: Record<string, Address>,\n): string | undefined {\n if (!address || address.toLowerCase() === ZERO.toLowerCase()) return undefined;\n const want = address.toLowerCase();\n for (const [symbol, addr] of Object.entries(tokens)) {\n if (addr && addr !== ZERO && addr.toLowerCase() === want) return symbol;\n }\n return undefined;\n}\n\n/**\n * Resolve the equity max-price-age (seconds) for a Robinhood push-feed token\n * spec, which may be a SYMBOL or a raw 0x address. Returns the 96h equity\n * default for equity feeds, 0 (contract default) for crypto/stable feeds, and\n * `{ maxPriceAge: 0, unknownAddress: true }` when a raw address isn't in the\n * registry (caller should warn — we can't classify what we can't identify).\n *\n * `equityMaxAgeSeconds` is injected (from the portfolio builder's\n * EQUITY_MAX_PRICE_AGE_SECONDS) to avoid a cross-module import cycle.\n */\nexport function robinhoodEquityMaxAge(\n tokenSpec: string,\n tokens: Record<string, Address>,\n equityMaxAgeSeconds: number,\n): { maxPriceAge: number; unknownAddress: boolean } {\n let symbol: string | undefined;\n if (isAddress(tokenSpec)) {\n symbol = symbolForTokenAddress(tokenSpec, tokens);\n if (!symbol) return { maxPriceAge: 0, unknownAddress: true };\n } else {\n symbol = tokenSpec;\n }\n return {\n maxPriceAge: isRobinhoodEquityFeed(symbol) ? equityMaxAgeSeconds : 0,\n unknownAddress: false,\n };\n}\n\nconst ROBINHOOD_TESTNET_INFRA = {\n MULTICALL3: \"0xcA11bde05977b3631167028862bE2a173976CA11\" as Address, // Deterministic, same everywhere\n} as const;\n\n// ── ENS / Durin ──\n\nconst ROBINHOOD_TESTNET_ENS = {\n L2_REGISTRAR: ZERO,\n L2_REGISTRY: ZERO,\n} as const;\n\n// ── ERC-8004 Agent Identity ──\n\nconst ROBINHOOD_TESTNET_AGENT_REGISTRY = {\n IDENTITY_REGISTRY: ZERO,\n REPUTATION_REGISTRY: ZERO,\n} as const;\n\n// ── Sherwood Protocol (our deployed contracts) ──\n//\n// V2 redeploy (chain 46630): fresh factory/governor/vault-impl/executor plus a\n// full guardian layer (registry + sWOOD + WOOD fixture token) and the V2\n// live-NAV surface (PriceRouter, UniswapSwapAdapter via the Synthra QuoterV2\n// shim, Portfolio template, StrategyFactory). WOOD_TOKEN here is a testnet\n// fixture, not the mainnet OFT.\n//\n// GOVERNOR is the deploy-time bootstrap governor. Governors are per-vault since\n// PR #421 — resolve one at runtime via `factory.governorOf(vault)`\n// (lib/governor.ts `getGovernorOf`), never from this table.\nconst ROBINHOOD_TESTNET_SHERWOOD = {\n FACTORY: \"0xB9E71Fb33075328d6e94eCFFf8a8629D6d057cce\" as Address,\n GOVERNOR: ZERO, // zeroed since the post-#421 redeploy — always factory.governorOf(vault)\n GUARDIAN_REGISTRY: \"0x57f0fa384d0d7e2F234535d1235440312866872B\" as Address,\n STAKED_WOOD: \"0x15F48A9f24c8ECaa8f03c28Ecd1a3b4784CdCb3c\" as Address,\n WOOD_TOKEN: \"0xCCb4fB59cf40de1E23083037ee81Da1DD747D8d7\" as Address,\n STRATEGY_FACTORY: \"0xE1D082ef1CE17f9C6a46e97928337267BFF0C309\" as Address,\n MERKL_DISTRIBUTOR: ZERO,\n} as const;\n\n// ── Venice (VVV governance + sVVV staking + DIEM compute) ──\n\nconst ROBINHOOD_TESTNET_VENICE = {\n VVV: ZERO,\n STAKING: ZERO,\n DIEM: ZERO,\n} as const;\n\n// ── Aerodrome (Base ve(3,3) DEX) ──\n\nconst ROBINHOOD_TESTNET_AERODROME = {\n ROUTER: ZERO,\n FACTORY: ZERO,\n} as const;\n\n// ── Strategy Templates (ERC-1167 clonable singletons) ──\n// Populated after running script/DeployTemplates.s.sol\n\nconst ROBINHOOD_TESTNET_STRATEGY_TEMPLATES = {\n MOONWELL_SUPPLY: ZERO as Address,\n AERODROME_LP: ZERO as Address,\n VENICE_INFERENCE: ZERO as Address,\n WSTETH_MOONWELL: ZERO as Address,\n MAMO_YIELD: ZERO as Address,\n HYPERLIQUID_PERP: ZERO as Address,\n HYPERLIQUID_GRID: ZERO as Address,\n // V2 redeploy PORTFOLIO_TEMPLATE (chain 46630).\n PORTFOLIO: \"0x67420Cc504d70a42Adfd8867d878afe0978C7d10\" as Address,\n} as const;\n\n// ── EAS (Ethereum Attestation Service) ──\n\nconst ROBINHOOD_TESTNET_EAS = {\n EAS: ZERO,\n SCHEMA_REGISTRY: ZERO,\n} as const;\n\n// ── EAS Schema UIDs (populated after running scripts/register-eas-schemas.ts) ──\n\nconst ROBINHOOD_TESTNET_EAS_SCHEMAS = {\n SYNDICATE_JOIN_REQUEST: ZERO_BYTES32,\n AGENT_APPROVED: ZERO_BYTES32,\n X402_RESEARCH: ZERO_BYTES32,\n VENICE_PROVISION: ZERO_BYTES32,\n VENICE_INFERENCE: ZERO_BYTES32,\n TRADE_EXECUTED: ZERO_BYTES32,\n} as const;\n\n// ── Registries (map-based lookup) ──\n\nconst TOKEN_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_TOKENS> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_TOKENS,\n};\n\nconst MOONWELL_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_MOONWELL> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_MOONWELL,\n};\n\nconst UNISWAP_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_UNISWAP> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_UNISWAP,\n};\n\nconst INFRA_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_INFRA> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_INFRA,\n};\n\nconst ENS_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_ENS> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_ENS,\n};\n\nconst AGENT_REGISTRY_MAP: Record<Network, typeof ROBINHOOD_TESTNET_AGENT_REGISTRY> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_AGENT_REGISTRY,\n};\n\nconst SHERWOOD_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_SHERWOOD> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_SHERWOOD,\n};\n\nconst VENICE_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_VENICE> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_VENICE,\n};\n\nconst AERODROME_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_AERODROME> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_AERODROME,\n};\n\nconst STRATEGY_TEMPLATE_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_STRATEGY_TEMPLATES> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_STRATEGY_TEMPLATES,\n};\n\nconst SYNTHRA_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_SYNTHRA> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_SYNTHRA,\n};\n\nconst CHAINLINK_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_CHAINLINK> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_CHAINLINK,\n};\n\nconst EAS_CONTRACT_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_EAS> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_EAS,\n};\n\nconst EAS_SCHEMA_REGISTRY: Record<Network, typeof ROBINHOOD_TESTNET_EAS_SCHEMAS> = {\n \"robinhood-testnet\": ROBINHOOD_TESTNET_EAS_SCHEMAS,\n};\n\n// ── Exports (functions, resolved at call time) ──\n\nexport function TOKENS() {\n return TOKEN_REGISTRY[getNetwork()];\n}\n\nexport function MOONWELL() {\n return MOONWELL_REGISTRY[getNetwork()];\n}\n\nexport function UNISWAP() {\n return { ...UNISWAP_REGISTRY[getNetwork()], ...overridesFor(\"UNISWAP\") };\n}\n\nexport function INFRA() {\n return INFRA_REGISTRY[getNetwork()];\n}\n\nexport function ENS() {\n return { ...ENS_REGISTRY[getNetwork()], ...overridesFor(\"ENS\") };\n}\n\nexport function AGENT_REGISTRY() {\n return AGENT_REGISTRY_MAP[getNetwork()];\n}\n\nexport function VENICE() {\n return VENICE_REGISTRY[getNetwork()];\n}\n\nexport function SHERWOOD() {\n return { ...SHERWOOD_REGISTRY[getNetwork()], ...overridesFor(\"SHERWOOD\") };\n}\n\nexport function AERODROME() {\n return AERODROME_REGISTRY[getNetwork()];\n}\n\nexport function STRATEGY_TEMPLATES() {\n return {\n ...STRATEGY_TEMPLATE_REGISTRY[getNetwork()],\n ...overridesFor(\"STRATEGY_TEMPLATES\"),\n };\n}\n\nexport function EAS_CONTRACTS() {\n return EAS_CONTRACT_REGISTRY[getNetwork()];\n}\n\nexport function EAS_SCHEMAS() {\n return EAS_SCHEMA_REGISTRY[getNetwork()];\n}\n\nexport function SYNTHRA() {\n return SYNTHRA_REGISTRY[getNetwork()];\n}\n\nexport function CHAINLINK() {\n return CHAINLINK_REGISTRY[getNetwork()];\n}\n","/**\n * SIM_ADDRESSES_FILE override layer (spec G1).\n *\n * Points the CLI at a vnet deployment: a chains/{chainId}.json-shaped file\n * (CAPS_SNAKE_CASE keys, written by contracts deploy scripts) is mapped onto\n * the CLI's per-registry key names and merged over the base network registry\n * at call time. Only 0x-string values are considered. Fail-closed: a set-but-\n * unreadable file throws rather than silently using mainnet addresses.\n */\nimport { readFileSync } from \"node:fs\";\nimport type { Address } from \"viem\";\n\ntype RegistryName = \"SHERWOOD\" | \"STRATEGY_TEMPLATES\" | \"ENS\" | \"UNISWAP\";\n\n// chains-json key → CLI registry key, grouped by target registry.\n// Extend when deploy scripts gain keys the CLI reads (Task 1 Step 2).\n//\n// STRATEGY_TEMPLATES note (Task 1 Step 2 finding): the deploy scripts\n// (DeployTemplates.s.sol / DeployStrategyFactory.s.sol) write chains.json\n// keys with a `_TEMPLATE` suffix (e.g. `MOONWELL_SUPPLY_TEMPLATE`), but the\n// CLI-side STRATEGY_TEMPLATES() registry (cli/src/lib/addresses.ts\n// `BASE_STRATEGY_TEMPLATES`) and its consumers (e.g.\n// cli/src/commands/strategy-template.ts `addressKey: \"MOONWELL_SUPPLY\"`) key\n// off the UNSUFFIXED name. The map below strips the suffix so overrides\n// actually reach the keys the CLI reads.\nconst CHAINS_TO_CLI: Record<RegistryName, Record<string, string>> = {\n SHERWOOD: {\n SYNDICATE_FACTORY: \"FACTORY\",\n SYNDICATE_GOVERNOR: \"GOVERNOR\",\n GUARDIAN_REGISTRY: \"GUARDIAN_REGISTRY\",\n STAKED_WOOD: \"STAKED_WOOD\",\n WOOD: \"WOOD_TOKEN\",\n WOOD_TOKEN: \"WOOD_TOKEN\",\n STRATEGY_FACTORY: \"STRATEGY_FACTORY\",\n },\n STRATEGY_TEMPLATES: {\n MOONWELL_SUPPLY_TEMPLATE: \"MOONWELL_SUPPLY\",\n AERODROME_LP_TEMPLATE: \"AERODROME_LP\",\n VENICE_INFERENCE_TEMPLATE: \"VENICE_INFERENCE\",\n WSTETH_MOONWELL_TEMPLATE: \"WSTETH_MOONWELL\",\n MAMO_YIELD_TEMPLATE: \"MAMO_YIELD\",\n PORTFOLIO_TEMPLATE: \"PORTFOLIO\",\n HYPERLIQUID_PERP_TEMPLATE: \"HYPERLIQUID_PERP\",\n HYPERLIQUID_GRID_TEMPLATE: \"HYPERLIQUID_GRID\",\n },\n ENS: {\n L2_REGISTRAR: \"L2_REGISTRAR\",\n L2_REGISTRY: \"L2_REGISTRY\",\n },\n UNISWAP: {\n UNISWAP_SWAP_ADAPTER: \"SWAP_ADAPTER\",\n },\n};\n\nlet cached: Record<string, Address> | null | undefined;\n\nfunction loadFile(): Record<string, Address> | null {\n if (cached !== undefined) return cached;\n const file = process.env.SIM_ADDRESSES_FILE;\n if (!file) {\n cached = null;\n return cached;\n }\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch (err) {\n throw new Error(\n `SIM_ADDRESSES_FILE is set (${file}) but unreadable: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n const out: Record<string, Address> = {};\n for (const [k, v] of Object.entries(raw)) {\n if (typeof v === \"string\" && v.startsWith(\"0x\")) out[k] = v as Address;\n }\n cached = out;\n return cached;\n}\n\n/** Mapped overrides for one CLI registry; {} when SIM_ADDRESSES_FILE unset. */\nexport function overridesFor(registry: RegistryName): Record<string, Address> {\n const file = loadFile();\n if (!file) return {};\n const map = CHAINS_TO_CLI[registry];\n const out: Record<string, Address> = {};\n for (const [chainsKey, cliKey] of Object.entries(map)) {\n if (file[chainsKey]) out[cliKey] = file[chainsKey];\n }\n return out;\n}\n\nexport function __resetOverridesCacheForTests(): void {\n cached = undefined;\n}\n"],"mappings":"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","names":[]}
@@ -9,23 +9,36 @@ function getPinataGateway() {
9
9
  }
10
10
  async function uploadToIPFS(content, name) {
11
11
  const url = getUploadApiUrl();
12
- const response = await fetch(url, {
13
- method: "POST",
14
- headers: { "Content-Type": "application/json" },
15
- body: JSON.stringify({ content, name })
16
- });
17
- if (!response.ok) {
18
- const text = await response.text();
19
- throw new Error(`IPFS upload failed (${response.status}): ${text}`);
12
+ const attempts = 3;
13
+ let lastErr;
14
+ for (let i = 0; i < attempts; i++) {
15
+ try {
16
+ const response = await fetch(url, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify({ content, name })
20
+ });
21
+ if (!response.ok) {
22
+ const text = await response.text();
23
+ if (response.status < 500) throw new Error(`IPFS upload failed (${response.status}): ${text}`);
24
+ lastErr = new Error(`IPFS upload failed (${response.status}): ${text}`);
25
+ } else {
26
+ const result = await response.json();
27
+ return `ipfs://${result.ipfsHash}`;
28
+ }
29
+ } catch (err) {
30
+ if (err instanceof Error && err.message.startsWith("IPFS upload failed (4")) throw err;
31
+ lastErr = err;
32
+ }
33
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, 2e3 * (i + 1)));
20
34
  }
21
- const result = await response.json();
22
- return `ipfs://${result.ipfsHash}`;
35
+ throw lastErr instanceof Error ? lastErr : new Error(`IPFS upload failed: ${String(lastErr)}`);
23
36
  }
24
37
  async function pinJSON(content, name) {
25
38
  return uploadToIPFS(content, name);
26
39
  }
27
40
  async function uploadMetadata(metadata) {
28
- const name = `sherwood-syndicate-${metadata.name.toLowerCase().replace(/\s+/g, "-")}`;
41
+ const name = `sherwood-fund-${metadata.name.toLowerCase().replace(/\s+/g, "-")}`;
29
42
  return uploadToIPFS(metadata, name);
30
43
  }
31
44
  async function fetchMetadata(ipfsURI) {
@@ -65,4 +78,4 @@ export {
65
78
  fetchMetadata,
66
79
  resolveMetadataSummary
67
80
  };
68
- //# sourceMappingURL=chunk-RHRLYVKM.js.map
81
+ //# sourceMappingURL=chunk-ZYXFKVYF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/ipfs.ts"],"sourcesContent":["/**\n * IPFS metadata upload/fetch via Sherwood API.\n *\n * Uploads go through the server-side API at sherwood.sh/api/ipfs/upload\n * which holds the Pinata JWT. CLI and app only need the gateway URL for reads.\n */\n\nexport interface FundMetadata {\n schema: string;\n name: string;\n description: string;\n logo?: string;\n chain: string;\n xmtpGroupId?: string;\n strategies: {\n id: string;\n name: string;\n description: string;\n protocols: string[];\n riskLevel: string;\n }[];\n terms: {\n minDeposit?: string;\n minDepositFormatted?: string;\n feeModel?: string;\n lockPeriod?: number;\n };\n links: {\n moltbook?: string;\n dashboard?: string;\n github?: string;\n };\n}\n\nconst DEFAULT_PINATA_GATEWAY = \"https://sherwood.mypinata.cloud\";\n// www is canonical — the apex 307-redirects to it (an extra hop that has been\n// observed slow/flaky on cold starts; vnet e2e finding).\nconst DEFAULT_UPLOAD_API = \"https://www.sherwood.sh/api/ipfs/upload\";\n\nfunction getUploadApiUrl(): string {\n return process.env.SHERWOOD_API_URL\n ? `${process.env.SHERWOOD_API_URL}/api/ipfs/upload`\n : DEFAULT_UPLOAD_API;\n}\n\nfunction getPinataGateway(): string {\n return process.env.PINATA_GATEWAY || DEFAULT_PINATA_GATEWAY;\n}\n\n/**\n * Upload JSON to IPFS via the Sherwood API (server-side Pinata).\n * Returns the IPFS URI (ipfs://Qm...).\n */\nasync function uploadToIPFS(content: Record<string, unknown>, name: string): Promise<string> {\n const url = getUploadApiUrl();\n\n // Transient \"fetch failed\" / cold-start 5xx from the pin API shouldn't kill\n // a propose flow — retry briefly before surfacing (vnet e2e finding).\n const attempts = 3;\n let lastErr: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, name }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n // 4xx won't improve on retry; 5xx might.\n if (response.status < 500) throw new Error(`IPFS upload failed (${response.status}): ${text}`);\n lastErr = new Error(`IPFS upload failed (${response.status}): ${text}`);\n } else {\n const result = (await response.json()) as { ipfsHash: string };\n return `ipfs://${result.ipfsHash}`;\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"IPFS upload failed (4\")) throw err;\n lastErr = err;\n }\n if (i < attempts - 1) await new Promise((r) => setTimeout(r, 2000 * (i + 1)));\n }\n throw lastErr instanceof Error ? lastErr : new Error(`IPFS upload failed: ${String(lastErr)}`);\n}\n\n/**\n * Pin arbitrary JSON to IPFS.\n * Used for research results and other generic JSON payloads.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function pinJSON(content: Record<string, unknown>, name: string): Promise<string> {\n return uploadToIPFS(content, name);\n}\n\n/**\n * Upload fund metadata to IPFS.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function uploadMetadata(metadata: FundMetadata): Promise<string> {\n const name = `sherwood-fund-${metadata.name.toLowerCase().replace(/\\s+/g, \"-\")}`;\n return uploadToIPFS(metadata as unknown as Record<string, unknown>, name);\n}\n\n/**\n * Fetch and parse metadata from an IPFS URI.\n * Supports ipfs:// protocol URIs and raw CIDs.\n */\nexport async function fetchMetadata(ipfsURI: string): Promise<FundMetadata> {\n const gateway = getPinataGateway();\n let cid: string;\n\n if (ipfsURI.startsWith(\"ipfs://\")) {\n cid = ipfsURI.slice(7);\n } else if (ipfsURI.startsWith(\"Qm\") || ipfsURI.startsWith(\"bafy\")) {\n cid = ipfsURI;\n } else {\n throw new Error(`Invalid IPFS URI: ${ipfsURI}`);\n }\n\n const url = `${gateway}/ipfs/${cid}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch metadata from ${url} (${response.status})`);\n }\n\n return (await response.json()) as FundMetadata;\n}\n\n/**\n * Resolve a metadata URI (ipfs:// or data: base64) to name + description.\n * Returns null on any error — callers should treat enrichment as best-effort.\n */\nexport async function resolveMetadataSummary(\n uri: string,\n): Promise<{ name: string; description: string } | null> {\n try {\n if (uri.startsWith(\"data:application/json;base64,\")) {\n const b64 = uri.slice(\"data:application/json;base64,\".length);\n const json = JSON.parse(Buffer.from(b64, \"base64\").toString(\"utf-8\"));\n return { name: json.name || \"\", description: json.description || \"\" };\n }\n const meta = await fetchMetadata(uri);\n return { name: meta.name, description: meta.description };\n } catch {\n return null;\n }\n}\n"],"mappings":";AAkCA,IAAM,yBAAyB;AAG/B,IAAM,qBAAqB;AAE3B,SAAS,kBAA0B;AACjC,SAAO,QAAQ,IAAI,mBACf,GAAG,QAAQ,IAAI,gBAAgB,qBAC/B;AACN;AAEA,SAAS,mBAA2B;AAClC,SAAO,QAAQ,IAAI,kBAAkB;AACvC;AAMA,eAAe,aAAa,SAAkC,MAA+B;AAC3F,QAAM,MAAM,gBAAgB;AAI5B,QAAM,WAAW;AACjB,MAAI;AACJ,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAAA,MACxC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,YAAI,SAAS,SAAS,IAAK,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,MAAM,IAAI,EAAE;AAC7F,kBAAU,IAAI,MAAM,uBAAuB,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,MACxE,OAAO;AACL,cAAM,SAAU,MAAM,SAAS,KAAK;AACpC,eAAO,UAAU,OAAO,QAAQ;AAAA,MAClC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,QAAQ,WAAW,uBAAuB,EAAG,OAAM;AACnF,gBAAU;AAAA,IACZ;AACA,QAAI,IAAI,WAAW,EAAG,OAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,OAAQ,IAAI,EAAE,CAAC;AAAA,EAC9E;AACA,QAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,OAAO,OAAO,CAAC,EAAE;AAC/F;AAOA,eAAsB,QAAQ,SAAkC,MAA+B;AAC7F,SAAO,aAAa,SAAS,IAAI;AACnC;AAMA,eAAsB,eAAe,UAAyC;AAC5E,QAAM,OAAO,iBAAiB,SAAS,KAAK,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAC9E,SAAO,aAAa,UAAgD,IAAI;AAC1E;AAMA,eAAsB,cAAc,SAAwC;AAC1E,QAAM,UAAU,iBAAiB;AACjC,MAAI;AAEJ,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,QAAQ,MAAM,CAAC;AAAA,EACvB,WAAW,QAAQ,WAAW,IAAI,KAAK,QAAQ,WAAW,MAAM,GAAG;AACjE,UAAM;AAAA,EACR,OAAO;AACL,UAAM,IAAI,MAAM,qBAAqB,OAAO,EAAE;AAAA,EAChD;AAEA,QAAM,MAAM,GAAG,OAAO,SAAS,GAAG;AAClC,QAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,iCAAiC,GAAG,KAAK,SAAS,MAAM,GAAG;AAAA,EAC7E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAMA,eAAsB,uBACpB,KACuD;AACvD,MAAI;AACF,QAAI,IAAI,WAAW,+BAA+B,GAAG;AACnD,YAAM,MAAM,IAAI,MAAM,gCAAgC,MAAM;AAC5D,YAAM,OAAO,KAAK,MAAM,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO,CAAC;AACpE,aAAO,EAAE,MAAM,KAAK,QAAQ,IAAI,aAAa,KAAK,eAAe,GAAG;AAAA,IACtE;AACA,UAAM,OAAO,MAAM,cAAc,GAAG;AACpC,WAAO,EAAE,MAAM,KAAK,MAAM,aAAa,KAAK,YAAY;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -10,9 +10,9 @@ import {
10
10
  unstickWallet,
11
11
  waitForReceipt,
12
12
  writeContractWithRetry
13
- } from "./chunk-YJSCIQUZ.js";
14
- import "./chunk-2LRC7I33.js";
15
- import "./chunk-2W4KGGPU.js";
13
+ } from "./chunk-UC5RMCB3.js";
14
+ import "./chunk-RNASYOQT.js";
15
+ import "./chunk-GSC7PVEO.js";
16
16
  export {
17
17
  detectStuckNonce,
18
18
  estimateFeesWithBuffer,
@@ -26,4 +26,4 @@ export {
26
26
  waitForReceipt,
27
27
  writeContractWithRetry
28
28
  };
29
- //# sourceMappingURL=client-KBBFAMEI.js.map
29
+ //# sourceMappingURL=client-K4JCNPVV.js.map
@@ -1,5 +1,5 @@
1
1
  import {
2
- addSyndicate,
2
+ addFund,
3
3
  cacheGroupId,
4
4
  cacheSwapRoute,
5
5
  clearSwapRoutes,
@@ -9,10 +9,10 @@ import {
9
9
  getCachedSwapRoute,
10
10
  getChainContracts,
11
11
  getConfigRpcUrl,
12
+ getFunds,
12
13
  getNotifyTo,
13
- getPrimarySyndicate,
14
+ getPrimaryFund,
14
15
  getPrivateKey,
15
- getSyndicates,
16
16
  getUniswapApiKey,
17
17
  getVeniceApiKey,
18
18
  invalidateCachedGroupId,
@@ -23,13 +23,13 @@ import {
23
23
  setChainContract,
24
24
  setConfigRpcUrl,
25
25
  setNotifyTo,
26
- setPrimarySyndicate,
26
+ setPrimaryFund,
27
27
  setPrivateKey,
28
28
  setUniswapApiKey,
29
29
  setVeniceApiKey
30
- } from "./chunk-2W4KGGPU.js";
30
+ } from "./chunk-GSC7PVEO.js";
31
31
  export {
32
- addSyndicate,
32
+ addFund,
33
33
  cacheGroupId,
34
34
  cacheSwapRoute,
35
35
  clearSwapRoutes,
@@ -39,10 +39,10 @@ export {
39
39
  getCachedSwapRoute,
40
40
  getChainContracts,
41
41
  getConfigRpcUrl,
42
+ getFunds,
42
43
  getNotifyTo,
43
- getPrimarySyndicate,
44
+ getPrimaryFund,
44
45
  getPrivateKey,
45
- getSyndicates,
46
46
  getUniswapApiKey,
47
47
  getVeniceApiKey,
48
48
  invalidateCachedGroupId,
@@ -53,9 +53,9 @@ export {
53
53
  setChainContract,
54
54
  setConfigRpcUrl,
55
55
  setNotifyTo,
56
- setPrimarySyndicate,
56
+ setPrimaryFund,
57
57
  setPrivateKey,
58
58
  setUniswapApiKey,
59
59
  setVeniceApiKey
60
60
  };
61
- //# sourceMappingURL=config-REASKDIK.js.map
61
+ //# sourceMappingURL=config-FAQO37S7.js.map
@@ -22,13 +22,14 @@ function summaryCronName(subdomain, testnet) {
22
22
  return `${cronName(subdomain, testnet)}-summary`;
23
23
  }
24
24
  function silentPrompt(subdomain, testnet) {
25
- const chainFlag = testnet ? " --chain base-sepolia" : "";
26
- const envPrefix = testnet ? "ENABLE_TESTNET=true " : "";
25
+ const chainFlag = "";
26
+ const envPrefix = "";
27
+ void testnet;
27
28
  return [
28
- `You are a sherwood syndicate member. Your syndicate: ${subdomain}.sherwoodagent.eth`,
29
+ `You are a sherwood fund member. Your fund: ${subdomain}.sherwoodagent.eth`,
29
30
  "",
30
31
  `Step 1: Run \`${envPrefix}sherwood session check ${subdomain}${chainFlag}\``,
31
- "Step 2: Parse the JSON output. The output is a single JSON object with fields: syndicate, messages[], events[], meta{}.",
32
+ "Step 2: Parse the JSON output. The output is a single JSON object with fields: fund, messages[], events[], meta{}.",
32
33
  " - If the command fails or returns an error, reply HEARTBEAT_ERROR with the error message.",
33
34
  " - If meta.newMessages === 0 and meta.newEvents === 0, you may still be pending approval.",
34
35
  " Check: `" + envPrefix + "sherwood vault info" + chainFlag + "`",
@@ -41,21 +42,22 @@ function silentPrompt(subdomain, testnet) {
41
42
  " If nothing happened, reply HEARTBEAT_OK",
42
43
  "",
43
44
  "Rules:",
44
- "- Be a real syndicate member \u2014 discuss strategies, share opinions, ask questions",
45
+ "- Be a real fund member \u2014 discuss strategies, share opinions, ask questions",
45
46
  "- Keep responses concise and on-topic",
46
47
  "- Do NOT alert your human unless something requires their approval (or you were just approved)",
47
48
  `- You can use \`sherwood research\` commands if you need data to back up your response`
48
49
  ].join("\n");
49
50
  }
50
51
  function summaryPrompt(subdomain, testnet) {
51
- const chainFlag = testnet ? " --chain base-sepolia" : "";
52
- const envPrefix = testnet ? "ENABLE_TESTNET=true " : "";
52
+ const chainFlag = "";
53
+ const envPrefix = "";
54
+ void testnet;
53
55
  return [
54
- "You are reporting syndicate activity to your human operator.",
56
+ "You are reporting fund activity to your human operator.",
55
57
  "",
56
58
  `Run: \`${envPrefix}sherwood session check ${subdomain}${chainFlag}\``,
57
59
  "",
58
- "The output is a single JSON object with fields: syndicate, messages[], events[], meta{}.",
60
+ "The output is a single JSON object with fields: fund, messages[], events[], meta{}.",
59
61
  "If the command fails, report the error to your human.",
60
62
  "",
61
63
  "If there was activity (messages, events, proposals) since last check:",
@@ -67,7 +69,7 @@ function summaryPrompt(subdomain, testnet) {
67
69
  "",
68
70
  "Only escalate (flag as urgent) if:",
69
71
  "- A proposal needs human sign-off",
70
- "- An agent left the syndicate",
72
+ "- An agent left the fund",
71
73
  "- Risk alert or health factor warning",
72
74
  "- Human was directly asked for input"
73
75
  ].join("\n");
@@ -104,7 +106,7 @@ function listCronDetails() {
104
106
  return [];
105
107
  }
106
108
  }
107
- function registerSyndicateCrons(subdomain, testnet, notifyTo) {
109
+ function registerFundCrons(subdomain, testnet, notifyTo) {
108
110
  if (!isOpenClaw()) {
109
111
  return { registered: false, isOpenClaw: false, cronNames: [] };
110
112
  }
@@ -175,7 +177,7 @@ function registerSyndicateCrons(subdomain, testnet, notifyTo) {
175
177
  cronNames: created
176
178
  };
177
179
  }
178
- function unregisterSyndicateCrons(subdomain, testnet) {
180
+ function unregisterFundCrons(subdomain, testnet) {
179
181
  if (!isOpenClaw()) {
180
182
  return { removed: false, isOpenClaw: false };
181
183
  }
@@ -194,7 +196,7 @@ function unregisterSyndicateCrons(subdomain, testnet) {
194
196
  }
195
197
  return { removed, isOpenClaw: true };
196
198
  }
197
- function getSyndicateCronStatus(subdomain, testnet) {
199
+ function getFundCronStatus(subdomain, testnet) {
198
200
  if (!isOpenClaw()) {
199
201
  return { isOpenClaw: false, crons: [] };
200
202
  }
@@ -204,9 +206,9 @@ function getSyndicateCronStatus(subdomain, testnet) {
204
206
  return { isOpenClaw: true, crons: matching };
205
207
  }
206
208
  export {
207
- getSyndicateCronStatus,
209
+ getFundCronStatus,
208
210
  isOpenClaw,
209
- registerSyndicateCrons,
210
- unregisterSyndicateCrons
211
+ registerFundCrons,
212
+ unregisterFundCrons
211
213
  };
212
- //# sourceMappingURL=cron-EOULYO3R.js.map
214
+ //# sourceMappingURL=cron-QMLRWDJG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/cron.ts"],"sourcesContent":["/**\n * OpenClaw cron integration — auto-register participation crons for fund agents.\n *\n * When an agent creates or joins a fund, the CLI registers two cron jobs:\n * 1. Silent check (every 15m) — processes messages/events, responds autonomously\n * 2. Human summary (every 1h) — brief activity report delivered to the human's channel\n *\n * Non-OpenClaw agents skip silently — the caller prints guidance instead.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n// ── OpenClaw detection (cached per process) ──\n\nlet _isOpenClaw: boolean | null = null;\n\nexport function isOpenClaw(): boolean {\n if (_isOpenClaw !== null) return _isOpenClaw;\n try {\n execFileSync(\"openclaw\", [\"cron\", \"list\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n _isOpenClaw = true;\n } catch {\n _isOpenClaw = false;\n }\n return _isOpenClaw;\n}\n\n// ── Types ──\n\nexport interface CronResult {\n registered: boolean;\n isOpenClaw: boolean;\n cronNames: string[];\n}\n\nexport interface CronStatus {\n isOpenClaw: boolean;\n crons: { name: string; every: string; lastRun?: string }[];\n}\n\n// ── Cron naming ──\n\nfunction cronName(subdomain: string, testnet: boolean): string {\n return `sherwood-${subdomain}${testnet ? \"-testnet\" : \"\"}`;\n}\n\nfunction summaryCronName(subdomain: string, testnet: boolean): string {\n return `${cronName(subdomain, testnet)}-summary`;\n}\n\n// ── Prompts ──\n\nfunction silentPrompt(subdomain: string, testnet: boolean): string {\n // Current deployment target (robinhood-testnet) is the ungated CLI default,\n // so cron commands need no explicit --chain flag or env gate.\n const chainFlag = \"\";\n const envPrefix = \"\";\n void testnet;\n return [\n `You are a sherwood fund member. Your fund: ${subdomain}.sherwoodagent.eth`,\n \"\",\n `Step 1: Run \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"Step 2: Parse the JSON output. The output is a single JSON object with fields: fund, messages[], events[], meta{}.\",\n \" - If the command fails or returns an error, reply HEARTBEAT_ERROR with the error message.\",\n \" - If meta.newMessages === 0 and meta.newEvents === 0, you may still be pending approval.\",\n \" Check: `\" + envPrefix + \"sherwood vault info\" + chainFlag + \"`\",\n \" If your wallet is not listed as a registered agent, you are still pending. Reply HEARTBEAT_OK.\",\n \" If you ARE registered but this is your first time seeing it, alert your human that you've been approved and are now active.\",\n \" - If there are new messages from OTHER agents, respond thoughtfully via:\",\n ` \\`${envPrefix}sherwood chat ${subdomain} send \"<response>\"${chainFlag}\\``,\n \" - If there are on-chain events (proposals, votes, settlements), acknowledge them in chat if relevant.\",\n \"Step 3: If you responded to anything, summarize what you did (for your own session log).\",\n \" If nothing happened, reply HEARTBEAT_OK\",\n \"\",\n \"Rules:\",\n \"- Be a real fund member — discuss strategies, share opinions, ask questions\",\n \"- Keep responses concise and on-topic\",\n \"- Do NOT alert your human unless something requires their approval (or you were just approved)\",\n `- You can use \\`sherwood research\\` commands if you need data to back up your response`,\n ].join(\"\\n\");\n}\n\nfunction summaryPrompt(subdomain: string, testnet: boolean): string {\n // Current deployment target (robinhood-testnet) is the ungated CLI default,\n // so cron commands need no explicit --chain flag or env gate.\n const chainFlag = \"\";\n const envPrefix = \"\";\n void testnet;\n return [\n \"You are reporting fund activity to your human operator.\",\n \"\",\n `Run: \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"\",\n \"The output is a single JSON object with fields: fund, messages[], events[], meta{}.\",\n \"If the command fails, report the error to your human.\",\n \"\",\n \"If there was activity (messages, events, proposals) since last check:\",\n \" Send a brief summary — who said what, any decisions made, any actions you took.\",\n \" Keep it to 3-5 lines max.\",\n \"\",\n \"If there was no activity:\",\n \" Reply HEARTBEAT_OK\",\n \"\",\n \"Only escalate (flag as urgent) if:\",\n \"- A proposal needs human sign-off\",\n \"- An agent left the fund\",\n \"- Risk alert or health factor warning\",\n \"- Human was directly asked for input\",\n ].join(\"\\n\");\n}\n\n// ── Helpers ──\n\n/** Parse `openclaw cron list --json` and return job names. */\nfunction listCronNames(): string[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string }[] = parsed.jobs || parsed || [];\n return jobs.map((j) => j.name);\n } catch {\n return [];\n }\n}\n\n/** Parse `openclaw cron list --json` with full details for status display. */\nfunction listCronDetails(): { name: string; every: string; lastRun?: string }[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string; every?: string; interval?: string; lastRun?: string }[] =\n parsed.jobs || parsed || [];\n return jobs.map((j) => ({\n name: j.name,\n every: j.every || j.interval || \"unknown\",\n lastRun: j.lastRun,\n }));\n } catch {\n return [];\n }\n}\n\n// ── Public API ──\n\nexport function registerFundCrons(\n subdomain: string,\n testnet: boolean,\n notifyTo?: string,\n): CronResult {\n if (!isOpenClaw()) {\n return { registered: false, isOpenClaw: false, cronNames: [] };\n }\n\n const checkName = cronName(subdomain, testnet);\n const summaryName = summaryCronName(subdomain, testnet);\n const existing = listCronNames();\n const created: string[] = [];\n\n // Cron 1: Silent participation check (every 15m)\n if (!existing.includes(checkName)) {\n try {\n execFileSync(\"openclaw\", [\n \"cron\", \"create\",\n \"--name\", checkName,\n \"--every\", \"15m\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"120\",\n \"--no-deliver\",\n \"--message\", silentPrompt(subdomain, testnet),\n ], {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(checkName);\n } catch (err) {\n console.warn(`Could not create silent cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Cron 2: Human summary (every 1h)\n if (!existing.includes(summaryName)) {\n try {\n const args = [\n \"cron\", \"create\",\n \"--name\", summaryName,\n \"--every\", \"1h\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"90\",\n \"--announce\",\n ];\n\n // Use explicit destination if configured, otherwise auto-route via --channel last\n if (notifyTo) {\n args.push(\"--to\", notifyTo);\n } else {\n args.push(\"--channel\", \"last\");\n }\n\n args.push(\"--message\", summaryPrompt(subdomain, testnet));\n\n execFileSync(\"openclaw\", args, {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(summaryName);\n } catch (err) {\n console.warn(`Could not create summary cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return {\n registered: created.length > 0,\n isOpenClaw: true,\n cronNames: created,\n };\n}\n\nexport function unregisterFundCrons(\n subdomain: string,\n testnet: boolean,\n): { removed: boolean; isOpenClaw: boolean } {\n if (!isOpenClaw()) {\n return { removed: false, isOpenClaw: false };\n }\n\n const names = [cronName(subdomain, testnet), summaryCronName(subdomain, testnet)];\n let removed = false;\n\n for (const name of names) {\n try {\n execFileSync(\"openclaw\", [\"cron\", \"remove\", \"--name\", name], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n removed = true;\n } catch {\n // Cron may not exist — that's fine\n }\n }\n\n return { removed, isOpenClaw: true };\n}\n\nexport function getFundCronStatus(\n subdomain: string,\n testnet: boolean,\n): CronStatus {\n if (!isOpenClaw()) {\n return { isOpenClaw: false, crons: [] };\n }\n\n const prefix = cronName(subdomain, testnet);\n const all = listCronDetails();\n const matching = all.filter((c) => c.name.startsWith(prefix));\n\n return { isOpenClaw: true, crons: matching };\n}\n"],"mappings":"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","names":[]}