@sherwoodagent/cli 0.10.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-4Q7G3DFO.js → chat-2X5FQI5X.js} +9 -6
  2. package/dist/{chat-4Q7G3DFO.js.map → chat-2X5FQI5X.js.map} +1 -1
  3. package/dist/{chunk-DTSTJUZH.js → chunk-56W62BYY.js} +8 -6
  4. package/dist/{chunk-DTSTJUZH.js.map → chunk-56W62BYY.js.map} +1 -1
  5. package/dist/{chunk-3EBFJLQR.js → chunk-6IUSJWAL.js} +9 -5
  6. package/dist/{chunk-3EBFJLQR.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-SGYOOHML.js → chunk-IXMM3TT3.js} +2 -2
  14. package/dist/{chunk-4MTHXGTK.js → chunk-KAZRNDZQ.js} +2 -2
  15. package/dist/config-U74QT4SC.js +37 -0
  16. package/dist/cron-RG46PYWA.js +203 -0
  17. package/dist/cron-RG46PYWA.js.map +1 -0
  18. package/dist/{eas-NZ7GCLPA.js → eas-HQ5OTAFW.js} +6 -4
  19. package/dist/index.js +64 -18
  20. package/dist/index.js.map +1 -1
  21. package/dist/network-ROF3SSAA.js +26 -0
  22. package/dist/network-ROF3SSAA.js.map +1 -0
  23. package/dist/research-HEZP7VPY.js +14 -0
  24. package/dist/research-HEZP7VPY.js.map +1 -0
  25. package/dist/{research-3XEIOMDP.js → research-PLYYYJ4F.js} +7 -5
  26. package/dist/{research-3XEIOMDP.js.map → research-PLYYYJ4F.js.map} +1 -1
  27. package/dist/{session-FVFIACYW.js → session-QBWUWXCH.js} +47 -5
  28. package/dist/session-QBWUWXCH.js.map +1 -0
  29. package/dist/{xmtp-JC5AUYRG.js → xmtp-A5G2GEWF.js} +10 -6
  30. package/dist/{xmtp-JC5AUYRG.js.map → xmtp-A5G2GEWF.js.map} +1 -1
  31. package/package.json +1 -1
  32. package/dist/chunk-5ADWTXNT.js +0 -233
  33. package/dist/chunk-5ADWTXNT.js.map +0 -1
  34. package/dist/research-ZR7HXITG.js +0 -12
  35. package/dist/session-FVFIACYW.js.map +0 -1
  36. /package/dist/{chunk-SGYOOHML.js.map → chunk-IXMM3TT3.js.map} +0 -0
  37. /package/dist/{chunk-4MTHXGTK.js.map → chunk-KAZRNDZQ.js.map} +0 -0
  38. /package/dist/{eas-NZ7GCLPA.js.map → config-U74QT4SC.js.map} +0 -0
  39. /package/dist/{research-ZR7HXITG.js.map → eas-HQ5OTAFW.js.map} +0 -0
@@ -0,0 +1,100 @@
1
+ // src/lib/config.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ var CONFIG_DIR = path.join(process.env.HOME || "~", ".sherwood");
5
+ var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
6
+ function loadConfig() {
7
+ if (fs.existsSync(CONFIG_PATH)) {
8
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
9
+ }
10
+ const config = { groupCache: {} };
11
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
12
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
13
+ return config;
14
+ }
15
+ function saveConfig(config) {
16
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
17
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
18
+ }
19
+ function cacheGroupId(subdomain, groupId) {
20
+ const config = loadConfig();
21
+ config.groupCache[subdomain] = groupId;
22
+ saveConfig(config);
23
+ }
24
+ function getCachedGroupId(subdomain) {
25
+ const config = loadConfig();
26
+ return config.groupCache[subdomain];
27
+ }
28
+ function setVeniceApiKey(apiKey) {
29
+ const config = loadConfig();
30
+ config.veniceApiKey = apiKey;
31
+ saveConfig(config);
32
+ }
33
+ function getVeniceApiKey() {
34
+ return loadConfig().veniceApiKey;
35
+ }
36
+ function setAgentId(agentId) {
37
+ const config = loadConfig();
38
+ config.agentId = agentId;
39
+ saveConfig(config);
40
+ }
41
+ function getAgentId() {
42
+ return loadConfig().agentId;
43
+ }
44
+ function setPrivateKey(key) {
45
+ const config = loadConfig();
46
+ config.privateKey = key.startsWith("0x") ? key : `0x${key}`;
47
+ saveConfig(config);
48
+ }
49
+ function getPrivateKey() {
50
+ return loadConfig().privateKey;
51
+ }
52
+ function getConfigRpcUrl(network) {
53
+ return loadConfig().rpc?.[network];
54
+ }
55
+ function setConfigRpcUrl(network, url) {
56
+ const config = loadConfig();
57
+ if (!config.rpc) config.rpc = {};
58
+ config.rpc[network] = url;
59
+ saveConfig(config);
60
+ }
61
+ function getChainContracts(chainId) {
62
+ const config = loadConfig();
63
+ return config.contracts?.[String(chainId)] ?? {};
64
+ }
65
+ function getNotifyTo() {
66
+ return loadConfig().notifyTo;
67
+ }
68
+ function setNotifyTo(id) {
69
+ const config = loadConfig();
70
+ config.notifyTo = id;
71
+ saveConfig(config);
72
+ }
73
+ function setChainContract(chainId, key, value) {
74
+ const config = loadConfig();
75
+ if (!config.contracts) config.contracts = {};
76
+ const cid = String(chainId);
77
+ if (!config.contracts[cid]) config.contracts[cid] = {};
78
+ config.contracts[cid][key] = value;
79
+ saveConfig(config);
80
+ }
81
+
82
+ export {
83
+ loadConfig,
84
+ saveConfig,
85
+ cacheGroupId,
86
+ getCachedGroupId,
87
+ setVeniceApiKey,
88
+ getVeniceApiKey,
89
+ setAgentId,
90
+ getAgentId,
91
+ setPrivateKey,
92
+ getPrivateKey,
93
+ getConfigRpcUrl,
94
+ setConfigRpcUrl,
95
+ getChainContracts,
96
+ getNotifyTo,
97
+ setNotifyTo,
98
+ setChainContract
99
+ };
100
+ //# sourceMappingURL=chunk-IIDZ2TK5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/config.ts"],"sourcesContent":["/**\n * Local config management — ~/.sherwood/config.json\n *\n * Stores group ID cache, per-chain contract addresses, and wallet config.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst CONFIG_DIR = path.join(process.env.HOME || \"~\", \".sherwood\");\nconst CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/** Per-chain user-specific addresses (stored by chainId). */\nexport interface ChainContracts {\n vault?: string; // user's default vault address\n}\n\nexport interface SherwoodConfig {\n dbEncryptionKey?: string; // legacy — no longer used, XMTP CLI manages its own DB\n privateKey?: string; // wallet private key (0x-prefixed)\n xmtpInboxId?: string;\n groupCache: Record<string, string>; // subdomain → XMTP group ID\n veniceApiKey?: string; // Venice AI inference API key\n agentId?: number; // ERC-8004 identity token ID\n contracts?: Record<string, ChainContracts>; // chainId → user addresses\n rpc?: Record<string, string>; // network name → custom RPC URL\n notifyTo?: string; // destination for cron summaries (Telegram chat ID, phone, etc.)\n}\n\nexport function loadConfig(): SherwoodConfig {\n if (fs.existsSync(CONFIG_PATH)) {\n return JSON.parse(fs.readFileSync(CONFIG_PATH, \"utf-8\"));\n }\n\n const config: SherwoodConfig = { groupCache: {} };\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n return config;\n}\n\nexport function saveConfig(config: SherwoodConfig): void {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\nexport function cacheGroupId(subdomain: string, groupId: string): void {\n const config = loadConfig();\n config.groupCache[subdomain] = groupId;\n saveConfig(config);\n}\n\nexport function getCachedGroupId(subdomain: string): string | undefined {\n const config = loadConfig();\n return config.groupCache[subdomain];\n}\n\nexport function setVeniceApiKey(apiKey: string): void {\n const config = loadConfig();\n config.veniceApiKey = apiKey;\n saveConfig(config);\n}\n\nexport function getVeniceApiKey(): string | undefined {\n return loadConfig().veniceApiKey;\n}\n\nexport function setAgentId(agentId: number): void {\n const config = loadConfig();\n config.agentId = agentId;\n saveConfig(config);\n}\n\nexport function getAgentId(): number | undefined {\n return loadConfig().agentId;\n}\n\nexport function setPrivateKey(key: string): void {\n const config = loadConfig();\n config.privateKey = key.startsWith(\"0x\") ? key : `0x${key}`;\n saveConfig(config);\n}\n\nexport function getPrivateKey(): string | undefined {\n return loadConfig().privateKey;\n}\n\n// ── Per-network RPC URLs ──\n\nexport function getConfigRpcUrl(network: string): string | undefined {\n return loadConfig().rpc?.[network];\n}\n\nexport function setConfigRpcUrl(network: string, url: string): void {\n const config = loadConfig();\n if (!config.rpc) config.rpc = {};\n config.rpc[network] = url;\n saveConfig(config);\n}\n\n// ── Per-chain contract addresses ──\n\nexport function getChainContracts(chainId: number): ChainContracts {\n const config = loadConfig();\n return config.contracts?.[String(chainId)] ?? {};\n}\n\nexport function getNotifyTo(): string | undefined {\n return loadConfig().notifyTo;\n}\n\nexport function setNotifyTo(id: string): void {\n const config = loadConfig();\n config.notifyTo = id;\n saveConfig(config);\n}\n\nexport function setChainContract(\n chainId: number,\n key: keyof ChainContracts,\n value: string,\n): void {\n const config = loadConfig();\n if (!config.contracts) config.contracts = {};\n const cid = String(chainId);\n if (!config.contracts[cid]) config.contracts[cid] = {};\n config.contracts[cid][key] = value;\n saveConfig(config);\n}\n"],"mappings":";AAMA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,IAAM,aAAa,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAK,WAAW;AACjE,IAAM,cAAc,KAAK,KAAK,YAAY,aAAa;AAmBhD,SAAS,aAA6B;AAC3C,MAAI,GAAG,WAAW,WAAW,GAAG;AAC9B,WAAO,KAAK,MAAM,GAAG,aAAa,aAAa,OAAO,CAAC;AAAA,EACzD;AAEA,QAAM,SAAyB,EAAE,YAAY,CAAC,EAAE;AAChD,KAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAC5C,KAAG,cAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC7D,SAAO;AACT;AAEO,SAAS,WAAW,QAA8B;AACvD,KAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAC5C,KAAG,cAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC/D;AAEO,SAAS,aAAa,WAAmB,SAAuB;AACrE,QAAM,SAAS,WAAW;AAC1B,SAAO,WAAW,SAAS,IAAI;AAC/B,aAAW,MAAM;AACnB;AAEO,SAAS,iBAAiB,WAAuC;AACtE,QAAM,SAAS,WAAW;AAC1B,SAAO,OAAO,WAAW,SAAS;AACpC;AAEO,SAAS,gBAAgB,QAAsB;AACpD,QAAM,SAAS,WAAW;AAC1B,SAAO,eAAe;AACtB,aAAW,MAAM;AACnB;AAEO,SAAS,kBAAsC;AACpD,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,WAAW,SAAuB;AAChD,QAAM,SAAS,WAAW;AAC1B,SAAO,UAAU;AACjB,aAAW,MAAM;AACnB;AAEO,SAAS,aAAiC;AAC/C,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,cAAc,KAAmB;AAC/C,QAAM,SAAS,WAAW;AAC1B,SAAO,aAAa,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,GAAG;AACzD,aAAW,MAAM;AACnB;AAEO,SAAS,gBAAoC;AAClD,SAAO,WAAW,EAAE;AACtB;AAIO,SAAS,gBAAgB,SAAqC;AACnE,SAAO,WAAW,EAAE,MAAM,OAAO;AACnC;AAEO,SAAS,gBAAgB,SAAiB,KAAmB;AAClE,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,OAAO,IAAK,QAAO,MAAM,CAAC;AAC/B,SAAO,IAAI,OAAO,IAAI;AACtB,aAAW,MAAM;AACnB;AAIO,SAAS,kBAAkB,SAAiC;AACjE,QAAM,SAAS,WAAW;AAC1B,SAAO,OAAO,YAAY,OAAO,OAAO,CAAC,KAAK,CAAC;AACjD;AAEO,SAAS,cAAkC;AAChD,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,YAAY,IAAkB;AAC5C,QAAM,SAAS,WAAW;AAC1B,SAAO,WAAW;AAClB,aAAW,MAAM;AACnB;AAEO,SAAS,iBACd,SACA,KACA,OACM;AACN,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,OAAO,UAAW,QAAO,YAAY,CAAC;AAC3C,QAAM,MAAM,OAAO,OAAO;AAC1B,MAAI,CAAC,OAAO,UAAU,GAAG,EAAG,QAAO,UAAU,GAAG,IAAI,CAAC;AACrD,SAAO,UAAU,GAAG,EAAE,GAAG,IAAI;AAC7B,aAAW,MAAM;AACnB;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getNetwork
3
- } from "./chunk-5ADWTXNT.js";
3
+ } from "./chunk-7CN3TSAA.js";
4
4
 
5
5
  // src/lib/addresses.ts
6
6
  var ZERO = "0x0000000000000000000000000000000000000000";
@@ -1392,4 +1392,4 @@ export {
1392
1392
  EAS_ABI,
1393
1393
  SYNDICATE_GOVERNOR_ABI
1394
1394
  };
1395
- //# sourceMappingURL=chunk-SGYOOHML.js.map
1395
+ //# sourceMappingURL=chunk-IXMM3TT3.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getAccount
3
- } from "./chunk-5ADWTXNT.js";
3
+ } from "./chunk-FWJBUK57.js";
4
4
 
5
5
  // src/providers/research/messari.ts
6
6
  import { base, baseSepolia } from "viem/chains";
@@ -436,4 +436,4 @@ export {
436
436
  NansenProvider,
437
437
  getResearchProvider
438
438
  };
439
- //# sourceMappingURL=chunk-4MTHXGTK.js.map
439
+ //# sourceMappingURL=chunk-KAZRNDZQ.js.map
@@ -0,0 +1,37 @@
1
+ import {
2
+ cacheGroupId,
3
+ getAgentId,
4
+ getCachedGroupId,
5
+ getChainContracts,
6
+ getConfigRpcUrl,
7
+ getNotifyTo,
8
+ getPrivateKey,
9
+ getVeniceApiKey,
10
+ loadConfig,
11
+ saveConfig,
12
+ setAgentId,
13
+ setChainContract,
14
+ setConfigRpcUrl,
15
+ setNotifyTo,
16
+ setPrivateKey,
17
+ setVeniceApiKey
18
+ } from "./chunk-IIDZ2TK5.js";
19
+ export {
20
+ cacheGroupId,
21
+ getAgentId,
22
+ getCachedGroupId,
23
+ getChainContracts,
24
+ getConfigRpcUrl,
25
+ getNotifyTo,
26
+ getPrivateKey,
27
+ getVeniceApiKey,
28
+ loadConfig,
29
+ saveConfig,
30
+ setAgentId,
31
+ setChainContract,
32
+ setConfigRpcUrl,
33
+ setNotifyTo,
34
+ setPrivateKey,
35
+ setVeniceApiKey
36
+ };
37
+ //# sourceMappingURL=config-U74QT4SC.js.map
@@ -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-DTSTJUZH.js";
10
- import "./chunk-SGYOOHML.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-NZ7GCLPA.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-DTSTJUZH.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-3EBFJLQR.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-SGYOOHML.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";
@@ -2526,7 +2533,10 @@ try {
2526
2533
  } catch {
2527
2534
  }
2528
2535
  async function loadXmtp() {
2529
- return import("./xmtp-JC5AUYRG.js");
2536
+ return import("./xmtp-A5G2GEWF.js");
2537
+ }
2538
+ async function loadCron() {
2539
+ return import("./cron-RG46PYWA.js");
2530
2540
  }
2531
2541
  var G3 = chalk7.green;
2532
2542
  var W3 = chalk7.white;
@@ -2701,6 +2711,16 @@ syndicate.command("create").description("Create a new syndicate via the factory
2701
2711
  console.warn(chalk7.yellow("\n \u26A0 Could not create XMTP chat group"));
2702
2712
  console.warn(chalk7.dim(` Recover later with: sherwood chat ${subdomain} init`));
2703
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
+ }
2704
2724
  spinner.stop();
2705
2725
  console.log();
2706
2726
  console.log(LABEL3(" \u25C6 Syndicate Created"));
@@ -2934,6 +2954,16 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2934
2954
  } catch {
2935
2955
  console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
2936
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
+ }
2937
2967
  return;
2938
2968
  }
2939
2969
  spinner.text = "Checking pending requests...";
@@ -2971,6 +3001,16 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2971
3001
  spinner.succeed("Join request created");
2972
3002
  console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
2973
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
+ }
2974
3014
  console.log();
2975
3015
  console.log(LABEL3(" \u25C6 Join Request Submitted"));
2976
3016
  SEP3();
@@ -3290,7 +3330,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
3290
3330
  await runLeveredSwap(opts);
3291
3331
  });
3292
3332
  program.command("providers").description("List available DeFi providers").action(async () => {
3293
- const { MessariProvider, NansenProvider } = await import("./research-ZR7HXITG.js");
3333
+ const { MessariProvider, NansenProvider } = await import("./research-HEZP7VPY.js");
3294
3334
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
3295
3335
  for (const p of providers) {
3296
3336
  const info = p.info();
@@ -3301,7 +3341,7 @@ ${info.name} (${info.type})`);
3301
3341
  }
3302
3342
  });
3303
3343
  try {
3304
- const { registerChatCommands } = await import("./chat-4Q7G3DFO.js");
3344
+ const { registerChatCommands } = await import("./chat-2X5FQI5X.js");
3305
3345
  registerChatCommands(program);
3306
3346
  } catch {
3307
3347
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
@@ -3311,17 +3351,17 @@ try {
3311
3351
  process.exit(1);
3312
3352
  });
3313
3353
  }
3314
- var { registerSessionCommands } = await import("./session-FVFIACYW.js");
3354
+ var { registerSessionCommands } = await import("./session-QBWUWXCH.js");
3315
3355
  registerSessionCommands(program);
3316
3356
  registerVeniceCommands(program);
3317
3357
  registerAllowanceCommands(program);
3318
3358
  registerIdentityCommands(program);
3319
3359
  registerProposalCommands(program);
3320
3360
  registerGovernorCommands(program);
3321
- var { registerResearchCommands } = await import("./research-3XEIOMDP.js");
3361
+ var { registerResearchCommands } = await import("./research-PLYYYJ4F.js");
3322
3362
  registerResearchCommands(program);
3323
3363
  var configCmd = program.command("config");
3324
- 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) => {
3325
3365
  let saved = false;
3326
3366
  if (opts.privateKey) {
3327
3367
  setPrivateKey(opts.privateKey);
@@ -3344,8 +3384,14 @@ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (
3344
3384
  console.log(chalk7.dim(` RPC: ${opts.rpc}`));
3345
3385
  saved = true;
3346
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
+ }
3347
3393
  if (!saved) {
3348
- 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"));
3349
3395
  process.exit(1);
3350
3396
  }
3351
3397
  });