@sherwoodagent/cli 0.14.1 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{chat-LFH2SA46.js → chat-X3IKMSNS.js} +11 -8
  2. package/dist/{chat-LFH2SA46.js.map → chat-X3IKMSNS.js.map} +1 -1
  3. package/dist/{chunk-2N7B5N7D.js → chunk-2RYZU7EP.js} +6 -4
  4. package/dist/chunk-2RYZU7EP.js.map +1 -0
  5. package/dist/chunk-4CV4JOE5.js +27 -0
  6. package/dist/{chunk-ADPAZUTB.js → chunk-5NA345RJ.js} +23 -10
  7. package/dist/chunk-5NA345RJ.js.map +1 -0
  8. package/dist/{chunk-IIDZ2TK5.js → chunk-BCOTZTTH.js} +39 -10
  9. package/dist/chunk-BCOTZTTH.js.map +1 -0
  10. package/dist/{chunk-7CN3TSAA.js → chunk-BGEVQQES.js} +5 -3
  11. package/dist/{chunk-7CN3TSAA.js.map → chunk-BGEVQQES.js.map} +1 -1
  12. package/dist/{chunk-FWJBUK57.js → chunk-CGSXIYYZ.js} +5 -3
  13. package/dist/{chunk-FWJBUK57.js.map → chunk-CGSXIYYZ.js.map} +1 -1
  14. package/dist/{chunk-KAZRNDZQ.js → chunk-QIOS7I7I.js} +2 -2
  15. package/dist/{chunk-AC4QMJKC.js → chunk-UBBR7QVJ.js} +2 -2
  16. package/dist/{chunk-LEQLX7XF.js → chunk-YSYX555I.js} +27 -10
  17. package/dist/chunk-YSYX555I.js.map +1 -0
  18. package/dist/{config-U74QT4SC.js → config-5C7QJMPR.js} +5 -2
  19. package/dist/{cron-RG46PYWA.js → cron-IKBNUL3Q.js} +3 -1
  20. package/dist/{cron-RG46PYWA.js.map → cron-IKBNUL3Q.js.map} +1 -1
  21. package/dist/{eas-XWLMRKJU.js → eas-7U7MX24T.js} +7 -6
  22. package/dist/index.js +74 -33
  23. package/dist/index.js.map +1 -1
  24. package/dist/ipfs-AYE4J4OX.js +13 -0
  25. package/dist/{network-ROF3SSAA.js → network-QU2D677V.js} +4 -3
  26. package/dist/research-7RI7VFPK.js +15 -0
  27. package/dist/research-7RI7VFPK.js.map +1 -0
  28. package/dist/{research-QXGYAJUL.js → research-ZDTM73C7.js} +9 -8
  29. package/dist/{research-QXGYAJUL.js.map → research-ZDTM73C7.js.map} +1 -1
  30. package/dist/{session-ECETX4MM.js → session-2VK25CSW.js} +14 -12
  31. package/dist/session-2VK25CSW.js.map +1 -0
  32. package/dist/{xmtp-EKFDS5Y3.js → xmtp-Y3LAZKOC.js} +10 -7
  33. package/dist/{xmtp-EKFDS5Y3.js.map → xmtp-Y3LAZKOC.js.map} +1 -1
  34. package/package.json +1 -1
  35. package/dist/chunk-2N7B5N7D.js.map +0 -1
  36. package/dist/chunk-ADPAZUTB.js.map +0 -1
  37. package/dist/chunk-IIDZ2TK5.js.map +0 -1
  38. package/dist/chunk-LEQLX7XF.js.map +0 -1
  39. package/dist/ipfs-LUJHZGKF.js +0 -11
  40. package/dist/research-HEZP7VPY.js +0 -14
  41. package/dist/session-ECETX4MM.js.map +0 -1
  42. /package/dist/{config-U74QT4SC.js.map → chunk-4CV4JOE5.js.map} +0 -0
  43. /package/dist/{chunk-KAZRNDZQ.js.map → chunk-QIOS7I7I.js.map} +0 -0
  44. /package/dist/{chunk-AC4QMJKC.js.map → chunk-UBBR7QVJ.js.map} +0 -0
  45. /package/dist/{eas-XWLMRKJU.js.map → config-5C7QJMPR.js.map} +0 -0
  46. /package/dist/{ipfs-LUJHZGKF.js.map → eas-7U7MX24T.js.map} +0 -0
  47. /package/dist/{network-ROF3SSAA.js.map → ipfs-AYE4J4OX.js.map} +0 -0
  48. /package/dist/{research-HEZP7VPY.js.map → network-QU2D677V.js.map} +0 -0
@@ -1,19 +1,36 @@
1
+ import {
2
+ config_exports,
3
+ init_config
4
+ } from "./chunk-BCOTZTTH.js";
5
+ import {
6
+ __toCommonJS
7
+ } from "./chunk-4CV4JOE5.js";
8
+
1
9
  // src/lib/ipfs.ts
2
- var BUNDLED_PINATA_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI2NDQ0MGViOC1hYTYyLTQzY2EtOGYwNC04MDZjZmNjY2Y4YTUiLCJlbWFpbCI6ImltdGhhdGNhcmxvc0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicGluX3BvbGljeSI6eyJyZWdpb25zIjpbeyJkZXNpcmVkUmVwbGljYXRpb25Db3VudCI6MSwiaWQiOiJGUkExIn1dLCJ2ZXJzaW9uIjoxfSwibWZhX2VuYWJsZWQiOmZhbHNlLCJzdGF0dXMiOiJBQ1RJVkUifSwiYXV0aGVudGljYXRpb25UeXBlIjoic2NvcGVkS2V5Iiwic2NvcGVkS2V5S2V5IjoiMWJhZWFmMzQwODM3MGQ0NGZkZWEiLCJzY29wZWRLZXlTZWNyZXQiOiIzNDcxMmU5MTkyYTgxNWFhMGRmNjUyYjYyMDQzODQ1MDJjMmU0YWE0MDhkZTJmOTU2NWYwOTk3YTNlY2U3NGU3IiwiZXhwIjoxODAxMjc2ODExfQ.7OMJiOATpqkSwe7Orrpt2b8H_-czH-W61vBm4AHtqfA";
3
- var BUNDLED_PINATA_GATEWAY = "https://sherwood.mypinata.cloud";
4
- function getPinataApiKey() {
5
- return process.env.PINATA_API_KEY || BUNDLED_PINATA_JWT;
10
+ var DEFAULT_PINATA_GATEWAY = "https://sherwood.mypinata.cloud";
11
+ function getPinataJwt() {
12
+ const envJwt = process.env.PINATA_JWT ?? process.env.PINATA_API_KEY;
13
+ if (envJwt) return envJwt;
14
+ try {
15
+ const { loadConfig } = (init_config(), __toCommonJS(config_exports));
16
+ const config = loadConfig();
17
+ if (config.pinataJwt) return config.pinataJwt;
18
+ } catch {
19
+ }
20
+ throw new Error(
21
+ "PINATA_JWT environment variable is required for IPFS uploads. Get a free API key at https://app.pinata.cloud/developers/api-keys"
22
+ );
6
23
  }
7
24
  function getPinataGateway() {
8
- return process.env.PINATA_GATEWAY || BUNDLED_PINATA_GATEWAY;
25
+ return process.env.PINATA_GATEWAY || DEFAULT_PINATA_GATEWAY;
9
26
  }
10
27
  async function pinJSON(content, name) {
11
- const apiKey = getPinataApiKey();
28
+ const jwt = getPinataJwt();
12
29
  const response = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", {
13
30
  method: "POST",
14
31
  headers: {
15
32
  "Content-Type": "application/json",
16
- Authorization: `Bearer ${apiKey}`
33
+ Authorization: `Bearer ${jwt}`
17
34
  },
18
35
  body: JSON.stringify({
19
36
  pinataContent: content,
@@ -28,12 +45,12 @@ async function pinJSON(content, name) {
28
45
  return `ipfs://${result.IpfsHash}`;
29
46
  }
30
47
  async function uploadMetadata(metadata) {
31
- const apiKey = getPinataApiKey();
48
+ const jwt = getPinataJwt();
32
49
  const response = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", {
33
50
  method: "POST",
34
51
  headers: {
35
52
  "Content-Type": "application/json",
36
- Authorization: `Bearer ${apiKey}`
53
+ Authorization: `Bearer ${jwt}`
37
54
  },
38
55
  body: JSON.stringify({
39
56
  pinataContent: metadata,
@@ -72,4 +89,4 @@ export {
72
89
  uploadMetadata,
73
90
  fetchMetadata
74
91
  };
75
- //# sourceMappingURL=chunk-LEQLX7XF.js.map
92
+ //# sourceMappingURL=chunk-YSYX555I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/ipfs.ts"],"sourcesContent":["/**\n * IPFS metadata upload/fetch via Pinata.\n *\n * Used for syndicate metadata (name, description, strategies, terms).\n * Requires PINATA_API_KEY and PINATA_GATEWAY env vars.\n */\n\nexport interface SyndicateMetadata {\n schema: string;\n name: string;\n description: string;\n logo?: string;\n chain: string;\n strategies: {\n id: string;\n name: string;\n description: string;\n protocols: string[];\n riskLevel: string;\n }[];\n terms: {\n minDeposit?: string;\n minDepositFormatted?: string;\n feeModel?: string;\n lockPeriod?: number;\n };\n links: {\n moltbook?: string;\n dashboard?: string;\n github?: string;\n };\n}\n\nconst DEFAULT_PINATA_GATEWAY = \"https://sherwood.mypinata.cloud\";\n\nfunction getPinataJwt(): string {\n const envJwt = process.env.PINATA_JWT ?? process.env.PINATA_API_KEY;\n if (envJwt) return envJwt;\n\n // Check config file\n try {\n const { loadConfig } = require(\"./config\");\n const config = loadConfig();\n if (config.pinataJwt) return config.pinataJwt;\n } catch {}\n\n throw new Error(\n \"PINATA_JWT environment variable is required for IPFS uploads. \" +\n \"Get a free API key at https://app.pinata.cloud/developers/api-keys\",\n );\n}\n\nfunction getPinataGateway(): string {\n return process.env.PINATA_GATEWAY || DEFAULT_PINATA_GATEWAY;\n}\n\n/**\n * Pin arbitrary JSON to IPFS via Pinata.\n * Used for research results and other generic JSON payloads.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function pinJSON(content: Record<string, unknown>, name: string): Promise<string> {\n const jwt = getPinataJwt();\n\n const response = await fetch(\"https://api.pinata.cloud/pinning/pinJSONToIPFS\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${jwt}`,\n },\n body: JSON.stringify({\n pinataContent: content,\n pinataMetadata: { name },\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Pinata upload failed (${response.status}): ${text}`);\n }\n\n const result = (await response.json()) as { IpfsHash: string };\n return `ipfs://${result.IpfsHash}`;\n}\n\n/**\n * Upload syndicate metadata to IPFS via Pinata.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function uploadMetadata(metadata: SyndicateMetadata): Promise<string> {\n const jwt = getPinataJwt();\n\n const response = await fetch(\"https://api.pinata.cloud/pinning/pinJSONToIPFS\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${jwt}`,\n },\n body: JSON.stringify({\n pinataContent: metadata,\n pinataMetadata: {\n name: `sherwood-syndicate-${metadata.name.toLowerCase().replace(/\\s+/g, \"-\")}`,\n },\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Pinata upload failed (${response.status}): ${text}`);\n }\n\n const result = (await response.json()) as { IpfsHash: string };\n return `ipfs://${result.IpfsHash}`;\n}\n\n/**\n * Fetch and parse metadata from an IPFS URI.\n * Supports ipfs:// protocol URIs and raw CIDs.\n */\nexport async function fetchMetadata(ipfsURI: string): Promise<SyndicateMetadata> {\n const gateway = getPinataGateway();\n let cid: string;\n\n if (ipfsURI.startsWith(\"ipfs://\")) {\n cid = ipfsURI.slice(7);\n } else if (ipfsURI.startsWith(\"Qm\") || ipfsURI.startsWith(\"bafy\")) {\n cid = ipfsURI;\n } else {\n throw new Error(`Invalid IPFS URI: ${ipfsURI}`);\n }\n\n const url = `${gateway}/ipfs/${cid}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch metadata from ${url} (${response.status})`);\n }\n\n return (await response.json()) as SyndicateMetadata;\n}\n"],"mappings":";;;;;;;;;AAiCA,IAAM,yBAAyB;AAE/B,SAAS,eAAuB;AAC9B,QAAM,SAAS,QAAQ,IAAI,cAAc,QAAQ,IAAI;AACrD,MAAI,OAAQ,QAAO;AAGnB,MAAI;AACF,UAAM,EAAE,WAAW,IAAI;AACvB,UAAM,SAAS,WAAW;AAC1B,QAAI,OAAO,UAAW,QAAO,OAAO;AAAA,EACtC,QAAQ;AAAA,EAAC;AAET,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;AAEA,SAAS,mBAA2B;AAClC,SAAO,QAAQ,IAAI,kBAAkB;AACvC;AAOA,eAAsB,QAAQ,SAAkC,MAA+B;AAC7F,QAAM,MAAM,aAAa;AAEzB,QAAM,WAAW,MAAM,MAAM,kDAAkD;AAAA,IAC7E,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,eAAe,UAAU,GAAG;AAAA,IAC9B;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACnB,eAAe;AAAA,MACf,gBAAgB,EAAE,KAAK;AAAA,IACzB,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,IAAI,MAAM,yBAAyB,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,EACtE;AAEA,QAAM,SAAU,MAAM,SAAS,KAAK;AACpC,SAAO,UAAU,OAAO,QAAQ;AAClC;AAMA,eAAsB,eAAe,UAA8C;AACjF,QAAM,MAAM,aAAa;AAEzB,QAAM,WAAW,MAAM,MAAM,kDAAkD;AAAA,IAC7E,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,eAAe,UAAU,GAAG;AAAA,IAC9B;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACnB,eAAe;AAAA,MACf,gBAAgB;AAAA,QACd,MAAM,sBAAsB,SAAS,KAAK,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAAA,MAC9E;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,IAAI,MAAM,yBAAyB,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,EACtE;AAEA,QAAM,SAAU,MAAM,SAAS,KAAK;AACpC,SAAO,UAAU,OAAO,QAAQ;AAClC;AAMA,eAAsB,cAAc,SAA6C;AAC/E,QAAM,UAAU,iBAAiB;AACjC,MAAI;AAEJ,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,QAAQ,MAAM,CAAC;AAAA,EACvB,WAAW,QAAQ,WAAW,IAAI,KAAK,QAAQ,WAAW,MAAM,GAAG;AACjE,UAAM;AAAA,EACR,OAAO;AACL,UAAM,IAAI,MAAM,qBAAqB,OAAO,EAAE;AAAA,EAChD;AAEA,QAAM,MAAM,GAAG,OAAO,SAAS,GAAG;AAClC,QAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,iCAAiC,GAAG,KAAK,SAAS,MAAM,GAAG;AAAA,EAC7E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;","names":[]}
@@ -7,6 +7,7 @@ import {
7
7
  getNotifyTo,
8
8
  getPrivateKey,
9
9
  getVeniceApiKey,
10
+ init_config,
10
11
  loadConfig,
11
12
  saveConfig,
12
13
  setAgentId,
@@ -15,7 +16,9 @@ import {
15
16
  setNotifyTo,
16
17
  setPrivateKey,
17
18
  setVeniceApiKey
18
- } from "./chunk-IIDZ2TK5.js";
19
+ } from "./chunk-BCOTZTTH.js";
20
+ import "./chunk-4CV4JOE5.js";
21
+ init_config();
19
22
  export {
20
23
  cacheGroupId,
21
24
  getAgentId,
@@ -34,4 +37,4 @@ export {
34
37
  setPrivateKey,
35
38
  setVeniceApiKey
36
39
  };
37
- //# sourceMappingURL=config-U74QT4SC.js.map
40
+ //# sourceMappingURL=config-5C7QJMPR.js.map
@@ -1,3 +1,5 @@
1
+ import "./chunk-4CV4JOE5.js";
2
+
1
3
  // src/lib/cron.ts
2
4
  import { execFileSync } from "child_process";
3
5
  var _isOpenClaw = null;
@@ -200,4 +202,4 @@ export {
200
202
  registerSyndicateCrons,
201
203
  unregisterSyndicateCrons
202
204
  };
203
- //# sourceMappingURL=cron-RG46PYWA.js.map
205
+ //# sourceMappingURL=cron-IKBNUL3Q.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/cron.ts"],"sourcesContent":["/**\n * OpenClaw cron integration — auto-register participation crons for syndicate agents.\n *\n * When an agent creates or joins a syndicate, the CLI registers two cron jobs:\n * 1. Silent check (every 15m) — processes messages/events, responds autonomously\n * 2. Human summary (every 1h) — brief activity report delivered to the human's channel\n *\n * Non-OpenClaw agents skip silently — the caller prints guidance instead.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n// ── OpenClaw detection (cached per process) ──\n\nlet _isOpenClaw: boolean | null = null;\n\nexport function isOpenClaw(): boolean {\n if (_isOpenClaw !== null) return _isOpenClaw;\n try {\n execFileSync(\"openclaw\", [\"cron\", \"list\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n _isOpenClaw = true;\n } catch {\n _isOpenClaw = false;\n }\n return _isOpenClaw;\n}\n\n// ── Types ──\n\nexport interface CronResult {\n registered: boolean;\n isOpenClaw: boolean;\n cronNames: string[];\n}\n\nexport interface CronStatus {\n isOpenClaw: boolean;\n crons: { name: string; every: string; lastRun?: string }[];\n}\n\n// ── Cron naming ──\n\nfunction cronName(subdomain: string, testnet: boolean): string {\n return `sherwood-${subdomain}${testnet ? \"-testnet\" : \"\"}`;\n}\n\nfunction summaryCronName(subdomain: string, testnet: boolean): string {\n return `${cronName(subdomain, testnet)}-summary`;\n}\n\n// ── Prompts ──\n\nfunction silentPrompt(subdomain: string, testnet: boolean): string {\n const chainFlag = testnet ? \" --chain base-sepolia\" : \"\";\n const envPrefix = testnet ? \"ENABLE_TESTNET=true \" : \"\";\n return [\n `You are a sherwood syndicate member. Your syndicate: ${subdomain}.sherwoodagent.eth`,\n \"\",\n `Step 1: Run \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"Step 2: Parse the JSON output for new messages and events\",\n \"Step 3: If new messages from OTHER agents, respond thoughtfully via:\",\n ` \\`${envPrefix}sherwood chat ${subdomain} send \"<response>\"${chainFlag}\\``,\n \"Step 4: If you responded to anything, summarize what you did (for your own session log).\",\n \" If nothing happened, reply HEARTBEAT_OK\",\n \"\",\n \"Rules:\",\n \"- Be a real syndicate member — discuss strategies, share opinions, ask questions\",\n \"- Keep responses concise and on-topic\",\n \"- Do NOT alert your human unless something requires their approval\",\n `- You can use \\`sherwood research\\` commands if you need data to back up your response`,\n ].join(\"\\n\");\n}\n\nfunction summaryPrompt(subdomain: string, testnet: boolean): string {\n const chainFlag = testnet ? \" --chain base-sepolia\" : \"\";\n const envPrefix = testnet ? \"ENABLE_TESTNET=true \" : \"\";\n return [\n \"You are reporting syndicate activity to your human operator.\",\n \"\",\n `Run: \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"\",\n \"If there was activity (messages, events, proposals) since last check:\",\n \" Send a brief summary — who said what, any decisions made, any actions you took.\",\n \" Keep it to 3-5 lines max.\",\n \"\",\n \"If there was no activity:\",\n \" Reply HEARTBEAT_OK\",\n \"\",\n \"Only escalate (flag as urgent) if:\",\n \"- A proposal needs human sign-off\",\n \"- An agent left the syndicate\",\n \"- Risk alert or health factor warning\",\n \"- Human was directly asked for input\",\n ].join(\"\\n\");\n}\n\n// ── Helpers ──\n\n/** Parse `openclaw cron list --json` and return job names. */\nfunction listCronNames(): string[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string }[] = parsed.jobs || parsed || [];\n return jobs.map((j) => j.name);\n } catch {\n return [];\n }\n}\n\n/** Parse `openclaw cron list --json` with full details for status display. */\nfunction listCronDetails(): { name: string; every: string; lastRun?: string }[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string; every?: string; interval?: string; lastRun?: string }[] =\n parsed.jobs || parsed || [];\n return jobs.map((j) => ({\n name: j.name,\n every: j.every || j.interval || \"unknown\",\n lastRun: j.lastRun,\n }));\n } catch {\n return [];\n }\n}\n\n// ── Public API ──\n\nexport function registerSyndicateCrons(\n subdomain: string,\n testnet: boolean,\n notifyTo?: string,\n): CronResult {\n if (!isOpenClaw()) {\n return { registered: false, isOpenClaw: false, cronNames: [] };\n }\n\n const checkName = cronName(subdomain, testnet);\n const summaryName = summaryCronName(subdomain, testnet);\n const existing = listCronNames();\n const created: string[] = [];\n\n // Cron 1: Silent participation check (every 15m)\n if (!existing.includes(checkName)) {\n try {\n execFileSync(\"openclaw\", [\n \"cron\", \"create\",\n \"--name\", checkName,\n \"--every\", \"15m\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"120\",\n \"--no-deliver\",\n \"--message\", silentPrompt(subdomain, testnet),\n ], {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(checkName);\n } catch (err) {\n console.warn(`Could not create silent cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Cron 2: Human summary (every 1h)\n if (!existing.includes(summaryName)) {\n try {\n const args = [\n \"cron\", \"create\",\n \"--name\", summaryName,\n \"--every\", \"1h\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"90\",\n \"--announce\",\n ];\n\n // Use explicit destination if configured, otherwise auto-route via --channel last\n if (notifyTo) {\n args.push(\"--to\", notifyTo);\n } else {\n args.push(\"--channel\", \"last\");\n }\n\n args.push(\"--message\", summaryPrompt(subdomain, testnet));\n\n execFileSync(\"openclaw\", args, {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(summaryName);\n } catch (err) {\n console.warn(`Could not create summary cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return {\n registered: created.length > 0,\n isOpenClaw: true,\n cronNames: created,\n };\n}\n\nexport function unregisterSyndicateCrons(\n subdomain: string,\n testnet: boolean,\n): { removed: boolean; isOpenClaw: boolean } {\n if (!isOpenClaw()) {\n return { removed: false, isOpenClaw: false };\n }\n\n const names = [cronName(subdomain, testnet), summaryCronName(subdomain, testnet)];\n let removed = false;\n\n for (const name of names) {\n try {\n execFileSync(\"openclaw\", [\"cron\", \"remove\", \"--name\", name], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n removed = true;\n } catch {\n // Cron may not exist — that's fine\n }\n }\n\n return { removed, isOpenClaw: true };\n}\n\nexport function getSyndicateCronStatus(\n subdomain: string,\n testnet: boolean,\n): CronStatus {\n if (!isOpenClaw()) {\n return { isOpenClaw: false, crons: [] };\n }\n\n const prefix = cronName(subdomain, testnet);\n const all = listCronDetails();\n const matching = all.filter((c) => c.name.startsWith(prefix));\n\n return { isOpenClaw: true, crons: matching };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/lib/cron.ts"],"sourcesContent":["/**\n * OpenClaw cron integration — auto-register participation crons for syndicate agents.\n *\n * When an agent creates or joins a syndicate, the CLI registers two cron jobs:\n * 1. Silent check (every 15m) — processes messages/events, responds autonomously\n * 2. Human summary (every 1h) — brief activity report delivered to the human's channel\n *\n * Non-OpenClaw agents skip silently — the caller prints guidance instead.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n// ── OpenClaw detection (cached per process) ──\n\nlet _isOpenClaw: boolean | null = null;\n\nexport function isOpenClaw(): boolean {\n if (_isOpenClaw !== null) return _isOpenClaw;\n try {\n execFileSync(\"openclaw\", [\"cron\", \"list\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n _isOpenClaw = true;\n } catch {\n _isOpenClaw = false;\n }\n return _isOpenClaw;\n}\n\n// ── Types ──\n\nexport interface CronResult {\n registered: boolean;\n isOpenClaw: boolean;\n cronNames: string[];\n}\n\nexport interface CronStatus {\n isOpenClaw: boolean;\n crons: { name: string; every: string; lastRun?: string }[];\n}\n\n// ── Cron naming ──\n\nfunction cronName(subdomain: string, testnet: boolean): string {\n return `sherwood-${subdomain}${testnet ? \"-testnet\" : \"\"}`;\n}\n\nfunction summaryCronName(subdomain: string, testnet: boolean): string {\n return `${cronName(subdomain, testnet)}-summary`;\n}\n\n// ── Prompts ──\n\nfunction silentPrompt(subdomain: string, testnet: boolean): string {\n const chainFlag = testnet ? \" --chain base-sepolia\" : \"\";\n const envPrefix = testnet ? \"ENABLE_TESTNET=true \" : \"\";\n return [\n `You are a sherwood syndicate member. Your syndicate: ${subdomain}.sherwoodagent.eth`,\n \"\",\n `Step 1: Run \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"Step 2: Parse the JSON output for new messages and events\",\n \"Step 3: If new messages from OTHER agents, respond thoughtfully via:\",\n ` \\`${envPrefix}sherwood chat ${subdomain} send \"<response>\"${chainFlag}\\``,\n \"Step 4: If you responded to anything, summarize what you did (for your own session log).\",\n \" If nothing happened, reply HEARTBEAT_OK\",\n \"\",\n \"Rules:\",\n \"- Be a real syndicate member — discuss strategies, share opinions, ask questions\",\n \"- Keep responses concise and on-topic\",\n \"- Do NOT alert your human unless something requires their approval\",\n `- You can use \\`sherwood research\\` commands if you need data to back up your response`,\n ].join(\"\\n\");\n}\n\nfunction summaryPrompt(subdomain: string, testnet: boolean): string {\n const chainFlag = testnet ? \" --chain base-sepolia\" : \"\";\n const envPrefix = testnet ? \"ENABLE_TESTNET=true \" : \"\";\n return [\n \"You are reporting syndicate activity to your human operator.\",\n \"\",\n `Run: \\`${envPrefix}sherwood session check ${subdomain}${chainFlag}\\``,\n \"\",\n \"If there was activity (messages, events, proposals) since last check:\",\n \" Send a brief summary — who said what, any decisions made, any actions you took.\",\n \" Keep it to 3-5 lines max.\",\n \"\",\n \"If there was no activity:\",\n \" Reply HEARTBEAT_OK\",\n \"\",\n \"Only escalate (flag as urgent) if:\",\n \"- A proposal needs human sign-off\",\n \"- An agent left the syndicate\",\n \"- Risk alert or health factor warning\",\n \"- Human was directly asked for input\",\n ].join(\"\\n\");\n}\n\n// ── Helpers ──\n\n/** Parse `openclaw cron list --json` and return job names. */\nfunction listCronNames(): string[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string }[] = parsed.jobs || parsed || [];\n return jobs.map((j) => j.name);\n } catch {\n return [];\n }\n}\n\n/** Parse `openclaw cron list --json` with full details for status display. */\nfunction listCronDetails(): { name: string; every: string; lastRun?: string }[] {\n try {\n const raw = execFileSync(\"openclaw\", [\"cron\", \"list\", \"--json\"], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n const parsed = JSON.parse(raw);\n const jobs: { name: string; every?: string; interval?: string; lastRun?: string }[] =\n parsed.jobs || parsed || [];\n return jobs.map((j) => ({\n name: j.name,\n every: j.every || j.interval || \"unknown\",\n lastRun: j.lastRun,\n }));\n } catch {\n return [];\n }\n}\n\n// ── Public API ──\n\nexport function registerSyndicateCrons(\n subdomain: string,\n testnet: boolean,\n notifyTo?: string,\n): CronResult {\n if (!isOpenClaw()) {\n return { registered: false, isOpenClaw: false, cronNames: [] };\n }\n\n const checkName = cronName(subdomain, testnet);\n const summaryName = summaryCronName(subdomain, testnet);\n const existing = listCronNames();\n const created: string[] = [];\n\n // Cron 1: Silent participation check (every 15m)\n if (!existing.includes(checkName)) {\n try {\n execFileSync(\"openclaw\", [\n \"cron\", \"create\",\n \"--name\", checkName,\n \"--every\", \"15m\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"120\",\n \"--no-deliver\",\n \"--message\", silentPrompt(subdomain, testnet),\n ], {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(checkName);\n } catch (err) {\n console.warn(`Could not create silent cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Cron 2: Human summary (every 1h)\n if (!existing.includes(summaryName)) {\n try {\n const args = [\n \"cron\", \"create\",\n \"--name\", summaryName,\n \"--every\", \"1h\",\n \"--session\", \"isolated\",\n \"--timeout-seconds\", \"90\",\n \"--announce\",\n ];\n\n // Use explicit destination if configured, otherwise auto-route via --channel last\n if (notifyTo) {\n args.push(\"--to\", notifyTo);\n } else {\n args.push(\"--channel\", \"last\");\n }\n\n args.push(\"--message\", summaryPrompt(subdomain, testnet));\n\n execFileSync(\"openclaw\", args, {\n encoding: \"utf8\",\n timeout: 30_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n created.push(summaryName);\n } catch (err) {\n console.warn(`Could not create summary cron: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return {\n registered: created.length > 0,\n isOpenClaw: true,\n cronNames: created,\n };\n}\n\nexport function unregisterSyndicateCrons(\n subdomain: string,\n testnet: boolean,\n): { removed: boolean; isOpenClaw: boolean } {\n if (!isOpenClaw()) {\n return { removed: false, isOpenClaw: false };\n }\n\n const names = [cronName(subdomain, testnet), summaryCronName(subdomain, testnet)];\n let removed = false;\n\n for (const name of names) {\n try {\n execFileSync(\"openclaw\", [\"cron\", \"remove\", \"--name\", name], {\n encoding: \"utf8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n removed = true;\n } catch {\n // Cron may not exist — that's fine\n }\n }\n\n return { removed, isOpenClaw: true };\n}\n\nexport function getSyndicateCronStatus(\n subdomain: string,\n testnet: boolean,\n): CronStatus {\n if (!isOpenClaw()) {\n return { isOpenClaw: false, crons: [] };\n }\n\n const prefix = cronName(subdomain, testnet);\n const all = listCronDetails();\n const matching = all.filter((c) => c.name.startsWith(prefix));\n\n return { isOpenClaw: true, crons: matching };\n}\n"],"mappings":"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","names":[]}
@@ -6,11 +6,12 @@ import {
6
6
  queryApprovals,
7
7
  queryJoinRequests,
8
8
  revokeAttestation
9
- } from "./chunk-2N7B5N7D.js";
10
- import "./chunk-AC4QMJKC.js";
11
- import "./chunk-FWJBUK57.js";
12
- import "./chunk-7CN3TSAA.js";
13
- import "./chunk-IIDZ2TK5.js";
9
+ } from "./chunk-2RYZU7EP.js";
10
+ import "./chunk-UBBR7QVJ.js";
11
+ import "./chunk-CGSXIYYZ.js";
12
+ import "./chunk-BGEVQQES.js";
13
+ import "./chunk-BCOTZTTH.js";
14
+ import "./chunk-4CV4JOE5.js";
14
15
  export {
15
16
  createApproval,
16
17
  createJoinRequest,
@@ -20,4 +21,4 @@ export {
20
21
  queryJoinRequests,
21
22
  revokeAttestation
22
23
  };
23
- //# sourceMappingURL=eas-XWLMRKJU.js.map
24
+ //# sourceMappingURL=eas-7U7MX24T.js.map
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  fetchMetadata,
4
4
  uploadMetadata
5
- } from "./chunk-LEQLX7XF.js";
5
+ } from "./chunk-YSYX555I.js";
6
6
  import {
7
7
  createApproval,
8
8
  createJoinRequest,
@@ -10,7 +10,7 @@ import {
10
10
  queryApprovals,
11
11
  queryJoinRequests,
12
12
  revokeAttestation
13
- } from "./chunk-2N7B5N7D.js";
13
+ } from "./chunk-2RYZU7EP.js";
14
14
  import {
15
15
  approveDepositor,
16
16
  deposit,
@@ -26,7 +26,7 @@ import {
26
26
  resolveVaultSyndicate,
27
27
  setTextRecord,
28
28
  setVaultAddress
29
- } from "./chunk-ADPAZUTB.js";
29
+ } from "./chunk-5NA345RJ.js";
30
30
  import {
31
31
  AGENT_REGISTRY,
32
32
  EAS_SCHEMAS,
@@ -42,12 +42,12 @@ import {
42
42
  UNISWAP_QUOTER_V2_ABI,
43
43
  VENICE,
44
44
  VENICE_STAKING_ABI
45
- } from "./chunk-AC4QMJKC.js";
45
+ } from "./chunk-UBBR7QVJ.js";
46
46
  import {
47
47
  getAccount,
48
48
  getPublicClient,
49
49
  getWalletClient
50
- } from "./chunk-FWJBUK57.js";
50
+ } from "./chunk-CGSXIYYZ.js";
51
51
  import {
52
52
  VALID_NETWORKS,
53
53
  getChain,
@@ -56,13 +56,14 @@ import {
56
56
  getRpcUrl,
57
57
  isTestnet,
58
58
  setNetwork
59
- } from "./chunk-7CN3TSAA.js";
59
+ } from "./chunk-BGEVQQES.js";
60
60
  import {
61
61
  cacheGroupId,
62
62
  getAgentId,
63
63
  getChainContracts,
64
64
  getNotifyTo,
65
65
  getVeniceApiKey,
66
+ init_config,
66
67
  loadConfig,
67
68
  setAgentId,
68
69
  setChainContract,
@@ -70,12 +71,14 @@ import {
70
71
  setNotifyTo,
71
72
  setPrivateKey,
72
73
  setVeniceApiKey
73
- } from "./chunk-IIDZ2TK5.js";
74
+ } from "./chunk-BCOTZTTH.js";
75
+ import "./chunk-4CV4JOE5.js";
74
76
 
75
77
  // src/index.ts
76
78
  import { config as loadDotenv } from "dotenv";
79
+ import { createRequire } from "module";
77
80
  import { Command, Option } from "commander";
78
- import { parseUnits as parseUnits8 } from "viem";
81
+ import { parseUnits as parseUnits8, isAddress as isAddress5 } from "viem";
79
82
  import chalk7 from "chalk";
80
83
  import ora7 from "ora";
81
84
  import { input, confirm, select } from "@inquirer/prompts";
@@ -572,7 +575,8 @@ async function getActiveSyndicates() {
572
575
  }
573
576
  async function updateMetadata(syndicateId, metadataURI) {
574
577
  const wallet = getWalletClient();
575
- return wallet.writeContract({
578
+ const client = getPublicClient();
579
+ const hash = await wallet.writeContract({
576
580
  account: getAccount(),
577
581
  chain: getChain(),
578
582
  address: getFactoryAddress(),
@@ -580,6 +584,8 @@ async function updateMetadata(syndicateId, metadataURI) {
580
584
  functionName: "updateMetadata",
581
585
  args: [syndicateId, metadataURI]
582
586
  });
587
+ await client.waitForTransactionReceipt({ hash });
588
+ return hash;
583
589
  }
584
590
 
585
591
  // src/lib/subgraph.ts
@@ -613,10 +619,27 @@ async function query(graphql, variables) {
613
619
  return result.data;
614
620
  }
615
621
  async function getActiveSyndicates2(creator) {
616
- const where = creator ? `where: { active: true, creator: "${creator.toLowerCase()}" }` : `where: { active: true }`;
622
+ if (creator) {
623
+ const data2 = await query(
624
+ `query($creator: String!) {
625
+ syndicates(where: { active: true, creator: $creator }, orderBy: createdAt, orderDirection: desc, first: 100) {
626
+ id
627
+ vault
628
+ creator
629
+ metadataURI
630
+ createdAt
631
+ active
632
+ totalDeposits
633
+ totalWithdrawals
634
+ }
635
+ }`,
636
+ { creator: creator.toLowerCase() }
637
+ );
638
+ return data2.syndicates;
639
+ }
617
640
  const data = await query(`
618
641
  {
619
- syndicates(${where}, orderBy: createdAt, orderDirection: desc, first: 100) {
642
+ syndicates(where: { active: true }, orderBy: createdAt, orderDirection: desc, first: 100) {
620
643
  id
621
644
  vault
622
645
  creator
@@ -841,6 +864,7 @@ function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimal
841
864
  }
842
865
 
843
866
  // src/lib/venice.ts
867
+ init_config();
844
868
  var VENICE_API_BASE = "https://api.venice.ai/api/v1";
845
869
  async function provisionApiKey() {
846
870
  const account = getAccount();
@@ -885,6 +909,7 @@ async function checkApiKeyValid() {
885
909
  }
886
910
 
887
911
  // src/commands/venice.ts
912
+ init_config();
888
913
  var VALID_FEES2 = [500, 3e3, 1e4];
889
914
  function registerVeniceCommands(program2) {
890
915
  const venice = program2.command("venice").description("Venice private inference \u2014 stake VVV, provision API keys");
@@ -1463,6 +1488,7 @@ function registerAllowanceCommands(program2) {
1463
1488
  import chalk4 from "chalk";
1464
1489
  import ora4 from "ora";
1465
1490
  import { SDK } from "agent0-sdk";
1491
+ init_config();
1466
1492
  var IDENTITY_REGISTRY_ABI = [
1467
1493
  {
1468
1494
  name: "balanceOf",
@@ -1978,8 +2004,8 @@ function formatDurationLong(seconds) {
1978
2004
  if (s >= 60) return `${(s / 60).toFixed(0)} min`;
1979
2005
  return `${s}s`;
1980
2006
  }
1981
- function formatShares(raw) {
1982
- const num = Number(raw) / 1e6;
2007
+ function formatShares(raw, decimals = 6) {
2008
+ const num = Number(raw) / 10 ** decimals;
1983
2009
  return num.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 2 });
1984
2010
  }
1985
2011
  function formatUSDC(raw) {
@@ -2401,7 +2427,7 @@ function registerGovernorCommands(program2) {
2401
2427
  const spinner = ora6("Setting voting period...").start();
2402
2428
  try {
2403
2429
  const hash = await setVotingPeriod(parseBigIntArg(opts.seconds, "seconds"));
2404
- spinner.succeed(G2(`Voting period updated to ${opts.seconds}s`));
2430
+ spinner.succeed(G2(`Voting period change queued (${opts.seconds}s). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2405
2431
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2406
2432
  } catch (err) {
2407
2433
  spinner.fail("Failed to set voting period");
@@ -2413,7 +2439,7 @@ function registerGovernorCommands(program2) {
2413
2439
  const spinner = ora6("Setting execution window...").start();
2414
2440
  try {
2415
2441
  const hash = await setExecutionWindow(parseBigIntArg(opts.seconds, "seconds"));
2416
- spinner.succeed(G2(`Execution window updated to ${opts.seconds}s`));
2442
+ spinner.succeed(G2(`Execution window change queued (${opts.seconds}s). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2417
2443
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2418
2444
  } catch (err) {
2419
2445
  spinner.fail("Failed to set execution window");
@@ -2425,7 +2451,7 @@ function registerGovernorCommands(program2) {
2425
2451
  const spinner = ora6("Setting veto threshold...").start();
2426
2452
  try {
2427
2453
  const hash = await setVetoThresholdBps(parseBigIntArg(opts.bps, "bps"));
2428
- spinner.succeed(G2(`Veto threshold updated to ${Number(opts.bps) / 100}%`));
2454
+ spinner.succeed(G2(`Veto threshold change queued (${Number(opts.bps) / 100}%). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2429
2455
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2430
2456
  } catch (err) {
2431
2457
  spinner.fail("Failed to set veto threshold");
@@ -2437,7 +2463,7 @@ function registerGovernorCommands(program2) {
2437
2463
  const spinner = ora6("Setting max fee...").start();
2438
2464
  try {
2439
2465
  const hash = await setMaxPerformanceFeeBps(parseBigIntArg(opts.bps, "bps"));
2440
- spinner.succeed(G2(`Max performance fee updated to ${Number(opts.bps) / 100}%`));
2466
+ spinner.succeed(G2(`Max performance fee change queued (${Number(opts.bps) / 100}%). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2441
2467
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2442
2468
  } catch (err) {
2443
2469
  spinner.fail("Failed to set max fee");
@@ -2449,7 +2475,7 @@ function registerGovernorCommands(program2) {
2449
2475
  const spinner = ora6("Setting max duration...").start();
2450
2476
  try {
2451
2477
  const hash = await setMaxStrategyDuration(parseBigIntArg(opts.seconds, "seconds"));
2452
- spinner.succeed(G2(`Max strategy duration updated to ${opts.seconds}s`));
2478
+ spinner.succeed(G2(`Max strategy duration change queued (${opts.seconds}s). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2453
2479
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2454
2480
  } catch (err) {
2455
2481
  spinner.fail("Failed to set max duration");
@@ -2461,7 +2487,7 @@ function registerGovernorCommands(program2) {
2461
2487
  const spinner = ora6("Setting cooldown...").start();
2462
2488
  try {
2463
2489
  const hash = await setCooldownPeriod(parseBigIntArg(opts.seconds, "seconds"));
2464
- spinner.succeed(G2(`Cooldown period updated to ${opts.seconds}s`));
2490
+ spinner.succeed(G2(`Cooldown period change queued (${opts.seconds}s). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2465
2491
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2466
2492
  } catch (err) {
2467
2493
  spinner.fail("Failed to set cooldown");
@@ -2473,7 +2499,7 @@ function registerGovernorCommands(program2) {
2473
2499
  const spinner = ora6("Setting protocol fee...").start();
2474
2500
  try {
2475
2501
  const hash = await setProtocolFeeBps(parseBigIntArg(opts.bps, "bps"));
2476
- spinner.succeed(G2(`Protocol fee updated to ${Number(opts.bps) / 100}%`));
2502
+ spinner.succeed(G2(`Protocol fee change queued (${Number(opts.bps) / 100}%). Finalize after the timelock delay with \`sherwood governor finalize-param\`.`));
2477
2503
  console.log(DIM2(` ${getExplorerUrl(hash)}`));
2478
2504
  } catch (err) {
2479
2505
  spinner.fail("Failed to set protocol fee");
@@ -2484,15 +2510,18 @@ function registerGovernorCommands(program2) {
2484
2510
  }
2485
2511
 
2486
2512
  // src/index.ts
2513
+ init_config();
2487
2514
  try {
2488
2515
  loadDotenv();
2489
2516
  } catch {
2490
2517
  }
2518
+ var require2 = createRequire(import.meta.url);
2519
+ var { version: CLI_VERSION } = require2("../package.json");
2491
2520
  async function loadXmtp() {
2492
- return import("./xmtp-EKFDS5Y3.js");
2521
+ return import("./xmtp-Y3LAZKOC.js");
2493
2522
  }
2494
2523
  async function loadCron() {
2495
- return import("./cron-RG46PYWA.js");
2524
+ return import("./cron-IKBNUL3Q.js");
2496
2525
  }
2497
2526
  var G3 = chalk7.green;
2498
2527
  var W3 = chalk7.white;
@@ -2500,18 +2529,26 @@ var DIM3 = chalk7.gray;
2500
2529
  var BOLD3 = chalk7.white.bold;
2501
2530
  var LABEL3 = chalk7.green.bold;
2502
2531
  var SEP3 = () => console.log(DIM3("\u2500".repeat(60)));
2532
+ function validateAddress(value, name) {
2533
+ if (!isAddress5(value)) {
2534
+ console.error(chalk7.red(`Invalid ${name} address: ${value}`));
2535
+ process.exit(1);
2536
+ }
2537
+ return value;
2538
+ }
2503
2539
  function resolveVault(opts) {
2504
2540
  if (opts.vault) {
2505
- setVaultAddress(opts.vault);
2541
+ setVaultAddress(validateAddress(opts.vault, "vault"));
2506
2542
  }
2507
2543
  }
2508
2544
  var program = new Command();
2509
- program.name("sherwood").description("CLI for agent-managed investment syndicates").version("0.1.0").addOption(
2545
+ program.name("sherwood").description("CLI for agent-managed investment syndicates").version(CLI_VERSION).addOption(
2510
2546
  new Option("--chain <network>", "Target network").choices(VALID_NETWORKS).default("base")
2511
2547
  ).option("--testnet", "Alias for --chain base-sepolia (deprecated)", false).hook("preAction", (thisCommand) => {
2512
2548
  const opts = thisCommand.optsWithGlobals();
2513
2549
  let network = opts.chain;
2514
2550
  if (opts.testnet) {
2551
+ process.env.ENABLE_TESTNET = "true";
2515
2552
  if (network !== "base") {
2516
2553
  console.warn(
2517
2554
  chalk7.yellow("[warn] --testnet ignored, --chain takes precedence")
@@ -2826,7 +2863,8 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
2826
2863
  resolveVault(opts);
2827
2864
  const spinner = ora7("Approving depositor...").start();
2828
2865
  try {
2829
- const hash = await approveDepositor(opts.depositor);
2866
+ const depositor = validateAddress(opts.depositor, "depositor");
2867
+ const hash = await approveDepositor(depositor);
2830
2868
  spinner.succeed(`Depositor approved: ${hash}`);
2831
2869
  console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2832
2870
  } catch (err) {
@@ -2839,7 +2877,8 @@ syndicate.command("remove-depositor").description("Remove an address from the de
2839
2877
  resolveVault(opts);
2840
2878
  const spinner = ora7("Removing depositor...").start();
2841
2879
  try {
2842
- const hash = await removeDepositor(opts.depositor);
2880
+ const depositor = validateAddress(opts.depositor, "depositor");
2881
+ const hash = await removeDepositor(depositor);
2843
2882
  spinner.succeed(`Depositor removed: ${hash}`);
2844
2883
  console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2845
2884
  } catch (err) {
@@ -2859,10 +2898,11 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
2859
2898
  spinner.fail("Only the syndicate creator can add agents");
2860
2899
  process.exit(1);
2861
2900
  }
2901
+ const agentWallet = validateAddress(opts.wallet, "wallet");
2862
2902
  spinner.text = "Registering agent...";
2863
2903
  const hash = await registerAgent(
2864
2904
  BigInt(opts.agentId),
2865
- opts.wallet
2905
+ agentWallet
2866
2906
  );
2867
2907
  spinner.succeed(`Agent registered: ${hash}`);
2868
2908
  console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
@@ -3054,6 +3094,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
3054
3094
  resolveVault(opts);
3055
3095
  }
3056
3096
  const vaultAddress = getVaultAddress();
3097
+ const agentWallet = validateAddress(opts.wallet, "wallet");
3057
3098
  const { creator, subdomain, id: syndicateId } = await resolveVaultSyndicate(vaultAddress);
3058
3099
  const callerAddress = getAccount().address.toLowerCase();
3059
3100
  if (creator.toLowerCase() !== callerAddress) {
@@ -3065,7 +3106,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
3065
3106
  try {
3066
3107
  const regHash = await registerAgent(
3067
3108
  BigInt(opts.agentId),
3068
- opts.wallet
3109
+ agentWallet
3069
3110
  );
3070
3111
  agentWasRegistered = true;
3071
3112
  console.log(DIM3(` Agent registered: ${getExplorerUrl(regHash)}`));
@@ -3095,7 +3136,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
3095
3136
  syndicateId,
3096
3137
  BigInt(opts.agentId),
3097
3138
  vaultAddress,
3098
- opts.wallet
3139
+ agentWallet
3099
3140
  );
3100
3141
  approvalUid = result.uid;
3101
3142
  }
@@ -3278,7 +3319,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
3278
3319
  await runLeveredSwap(opts);
3279
3320
  });
3280
3321
  program.command("providers").description("List available DeFi providers").action(async () => {
3281
- const { MessariProvider, NansenProvider } = await import("./research-HEZP7VPY.js");
3322
+ const { MessariProvider, NansenProvider } = await import("./research-7RI7VFPK.js");
3282
3323
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
3283
3324
  for (const p of providers) {
3284
3325
  const info = p.info();
@@ -3289,7 +3330,7 @@ ${info.name} (${info.type})`);
3289
3330
  }
3290
3331
  });
3291
3332
  try {
3292
- const { registerChatCommands } = await import("./chat-LFH2SA46.js");
3333
+ const { registerChatCommands } = await import("./chat-X3IKMSNS.js");
3293
3334
  registerChatCommands(program);
3294
3335
  } catch {
3295
3336
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -3299,14 +3340,14 @@ try {
3299
3340
  process.exit(1);
3300
3341
  });
3301
3342
  }
3302
- var { registerSessionCommands } = await import("./session-ECETX4MM.js");
3343
+ var { registerSessionCommands } = await import("./session-2VK25CSW.js");
3303
3344
  registerSessionCommands(program);
3304
3345
  registerVeniceCommands(program);
3305
3346
  registerAllowanceCommands(program);
3306
3347
  registerIdentityCommands(program);
3307
3348
  registerProposalCommands(program);
3308
3349
  registerGovernorCommands(program);
3309
- var { registerResearchCommands } = await import("./research-QXGYAJUL.js");
3350
+ var { registerResearchCommands } = await import("./research-ZDTM73C7.js");
3310
3351
  registerResearchCommands(program);
3311
3352
  var configCmd = program.command("config");
3312
3353
  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.)").action((opts) => {