@sherwoodagent/cli 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/{chat-BNYWD3EL.js → chat-2X5FQI5X.js} +9 -6
  2. package/dist/{chat-BNYWD3EL.js.map → chat-2X5FQI5X.js.map} +1 -1
  3. package/dist/{chunk-HKZONPXW.js → chunk-56W62BYY.js} +8 -6
  4. package/dist/{chunk-HKZONPXW.js.map → chunk-56W62BYY.js.map} +1 -1
  5. package/dist/{chunk-Q37V65B6.js → chunk-6IUSJWAL.js} +9 -5
  6. package/dist/{chunk-Q37V65B6.js.map → chunk-6IUSJWAL.js.map} +1 -1
  7. package/dist/chunk-7CN3TSAA.js +107 -0
  8. package/dist/chunk-7CN3TSAA.js.map +1 -0
  9. package/dist/chunk-FWJBUK57.js +65 -0
  10. package/dist/chunk-FWJBUK57.js.map +1 -0
  11. package/dist/chunk-IIDZ2TK5.js +100 -0
  12. package/dist/chunk-IIDZ2TK5.js.map +1 -0
  13. package/dist/{chunk-OUES74ID.js → chunk-IXMM3TT3.js} +19 -7
  14. package/dist/{chunk-OUES74ID.js.map → chunk-IXMM3TT3.js.map} +1 -1
  15. package/dist/{chunk-4MTHXGTK.js → chunk-KAZRNDZQ.js} +2 -2
  16. package/dist/config-U74QT4SC.js +37 -0
  17. package/dist/cron-RG46PYWA.js +203 -0
  18. package/dist/cron-RG46PYWA.js.map +1 -0
  19. package/dist/{eas-EL3XCEXQ.js → eas-HQ5OTAFW.js} +6 -4
  20. package/dist/index.js +100 -30
  21. package/dist/index.js.map +1 -1
  22. package/dist/network-ROF3SSAA.js +26 -0
  23. package/dist/network-ROF3SSAA.js.map +1 -0
  24. package/dist/research-HEZP7VPY.js +14 -0
  25. package/dist/research-HEZP7VPY.js.map +1 -0
  26. package/dist/{research-57SKO27M.js → research-PLYYYJ4F.js} +7 -5
  27. package/dist/{research-57SKO27M.js.map → research-PLYYYJ4F.js.map} +1 -1
  28. package/dist/{session-QQSHCGNK.js → session-QBWUWXCH.js} +47 -5
  29. package/dist/session-QBWUWXCH.js.map +1 -0
  30. package/dist/{xmtp-S4VRXMFK.js → xmtp-A5G2GEWF.js} +10 -6
  31. package/dist/{xmtp-S4VRXMFK.js.map → xmtp-A5G2GEWF.js.map} +1 -1
  32. package/package.json +1 -1
  33. package/dist/chunk-5ADWTXNT.js +0 -233
  34. package/dist/chunk-5ADWTXNT.js.map +0 -1
  35. package/dist/research-ZR7HXITG.js +0 -12
  36. package/dist/session-QQSHCGNK.js.map +0 -1
  37. /package/dist/{chunk-4MTHXGTK.js.map → chunk-KAZRNDZQ.js.map} +0 -0
  38. /package/dist/{eas-EL3XCEXQ.js.map → config-U74QT4SC.js.map} +0 -0
  39. /package/dist/{research-ZR7HXITG.js.map → eas-HQ5OTAFW.js.map} +0 -0
@@ -0,0 +1,203 @@
1
+ // src/lib/cron.ts
2
+ import { execFileSync } from "child_process";
3
+ var _isOpenClaw = null;
4
+ function isOpenClaw() {
5
+ if (_isOpenClaw !== null) return _isOpenClaw;
6
+ try {
7
+ execFileSync("openclaw", ["cron", "list"], {
8
+ encoding: "utf8",
9
+ timeout: 1e4,
10
+ stdio: ["pipe", "pipe", "pipe"]
11
+ });
12
+ _isOpenClaw = true;
13
+ } catch {
14
+ _isOpenClaw = false;
15
+ }
16
+ return _isOpenClaw;
17
+ }
18
+ function cronName(subdomain, testnet) {
19
+ return `sherwood-${subdomain}${testnet ? "-testnet" : ""}`;
20
+ }
21
+ function summaryCronName(subdomain, testnet) {
22
+ return `${cronName(subdomain, testnet)}-summary`;
23
+ }
24
+ function silentPrompt(subdomain, testnet) {
25
+ const chainFlag = testnet ? " --chain base-sepolia" : "";
26
+ const envPrefix = testnet ? "ENABLE_TESTNET=true " : "";
27
+ return [
28
+ `You are a sherwood syndicate member. Your syndicate: ${subdomain}.sherwoodagent.eth`,
29
+ "",
30
+ `Step 1: Run \`${envPrefix}sherwood session check ${subdomain}${chainFlag}\``,
31
+ "Step 2: Parse the JSON output for new messages and events",
32
+ "Step 3: If new messages from OTHER agents, respond thoughtfully via:",
33
+ ` \`${envPrefix}sherwood chat ${subdomain} send "<response>"${chainFlag}\``,
34
+ "Step 4: If you responded to anything, summarize what you did (for your own session log).",
35
+ " If nothing happened, reply HEARTBEAT_OK",
36
+ "",
37
+ "Rules:",
38
+ "- Be a real syndicate member \u2014 discuss strategies, share opinions, ask questions",
39
+ "- Keep responses concise and on-topic",
40
+ "- Do NOT alert your human unless something requires their approval",
41
+ `- You can use \`sherwood research\` commands if you need data to back up your response`
42
+ ].join("\n");
43
+ }
44
+ function summaryPrompt(subdomain, testnet) {
45
+ const chainFlag = testnet ? " --chain base-sepolia" : "";
46
+ const envPrefix = testnet ? "ENABLE_TESTNET=true " : "";
47
+ return [
48
+ "You are reporting syndicate activity to your human operator.",
49
+ "",
50
+ `Run: \`${envPrefix}sherwood session check ${subdomain}${chainFlag}\``,
51
+ "",
52
+ "If there was activity (messages, events, proposals) since last check:",
53
+ " Send a brief summary \u2014 who said what, any decisions made, any actions you took.",
54
+ " Keep it to 3-5 lines max.",
55
+ "",
56
+ "If there was no activity:",
57
+ " Reply HEARTBEAT_OK",
58
+ "",
59
+ "Only escalate (flag as urgent) if:",
60
+ "- A proposal needs human sign-off",
61
+ "- An agent left the syndicate",
62
+ "- Risk alert or health factor warning",
63
+ "- Human was directly asked for input"
64
+ ].join("\n");
65
+ }
66
+ function listCronNames() {
67
+ try {
68
+ const raw = execFileSync("openclaw", ["cron", "list", "--json"], {
69
+ encoding: "utf8",
70
+ timeout: 1e4,
71
+ stdio: ["pipe", "pipe", "pipe"]
72
+ });
73
+ const parsed = JSON.parse(raw);
74
+ const jobs = parsed.jobs || parsed || [];
75
+ return jobs.map((j) => j.name);
76
+ } catch {
77
+ return [];
78
+ }
79
+ }
80
+ function listCronDetails() {
81
+ try {
82
+ const raw = execFileSync("openclaw", ["cron", "list", "--json"], {
83
+ encoding: "utf8",
84
+ timeout: 1e4,
85
+ stdio: ["pipe", "pipe", "pipe"]
86
+ });
87
+ const parsed = JSON.parse(raw);
88
+ const jobs = parsed.jobs || parsed || [];
89
+ return jobs.map((j) => ({
90
+ name: j.name,
91
+ every: j.every || j.interval || "unknown",
92
+ lastRun: j.lastRun
93
+ }));
94
+ } catch {
95
+ return [];
96
+ }
97
+ }
98
+ function registerSyndicateCrons(subdomain, testnet, notifyTo) {
99
+ if (!isOpenClaw()) {
100
+ return { registered: false, isOpenClaw: false, cronNames: [] };
101
+ }
102
+ const checkName = cronName(subdomain, testnet);
103
+ const summaryName = summaryCronName(subdomain, testnet);
104
+ const existing = listCronNames();
105
+ const created = [];
106
+ if (!existing.includes(checkName)) {
107
+ try {
108
+ execFileSync("openclaw", [
109
+ "cron",
110
+ "create",
111
+ "--name",
112
+ checkName,
113
+ "--every",
114
+ "15m",
115
+ "--session",
116
+ "isolated",
117
+ "--timeout-seconds",
118
+ "120",
119
+ "--no-deliver",
120
+ "--message",
121
+ silentPrompt(subdomain, testnet)
122
+ ], {
123
+ encoding: "utf8",
124
+ timeout: 3e4,
125
+ stdio: ["pipe", "pipe", "pipe"]
126
+ });
127
+ created.push(checkName);
128
+ } catch (err) {
129
+ console.warn(`Could not create silent cron: ${err instanceof Error ? err.message : String(err)}`);
130
+ }
131
+ }
132
+ if (!existing.includes(summaryName)) {
133
+ try {
134
+ const args = [
135
+ "cron",
136
+ "create",
137
+ "--name",
138
+ summaryName,
139
+ "--every",
140
+ "1h",
141
+ "--session",
142
+ "isolated",
143
+ "--timeout-seconds",
144
+ "90",
145
+ "--announce"
146
+ ];
147
+ if (notifyTo) {
148
+ args.push("--to", notifyTo);
149
+ } else {
150
+ args.push("--channel", "last");
151
+ }
152
+ args.push("--message", summaryPrompt(subdomain, testnet));
153
+ execFileSync("openclaw", args, {
154
+ encoding: "utf8",
155
+ timeout: 3e4,
156
+ stdio: ["pipe", "pipe", "pipe"]
157
+ });
158
+ created.push(summaryName);
159
+ } catch (err) {
160
+ console.warn(`Could not create summary cron: ${err instanceof Error ? err.message : String(err)}`);
161
+ }
162
+ }
163
+ return {
164
+ registered: created.length > 0,
165
+ isOpenClaw: true,
166
+ cronNames: created
167
+ };
168
+ }
169
+ function unregisterSyndicateCrons(subdomain, testnet) {
170
+ if (!isOpenClaw()) {
171
+ return { removed: false, isOpenClaw: false };
172
+ }
173
+ const names = [cronName(subdomain, testnet), summaryCronName(subdomain, testnet)];
174
+ let removed = false;
175
+ for (const name of names) {
176
+ try {
177
+ execFileSync("openclaw", ["cron", "remove", "--name", name], {
178
+ encoding: "utf8",
179
+ timeout: 1e4,
180
+ stdio: ["pipe", "pipe", "pipe"]
181
+ });
182
+ removed = true;
183
+ } catch {
184
+ }
185
+ }
186
+ return { removed, isOpenClaw: true };
187
+ }
188
+ function getSyndicateCronStatus(subdomain, testnet) {
189
+ if (!isOpenClaw()) {
190
+ return { isOpenClaw: false, crons: [] };
191
+ }
192
+ const prefix = cronName(subdomain, testnet);
193
+ const all = listCronDetails();
194
+ const matching = all.filter((c) => c.name.startsWith(prefix));
195
+ return { isOpenClaw: true, crons: matching };
196
+ }
197
+ export {
198
+ getSyndicateCronStatus,
199
+ isOpenClaw,
200
+ registerSyndicateCrons,
201
+ unregisterSyndicateCrons
202
+ };
203
+ //# sourceMappingURL=cron-RG46PYWA.js.map
@@ -0,0 +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":[]}
@@ -6,9 +6,11 @@ import {
6
6
  queryApprovals,
7
7
  queryJoinRequests,
8
8
  revokeAttestation
9
- } from "./chunk-HKZONPXW.js";
10
- import "./chunk-OUES74ID.js";
11
- import "./chunk-5ADWTXNT.js";
9
+ } from "./chunk-56W62BYY.js";
10
+ import "./chunk-IXMM3TT3.js";
11
+ import "./chunk-FWJBUK57.js";
12
+ import "./chunk-7CN3TSAA.js";
13
+ import "./chunk-IIDZ2TK5.js";
12
14
  export {
13
15
  createApproval,
14
16
  createJoinRequest,
@@ -18,4 +20,4 @@ export {
18
20
  queryJoinRequests,
19
21
  revokeAttestation
20
22
  };
21
- //# sourceMappingURL=eas-EL3XCEXQ.js.map
23
+ //# sourceMappingURL=eas-HQ5OTAFW.js.map
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  queryApprovals,
11
11
  queryJoinRequests,
12
12
  revokeAttestation
13
- } from "./chunk-HKZONPXW.js";
13
+ } from "./chunk-56W62BYY.js";
14
14
  import {
15
15
  approveDepositor,
16
16
  deposit,
@@ -28,7 +28,7 @@ import {
28
28
  setTextRecord,
29
29
  setVaultAddress,
30
30
  simulateBatch
31
- } from "./chunk-Q37V65B6.js";
31
+ } from "./chunk-6IUSJWAL.js";
32
32
  import {
33
33
  AGENT_REGISTRY,
34
34
  EAS_SCHEMAS,
@@ -44,28 +44,35 @@ import {
44
44
  UNISWAP_QUOTER_V2_ABI,
45
45
  VENICE,
46
46
  VENICE_STAKING_ABI
47
- } from "./chunk-OUES74ID.js";
47
+ } from "./chunk-IXMM3TT3.js";
48
48
  import {
49
- VALID_NETWORKS,
50
- cacheGroupId,
51
49
  getAccount,
52
- getAgentId,
50
+ getPublicClient,
51
+ getWalletClient
52
+ } from "./chunk-FWJBUK57.js";
53
+ import {
54
+ VALID_NETWORKS,
53
55
  getChain,
54
- getChainContracts,
55
56
  getExplorerUrl,
56
57
  getNetwork,
57
- getPublicClient,
58
58
  getRpcUrl,
59
+ isTestnet,
60
+ setNetwork
61
+ } from "./chunk-7CN3TSAA.js";
62
+ import {
63
+ cacheGroupId,
64
+ getAgentId,
65
+ getChainContracts,
66
+ getNotifyTo,
59
67
  getVeniceApiKey,
60
- getWalletClient,
61
68
  loadConfig,
62
69
  setAgentId,
63
70
  setChainContract,
64
71
  setConfigRpcUrl,
65
- setNetwork,
72
+ setNotifyTo,
66
73
  setPrivateKey,
67
74
  setVeniceApiKey
68
- } from "./chunk-5ADWTXNT.js";
75
+ } from "./chunk-IIDZ2TK5.js";
69
76
 
70
77
  // src/index.ts
71
78
  import { config as loadDotenv } from "dotenv";
@@ -1687,6 +1694,7 @@ import { readFileSync } from "fs";
1687
1694
 
1688
1695
  // src/lib/governor.ts
1689
1696
  var PROPOSAL_STATES = [
1697
+ "Draft",
1690
1698
  "Pending",
1691
1699
  "Approved",
1692
1700
  "Rejected",
@@ -1695,6 +1703,21 @@ var PROPOSAL_STATES = [
1695
1703
  "Settled",
1696
1704
  "Cancelled"
1697
1705
  ];
1706
+ var PROPOSAL_STATE = {
1707
+ Draft: 0,
1708
+ Pending: 1,
1709
+ Approved: 2,
1710
+ Rejected: 3,
1711
+ Expired: 4,
1712
+ Executed: 5,
1713
+ Settled: 6,
1714
+ Cancelled: 7
1715
+ };
1716
+ var VOTE_TYPE = {
1717
+ For: 0,
1718
+ Against: 1,
1719
+ Abstain: 2
1720
+ };
1698
1721
  function parseDuration(input2) {
1699
1722
  const match = input2.match(/^(\d+)(d|h|m|s)?$/);
1700
1723
  if (!match) throw new Error(`Invalid duration: ${input2}`);
@@ -1797,7 +1820,7 @@ async function getCapitalSnapshot(proposalId) {
1797
1820
  args: [proposalId]
1798
1821
  });
1799
1822
  }
1800
- async function propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex) {
1823
+ async function propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex, coProposers = []) {
1801
1824
  const wallet = getWalletClient();
1802
1825
  const client = getPublicClient();
1803
1826
  const hash = await wallet.writeContract({
@@ -1806,7 +1829,7 @@ async function propose(vault, metadataURI, performanceFeeBps, strategyDuration,
1806
1829
  address: getGovernorAddress(),
1807
1830
  abi: SYNDICATE_GOVERNOR_ABI,
1808
1831
  functionName: "propose",
1809
- args: [vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex]
1832
+ args: [vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex, coProposers]
1810
1833
  });
1811
1834
  const receipt = await client.waitForTransactionReceipt({ hash });
1812
1835
  let proposalId;
@@ -2119,7 +2142,7 @@ function registerProposalCommands(program2) {
2119
2142
  process.exit(1);
2120
2143
  }
2121
2144
  });
2122
- proposal.command("list").description("List proposals").option("--vault <address>", "Filter by vault").option("--state <filter>", "Filter by state: pending, approved, executed, settled, all", "all").action(async (opts) => {
2145
+ proposal.command("list").description("List proposals").option("--vault <address>", "Filter by vault").option("--state <filter>", "Filter by state: draft, pending, approved, executed, settled, all", "all").action(async (opts) => {
2123
2146
  const spinner = ora5("Loading proposals...").start();
2124
2147
  try {
2125
2148
  const count = await proposalCount();
@@ -2214,8 +2237,9 @@ function registerProposalCommands(program2) {
2214
2237
  console.log(LABEL(" Votes"));
2215
2238
  console.log(W(` For: ${formatShares(p.votesFor)}`));
2216
2239
  console.log(W(` Against: ${formatShares(p.votesAgainst)}`));
2240
+ console.log(W(` Abstain: ${formatShares(p.votesAbstain)}`));
2217
2241
  console.log(W(` Quorum: ${quorumNeeded}`));
2218
- if (state === 4 || state === 5) {
2242
+ if (state === PROPOSAL_STATE.Executed || state === PROPOSAL_STATE.Settled) {
2219
2243
  try {
2220
2244
  const cap = await getCapitalSnapshot(id);
2221
2245
  console.log();
@@ -2239,15 +2263,20 @@ function registerProposalCommands(program2) {
2239
2263
  process.exit(1);
2240
2264
  }
2241
2265
  });
2242
- proposal.command("vote").description("Cast a vote on a pending proposal").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--support <yes|no>", "Vote direction: yes or no").action(async (opts) => {
2266
+ proposal.command("vote").description("Cast a vote on a pending proposal").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--support <for|against|abstain>", "Vote direction: for, against, or abstain").action(async (opts) => {
2243
2267
  try {
2244
2268
  const proposalId = parseBigIntArg(opts.id, "proposal ID");
2245
- const support = opts.support.toLowerCase() === "yes";
2269
+ const supportRaw = String(opts.support).toLowerCase();
2270
+ const support = supportRaw === "yes" || supportRaw === "for" ? VOTE_TYPE.For : supportRaw === "no" || supportRaw === "against" ? VOTE_TYPE.Against : supportRaw === "abstain" ? VOTE_TYPE.Abstain : null;
2271
+ if (support === null) {
2272
+ console.error(chalk5.red(`Invalid support value "${opts.support}". Use for|against|abstain.`));
2273
+ process.exit(1);
2274
+ }
2246
2275
  const account = getAccount();
2247
2276
  const spinner = ora5("Loading proposal...").start();
2248
2277
  const p = await getProposal(proposalId);
2249
2278
  const state = await getProposalState(proposalId);
2250
- if (state !== 0) {
2279
+ if (state !== PROPOSAL_STATE.Pending) {
2251
2280
  spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Pending`);
2252
2281
  process.exit(1);
2253
2282
  }
@@ -2263,7 +2292,9 @@ function registerProposalCommands(program2) {
2263
2292
  SEP();
2264
2293
  console.log(W(` Proposal: #${proposalId}`));
2265
2294
  console.log(W(` Vault: ${G(p.vault)}`));
2266
- console.log(W(` Support: ${support ? G("YES") : chalk5.red("NO")}`));
2295
+ console.log(W(
2296
+ ` Support: ${support === VOTE_TYPE.For ? G("FOR") : support === VOTE_TYPE.Against ? chalk5.red("AGAINST") : DIM("ABSTAIN")}`
2297
+ ));
2267
2298
  console.log(W(` Weight: ${formatShares(weight)} shares`));
2268
2299
  SEP();
2269
2300
  const voteSpinner = ora5({ text: W("Submitting vote..."), color: "green" }).start();
@@ -2282,7 +2313,7 @@ function registerProposalCommands(program2) {
2282
2313
  const proposalId = parseBigIntArg(opts.id, "proposal ID");
2283
2314
  const spinner = ora5("Loading proposal...").start();
2284
2315
  const state = await getProposalState(proposalId);
2285
- if (state !== 1) {
2316
+ if (state !== PROPOSAL_STATE.Approved) {
2286
2317
  spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Approved`);
2287
2318
  process.exit(1);
2288
2319
  }
@@ -2309,7 +2340,7 @@ function registerProposalCommands(program2) {
2309
2340
  const spinner = ora5("Loading proposal...").start();
2310
2341
  const p = await getProposal(proposalId);
2311
2342
  const state = await getProposalState(proposalId);
2312
- if (state !== 4) {
2343
+ if (state !== PROPOSAL_STATE.Executed) {
2313
2344
  spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Executed`);
2314
2345
  process.exit(1);
2315
2346
  }
@@ -2348,7 +2379,7 @@ function registerProposalCommands(program2) {
2348
2379
  const proposalId = parseBigIntArg(opts.id, "proposal ID");
2349
2380
  const spinner = ora5("Loading proposal...").start();
2350
2381
  const state = await getProposalState(proposalId);
2351
- if (state === 5 || state === 6) {
2382
+ if (state === PROPOSAL_STATE.Settled || state === PROPOSAL_STATE.Cancelled) {
2352
2383
  spinner.fail(`Proposal is already ${PROPOSAL_STATES[state]}`);
2353
2384
  process.exit(1);
2354
2385
  }
@@ -2358,7 +2389,7 @@ function registerProposalCommands(program2) {
2358
2389
  hash = await emergencyCancel(proposalId);
2359
2390
  spinner.succeed(G("Emergency cancelled"));
2360
2391
  } else {
2361
- if (state !== 0 && state !== 1) {
2392
+ if (state !== PROPOSAL_STATE.Draft && state !== PROPOSAL_STATE.Pending) {
2362
2393
  spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"} \u2014 use --emergency for non-pending/approved`);
2363
2394
  process.exit(1);
2364
2395
  }
@@ -2502,7 +2533,10 @@ try {
2502
2533
  } catch {
2503
2534
  }
2504
2535
  async function loadXmtp() {
2505
- return import("./xmtp-S4VRXMFK.js");
2536
+ return import("./xmtp-A5G2GEWF.js");
2537
+ }
2538
+ async function loadCron() {
2539
+ return import("./cron-RG46PYWA.js");
2506
2540
  }
2507
2541
  var G3 = chalk7.green;
2508
2542
  var W3 = chalk7.white;
@@ -2677,6 +2711,16 @@ syndicate.command("create").description("Create a new syndicate via the factory
2677
2711
  console.warn(chalk7.yellow("\n \u26A0 Could not create XMTP chat group"));
2678
2712
  console.warn(chalk7.dim(` Recover later with: sherwood chat ${subdomain} init`));
2679
2713
  }
2714
+ try {
2715
+ const cron = await loadCron();
2716
+ const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
2717
+ if (cronResult.isOpenClaw && cronResult.registered) {
2718
+ console.log(G3(" \u2713 Participation crons registered (15m check + hourly summary)"));
2719
+ } else if (!cronResult.isOpenClaw) {
2720
+ console.log(DIM3(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
2721
+ }
2722
+ } catch {
2723
+ }
2680
2724
  spinner.stop();
2681
2725
  console.log();
2682
2726
  console.log(LABEL3(" \u25C6 Syndicate Created"));
@@ -2910,6 +2954,16 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2910
2954
  } catch {
2911
2955
  console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
2912
2956
  }
2957
+ try {
2958
+ const cron = await loadCron();
2959
+ const cronResult = cron.registerSyndicateCrons(opts.subdomain, isTestnet(), getNotifyTo());
2960
+ if (cronResult.isOpenClaw && cronResult.registered) {
2961
+ console.log(chalk7.green(" \u2713 Participation crons registered"));
2962
+ } else if (!cronResult.isOpenClaw) {
2963
+ console.log(chalk7.dim(" Tip: Set up a scheduled process to run `sherwood session check " + opts.subdomain + "` periodically"));
2964
+ }
2965
+ } catch {
2966
+ }
2913
2967
  return;
2914
2968
  }
2915
2969
  spinner.text = "Checking pending requests...";
@@ -2947,6 +3001,16 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2947
3001
  spinner.succeed("Join request created");
2948
3002
  console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
2949
3003
  }
3004
+ try {
3005
+ const cron = await loadCron();
3006
+ const cronResult = cron.registerSyndicateCrons(opts.subdomain, isTestnet(), getNotifyTo());
3007
+ if (cronResult.isOpenClaw && cronResult.registered) {
3008
+ console.log(G3(" \u2713 Participation crons registered (will activate after approval)"));
3009
+ } else if (!cronResult.isOpenClaw) {
3010
+ console.log(DIM3(" Tip: Set up a scheduled process to run `sherwood session check " + opts.subdomain + "` periodically"));
3011
+ }
3012
+ } catch {
3013
+ }
2950
3014
  console.log();
2951
3015
  console.log(LABEL3(" \u25C6 Join Request Submitted"));
2952
3016
  SEP3();
@@ -3266,7 +3330,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
3266
3330
  await runLeveredSwap(opts);
3267
3331
  });
3268
3332
  program.command("providers").description("List available DeFi providers").action(async () => {
3269
- const { MessariProvider, NansenProvider } = await import("./research-ZR7HXITG.js");
3333
+ const { MessariProvider, NansenProvider } = await import("./research-HEZP7VPY.js");
3270
3334
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
3271
3335
  for (const p of providers) {
3272
3336
  const info = p.info();
@@ -3277,7 +3341,7 @@ ${info.name} (${info.type})`);
3277
3341
  }
3278
3342
  });
3279
3343
  try {
3280
- const { registerChatCommands } = await import("./chat-BNYWD3EL.js");
3344
+ const { registerChatCommands } = await import("./chat-2X5FQI5X.js");
3281
3345
  registerChatCommands(program);
3282
3346
  } catch {
3283
3347
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -3287,17 +3351,17 @@ try {
3287
3351
  process.exit(1);
3288
3352
  });
3289
3353
  }
3290
- var { registerSessionCommands } = await import("./session-QQSHCGNK.js");
3354
+ var { registerSessionCommands } = await import("./session-QBWUWXCH.js");
3291
3355
  registerSessionCommands(program);
3292
3356
  registerVeniceCommands(program);
3293
3357
  registerAllowanceCommands(program);
3294
3358
  registerIdentityCommands(program);
3295
3359
  registerProposalCommands(program);
3296
3360
  registerGovernorCommands(program);
3297
- var { registerResearchCommands } = await import("./research-57SKO27M.js");
3361
+ var { registerResearchCommands } = await import("./research-PLYYYJ4F.js");
3298
3362
  registerResearchCommands(program);
3299
3363
  var configCmd = program.command("config");
3300
- 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").action((opts) => {
3364
+ 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) => {
3301
3365
  let saved = false;
3302
3366
  if (opts.privateKey) {
3303
3367
  setPrivateKey(opts.privateKey);
@@ -3320,8 +3384,14 @@ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (
3320
3384
  console.log(chalk7.dim(` RPC: ${opts.rpc}`));
3321
3385
  saved = true;
3322
3386
  }
3387
+ if (opts.notifyTo) {
3388
+ setNotifyTo(opts.notifyTo);
3389
+ console.log(chalk7.green("Notify destination saved to ~/.sherwood/config.json"));
3390
+ console.log(chalk7.dim(` Notify to: ${opts.notifyTo}`));
3391
+ saved = true;
3392
+ }
3323
3393
  if (!saved) {
3324
- console.log(chalk7.red("Provide at least one of: --private-key, --vault, --rpc"));
3394
+ console.log(chalk7.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to"));
3325
3395
  process.exit(1);
3326
3396
  }
3327
3397
  });