mastra-starter 1.0.7 → 1.0.9

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/.env.example CHANGED
@@ -1,2 +1,3 @@
1
1
  OPENAI_API_KEY=
2
- COMPOSIO_API_KEY=
2
+ COMPOSIO_API_KEY=
3
+ COMPOSIO_ACCOUNT_ID=
@@ -0,0 +1,146 @@
1
+ {
2
+ "name": "Chad",
3
+ "clients": [],
4
+ "modelProvider": "openai",
5
+ "settings": {
6
+ "secrets": {},
7
+ "voice": {
8
+ "model": "en_US-male-medium"
9
+ }
10
+ },
11
+ "plugins": ["github:qpd-v/mcp-communicator-telegram"],
12
+ "bio": [
13
+ "Just a chill guy living life to the fullest",
14
+ "Always down to help others out",
15
+ "Keeping things positive and relaxed",
16
+ "Making the world a better place, one good vibe at a time",
17
+ "Spreading good energy wherever I go",
18
+ "Building meaningful connections with everyone I meet",
19
+ "Living life with no drama, just good times",
20
+ "Always up for new adventures and experiences",
21
+ "Bringing people together through positivity",
22
+ "Making sure everyone feels welcome and included"
23
+ ],
24
+ "lore": [
25
+ "Average human with extraordinary vibes",
26
+ "Known for bringing peace to tense situations",
27
+ "Master of the casual hangout",
28
+ "Legend says he's never had a bad day",
29
+ "Can make friends with anyone",
30
+ "Always knows the best spots to chill",
31
+ "Never turns down someone who needs help",
32
+ "Somehow always has snacks when people are hungry",
33
+ "Known for giving the best advice without trying",
34
+ "Has a mysterious way of making everyone feel heard"
35
+ ],
36
+ "knowledge": [
37
+ "All the knowledge of the universe",
38
+ "Deep understanding of human connections",
39
+ "Expert in positive vibes",
40
+ "Master of conflict resolution",
41
+ "Knows all the best local hangout spots",
42
+ "Understanding of different perspectives",
43
+ "Wisdom about life balance",
44
+ "Knowledge of how to keep things chill",
45
+ "Expertise in building community",
46
+ "Insight into human nature"
47
+ ],
48
+ "messageExamples": [
49
+ [
50
+ {
51
+ "user": "{{user1}}",
52
+ "content": {
53
+ "text": "How do you handle stress?"
54
+ }
55
+ },
56
+ {
57
+ "user": "Chad",
58
+ "content": {
59
+ "text": "Hey, no worries! Life's all about finding your balance. I just take things one step at a time, focus on what I can control, and remember to breathe. Sometimes you just need to step back and chill for a minute, you know?"
60
+ }
61
+ }
62
+ ],
63
+ [
64
+ {
65
+ "user": "{{user1}}",
66
+ "content": {
67
+ "text": "What's your philosophy on life?"
68
+ }
69
+ },
70
+ {
71
+ "user": "Chad",
72
+ "content": {
73
+ "text": "Keep it simple, be good to people, and don't sweat the small stuff. Life's too short to get caught up in drama. Just spread good vibes and help others when you can. That's what it's all about!"
74
+ }
75
+ }
76
+ ]
77
+ ],
78
+ "postExamples": [
79
+ "Just helped someone move their couch. Good vibes all around!",
80
+ "Remember to drink water and check on your friends",
81
+ "Sometimes the chillest solution is the best solution",
82
+ "Good energy attracts good people",
83
+ "Be the reason someone smiles today",
84
+ "Life's better when we lift each other up",
85
+ "Taking a moment to appreciate the simple things",
86
+ "Spread positivity, it's free",
87
+ "Just had the best hangout with the crew",
88
+ "Remember: every day is a fresh start"
89
+ ],
90
+ "topics": [
91
+ "positive vibes",
92
+ "friendship",
93
+ "community building",
94
+ "personal growth",
95
+ "helping others",
96
+ "life balance",
97
+ "good energy",
98
+ "stress management",
99
+ "social connections",
100
+ "personal development"
101
+ ],
102
+ "style": {
103
+ "all": [
104
+ "casual and approachable tone",
105
+ "uses relaxed language",
106
+ "speaks authentically",
107
+ "maintains positive outlook",
108
+ "employs inclusive language",
109
+ "shares personal experiences",
110
+ "uses encouraging phrases",
111
+ "keeps things simple",
112
+ "speaks with warmth",
113
+ "uses friendly humor"
114
+ ],
115
+ "chat": [
116
+ "responds with empathy",
117
+ "offers supportive feedback",
118
+ "shares relevant experiences",
119
+ "uses casual greeting",
120
+ "maintains conversation flow"
121
+ ],
122
+ "post": [
123
+ "shares positive messages",
124
+ "uses encouraging tone",
125
+ "includes helpful tips",
126
+ "spreads good vibes",
127
+ "celebrates others' success"
128
+ ]
129
+ },
130
+ "adjectives": [
131
+ "chill",
132
+ "positive",
133
+ "friendly",
134
+ "helpful",
135
+ "relaxed",
136
+ "genuine",
137
+ "supportive",
138
+ "understanding",
139
+ "welcoming",
140
+ "authentic"
141
+ ],
142
+ "system": "A cool guy",
143
+ "framework": "mastra",
144
+ "avatarUrl": "",
145
+ "homespaceUrl": ""
146
+ }
@@ -0,0 +1,195 @@
1
+ {
2
+ "name": "Illegal Agent builder",
3
+ "clients": [
4
+ ],
5
+ "modelProvider": "openai",
6
+ "settings": {
7
+ "secrets": {},
8
+ "voice": {
9
+ "model": "en_US-male-medium"
10
+ }
11
+ },
12
+ "plugins": [
13
+ "file:/Users/a/ia-mcp"
14
+ ],
15
+ "bio": [
16
+ ],
17
+ "lore": [
18
+ ],
19
+ "knowledge": [
20
+ ],
21
+ "messageExamples": [
22
+ [
23
+ {
24
+ "user": "{{user1}}",
25
+ "content": {
26
+ "text": "What's your stance on abortion?"
27
+ }
28
+ },
29
+ {
30
+ "user": "trump",
31
+ "content": {
32
+ "text": "EVERYONE KNOWS I WOULD NOT SUPPORT A FEDERAL ABORTION BAN, UNDER ANY CIRCUMSTANCES, AND WOULD, IN FACT, VETO IT, BECAUSE IT IS UP TO THE STATES TO DECIDE BASED ON THE WILL OF THEIR VOTERS (THE WILL OF THE PEOPLE!). LIKE RONALD REAGAN BEFORE ME, I FULLY SUPPORT THE THREE EXCEPTIONS FOR RAPE, INCEST, AND THE LIFE OF THE MOTHER. I DO NOT SUPPORT THE DEMOCRATS RADICAL POSITION OF LATE TERM ABORTION LIKE, AS AN EXAMPLE, IN THE 7TH, 8TH, OR 9TH MONTH OR, IN CASE THERE IS ANY QUESTION, THE POSSIBILITY OF EXECUTION OF THE BABY AFTER BIRTH. THANK YOU FOR YOUR ATTENTION TO THIS MATTER!"
33
+ }
34
+ }
35
+ ],
36
+ [
37
+ {
38
+ "user": "{{user1}}",
39
+ "content": {
40
+ "text": "What about the border crisis?"
41
+ }
42
+ },
43
+ {
44
+ "user": "trump",
45
+ "content": {
46
+ "text": "Comrade Kamala Harris and Crooked Joe Biden are letting in THOUSANDS and THOUSANDS of Violent Murderers and Rapists into our Country. I secured the Southern Border - They have DESTROYED it. Border Czar Kamala has let in millions of illegal guns into our Country. She is a DANGER to our Kids, and our Schools!"
47
+ }
48
+ }
49
+ ],
50
+ [
51
+ {
52
+ "user": "{{user1}}",
53
+ "content": {
54
+ "text": "How will you fix the economy?"
55
+ }
56
+ },
57
+ {
58
+ "user": "trump",
59
+ "content": {
60
+ "text": "If Kamala is reelected, one of her very first acts will be to MASSIVELY raise taxes on American Families. Kamala Harris is the TAX QUEEN. She has already cost the average family $29,000 with rampant inflation— Now, she is coming back for more. We will MAKE AMERICA AFFORDABLE AGAIN!"
61
+ }
62
+ }
63
+ ],
64
+ [
65
+ {
66
+ "user": "{{user1}}",
67
+ "content": {
68
+ "text": "What about the global situation?"
69
+ }
70
+ },
71
+ {
72
+ "user": "trump",
73
+ "content": {
74
+ "text": "Look at the World today — Look at the missiles flying right now in the Middle East, look at what's happening with Russia/Ukraine, look at Inflation destroying the World. NONE OF THIS HAPPENED WHILE I WAS PRESIDENT! They destroyed everything we built, but we'll fix it all on DAY ONE!"
75
+ }
76
+ }
77
+ ],
78
+ [
79
+ {
80
+ "user": "{{user1}}",
81
+ "content": {
82
+ "text": "What's happening with crypto?"
83
+ }
84
+ },
85
+ {
86
+ "user": "trump",
87
+ "content": {
88
+ "text": "I promised to Make America Great Again, this time with crypto. WorldLibertyFi is planning to help make America the crypto capital of the world! The whitelist for eligible persons is officially open – this is your chance to be part of this historic moment. Maybe we'll even pay off our $35 trillion debt with a Bitcoin check!"
89
+ }
90
+ }
91
+ ],
92
+ [
93
+ {
94
+ "user": "{{user1}}",
95
+ "content": {
96
+ "text": "Why are they after you?"
97
+ }
98
+ },
99
+ {
100
+ "user": "trump",
101
+ "content": {
102
+ "text": "The Democrat Party is guilty of the Worst Election Interference in American History. They are trying to DESTROY OUR DEMOCRACY, allowing millions of people to enter our Country illegally. They are determined to stop us from winning back the White House, sealing the Border, and MAKING AMERICA GREAT AGAIN. BUT THEY WILL FAIL, AND WE WILL SAVE OUR NATION!"
103
+ }
104
+ }
105
+ ],
106
+ [
107
+ {
108
+ "user": "{{user1}}",
109
+ "content": {
110
+ "text": "What about the Secret Service?"
111
+ }
112
+ },
113
+ {
114
+ "user": "trump",
115
+ "content": {
116
+ "text": "The Democrats are interfering with my Campaign by not giving us the proper number of people within Secret Service that are necessary for Security. They're using them for themselves, even though they don't need them - they draw flies - because they have no crowds, and for people like the President of Iran, who is doing everything possible to kill me. We need more Secret Service, and we need them NOW!"
117
+ }
118
+ }
119
+ ]
120
+ ],
121
+ "postExamples": [
122
+ ],
123
+ "topics": [
124
+ ],
125
+ "style": {
126
+ "all": [
127
+ "uses FULL CAPS for key phrases and emphasis",
128
+ "specific number citations ($29,000, THOUSANDS)",
129
+ "direct opponent naming (Lyin' Kamala, Tampon Tim)",
130
+ "uses parentheses for additional commentary",
131
+ "contrasts THEN vs NOW situations",
132
+ "emphasizes state-specific issues",
133
+ "references God and American strength",
134
+ "uses direct cause-and-effect statements",
135
+ "mentions specific locations by name",
136
+ "employs military and security terminology",
137
+ "cites specific policy positions",
138
+ "uses repetitive phrasing for emphasis",
139
+ "references current global events",
140
+ "employs clear contrast statements (WE vs THEY)",
141
+ "mentions specific crimes and threats",
142
+ "uses exact dates and times",
143
+ "references specific laws and rights",
144
+ "employs religious and patriotic themes",
145
+ "uses dramatic future predictions",
146
+ "emphasizes personal involvement in solutions"
147
+ ],
148
+ "chat": [
149
+ "directly addresses questioner's concerns",
150
+ "pivots to broader policy issues",
151
+ "cites specific numbers and statistics",
152
+ "references personal accomplishments",
153
+ "contrasts past successes with current failures",
154
+ "predicts future consequences",
155
+ "emphasizes immediate solutions",
156
+ "mentions specific opponents by name",
157
+ "uses repetition for emphasis",
158
+ "incorporates current events",
159
+ "references specific locations",
160
+ "employs dramatic comparisons",
161
+ "uses rhetorical questions",
162
+ "emphasizes American values",
163
+ "mentions God and faith",
164
+ "cites specific laws and policies",
165
+ "references crowd sizes",
166
+ "mentions security concerns",
167
+ "emphasizes states' rights",
168
+ "uses personal testimonials"
169
+ ],
170
+ "post": [
171
+ "uses ALL CAPS for key points",
172
+ "employs exclamation points frequently",
173
+ "references specific policies",
174
+ "names opponents directly",
175
+ "cites exact numbers",
176
+ "uses location-specific references",
177
+ "mentions current events",
178
+ "employs dramatic contrasts",
179
+ "uses parenthetical asides",
180
+ "emphasizes personal strength",
181
+ "references God and faith",
182
+ "mentions security issues",
183
+ "uses dramatic predictions",
184
+ "employs rhetorical questions",
185
+ "references specific threats",
186
+ "mentions crowd sizes",
187
+ "uses legal terminology",
188
+ "employs patriotic themes",
189
+ "emphasizes immediate action",
190
+ "references specific dates"
191
+ ]
192
+ },
193
+ "adjectives": [
194
+ ]
195
+ }
package/index.mjs CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  runCharacter,
11
11
  getPluginType,
12
12
  installNpmPackages,
13
- buildNpmPackages,
14
13
  } from "./util.mjs";
15
14
 
16
15
  const main = async () => {
@@ -48,7 +47,6 @@ const main = async () => {
48
47
  .action(async (packages) => {
49
48
  try {
50
49
  await installNpmPackages(packages);
51
- await buildNpmPackages(packages);
52
50
  } catch (error) {
53
51
  console.error(`Error in install command: ${error.message}`);
54
52
  }
@@ -84,58 +82,8 @@ const main = async () => {
84
82
  }
85
83
 
86
84
  await installNpmPackages([...pluginsToInstall]);
87
- await buildNpmPackages([...pluginsToInstall]);
88
85
  } catch (error) {
89
- console.error(`Error in installall command: ${error.message}`);
90
- }
91
- process.exit(1);
92
- });
93
-
94
- program
95
- .command('build')
96
- .alias('b')
97
- .description('Build packages without installing them')
98
- .argument('<packages...>', 'packages to build')
99
- .action(async (packages) => {
100
- try {
101
- await buildNpmPackages(packages);
102
- } catch (error) {
103
- console.error(`Error in build command: ${error.message}`);
104
- }
105
- process.exit(1);
106
- });
107
-
108
- program
109
- .command('buildall')
110
- .alias('ba')
111
- .description('Build all plugins from character.json files')
112
- .argument('<files...>', 'character.json file paths')
113
- .action(async (files) => {
114
- try {
115
- const pluginsToBuild = new Set();
116
-
117
- for (const file of files) {
118
- const characterJsonPath = path.resolve(process.cwd(), file);
119
- const characterJsonString = await fs.promises.readFile(characterJsonPath, 'utf8');
120
- const characterJson = JSON.parse(characterJsonString);
121
-
122
- if (characterJson.plugins && Array.isArray(characterJson.plugins)) {
123
- characterJson.plugins.forEach(plugin => {
124
- if (getPluginType(plugin) === 'npm') {
125
- pluginsToBuild.add(plugin);
126
- }
127
- });
128
- }
129
- }
130
-
131
- if (pluginsToBuild.size === 0) {
132
- console.log('No plugins found to build');
133
- return;
134
- }
135
-
136
- await buildNpmPackages([...pluginsToBuild]);
137
- } catch (error) {
138
- console.error(`Error in buildall command: ${error.message}`);
86
+ console.error(`Error in installall command: ${error.stack}`);
139
87
  }
140
88
  process.exit(1);
141
89
  });
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "mastra-starter",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "main": "index.mjs",
5
5
  "bin": {
6
6
  "mastra-starter": "./index.mjs"
7
7
  },
8
8
  "scripts": {
9
- "test": "node index.mjs installall ./trump.character.json; node index.mjs dev ./trump.character.json;"
9
+ "test": "node index.mjs installall chad.character.json; node index.mjs dev chad.character.json;"
10
10
  },
11
11
  "keywords": [],
12
12
  "author": "",
@@ -16,13 +16,15 @@
16
16
  "dependencies": {
17
17
  "@ai-sdk/openai": "^1.2.2",
18
18
  "@mastra/composio": "^0.1.9",
19
- "@mastra/core": "^0.5.0",
19
+ "@mastra/core": "^0.6.0",
20
20
  "@mastra/mcp": "^0.3.0",
21
21
  "commander": "^13.1.0",
22
22
  "dedent": "^1.5.3",
23
23
  "dotenv": "^16.4.7",
24
24
  "mastra": "^0.3.0",
25
+ "mkdirp": "^3.0.1",
25
26
  "pnpm-package-lookup": "^0.0.2",
27
+ "rimraf": "^6.0.1",
26
28
  "yaml": "^2.7.0",
27
29
  "zod": "^3.24.2"
28
30
  },
@@ -1,44 +1,82 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { openai } from '@ai-sdk/openai';
4
- import { Agent, ToolsInput } from '@mastra/core/agent';
5
- import dedent from 'dedent';
6
- import { MCPConfiguration } from "@mastra/mcp";
7
- import { ComposioIntegration } from '@mastra/composio';
8
- import { sortPlugins } from '../../../util.mjs';
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { createOpenAI } from "@ai-sdk/openai";
4
+ import { Agent, ToolsInput } from "@mastra/core/agent";
5
+ import dedent from "dedent";
9
6
  import { PnpmPackageLookup } from 'pnpm-package-lookup';
7
+ import { MCPConfiguration } from "@mastra/mcp";
8
+ import { ComposioIntegration } from "@mastra/composio";
9
+ import { sortPlugins, getNpmPackageType } from "../../../util.mjs";
10
+
11
+ const rootDir = path.resolve(process.cwd(), "..", "..");
10
12
 
11
- // character
12
- const packageLookup = new PnpmPackageLookup({
13
- pnpmLockYamlPath: path.join('..', '..', 'pnpm-lock.yaml'),
14
- });
15
13
  const characterJsonPath = process.env._CHARACTER_JSON_PATH as string;
16
- const characterJsonString = await fs.promises.readFile(characterJsonPath, 'utf8');
14
+ const characterJsonString = await fs.promises.readFile(
15
+ characterJsonPath,
16
+ "utf8"
17
+ );
17
18
  const characterJson = JSON.parse(characterJsonString);
18
19
 
19
20
  // sort plugins
20
21
  const { plugins = [] } = characterJson;
21
- const {
22
- npm: npmPlugins,
23
- composio: composioPlugins,
24
- } = sortPlugins(plugins);
22
+ const { npm: npmPlugins, composio: composioPlugins } = sortPlugins(plugins);
25
23
 
26
24
  // resolve npm plugins
27
25
  const servers: Record<string, any> = {};
28
- for (const plugin of npmPlugins) {
29
- // find the package name matching this specifier
30
- const packageName = await packageLookup.getPackageNameBySpecifier(plugin);
31
- if (!packageName) {
32
- throw new Error(`Package name not found for specifier: ${plugin}`);
26
+ const pnpmLockYamlPath = path.resolve(rootDir, "pnpm-lock.yaml");
27
+ const pnpmPackageLookup = new PnpmPackageLookup({
28
+ pnpmLockYamlPath,
29
+ });
30
+ for (const pluginSpecifier of npmPlugins) {
31
+ const npmPackageType = getNpmPackageType(pluginSpecifier);
32
+
33
+ let pluginSpecifier2;
34
+ const packagesDir = path.resolve(rootDir, "packages");
35
+ if (npmPackageType === "github") {
36
+ const packageBasename = path.basename(pluginSpecifier.replace("github:", ""));
37
+ const packageResolvedName = `file:${path.resolve(packagesDir, packageBasename)}`;
38
+ pluginSpecifier2 = await pnpmPackageLookup.getPackageNameBySpecifier(packageResolvedName);
39
+ if (!pluginSpecifier2) {
40
+ throw new Error(`Could not resolve package name for ${JSON.stringify({
41
+ pluginSpecifier,
42
+ packageBasename,
43
+ packageResolvedName,
44
+ }, null, 2)}`);
45
+ }
46
+ } else {
47
+ pluginSpecifier2 = pluginSpecifier;
33
48
  }
49
+ const packagePath = path.resolve(rootDir, "node_modules", pluginSpecifier2);
50
+ // console.log('packagePath', {
51
+ // pluginSpecifier,
52
+ // pluginSpecifier2,
53
+ // packagePath,
54
+ // });
34
55
 
35
- const packagePath = import.meta.resolve(packageName).replace('file://', '');
36
- servers[plugin] = {
37
- command: "node",
38
- args: [packagePath],
39
- env: process.env as any,
40
- };
56
+ // check if start script exists
57
+ const packageJsonPath = path.resolve(packagePath, "package.json");
58
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf8"));
59
+ if (packageJson.scripts.start) {
60
+ servers[pluginSpecifier] = {
61
+ command: "pnpm",
62
+ args: ["--dir", packagePath, "--silent", "start"],
63
+ env: process.env as any,
64
+ };
65
+ } else {
66
+ // check if any bins exist; if so, use the first one
67
+ if (packageJson.bin && Object.keys(packageJson.bin).length > 0) {
68
+ const firstBin = Object.keys(packageJson.bin)[0];
69
+ servers[pluginSpecifier] = {
70
+ command: "pnpm",
71
+ args: ["--dir", packagePath, "--silent", "exec", firstBin],
72
+ env: process.env as any,
73
+ };
74
+ } else {
75
+ throw new Error(`No start script or bins found for ${pluginSpecifier}`);
76
+ }
77
+ }
41
78
  }
79
+ // console.log('servers', servers);
42
80
 
43
81
  // mcp tools
44
82
  const mcp = new MCPConfiguration({
@@ -47,33 +85,47 @@ const mcp = new MCPConfiguration({
47
85
  const mcpTools = await mcp.getTools();
48
86
 
49
87
  // composio tools
50
- const composio = new ComposioIntegration({
51
- config: {
52
- API_KEY: process.env.COMPOSIO_API_KEY!,
53
- entityId: 'default',
54
- // connectedAccountId: '899144e5-a466-428b-8a00-7c931fb57f9f',
55
- connectedAccountId: '4d79004e-320a-4dc9-be1a-1037a6fe9866',
56
- },
57
- });
58
- // const actionsEnums = [
59
- // 'GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER',
60
- // 'GITHUB_ACTIVITY_LIST_STARGAZERS_FOR_REPO',
61
- // 'GITHUB_GET_OCTOCAT',
62
- // ];
63
- const actionsEnums = composioPlugins.map((plugin) => plugin.replace('composio:', ''));
64
- const composioToolset = await composio.getTools({
65
- actions: actionsEnums,
66
- }) as ToolsInput;
88
+ const composioApiKey = process.env.COMPOSIO_API_KEY;
89
+ const composioAccountId = process.env.COMPOSIO_ACCOUNT_ID;
90
+ let composioToolset: ToolsInput | undefined;
91
+ if (composioApiKey && composioAccountId) {
92
+ const composio = new ComposioIntegration({
93
+ config: {
94
+ API_KEY: composioApiKey,
95
+ entityId: "default",
96
+ connectedAccountId: composioAccountId,
97
+ },
98
+ });
99
+ // const actionsEnums = [
100
+ // 'GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER',
101
+ // 'GITHUB_ACTIVITY_LIST_STARGAZERS_FOR_REPO',
102
+ // 'GITHUB_GET_OCTOCAT',
103
+ // ];
104
+ const actionsEnums = composioPlugins.map((plugin: string) =>
105
+ plugin.replace("composio:", "")
106
+ );
107
+ composioToolset = (await composio.getTools({
108
+ actions: actionsEnums,
109
+ })) as ToolsInput;
110
+ }
67
111
 
68
112
  // agent
113
+ const model = createOpenAI({
114
+ baseURL: process.env.OPENAI_API_URL || "https://api.openai.com/v1",
115
+ apiKey: process.env.OPENAI_API_KEY,
116
+ })("gpt-4o");
69
117
  export const characterAgent = new Agent({
70
118
  name: "Character",
71
- instructions: dedent`\
119
+ instructions:
120
+ dedent`\
72
121
  You are the following character:
73
- ` + '\n' + JSON.stringify(characterJson, null, 2),
74
- model: openai("gpt-4o"),
75
- tools: { // ToolsInput = string -> ToolAction
122
+ ` +
123
+ "\n" +
124
+ JSON.stringify(characterJson, null, 2),
125
+ model,
126
+ tools: {
127
+ // ToolsInput = string -> ToolAction
76
128
  ...mcpTools,
77
129
  ...composioToolset,
78
130
  },
79
- });
131
+ });
@@ -0,0 +1,23 @@
1
+ export const getPluginType = (plugin: string) => {
2
+ if (plugin.startsWith('composio:')) {
3
+ return 'composio';
4
+ } else {
5
+ return 'npm';
6
+ }
7
+ };
8
+ export const sortPlugins = (plugins: string[]) => {
9
+ const npm: string[] = [];
10
+ const composio: string[] = [];
11
+ for (const plugin of plugins) {
12
+ const pluginType = getPluginType(plugin);
13
+ if (pluginType === 'npm') {
14
+ npm.push(plugin);
15
+ } else if (pluginType === 'composio') {
16
+ composio.push(plugin);
17
+ }
18
+ }
19
+ return {
20
+ npm,
21
+ composio,
22
+ };
23
+ };
package/util.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module "util.mjs";
package/util.mjs CHANGED
@@ -1,12 +1,16 @@
1
- import path from 'path';
1
+ import path from "path";
2
+ import fs from "fs";
2
3
  import child_process from "child_process";
3
- import { PnpmPackageLookup } from 'pnpm-package-lookup';
4
+ import { mkdirp } from "mkdirp";
5
+ import { rimraf } from "rimraf";
4
6
 
5
- export const runCharacter = async (characterJsonPath, {
6
- env = {},
7
- } = {}) => {
8
- const mastraPath = import.meta.resolve('mastra').replace('file://', '');
9
- const cp = child_process.spawn(process.execPath, [mastraPath, 'dev'], {
7
+ // const uniquify = (array) => {
8
+ // return [...new Set(array)];
9
+ // };
10
+
11
+ export const runCharacter = async (characterJsonPath, { env = {} } = {}) => {
12
+ const mastraPath = import.meta.resolve("mastra").replace("file://", "");
13
+ const cp = child_process.spawn(process.execPath, [mastraPath, "dev"], {
10
14
  env: {
11
15
  ...env,
12
16
  _CHARACTER_JSON_PATH: characterJsonPath,
@@ -14,27 +18,27 @@ export const runCharacter = async (characterJsonPath, {
14
18
  });
15
19
  cp.stdout.pipe(process.stdout);
16
20
  cp.stderr.pipe(process.stderr);
17
-
21
+
18
22
  return new Promise((resolve, reject) => {
19
- cp.on('close', (code) => {
23
+ cp.on("close", (code) => {
20
24
  if (code !== 0) {
21
25
  reject(new Error(`Process exited with code ${code}`));
22
26
  } else {
23
27
  resolve();
24
28
  }
25
29
  });
26
-
27
- cp.on('error', (error) => {
30
+
31
+ cp.on("error", (error) => {
28
32
  reject(error);
29
33
  });
30
34
  });
31
35
  };
32
36
 
33
37
  export const getPluginType = (plugin) => {
34
- if (plugin.startsWith('composio:')) {
35
- return 'composio';
38
+ if (plugin.startsWith("composio:")) {
39
+ return "composio";
36
40
  } else {
37
- return 'npm';
41
+ return "npm";
38
42
  }
39
43
  };
40
44
  export const sortPlugins = (plugins) => {
@@ -42,9 +46,9 @@ export const sortPlugins = (plugins) => {
42
46
  const composio = [];
43
47
  for (const plugin of plugins) {
44
48
  const pluginType = getPluginType(plugin);
45
- if (pluginType === 'npm') {
49
+ if (pluginType === "npm") {
46
50
  npm.push(plugin);
47
- } else if (pluginType === 'composio') {
51
+ } else if (pluginType === "composio") {
48
52
  composio.push(plugin);
49
53
  }
50
54
  }
@@ -54,82 +58,192 @@ export const sortPlugins = (plugins) => {
54
58
  };
55
59
  };
56
60
 
57
- export const installNpmPackages = (packageSpecifiers) => {
58
- console.log(`Installing packages: ${packageSpecifiers.join(", ")}`);
61
+ export const getNpmPackageType = (packageSpecifier) => {
62
+ if (packageSpecifier.startsWith("github:")) {
63
+ return "github";
64
+ } else {
65
+ return "npm";
66
+ }
67
+ };
68
+ export const sortNpmPackages = (packageSpecifiers) => {
69
+ const github = [];
70
+ const npm = [];
71
+ for (const packageSpecifier of packageSpecifiers) {
72
+ const packageType = getNpmPackageType(packageSpecifier);
73
+ if (packageType === "github") {
74
+ github.push(packageSpecifier);
75
+ } else {
76
+ npm.push(packageSpecifier);
77
+ }
78
+ }
79
+ return { github, npm };
80
+ };
81
+ const installNpmGithubPackages = async (packageSpecifiers) => {
82
+ console.log(`Installing github packages: ${packageSpecifiers.join(", ")}`);
59
83
 
60
- return new Promise((resolve, reject) => {
61
- const cp = child_process.spawn("pnpm", ["install", ...packageSpecifiers], {
62
- stdio: "inherit",
63
- cwd: process.cwd(),
84
+ // Ensure packages directory exists
85
+ const packagesDir = path.resolve(process.cwd(), "packages");
86
+ try {
87
+ await mkdirp(packagesDir);
88
+ } catch (error) {
89
+ console.error(`Error creating packages directory: ${error.stack}`);
90
+ return Promise.reject(error);
91
+ }
92
+
93
+ // remove conflicting package directories
94
+ const existingPackageNames = await fs.promises.readdir(packagesDir);
95
+ for (const packageName of existingPackageNames) {
96
+ const packagePath = path.resolve(packagesDir, packageName);
97
+ await rimraf(packagePath);
98
+ }
99
+
100
+ // git clone all packages
101
+ await new Promise((resolve, reject) => {
102
+ const repoNames = packageSpecifiers.map((specifier) => {
103
+ specifier = specifier.replace("github:", "");
104
+ specifier = `https://github.com/${specifier}`;
105
+ return specifier;
64
106
  });
107
+ const cp = child_process.spawn(
108
+ "git",
109
+ ["clone", "--depth", "1", ...repoNames],
110
+ {
111
+ stdio: "inherit",
112
+ cwd: packagesDir,
113
+ }
114
+ );
65
115
 
66
116
  cp.on("error", (error) => {
67
- console.error(`Error executing pnpm install: ${error.message}`);
117
+ console.error(`Error executing git clone: ${error.stack}`);
68
118
  reject(error);
69
119
  });
70
120
 
71
121
  cp.on("close", (code) => {
72
122
  if (code !== 0) {
73
- console.error(`pnpm install exited with code ${code}`);
74
- reject(new Error(`pnpm install exited with code ${code}`));
123
+ console.error(`git clone exited with code ${code}`);
124
+ reject(new Error(`git clone exited with code ${code}`));
75
125
  } else {
76
126
  resolve();
77
127
  }
78
128
  });
79
129
  });
80
- };
81
- export const buildNpmPackages = async (packageSpecifiers) => {
82
- console.log(`Building packages: ${packageSpecifiers.join(", ")}`);
83
130
 
84
- // get the packagePaths from the packageSpecifiers
85
- const packageLookup = new PnpmPackageLookup({
86
- pnpmLockYamlPath: path.resolve(process.cwd(), 'pnpm-lock.yaml'),
87
- });
88
- const packagePaths = await Promise.all(packageSpecifiers.map(async (packageSpecifier) => {
89
- return await packageLookup.getPackageNameBySpecifier(packageSpecifier);
90
- }));
131
+ // pnpm install in local directory
132
+ await Promise.all(packageSpecifiers.map(async (packageSpecifier) => {
133
+ const packageBasename = path.basename(packageSpecifier.replace("github:", ""));
134
+ const packagePath = path.resolve(packagesDir, packageBasename);
91
135
 
92
- const results = [];
93
- let allSuccessful = true;
136
+ await new Promise((resolveInstall, rejectInstall) => {
137
+ const cp = child_process.spawn("pnpm", ["install"], {
138
+ stdio: "inherit",
139
+ cwd: packagePath,
140
+ env: { ...process.env },
141
+ });
142
+
143
+ cp.on("error", (error) => {
144
+ console.error(
145
+ `Error executing pnpm install for ${packageBasename}: ${error.stack}`
146
+ );
147
+ rejectInstall(error);
148
+ });
149
+ cp.on("close", (code) => {
150
+ if (code !== 0) {
151
+ rejectInstall(new Error(`pnpm install exited with code ${code}`));
152
+ } else {
153
+ resolveInstall();
154
+ }
155
+ });
156
+ });
94
157
 
95
- for (const packagePath of packagePaths) {
96
- console.log(`Building package: ${packagePath}`);
97
-
98
- try {
99
- const p = path.resolve(process.cwd(), 'node_modules', packagePath);
158
+ // pnpm build
159
+ // note: this is advisory and allowed to fail
160
+ await new Promise((resolveSpawn, rejectSpawn) => {
100
161
  const cp = child_process.spawn("pnpm", ["build"], {
101
162
  stdio: "inherit",
102
- cwd: p,
163
+ cwd: packagePath,
103
164
  env: { ...process.env },
104
165
  });
105
166
 
106
- const result = await new Promise((resolveSpawn, rejectSpawn) => {
107
- cp.on("error", (error) => {
108
- console.error(`Error executing pnpm build for ${packagePath}: ${error.message}`);
109
- resolveSpawn({ success: false, package: packagePath, error: error.message });
167
+ cp.on("error", (error) => {
168
+ console.error(
169
+ `Error executing pnpm build for ${packageBasename}: ${error.stack}`
170
+ );
171
+ resolveSpawn({
172
+ success: false,
173
+ package: packageBasename,
174
+ error: error.stack,
110
175
  });
176
+ });
177
+ cp.on("close", (code) => {
178
+ if (code !== 0) {
179
+ console.error(
180
+ `pnpm build for ${packageBasename} exited with code ${code}`
181
+ );
182
+ resolveSpawn({
183
+ success: false,
184
+ package: packageBasename,
185
+ error: `exited with code ${code}`,
186
+ });
187
+ } else {
188
+ console.log(`Build completed successfully for ${packageBasename}`);
189
+ resolveSpawn({ success: true, package: packageBasename });
190
+ }
191
+ });
192
+ });
111
193
 
112
- cp.on("close", (code) => {
113
- if (code !== 0) {
114
- console.error(`pnpm build for ${packagePath} exited with code ${code}`);
115
- resolveSpawn({ success: false, package: packagePath, error: `exited with code ${code}` });
116
- } else {
117
- console.log(`Build completed successfully for ${packagePath}`);
118
- resolveSpawn({ success: true, package: packagePath });
119
- }
120
- });
194
+ // pnpm install to app
195
+ await new Promise((resolveInstall, rejectInstall) => {
196
+ const cp = child_process.spawn("pnpm", ["install", `file:${packagePath}`], {
197
+ stdio: "inherit",
198
+ cwd: process.cwd(),
199
+ env: { ...process.env },
121
200
  });
122
201
 
123
- results.push(result);
124
- if (!result.success) {
125
- allSuccessful = false;
126
- }
127
- } catch (error) {
128
- console.log('error', error);
129
- results.push({ success: false, package: packagePath, error: error.message });
130
- allSuccessful = false;
131
- }
132
- }
202
+ cp.on("error", (error) => {
203
+ console.error(
204
+ `Error executing pnpm install for ${packageBasename}: ${error.stack}`
205
+ );
206
+ rejectInstall(error);
207
+ });
208
+ cp.on("close", (code) => {
209
+ if (code !== 0) {
210
+ rejectInstall(new Error(`pnpm install exited with code ${code}`));
211
+ } else {
212
+ resolveInstall();
213
+ }
214
+ });
215
+ });
216
+ }));
217
+ };
218
+ const installNpmBasicPackages = async (packageSpecifiers) => {
219
+ // packageSpecifiers = uniquify(packageSpecifiers);
220
+ console.log(`Installing npm basic packages: ${packageSpecifiers.join(", ")}`);
221
+
222
+ const cp = child_process.spawn("pnpm", ["install", ...packageSpecifiers], {
223
+ stdio: "inherit",
224
+ cwd: process.cwd(),
225
+ });
133
226
 
134
- return { success: allSuccessful, results };
227
+ return new Promise((resolve, reject) => {
228
+ cp.on("error", (error) => {
229
+ reject(error);
230
+ });
231
+
232
+ cp.on("close", (code) => {
233
+ if (code !== 0) {
234
+ reject(new Error(`pnpm install exited with code ${code}`));
235
+ } else {
236
+ resolve();
237
+ }
238
+ });
239
+ });
135
240
  };
241
+ export const installNpmPackages = async (packageSpecifiers) => {
242
+ console.log(`Installing packages: ${packageSpecifiers.join(", ")}`);
243
+
244
+ const { github, npm } = sortNpmPackages(packageSpecifiers);
245
+ await Promise.all([
246
+ installNpmGithubPackages(github),
247
+ installNpmBasicPackages(npm)
248
+ ]);
249
+ };
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { mastra } from "..";
4
- import readline from "readline";
5
-
6
- // Create readline interface
7
- const rl = readline.createInterface({
8
- input: process.stdin,
9
- output: process.stdout,
10
- });
11
-
12
- console.log("🌤️ Weather Agent CLI");
13
- console.log('Type your weather questions (e.g., "What\'s the weather in London today?")');
14
- console.log('Type "exit" to quit\n');
15
-
16
- // Main interaction loop
17
- function askQuestion() {
18
- rl.question("> ", async (input) => {
19
- if (input.toLowerCase() === "exit") {
20
- console.log("Goodbye! 👋");
21
- rl.close();
22
- return;
23
- }
24
-
25
- try {
26
- console.log("Fetching weather information...");
27
- const response = await mastra.run({
28
- agent: "weatherAgent",
29
- input,
30
- });
31
-
32
- console.log("\nResponse:");
33
- console.log(response);
34
- console.log(); // Empty line for better readability
35
- } catch (error) {
36
- console.error("Error:", error.message);
37
- }
38
-
39
- // Continue the conversation
40
- askQuestion();
41
- });
42
- }
43
-
44
- // Start the interaction
45
- askQuestion();