@elizaos/cli 1.3.0 → 1.3.2

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 (148) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-2CUIHNPL.js → chunk-5GUS4CFO.js} +7 -2
  3. package/dist/{chunk-2ALAPQLV.js → chunk-E6XYTE3A.js} +296 -311
  4. package/dist/chunk-GXWWPFBO.js +39 -0
  5. package/dist/{chunk-I77ZRNYO.js → chunk-T2QDIXGU.js} +2 -2
  6. package/dist/commands/agent/actions/index.d.ts +5 -0
  7. package/dist/commands/agent/actions/index.js +2 -2
  8. package/dist/commands/agent/index.d.ts +2 -2
  9. package/dist/commands/agent/index.js +2 -2
  10. package/dist/commands/create/actions/index.js +3 -3
  11. package/dist/commands/create/index.js +4 -4
  12. package/dist/commands/shared/index.d.ts +11 -28
  13. package/dist/commands/shared/index.js +7 -3
  14. package/dist/index.js +541 -450
  15. package/dist/{registry-N626N4VG.js → registry-433S5F3Y.js} +2 -2
  16. package/dist/templates/plugin-quick-starter/.gitignore +66 -0
  17. package/dist/templates/plugin-quick-starter/.npmignore +5 -0
  18. package/dist/templates/plugin-quick-starter/package.json +11 -3
  19. package/dist/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  20. package/dist/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  21. package/dist/templates/plugin-quick-starter/src/plugin.ts +7 -13
  22. package/dist/templates/plugin-starter/.gitignore +66 -0
  23. package/dist/templates/plugin-starter/.npmignore +5 -0
  24. package/dist/templates/plugin-starter/README.md +1 -1
  25. package/dist/templates/plugin-starter/package.json +11 -3
  26. package/dist/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  27. package/dist/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  28. package/dist/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  29. package/dist/templates/plugin-starter/src/plugin.ts +18 -22
  30. package/dist/templates/project-starter/.gitignore +57 -0
  31. package/dist/templates/project-starter/.npmignore +11 -0
  32. package/dist/templates/project-starter/README.md +1 -1
  33. package/dist/templates/project-starter/package.json +4 -4
  34. package/dist/templates/project-starter/src/__tests__/env.test.ts +3 -1
  35. package/dist/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  36. package/dist/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  37. package/dist/templates/project-starter/tsup.config.ts +2 -1
  38. package/dist/templates/project-tee-starter/.dockerignore +64 -14
  39. package/dist/templates/project-tee-starter/.gitignore +57 -0
  40. package/dist/templates/project-tee-starter/.npmignore +6 -0
  41. package/dist/templates/project-tee-starter/Dockerfile +9 -5
  42. package/dist/templates/project-tee-starter/GUIDE.md +103 -42
  43. package/dist/templates/project-tee-starter/README.md +39 -19
  44. package/dist/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  45. package/dist/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  46. package/dist/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  47. package/dist/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  48. package/dist/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  49. package/dist/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  50. package/dist/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  51. package/dist/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  52. package/dist/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  53. package/dist/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  54. package/dist/templates/project-tee-starter/docker-compose.yaml +5 -2
  55. package/dist/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  56. package/dist/templates/project-tee-starter/package.json +34 -14
  57. package/dist/templates/project-tee-starter/postcss.config.js +3 -0
  58. package/dist/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  59. package/dist/templates/project-tee-starter/scripts/test-all.sh +82 -0
  60. package/dist/templates/project-tee-starter/src/frontend/index.css +106 -0
  61. package/dist/templates/project-tee-starter/src/frontend/index.html +20 -0
  62. package/dist/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  63. package/dist/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  64. package/dist/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  65. package/dist/templates/project-tee-starter/src/index.ts +6 -6
  66. package/dist/templates/project-tee-starter/src/plugin.ts +209 -59
  67. package/dist/templates/project-tee-starter/tailwind.config.js +62 -0
  68. package/dist/templates/project-tee-starter/tsconfig.build.json +2 -2
  69. package/dist/templates/project-tee-starter/tsconfig.json +8 -5
  70. package/dist/templates/project-tee-starter/tsup.config.ts +3 -2
  71. package/dist/templates/project-tee-starter/vite.config.ts +39 -0
  72. package/dist/url-utils-CKc_Ebt_.d.ts +35 -0
  73. package/dist/{utils-H66532NB.js → utils-DBLSDYBF.js} +2 -2
  74. package/package.json +12 -7
  75. package/templates/plugin-quick-starter/.gitignore +66 -0
  76. package/templates/plugin-quick-starter/.npmignore +5 -0
  77. package/templates/plugin-quick-starter/package.json +11 -3
  78. package/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  79. package/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  80. package/templates/plugin-quick-starter/src/plugin.ts +7 -13
  81. package/templates/plugin-starter/.gitignore +66 -0
  82. package/templates/plugin-starter/.npmignore +5 -0
  83. package/templates/plugin-starter/README.md +1 -1
  84. package/templates/plugin-starter/package.json +11 -3
  85. package/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  86. package/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  87. package/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  88. package/templates/plugin-starter/src/plugin.ts +18 -22
  89. package/templates/project-starter/.gitignore +57 -0
  90. package/templates/project-starter/.npmignore +11 -0
  91. package/templates/project-starter/README.md +1 -1
  92. package/templates/project-starter/package.json +4 -4
  93. package/templates/project-starter/src/__tests__/env.test.ts +3 -1
  94. package/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  95. package/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  96. package/templates/project-starter/tsup.config.ts +2 -1
  97. package/templates/project-tee-starter/.dockerignore +64 -14
  98. package/templates/project-tee-starter/.gitignore +57 -0
  99. package/templates/project-tee-starter/.npmignore +6 -0
  100. package/templates/project-tee-starter/Dockerfile +9 -5
  101. package/templates/project-tee-starter/GUIDE.md +103 -42
  102. package/templates/project-tee-starter/README.md +39 -19
  103. package/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  104. package/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  105. package/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  106. package/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  107. package/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  108. package/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  109. package/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  110. package/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  111. package/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  112. package/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  113. package/templates/project-tee-starter/docker-compose.yaml +5 -2
  114. package/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  115. package/templates/project-tee-starter/package.json +34 -14
  116. package/templates/project-tee-starter/postcss.config.js +3 -0
  117. package/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  118. package/templates/project-tee-starter/scripts/test-all.sh +82 -0
  119. package/templates/project-tee-starter/src/frontend/index.css +106 -0
  120. package/templates/project-tee-starter/src/frontend/index.html +20 -0
  121. package/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  122. package/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  123. package/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  124. package/templates/project-tee-starter/src/index.ts +6 -6
  125. package/templates/project-tee-starter/src/plugin.ts +209 -59
  126. package/templates/project-tee-starter/tailwind.config.js +62 -0
  127. package/templates/project-tee-starter/tsconfig.build.json +2 -2
  128. package/templates/project-tee-starter/tsconfig.json +8 -5
  129. package/templates/project-tee-starter/tsup.config.ts +3 -2
  130. package/templates/project-tee-starter/vite.config.ts +39 -0
  131. package/dist/chunk-4O6EZU37.js +0 -14
  132. package/dist/migration-guides/advanced-migration-guide.md +0 -459
  133. package/dist/migration-guides/completion-requirements.md +0 -379
  134. package/dist/migration-guides/integrated-migration-loop.md +0 -392
  135. package/dist/migration-guides/migration-guide.md +0 -712
  136. package/dist/migration-guides/prompt-and-generation-guide.md +0 -702
  137. package/dist/migration-guides/state-and-providers-guide.md +0 -544
  138. package/dist/migration-guides/testing-guide.md +0 -1021
  139. package/dist/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  140. package/dist/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  141. package/dist/templates/plugin-starter/dist/index.js +0 -387
  142. package/dist/templates/plugin-starter/dist/index.js.map +0 -1
  143. package/templates/plugin-starter/dist/.vite/manifest.json +0 -11
  144. package/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  145. package/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  146. package/templates/plugin-starter/dist/index.d.ts +0 -14
  147. package/templates/plugin-starter/dist/index.js +0 -387
  148. package/templates/plugin-starter/dist/index.js.map +0 -1
@@ -1,182 +1,609 @@
1
1
  import { describe, expect, it, spyOn, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
2
2
  import { starterPlugin, StarterService } from '../index';
3
- import { ModelType, logger } from '@elizaos/core';
3
+ import {
4
+ type IAgentRuntime,
5
+ type Memory,
6
+ type State,
7
+ type Content,
8
+ type HandlerCallback,
9
+ EventType,
10
+ type MessagePayload,
11
+ type WorldPayload,
12
+ type UUID,
13
+ ModelType,
14
+ logger,
15
+ } from '@elizaos/core';
4
16
  import dotenv from 'dotenv';
17
+ import { z } from 'zod';
18
+ import {
19
+ createMockRuntime,
20
+ createTestMemory,
21
+ createTestState,
22
+ createUUID,
23
+ testFixtures,
24
+ assertSpyCalledWith,
25
+ } from './test-utils';
5
26
 
6
27
  // Setup environment variables
7
28
  dotenv.config();
8
29
 
9
- // Need to spy on logger for documentation
10
- beforeAll(() => {
11
- spyOn(logger, 'info');
12
- spyOn(logger, 'error');
13
- spyOn(logger, 'warn');
14
- spyOn(logger, 'debug');
30
+ describe('Plugin Configuration', () => {
31
+ it('should have correct plugin metadata', () => {
32
+ // Check that plugin has required metadata (values will change when template is used)
33
+ expect(starterPlugin.name).toBeDefined();
34
+ expect(starterPlugin.name).toMatch(/^[a-z0-9-]+$/); // Valid plugin name format
35
+ expect(starterPlugin.description).toBeDefined();
36
+ expect(starterPlugin.description.length).toBeGreaterThan(0);
37
+ expect(starterPlugin.actions).toBeDefined();
38
+ expect(starterPlugin.actions?.length).toBeGreaterThan(0);
39
+ expect(starterPlugin.providers).toBeDefined();
40
+ expect(starterPlugin.providers?.length).toBeGreaterThan(0);
41
+ expect(starterPlugin.services).toBeDefined();
42
+ expect(starterPlugin.services?.length).toBeGreaterThan(0);
43
+ expect(starterPlugin.models).toBeDefined();
44
+ expect(starterPlugin.models?.[ModelType.TEXT_SMALL]).toBeDefined();
45
+ expect(starterPlugin.models?.[ModelType.TEXT_LARGE]).toBeDefined();
46
+ expect(starterPlugin.routes).toBeDefined();
47
+ expect(starterPlugin.routes?.length).toBeGreaterThan(0);
48
+ expect(starterPlugin.events).toBeDefined();
49
+ });
50
+
51
+ it('should initialize with valid configuration', async () => {
52
+ const runtime = createMockRuntime();
53
+ const config = { EXAMPLE_PLUGIN_VARIABLE: 'test-value' };
54
+
55
+ if (starterPlugin.init) {
56
+ await starterPlugin.init(config, runtime);
57
+ }
58
+
59
+ // Note: registerService is not called in init, services are registered later
60
+ // This is handled by the runtime during plugin loading
61
+ expect(process.env.EXAMPLE_PLUGIN_VARIABLE).toBe('test-value');
62
+ });
63
+
64
+ it('should handle initialization without config', async () => {
65
+ const runtime = createMockRuntime();
66
+
67
+ if (starterPlugin.init) {
68
+ // Init should not throw even with empty config
69
+ await starterPlugin.init({}, runtime);
70
+ }
71
+ });
72
+
73
+ it('should throw error for invalid configuration', async () => {
74
+ const runtime = createMockRuntime({
75
+ getSetting: () => 'invalid-json',
76
+ });
77
+
78
+ const zodError = new z.ZodError([
79
+ {
80
+ code: 'invalid_type',
81
+ expected: 'object',
82
+ received: 'string',
83
+ path: [],
84
+ message: 'Expected object, received string',
85
+ },
86
+ ]);
87
+
88
+ if (starterPlugin.init) {
89
+ // The init function validates the config but doesn't throw for invalid JSON in getSetting
90
+ // It only validates the config passed to init
91
+ await starterPlugin.init({}, runtime);
92
+ }
93
+ });
15
94
  });
16
95
 
17
- afterAll(() => {
18
- // No global restore needed in bun:test
96
+ describe('Hello World Action', () => {
97
+ let runtime: IAgentRuntime;
98
+ const helloWorldAction = starterPlugin.actions?.[0];
99
+
100
+ beforeEach(() => {
101
+ runtime = createMockRuntime();
102
+ });
103
+
104
+ it('should have hello world action', () => {
105
+ expect(helloWorldAction).toBeDefined();
106
+ expect(helloWorldAction?.name).toBe('HELLO_WORLD');
107
+ });
108
+
109
+ it('should always validate messages (current implementation)', async () => {
110
+ if (!helloWorldAction?.validate) {
111
+ throw new Error('Hello world action validate not found');
112
+ }
113
+
114
+ // The simple implementation always returns true
115
+ const testCases = [
116
+ { text: 'say hello', expected: true },
117
+ { text: 'hello world', expected: true },
118
+ { text: 'goodbye', expected: true },
119
+ { text: '', expected: true },
120
+ { text: ' ', expected: true },
121
+ ];
122
+
123
+ for (const { text, expected } of testCases) {
124
+ const message = createTestMemory({
125
+ content: { text, source: 'test' },
126
+ });
127
+ const isValid = await helloWorldAction.validate(runtime, message);
128
+ expect(isValid).toBe(expected);
129
+ }
130
+ });
131
+
132
+ it('should validate even without text content', async () => {
133
+ if (!helloWorldAction?.validate) {
134
+ throw new Error('Hello world action validate not found');
135
+ }
136
+
137
+ const messageWithoutText = createTestMemory({
138
+ content: { source: 'test' } as Content,
139
+ });
140
+
141
+ const isValid = await helloWorldAction.validate(runtime, messageWithoutText);
142
+ // Always returns true in simple implementation
143
+ expect(isValid).toBe(true);
144
+ });
145
+
146
+ it('should properly validate hello messages', async () => {
147
+ if (!helloWorldAction?.validate) {
148
+ throw new Error('Hello world action validate not found');
149
+ }
150
+
151
+ // Test that it accepts hello-related keywords
152
+ const helloMessages = ['hello', 'hi there', 'hey!', 'greetings', 'howdy partner'];
153
+ for (const text of helloMessages) {
154
+ const message = createTestMemory({
155
+ content: { text, source: 'test' },
156
+ });
157
+ const isValid = await helloWorldAction.validate(runtime, message);
158
+ expect(isValid).toBe(true);
159
+ }
160
+
161
+ // Test that it accepts all messages (simple implementation)
162
+ const nonHelloMessages = ['goodbye', 'what is the weather', 'tell me a joke', ''];
163
+ for (const text of nonHelloMessages) {
164
+ const message = createTestMemory({
165
+ content: { text, source: 'test' },
166
+ });
167
+ const isValid = await helloWorldAction.validate(runtime, message);
168
+ expect(isValid).toBe(true);
169
+ }
170
+ });
171
+
172
+ it('should handle hello world action with callback', async () => {
173
+ if (!helloWorldAction?.handler) {
174
+ throw new Error('Hello world action handler not found');
175
+ }
176
+
177
+ const message = createTestMemory({
178
+ content: { text: 'say hello', source: 'test' },
179
+ });
180
+
181
+ let callbackContent: any = null;
182
+ const callback: HandlerCallback = async (content: Content) => {
183
+ callbackContent = content;
184
+ return [];
185
+ };
186
+
187
+ const result = await helloWorldAction.handler(runtime, message, undefined, undefined, callback);
188
+
189
+ // The action returns a simple greeting
190
+ expect(result).toHaveProperty('text', 'Hello world!');
191
+ expect(result).toHaveProperty('success', true);
192
+ expect(result).toHaveProperty('data');
193
+ expect((result as any).data).toHaveProperty('actions', ['HELLO_WORLD']);
194
+ expect((result as any).data).toHaveProperty('source', 'test');
195
+
196
+ // Callback should receive the same greeting
197
+ expect(callbackContent).toBeDefined();
198
+ expect(callbackContent.text).toBe('Hello world!');
199
+ expect(callbackContent.actions).toEqual(['HELLO_WORLD']);
200
+ expect(callbackContent.source).toBe('test');
201
+ });
202
+
203
+ it('should handle errors gracefully', async () => {
204
+ if (!helloWorldAction?.handler) {
205
+ throw new Error('Hello world action handler not found');
206
+ }
207
+
208
+ const message = createTestMemory({
209
+ content: { text: 'say hello', source: 'test' },
210
+ });
211
+
212
+ const errorCallback: HandlerCallback = async () => {
213
+ throw new Error('Callback error');
214
+ };
215
+
216
+ const result = await helloWorldAction.handler(
217
+ runtime,
218
+ message,
219
+ undefined,
220
+ undefined,
221
+ errorCallback
222
+ );
223
+
224
+ expect(result).toHaveProperty('success', false);
225
+ expect(result).toHaveProperty('error');
226
+ expect((result as any).error?.message).toBe('Callback error');
227
+ // Logger is mocked but not set up as a spy in runtime
228
+ expect(logger.error).toHaveBeenCalled();
229
+ });
230
+
231
+ it('should handle missing callback gracefully', async () => {
232
+ if (!helloWorldAction?.handler) {
233
+ throw new Error('Hello world action handler not found');
234
+ }
235
+
236
+ const message = createTestMemory({
237
+ content: { text: 'say hello', source: 'test' },
238
+ });
239
+
240
+ const result = await helloWorldAction.handler(
241
+ runtime,
242
+ message,
243
+ undefined,
244
+ undefined,
245
+ undefined
246
+ );
247
+
248
+ // The action returns a simple greeting
249
+ expect(result).toHaveProperty('text', 'Hello world!');
250
+ expect(result).toHaveProperty('success', true);
251
+ });
252
+
253
+ it('should handle state parameter correctly', async () => {
254
+ if (!helloWorldAction?.handler) {
255
+ throw new Error('Hello world action handler not found');
256
+ }
257
+
258
+ const message = createTestMemory({
259
+ content: { text: 'say hello', source: 'test' },
260
+ });
261
+
262
+ const state = createTestState({
263
+ values: { customValue: 'test-state' },
264
+ });
265
+
266
+ const result = await helloWorldAction.handler(runtime, message, state, undefined, undefined);
267
+
268
+ expect(result).toHaveProperty('success', true);
269
+ });
19
270
  });
20
271
 
21
- // Create a real runtime for testing
22
- function createRealRuntime() {
23
- const services = new Map();
24
-
25
- // Create a real service instance if needed
26
- const createService = (serviceType: string) => {
27
- if (serviceType === StarterService.serviceType) {
28
- return new StarterService({
29
- character: {
30
- name: 'Test Character',
31
- system: 'You are a helpful assistant for testing.',
32
- },
33
- } as any);
34
- }
35
- return null;
36
- };
37
-
38
- return {
39
- character: {
40
- name: 'Test Character',
41
- system: 'You are a helpful assistant for testing.',
42
- plugins: [],
43
- settings: {},
44
- },
45
- getSetting: (key: string) => null,
46
- models: starterPlugin.models,
47
- db: {
48
- get: async (key: string) => null,
49
- set: async (key: string, value: any) => true,
50
- delete: async (key: string) => true,
51
- getKeys: async (pattern: string) => [],
52
- },
53
- getService: (serviceType: string) => {
54
- // Log the service request for debugging
55
- logger.debug(`Requesting service: ${serviceType}`);
56
-
57
- // Get from cache or create new
58
- if (!services.has(serviceType)) {
59
- logger.debug(`Creating new service: ${serviceType}`);
60
- services.set(serviceType, createService(serviceType));
61
- }
62
-
63
- return services.get(serviceType);
64
- },
65
- registerService: (serviceType: string, service: any) => {
66
- logger.debug(`Registering service: ${serviceType}`);
67
- services.set(serviceType, service);
68
- },
69
- };
70
- }
272
+ describe('Hello World Provider', () => {
273
+ const provider = starterPlugin.providers?.[0];
274
+ let runtime: IAgentRuntime;
71
275
 
72
- describe('Plugin Configuration', () => {
73
- it('should have correct plugin metadata', () => {
74
- expect(starterPlugin.name).toBe('plugin-starter');
75
- expect(starterPlugin.description).toBe('Plugin starter for elizaOS');
76
- expect(starterPlugin.config).toBeDefined();
276
+ beforeEach(() => {
277
+ runtime = createMockRuntime();
77
278
  });
78
279
 
79
- it('should include the EXAMPLE_PLUGIN_VARIABLE in config', () => {
80
- expect(starterPlugin.config).toHaveProperty('EXAMPLE_PLUGIN_VARIABLE');
280
+ it('should have hello world provider', () => {
281
+ expect(provider).toBeDefined();
282
+ expect(provider?.name).toBe('HELLO_WORLD_PROVIDER');
81
283
  });
82
284
 
83
- it('should initialize properly', async () => {
84
- const originalEnv = process.env.EXAMPLE_PLUGIN_VARIABLE;
285
+ it('should provide hello world data', async () => {
286
+ if (!provider?.get) {
287
+ throw new Error('Hello world provider not found');
288
+ }
289
+
290
+ const message = createTestMemory();
291
+ const state = createTestState();
85
292
 
86
- try {
87
- process.env.EXAMPLE_PLUGIN_VARIABLE = 'test-value';
293
+ const result = await provider.get(runtime, message, state);
88
294
 
89
- // Initialize with config - using real runtime
90
- const runtime = createRealRuntime();
295
+ expect(result).toHaveProperty('text', 'I am a provider');
296
+ expect(result).toHaveProperty('data');
297
+ expect(result).toHaveProperty('values');
298
+ expect(result.data).toEqual({});
299
+ expect(result.values).toEqual({});
300
+ });
91
301
 
92
- if (starterPlugin.init) {
93
- await starterPlugin.init({ EXAMPLE_PLUGIN_VARIABLE: 'test-value' }, runtime as any);
94
- expect(true).toBe(true); // If we got here, init succeeded
95
- }
96
- } finally {
97
- process.env.EXAMPLE_PLUGIN_VARIABLE = originalEnv;
302
+ it('should provide consistent structure across calls', async () => {
303
+ if (!provider?.get) {
304
+ throw new Error('Hello world provider not found');
98
305
  }
306
+
307
+ const message = createTestMemory();
308
+ const state = createTestState();
309
+
310
+ const result1 = await provider.get(runtime, message, state);
311
+ const result2 = await provider.get(runtime, message, state);
312
+
313
+ // Simple provider returns consistent static results
314
+ expect(result1.text).toBe('I am a provider');
315
+ expect(result2.text).toBe('I am a provider');
316
+ expect(result1.data).toEqual({});
317
+ expect(result2.data).toEqual({});
318
+ expect(result1.values).toEqual({});
319
+ expect(result2.values).toEqual({});
99
320
  });
100
321
 
101
- it('should have a valid config', () => {
102
- expect(starterPlugin.config).toBeDefined();
103
- if (starterPlugin.config) {
104
- // Check if the config has expected EXAMPLE_PLUGIN_VARIABLE property
105
- expect(Object.keys(starterPlugin.config)).toContain('EXAMPLE_PLUGIN_VARIABLE');
322
+ it('should handle different input states', async () => {
323
+ if (!provider?.get) {
324
+ throw new Error('Hello world provider not found');
106
325
  }
326
+
327
+ const message = createTestMemory({ content: { text: 'different message' } });
328
+ const customState = createTestState({
329
+ values: { custom: 'value' },
330
+ data: { custom: 'data' },
331
+ });
332
+
333
+ const result = await provider.get(runtime, message, customState);
334
+
335
+ // Provider output is static in simple implementation
336
+ expect(result.text).toBe('I am a provider');
337
+ expect(result.data).toEqual({});
338
+ expect(result.values).toEqual({});
107
339
  });
108
340
  });
109
341
 
110
- describe('Plugin Models', () => {
111
- it('should have TEXT_SMALL model defined', () => {
112
- expect(starterPlugin.models?.[ModelType.TEXT_SMALL]).toBeDefined();
113
- if (starterPlugin.models) {
114
- expect(typeof starterPlugin.models[ModelType.TEXT_SMALL]).toBe('function');
342
+ describe('Model Handlers', () => {
343
+ let runtime: IAgentRuntime;
344
+
345
+ beforeEach(() => {
346
+ runtime = createMockRuntime();
347
+ });
348
+
349
+ it('should handle TEXT_SMALL model', async () => {
350
+ const handler = starterPlugin.models?.[ModelType.TEXT_SMALL];
351
+ if (!handler) {
352
+ throw new Error('TEXT_SMALL model handler not found');
115
353
  }
354
+
355
+ const result = await handler(
356
+ {
357
+ params: {
358
+ prompt: 'Test prompt',
359
+ temperature: 0.7,
360
+ },
361
+ },
362
+ runtime
363
+ );
364
+
365
+ expect(result).toContain('Never gonna give you up');
116
366
  });
117
367
 
118
- it('should have TEXT_LARGE model defined', () => {
119
- expect(starterPlugin.models?.[ModelType.TEXT_LARGE]).toBeDefined();
120
- if (starterPlugin.models) {
121
- expect(typeof starterPlugin.models[ModelType.TEXT_LARGE]).toBe('function');
368
+ it('should handle TEXT_LARGE model with custom parameters', async () => {
369
+ const handler = starterPlugin.models?.[ModelType.TEXT_LARGE];
370
+ if (!handler) {
371
+ throw new Error('TEXT_LARGE model handler not found');
122
372
  }
373
+
374
+ const result = await handler(
375
+ {
376
+ params: {
377
+ prompt: 'Test prompt with custom settings',
378
+ temperature: 0.9,
379
+ maxTokens: 500,
380
+ },
381
+ },
382
+ runtime
383
+ );
384
+
385
+ expect(result).toContain('Never gonna make you cry');
123
386
  });
124
387
 
125
- it('should return a response from TEXT_SMALL model', async () => {
126
- if (starterPlugin.models?.[ModelType.TEXT_SMALL]) {
127
- const runtime = createRealRuntime();
128
- const result = await starterPlugin.models[ModelType.TEXT_SMALL](runtime as any, {
129
- prompt: 'test',
130
- });
388
+ it('should handle empty prompt', async () => {
389
+ const handler = starterPlugin.models?.[ModelType.TEXT_SMALL];
390
+ if (!handler) {
391
+ throw new Error('TEXT_SMALL model handler not found');
392
+ }
393
+
394
+ const result = await handler(
395
+ {
396
+ params: {
397
+ prompt: '',
398
+ temperature: 0.7,
399
+ },
400
+ },
401
+ runtime
402
+ );
131
403
 
132
- // Check that we get a non-empty string response
133
- expect(result).toBeTruthy();
134
- expect(typeof result).toBe('string');
135
- expect(result.length).toBeGreaterThan(10);
404
+ expect(typeof result).toBe('string');
405
+ expect(result.length).toBeGreaterThan(0);
406
+ });
407
+
408
+ it('should handle missing parameters', async () => {
409
+ const handler = starterPlugin.models?.[ModelType.TEXT_LARGE];
410
+ if (!handler) {
411
+ throw new Error('TEXT_LARGE model handler not found');
136
412
  }
413
+
414
+ const result = await handler(
415
+ {
416
+ params: {
417
+ prompt: 'Test prompt',
418
+ },
419
+ },
420
+ runtime
421
+ );
422
+
423
+ expect(typeof result).toBe('string');
424
+ expect(result.length).toBeGreaterThan(0);
425
+ });
426
+ });
427
+
428
+ describe('API Routes', () => {
429
+ let runtime: IAgentRuntime;
430
+
431
+ beforeEach(() => {
432
+ runtime = createMockRuntime();
433
+ });
434
+
435
+ it('should handle hello world route', async () => {
436
+ const helloRoute = starterPlugin.routes?.find((r) => r.name === 'hello-world-route');
437
+ if (!helloRoute?.handler) {
438
+ throw new Error('Hello world route handler not found');
439
+ }
440
+
441
+ const mockRes = {
442
+ json: (data: any) => {
443
+ mockRes._jsonData = data;
444
+ },
445
+ _jsonData: null as any,
446
+ };
447
+
448
+ await helloRoute.handler({}, mockRes, runtime);
449
+
450
+ expect(mockRes._jsonData).toBeDefined();
451
+ expect(mockRes._jsonData.message).toBe('Hello World!');
452
+ });
453
+
454
+ it('should validate route configuration', () => {
455
+ const helloRoute = starterPlugin.routes?.find((r) => r.name === 'hello-world-route');
456
+
457
+ expect(helloRoute).toBeDefined();
458
+ expect(helloRoute?.path).toBe('/helloworld');
459
+ expect(helloRoute?.type).toBe('GET');
460
+ // Routes don't have a public property in the current implementation
461
+ expect(helloRoute?.handler).toBeDefined();
462
+ });
463
+
464
+ it('should handle request with query parameters', async () => {
465
+ const helloRoute = starterPlugin.routes?.find((r) => r.name === 'hello-world-route');
466
+ if (!helloRoute?.handler) {
467
+ throw new Error('Hello world route handler not found');
468
+ }
469
+
470
+ const mockReq = {
471
+ query: {
472
+ name: 'Test User',
473
+ },
474
+ };
475
+
476
+ const mockRes = {
477
+ json: (data: any) => {
478
+ mockRes._jsonData = data;
479
+ },
480
+ _jsonData: null as any,
481
+ };
482
+
483
+ await helloRoute.handler(mockReq, mockRes, runtime);
484
+
485
+ expect(mockRes._jsonData).toBeDefined();
486
+ expect(mockRes._jsonData.message).toBe('Hello World!');
487
+ });
488
+ });
489
+
490
+ describe('Event Handlers', () => {
491
+ beforeEach(() => {
492
+ // Clear logger spy calls
493
+ (logger.debug as any).calls = [];
494
+ (logger.info as any).calls = [];
495
+ (logger.error as any).calls = [];
496
+ });
497
+
498
+ it('should log when MESSAGE_RECEIVED event is triggered', async () => {
499
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
500
+ if (!handler) {
501
+ throw new Error('MESSAGE_RECEIVED event handler not found');
502
+ }
503
+
504
+ const payload = testFixtures.messagePayload();
505
+ await handler(payload);
506
+
507
+ expect(logger.debug).toHaveBeenCalled();
508
+ });
509
+
510
+ it('should handle malformed event payload', async () => {
511
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
512
+ if (!handler) {
513
+ throw new Error('MESSAGE_RECEIVED event handler not found');
514
+ }
515
+
516
+ const malformedPayload = {
517
+ // Missing required fields
518
+ runtime: createMockRuntime(),
519
+ };
520
+
521
+ // Should not throw
522
+ // Handler doesn't actually use the payload, just logs
523
+ await handler(malformedPayload as any);
524
+ });
525
+
526
+ it('should handle event with empty message content', async () => {
527
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
528
+ if (!handler) {
529
+ throw new Error('MESSAGE_RECEIVED event handler not found');
530
+ }
531
+
532
+ const payload = testFixtures.messagePayload({
533
+ content: {},
534
+ });
535
+
536
+ await handler(payload);
537
+ expect(logger.debug).toHaveBeenCalled();
137
538
  });
138
539
  });
139
540
 
140
541
  describe('StarterService', () => {
141
- it('should start the service', async () => {
142
- const runtime = createRealRuntime();
143
- const startResult = await StarterService.start(runtime as any);
542
+ let runtime: IAgentRuntime;
144
543
 
145
- expect(startResult).toBeDefined();
146
- expect(startResult.constructor.name).toBe('StarterService');
544
+ beforeEach(() => {
545
+ runtime = createMockRuntime();
546
+ // Clear logger spy calls
547
+ (logger.info as any).calls = [];
548
+ (logger.error as any).calls = [];
549
+ });
147
550
 
148
- // Test real functionality - check stop method is available
149
- expect(typeof startResult.stop).toBe('function');
551
+ it('should start the service', async () => {
552
+ const service = await StarterService.start(runtime);
553
+ expect(service).toBeInstanceOf(StarterService);
554
+ expect(logger.info).toHaveBeenCalled();
150
555
  });
151
556
 
152
- it('should stop the service', async () => {
153
- const runtime = createRealRuntime();
557
+ it('should have correct service type', () => {
558
+ expect(StarterService.serviceType).toBe('starter');
559
+ });
154
560
 
155
- // Register a real service first
156
- const service = new StarterService(runtime as any);
157
- runtime.registerService(StarterService.serviceType, service);
561
+ it('should stop service correctly', async () => {
562
+ // Start service
563
+ const service = await StarterService.start(runtime);
158
564
 
159
- // Spy on the real service's stop method
160
- const stopSpy = spyOn(service, 'stop');
565
+ // Create a new runtime with the service registered
566
+ const runtimeWithService = createMockRuntime({
567
+ getService: () => service as any,
568
+ });
161
569
 
162
- // Call the static stop method
163
- await StarterService.stop(runtime as any);
570
+ // Stop service
571
+ await StarterService.stop(runtimeWithService);
572
+ expect(logger.info).toHaveBeenCalled();
573
+ });
574
+
575
+ it('should throw error when stopping non-existent service', async () => {
576
+ const emptyRuntime = createMockRuntime({
577
+ getService: () => null,
578
+ });
164
579
 
165
- // Verify the service's stop method was called
166
- expect(stopSpy).toHaveBeenCalled();
580
+ await expect(StarterService.stop(emptyRuntime)).rejects.toThrow('Starter service not found');
167
581
  });
168
582
 
169
- it('should throw an error when stopping a non-existent service', async () => {
170
- const runtime = createRealRuntime();
171
- // Don't register a service, so getService will return null
583
+ it('should handle multiple start/stop cycles', async () => {
584
+ // First cycle
585
+ const service1 = await StarterService.start(runtime);
586
+ expect(service1).toBeInstanceOf(StarterService);
172
587
 
173
- // We'll patch the getService function to ensure it returns null
174
- const originalGetService = runtime.getService;
175
- runtime.getService = () => null;
588
+ const runtimeWithService1 = createMockRuntime({
589
+ getService: () => service1 as any,
590
+ });
591
+ await StarterService.stop(runtimeWithService1);
176
592
 
177
- await expect(StarterService.stop(runtime as any)).rejects.toThrow('Starter service not found');
593
+ // Second cycle
594
+ const service2 = await StarterService.start(runtime);
595
+ expect(service2).toBeInstanceOf(StarterService);
596
+
597
+ const runtimeWithService2 = createMockRuntime({
598
+ getService: () => service2 as any,
599
+ });
600
+ await StarterService.stop(runtimeWithService2);
601
+ });
178
602
 
179
- // Restore original getService function
180
- runtime.getService = originalGetService;
603
+ it('should provide capability description', async () => {
604
+ const service = await StarterService.start(runtime);
605
+ expect(service.capabilityDescription).toBe(
606
+ 'This is a starter service which is attached to the agent through the starter plugin.'
607
+ );
181
608
  });
182
609
  });