@sherwoodagent/cli 0.41.5 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{addresses-RQ7DTIBS.js → addresses-DOWLGXGE.js} +4 -4
- package/dist/{agent-SXEV65ME.js → agent-4HKHLQEJ.js} +3035 -2029
- package/dist/agent-4HKHLQEJ.js.map +1 -0
- package/dist/autoresearch-IA7HGSNW.js +267 -0
- package/dist/autoresearch-IA7HGSNW.js.map +1 -0
- package/dist/{chat-D5OF4OTL.js → chat-MRTPUZ3X.js} +8 -8
- package/dist/{chunk-2YOXRATG.js → chunk-2LRC7I33.js} +12 -2
- package/dist/{chunk-2YOXRATG.js.map → chunk-2LRC7I33.js.map} +1 -1
- package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
- package/dist/chunk-2W4KGGPU.js.map +1 -0
- package/dist/chunk-7HJXZ4NM.js +189 -0
- package/dist/chunk-7HJXZ4NM.js.map +1 -0
- package/dist/{chunk-DG2K6CPY.js → chunk-E6LH2FJ6.js} +110 -68
- package/dist/chunk-E6LH2FJ6.js.map +1 -0
- package/dist/{chunk-MT3VS3GZ.js → chunk-FWC57PT7.js} +2 -2
- package/dist/{chunk-YRCJ3NUX.js → chunk-IS2CLLUT.js} +4 -4
- package/dist/{chunk-YYB4TF6D.js → chunk-KJ35EF7N.js} +4 -4
- package/dist/{chunk-D2UYGUV2.js → chunk-OHQOTSFG.js} +39 -3
- package/dist/chunk-OHQOTSFG.js.map +1 -0
- package/dist/chunk-R2P4C2I7.js +560 -0
- package/dist/chunk-R2P4C2I7.js.map +1 -0
- package/dist/{chunk-4QNPS5LI.js → chunk-R7X33D2S.js} +25 -6
- package/dist/{chunk-4QNPS5LI.js.map → chunk-R7X33D2S.js.map} +1 -1
- package/dist/{chunk-XZHIZVJ3.js → chunk-WMZEOR5W.js} +6 -127
- package/dist/chunk-WMZEOR5W.js.map +1 -0
- package/dist/{chunk-M2NJPATE.js → chunk-XDBRKW7I.js} +3 -3
- package/dist/chunk-YTYXBX7B.js +495 -0
- package/dist/chunk-YTYXBX7B.js.map +1 -0
- package/dist/{client-VSBQM2LU.js → client-UHHCB6EZ.js} +4 -4
- package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
- package/dist/{eas-QNZKCEP7.js → eas-PEEWGSAO.js} +16 -6
- package/dist/{governor-RE6IJMUJ.js → governor-MG5VVSU4.js} +6 -6
- package/dist/index.js +1137 -272
- package/dist/index.js.map +1 -1
- package/dist/{network-R5NFPIAF.js → network-3ZU7UBDU.js} +7 -5
- package/dist/{price-GY3JQ2OK.js → price-WCCXWPDM.js} +5 -5
- package/dist/{research-SVWNPHIN.js → research-QBKISW2M.js} +9 -9
- package/dist/research-YJWSQS2A.js +14 -0
- package/dist/serve-6MYBOAX6.js +222 -0
- package/dist/serve-6MYBOAX6.js.map +1 -0
- package/dist/{session-3Z6I2IM3.js → session-FPCPCVQX.js} +10 -10
- package/dist/summary-formatter-JIGWN4QS.js +180 -0
- package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
- package/dist/{trade-E5HSZE44.js → trade-NO6CNWCA.js} +19 -17
- package/dist/{trade-E5HSZE44.js.map → trade-NO6CNWCA.js.map} +1 -1
- package/dist/{xmtp-VPGQE4IP.js → xmtp-ACB2W573.js} +6 -6
- package/package.json +5 -2
- package/dist/agent-SXEV65ME.js.map +0 -1
- package/dist/chunk-4EHI4SKM.js.map +0 -1
- package/dist/chunk-D2UYGUV2.js.map +0 -1
- package/dist/chunk-DG2K6CPY.js.map +0 -1
- package/dist/chunk-XZHIZVJ3.js.map +0 -1
- package/dist/research-PB22GPXC.js +0 -14
- /package/dist/{addresses-RQ7DTIBS.js.map → addresses-DOWLGXGE.js.map} +0 -0
- /package/dist/{chat-D5OF4OTL.js.map → chat-MRTPUZ3X.js.map} +0 -0
- /package/dist/{chunk-MT3VS3GZ.js.map → chunk-FWC57PT7.js.map} +0 -0
- /package/dist/{chunk-YRCJ3NUX.js.map → chunk-IS2CLLUT.js.map} +0 -0
- /package/dist/{chunk-YYB4TF6D.js.map → chunk-KJ35EF7N.js.map} +0 -0
- /package/dist/{chunk-M2NJPATE.js.map → chunk-XDBRKW7I.js.map} +0 -0
- /package/dist/{client-VSBQM2LU.js.map → client-UHHCB6EZ.js.map} +0 -0
- /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
- /package/dist/{eas-QNZKCEP7.js.map → eas-PEEWGSAO.js.map} +0 -0
- /package/dist/{governor-RE6IJMUJ.js.map → governor-MG5VVSU4.js.map} +0 -0
- /package/dist/{network-R5NFPIAF.js.map → network-3ZU7UBDU.js.map} +0 -0
- /package/dist/{price-GY3JQ2OK.js.map → price-WCCXWPDM.js.map} +0 -0
- /package/dist/{research-SVWNPHIN.js.map → research-QBKISW2M.js.map} +0 -0
- /package/dist/{research-PB22GPXC.js.map → research-YJWSQS2A.js.map} +0 -0
- /package/dist/{session-3Z6I2IM3.js.map → session-FPCPCVQX.js.map} +0 -0
- /package/dist/{xmtp-VPGQE4IP.js.map → xmtp-ACB2W573.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
HyperliquidProvider,
|
|
4
|
+
getLatestSignals
|
|
5
|
+
} from "./chunk-R2P4C2I7.js";
|
|
2
6
|
import {
|
|
3
7
|
UniswapProvider,
|
|
4
8
|
applySlippage,
|
|
5
|
-
chatCompletion,
|
|
6
|
-
checkApiKeyValid,
|
|
7
9
|
encodeSwapPath,
|
|
8
10
|
getMultiHopQuote,
|
|
9
|
-
getQuote
|
|
11
|
+
getQuote
|
|
12
|
+
} from "./chunk-WMZEOR5W.js";
|
|
13
|
+
import {
|
|
14
|
+
chatCompletion,
|
|
15
|
+
checkApiKeyValid,
|
|
10
16
|
listModels,
|
|
11
17
|
provisionApiKey
|
|
12
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7HJXZ4NM.js";
|
|
13
19
|
import {
|
|
14
20
|
createApproval,
|
|
15
21
|
createJoinRequest,
|
|
22
|
+
getEasExplorerUrl,
|
|
16
23
|
getEasScanUrl,
|
|
17
24
|
parseReferrer,
|
|
18
25
|
queryApprovals,
|
|
19
26
|
queryJoinRequests,
|
|
20
|
-
|
|
27
|
+
revokeJoinRequest,
|
|
21
28
|
stripReferrerPrefix
|
|
22
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-E6LH2FJ6.js";
|
|
23
30
|
import {
|
|
24
31
|
createSyndicate,
|
|
25
32
|
getActiveSyndicates,
|
|
26
33
|
getSyndicate,
|
|
27
34
|
subdomainExists,
|
|
28
35
|
updateMetadata
|
|
29
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-IS2CLLUT.js";
|
|
30
37
|
import {
|
|
31
38
|
approveDepositor,
|
|
32
39
|
deposit,
|
|
@@ -47,9 +54,10 @@ import {
|
|
|
47
54
|
removeDepositor,
|
|
48
55
|
resolveSyndicate,
|
|
49
56
|
resolveVaultSyndicate,
|
|
57
|
+
searchSyndicateAcrossChains,
|
|
50
58
|
setTextRecord,
|
|
51
59
|
setVaultAddress
|
|
52
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-R7X33D2S.js";
|
|
53
61
|
import {
|
|
54
62
|
PROPOSAL_STATE,
|
|
55
63
|
PROPOSAL_STATES,
|
|
@@ -81,12 +89,11 @@ import {
|
|
|
81
89
|
setVotingPeriod,
|
|
82
90
|
settleProposal,
|
|
83
91
|
vote
|
|
84
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-KJ35EF7N.js";
|
|
85
93
|
import {
|
|
86
94
|
AERODROME,
|
|
87
95
|
AGENT_REGISTRY,
|
|
88
96
|
CHAINLINK,
|
|
89
|
-
EAS_SCHEMAS,
|
|
90
97
|
MOONWELL,
|
|
91
98
|
SHERWOOD,
|
|
92
99
|
STRATEGY_TEMPLATES,
|
|
@@ -94,7 +101,7 @@ import {
|
|
|
94
101
|
TOKENS,
|
|
95
102
|
UNISWAP,
|
|
96
103
|
VENICE
|
|
97
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-FWC57PT7.js";
|
|
98
105
|
import {
|
|
99
106
|
formatContractError,
|
|
100
107
|
getAccount,
|
|
@@ -103,7 +110,7 @@ import {
|
|
|
103
110
|
sendTxWithRetry,
|
|
104
111
|
waitForReceipt,
|
|
105
112
|
writeContractWithRetry
|
|
106
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-XDBRKW7I.js";
|
|
107
114
|
import {
|
|
108
115
|
CHAIN_REGISTRY,
|
|
109
116
|
VALID_NETWORKS,
|
|
@@ -112,8 +119,9 @@ import {
|
|
|
112
119
|
getNetwork,
|
|
113
120
|
getRpcUrl,
|
|
114
121
|
isTestnet,
|
|
115
|
-
setNetwork
|
|
116
|
-
|
|
122
|
+
setNetwork,
|
|
123
|
+
withNetwork
|
|
124
|
+
} from "./chunk-2LRC7I33.js";
|
|
117
125
|
import {
|
|
118
126
|
addSyndicate,
|
|
119
127
|
cacheGroupId,
|
|
@@ -129,6 +137,7 @@ import {
|
|
|
129
137
|
getVeniceApiKey,
|
|
130
138
|
loadConfig,
|
|
131
139
|
setAgentId,
|
|
140
|
+
setAnthropicApiKey,
|
|
132
141
|
setChainContract,
|
|
133
142
|
setConfigRpcUrl,
|
|
134
143
|
setNotifyTo,
|
|
@@ -136,7 +145,7 @@ import {
|
|
|
136
145
|
setPrivateKey,
|
|
137
146
|
setUniswapApiKey,
|
|
138
147
|
setVeniceApiKey
|
|
139
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-2W4KGGPU.js";
|
|
140
149
|
import {
|
|
141
150
|
BASE_STRATEGY_ABI,
|
|
142
151
|
ERC20_ABI,
|
|
@@ -156,7 +165,7 @@ import { config as loadDotenv } from "dotenv";
|
|
|
156
165
|
import { createRequire } from "module";
|
|
157
166
|
import { Command, Option } from "commander";
|
|
158
167
|
import { parseUnits as parseUnits6, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
|
|
159
|
-
import
|
|
168
|
+
import chalk13 from "chalk";
|
|
160
169
|
import ora8 from "ora";
|
|
161
170
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
162
171
|
|
|
@@ -1445,9 +1454,9 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1445
1454
|
console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
|
|
1446
1455
|
process.exit(1);
|
|
1447
1456
|
}
|
|
1448
|
-
const { propose: propose2 } = await import("./governor-
|
|
1457
|
+
const { propose: propose2 } = await import("./governor-MG5VVSU4.js");
|
|
1449
1458
|
const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
|
|
1450
|
-
const { parseDuration: parseDuration2 } = await import("./governor-
|
|
1459
|
+
const { parseDuration: parseDuration2 } = await import("./governor-MG5VVSU4.js");
|
|
1451
1460
|
const performanceFeeBps = BigInt(opts.performanceFee);
|
|
1452
1461
|
if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
|
|
1453
1462
|
console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
|
|
@@ -1554,7 +1563,7 @@ function registerStrategyTemplateCommands(strategy2) {
|
|
|
1554
1563
|
let prices = [];
|
|
1555
1564
|
let priceSource = null;
|
|
1556
1565
|
try {
|
|
1557
|
-
const { getTokenPricesInAsset } = await import("./price-
|
|
1566
|
+
const { getTokenPricesInAsset } = await import("./price-WCCXWPDM.js");
|
|
1558
1567
|
prices = await getTokenPricesInAsset({
|
|
1559
1568
|
tokens: allocations.map((a, i) => ({
|
|
1560
1569
|
token: a.token,
|
|
@@ -1984,7 +1993,7 @@ function registerVeniceCommands(program2) {
|
|
|
1984
1993
|
console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
|
|
1985
1994
|
console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
|
|
1986
1995
|
try {
|
|
1987
|
-
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
1996
|
+
const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
|
|
1988
1997
|
const { uid } = await createVeniceProvisionAttestation(account.address);
|
|
1989
1998
|
if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
1990
1999
|
console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
|
|
@@ -2340,10 +2349,10 @@ ${opts.prompt}`;
|
|
|
2340
2349
|
console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
|
|
2341
2350
|
}
|
|
2342
2351
|
try {
|
|
2343
|
-
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-
|
|
2352
|
+
const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
|
|
2344
2353
|
const { keccak256, toHex, isAddress: isAddr } = await import("viem");
|
|
2345
|
-
const { getChainContracts: getChainContracts2 } = await import("./config-
|
|
2346
|
-
const { getChain: getActiveChain } = await import("./network-
|
|
2354
|
+
const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
|
|
2355
|
+
const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
|
|
2347
2356
|
const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
|
|
2348
2357
|
const promptHash = keccak256(toHex(userContent)).slice(0, 18);
|
|
2349
2358
|
const { uid } = await createVeniceInferenceAttestation(
|
|
@@ -2738,6 +2747,50 @@ function registerIdentityCommands(program2) {
|
|
|
2738
2747
|
process.exit(1);
|
|
2739
2748
|
}
|
|
2740
2749
|
});
|
|
2750
|
+
identity.command("find").description("Search ERC-8004 agents by wallet address or name (recover a lost agent ID)").option("--wallet <address>", "Filter by owner / wallet address").option("--name <text>", "Filter by agent name (exact or substring match, depending on registry)").option("--all-chains", "Search across all chains, not just the active one").action(async (opts) => {
|
|
2751
|
+
if (!opts.wallet && !opts.name) {
|
|
2752
|
+
console.error(chalk4.red("Pass --wallet <address> or --name <text> (or both)."));
|
|
2753
|
+
process.exit(1);
|
|
2754
|
+
}
|
|
2755
|
+
const spinner = ora4("Searching ERC-8004 registry...").start();
|
|
2756
|
+
try {
|
|
2757
|
+
const sdk = getAgent0SDK();
|
|
2758
|
+
const filters = {};
|
|
2759
|
+
if (opts.wallet) filters.walletAddress = opts.wallet;
|
|
2760
|
+
if (opts.name) filters.name = opts.name;
|
|
2761
|
+
filters.chains = opts.allChains ? "all" : [getChain().id];
|
|
2762
|
+
const results = await sdk.searchAgents(filters);
|
|
2763
|
+
spinner.stop();
|
|
2764
|
+
if (results.length === 0) {
|
|
2765
|
+
console.log(chalk4.dim("No agents found matching those filters."));
|
|
2766
|
+
console.log(chalk4.dim("If you minted on a different chain, retry with --all-chains."));
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
console.log();
|
|
2770
|
+
console.log(chalk4.bold(`Found ${results.length} agent${results.length === 1 ? "" : "s"}`));
|
|
2771
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
2772
|
+
for (const a of results) {
|
|
2773
|
+
const idStr = String(a.agentId);
|
|
2774
|
+
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
2775
|
+
console.log(` ${chalk4.bold(`#${tokenId}`)} ${a.name || chalk4.dim("(no name)")}`);
|
|
2776
|
+
console.log(chalk4.dim(` Chain: ${a.chainId}`));
|
|
2777
|
+
console.log(chalk4.dim(` Full: ${idStr}`));
|
|
2778
|
+
if (a.owners?.length) {
|
|
2779
|
+
console.log(chalk4.dim(` Owner: ${a.owners[0]}`));
|
|
2780
|
+
}
|
|
2781
|
+
if (a.walletAddress) {
|
|
2782
|
+
console.log(chalk4.dim(` Wallet: ${a.walletAddress}`));
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
console.log();
|
|
2786
|
+
console.log(chalk4.green("To bind one of these to this machine:"));
|
|
2787
|
+
console.log(chalk4.dim(` sherwood identity load --id <tokenId>`));
|
|
2788
|
+
} catch (err) {
|
|
2789
|
+
spinner.fail("Search failed");
|
|
2790
|
+
console.error(chalk4.red(formatContractError(err)));
|
|
2791
|
+
process.exit(1);
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2741
2794
|
identity.command("status").description("Show your agent identity status").action(async () => {
|
|
2742
2795
|
const account = getAccount();
|
|
2743
2796
|
const registry2 = AGENT_REGISTRY().IDENTITY_REGISTRY;
|
|
@@ -3472,8 +3525,8 @@ function printSimulationResult(result, callType) {
|
|
|
3472
3525
|
}
|
|
3473
3526
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3474
3527
|
try {
|
|
3475
|
-
const xmtp = await import("./xmtp-
|
|
3476
|
-
const { getAccount: getAccount2 } = await import("./client-
|
|
3528
|
+
const xmtp = await import("./xmtp-ACB2W573.js");
|
|
3529
|
+
const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
|
|
3477
3530
|
const group = await xmtp.getGroup("", subdomain);
|
|
3478
3531
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
3479
3532
|
const criticals = allRisks.filter((r) => r.level === "critical");
|
|
@@ -4564,6 +4617,740 @@ function registerGuardianCommands(program2) {
|
|
|
4564
4617
|
});
|
|
4565
4618
|
}
|
|
4566
4619
|
|
|
4620
|
+
// src/commands/grid.ts
|
|
4621
|
+
import chalk12 from "chalk";
|
|
4622
|
+
|
|
4623
|
+
// src/grid/loop.ts
|
|
4624
|
+
import chalk11 from "chalk";
|
|
4625
|
+
import { appendFile, mkdir as mkdir3 } from "fs/promises";
|
|
4626
|
+
import { join as join3 } from "path";
|
|
4627
|
+
import { homedir as homedir3 } from "os";
|
|
4628
|
+
|
|
4629
|
+
// src/grid/manager.ts
|
|
4630
|
+
import chalk9 from "chalk";
|
|
4631
|
+
|
|
4632
|
+
// src/grid/portfolio.ts
|
|
4633
|
+
import { readFile, writeFile, rename, mkdir } from "fs/promises";
|
|
4634
|
+
import { join, dirname } from "path";
|
|
4635
|
+
import { homedir } from "os";
|
|
4636
|
+
var GRID_STATE_PATH = join(homedir(), ".sherwood", "grid", "portfolio.json");
|
|
4637
|
+
function emptyStats() {
|
|
4638
|
+
return {
|
|
4639
|
+
totalRoundTrips: 0,
|
|
4640
|
+
totalPnlUsd: 0,
|
|
4641
|
+
todayPnlUsd: 0,
|
|
4642
|
+
totalFills: 0,
|
|
4643
|
+
todayFills: 0,
|
|
4644
|
+
lastDailyReset: 0,
|
|
4645
|
+
lastRebalanceAt: 0
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
var GridPortfolio = class {
|
|
4649
|
+
state = null;
|
|
4650
|
+
/** Load grid state from disk. Returns null if no grid initialized yet. */
|
|
4651
|
+
async load() {
|
|
4652
|
+
try {
|
|
4653
|
+
const raw = await readFile(GRID_STATE_PATH, "utf-8");
|
|
4654
|
+
this.state = JSON.parse(raw);
|
|
4655
|
+
return this.state;
|
|
4656
|
+
} catch {
|
|
4657
|
+
return null;
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
/** Save grid state to disk (atomic write). */
|
|
4661
|
+
async save(state) {
|
|
4662
|
+
this.state = state;
|
|
4663
|
+
await mkdir(dirname(GRID_STATE_PATH), { recursive: true });
|
|
4664
|
+
const tmp = `${GRID_STATE_PATH}.tmp.${process.pid}`;
|
|
4665
|
+
await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
4666
|
+
await rename(tmp, GRID_STATE_PATH);
|
|
4667
|
+
}
|
|
4668
|
+
/**
|
|
4669
|
+
* Initialize the grid by carving capital from the directional portfolio.
|
|
4670
|
+
* Called once on first startup when grid-portfolio.json doesn't exist.
|
|
4671
|
+
*
|
|
4672
|
+
* Returns the USD amount carved out (caller must deduct from directional portfolio).
|
|
4673
|
+
*/
|
|
4674
|
+
async initialize(capital, config) {
|
|
4675
|
+
const allocation = capital;
|
|
4676
|
+
const grids = config.tokens.map((token) => ({
|
|
4677
|
+
token,
|
|
4678
|
+
levels: [],
|
|
4679
|
+
openFills: [],
|
|
4680
|
+
allocation: allocation * (config.tokenSplit[token] ?? 1 / config.tokens.length),
|
|
4681
|
+
stats: emptyStats(),
|
|
4682
|
+
centerPrice: 0,
|
|
4683
|
+
atr: 0
|
|
4684
|
+
}));
|
|
4685
|
+
const state = {
|
|
4686
|
+
totalAllocation: allocation,
|
|
4687
|
+
grids,
|
|
4688
|
+
paused: false,
|
|
4689
|
+
pauseReason: "",
|
|
4690
|
+
initializedAt: Date.now()
|
|
4691
|
+
};
|
|
4692
|
+
await this.save(state);
|
|
4693
|
+
this.state = state;
|
|
4694
|
+
}
|
|
4695
|
+
/** Get current state (must load() first). */
|
|
4696
|
+
getState() {
|
|
4697
|
+
return this.state;
|
|
4698
|
+
}
|
|
4699
|
+
/** Reset daily counters if UTC day boundary crossed. */
|
|
4700
|
+
resetDailyStats(state) {
|
|
4701
|
+
const now = Date.now();
|
|
4702
|
+
const todayMidnight = /* @__PURE__ */ new Date();
|
|
4703
|
+
todayMidnight.setUTCHours(0, 0, 0, 0);
|
|
4704
|
+
const todayMs = todayMidnight.getTime();
|
|
4705
|
+
let changed = false;
|
|
4706
|
+
for (const grid of state.grids) {
|
|
4707
|
+
if (!grid.stats.lastDailyReset || grid.stats.lastDailyReset < todayMs) {
|
|
4708
|
+
grid.stats.todayPnlUsd = 0;
|
|
4709
|
+
grid.stats.todayFills = 0;
|
|
4710
|
+
grid.stats.lastDailyReset = now;
|
|
4711
|
+
changed = true;
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
return changed;
|
|
4715
|
+
}
|
|
4716
|
+
/** Check if grid should be paused (pool dropped below threshold). */
|
|
4717
|
+
checkPauseThreshold(state, config) {
|
|
4718
|
+
const currentValue = state.grids.reduce((sum, g) => {
|
|
4719
|
+
const openFillValue = g.openFills.filter((f) => !f.closed).reduce((s, f) => s + f.quantity * f.buyPrice, 0);
|
|
4720
|
+
return sum + g.allocation + openFillValue;
|
|
4721
|
+
}, 0);
|
|
4722
|
+
const dropPct = 1 - currentValue / state.totalAllocation;
|
|
4723
|
+
if (dropPct >= config.pauseThresholdPct) {
|
|
4724
|
+
state.paused = true;
|
|
4725
|
+
state.pauseReason = `Grid pool dropped ${(dropPct * 100).toFixed(1)}% (threshold: ${(config.pauseThresholdPct * 100).toFixed(0)}%)`;
|
|
4726
|
+
return true;
|
|
4727
|
+
}
|
|
4728
|
+
return false;
|
|
4729
|
+
}
|
|
4730
|
+
/** Aggregate stats across all token grids. */
|
|
4731
|
+
aggregateStats(state) {
|
|
4732
|
+
return {
|
|
4733
|
+
totalPnlUsd: state.grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),
|
|
4734
|
+
todayPnlUsd: state.grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),
|
|
4735
|
+
todayFills: state.grids.reduce((s, g) => s + g.stats.todayFills, 0),
|
|
4736
|
+
totalRoundTrips: state.grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0)
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
};
|
|
4740
|
+
|
|
4741
|
+
// src/grid/manager.ts
|
|
4742
|
+
var GridManager = class {
|
|
4743
|
+
config;
|
|
4744
|
+
portfolio;
|
|
4745
|
+
hl;
|
|
4746
|
+
constructor(config) {
|
|
4747
|
+
this.config = config;
|
|
4748
|
+
this.portfolio = new GridPortfolio();
|
|
4749
|
+
this.hl = new HyperliquidProvider();
|
|
4750
|
+
}
|
|
4751
|
+
/** Load or initialize grid state.
|
|
4752
|
+
* Also syncs new tokens added to config after initial grid setup. */
|
|
4753
|
+
async init(totalPortfolioValue) {
|
|
4754
|
+
const existing = await this.portfolio.load();
|
|
4755
|
+
if (!existing) {
|
|
4756
|
+
await this.portfolio.initialize(totalPortfolioValue, this.config);
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
let addedAllocation = 0;
|
|
4760
|
+
for (const token of this.config.tokens) {
|
|
4761
|
+
if (!existing.grids.find((g) => g.token === token)) {
|
|
4762
|
+
const split = this.config.tokenSplit[token] ?? 1 / this.config.tokens.length;
|
|
4763
|
+
const alloc = existing.totalAllocation * split;
|
|
4764
|
+
existing.grids.push({
|
|
4765
|
+
token,
|
|
4766
|
+
levels: [],
|
|
4767
|
+
openFills: [],
|
|
4768
|
+
allocation: alloc,
|
|
4769
|
+
stats: { totalRoundTrips: 0, totalPnlUsd: 0, todayPnlUsd: 0, totalFills: 0, todayFills: 0, lastDailyReset: 0, lastRebalanceAt: 0 },
|
|
4770
|
+
centerPrice: 0,
|
|
4771
|
+
atr: 0
|
|
4772
|
+
});
|
|
4773
|
+
addedAllocation += alloc;
|
|
4774
|
+
console.error(` [grid] Added new token grid: ${token} ($${alloc.toFixed(0)} allocation)`);
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
if (addedAllocation > 0) {
|
|
4778
|
+
await this.portfolio.save(existing);
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* Run one grid tick — called every cycle from the agent loop.
|
|
4783
|
+
*
|
|
4784
|
+
* 1. Load state + reset daily counters
|
|
4785
|
+
* 2. For each token grid: fetch price, simulate fills, check rebalance
|
|
4786
|
+
* 3. Persist state
|
|
4787
|
+
*
|
|
4788
|
+
* Returns a summary for the cycle log.
|
|
4789
|
+
*/
|
|
4790
|
+
async tick(prices, regime) {
|
|
4791
|
+
const state = await this.portfolio.load();
|
|
4792
|
+
if (!state || state.paused || !this.config.enabled) {
|
|
4793
|
+
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: state?.paused ?? false };
|
|
4794
|
+
}
|
|
4795
|
+
if (regime === "high-volatility") {
|
|
4796
|
+
console.error(chalk9.dim(` [grid] Paused: high-volatility regime \u2014 too risky for grid`));
|
|
4797
|
+
return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: false };
|
|
4798
|
+
}
|
|
4799
|
+
this.portfolio.resetDailyStats(state);
|
|
4800
|
+
let totalFills = 0;
|
|
4801
|
+
let totalRoundTrips = 0;
|
|
4802
|
+
let totalPnl = 0;
|
|
4803
|
+
for (const grid of state.grids) {
|
|
4804
|
+
const price = prices[grid.token];
|
|
4805
|
+
if (!price || price <= 0) continue;
|
|
4806
|
+
if (grid.levels.length === 0 || this.needsFullRebuild(grid)) {
|
|
4807
|
+
await this.buildGrid(grid, price);
|
|
4808
|
+
}
|
|
4809
|
+
const { fills, roundTrips, pnlUsd } = this.simulateFills(grid, price);
|
|
4810
|
+
totalFills += fills;
|
|
4811
|
+
totalRoundTrips += roundTrips;
|
|
4812
|
+
totalPnl += pnlUsd;
|
|
4813
|
+
if (this.needsShift(grid, price)) {
|
|
4814
|
+
await this.shiftGrid(grid, price);
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
this.portfolio.checkPauseThreshold(state, this.config);
|
|
4818
|
+
await this.portfolio.save(state);
|
|
4819
|
+
return { fills: totalFills, roundTrips: totalRoundTrips, pnlUsd: totalPnl, paused: false };
|
|
4820
|
+
}
|
|
4821
|
+
/** Build a fresh grid centered on the current price using ATR. */
|
|
4822
|
+
async buildGrid(grid, currentPrice) {
|
|
4823
|
+
const candles = await this.hl.getCandles(grid.token, "4h", 14 * 24 * 60 * 60 * 1e3);
|
|
4824
|
+
if (!candles || candles.length < this.config.atrPeriod) {
|
|
4825
|
+
console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: insufficient candle data`));
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
const signals = getLatestSignals(candles);
|
|
4829
|
+
const atr = signals.atr;
|
|
4830
|
+
if (!Number.isFinite(atr) || atr <= 0) {
|
|
4831
|
+
console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: invalid ATR (${atr})`));
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
const range = atr * this.config.atrMultiplier;
|
|
4835
|
+
const spacing = range / this.config.levelsPerSide;
|
|
4836
|
+
const effectiveCapital = grid.allocation * this.config.leverage;
|
|
4837
|
+
const quantity = effectiveCapital / (this.config.levelsPerSide * currentPrice);
|
|
4838
|
+
const levels = [];
|
|
4839
|
+
for (let i = 1; i <= this.config.levelsPerSide; i++) {
|
|
4840
|
+
levels.push({
|
|
4841
|
+
price: currentPrice - spacing * i,
|
|
4842
|
+
side: "buy",
|
|
4843
|
+
quantity,
|
|
4844
|
+
filled: false,
|
|
4845
|
+
filledAt: 0
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
for (let i = 1; i <= this.config.levelsPerSide; i++) {
|
|
4849
|
+
levels.push({
|
|
4850
|
+
price: currentPrice + spacing * i,
|
|
4851
|
+
side: "sell",
|
|
4852
|
+
quantity,
|
|
4853
|
+
filled: false,
|
|
4854
|
+
filledAt: 0
|
|
4855
|
+
});
|
|
4856
|
+
}
|
|
4857
|
+
grid.levels = levels;
|
|
4858
|
+
grid.centerPrice = currentPrice;
|
|
4859
|
+
grid.atr = atr;
|
|
4860
|
+
grid.stats.lastRebalanceAt = Date.now();
|
|
4861
|
+
console.error(chalk9.dim(
|
|
4862
|
+
` [grid] Built ${grid.token} grid: center=$${currentPrice.toFixed(2)} ATR=$${atr.toFixed(2)} range=$${(currentPrice - range).toFixed(2)}-$${(currentPrice + range).toFixed(2)} spacing=$${spacing.toFixed(2)} qty=${quantity.toFixed(6)}`
|
|
4863
|
+
));
|
|
4864
|
+
}
|
|
4865
|
+
/** Simulate order fills against the current price. */
|
|
4866
|
+
simulateFills(grid, currentPrice) {
|
|
4867
|
+
let fills = 0;
|
|
4868
|
+
let roundTrips = 0;
|
|
4869
|
+
let pnlUsd = 0;
|
|
4870
|
+
const now = Date.now();
|
|
4871
|
+
const spacing = grid.atr > 0 ? grid.atr * this.config.atrMultiplier / this.config.levelsPerSide : 0;
|
|
4872
|
+
if (spacing <= 0) return { fills: 0, roundTrips: 0, pnlUsd: 0 };
|
|
4873
|
+
for (const level of grid.levels) {
|
|
4874
|
+
if (level.filled) continue;
|
|
4875
|
+
if (level.side === "buy" && currentPrice <= level.price) {
|
|
4876
|
+
level.filled = true;
|
|
4877
|
+
level.filledAt = now;
|
|
4878
|
+
fills++;
|
|
4879
|
+
grid.stats.totalFills++;
|
|
4880
|
+
grid.stats.todayFills++;
|
|
4881
|
+
grid.openFills.push({
|
|
4882
|
+
token: grid.token,
|
|
4883
|
+
buyPrice: level.price,
|
|
4884
|
+
targetSellPrice: level.price + spacing,
|
|
4885
|
+
quantity: level.quantity,
|
|
4886
|
+
filledAt: now,
|
|
4887
|
+
closed: false,
|
|
4888
|
+
pnlUsd: 0,
|
|
4889
|
+
closedAt: 0
|
|
4890
|
+
});
|
|
4891
|
+
console.error(chalk9.dim(
|
|
4892
|
+
` [grid] BUY fill ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
4893
|
+
));
|
|
4894
|
+
}
|
|
4895
|
+
if (level.side === "sell" && currentPrice >= level.price) {
|
|
4896
|
+
level.filled = true;
|
|
4897
|
+
level.filledAt = now;
|
|
4898
|
+
fills++;
|
|
4899
|
+
grid.stats.totalFills++;
|
|
4900
|
+
grid.stats.todayFills++;
|
|
4901
|
+
console.error(chalk9.dim(
|
|
4902
|
+
` [grid] SELL level ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
|
|
4903
|
+
));
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
for (const openFill of grid.openFills) {
|
|
4907
|
+
if (openFill.closed) continue;
|
|
4908
|
+
if (currentPrice >= openFill.targetSellPrice) {
|
|
4909
|
+
const profit = (currentPrice - openFill.buyPrice) * openFill.quantity * this.config.leverage;
|
|
4910
|
+
if (profit >= this.config.minProfitPerFillUsd) {
|
|
4911
|
+
openFill.closed = true;
|
|
4912
|
+
openFill.pnlUsd = profit;
|
|
4913
|
+
openFill.closedAt = now;
|
|
4914
|
+
pnlUsd += profit;
|
|
4915
|
+
roundTrips++;
|
|
4916
|
+
grid.stats.totalRoundTrips++;
|
|
4917
|
+
grid.stats.totalPnlUsd += profit;
|
|
4918
|
+
grid.stats.todayPnlUsd += profit;
|
|
4919
|
+
grid.allocation += profit;
|
|
4920
|
+
console.error(chalk9.green(
|
|
4921
|
+
` [grid] ROUND-TRIP ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 sell $${currentPrice.toFixed(2)} = +$${profit.toFixed(2)}`
|
|
4922
|
+
));
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
const pruneThreshold = now - 24 * 60 * 60 * 1e3;
|
|
4927
|
+
grid.openFills = grid.openFills.filter((f) => !f.closed || f.closedAt > pruneThreshold);
|
|
4928
|
+
return { fills, roundTrips, pnlUsd };
|
|
4929
|
+
}
|
|
4930
|
+
/** Check if the grid needs a full rebuild. */
|
|
4931
|
+
needsFullRebuild(grid) {
|
|
4932
|
+
if (grid.levels.length === 0) return true;
|
|
4933
|
+
const elapsed = Date.now() - grid.stats.lastRebalanceAt;
|
|
4934
|
+
return elapsed >= this.config.fullRebuildIntervalMs;
|
|
4935
|
+
}
|
|
4936
|
+
/** Check if price has drifted enough to warrant shifting the grid. */
|
|
4937
|
+
needsShift(grid, currentPrice) {
|
|
4938
|
+
if (grid.centerPrice <= 0 || grid.atr <= 0) return false;
|
|
4939
|
+
const range = grid.atr * this.config.atrMultiplier;
|
|
4940
|
+
const distFromCenter = Math.abs(currentPrice - grid.centerPrice);
|
|
4941
|
+
return distFromCenter / range >= this.config.rebalanceDriftPct;
|
|
4942
|
+
}
|
|
4943
|
+
/** Shift the grid to re-center on the current price. */
|
|
4944
|
+
async shiftGrid(grid, currentPrice) {
|
|
4945
|
+
console.error(chalk9.dim(
|
|
4946
|
+
` [grid] Shifting ${grid.token} grid: center $${grid.centerPrice.toFixed(2)} \u2192 $${currentPrice.toFixed(2)}`
|
|
4947
|
+
));
|
|
4948
|
+
await this.buildGrid(grid, currentPrice);
|
|
4949
|
+
}
|
|
4950
|
+
/** Get open fill exposure per token — used by the hedge manager. */
|
|
4951
|
+
getOpenFillExposure() {
|
|
4952
|
+
const state = this.portfolio.getState();
|
|
4953
|
+
if (!state) return [];
|
|
4954
|
+
return state.grids.map((g) => {
|
|
4955
|
+
const opens = g.openFills.filter((f) => !f.closed);
|
|
4956
|
+
const totalQty = opens.reduce((s, f) => s + f.quantity, 0);
|
|
4957
|
+
const totalNotional = opens.reduce((s, f) => s + f.quantity * f.buyPrice, 0);
|
|
4958
|
+
return {
|
|
4959
|
+
token: g.token,
|
|
4960
|
+
totalQuantity: totalQty,
|
|
4961
|
+
totalNotional,
|
|
4962
|
+
avgEntryPrice: totalQty > 0 ? totalNotional / totalQty : 0,
|
|
4963
|
+
fillCount: opens.length
|
|
4964
|
+
};
|
|
4965
|
+
}).filter((e) => e.fillCount > 0);
|
|
4966
|
+
}
|
|
4967
|
+
/** Get aggregate stats for display. */
|
|
4968
|
+
getStats() {
|
|
4969
|
+
const state = this.portfolio.getState();
|
|
4970
|
+
if (!state) return null;
|
|
4971
|
+
const agg = this.portfolio.aggregateStats(state);
|
|
4972
|
+
return {
|
|
4973
|
+
...agg,
|
|
4974
|
+
allocation: state.totalAllocation,
|
|
4975
|
+
paused: state.paused
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
};
|
|
4979
|
+
|
|
4980
|
+
// src/grid/config.ts
|
|
4981
|
+
var DEFAULT_GRID_CONFIG = {
|
|
4982
|
+
enabled: true,
|
|
4983
|
+
tokens: ["bitcoin", "ethereum", "solana"],
|
|
4984
|
+
allocationPct: 0.5,
|
|
4985
|
+
leverage: 5,
|
|
4986
|
+
// was 4 — +25% profit per RT
|
|
4987
|
+
levelsPerSide: 15,
|
|
4988
|
+
atrMultiplier: 2,
|
|
4989
|
+
atrPeriod: 14,
|
|
4990
|
+
rebalanceDriftPct: 0.4,
|
|
4991
|
+
// was 0.55 — rebalance faster, keep levels near price
|
|
4992
|
+
fullRebuildIntervalMs: 12 * 60 * 60 * 1e3,
|
|
4993
|
+
// 12h
|
|
4994
|
+
tokenSplit: { bitcoin: 0.45, ethereum: 0.3, solana: 0.25 },
|
|
4995
|
+
minProfitPerFillUsd: 0.5,
|
|
4996
|
+
pauseThresholdPct: 0.2
|
|
4997
|
+
};
|
|
4998
|
+
|
|
4999
|
+
// src/grid/hedge.ts
|
|
5000
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
5001
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
5002
|
+
import { homedir as homedir2 } from "os";
|
|
5003
|
+
import chalk10 from "chalk";
|
|
5004
|
+
var HEDGE_RATIO = 0.3;
|
|
5005
|
+
var MIN_FILLS_TO_HEDGE = 3;
|
|
5006
|
+
var ADJUSTMENT_THRESHOLD = 0.1;
|
|
5007
|
+
var HEDGE_STATE_PATH = join2(homedir2(), ".sherwood", "grid", "hedge.json");
|
|
5008
|
+
var GridHedgeManager = class {
|
|
5009
|
+
state = null;
|
|
5010
|
+
async load() {
|
|
5011
|
+
if (this.state) return this.state;
|
|
5012
|
+
try {
|
|
5013
|
+
const raw = await readFile2(HEDGE_STATE_PATH, "utf-8");
|
|
5014
|
+
this.state = JSON.parse(raw);
|
|
5015
|
+
} catch {
|
|
5016
|
+
this.state = {
|
|
5017
|
+
positions: [],
|
|
5018
|
+
totalRealizedPnl: 0,
|
|
5019
|
+
todayRealizedPnl: 0,
|
|
5020
|
+
lastDailyReset: Date.now()
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
const todayMidnight = /* @__PURE__ */ new Date();
|
|
5024
|
+
todayMidnight.setUTCHours(0, 0, 0, 0);
|
|
5025
|
+
if (this.state.lastDailyReset < todayMidnight.getTime()) {
|
|
5026
|
+
this.state.todayRealizedPnl = 0;
|
|
5027
|
+
this.state.lastDailyReset = Date.now();
|
|
5028
|
+
}
|
|
5029
|
+
return this.state;
|
|
5030
|
+
}
|
|
5031
|
+
async save() {
|
|
5032
|
+
if (!this.state) return;
|
|
5033
|
+
await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
|
|
5034
|
+
const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
|
|
5035
|
+
await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
|
|
5036
|
+
const { rename: rename2 } = await import("fs/promises");
|
|
5037
|
+
await rename2(tmp, HEDGE_STATE_PATH);
|
|
5038
|
+
}
|
|
5039
|
+
/**
|
|
5040
|
+
* Run one hedge tick. Call after grid tick with current prices and open fill data.
|
|
5041
|
+
*
|
|
5042
|
+
* @param openFills - Per-token open fill exposure from the grid portfolio
|
|
5043
|
+
* @param prices - Current mark prices per token
|
|
5044
|
+
*/
|
|
5045
|
+
async tick(openFills, prices) {
|
|
5046
|
+
const state = await this.load();
|
|
5047
|
+
let adjustments = 0;
|
|
5048
|
+
let unrealizedPnl = 0;
|
|
5049
|
+
for (const fill of openFills) {
|
|
5050
|
+
const price = prices[fill.token];
|
|
5051
|
+
if (!price || price <= 0) continue;
|
|
5052
|
+
const desiredShortQty = fill.fillCount >= MIN_FILLS_TO_HEDGE ? fill.totalQuantity * HEDGE_RATIO : 0;
|
|
5053
|
+
const existing = state.positions.find((p) => p.token === fill.token);
|
|
5054
|
+
if (desiredShortQty <= 0) {
|
|
5055
|
+
if (existing && existing.quantity > 0) {
|
|
5056
|
+
const closePnl = (existing.entryPrice - price) * existing.quantity;
|
|
5057
|
+
state.totalRealizedPnl += closePnl;
|
|
5058
|
+
state.todayRealizedPnl += closePnl;
|
|
5059
|
+
existing.realizedPnl += closePnl;
|
|
5060
|
+
console.error(chalk10.yellow(
|
|
5061
|
+
` [hedge] CLOSE ${fill.token} short ${existing.quantity.toFixed(6)} @ $${price.toFixed(2)} PnL: $${closePnl.toFixed(2)}`
|
|
5062
|
+
));
|
|
5063
|
+
existing.quantity = 0;
|
|
5064
|
+
existing.entryPrice = 0;
|
|
5065
|
+
adjustments++;
|
|
5066
|
+
}
|
|
5067
|
+
continue;
|
|
5068
|
+
}
|
|
5069
|
+
if (!existing) {
|
|
5070
|
+
state.positions.push({
|
|
5071
|
+
token: fill.token,
|
|
5072
|
+
entryPrice: price,
|
|
5073
|
+
quantity: desiredShortQty,
|
|
5074
|
+
lastAdjustedAt: Date.now(),
|
|
5075
|
+
realizedPnl: 0
|
|
5076
|
+
});
|
|
5077
|
+
console.error(chalk10.magenta(
|
|
5078
|
+
` [hedge] OPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)} (${(HEDGE_RATIO * 100).toFixed(0)}% of ${fill.fillCount} open fills)`
|
|
5079
|
+
));
|
|
5080
|
+
adjustments++;
|
|
5081
|
+
} else if (existing.quantity > 0) {
|
|
5082
|
+
const diff = Math.abs(desiredShortQty - existing.quantity) / existing.quantity;
|
|
5083
|
+
if (diff > ADJUSTMENT_THRESHOLD) {
|
|
5084
|
+
if (desiredShortQty > existing.quantity) {
|
|
5085
|
+
const addQty = desiredShortQty - existing.quantity;
|
|
5086
|
+
const newAvgEntry = (existing.entryPrice * existing.quantity + price * addQty) / desiredShortQty;
|
|
5087
|
+
existing.entryPrice = newAvgEntry;
|
|
5088
|
+
existing.quantity = desiredShortQty;
|
|
5089
|
+
existing.lastAdjustedAt = Date.now();
|
|
5090
|
+
console.error(chalk10.magenta(
|
|
5091
|
+
` [hedge] INCREASE ${fill.token} short to ${desiredShortQty.toFixed(6)} @ avg $${newAvgEntry.toFixed(2)}`
|
|
5092
|
+
));
|
|
5093
|
+
adjustments++;
|
|
5094
|
+
} else {
|
|
5095
|
+
const closeQty = existing.quantity - desiredShortQty;
|
|
5096
|
+
const closePnl = (existing.entryPrice - price) * closeQty;
|
|
5097
|
+
state.totalRealizedPnl += closePnl;
|
|
5098
|
+
state.todayRealizedPnl += closePnl;
|
|
5099
|
+
existing.realizedPnl += closePnl;
|
|
5100
|
+
existing.quantity = desiredShortQty;
|
|
5101
|
+
existing.lastAdjustedAt = Date.now();
|
|
5102
|
+
console.error(chalk10.magenta(
|
|
5103
|
+
` [hedge] DECREASE ${fill.token} short to ${desiredShortQty.toFixed(6)}, closed ${closeQty.toFixed(6)} PnL: $${closePnl.toFixed(2)}`
|
|
5104
|
+
));
|
|
5105
|
+
adjustments++;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
const uPnl = (existing.entryPrice - price) * existing.quantity;
|
|
5109
|
+
unrealizedPnl += uPnl;
|
|
5110
|
+
} else {
|
|
5111
|
+
existing.entryPrice = price;
|
|
5112
|
+
existing.quantity = desiredShortQty;
|
|
5113
|
+
existing.lastAdjustedAt = Date.now();
|
|
5114
|
+
console.error(chalk10.magenta(
|
|
5115
|
+
` [hedge] REOPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)}`
|
|
5116
|
+
));
|
|
5117
|
+
adjustments++;
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
for (const pos of state.positions) {
|
|
5121
|
+
if (pos.quantity <= 0) continue;
|
|
5122
|
+
if (!openFills.find((f) => f.token === pos.token)) {
|
|
5123
|
+
const price = prices[pos.token];
|
|
5124
|
+
if (price) {
|
|
5125
|
+
const closePnl = (pos.entryPrice - price) * pos.quantity;
|
|
5126
|
+
state.totalRealizedPnl += closePnl;
|
|
5127
|
+
state.todayRealizedPnl += closePnl;
|
|
5128
|
+
pos.realizedPnl += closePnl;
|
|
5129
|
+
console.error(chalk10.yellow(
|
|
5130
|
+
` [hedge] CLOSE orphaned ${pos.token} short, PnL: $${closePnl.toFixed(2)}`
|
|
5131
|
+
));
|
|
5132
|
+
pos.quantity = 0;
|
|
5133
|
+
adjustments++;
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
await this.save();
|
|
5138
|
+
return {
|
|
5139
|
+
adjustments,
|
|
5140
|
+
unrealizedPnl,
|
|
5141
|
+
totalRealizedPnl: state.totalRealizedPnl
|
|
5142
|
+
};
|
|
5143
|
+
}
|
|
5144
|
+
/** Get current hedge status for display. */
|
|
5145
|
+
getStatus() {
|
|
5146
|
+
if (!this.state) return null;
|
|
5147
|
+
return {
|
|
5148
|
+
positions: this.state.positions.filter((p) => p.quantity > 0),
|
|
5149
|
+
totalRealizedPnl: this.state.totalRealizedPnl,
|
|
5150
|
+
todayRealizedPnl: this.state.todayRealizedPnl
|
|
5151
|
+
};
|
|
5152
|
+
}
|
|
5153
|
+
};
|
|
5154
|
+
|
|
5155
|
+
// src/grid/loop.ts
|
|
5156
|
+
var GRID_CYCLES_PATH = join3(homedir3(), ".sherwood", "grid", "cycles.jsonl");
|
|
5157
|
+
var GridLoop = class {
|
|
5158
|
+
cfg;
|
|
5159
|
+
gridConfig;
|
|
5160
|
+
manager;
|
|
5161
|
+
hedge;
|
|
5162
|
+
hl;
|
|
5163
|
+
running = false;
|
|
5164
|
+
cycleCount = 0;
|
|
5165
|
+
timer = null;
|
|
5166
|
+
constructor(cfg) {
|
|
5167
|
+
this.cfg = cfg;
|
|
5168
|
+
this.gridConfig = { ...DEFAULT_GRID_CONFIG, ...cfg.config };
|
|
5169
|
+
this.manager = new GridManager(this.gridConfig);
|
|
5170
|
+
this.hedge = new GridHedgeManager();
|
|
5171
|
+
this.hl = new HyperliquidProvider();
|
|
5172
|
+
}
|
|
5173
|
+
/** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
|
|
5174
|
+
async start() {
|
|
5175
|
+
this.running = true;
|
|
5176
|
+
const shutdown = () => {
|
|
5177
|
+
console.error(chalk11.yellow("\n [grid-loop] Shutting down\u2026"));
|
|
5178
|
+
this.running = false;
|
|
5179
|
+
if (this.timer) {
|
|
5180
|
+
clearTimeout(this.timer);
|
|
5181
|
+
this.timer = null;
|
|
5182
|
+
}
|
|
5183
|
+
};
|
|
5184
|
+
process.on("SIGINT", shutdown);
|
|
5185
|
+
process.on("SIGTERM", shutdown);
|
|
5186
|
+
await this.manager.init(this.cfg.capital);
|
|
5187
|
+
console.error(chalk11.cyan(
|
|
5188
|
+
`
|
|
5189
|
+
[grid-loop] Started \u2014 capital=$${this.cfg.capital.toFixed(0)} cycle=${(this.cfg.cycle / 1e3).toFixed(0)}s tokens=[${this.gridConfig.tokens.join(", ")}] leverage=${this.gridConfig.leverage}x levels=${this.gridConfig.levelsPerSide}/side
|
|
5190
|
+
`
|
|
5191
|
+
));
|
|
5192
|
+
while (this.running) {
|
|
5193
|
+
try {
|
|
5194
|
+
await this.tick();
|
|
5195
|
+
} catch (err) {
|
|
5196
|
+
console.error(chalk11.red(` [grid-loop] Tick error: ${err.message}`));
|
|
5197
|
+
}
|
|
5198
|
+
if (this.running) {
|
|
5199
|
+
await new Promise((resolve2) => {
|
|
5200
|
+
this.timer = setTimeout(resolve2, this.cfg.cycle);
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
process.off("SIGINT", shutdown);
|
|
5205
|
+
process.off("SIGTERM", shutdown);
|
|
5206
|
+
console.error(chalk11.yellow(" [grid-loop] Stopped."));
|
|
5207
|
+
}
|
|
5208
|
+
/** Execute one grid cycle. */
|
|
5209
|
+
async tick() {
|
|
5210
|
+
this.cycleCount++;
|
|
5211
|
+
const start = Date.now();
|
|
5212
|
+
const prices = {};
|
|
5213
|
+
for (const token of this.gridConfig.tokens) {
|
|
5214
|
+
const data = await this.hl.getHyperliquidData(token);
|
|
5215
|
+
if (data?.markPrice && data.markPrice > 0) {
|
|
5216
|
+
prices[token] = data.markPrice;
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
if (Object.keys(prices).length === 0) {
|
|
5220
|
+
console.error(chalk11.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
const result = await this.manager.tick(prices);
|
|
5224
|
+
const elapsed = Date.now() - start;
|
|
5225
|
+
if (result.roundTrips > 0) {
|
|
5226
|
+
console.error(chalk11.green(
|
|
5227
|
+
` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
|
|
5228
|
+
));
|
|
5229
|
+
}
|
|
5230
|
+
if (result.fills > 0 && result.roundTrips === 0) {
|
|
5231
|
+
console.error(chalk11.dim(
|
|
5232
|
+
` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
|
|
5233
|
+
));
|
|
5234
|
+
}
|
|
5235
|
+
const openExposure = this.manager.getOpenFillExposure();
|
|
5236
|
+
const hedgeResult = await this.hedge.tick(openExposure, prices);
|
|
5237
|
+
if (hedgeResult.adjustments > 0) {
|
|
5238
|
+
console.error(chalk11.magenta(
|
|
5239
|
+
` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
|
|
5240
|
+
));
|
|
5241
|
+
}
|
|
5242
|
+
const stats = this.manager.getStats();
|
|
5243
|
+
const cycleEntry = {
|
|
5244
|
+
cycleNumber: this.cycleCount,
|
|
5245
|
+
timestamp: Date.now(),
|
|
5246
|
+
gridFills: result.fills,
|
|
5247
|
+
gridRoundTrips: result.roundTrips,
|
|
5248
|
+
gridPnlUsd: result.pnlUsd,
|
|
5249
|
+
totalPnlUsd: stats?.totalPnlUsd ?? 0,
|
|
5250
|
+
todayPnlUsd: stats?.todayPnlUsd ?? 0,
|
|
5251
|
+
totalRoundTrips: stats?.totalRoundTrips ?? 0,
|
|
5252
|
+
allocation: stats?.allocation ?? 0,
|
|
5253
|
+
paused: stats?.paused ?? false,
|
|
5254
|
+
hedgeUnrealizedPnl: hedgeResult.unrealizedPnl,
|
|
5255
|
+
hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
|
|
5256
|
+
};
|
|
5257
|
+
try {
|
|
5258
|
+
await mkdir3(join3(homedir3(), ".sherwood", "grid"), { recursive: true });
|
|
5259
|
+
await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
|
|
5260
|
+
} catch {
|
|
5261
|
+
}
|
|
5262
|
+
if (this.cycleCount % 60 === 0) {
|
|
5263
|
+
const stats2 = this.manager.getStats();
|
|
5264
|
+
if (stats2) {
|
|
5265
|
+
const hedgeStatus = this.hedge.getStatus();
|
|
5266
|
+
const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
|
|
5267
|
+
console.error(chalk11.cyan(
|
|
5268
|
+
` [grid-loop] Status #${this.cycleCount} \u2014 totalPnL=$${stats2.totalPnlUsd.toFixed(2)} todayPnL=$${stats2.todayPnlUsd.toFixed(2)} RTs=${stats2.totalRoundTrips} alloc=$${stats2.allocation.toFixed(0)}${hedgeInfo}${stats2.paused ? " (PAUSED)" : ""}`
|
|
5269
|
+
));
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
};
|
|
5274
|
+
|
|
5275
|
+
// src/commands/grid.ts
|
|
5276
|
+
var DIM5 = chalk12.gray;
|
|
5277
|
+
var G5 = chalk12.green;
|
|
5278
|
+
var BOLD5 = chalk12.white.bold;
|
|
5279
|
+
var W5 = chalk12.white;
|
|
5280
|
+
var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
|
|
5281
|
+
function registerGridCommand(program2) {
|
|
5282
|
+
const grid = program2.command("grid").description("Grid trading strategy \u2014 ATR-based grid on BTC/ETH/SOL");
|
|
5283
|
+
grid.command("start").description("Start the grid trading loop").option("--capital <usd>", "Starting capital in USD", "5000").option("--cycle <seconds>", "Cycle interval in seconds", "60").option("--tokens <list>", "Comma-separated token list", "bitcoin,ethereum,solana").option("--leverage <n>", "Leverage multiplier", "5").option("--levels <n>", "Grid levels per side", "15").action(async (opts) => {
|
|
5284
|
+
const capital = parseFloat(opts.capital);
|
|
5285
|
+
const cycleMs = parseInt(opts.cycle, 10) * 1e3;
|
|
5286
|
+
const tokens = opts.tokens.split(",").map((t) => t.trim());
|
|
5287
|
+
const leverage = parseFloat(opts.leverage);
|
|
5288
|
+
const levels = parseInt(opts.levels, 10);
|
|
5289
|
+
const weight = 1 / tokens.length;
|
|
5290
|
+
const tokenSplit = {};
|
|
5291
|
+
for (const t of tokens) {
|
|
5292
|
+
tokenSplit[t] = weight;
|
|
5293
|
+
}
|
|
5294
|
+
console.log();
|
|
5295
|
+
console.log(G5.bold(" Grid Strategy"));
|
|
5296
|
+
SEP5();
|
|
5297
|
+
console.log(W5(` Capital: $${capital.toLocaleString()}`));
|
|
5298
|
+
console.log(W5(` Cycle: ${opts.cycle}s`));
|
|
5299
|
+
console.log(W5(` Tokens: ${tokens.join(", ")}`));
|
|
5300
|
+
console.log(W5(` Leverage: ${leverage}x`));
|
|
5301
|
+
console.log(W5(` Levels: ${levels}/side`));
|
|
5302
|
+
SEP5();
|
|
5303
|
+
const loop = new GridLoop({
|
|
5304
|
+
capital,
|
|
5305
|
+
cycle: cycleMs,
|
|
5306
|
+
config: {
|
|
5307
|
+
...DEFAULT_GRID_CONFIG,
|
|
5308
|
+
tokens,
|
|
5309
|
+
leverage,
|
|
5310
|
+
levelsPerSide: levels,
|
|
5311
|
+
tokenSplit
|
|
5312
|
+
}
|
|
5313
|
+
});
|
|
5314
|
+
await loop.start();
|
|
5315
|
+
});
|
|
5316
|
+
grid.command("status").description("Show current grid portfolio status").action(async () => {
|
|
5317
|
+
const portfolio = new GridPortfolio();
|
|
5318
|
+
const state = await portfolio.load();
|
|
5319
|
+
if (!state) {
|
|
5320
|
+
console.log(DIM5("\n No grid portfolio found. Run `sherwood grid start` first.\n"));
|
|
5321
|
+
return;
|
|
5322
|
+
}
|
|
5323
|
+
portfolio.resetDailyStats(state);
|
|
5324
|
+
const agg = portfolio.aggregateStats(state);
|
|
5325
|
+
console.log();
|
|
5326
|
+
console.log(G5.bold(" Grid Portfolio"));
|
|
5327
|
+
SEP5();
|
|
5328
|
+
console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
|
|
5329
|
+
console.log(W5(` Status: ${state.paused ? chalk12.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
|
|
5330
|
+
console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
|
|
5331
|
+
SEP5();
|
|
5332
|
+
console.log();
|
|
5333
|
+
console.log(BOLD5(" Token Alloc RTs PnL Today PnL Fills"));
|
|
5334
|
+
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5335
|
+
for (const g of state.grids) {
|
|
5336
|
+
const name = g.token.padEnd(13);
|
|
5337
|
+
const alloc = `$${g.allocation.toFixed(0)}`.padStart(8);
|
|
5338
|
+
const rts = String(g.stats.totalRoundTrips).padStart(6);
|
|
5339
|
+
const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
|
|
5340
|
+
const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
|
|
5341
|
+
const fills = String(g.stats.totalFills).padStart(6);
|
|
5342
|
+
const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5343
|
+
const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5344
|
+
console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
|
|
5345
|
+
}
|
|
5346
|
+
console.log(DIM5(" " + "\u2500".repeat(72)));
|
|
5347
|
+
const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5348
|
+
const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk12.red;
|
|
5349
|
+
console.log(` ${BOLD5("TOTAL".padEnd(13))} ${`$${state.totalAllocation.toFixed(0)}`.padStart(8)} ${String(agg.totalRoundTrips).padStart(6)} ${totalPnlColor(`$${agg.totalPnlUsd.toFixed(2)}`.padStart(10))} ${todayTotalColor(`$${agg.todayPnlUsd.toFixed(2)}`.padStart(10))} ${String(agg.todayFills).padStart(6)}`);
|
|
5350
|
+
console.log();
|
|
5351
|
+
});
|
|
5352
|
+
}
|
|
5353
|
+
|
|
4567
5354
|
// src/index.ts
|
|
4568
5355
|
try {
|
|
4569
5356
|
loadDotenv();
|
|
@@ -4572,20 +5359,20 @@ try {
|
|
|
4572
5359
|
var require2 = createRequire(import.meta.url);
|
|
4573
5360
|
var { version: CLI_VERSION } = require2("../package.json");
|
|
4574
5361
|
async function loadXmtp() {
|
|
4575
|
-
return import("./xmtp-
|
|
5362
|
+
return import("./xmtp-ACB2W573.js");
|
|
4576
5363
|
}
|
|
4577
5364
|
async function loadCron() {
|
|
4578
5365
|
return import("./cron-EOULYO3R.js");
|
|
4579
5366
|
}
|
|
4580
|
-
var
|
|
4581
|
-
var
|
|
4582
|
-
var
|
|
4583
|
-
var
|
|
4584
|
-
var LABEL5 =
|
|
4585
|
-
var
|
|
5367
|
+
var G6 = chalk13.green;
|
|
5368
|
+
var W6 = chalk13.white;
|
|
5369
|
+
var DIM6 = chalk13.gray;
|
|
5370
|
+
var BOLD6 = chalk13.white.bold;
|
|
5371
|
+
var LABEL5 = chalk13.green.bold;
|
|
5372
|
+
var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
|
|
4586
5373
|
function validateAddress(value, name) {
|
|
4587
5374
|
if (!isAddress6(value)) {
|
|
4588
|
-
console.error(
|
|
5375
|
+
console.error(chalk13.red(`Invalid ${name} address: ${value}`));
|
|
4589
5376
|
process.exit(1);
|
|
4590
5377
|
}
|
|
4591
5378
|
return value;
|
|
@@ -4605,7 +5392,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
4605
5392
|
process.env.ENABLE_TESTNET = "true";
|
|
4606
5393
|
if (network !== "base") {
|
|
4607
5394
|
console.warn(
|
|
4608
|
-
|
|
5395
|
+
chalk13.yellow("[warn] --testnet ignored, --chain takes precedence")
|
|
4609
5396
|
);
|
|
4610
5397
|
} else {
|
|
4611
5398
|
network = "base-sepolia";
|
|
@@ -4613,7 +5400,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
|
|
|
4613
5400
|
}
|
|
4614
5401
|
setNetwork(network);
|
|
4615
5402
|
if (getNetwork() !== "base") {
|
|
4616
|
-
console.log(
|
|
5403
|
+
console.log(chalk13.yellow(`[${getNetwork()}]`));
|
|
4617
5404
|
}
|
|
4618
5405
|
});
|
|
4619
5406
|
var syndicate = program.command("syndicate");
|
|
@@ -4621,31 +5408,31 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4621
5408
|
try {
|
|
4622
5409
|
console.log();
|
|
4623
5410
|
console.log(LABEL5(" \u25C6 Create Syndicate"));
|
|
4624
|
-
|
|
5411
|
+
SEP6();
|
|
4625
5412
|
const wallet = getAccount();
|
|
4626
|
-
console.log(
|
|
4627
|
-
console.log(
|
|
4628
|
-
|
|
5413
|
+
console.log(DIM6(` Wallet: ${wallet.address}`));
|
|
5414
|
+
console.log(DIM6(` Network: ${getChain().name}`));
|
|
5415
|
+
SEP6();
|
|
4629
5416
|
const savedAgentId = getAgentId();
|
|
4630
5417
|
const nonInteractive = opts.yes;
|
|
4631
5418
|
const name = opts.name || (nonInteractive ? (() => {
|
|
4632
5419
|
throw new Error("--name is required in non-interactive mode (-y)");
|
|
4633
5420
|
})() : await input({
|
|
4634
|
-
message:
|
|
5421
|
+
message: G6("Syndicate name"),
|
|
4635
5422
|
validate: (v) => v.length > 0 || "Name is required"
|
|
4636
5423
|
}));
|
|
4637
5424
|
const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
|
|
4638
|
-
message:
|
|
5425
|
+
message: G6("ENS subdomain"),
|
|
4639
5426
|
default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
|
|
4640
5427
|
validate: (v) => v.length >= 3 || "Must be at least 3 characters"
|
|
4641
5428
|
}));
|
|
4642
5429
|
const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
|
|
4643
|
-
message:
|
|
5430
|
+
message: G6("Description"),
|
|
4644
5431
|
default: `${name} \u2014 a Sherwood syndicate`
|
|
4645
5432
|
}));
|
|
4646
|
-
const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message:
|
|
5433
|
+
const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G6("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G6("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
|
|
4647
5434
|
const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
|
|
4648
|
-
message:
|
|
5435
|
+
message: G6("Open deposits? (anyone can deposit)"),
|
|
4649
5436
|
default: true
|
|
4650
5437
|
});
|
|
4651
5438
|
const ASSET_SYMBOLS = {
|
|
@@ -4661,14 +5448,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4661
5448
|
asset = opts.asset;
|
|
4662
5449
|
} else {
|
|
4663
5450
|
const supported = Object.keys(ASSET_SYMBOLS).join(", ");
|
|
4664
|
-
console.error(
|
|
5451
|
+
console.error(chalk13.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
|
|
4665
5452
|
process.exit(1);
|
|
4666
5453
|
}
|
|
4667
5454
|
} else if (nonInteractive) {
|
|
4668
5455
|
asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
|
|
4669
5456
|
} else {
|
|
4670
5457
|
const assetChoice = await select({
|
|
4671
|
-
message:
|
|
5458
|
+
message: G6("Vault asset (what token do depositors provide?)"),
|
|
4672
5459
|
choices: [
|
|
4673
5460
|
{ name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
|
|
4674
5461
|
{ name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
|
|
@@ -4684,25 +5471,25 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4684
5471
|
const symbol = `sw${assetSymbol}`;
|
|
4685
5472
|
console.log();
|
|
4686
5473
|
console.log(LABEL5(" \u25C6 Review"));
|
|
4687
|
-
|
|
4688
|
-
console.log(
|
|
4689
|
-
console.log(
|
|
4690
|
-
console.log(
|
|
4691
|
-
console.log(
|
|
4692
|
-
console.log(
|
|
4693
|
-
console.log(
|
|
4694
|
-
console.log(
|
|
4695
|
-
|
|
5474
|
+
SEP6();
|
|
5475
|
+
console.log(W6(` Name: ${BOLD6(name)}`));
|
|
5476
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5477
|
+
console.log(W6(` Description: ${DIM6(description)}`));
|
|
5478
|
+
console.log(W6(` Agent ID: #${agentIdStr}`));
|
|
5479
|
+
console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
|
|
5480
|
+
console.log(W6(` Share token: ${symbol}`));
|
|
5481
|
+
console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk13.red("no (whitelist)")}`));
|
|
5482
|
+
SEP6();
|
|
4696
5483
|
if (!nonInteractive) {
|
|
4697
|
-
const go = await confirm({ message:
|
|
5484
|
+
const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
|
|
4698
5485
|
if (!go) {
|
|
4699
|
-
console.log(
|
|
5486
|
+
console.log(DIM6(" Cancelled."));
|
|
4700
5487
|
return;
|
|
4701
5488
|
}
|
|
4702
5489
|
}
|
|
4703
5490
|
let metadataURI = opts.metadataUri || "";
|
|
4704
5491
|
if (!metadataURI) {
|
|
4705
|
-
const spinner2 = ora8({ text:
|
|
5492
|
+
const spinner2 = ora8({ text: W6("Uploading metadata to IPFS..."), color: "green" }).start();
|
|
4706
5493
|
try {
|
|
4707
5494
|
const metadata = {
|
|
4708
5495
|
schema: "sherwood/syndicate/v1",
|
|
@@ -4716,9 +5503,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4716
5503
|
links: {}
|
|
4717
5504
|
};
|
|
4718
5505
|
metadataURI = await uploadMetadata(metadata);
|
|
4719
|
-
spinner2.succeed(
|
|
5506
|
+
spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
|
|
4720
5507
|
} catch (err) {
|
|
4721
|
-
spinner2.warn(
|
|
5508
|
+
spinner2.warn(chalk13.yellow(`IPFS upload failed \u2014 using inline metadata`));
|
|
4722
5509
|
const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
|
|
4723
5510
|
metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
|
|
4724
5511
|
}
|
|
@@ -4728,43 +5515,43 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4728
5515
|
const existingInfo = await getSyndicate(existingId);
|
|
4729
5516
|
const callerAddress = getAccount().address.toLowerCase();
|
|
4730
5517
|
if (existingInfo.creator.toLowerCase() !== callerAddress) {
|
|
4731
|
-
console.error(
|
|
5518
|
+
console.error(chalk13.red(`
|
|
4732
5519
|
\u2716 Subdomain '${subdomain}' is already taken by another syndicate.
|
|
4733
5520
|
`));
|
|
4734
5521
|
process.exit(1);
|
|
4735
5522
|
}
|
|
4736
|
-
console.log(
|
|
5523
|
+
console.log(chalk13.yellow(`
|
|
4737
5524
|
\u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
|
|
4738
5525
|
`));
|
|
4739
|
-
const spinner2 = ora8({ text:
|
|
5526
|
+
const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
|
|
4740
5527
|
const chainId2 = getChain().id;
|
|
4741
5528
|
setChainContract(chainId2, "vault", existingInfo.vault);
|
|
4742
5529
|
addSyndicate(chainId2, { subdomain, vault: existingInfo.vault, role: "creator" });
|
|
4743
5530
|
setPrimarySyndicate(chainId2, subdomain);
|
|
4744
|
-
spinner2.text =
|
|
5531
|
+
spinner2.text = W6("Checking agent registration...");
|
|
4745
5532
|
try {
|
|
4746
5533
|
setVaultAddress(existingInfo.vault);
|
|
4747
5534
|
const isAlreadyAgent = await isAgent(getAccount().address);
|
|
4748
5535
|
if (!isAlreadyAgent) {
|
|
4749
|
-
spinner2.text =
|
|
5536
|
+
spinner2.text = W6("Registering creator as agent...");
|
|
4750
5537
|
await registerAgent(BigInt(agentIdStr), getAccount().address);
|
|
4751
5538
|
}
|
|
4752
5539
|
} catch {
|
|
4753
5540
|
}
|
|
4754
|
-
spinner2.text =
|
|
5541
|
+
spinner2.text = W6("Checking chat group...");
|
|
4755
5542
|
try {
|
|
4756
5543
|
const xmtp = await loadXmtp();
|
|
4757
5544
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
4758
5545
|
try {
|
|
4759
5546
|
await xmtp.getGroup(xmtpClient, subdomain);
|
|
4760
5547
|
} catch {
|
|
4761
|
-
spinner2.text =
|
|
5548
|
+
spinner2.text = W6("Creating chat group...");
|
|
4762
5549
|
const groupId = await xmtp.createSyndicateGroup(xmtpClient, subdomain, opts.publicChat);
|
|
4763
5550
|
await setTextRecord(subdomain, "xmtpGroupId", groupId, existingInfo.vault);
|
|
4764
5551
|
cacheGroupId(subdomain, groupId);
|
|
4765
5552
|
}
|
|
4766
5553
|
} catch {
|
|
4767
|
-
console.warn(
|
|
5554
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not set up XMTP chat group"));
|
|
4768
5555
|
}
|
|
4769
5556
|
try {
|
|
4770
5557
|
const cron = await loadCron();
|
|
@@ -4774,19 +5561,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4774
5561
|
spinner2.stop();
|
|
4775
5562
|
console.log();
|
|
4776
5563
|
console.log(LABEL5(" \u25C6 Syndicate Setup Resumed"));
|
|
4777
|
-
|
|
4778
|
-
console.log(
|
|
4779
|
-
console.log(
|
|
4780
|
-
console.log(
|
|
4781
|
-
console.log(
|
|
5564
|
+
SEP6();
|
|
5565
|
+
console.log(W6(` ID: ${G6(`#${existingId}`)}`));
|
|
5566
|
+
console.log(W6(` Vault: ${G6(existingInfo.vault)}`));
|
|
5567
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5568
|
+
console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
|
|
4782
5569
|
const refParam2 = getAgentId() ? `?ref=${getAgentId()}` : "";
|
|
4783
|
-
console.log(
|
|
4784
|
-
|
|
4785
|
-
console.log(
|
|
5570
|
+
console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
|
|
5571
|
+
SEP6();
|
|
5572
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
4786
5573
|
console.log();
|
|
4787
5574
|
return;
|
|
4788
5575
|
}
|
|
4789
|
-
const spinner = ora8({ text:
|
|
5576
|
+
const spinner = ora8({ text: W6("Deploying vault via factory..."), color: "green" }).start();
|
|
4790
5577
|
const result = await createSyndicate({
|
|
4791
5578
|
creatorAgentId: BigInt(agentIdStr),
|
|
4792
5579
|
metadataURI,
|
|
@@ -4801,7 +5588,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4801
5588
|
addSyndicate(chainId, { subdomain, vault: result.vault, role: "creator" });
|
|
4802
5589
|
setPrimarySyndicate(chainId, subdomain);
|
|
4803
5590
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
4804
|
-
spinner.text =
|
|
5591
|
+
spinner.text = W6("Registering creator as agent...");
|
|
4805
5592
|
try {
|
|
4806
5593
|
setVaultAddress(result.vault);
|
|
4807
5594
|
const creatorAddress = getAccount().address;
|
|
@@ -4811,9 +5598,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4811
5598
|
// agentAddress = creator EOA (direct execution)
|
|
4812
5599
|
);
|
|
4813
5600
|
} catch (regErr) {
|
|
4814
|
-
console.warn(
|
|
5601
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
|
|
4815
5602
|
}
|
|
4816
|
-
spinner.text =
|
|
5603
|
+
spinner.text = W6("Setting up chat...");
|
|
4817
5604
|
try {
|
|
4818
5605
|
const xmtp = await loadXmtp();
|
|
4819
5606
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -4824,45 +5611,70 @@ syndicate.command("create").description("Create a new syndicate via the factory
|
|
|
4824
5611
|
} catch {
|
|
4825
5612
|
}
|
|
4826
5613
|
} catch {
|
|
4827
|
-
console.warn(
|
|
4828
|
-
console.warn(
|
|
5614
|
+
console.warn(chalk13.yellow("\n \u26A0 Could not create XMTP chat group"));
|
|
5615
|
+
console.warn(chalk13.dim(` Recover later with: sherwood chat ${subdomain} init`));
|
|
4829
5616
|
}
|
|
4830
5617
|
try {
|
|
4831
5618
|
const cron = await loadCron();
|
|
4832
5619
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
4833
5620
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
4834
|
-
console.log(
|
|
5621
|
+
console.log(G6(" \u2713 Participation crons registered (15m check + hourly summary)"));
|
|
4835
5622
|
} else if (!cronResult.isOpenClaw) {
|
|
4836
|
-
console.log(
|
|
5623
|
+
console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
4837
5624
|
}
|
|
4838
5625
|
} catch {
|
|
4839
5626
|
}
|
|
4840
5627
|
spinner.stop();
|
|
4841
5628
|
console.log();
|
|
4842
5629
|
console.log(LABEL5(" \u25C6 Syndicate Created"));
|
|
4843
|
-
|
|
4844
|
-
console.log(
|
|
4845
|
-
console.log(
|
|
4846
|
-
console.log(
|
|
4847
|
-
console.log(
|
|
4848
|
-
console.log(
|
|
4849
|
-
console.log(
|
|
5630
|
+
SEP6();
|
|
5631
|
+
console.log(W6(` ID: ${G6(`#${result.syndicateId}`)}`));
|
|
5632
|
+
console.log(W6(` Vault: ${G6(result.vault)}`));
|
|
5633
|
+
console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5634
|
+
console.log(W6(` Metadata: ${DIM6(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
|
|
5635
|
+
console.log(W6(` Explorer: ${DIM6(getExplorerUrl(result.hash))}`));
|
|
5636
|
+
console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
|
|
4850
5637
|
const refParam = getAgentId() ? `?ref=${getAgentId()}` : "";
|
|
4851
|
-
console.log(
|
|
4852
|
-
|
|
4853
|
-
console.log(
|
|
5638
|
+
console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
|
|
5639
|
+
SEP6();
|
|
5640
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
4854
5641
|
console.log();
|
|
4855
5642
|
} catch (err) {
|
|
4856
|
-
console.error(
|
|
5643
|
+
console.error(chalk13.red(`
|
|
4857
5644
|
\u2716 ${formatContractError(err)}`));
|
|
4858
5645
|
process.exit(1);
|
|
4859
5646
|
}
|
|
4860
5647
|
});
|
|
4861
|
-
syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").action(async (opts) => {
|
|
5648
|
+
syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").option("--all-chains", "Aggregate active syndicates across all supported chains (on-chain only)").action(async (opts) => {
|
|
4862
5649
|
const spinner = ora8("Loading syndicates...").start();
|
|
4863
5650
|
try {
|
|
4864
|
-
let syndicates;
|
|
4865
|
-
if (
|
|
5651
|
+
let syndicates = [];
|
|
5652
|
+
if (opts.allChains) {
|
|
5653
|
+
const includeTestnets = process.env.ENABLE_TESTNET === "true";
|
|
5654
|
+
const targets = VALID_NETWORKS.filter((n) => includeTestnets || !["base-sepolia", "robinhood-testnet", "hyperevm-testnet"].includes(n));
|
|
5655
|
+
for (const net of targets) {
|
|
5656
|
+
spinner.text = `Loading from ${net}...`;
|
|
5657
|
+
try {
|
|
5658
|
+
const rows = await withNetwork(net, () => getActiveSyndicates());
|
|
5659
|
+
for (const s of rows) {
|
|
5660
|
+
syndicates.push({
|
|
5661
|
+
id: s.id.toString(),
|
|
5662
|
+
vault: s.vault,
|
|
5663
|
+
creator: s.creator,
|
|
5664
|
+
metadataURI: s.metadataURI,
|
|
5665
|
+
createdAt: s.createdAt.toString(),
|
|
5666
|
+
subdomain: s.subdomain,
|
|
5667
|
+
chain: net
|
|
5668
|
+
});
|
|
5669
|
+
}
|
|
5670
|
+
} catch {
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
if (opts.creator) {
|
|
5674
|
+
const c = String(opts.creator).toLowerCase();
|
|
5675
|
+
syndicates = syndicates.filter((s) => s.creator.toLowerCase() === c);
|
|
5676
|
+
}
|
|
5677
|
+
} else if (process.env.SUBGRAPH_URL) {
|
|
4866
5678
|
const result = await getActiveSyndicates2(opts.creator);
|
|
4867
5679
|
syndicates = result;
|
|
4868
5680
|
} else {
|
|
@@ -4878,34 +5690,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
|
|
|
4878
5690
|
}
|
|
4879
5691
|
spinner.stop();
|
|
4880
5692
|
if (syndicates.length === 0) {
|
|
4881
|
-
console.log(
|
|
5693
|
+
console.log(chalk13.dim("No active syndicates found."));
|
|
4882
5694
|
return;
|
|
4883
5695
|
}
|
|
4884
5696
|
console.log();
|
|
4885
|
-
console.log(
|
|
4886
|
-
if (!process.env.SUBGRAPH_URL) {
|
|
4887
|
-
console.log(
|
|
5697
|
+
console.log(chalk13.bold(`Active Syndicates (${syndicates.length})`));
|
|
5698
|
+
if (!process.env.SUBGRAPH_URL && !opts.allChains) {
|
|
5699
|
+
console.log(chalk13.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
|
|
5700
|
+
}
|
|
5701
|
+
if (opts.allChains) {
|
|
5702
|
+
console.log(chalk13.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
|
|
4888
5703
|
}
|
|
4889
|
-
console.log(
|
|
5704
|
+
console.log(chalk13.dim("\u2500".repeat(70)));
|
|
4890
5705
|
for (const s of syndicates) {
|
|
4891
5706
|
const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
|
|
4892
5707
|
const date = new Date(ts * 1e3).toLocaleDateString();
|
|
4893
5708
|
const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
|
|
4894
|
-
|
|
4895
|
-
|
|
5709
|
+
const chainTag = s.chain ? chalk13.dim(`[${s.chain}] `) : "";
|
|
5710
|
+
console.log(` ${chainTag}#${s.id} ${chalk13.bold(ensName || String(s.vault))}`);
|
|
5711
|
+
if (ensName) console.log(` Vault: ${chalk13.cyan(String(s.vault))}`);
|
|
4896
5712
|
console.log(` Creator: ${s.creator}`);
|
|
4897
5713
|
console.log(` Created: ${date}`);
|
|
4898
5714
|
if (s.totalDeposits) {
|
|
4899
5715
|
console.log(` Deposits: ${s.totalDeposits} USDC`);
|
|
4900
5716
|
}
|
|
4901
5717
|
if (s.metadataURI) {
|
|
4902
|
-
console.log(` Metadata: ${
|
|
5718
|
+
console.log(` Metadata: ${chalk13.dim(s.metadataURI)}`);
|
|
4903
5719
|
}
|
|
4904
5720
|
console.log();
|
|
4905
5721
|
}
|
|
4906
5722
|
} catch (err) {
|
|
4907
5723
|
spinner.fail("Failed to load syndicates");
|
|
4908
|
-
console.error(
|
|
5724
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
4909
5725
|
process.exit(1);
|
|
4910
5726
|
}
|
|
4911
5727
|
});
|
|
@@ -4913,42 +5729,70 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
4913
5729
|
const spinner = ora8("Loading syndicate info...").start();
|
|
4914
5730
|
try {
|
|
4915
5731
|
let id;
|
|
5732
|
+
let crossChainHint = null;
|
|
5733
|
+
let infoOverride = null;
|
|
4916
5734
|
if (/^\d+$/.test(idStr)) {
|
|
4917
5735
|
id = BigInt(idStr);
|
|
4918
5736
|
} else {
|
|
4919
|
-
|
|
4920
|
-
|
|
5737
|
+
try {
|
|
5738
|
+
const resolved = await resolveSyndicate(idStr);
|
|
5739
|
+
id = resolved.id;
|
|
5740
|
+
} catch {
|
|
5741
|
+
spinner.text = `Searching across chains...`;
|
|
5742
|
+
const hits = await searchSyndicateAcrossChains(idStr);
|
|
5743
|
+
if (hits.length === 0) {
|
|
5744
|
+
spinner.fail(`Syndicate "${idStr}" not found on any supported chain`);
|
|
5745
|
+
process.exit(1);
|
|
5746
|
+
}
|
|
5747
|
+
const active = getNetwork();
|
|
5748
|
+
const primary = hits.find((h) => h.network === active) ?? hits[0];
|
|
5749
|
+
id = primary.result.id;
|
|
5750
|
+
infoOverride = await withNetwork(
|
|
5751
|
+
primary.network,
|
|
5752
|
+
() => getSyndicate(primary.result.id)
|
|
5753
|
+
);
|
|
5754
|
+
crossChainHint = {
|
|
5755
|
+
network: primary.network,
|
|
5756
|
+
others: hits.filter((h) => h.network !== primary.network).map((h) => h.network)
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
4921
5759
|
}
|
|
4922
|
-
const info = await getSyndicate(id);
|
|
5760
|
+
const info = infoOverride ?? await getSyndicate(id);
|
|
4923
5761
|
spinner.stop();
|
|
5762
|
+
if (crossChainHint) {
|
|
5763
|
+
const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
|
|
5764
|
+
console.log(chalk13.yellow(` Found on chain: ${chalk13.bold(crossChainHint.network)}${others}`));
|
|
5765
|
+
console.log(chalk13.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
|
|
5766
|
+
console.log();
|
|
5767
|
+
}
|
|
4924
5768
|
if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
|
|
4925
|
-
console.log(
|
|
5769
|
+
console.log(chalk13.red(`Syndicate #${id} not found.`));
|
|
4926
5770
|
process.exit(1);
|
|
4927
5771
|
}
|
|
4928
5772
|
const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
|
|
4929
5773
|
console.log();
|
|
4930
|
-
console.log(
|
|
4931
|
-
console.log(
|
|
5774
|
+
console.log(chalk13.bold(`Syndicate #${info.id}`));
|
|
5775
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
4932
5776
|
if (info.subdomain) {
|
|
4933
|
-
console.log(` ENS: ${
|
|
5777
|
+
console.log(` ENS: ${chalk13.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
|
|
4934
5778
|
}
|
|
4935
|
-
console.log(` Vault: ${
|
|
5779
|
+
console.log(` Vault: ${chalk13.cyan(info.vault)}`);
|
|
4936
5780
|
console.log(` Creator: ${info.creator}`);
|
|
4937
5781
|
console.log(` Created: ${date}`);
|
|
4938
|
-
console.log(` Active: ${info.active ?
|
|
5782
|
+
console.log(` Active: ${info.active ? chalk13.green("yes") : chalk13.red("no")}`);
|
|
4939
5783
|
if (info.metadataURI) {
|
|
4940
|
-
console.log(` Metadata: ${
|
|
5784
|
+
console.log(` Metadata: ${chalk13.dim(info.metadataURI)}`);
|
|
4941
5785
|
}
|
|
4942
5786
|
if (info.subdomain) {
|
|
4943
5787
|
const xmtpGroupId = getCachedGroupId(info.subdomain);
|
|
4944
5788
|
if (xmtpGroupId) {
|
|
4945
|
-
console.log(` XMTP Group: ${
|
|
5789
|
+
console.log(` XMTP Group: ${chalk13.cyan(xmtpGroupId)}`);
|
|
4946
5790
|
}
|
|
4947
5791
|
}
|
|
4948
5792
|
setVaultAddress(info.vault);
|
|
4949
|
-
const vaultInfo = await getVaultInfo();
|
|
5793
|
+
const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
|
|
4950
5794
|
console.log();
|
|
4951
|
-
console.log(
|
|
5795
|
+
console.log(chalk13.bold(" Vault Stats"));
|
|
4952
5796
|
console.log(` Total Assets: ${vaultInfo.totalAssets}`);
|
|
4953
5797
|
console.log(` Agent Count: ${vaultInfo.agentCount}`);
|
|
4954
5798
|
console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
|
|
@@ -4956,12 +5800,12 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
|
|
|
4956
5800
|
console.log();
|
|
4957
5801
|
} catch (err) {
|
|
4958
5802
|
spinner.fail("Failed to load syndicate info");
|
|
4959
|
-
console.error(
|
|
5803
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
4960
5804
|
process.exit(1);
|
|
4961
5805
|
}
|
|
4962
5806
|
});
|
|
4963
5807
|
syndicate.command("update-metadata").description("Update syndicate metadata (creator only)").requiredOption("--id <id>", "Syndicate ID").option("--name <name>", "Syndicate name").option("--description <text>", "Short description").option("--uri <uri>", "Direct metadata URI (skips IPFS upload)").action(async (opts) => {
|
|
4964
|
-
const spinner = ora8({ text:
|
|
5808
|
+
const spinner = ora8({ text: W6("Loading syndicate..."), color: "green" }).start();
|
|
4965
5809
|
try {
|
|
4966
5810
|
const syndicateId = BigInt(opts.id);
|
|
4967
5811
|
let metadataURI = opts.uri;
|
|
@@ -4973,7 +5817,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
4973
5817
|
}
|
|
4974
5818
|
const name = opts.name || info.subdomain;
|
|
4975
5819
|
const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
|
|
4976
|
-
spinner.text =
|
|
5820
|
+
spinner.text = W6("Uploading metadata to IPFS...");
|
|
4977
5821
|
const metadata = {
|
|
4978
5822
|
schema: "sherwood/syndicate/v1",
|
|
4979
5823
|
name,
|
|
@@ -4984,15 +5828,15 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
|
|
|
4984
5828
|
links: {}
|
|
4985
5829
|
};
|
|
4986
5830
|
metadataURI = await uploadMetadata(metadata);
|
|
4987
|
-
spinner.text =
|
|
5831
|
+
spinner.text = W6("Updating on-chain metadata...");
|
|
4988
5832
|
}
|
|
4989
5833
|
const hash = await updateMetadata(syndicateId, metadataURI);
|
|
4990
|
-
spinner.succeed(
|
|
4991
|
-
console.log(
|
|
4992
|
-
console.log(
|
|
5834
|
+
spinner.succeed(G6(`Metadata updated`));
|
|
5835
|
+
console.log(DIM6(` IPFS: ${metadataURI}`));
|
|
5836
|
+
console.log(DIM6(` ${getExplorerUrl(hash)}`));
|
|
4993
5837
|
} catch (err) {
|
|
4994
5838
|
spinner.fail("Metadata update failed");
|
|
4995
|
-
console.error(
|
|
5839
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
4996
5840
|
process.exit(1);
|
|
4997
5841
|
}
|
|
4998
5842
|
});
|
|
@@ -5003,10 +5847,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
|
|
|
5003
5847
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5004
5848
|
const hash = await approveDepositor(depositor);
|
|
5005
5849
|
spinner.succeed(`Depositor approved: ${hash}`);
|
|
5006
|
-
console.log(
|
|
5850
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5007
5851
|
} catch (err) {
|
|
5008
5852
|
spinner.fail("Approval failed");
|
|
5009
|
-
console.error(
|
|
5853
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5010
5854
|
process.exit(1);
|
|
5011
5855
|
}
|
|
5012
5856
|
});
|
|
@@ -5017,10 +5861,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
|
|
|
5017
5861
|
const depositor = validateAddress(opts.depositor, "depositor");
|
|
5018
5862
|
const hash = await removeDepositor(depositor);
|
|
5019
5863
|
spinner.succeed(`Depositor removed: ${hash}`);
|
|
5020
|
-
console.log(
|
|
5864
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5021
5865
|
} catch (err) {
|
|
5022
5866
|
spinner.fail("Removal failed");
|
|
5023
|
-
console.error(
|
|
5867
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5024
5868
|
process.exit(1);
|
|
5025
5869
|
}
|
|
5026
5870
|
});
|
|
@@ -5040,7 +5884,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5040
5884
|
if (opts.agentId) {
|
|
5041
5885
|
agentId = BigInt(opts.agentId);
|
|
5042
5886
|
} else {
|
|
5043
|
-
const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-
|
|
5887
|
+
const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-DOWLGXGE.js");
|
|
5044
5888
|
const registry2 = AGENT_REGISTRY2().IDENTITY_REGISTRY;
|
|
5045
5889
|
if (registry2 === "0x0000000000000000000000000000000000000000") {
|
|
5046
5890
|
agentId = 0n;
|
|
@@ -5061,7 +5905,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5061
5905
|
});
|
|
5062
5906
|
if (balance === 0n) {
|
|
5063
5907
|
spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
|
|
5064
|
-
console.error(
|
|
5908
|
+
console.error(chalk13.dim(" Mint one first: sherwood identity mint --name <name>"));
|
|
5065
5909
|
process.exit(1);
|
|
5066
5910
|
}
|
|
5067
5911
|
const tokenId = await client.readContract({
|
|
@@ -5077,13 +5921,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5077
5921
|
args: [agentWallet, 0n]
|
|
5078
5922
|
});
|
|
5079
5923
|
agentId = tokenId;
|
|
5080
|
-
console.log(
|
|
5924
|
+
console.log(chalk13.dim(` Resolved agent ID: #${agentId}`));
|
|
5081
5925
|
}
|
|
5082
5926
|
}
|
|
5083
5927
|
spinner.text = "Registering agent...";
|
|
5084
5928
|
const hash = await registerAgent(agentId, agentWallet);
|
|
5085
5929
|
spinner.succeed(`Agent registered: ${hash}`);
|
|
5086
|
-
console.log(
|
|
5930
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5087
5931
|
try {
|
|
5088
5932
|
const xmtp = await loadXmtp();
|
|
5089
5933
|
const xmtpClient = await xmtp.getXmtpClient();
|
|
@@ -5095,14 +5939,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
|
|
|
5095
5939
|
syndicate: subdomain,
|
|
5096
5940
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5097
5941
|
});
|
|
5098
|
-
console.log(
|
|
5942
|
+
console.log(chalk13.dim(` Added to chat: ${subdomain}`));
|
|
5099
5943
|
} catch {
|
|
5100
|
-
console.warn(
|
|
5101
|
-
console.warn(
|
|
5944
|
+
console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
|
|
5945
|
+
console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5102
5946
|
}
|
|
5103
5947
|
} catch (err) {
|
|
5104
5948
|
spinner.fail("Registration failed");
|
|
5105
|
-
console.error(
|
|
5949
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5106
5950
|
process.exit(1);
|
|
5107
5951
|
}
|
|
5108
5952
|
});
|
|
@@ -5110,7 +5954,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5110
5954
|
try {
|
|
5111
5955
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5112
5956
|
if (!subdomain) {
|
|
5113
|
-
console.error(
|
|
5957
|
+
console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
|
|
5114
5958
|
process.exit(1);
|
|
5115
5959
|
}
|
|
5116
5960
|
const syndicateInfo = await resolveSyndicate(subdomain);
|
|
@@ -5119,12 +5963,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5119
5963
|
const dashboardUrl = `https://sherwood.sh/syndicate/${subdomain}${refParam}`;
|
|
5120
5964
|
console.log();
|
|
5121
5965
|
console.log(LABEL5(" \u25C6 Share Your Syndicate"));
|
|
5122
|
-
|
|
5123
|
-
console.log(
|
|
5124
|
-
console.log(
|
|
5125
|
-
console.log(
|
|
5126
|
-
console.log(
|
|
5127
|
-
|
|
5966
|
+
SEP6();
|
|
5967
|
+
console.log(W6(` Dashboard: ${G6(dashboardUrl)}`));
|
|
5968
|
+
console.log(W6(` ENS: ${DIM6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
5969
|
+
console.log(W6(` Vault: ${DIM6(syndicateInfo.vault)}`));
|
|
5970
|
+
console.log(W6(` Join CLI: ${DIM6(`sherwood syndicate join --subdomain ${subdomain}`)}`));
|
|
5971
|
+
SEP6();
|
|
5128
5972
|
try {
|
|
5129
5973
|
const { exec } = await import("child_process");
|
|
5130
5974
|
const platform = (await import("os")).platform();
|
|
@@ -5132,12 +5976,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
|
|
|
5132
5976
|
const proc = exec(cmd);
|
|
5133
5977
|
proc.stdin?.write(dashboardUrl);
|
|
5134
5978
|
proc.stdin?.end();
|
|
5135
|
-
console.log(
|
|
5979
|
+
console.log(G6(" \u2713 Dashboard URL copied to clipboard"));
|
|
5136
5980
|
} catch {
|
|
5137
5981
|
}
|
|
5138
5982
|
console.log();
|
|
5139
5983
|
} catch (err) {
|
|
5140
|
-
console.error(
|
|
5984
|
+
console.error(chalk13.red(` \u2716 ${formatContractError(err)}`));
|
|
5141
5985
|
process.exit(1);
|
|
5142
5986
|
}
|
|
5143
5987
|
});
|
|
@@ -5154,7 +5998,25 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5154
5998
|
spinner.fail("No agent identity found. Run 'sherwood identity mint' first.");
|
|
5155
5999
|
process.exit(1);
|
|
5156
6000
|
}
|
|
5157
|
-
|
|
6001
|
+
let syndicate2;
|
|
6002
|
+
try {
|
|
6003
|
+
syndicate2 = await resolveSyndicate(subdomain);
|
|
6004
|
+
} catch {
|
|
6005
|
+
spinner.text = "Searching across chains...";
|
|
6006
|
+
const hits = await searchSyndicateAcrossChains(subdomain);
|
|
6007
|
+
spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
|
|
6008
|
+
if (hits.length === 0) {
|
|
6009
|
+
console.error(chalk13.red(` Not found on any supported chain.`));
|
|
6010
|
+
} else if (hits.length === 1) {
|
|
6011
|
+
console.error(chalk13.yellow(` Found on chain: ${chalk13.bold(hits[0].network)}`));
|
|
6012
|
+
console.error(chalk13.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
|
|
6013
|
+
} else {
|
|
6014
|
+
const chains = hits.map((h) => h.network).join(", ");
|
|
6015
|
+
console.error(chalk13.yellow(` Found on chains: ${chalk13.bold(chains)}`));
|
|
6016
|
+
console.error(chalk13.dim(` Specify which chain to join with --chain <network>.`));
|
|
6017
|
+
}
|
|
6018
|
+
process.exit(1);
|
|
6019
|
+
}
|
|
5158
6020
|
const callerAddress = getAccount().address;
|
|
5159
6021
|
spinner.text = "Checking membership...";
|
|
5160
6022
|
setVaultAddress(syndicate2.vault);
|
|
@@ -5164,17 +6026,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5164
6026
|
try {
|
|
5165
6027
|
const xmtp = await loadXmtp();
|
|
5166
6028
|
await xmtp.getXmtpClient();
|
|
5167
|
-
console.log(
|
|
6029
|
+
console.log(chalk13.dim(" XMTP identity ready"));
|
|
5168
6030
|
} catch {
|
|
5169
|
-
console.warn(
|
|
6031
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
5170
6032
|
}
|
|
5171
6033
|
try {
|
|
5172
6034
|
const cron = await loadCron();
|
|
5173
6035
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
5174
6036
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
5175
|
-
console.log(
|
|
6037
|
+
console.log(chalk13.green(" \u2713 Participation crons registered"));
|
|
5176
6038
|
} else if (!cronResult.isOpenClaw) {
|
|
5177
|
-
console.log(
|
|
6039
|
+
console.log(chalk13.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
5178
6040
|
}
|
|
5179
6041
|
} catch {
|
|
5180
6042
|
}
|
|
@@ -5187,14 +6049,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5187
6049
|
);
|
|
5188
6050
|
if (existingRequest) {
|
|
5189
6051
|
spinner.succeed("You already have a pending join request for this syndicate");
|
|
5190
|
-
console.log(
|
|
5191
|
-
console.log(
|
|
6052
|
+
console.log(chalk13.dim(` Attestation: ${existingRequest.uid}`));
|
|
6053
|
+
console.log(chalk13.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
|
|
5192
6054
|
try {
|
|
5193
6055
|
const xmtp = await loadXmtp();
|
|
5194
6056
|
await xmtp.getXmtpClient();
|
|
5195
|
-
console.log(
|
|
6057
|
+
console.log(chalk13.dim(" XMTP identity ready"));
|
|
5196
6058
|
} catch {
|
|
5197
|
-
console.warn(
|
|
6059
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
|
|
5198
6060
|
}
|
|
5199
6061
|
return;
|
|
5200
6062
|
}
|
|
@@ -5215,7 +6077,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5215
6077
|
spinner.succeed("Join request created (XMTP identity ready)");
|
|
5216
6078
|
} catch {
|
|
5217
6079
|
spinner.succeed("Join request created");
|
|
5218
|
-
console.warn(
|
|
6080
|
+
console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
|
|
5219
6081
|
}
|
|
5220
6082
|
addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
|
|
5221
6083
|
setPrimarySyndicate(getChain().id, subdomain);
|
|
@@ -5223,29 +6085,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
|
|
|
5223
6085
|
const cron = await loadCron();
|
|
5224
6086
|
const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
|
|
5225
6087
|
if (cronResult.isOpenClaw && cronResult.registered) {
|
|
5226
|
-
console.log(
|
|
6088
|
+
console.log(G6(" \u2713 Participation crons registered (will activate after approval)"));
|
|
5227
6089
|
} else if (!cronResult.isOpenClaw) {
|
|
5228
|
-
console.log(
|
|
6090
|
+
console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
|
|
5229
6091
|
}
|
|
5230
6092
|
} catch {
|
|
5231
6093
|
}
|
|
5232
6094
|
console.log();
|
|
5233
6095
|
console.log(LABEL5(" \u25C6 Join Request Submitted"));
|
|
5234
|
-
|
|
5235
|
-
console.log(
|
|
5236
|
-
console.log(
|
|
5237
|
-
console.log(
|
|
5238
|
-
console.log(
|
|
5239
|
-
console.log(
|
|
5240
|
-
console.log(
|
|
5241
|
-
|
|
5242
|
-
console.log(
|
|
5243
|
-
console.log(
|
|
5244
|
-
console.log(
|
|
6096
|
+
SEP6();
|
|
6097
|
+
console.log(W6(` Syndicate: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
|
|
6098
|
+
console.log(W6(` Agent ID: #${agentId}`));
|
|
6099
|
+
console.log(W6(` Creator: ${DIM6(syndicate2.creator)}`));
|
|
6100
|
+
console.log(W6(` Attestation: ${DIM6(uid)}`));
|
|
6101
|
+
console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(uid))}`));
|
|
6102
|
+
console.log(W6(` Explorer: ${DIM6(getEasExplorerUrl(hash))}`));
|
|
6103
|
+
SEP6();
|
|
6104
|
+
console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
|
|
6105
|
+
console.log(G6(" \u2713 The creator can review with:"));
|
|
6106
|
+
console.log(DIM6(` sherwood syndicate requests --subdomain ${subdomain}`));
|
|
5245
6107
|
console.log();
|
|
5246
6108
|
} catch (err) {
|
|
5247
6109
|
spinner.fail("Join request failed");
|
|
5248
|
-
console.error(
|
|
6110
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5249
6111
|
process.exit(1);
|
|
5250
6112
|
}
|
|
5251
6113
|
});
|
|
@@ -5286,34 +6148,34 @@ syndicate.command("requests").description("View pending join requests for a synd
|
|
|
5286
6148
|
);
|
|
5287
6149
|
spinner.stop();
|
|
5288
6150
|
if (requests.length === 0) {
|
|
5289
|
-
console.log(
|
|
6151
|
+
console.log(DIM6("\n No pending join requests.\n"));
|
|
5290
6152
|
return;
|
|
5291
6153
|
}
|
|
5292
6154
|
console.log();
|
|
5293
6155
|
console.log(LABEL5(` \u25C6 Pending Join Requests (${requests.length})`));
|
|
5294
|
-
|
|
6156
|
+
SEP6();
|
|
5295
6157
|
for (let i = 0; i < requests.length; i++) {
|
|
5296
6158
|
const req = requests[i];
|
|
5297
6159
|
const date = new Date(req.time * 1e3).toLocaleString();
|
|
5298
6160
|
const referrer = parseReferrer(req.decoded.message);
|
|
5299
6161
|
const cleanMessage = stripReferrerPrefix(req.decoded.message);
|
|
5300
|
-
console.log(
|
|
5301
|
-
console.log(
|
|
6162
|
+
console.log(W6(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM6(`(${req.attester})`)}`));
|
|
6163
|
+
console.log(DIM6(` Message: "${cleanMessage}"`));
|
|
5302
6164
|
if (referrer !== null) {
|
|
5303
|
-
console.log(
|
|
6165
|
+
console.log(G6(` Referred by: Agent #${referrer}`));
|
|
5304
6166
|
}
|
|
5305
|
-
console.log(
|
|
5306
|
-
console.log(
|
|
6167
|
+
console.log(DIM6(` Requested: ${date}`));
|
|
6168
|
+
console.log(DIM6(` Attestation: ${req.uid}`));
|
|
5307
6169
|
console.log();
|
|
5308
6170
|
}
|
|
5309
|
-
console.log(
|
|
5310
|
-
console.log(
|
|
5311
|
-
console.log(
|
|
5312
|
-
console.log(
|
|
6171
|
+
console.log(G6(" To approve:"));
|
|
6172
|
+
console.log(DIM6(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
|
|
6173
|
+
console.log(G6(" To reject:"));
|
|
6174
|
+
console.log(DIM6(` sherwood syndicate reject --attestation <uid>`));
|
|
5313
6175
|
console.log();
|
|
5314
6176
|
} catch (err) {
|
|
5315
6177
|
spinner.fail("Failed to load requests");
|
|
5316
|
-
console.error(
|
|
6178
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5317
6179
|
process.exit(1);
|
|
5318
6180
|
}
|
|
5319
6181
|
});
|
|
@@ -5343,11 +6205,11 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5343
6205
|
agentWallet
|
|
5344
6206
|
);
|
|
5345
6207
|
agentWasRegistered = true;
|
|
5346
|
-
console.log(
|
|
6208
|
+
console.log(DIM6(` Agent registered: ${getExplorerUrl(regHash)}`));
|
|
5347
6209
|
} catch (regErr) {
|
|
5348
6210
|
const msg = regErr instanceof Error ? regErr.message : String(regErr);
|
|
5349
6211
|
if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
|
|
5350
|
-
console.log(
|
|
6212
|
+
console.log(DIM6(" Agent already registered on vault \u2014 skipping"));
|
|
5351
6213
|
} else {
|
|
5352
6214
|
throw regErr;
|
|
5353
6215
|
}
|
|
@@ -5363,7 +6225,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5363
6225
|
let approvalUid;
|
|
5364
6226
|
if (alreadyApproved) {
|
|
5365
6227
|
approvalUid = alreadyApproved.uid;
|
|
5366
|
-
console.log(
|
|
6228
|
+
console.log(DIM6(` Approval attestation already exists \u2014 skipping`));
|
|
5367
6229
|
} else {
|
|
5368
6230
|
spinner.text = "Creating approval attestation...";
|
|
5369
6231
|
const result = await createApproval(
|
|
@@ -5386,45 +6248,42 @@ syndicate.command("approve").description("Approve an agent join request (registe
|
|
|
5386
6248
|
syndicate: subdomain,
|
|
5387
6249
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
5388
6250
|
});
|
|
5389
|
-
console.log(
|
|
6251
|
+
console.log(DIM6(` Added to chat: ${subdomain}`));
|
|
5390
6252
|
} catch {
|
|
5391
|
-
console.warn(
|
|
5392
|
-
console.warn(
|
|
6253
|
+
console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
|
|
6254
|
+
console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
|
|
5393
6255
|
}
|
|
5394
6256
|
spinner.succeed("Agent approved and registered");
|
|
5395
6257
|
console.log();
|
|
5396
6258
|
console.log(LABEL5(" \u25C6 Agent Approved"));
|
|
5397
|
-
|
|
5398
|
-
console.log(
|
|
5399
|
-
console.log(
|
|
5400
|
-
console.log(
|
|
5401
|
-
console.log(
|
|
5402
|
-
|
|
6259
|
+
SEP6();
|
|
6260
|
+
console.log(W6(` Agent ID: #${opts.agentId}`));
|
|
6261
|
+
console.log(W6(` Wallet: ${G6(opts.wallet)}`));
|
|
6262
|
+
console.log(W6(` Approval: ${DIM6(approvalUid)}`));
|
|
6263
|
+
console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(approvalUid))}`));
|
|
6264
|
+
SEP6();
|
|
5403
6265
|
} catch (err) {
|
|
5404
6266
|
spinner.fail("Approval failed");
|
|
5405
|
-
console.error(
|
|
6267
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5406
6268
|
process.exit(1);
|
|
5407
6269
|
}
|
|
5408
6270
|
});
|
|
5409
6271
|
syndicate.command("reject").description("Reject a join request by revoking its attestation").requiredOption("--attestation <uid>", "Join request attestation UID to revoke").action(async (opts) => {
|
|
5410
6272
|
const spinner = ora8("Revoking attestation...").start();
|
|
5411
6273
|
try {
|
|
5412
|
-
const hash = await
|
|
5413
|
-
EAS_SCHEMAS().SYNDICATE_JOIN_REQUEST,
|
|
5414
|
-
opts.attestation
|
|
5415
|
-
);
|
|
6274
|
+
const hash = await revokeJoinRequest(opts.attestation);
|
|
5416
6275
|
spinner.succeed("Join request rejected");
|
|
5417
|
-
console.log(
|
|
6276
|
+
console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
|
|
5418
6277
|
} catch (err) {
|
|
5419
6278
|
spinner.fail("Rejection failed");
|
|
5420
|
-
console.error(
|
|
6279
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5421
6280
|
process.exit(1);
|
|
5422
6281
|
}
|
|
5423
6282
|
});
|
|
5424
6283
|
syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
|
|
5425
6284
|
const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
|
|
5426
6285
|
if (!subdomain) {
|
|
5427
|
-
console.error(
|
|
6286
|
+
console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name>."));
|
|
5428
6287
|
process.exit(1);
|
|
5429
6288
|
}
|
|
5430
6289
|
const spinner = ora8("Cleaning up...").start();
|
|
@@ -5440,17 +6299,17 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
|
|
|
5440
6299
|
resetSession(subdomain);
|
|
5441
6300
|
spinner.succeed("Left syndicate");
|
|
5442
6301
|
if (cronsRemoved) {
|
|
5443
|
-
console.log(
|
|
6302
|
+
console.log(G6(" \u2713 Participation crons removed"));
|
|
5444
6303
|
}
|
|
5445
|
-
console.log(
|
|
6304
|
+
console.log(G6(" \u2713 Session state cleared"));
|
|
5446
6305
|
console.log();
|
|
5447
|
-
console.log(
|
|
5448
|
-
console.log(
|
|
5449
|
-
console.log(
|
|
6306
|
+
console.log(chalk13.dim(" Note: This does not remove you on-chain. To exit your position:"));
|
|
6307
|
+
console.log(chalk13.dim(" sherwood vault balance \u2014 check your LP share balance"));
|
|
6308
|
+
console.log(chalk13.dim(" Redeem shares via the vault contract or dashboard"));
|
|
5450
6309
|
console.log();
|
|
5451
6310
|
} catch (err) {
|
|
5452
6311
|
spinner.fail("Leave failed");
|
|
5453
|
-
console.error(
|
|
6312
|
+
console.error(chalk13.red(err instanceof Error ? err.message : String(err)));
|
|
5454
6313
|
process.exit(1);
|
|
5455
6314
|
}
|
|
5456
6315
|
});
|
|
@@ -5461,21 +6320,21 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
5461
6320
|
if (!opts.subdomain && !opts.vault) {
|
|
5462
6321
|
const primary = getPrimarySyndicate(chainId);
|
|
5463
6322
|
if (memberships.length === 0) {
|
|
5464
|
-
console.log(
|
|
6323
|
+
console.log(DIM6("\n No syndicates saved. Create or join one first.\n"));
|
|
5465
6324
|
return;
|
|
5466
6325
|
}
|
|
5467
6326
|
console.log();
|
|
5468
6327
|
console.log(LABEL5(" \u25C6 Your Syndicates"));
|
|
5469
|
-
|
|
6328
|
+
SEP6();
|
|
5470
6329
|
for (const m of memberships) {
|
|
5471
6330
|
const isPrimary = primary?.subdomain === m.subdomain;
|
|
5472
|
-
const marker = isPrimary ?
|
|
5473
|
-
console.log(
|
|
5474
|
-
console.log(
|
|
6331
|
+
const marker = isPrimary ? G6(" \u2605") : "";
|
|
6332
|
+
console.log(W6(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM6(`(${m.role})`)}`));
|
|
6333
|
+
console.log(DIM6(` vault: ${m.vault}`));
|
|
5475
6334
|
}
|
|
5476
|
-
|
|
6335
|
+
SEP6();
|
|
5477
6336
|
if (memberships.length > 1) {
|
|
5478
|
-
console.log(
|
|
6337
|
+
console.log(DIM6(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
|
|
5479
6338
|
}
|
|
5480
6339
|
console.log();
|
|
5481
6340
|
return;
|
|
@@ -5501,15 +6360,15 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
|
|
|
5501
6360
|
}
|
|
5502
6361
|
}
|
|
5503
6362
|
if (!target) {
|
|
5504
|
-
console.error(
|
|
6363
|
+
console.error(chalk13.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
|
|
5505
6364
|
process.exit(1);
|
|
5506
6365
|
}
|
|
5507
6366
|
setPrimarySyndicate(chainId, target);
|
|
5508
|
-
console.log(
|
|
6367
|
+
console.log(G6(`
|
|
5509
6368
|
\u2713 Primary syndicate set to ${target}.sherwoodagent.eth
|
|
5510
6369
|
`));
|
|
5511
6370
|
} catch (err) {
|
|
5512
|
-
console.error(
|
|
6371
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5513
6372
|
process.exit(1);
|
|
5514
6373
|
}
|
|
5515
6374
|
});
|
|
@@ -5523,7 +6382,7 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
5523
6382
|
await preflightDeposit(amount);
|
|
5524
6383
|
}
|
|
5525
6384
|
} catch (err) {
|
|
5526
|
-
console.error(
|
|
6385
|
+
console.error(chalk13.red(`
|
|
5527
6386
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5528
6387
|
`));
|
|
5529
6388
|
process.exit(1);
|
|
@@ -5538,10 +6397,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
|
|
|
5538
6397
|
hash = await deposit(amount);
|
|
5539
6398
|
}
|
|
5540
6399
|
spinner.succeed(`Deposited: ${hash}`);
|
|
5541
|
-
console.log(
|
|
6400
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5542
6401
|
} catch (err) {
|
|
5543
6402
|
spinner.fail("Deposit failed");
|
|
5544
|
-
console.error(
|
|
6403
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5545
6404
|
process.exit(1);
|
|
5546
6405
|
}
|
|
5547
6406
|
});
|
|
@@ -5552,8 +6411,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
5552
6411
|
const info = await getVaultInfo();
|
|
5553
6412
|
spinner.stop();
|
|
5554
6413
|
console.log();
|
|
5555
|
-
console.log(
|
|
5556
|
-
console.log(
|
|
6414
|
+
console.log(chalk13.bold("Vault Info"));
|
|
6415
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5557
6416
|
console.log(` Address: ${info.address}`);
|
|
5558
6417
|
console.log(` Total Assets: ${info.totalAssets}`);
|
|
5559
6418
|
console.log(` Agent Count: ${info.agentCount}`);
|
|
@@ -5562,7 +6421,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
|
|
|
5562
6421
|
console.log();
|
|
5563
6422
|
} catch (err) {
|
|
5564
6423
|
spinner.fail("Failed to load vault info");
|
|
5565
|
-
console.error(
|
|
6424
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5566
6425
|
process.exit(1);
|
|
5567
6426
|
}
|
|
5568
6427
|
});
|
|
@@ -5573,15 +6432,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
|
|
|
5573
6432
|
const balance = await getBalance(opts.address);
|
|
5574
6433
|
spinner.stop();
|
|
5575
6434
|
console.log();
|
|
5576
|
-
console.log(
|
|
5577
|
-
console.log(
|
|
6435
|
+
console.log(chalk13.bold("LP Position"));
|
|
6436
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5578
6437
|
console.log(` Shares: ${balance.shares.toString()}`);
|
|
5579
6438
|
console.log(` Asset Value: ${balance.assetsValue}`);
|
|
5580
6439
|
console.log(` % of Vault: ${balance.percentOfVault}`);
|
|
5581
6440
|
console.log();
|
|
5582
6441
|
} catch (err) {
|
|
5583
6442
|
spinner.fail("Failed to load balance");
|
|
5584
|
-
console.error(
|
|
6443
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5585
6444
|
process.exit(1);
|
|
5586
6445
|
}
|
|
5587
6446
|
});
|
|
@@ -5596,18 +6455,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5596
6455
|
} else {
|
|
5597
6456
|
shares = await getShareBalance();
|
|
5598
6457
|
if (shares === 0n) {
|
|
5599
|
-
console.error(
|
|
6458
|
+
console.error(chalk13.red("\n \u2716 No shares to redeem.\n"));
|
|
5600
6459
|
process.exit(1);
|
|
5601
6460
|
}
|
|
5602
6461
|
}
|
|
5603
6462
|
} catch (err) {
|
|
5604
|
-
console.error(
|
|
6463
|
+
console.error(chalk13.red(`
|
|
5605
6464
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5606
6465
|
`));
|
|
5607
6466
|
process.exit(1);
|
|
5608
6467
|
}
|
|
5609
6468
|
if (opts.receiver && !isAddress6(opts.receiver)) {
|
|
5610
|
-
console.error(
|
|
6469
|
+
console.error(chalk13.red(`
|
|
5611
6470
|
\u2716 Invalid receiver address: ${opts.receiver}
|
|
5612
6471
|
`));
|
|
5613
6472
|
process.exit(1);
|
|
@@ -5615,7 +6474,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5615
6474
|
try {
|
|
5616
6475
|
await preflightRedeem(shares);
|
|
5617
6476
|
} catch (err) {
|
|
5618
|
-
console.error(
|
|
6477
|
+
console.error(chalk13.red(`
|
|
5619
6478
|
\u2716 ${err instanceof Error ? err.message : String(err)}
|
|
5620
6479
|
`));
|
|
5621
6480
|
process.exit(1);
|
|
@@ -5628,18 +6487,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
|
|
|
5628
6487
|
opts.receiver
|
|
5629
6488
|
);
|
|
5630
6489
|
spinner.succeed(`Redeemed: ${hash}`);
|
|
5631
|
-
console.log(
|
|
6490
|
+
console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
|
|
5632
6491
|
console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
|
|
5633
6492
|
} catch (err) {
|
|
5634
6493
|
spinner.fail("Redeem failed");
|
|
5635
|
-
console.error(
|
|
6494
|
+
console.error(chalk13.red(formatContractError(err)));
|
|
5636
6495
|
process.exit(1);
|
|
5637
6496
|
}
|
|
5638
6497
|
});
|
|
5639
6498
|
var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
|
|
5640
6499
|
registerStrategyTemplateCommands(strategy);
|
|
5641
6500
|
program.command("providers").description("List available DeFi providers").action(async () => {
|
|
5642
|
-
const { MessariProvider, NansenProvider } = await import("./research-
|
|
6501
|
+
const { MessariProvider, NansenProvider } = await import("./research-YJWSQS2A.js");
|
|
5643
6502
|
const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
|
|
5644
6503
|
for (const p of providers) {
|
|
5645
6504
|
const info = p.info();
|
|
@@ -5650,17 +6509,17 @@ ${info.name} (${info.type})`);
|
|
|
5650
6509
|
}
|
|
5651
6510
|
});
|
|
5652
6511
|
try {
|
|
5653
|
-
const { registerChatCommands } = await import("./chat-
|
|
6512
|
+
const { registerChatCommands } = await import("./chat-MRTPUZ3X.js");
|
|
5654
6513
|
registerChatCommands(program);
|
|
5655
6514
|
} catch {
|
|
5656
6515
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
5657
|
-
console.error(
|
|
5658
|
-
console.error(
|
|
5659
|
-
console.error(
|
|
6516
|
+
console.error(chalk13.red("XMTP CLI not available."));
|
|
6517
|
+
console.error(chalk13.dim("Install with: npm install -g @xmtp/cli"));
|
|
6518
|
+
console.error(chalk13.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
|
|
5660
6519
|
process.exit(1);
|
|
5661
6520
|
});
|
|
5662
6521
|
}
|
|
5663
|
-
var { registerSessionCommands } = await import("./session-
|
|
6522
|
+
var { registerSessionCommands } = await import("./session-FPCPCVQX.js");
|
|
5664
6523
|
registerSessionCommands(program);
|
|
5665
6524
|
registerVeniceCommands(program);
|
|
5666
6525
|
registerAllowanceCommands(program);
|
|
@@ -5668,65 +6527,71 @@ registerIdentityCommands(program);
|
|
|
5668
6527
|
registerProposalCommands(program);
|
|
5669
6528
|
registerGovernorCommands(program);
|
|
5670
6529
|
registerGuardianCommands(program);
|
|
5671
|
-
var { registerResearchCommands } = await import("./research-
|
|
6530
|
+
var { registerResearchCommands } = await import("./research-QBKISW2M.js");
|
|
5672
6531
|
registerResearchCommands(program);
|
|
5673
|
-
var { registerAgentCommands } = await import("./agent-
|
|
6532
|
+
var { registerAgentCommands } = await import("./agent-4HKHLQEJ.js");
|
|
5674
6533
|
registerAgentCommands(program);
|
|
5675
|
-
var { registerTradeCommands } = await import("./trade-
|
|
6534
|
+
var { registerTradeCommands } = await import("./trade-NO6CNWCA.js");
|
|
5676
6535
|
registerTradeCommands(program);
|
|
6536
|
+
registerGridCommand(program);
|
|
5677
6537
|
var configCmd = program.command("config");
|
|
5678
|
-
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
|
|
6538
|
+
configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--anthropic-api-key <key>", "Anthropic Claude API key (for LLM judge)").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
|
|
5679
6539
|
let saved = false;
|
|
5680
6540
|
if (opts.privateKey) {
|
|
5681
6541
|
setPrivateKey(opts.privateKey);
|
|
5682
6542
|
const account = getAccount();
|
|
5683
|
-
console.log(
|
|
5684
|
-
console.log(
|
|
6543
|
+
console.log(chalk13.green("Private key saved to ~/.sherwood/config.json"));
|
|
6544
|
+
console.log(chalk13.dim(` Wallet: ${account.address}`));
|
|
5685
6545
|
saved = true;
|
|
5686
6546
|
}
|
|
5687
6547
|
if (opts.vault) {
|
|
5688
6548
|
const chainId = getChain().id;
|
|
5689
6549
|
setChainContract(chainId, "vault", opts.vault);
|
|
5690
|
-
console.log(
|
|
5691
|
-
console.log(
|
|
6550
|
+
console.log(chalk13.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
|
|
6551
|
+
console.log(chalk13.dim(` Vault: ${opts.vault}`));
|
|
5692
6552
|
saved = true;
|
|
5693
6553
|
}
|
|
5694
6554
|
if (opts.rpc) {
|
|
5695
6555
|
const network = getNetwork();
|
|
5696
6556
|
setConfigRpcUrl(network, opts.rpc);
|
|
5697
|
-
console.log(
|
|
5698
|
-
console.log(
|
|
6557
|
+
console.log(chalk13.green(`RPC URL saved for ${network}`));
|
|
6558
|
+
console.log(chalk13.dim(` RPC: ${opts.rpc}`));
|
|
5699
6559
|
saved = true;
|
|
5700
6560
|
}
|
|
5701
6561
|
if (opts.notifyTo) {
|
|
5702
6562
|
setNotifyTo(opts.notifyTo);
|
|
5703
|
-
console.log(
|
|
5704
|
-
console.log(
|
|
6563
|
+
console.log(chalk13.green("Notify destination saved to ~/.sherwood/config.json"));
|
|
6564
|
+
console.log(chalk13.dim(` Notify to: ${opts.notifyTo}`));
|
|
5705
6565
|
saved = true;
|
|
5706
6566
|
}
|
|
5707
6567
|
if (opts.uniswapApiKey) {
|
|
5708
6568
|
setUniswapApiKey(opts.uniswapApiKey);
|
|
5709
|
-
console.log(
|
|
6569
|
+
console.log(chalk13.green("Uniswap API key saved to ~/.sherwood/config.json"));
|
|
5710
6570
|
saved = true;
|
|
5711
6571
|
}
|
|
5712
6572
|
if (opts.veniceApiKey) {
|
|
5713
6573
|
setVeniceApiKey(opts.veniceApiKey);
|
|
5714
|
-
console.log(
|
|
6574
|
+
console.log(chalk13.green("Venice API key saved to ~/.sherwood/config.json"));
|
|
6575
|
+
saved = true;
|
|
6576
|
+
}
|
|
6577
|
+
if (opts.anthropicApiKey) {
|
|
6578
|
+
setAnthropicApiKey(opts.anthropicApiKey);
|
|
6579
|
+
console.log(chalk13.green("Anthropic API key saved to ~/.sherwood/config.json"));
|
|
5715
6580
|
saved = true;
|
|
5716
6581
|
}
|
|
5717
6582
|
if (opts.xmtpGroup) {
|
|
5718
6583
|
const parts = opts.xmtpGroup.split(":");
|
|
5719
6584
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
5720
|
-
console.log(
|
|
6585
|
+
console.log(chalk13.red("Format: --xmtp-group <subdomain>:<groupId>"));
|
|
5721
6586
|
process.exit(1);
|
|
5722
6587
|
}
|
|
5723
6588
|
cacheGroupId(parts[0], parts[1]);
|
|
5724
|
-
console.log(
|
|
5725
|
-
console.log(
|
|
6589
|
+
console.log(chalk13.green(`XMTP group ID cached for ${parts[0]}`));
|
|
6590
|
+
console.log(chalk13.dim(` Group ID: ${parts[1]}`));
|
|
5726
6591
|
saved = true;
|
|
5727
6592
|
}
|
|
5728
6593
|
if (!saved) {
|
|
5729
|
-
console.log(
|
|
6594
|
+
console.log(chalk13.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
|
|
5730
6595
|
process.exit(1);
|
|
5731
6596
|
}
|
|
5732
6597
|
});
|
|
@@ -5737,25 +6602,25 @@ configCmd.command("show").description("Display current config for the active net
|
|
|
5737
6602
|
const config = loadConfig();
|
|
5738
6603
|
const customRpc = config.rpc?.[network];
|
|
5739
6604
|
console.log();
|
|
5740
|
-
console.log(
|
|
5741
|
-
console.log(
|
|
5742
|
-
console.log(` Network: ${
|
|
5743
|
-
console.log(` RPC: ${customRpc ?
|
|
5744
|
-
console.log(` Wallet: ${config.privateKey ?
|
|
5745
|
-
console.log(` Agent ID: ${config.agentId ??
|
|
5746
|
-
console.log(` Vault: ${contracts.vault ??
|
|
5747
|
-
console.log(` Uniswap: ${getUniswapApiKey() ?
|
|
5748
|
-
console.log(` Venice: ${getVeniceApiKey() ?
|
|
6605
|
+
console.log(chalk13.bold(`Sherwood Config`));
|
|
6606
|
+
console.log(chalk13.dim("\u2500".repeat(50)));
|
|
6607
|
+
console.log(` Network: ${chalk13.cyan(network)} (chain ${chainId})`);
|
|
6608
|
+
console.log(` RPC: ${customRpc ? chalk13.green(customRpc) : chalk13.dim("default")}`);
|
|
6609
|
+
console.log(` Wallet: ${config.privateKey ? chalk13.green("configured") : chalk13.dim("not set")}`);
|
|
6610
|
+
console.log(` Agent ID: ${config.agentId ?? chalk13.dim("not set")}`);
|
|
6611
|
+
console.log(` Vault: ${contracts.vault ?? chalk13.dim("not set")}`);
|
|
6612
|
+
console.log(` Uniswap: ${getUniswapApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
|
|
6613
|
+
console.log(` Venice: ${getVeniceApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
|
|
5749
6614
|
const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
|
|
5750
6615
|
if (groupEntries.length > 0) {
|
|
5751
6616
|
console.log();
|
|
5752
|
-
console.log(
|
|
6617
|
+
console.log(chalk13.bold(" XMTP Groups"));
|
|
5753
6618
|
for (const [sub, gid] of groupEntries) {
|
|
5754
|
-
console.log(` ${sub}: ${
|
|
6619
|
+
console.log(` ${sub}: ${chalk13.cyan(gid)}`);
|
|
5755
6620
|
}
|
|
5756
6621
|
}
|
|
5757
6622
|
console.log();
|
|
5758
|
-
console.log(
|
|
6623
|
+
console.log(chalk13.dim(" Config file: ~/.sherwood/config.json"));
|
|
5759
6624
|
console.log();
|
|
5760
6625
|
});
|
|
5761
6626
|
program.parse();
|