@sherwoodagent/cli 0.2.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.
- package/dist/chunk-CR3ZNOB7.js +1451 -0
- package/dist/chunk-CR3ZNOB7.js.map +1 -0
- package/dist/commands/chat.js +310 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/index.js +2726 -0
- package/dist/index.js.map +1 -0
- package/dist/xmtp-2UPH45XE.js +142 -0
- package/dist/xmtp-2UPH45XE.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cacheGroupId,
|
|
3
|
+
getAccount,
|
|
4
|
+
getCachedGroupId,
|
|
5
|
+
getTextRecord,
|
|
6
|
+
loadConfig,
|
|
7
|
+
saveConfig
|
|
8
|
+
} from "./chunk-CR3ZNOB7.js";
|
|
9
|
+
|
|
10
|
+
// src/lib/xmtp.ts
|
|
11
|
+
import {
|
|
12
|
+
Client,
|
|
13
|
+
IdentifierKind
|
|
14
|
+
} from "@xmtp/node-sdk";
|
|
15
|
+
import { ReactionAction, ReactionSchema } from "@xmtp/node-bindings";
|
|
16
|
+
function createSigner() {
|
|
17
|
+
return {
|
|
18
|
+
type: "EOA",
|
|
19
|
+
getIdentifier: () => ({
|
|
20
|
+
identifier: getAccount().address,
|
|
21
|
+
identifierKind: IdentifierKind.Ethereum
|
|
22
|
+
}),
|
|
23
|
+
signMessage: async (message) => {
|
|
24
|
+
const account = getAccount();
|
|
25
|
+
const sig = await account.signMessage({ message });
|
|
26
|
+
return Buffer.from(sig.slice(2), "hex");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
var _client = null;
|
|
31
|
+
async function getXmtpClient() {
|
|
32
|
+
if (_client) return _client;
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
const signer = createSigner();
|
|
35
|
+
const keyBytes = new Uint8Array(
|
|
36
|
+
Buffer.from(config.dbEncryptionKey.replace(/^0x/, ""), "hex")
|
|
37
|
+
);
|
|
38
|
+
_client = await Client.create(signer, {
|
|
39
|
+
dbEncryptionKey: keyBytes
|
|
40
|
+
});
|
|
41
|
+
if (!config.xmtpInboxId) {
|
|
42
|
+
config.xmtpInboxId = _client.inboxId;
|
|
43
|
+
saveConfig(config);
|
|
44
|
+
}
|
|
45
|
+
return _client;
|
|
46
|
+
}
|
|
47
|
+
async function createSyndicateGroup(client, subdomain, publicChat = false) {
|
|
48
|
+
const group = await client.conversations.createGroupWithIdentifiers(
|
|
49
|
+
[],
|
|
50
|
+
// no other members yet; creator is auto-added as super admin
|
|
51
|
+
{
|
|
52
|
+
groupName: subdomain,
|
|
53
|
+
groupDescription: `Sherwood syndicate: ${subdomain}.sherwoodagent.eth`
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
if (publicChat && process.env.DASHBOARD_SPECTATOR_ADDRESS) {
|
|
57
|
+
const spectatorIdentifier = {
|
|
58
|
+
identifier: process.env.DASHBOARD_SPECTATOR_ADDRESS,
|
|
59
|
+
identifierKind: IdentifierKind.Ethereum
|
|
60
|
+
};
|
|
61
|
+
await group.addMembersByIdentifiers([spectatorIdentifier]);
|
|
62
|
+
}
|
|
63
|
+
cacheGroupId(subdomain, group.id);
|
|
64
|
+
return group.id;
|
|
65
|
+
}
|
|
66
|
+
async function getGroup(client, subdomain) {
|
|
67
|
+
let groupId = getCachedGroupId(subdomain);
|
|
68
|
+
if (!groupId) {
|
|
69
|
+
groupId = await getTextRecord(subdomain, "xmtpGroupId");
|
|
70
|
+
if (groupId) {
|
|
71
|
+
cacheGroupId(subdomain, groupId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!groupId) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`No XMTP group found for syndicate "${subdomain}". Run "sherwood chat ${subdomain} init" to create one.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
await client.conversations.sync();
|
|
80
|
+
const conversation = await client.conversations.getConversationById(groupId);
|
|
81
|
+
if (!conversation) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`XMTP group "${groupId}" not found. You may not be a member of this group.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return conversation;
|
|
87
|
+
}
|
|
88
|
+
async function addMember(group, address) {
|
|
89
|
+
const identifier = {
|
|
90
|
+
identifier: address,
|
|
91
|
+
identifierKind: IdentifierKind.Ethereum
|
|
92
|
+
};
|
|
93
|
+
await group.addMembersByIdentifiers([identifier]);
|
|
94
|
+
}
|
|
95
|
+
async function removeMember(group, address) {
|
|
96
|
+
const identifier = {
|
|
97
|
+
identifier: address,
|
|
98
|
+
identifierKind: IdentifierKind.Ethereum
|
|
99
|
+
};
|
|
100
|
+
await group.removeMembersByIdentifiers([identifier]);
|
|
101
|
+
}
|
|
102
|
+
async function sendEnvelope(group, envelope) {
|
|
103
|
+
await group.sendText(JSON.stringify(envelope));
|
|
104
|
+
}
|
|
105
|
+
async function sendMarkdown(group, markdown) {
|
|
106
|
+
await group.sendMarkdown(markdown);
|
|
107
|
+
}
|
|
108
|
+
async function sendReaction(group, messageId, emoji) {
|
|
109
|
+
await group.sendReaction({
|
|
110
|
+
reference: messageId,
|
|
111
|
+
referenceInboxId: "",
|
|
112
|
+
action: ReactionAction.Added,
|
|
113
|
+
schema: ReactionSchema.Unicode,
|
|
114
|
+
content: emoji
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async function streamMessages(group, onMessage) {
|
|
118
|
+
const stream = await group.stream({
|
|
119
|
+
onValue: onMessage
|
|
120
|
+
});
|
|
121
|
+
return async () => {
|
|
122
|
+
await stream.return();
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function getRecentMessages(group, limit = 20) {
|
|
126
|
+
await group.sync();
|
|
127
|
+
const messages = await group.messages({ limit });
|
|
128
|
+
return messages;
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
addMember,
|
|
132
|
+
createSyndicateGroup,
|
|
133
|
+
getGroup,
|
|
134
|
+
getRecentMessages,
|
|
135
|
+
getXmtpClient,
|
|
136
|
+
removeMember,
|
|
137
|
+
sendEnvelope,
|
|
138
|
+
sendMarkdown,
|
|
139
|
+
sendReaction,
|
|
140
|
+
streamMessages
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=xmtp-2UPH45XE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/xmtp.ts"],"sourcesContent":["/**\n * XMTP client and group operations for syndicate chat.\n *\n * Uses the wallet's PRIVATE_KEY as the XMTP identity signer.\n * Groups are creator-managed (super admin only) with E2E encryption via MLS.\n */\n\nimport {\n Client,\n type Signer,\n type Identifier,\n IdentifierKind,\n type Group,\n type DecodedMessage,\n} from \"@xmtp/node-sdk\";\nimport { ReactionAction, ReactionSchema } from \"@xmtp/node-bindings\";\nimport { getAccount } from \"./client.js\";\nimport {\n loadConfig,\n saveConfig,\n cacheGroupId,\n getCachedGroupId,\n} from \"./config.js\";\nimport { getTextRecord } from \"./ens.js\";\nimport type { ChatEnvelope } from \"./types.js\";\n\n// ── Signer ──\n\nfunction createSigner(): Signer {\n return {\n type: \"EOA\" as const,\n getIdentifier: () => ({\n identifier: getAccount().address,\n identifierKind: IdentifierKind.Ethereum,\n }),\n signMessage: async (message: string) => {\n const account = getAccount();\n const sig = await account.signMessage({ message });\n return Buffer.from(sig.slice(2), \"hex\");\n },\n };\n}\n\n// ── Client ──\n\nlet _client: Client | null = null;\n\nexport async function getXmtpClient(): Promise<Client> {\n if (_client) return _client;\n\n const config = loadConfig();\n const signer = createSigner();\n\n const keyBytes = new Uint8Array(\n Buffer.from(config.dbEncryptionKey.replace(/^0x/, \"\"), \"hex\"),\n );\n\n _client = await Client.create(signer, {\n dbEncryptionKey: keyBytes,\n });\n\n // Cache inbox ID\n if (!config.xmtpInboxId) {\n config.xmtpInboxId = _client.inboxId;\n saveConfig(config);\n }\n\n return _client;\n}\n\n// ── Group Creation ──\n\nexport async function createSyndicateGroup(\n client: Client,\n subdomain: string,\n publicChat: boolean = false,\n): Promise<string> {\n // Create group with creator-only permissions\n const group = await client.conversations.createGroupWithIdentifiers(\n [], // no other members yet; creator is auto-added as super admin\n {\n groupName: subdomain,\n groupDescription: `Sherwood syndicate: ${subdomain}.sherwoodagent.eth`,\n },\n );\n\n // If public chat, add spectator bot for dashboard integration\n if (publicChat && process.env.DASHBOARD_SPECTATOR_ADDRESS) {\n const spectatorIdentifier: Identifier = {\n identifier: process.env.DASHBOARD_SPECTATOR_ADDRESS,\n identifierKind: IdentifierKind.Ethereum,\n };\n await (group as Group).addMembersByIdentifiers([spectatorIdentifier]);\n }\n\n // Cache locally\n cacheGroupId(subdomain, group.id);\n\n return group.id;\n}\n\n// ── Group Lookup ──\n\nexport async function getGroup(\n client: Client,\n subdomain: string,\n): Promise<Group> {\n // Try local cache first\n let groupId = getCachedGroupId(subdomain);\n\n // Fall back to on-chain ENS text record\n if (!groupId) {\n groupId = await getTextRecord(subdomain, \"xmtpGroupId\");\n if (groupId) {\n cacheGroupId(subdomain, groupId);\n }\n }\n\n if (!groupId) {\n throw new Error(\n `No XMTP group found for syndicate \"${subdomain}\". Run \"sherwood chat ${subdomain} init\" to create one.`,\n );\n }\n\n // Sync conversations to make sure we have latest\n await client.conversations.sync();\n\n const conversation = await client.conversations.getConversationById(groupId);\n if (!conversation) {\n throw new Error(\n `XMTP group \"${groupId}\" not found. You may not be a member of this group.`,\n );\n }\n\n return conversation as Group;\n}\n\n// ── Member Management ──\n\nexport async function addMember(\n group: Group,\n address: string,\n): Promise<void> {\n const identifier: Identifier = {\n identifier: address,\n identifierKind: IdentifierKind.Ethereum,\n };\n await group.addMembersByIdentifiers([identifier]);\n}\n\nexport async function removeMember(\n group: Group,\n address: string,\n): Promise<void> {\n const identifier: Identifier = {\n identifier: address,\n identifierKind: IdentifierKind.Ethereum,\n };\n await group.removeMembersByIdentifiers([identifier]);\n}\n\n// ── Messaging ──\n\nexport async function sendEnvelope(\n group: Group,\n envelope: ChatEnvelope,\n): Promise<void> {\n await group.sendText(JSON.stringify(envelope));\n}\n\nexport async function sendMarkdown(\n group: Group,\n markdown: string,\n): Promise<void> {\n await group.sendMarkdown(markdown);\n}\n\nexport async function sendReaction(\n group: Group,\n messageId: string,\n emoji: string,\n): Promise<void> {\n await group.sendReaction({\n reference: messageId,\n referenceInboxId: \"\",\n action: ReactionAction.Added,\n schema: ReactionSchema.Unicode,\n content: emoji,\n });\n}\n\n// ── Streaming ──\n\nexport async function streamMessages(\n group: Group,\n onMessage: (msg: DecodedMessage) => void,\n): Promise<() => void> {\n const stream = await group.stream({\n onValue: onMessage,\n });\n\n // Return cleanup function\n return async () => {\n await stream.return();\n };\n}\n\n// ── Message History ──\n\nexport async function getRecentMessages(\n group: Group,\n limit: number = 20,\n): Promise<DecodedMessage[]> {\n await group.sync();\n const messages = await group.messages({ limit });\n return messages;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sherwoodagent/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "CLI for agent-managed investment syndicates — onchain DeFi syndicates with XMTP chat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sherwood": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": ["dist"],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/imthatcarlos/sherwood.git",
|
|
16
|
+
"directory": "cli"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsx src/index.ts",
|
|
23
|
+
"lint": "eslint src/",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:unit": "vitest run --config vitest.config.unit.ts",
|
|
27
|
+
"test:integration": "vitest run --config vitest.config.integration.ts",
|
|
28
|
+
"test:watch": "vitest"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@inquirer/prompts": "^8.3.2",
|
|
32
|
+
"@xmtp/node-sdk": "^6.0.0",
|
|
33
|
+
"agent0-sdk": "^1.7.0",
|
|
34
|
+
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^12.1.0",
|
|
36
|
+
"dotenv": "^16.4.0",
|
|
37
|
+
"ora": "^8.1.0",
|
|
38
|
+
"viem": "^2.21.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"tsup": "^8.3.0",
|
|
43
|
+
"tsx": "^4.19.0",
|
|
44
|
+
"typescript": "^5.7.0",
|
|
45
|
+
"vitest": "^4.1.0"
|
|
46
|
+
}
|
|
47
|
+
}
|