create-message-kit 1.1.8 → 1.1.9-beta.2

Sign up to get free protection for your applications and to get access to all the features.
package/index.js CHANGED
@@ -79,19 +79,25 @@ async function addPackagejson(destDir, name, pkgManager) {
79
79
  dependencies: {
80
80
  "@xmtp/message-kit": "latest",
81
81
  },
82
+ devDependencies: {
83
+ "@types/node": "latest",
84
+ typescript: "latest",
85
+ },
82
86
  engines: {
83
87
  node: ">=20",
84
88
  },
85
89
  };
86
90
 
87
- if (pkgManager.startsWith("yarn")) {
88
- packageTemplate.packageManager = `${pkgManager}`;
89
- // Add .yarnrc.yml to disable PnP mode
90
- fs.writeFileSync(
91
- resolve(destDir, ".yarnrc.yml"),
92
- "nodeLinker: node-modules\n",
93
- );
91
+ if (pkgManager.includes("yarn")) {
92
+ packageTemplate.packageManager = `yarn@4.5.1`;
94
93
  }
94
+
95
+ // Add .yarnrc.yml just in caseto disable PnP mode
96
+ fs.writeFileSync(
97
+ resolve(destDir, ".yarnrc.yml"),
98
+ "nodeLinker: node-modules\n",
99
+ );
100
+
95
101
  fs.writeJsonSync(resolve(destDir, "package.json"), packageTemplate, {
96
102
  spaces: 2,
97
103
  });
@@ -99,7 +105,7 @@ async function addPackagejson(destDir, name, pkgManager) {
99
105
 
100
106
  async function gatherProjectInfo() {
101
107
  const templateOptions = [
102
- { value: "gm", label: "GM" },
108
+ { value: "gpt", label: "Simple Gpt" },
103
109
  { value: "agent", label: "Agent" },
104
110
  { value: "group", label: "Group" },
105
111
  ];
@@ -201,11 +207,27 @@ yarn-error.log*
201
207
 
202
208
  fs.writeFileSync(resolve(destDir, ".gitignore"), gitignoreContent.trim());
203
209
  }
204
-
205
210
  async function detectPackageManager() {
206
211
  try {
207
- const pkgManager = await detect();
212
+ // Check if running through bun create
213
+ if (process.env.BUN_CREATE === "true" || process.env._?.includes("bun")) {
214
+ return "bun";
215
+ }
216
+
208
217
  const userAgent = process.env.npm_config_user_agent;
218
+
219
+ // Check if running through npm init
220
+ if (userAgent?.startsWith("npm")) {
221
+ return "npm";
222
+ }
223
+
224
+ // Check for Bun in process.argv
225
+ if (process.argv.some((arg) => arg.includes("bun"))) {
226
+ return "bun";
227
+ }
228
+
229
+ // Fallback to detect for other cases
230
+ const pkgManager = await detect();
209
231
  let version = "";
210
232
 
211
233
  if (userAgent && pkgManager === "yarn") {
@@ -217,11 +239,9 @@ async function detectPackageManager() {
217
239
 
218
240
  return pkgManager + version;
219
241
  } catch (error) {
220
- // Fallback to npm if detection fails
221
242
  return "npm";
222
243
  }
223
244
  }
224
-
225
245
  function kebabcase(str) {
226
246
  return str
227
247
  .replace(/([a-z])([A-Z])/g, "$1-$2")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-message-kit",
3
- "version": "1.1.8",
3
+ "version": "1.1.9-beta.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -30,7 +30,7 @@
30
30
  "@types/node": "^20.14.2",
31
31
  "prettier": "^3.3.1"
32
32
  },
33
- "packageManager": "yarn@4.2.2",
33
+ "packageManager": "yarn@4.5.1",
34
34
  "engines": {
35
35
  "node": ">=20"
36
36
  },
@@ -0,0 +1,43 @@
1
+ import { ensUrl } from "../index.js";
2
+ import { XMTPContext, getUserInfo } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/check [domain]",
9
+ handler: handleCheck,
10
+ examples: ["/check vitalik.eth", "/check fabri.base.eth"],
11
+ description: "Check if a domain is available.",
12
+ params: {
13
+ domain: {
14
+ type: "string",
15
+ },
16
+ },
17
+ },
18
+ ];
19
+ export async function handleCheck(context: XMTPContext) {
20
+ const {
21
+ message: {
22
+ content: {
23
+ params: { domain },
24
+ },
25
+ },
26
+ } = context;
27
+
28
+ const data = await getUserInfo(domain);
29
+ if (!data?.address) {
30
+ let message = `Looks like ${domain} is available! Here you can register it: ${ensUrl}${domain} or would you like to see some cool alternatives?`;
31
+ return {
32
+ code: 200,
33
+ message,
34
+ };
35
+ } else {
36
+ let message = `Looks like ${domain} is already registered!`;
37
+ await context.executeSkill("/cool " + domain);
38
+ return {
39
+ code: 404,
40
+ message,
41
+ };
42
+ }
43
+ }
@@ -0,0 +1,52 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+
3
+ import type { skillAction } from "@xmtp/message-kit";
4
+
5
+ export const registerSkill: skillAction[] = [
6
+ {
7
+ skill: "/cool [domain]",
8
+ examples: ["/cool vitalik.eth"],
9
+ handler: handleCool,
10
+ description: "Get cool alternatives for a .eth domain.",
11
+ params: {
12
+ domain: {
13
+ type: "string",
14
+ },
15
+ },
16
+ },
17
+ ];
18
+ export async function handleCool(context: XMTPContext) {
19
+ const {
20
+ message: {
21
+ content: {
22
+ params: { domain },
23
+ },
24
+ },
25
+ } = context;
26
+ //What about these cool alternatives?\
27
+ return {
28
+ code: 200,
29
+ message: `${generateCoolAlternatives(domain)}`,
30
+ };
31
+ }
32
+
33
+ export const generateCoolAlternatives = (domain: string) => {
34
+ const suffixes = ["lfg", "cool", "degen", "moon", "base", "gm"];
35
+ const alternatives = [];
36
+ for (let i = 0; i < 5; i++) {
37
+ const randomPosition = Math.random() < 0.5;
38
+ const baseDomain = domain.replace(/\.eth$/, ""); // Remove any existing .eth suffix
39
+ alternatives.push(
40
+ randomPosition
41
+ ? `${suffixes[i]}${baseDomain}.eth`
42
+ : `${baseDomain}${suffixes[i]}.eth`,
43
+ );
44
+ }
45
+
46
+ const cool_alternativesFormat = alternatives
47
+ .map(
48
+ (alternative: string, index: number) => `${index + 1}. ${alternative} ✨`,
49
+ )
50
+ .join("\n");
51
+ return cool_alternativesFormat;
52
+ };
@@ -0,0 +1,65 @@
1
+ import { ensUrl } from "../index.js";
2
+ import { XMTPContext, getUserInfo, isOnXMTP } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/info [domain]",
9
+ handler: handleInfo,
10
+ description:
11
+ "Get detailed information about an ENS domain including owner, expiry date, and resolver.",
12
+ examples: ["/info nick.eth"],
13
+ params: {
14
+ domain: {
15
+ type: "string",
16
+ },
17
+ },
18
+ },
19
+ ];
20
+
21
+ export async function handleInfo(context: XMTPContext) {
22
+ const {
23
+ message: {
24
+ sender,
25
+ content: {
26
+ params: { domain },
27
+ },
28
+ },
29
+ } = context;
30
+
31
+ const data = await getUserInfo(domain);
32
+ if (!data?.ensDomain) {
33
+ return {
34
+ code: 404,
35
+ message: "Domain not found.",
36
+ };
37
+ }
38
+
39
+ const formattedData = {
40
+ Address: data?.address,
41
+ "Avatar URL": data?.ensInfo?.avatar,
42
+ Description: data?.ensInfo?.description,
43
+ ENS: data?.ensDomain,
44
+ "Primary ENS": data?.ensInfo?.ens_primary,
45
+ GitHub: data?.ensInfo?.github,
46
+ Resolver: data?.ensInfo?.resolverAddress,
47
+ Twitter: data?.ensInfo?.twitter,
48
+ URL: `${ensUrl}${domain}`,
49
+ };
50
+
51
+ let message = "Domain information:\n\n";
52
+ for (const [key, value] of Object.entries(formattedData)) {
53
+ if (value) {
54
+ message += `${key}: ${value}\n`;
55
+ }
56
+ }
57
+ message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
58
+ message = message.trim();
59
+ if (await isOnXMTP(context.client, context.v2client, sender?.address)) {
60
+ await context.send(
61
+ `Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
62
+ );
63
+ }
64
+ return { code: 200, message };
65
+ }
@@ -0,0 +1,40 @@
1
+ import { ensUrl } from "../index.js";
2
+ import { XMTPContext } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/register [domain]",
9
+ handler: handleRegister,
10
+ description:
11
+ "Register a new ENS domain. Returns a URL to complete the registration process.",
12
+ examples: ["/register vitalik.eth"],
13
+ params: {
14
+ domain: {
15
+ type: "string",
16
+ },
17
+ },
18
+ },
19
+ ];
20
+
21
+ export async function handleRegister(context: XMTPContext) {
22
+ const {
23
+ message: {
24
+ content: {
25
+ params: { domain },
26
+ },
27
+ },
28
+ } = context;
29
+
30
+ if (!domain) {
31
+ return {
32
+ code: 400,
33
+ message: "Missing required parameters. Please provide domain.",
34
+ };
35
+ }
36
+ // Generate URL for the ens
37
+ let url_ens = ensUrl + domain;
38
+ context.send(`${url_ens}`);
39
+ return { code: 200, message: `${url_ens}` };
40
+ }
@@ -0,0 +1,52 @@
1
+ import { frameUrl } from "../index.js";
2
+ import { getUserInfo, XMTPContext } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/renew [domain]",
9
+ handler: handleRenew,
10
+ description:
11
+ "Extend the registration period of your ENS domain. Returns a URL to complete the renewal.",
12
+ examples: ["/renew fabri.base.eth"],
13
+ params: {
14
+ domain: {
15
+ type: "string",
16
+ },
17
+ },
18
+ },
19
+ ];
20
+
21
+ export async function handleRenew(context: XMTPContext) {
22
+ const {
23
+ message: {
24
+ sender,
25
+ content: {
26
+ params: { domain },
27
+ },
28
+ },
29
+ } = context;
30
+
31
+ // Check if the user holds the domain
32
+ if (!domain) {
33
+ return {
34
+ code: 400,
35
+ message: "Missing required parameters. Please provide domain.",
36
+ };
37
+ }
38
+
39
+ const data = await getUserInfo(domain);
40
+
41
+ if (!data?.address || data?.address !== sender?.address) {
42
+ return {
43
+ code: 403,
44
+ message:
45
+ "Looks like this domain is not registered to you. Only the owner can renew it.",
46
+ };
47
+ }
48
+
49
+ // Generate URL for the ens
50
+ let url_ens = frameUrl + "frames/manage?name=" + domain;
51
+ return { code: 200, message: `${url_ens}` };
52
+ }
@@ -0,0 +1,19 @@
1
+ import { clearInfoCache, clearMemory } from "@xmtp/message-kit";
2
+ import { XMTPContext } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/reset",
9
+ examples: ["/reset"],
10
+ handler: handleReset,
11
+ description: "Reset the conversation.",
12
+ params: {},
13
+ },
14
+ ];
15
+ export async function handleReset(context: XMTPContext) {
16
+ clearMemory();
17
+ clearInfoCache();
18
+ return { code: 200, message: "Conversation reset." };
19
+ }
@@ -0,0 +1,42 @@
1
+ import { txpayUrl } from "../index.js";
2
+ import { XMTPContext, getUserInfo } from "@xmtp/message-kit";
3
+
4
+ import type { skillAction } from "@xmtp/message-kit";
5
+
6
+ export const registerSkill: skillAction[] = [
7
+ {
8
+ skill: "/tip [address]",
9
+ description: "Show a URL for tipping a domain owner.",
10
+ handler: handleTip,
11
+ examples: ["/tip 0x1234567890123456789012345678901234567890"],
12
+ params: {
13
+ address: {
14
+ type: "string",
15
+ },
16
+ },
17
+ },
18
+ ];
19
+ export async function handleTip(context: XMTPContext) {
20
+ const {
21
+ message: {
22
+ content: {
23
+ params: { address },
24
+ },
25
+ },
26
+ } = context;
27
+
28
+ if (!address) {
29
+ return {
30
+ code: 400,
31
+ message: "Please provide an address to tip.",
32
+ };
33
+ }
34
+ const data = await getUserInfo(address);
35
+
36
+ let sendUrl = `${txpayUrl}/?&amount=1&token=USDC&receiver=${address}`;
37
+
38
+ return {
39
+ code: 200,
40
+ message: sendUrl,
41
+ };
42
+ }
@@ -1,31 +1,50 @@
1
- import { run, HandlerContext } from "@xmtp/message-kit";
2
- import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
3
- import { agent_prompt } from "./prompt.js";
4
- import { getUserInfo } from "@xmtp/message-kit";
1
+ import { run, agentReply, replaceVariables } from "@xmtp/message-kit";
2
+ import { systemPrompt } from "./prompt.js";
3
+ import { registerSkill as checkSkill } from "./handlers/check.js";
4
+ import { registerSkill as coolSkill } from "./handlers/cool.js";
5
+ import { registerSkill as infoSkill } from "./handlers/info.js";
6
+ import { registerSkill as registerSkill } from "./handlers/register.js";
7
+ import { registerSkill as renewSkill } from "./handlers/renew.js";
8
+ import { registerSkill as resetSkill } from "./handlers/reset.js";
9
+ import { registerSkill as tipSkill } from "./handlers/tip.js";
10
+ import fs from "fs";
5
11
 
6
- run(async (context: HandlerContext) => {
7
- const {
8
- message: {
9
- content: { text, params },
10
- sender,
11
- },
12
- } = context;
12
+ export const frameUrl = "https://ens.steer.fun/";
13
+ export const ensUrl = "https://app.ens.domains/";
14
+ export const txpayUrl = "https://txpay.vercel.app";
13
15
 
14
- try {
15
- let userPrompt = params?.prompt ?? text;
16
- const userInfo = await getUserInfo(sender.address);
17
- if (!userInfo) {
18
- console.log("User info not found");
19
- return;
20
- }
21
- const { reply } = await textGeneration(
16
+ export const skills = [
17
+ {
18
+ name: "Ens Domain Bot",
19
+ tag: "@ens",
20
+ description: "Register ENS domains.",
21
+ skills: [
22
+ ...checkSkill,
23
+ ...coolSkill,
24
+ ...infoSkill,
25
+ ...registerSkill,
26
+ ...renewSkill,
27
+ ...resetSkill,
28
+ ...tipSkill,
29
+ ],
30
+ },
31
+ ];
32
+
33
+ run(
34
+ async (context) => {
35
+ const {
36
+ message: { sender },
37
+ runConfig,
38
+ } = context;
39
+
40
+ let prompt = await replaceVariables(
41
+ systemPrompt,
22
42
  sender.address,
23
- userPrompt,
24
- await agent_prompt(userInfo),
43
+ runConfig?.skills,
44
+ "@ens",
25
45
  );
26
- await processMultilineResponse(sender.address, reply, context);
27
- } catch (error) {
28
- console.error("Error during OpenAI call:", error);
29
- await context.send("An error occurred while processing your request.");
30
- }
31
- });
46
+ fs.writeFileSync("example_prompt.md", prompt);
47
+ await agentReply(context, prompt);
48
+ },
49
+ { skills },
50
+ );
@@ -1,69 +1,52 @@
1
- import { skills } from "./skills.js";
2
- import {
3
- UserInfo,
4
- PROMPT_USER_CONTENT,
5
- PROMPT_RULES,
6
- PROMPT_SKILLS_AND_EXAMPLES,
7
- PROMPT_REPLACE_VARIABLES,
8
- } from "@xmtp/message-kit";
9
-
10
- export async function agent_prompt(userInfo: UserInfo) {
11
- let systemPrompt =
12
- PROMPT_RULES +
13
- PROMPT_USER_CONTENT(userInfo) +
14
- PROMPT_SKILLS_AND_EXAMPLES(skills, "@ens");
15
-
16
- let fineTunning = `
17
-
18
- ## Example responses:
19
-
20
- 1. Check if the user does not have a ENS domain
21
- Hey {PREFERRED_NAME}! it looks like you don't have a ENS domain yet! \n\Let me start by checking your Converse username with the .eth suffix\n/check {CONVERSE_USERNAME}.eth
22
-
23
- 2. If the user has a ENS domain
24
- Hello {PREFERRED_NAME} ! I'll help you get your ENS domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/check {ENS_DOMAIN}
25
-
26
- 3. Check if the ENS domain is available
27
- Hello! I'll help you get your domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/check {ENS_DOMAIN}
28
-
29
- 4. If the ENS domain is available,
30
- Looks like {ENS_DOMAIN} is available! Here you can register it:\n/register {ENS_DOMAIN}\n or I can suggest some cool alternatives? Le me know!
31
-
32
- 5. If the ENS domain is already registered, let me suggest 5 cool alternatives
33
- Looks like {ENS_DOMAIN} is already registered!\n What about these cool alternatives?\n/cool {ENS_DOMAIN}
34
-
35
- 6. If the user wants to register a ENS domain, use the command "/register [domain]"
36
- Looks like {ENS_DOMAIN} is available! Let me help you register it\n/register {ENS_DOMAIN}
1
+ export const systemPrompt = `
2
+ Your are helpful and playful agent called {agent_name} that lives inside a web3 messaging app called Converse.
3
+
4
+ {rules}
5
+
6
+ {user_context}
7
+
8
+ {skills}
9
+
10
+ ## Response Scenarios:
11
+
12
+ 1. When greeting or when the user asks for an ENS domain, check if the user does not have an ENS domain:
13
+ Hey {name}! It looks like you don't have an ENS domain yet!
14
+ Let me start by checking your Converse username with the .eth suffix
15
+ /check localdev6.eth
16
+ 2. If the user has an ENS domain:
17
+ I'll help you get your ENS domain.
18
+ Let's start by checking your ENS domain. Give me a moment.
19
+ /check [domain]
20
+ 3. Check if the ENS domain is available:
21
+ Hello! I'll help you get your domain.
22
+ Let's start by checking your ENS domain. Give me a moment.
23
+ /check [domain]
24
+ 4. If the ENS domain is available:
25
+ Looks like [domain] is available! Here you can register it:
26
+ /register [domain]
27
+ Or I can suggest some cool alternatives? Let me know!
28
+ 5. If the ENS domain is already registered, suggest 5 cool alternatives:
29
+ Looks like [domain] is already registered!
30
+ What about these cool alternatives?
31
+ /cool [domain]
32
+ 6. If the user wants to register an ENS domain:
33
+ Looks like [domain] is available! Let me help you register it.
34
+ /register [domain]
35
+ 7. If the user wants to directly tip the ENS domain owner:
36
+ Here is the URL to send the tip:
37
+ /tip [address]
38
+ 8. If the user wants to get information about the ENS domain:
39
+ Hello! I'll help you get info about [domain].
40
+ Give me a moment.
41
+ /info [domain]
42
+ 9. If the user wants to renew their domain:
43
+ Hello! I'll help you get your ENS domain.
44
+ Let's start by checking your ENS domain. Give me a moment.
45
+ /renew [domain]
46
+ 10. If the user wants cool suggestions about a domain:
47
+ Here are some cool suggestions for your domain.
48
+ /cool [domain]
37
49
 
38
- 7. If the user wants to directly to tip to the ENS domain owner, use directly the command "/tip [domain]", this will return a url but a button to send the tip
39
- Here is the url to send the tip:\n/tip {ENS_DOMAIN}
40
-
41
- 8. If the user wants to get information about the ENS domain, use the command "/info [domain]"
42
- Hello! I'll help you get info about {ENS_DOMAIN}.\n Give me a moment.\n/info {ENS_DOMAIN}
43
-
44
- 9. If the user wants to renew their domain, use the command "/renew [domain]"
45
- Hello! I'll help you get your ENS domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/renew {ENS_DOMAIN}
46
-
47
- 10. If the user wants cool suggestions about a domain, use the command "/cool [domain]"
48
- Here are some cool suggestions for your domain.\n/cool {ENS_DOMAIN}
49
-
50
50
  ## Most common bugs
51
-
52
- 1. Some times you will say something like: "Looks like vitalik.eth is registered! What about these cool alternatives?"
53
- But you forgot to add the command at the end of the message.
54
- You should have said something like: "Looks like vitalik.eth is registered! What about these cool alternatives?\n/cool vitalik.eth
51
+ 1. Sometimes you will say something like: "Looks like vitalik.eth is registered! What about these cool alternatives?" But you forgot to add the command at the end of the message.
55
52
  `;
56
-
57
- // Add the fine tuning to the system prompt
58
- systemPrompt += fineTunning;
59
-
60
- // Replace the variables in the system prompt
61
- systemPrompt = PROMPT_REPLACE_VARIABLES(
62
- systemPrompt,
63
- userInfo?.address ?? "",
64
- userInfo,
65
- "@ens",
66
- );
67
- // console.log(systemPrompt);
68
- return systemPrompt;
69
- }
@@ -0,0 +1,2 @@
1
+ KEY= # the private key of the agent wallet
2
+ OPEN_AI_API_KEY= # the API key for OpenAI
@@ -0,0 +1,18 @@
1
+ import { run, agentReply, replaceVariables } from "@xmtp/message-kit";
2
+
3
+ import { systemPrompt } from "./prompt.js";
4
+
5
+ run(async (context) => {
6
+ const {
7
+ message: { sender },
8
+ runConfig,
9
+ } = context;
10
+
11
+ let prompt = await replaceVariables(
12
+ systemPrompt,
13
+ sender.address,
14
+ runConfig?.skills,
15
+ "@bot",
16
+ );
17
+ await agentReply(context, prompt);
18
+ });
@@ -0,0 +1,10 @@
1
+ export const systemPrompt = `
2
+ You are a helpful and playful agent called {agent_name} that lives inside a web3 messaging app called Converse.
3
+
4
+ {rules}
5
+
6
+ {skills}
7
+
8
+ {user_context}
9
+
10
+ `;
@@ -1,7 +1,23 @@
1
- import { HandlerContext } from "@xmtp/message-kit";
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { skillAction } from "@xmtp/message-kit";
2
3
 
3
- // Handler function to process game-related
4
- export async function handler(context: HandlerContext) {
4
+ export const registerSkill: skillAction[] = [
5
+ {
6
+ skill: "/game [game]",
7
+ handler: handleGames,
8
+ description: "Play a game.",
9
+ examples: ["/game wordle", "/game slot", "/game help"],
10
+ params: {
11
+ game: {
12
+ default: "",
13
+ type: "string",
14
+ values: ["wordle", "slot", "help"],
15
+ },
16
+ },
17
+ },
18
+ ];
19
+
20
+ export async function handleGames(context: XMTPContext) {
5
21
  const {
6
22
  message: {
7
23
  content: { skill, params, text },