@elizaos/plugin-ngrok 2.0.0-beta.1 → 2.0.11-beta.7

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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -294
  3. package/package.json +24 -7
  4. package/src/__tests__/test-utils.ts +2 -2
  5. package/src/__tests__/unit/environment.test.ts +1 -1
  6. package/dist/__tests__/NgrokTestSuite.d.ts +0 -6
  7. package/dist/__tests__/NgrokTestSuite.d.ts.map +0 -1
  8. package/dist/__tests__/NgrokTestSuite.js +0 -92
  9. package/dist/__tests__/NgrokTestSuite.js.map +0 -1
  10. package/dist/actions/get-tunnel-status.d.ts +0 -4
  11. package/dist/actions/get-tunnel-status.d.ts.map +0 -1
  12. package/dist/actions/get-tunnel-status.js +0 -186
  13. package/dist/actions/get-tunnel-status.js.map +0 -1
  14. package/dist/actions/start-tunnel.d.ts +0 -4
  15. package/dist/actions/start-tunnel.d.ts.map +0 -1
  16. package/dist/actions/start-tunnel.js +0 -221
  17. package/dist/actions/start-tunnel.js.map +0 -1
  18. package/dist/actions/stop-tunnel.d.ts +0 -4
  19. package/dist/actions/stop-tunnel.d.ts.map +0 -1
  20. package/dist/actions/stop-tunnel.js +0 -174
  21. package/dist/actions/stop-tunnel.js.map +0 -1
  22. package/dist/environment.d.ts +0 -12
  23. package/dist/environment.d.ts.map +0 -1
  24. package/dist/environment.js +0 -68
  25. package/dist/environment.js.map +0 -1
  26. package/dist/index.d.ts +0 -13
  27. package/dist/index.d.ts.map +0 -1
  28. package/dist/index.js +0 -29
  29. package/dist/index.js.map +0 -1
  30. package/dist/services/NgrokService.d.ts +0 -30
  31. package/dist/services/NgrokService.d.ts.map +0 -1
  32. package/dist/services/NgrokService.js +0 -333
  33. package/dist/services/NgrokService.js.map +0 -1
  34. package/src/__tests__/e2e/real-ngrok.test.ts +0 -543
  35. package/src/__tests__/unit/actions.test.ts +0 -402
  36. package/src/actions/get-tunnel-status.ts +0 -218
  37. package/src/actions/start-tunnel.ts +0 -255
  38. package/src/actions/stop-tunnel.ts +0 -203
@@ -1,402 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
2
- import type { HandlerCallback, IAgentRuntime, Memory, State } from '@elizaos/core';
3
-
4
- type SimpleMock<TArgs extends readonly unknown[] = unknown[], TReturn = unknown> = ((
5
- ...args: TArgs
6
- ) => TReturn) & {
7
- calls: TArgs[];
8
- _returnValue: TReturn | undefined;
9
- _implementation: ((...args: unknown[]) => TReturn) | null;
10
- mockReturnValue: (value: TReturn) => SimpleMock<TArgs, TReturn>;
11
- mockResolvedValue: (value: Awaited<TReturn>) => SimpleMock<TArgs, TReturn>;
12
- mockRejectedValue: (reason: unknown) => SimpleMock<TArgs, TReturn>;
13
- mockImplementation: (impl: (...args: unknown[]) => TReturn) => SimpleMock<TArgs, TReturn>;
14
- mock: { calls: TArgs[]; results: unknown[] };
15
- };
16
-
17
- // Use local mock implementation until core test-utils build issue is resolved
18
- const mock = <const TArgs extends readonly unknown[] = unknown[], TReturn = unknown>(): SimpleMock<
19
- TArgs,
20
- TReturn
21
- > => {
22
- const calls: TArgs[] = [];
23
- const fn = ((...args: TArgs) => {
24
- calls.push(args);
25
- if (typeof fn._implementation === 'function') {
26
- return fn._implementation(...args);
27
- }
28
- return fn._returnValue as TReturn;
29
- }) as SimpleMock<TArgs, TReturn>;
30
- fn.calls = calls;
31
- fn._returnValue = undefined;
32
- fn._implementation = null;
33
- fn.mockReturnValue = (value: TReturn) => {
34
- fn._returnValue = value;
35
- fn._implementation = null;
36
- return fn;
37
- };
38
- fn.mockResolvedValue = (value: Awaited<TReturn>) => {
39
- fn._returnValue = Promise.resolve(value) as TReturn;
40
- fn._implementation = null;
41
- return fn;
42
- };
43
- fn.mockRejectedValue = (reason: unknown) => {
44
- fn._returnValue = Promise.reject(reason) as TReturn;
45
- fn._implementation = null;
46
- return fn;
47
- };
48
- fn.mockImplementation = (impl: (...args: unknown[]) => TReturn) => {
49
- fn._implementation = impl;
50
- fn._returnValue = undefined;
51
- return fn;
52
- };
53
- fn.mock = { calls, results: [] };
54
- return fn;
55
- };
56
-
57
- import { getTunnelStatusAction } from '../../actions/get-tunnel-status';
58
- import { startTunnelAction } from '../../actions/start-tunnel';
59
- import { stopTunnelAction } from '../../actions/stop-tunnel';
60
- import { MockNgrokService } from '../mocks/NgrokServiceMock';
61
- import { createMockMemory, createMockRuntime, createMockState } from '../test-utils';
62
-
63
- describe('Ngrok Actions - Validation and Error Handling', () => {
64
- let mockRuntime: IAgentRuntime;
65
- let mockTunnelService: MockNgrokService;
66
- let mockCallback: HandlerCallback;
67
- let mockMemory: Memory;
68
- let mockState: State;
69
- let mockUseModel: ReturnType<typeof mock>;
70
-
71
- beforeEach(() => {
72
- mockTunnelService = new MockNgrokService({} as IAgentRuntime);
73
-
74
- // Reset all mocks to default behavior
75
- mockTunnelService.startTunnel.mockResolvedValue('https://test.ngrok.io');
76
- mockTunnelService.stopTunnel.mockResolvedValue(undefined);
77
- mockTunnelService.isActive.mockReturnValue(false);
78
- mockTunnelService.getStatus.mockReturnValue({
79
- active: false,
80
- url: null,
81
- port: null,
82
- startedAt: null,
83
- provider: 'ngrok',
84
- });
85
-
86
- mockUseModel = mock().mockResolvedValue('{"port": 8080}');
87
- mockRuntime = createMockRuntime({
88
- getService: mock().mockImplementation((name: string) => {
89
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
90
- return mockTunnelService;
91
- }
92
- return null;
93
- }),
94
- useModel: mockUseModel,
95
- });
96
-
97
- mockCallback = mock();
98
- mockMemory = createMockMemory();
99
- mockState = createMockState();
100
- });
101
-
102
- afterEach(() => {
103
- // Clear any mock state between tests
104
- });
105
-
106
- describe('startTunnelAction - Validation', () => {
107
- it('should validate that tunnel service exists', async () => {
108
- const runtimeWithoutService = createMockRuntime({
109
- getService: mock().mockReturnValue(null),
110
- });
111
-
112
- const isValid = await startTunnelAction.validate(runtimeWithoutService, mockMemory);
113
-
114
- expect(isValid).toBe(false);
115
- });
116
-
117
- it('should validate when tunnel service is available', async () => {
118
- const isValid = await startTunnelAction.validate(mockRuntime, mockMemory);
119
-
120
- expect(isValid).toBe(true);
121
- });
122
- });
123
-
124
- describe('startTunnelAction - Error Handling', () => {
125
- it('should handle service not available', async () => {
126
- const runtimeWithoutService = createMockRuntime({
127
- getService: mock().mockReturnValue(null),
128
- });
129
- const memory = createMockMemory({
130
- content: { text: 'start tunnel on port 8080' },
131
- });
132
-
133
- const result = await startTunnelAction.handler(
134
- runtimeWithoutService,
135
- memory,
136
- mockState,
137
- {},
138
- mockCallback
139
- );
140
-
141
- expect(result.values?.success).toBe(false);
142
- expect(result.values?.error).toBe('service_unavailable');
143
- expect(mockCallback.calls.length).toBeGreaterThan(0);
144
- });
145
-
146
- it('should handle invalid port numbers gracefully', async () => {
147
- const runtimeWithInvalidPort = createMockRuntime({
148
- getService: mock().mockImplementation((name: string) => {
149
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
150
- return mockTunnelService;
151
- }
152
- return null;
153
- }),
154
- useModel: mock().mockResolvedValue('{"port": -1}'),
155
- });
156
- const memory = createMockMemory({
157
- content: { text: 'start tunnel on port -1' },
158
- });
159
- mockTunnelService.startTunnel.mockResolvedValue('https://test.ngrok.io');
160
-
161
- const result = await startTunnelAction.handler(
162
- runtimeWithInvalidPort,
163
- memory,
164
- mockState,
165
- {},
166
- mockCallback
167
- );
168
-
169
- expect(result.values?.success).toBe(true);
170
- expect(result.values?.tunnelUrl).toContain('ngrok.io');
171
- expect(mockTunnelService.startTunnel.calls.length).toBeGreaterThan(0);
172
- });
173
-
174
- it('should handle port extraction failure', async () => {
175
- mockMemory.content = { text: 'start tunnel' };
176
- mockUseModel.mockResolvedValue('invalid json');
177
- mockTunnelService.startTunnel.mockResolvedValue('https://test.ngrok.io');
178
-
179
- const result = await startTunnelAction.handler(
180
- mockRuntime,
181
- mockMemory,
182
- mockState,
183
- {},
184
- mockCallback
185
- );
186
-
187
- expect(result.values?.success).toBe(true);
188
- expect(result.values?.tunnelUrl).toContain('ngrok.io');
189
- expect(mockTunnelService.startTunnel.calls.length).toBeGreaterThan(0);
190
- expect(mockTunnelService.startTunnel.calls[0][0]).toBe(3000); // Should use default
191
- });
192
-
193
- it('should handle tunnel start failure', async () => {
194
- // Create a fresh mock service for this test that will fail
195
- const failingTunnelService = new MockNgrokService({} as IAgentRuntime);
196
- failingTunnelService.startTunnel.mockRejectedValue(new Error('Ngrok auth failed'));
197
- failingTunnelService.isActive.mockReturnValue(false);
198
-
199
- const failingRuntime = createMockRuntime({
200
- getService: mock().mockImplementation((name: string) => {
201
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
202
- return failingTunnelService;
203
- }
204
- return null;
205
- }),
206
- useModel: mock().mockResolvedValue('{"port": 8080}'),
207
- });
208
-
209
- mockMemory.content = { text: 'start tunnel on port 8080' };
210
-
211
- const result = await startTunnelAction.handler(
212
- failingRuntime,
213
- mockMemory,
214
- mockState,
215
- {},
216
- mockCallback
217
- );
218
-
219
- expect(result.values?.success).toBe(false);
220
- expect(result.values?.error).toBe('Ngrok auth failed');
221
- expect(mockCallback.calls.length).toBeGreaterThan(0);
222
- const call = mockCallback.calls[0][0];
223
- expect(call.text).toContain('Failed to start ngrok tunnel');
224
- expect(call.metadata.error).toBe('Ngrok auth failed');
225
- });
226
-
227
- it('should handle port already in use', async () => {
228
- // Create a fresh mock service for this test that reports as active
229
- const activeTunnelService = new MockNgrokService({} as IAgentRuntime);
230
- activeTunnelService.isActive.mockReturnValue(true);
231
- activeTunnelService.getStatus.mockReturnValue({
232
- active: true,
233
- url: 'https://existing.ngrok.io',
234
- port: 8080,
235
- startedAt: new Date(),
236
- provider: 'ngrok',
237
- });
238
-
239
- const activeRuntime = createMockRuntime({
240
- getService: mock().mockImplementation((name: string) => {
241
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
242
- return activeTunnelService;
243
- }
244
- return null;
245
- }),
246
- useModel: mock().mockResolvedValue('{"port": 8080}'),
247
- });
248
-
249
- mockMemory.content = { text: 'start tunnel on port 8080' };
250
-
251
- const result = await startTunnelAction.handler(
252
- activeRuntime,
253
- mockMemory,
254
- mockState,
255
- {},
256
- mockCallback
257
- );
258
-
259
- expect(result.values?.success).toBe(false);
260
- expect(result.values?.error).toBe('tunnel_already_active');
261
- expect(mockCallback.calls.length).toBeGreaterThan(0);
262
- expect(mockCallback.calls[0][0].text).toContain('Tunnel is already active');
263
- });
264
- });
265
-
266
- describe('stopTunnelAction - Validation', () => {
267
- it('should validate that tunnel service exists', async () => {
268
- const runtimeWithoutService = createMockRuntime({
269
- getService: mock().mockReturnValue(null),
270
- });
271
-
272
- const isValid = await stopTunnelAction.validate(runtimeWithoutService, mockMemory);
273
-
274
- expect(isValid).toBe(false);
275
- });
276
- });
277
-
278
- describe('stopTunnelAction - Error Handling', () => {
279
- it('should handle service not available', async () => {
280
- const runtimeWithoutService = createMockRuntime({
281
- getService: mock().mockReturnValue(null),
282
- });
283
-
284
- const result = await stopTunnelAction.handler(
285
- runtimeWithoutService,
286
- mockMemory,
287
- mockState,
288
- {},
289
- mockCallback
290
- );
291
-
292
- expect(result.values?.success).toBe(false);
293
- expect(result.values?.error).toBe('service_unavailable');
294
- expect(mockCallback.calls.length).toBeGreaterThan(0);
295
- expect(mockCallback.calls[0][0].text).toContain('Tunnel service is not available');
296
- });
297
-
298
- it('should handle stopping when no tunnel is active', async () => {
299
- mockTunnelService.isActive.mockReturnValue(false);
300
-
301
- const result = await stopTunnelAction.handler(
302
- mockRuntime,
303
- mockMemory,
304
- mockState,
305
- {},
306
- mockCallback
307
- );
308
-
309
- expect(result.values?.success).toBe(true);
310
- expect(result.values?.wasActive).toBe(false);
311
- expect(mockCallback.calls.length).toBeGreaterThan(0);
312
- expect(mockCallback.calls[0][0].text).toContain('No tunnel is currently running');
313
- expect(mockTunnelService.stopTunnel.calls.length).toBe(0);
314
- });
315
-
316
- it('should handle stop failure gracefully', async () => {
317
- // Create a fresh mock service for this test that will fail to stop
318
- const failingStopService = new MockNgrokService({} as IAgentRuntime);
319
- failingStopService.isActive.mockReturnValue(true);
320
- failingStopService.getStatus.mockReturnValue({
321
- active: true,
322
- url: 'https://test.ngrok.io',
323
- port: 8080,
324
- startedAt: new Date(),
325
- provider: 'ngrok',
326
- });
327
- failingStopService.stopTunnel.mockRejectedValue(new Error('Stop failed'));
328
-
329
- const failingStopRuntime = createMockRuntime({
330
- getService: mock().mockImplementation((name: string) => {
331
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
332
- return failingStopService;
333
- }
334
- return null;
335
- }),
336
- });
337
-
338
- const result = await stopTunnelAction.handler(
339
- failingStopRuntime,
340
- mockMemory,
341
- mockState,
342
- {},
343
- mockCallback
344
- );
345
-
346
- expect(result.values?.success).toBe(false);
347
- expect(result.values?.error).toBe('Stop failed');
348
- expect(mockCallback.calls.length).toBeGreaterThan(0);
349
- const call = mockCallback.calls[0][0];
350
- expect(call.text).toContain('Failed to stop ngrok tunnel');
351
- expect(call.metadata.error).toBe('Stop failed');
352
- });
353
- });
354
-
355
- describe('getTunnelStatusAction - Edge Cases', () => {
356
- it('should handle service not available', async () => {
357
- const runtimeWithoutService = createMockRuntime({
358
- getService: mock().mockReturnValue(null),
359
- });
360
-
361
- const result = await getTunnelStatusAction.handler(
362
- runtimeWithoutService,
363
- mockMemory,
364
- mockState,
365
- {},
366
- mockCallback
367
- );
368
-
369
- expect(result.values?.success).toBe(false);
370
- expect(result.values?.error).toBe('Tunnel service not found');
371
- });
372
-
373
- it('should format uptime correctly', async () => {
374
- const startTime = new Date();
375
- startTime.setHours(startTime.getHours() - 2); // 2 hours ago
376
-
377
- // Create a fresh mock service for this test with active tunnel
378
- const activeTunnelService = new MockNgrokService({} as IAgentRuntime);
379
- activeTunnelService.getStatus.mockReturnValue({
380
- active: true,
381
- url: 'https://test.ngrok.io',
382
- port: 8080,
383
- startedAt: startTime,
384
- provider: 'ngrok',
385
- });
386
-
387
- const activeRuntime = createMockRuntime({
388
- getService: mock().mockImplementation((name: string) => {
389
- if (name === 'tunnel' || name === 'ngrok-tunnel') {
390
- return activeTunnelService;
391
- }
392
- return null;
393
- }),
394
- });
395
-
396
- await getTunnelStatusAction.handler(activeRuntime, mockMemory, mockState, {}, mockCallback);
397
-
398
- expect(mockCallback.calls.length).toBeGreaterThan(0);
399
- expect(mockCallback.calls[0][0].text).toContain('2 hours');
400
- });
401
- });
402
- });
@@ -1,218 +0,0 @@
1
- import {
2
- type Action,
3
- type ActionExample,
4
- type ActionResult,
5
- elizaLogger,
6
- type HandlerCallback,
7
- type IAgentRuntime,
8
- type Memory,
9
- type State,
10
- } from '@elizaos/core';
11
- import { getTunnelService } from '@elizaos/plugin-tunnel';
12
-
13
- export const getTunnelStatusAction: Action = {
14
- name: 'GET_TUNNEL_STATUS',
15
- similes: ['TUNNEL_STATUS', 'CHECK_TUNNEL', 'NGROK_STATUS', 'TUNNEL_INFO'],
16
- description:
17
- 'Get the current status of the ngrok tunnel including URL, port, and uptime information. Supports action chaining by providing tunnel metadata for monitoring workflows, health checks, or conditional tunnel management.',
18
- validate: async (runtime: IAgentRuntime, _message: Memory) => {
19
- return !!getTunnelService(runtime);
20
- },
21
- handler: async (
22
- runtime: IAgentRuntime,
23
- _message: Memory,
24
- _state?: State,
25
- _options?: unknown,
26
- callback?: HandlerCallback
27
- ): Promise<ActionResult> => {
28
- try {
29
- elizaLogger.info('Getting ngrok tunnel status...');
30
-
31
- const tunnelService = getTunnelService(runtime);
32
- if (!tunnelService) {
33
- throw new Error('Tunnel service not found');
34
- }
35
-
36
- const status = tunnelService.getStatus();
37
-
38
- let responseText: string;
39
- const response = {
40
- ...status,
41
- uptime: 'N/A',
42
- };
43
-
44
- if (status.active) {
45
- if (status.startedAt) {
46
- const uptimeMs = Date.now() - new Date(status.startedAt).getTime();
47
- const minutes = Math.floor(uptimeMs / 60000);
48
- const hours = Math.floor(minutes / 60);
49
-
50
- if (hours > 0) {
51
- response.uptime = `${hours} hour${hours > 1 ? 's' : ''}, ${minutes % 60} minute${
52
- minutes % 60 !== 1 ? 's' : ''
53
- }`;
54
- } else {
55
- response.uptime = `${minutes} minute${minutes !== 1 ? 's' : ''}`;
56
- }
57
- }
58
-
59
- responseText = `✅ Ngrok tunnel is active!\n\n🌐 Public URL: ${status.url}\n🔌 Local Port: ${status.port}\n⏱️ Uptime: ${response.uptime}\n🏢 Provider: ${status.provider}\n\nYour local service is accessible from the internet.`;
60
- } else {
61
- responseText =
62
- '❌ No active ngrok tunnel.\n\nTo start a tunnel, say "start ngrok tunnel on port [PORT]"';
63
- }
64
-
65
- const startedAtIso = status.startedAt ? status.startedAt.toISOString() : null;
66
-
67
- if (callback) {
68
- await callback({
69
- text: responseText,
70
- metadata: {
71
- action: 'tunnel_status',
72
- uptime: response.uptime,
73
- active: status.active,
74
- url: status.url,
75
- port: status.port,
76
- startedAt: startedAtIso,
77
- provider: status.provider,
78
- backend: status.backend ?? null,
79
- },
80
- });
81
- }
82
-
83
- return {
84
- success: true,
85
- text: responseText,
86
- values: {
87
- success: true,
88
- isActive: status.active,
89
- tunnelUrl: status.url,
90
- port: status.port,
91
- uptime: response.uptime,
92
- provider: status.provider,
93
- },
94
- data: {
95
- action: 'GET_TUNNEL_STATUS',
96
- tunnelStatus: {
97
- active: status.active,
98
- url: status.url,
99
- port: status.port,
100
- startedAt: startedAtIso,
101
- provider: status.provider,
102
- backend: status.backend ?? null,
103
- uptime: response.uptime,
104
- checkedAt: new Date().toISOString(),
105
- },
106
- },
107
- };
108
- } catch (error) {
109
- const message = error instanceof Error ? error.message : String(error);
110
- const stack = error instanceof Error ? (error.stack ?? null) : null;
111
- elizaLogger.error(`Failed to get tunnel status: ${message}`);
112
-
113
- if (callback) {
114
- await callback({
115
- text: `❌ Failed to get tunnel status: ${message}`,
116
- metadata: {
117
- error: message,
118
- action: 'tunnel_status_failed',
119
- },
120
- });
121
- }
122
-
123
- return {
124
- success: false,
125
- text: `❌ Failed to get tunnel status: ${message}`,
126
- values: {
127
- success: false,
128
- error: message,
129
- },
130
- data: {
131
- action: 'GET_TUNNEL_STATUS',
132
- errorType: 'status_check_failed',
133
- errorDetails: stack,
134
- },
135
- };
136
- }
137
- },
138
- examples: [
139
- [
140
- {
141
- name: '{{user}}',
142
- content: {
143
- text: 'What is the tunnel status?',
144
- },
145
- },
146
- {
147
- name: '{{agent}}',
148
- content: {
149
- text: '✅ Ngrok tunnel is active!\n\n🌐 Public URL: https://abc123.ngrok.io\n🔌 Local Port: 3000\n⏱️ Uptime: 15 minutes\n🏢 Provider: ngrok\n\nYour local service is accessible from the internet.',
150
- actions: ['GET_TUNNEL_STATUS'],
151
- },
152
- },
153
- ],
154
- [
155
- {
156
- name: '{{user}}',
157
- content: {
158
- text: "Check tunnel status and restart it if it's been running too long",
159
- },
160
- },
161
- {
162
- name: '{{agent}}',
163
- content: {
164
- text: "I'll check the current tunnel status and restart it if needed.",
165
- thought:
166
- 'User wants me to monitor tunnel uptime and restart if necessary - I should check status first, then decide whether to restart based on uptime.',
167
- actions: ['GET_TUNNEL_STATUS'],
168
- },
169
- },
170
- {
171
- name: '{{agent}}',
172
- content: {
173
- text: "Tunnel has been running for 2 hours. That seems like a long time - I'll restart it for optimal performance.",
174
- thought:
175
- 'Status shows the tunnel has been up for 2 hours, which is quite long. I should stop and restart it as requested.',
176
- actions: ['STOP_TUNNEL'],
177
- },
178
- },
179
- {
180
- name: '{{agent}}',
181
- content: {
182
- text: 'Tunnel stopped. Now starting a fresh tunnel...',
183
- thought:
184
- 'Old tunnel is down, now I can start a new fresh tunnel for optimal performance.',
185
- actions: ['START_TUNNEL'],
186
- },
187
- },
188
- ],
189
- [
190
- {
191
- name: '{{user}}',
192
- content: {
193
- text: 'Get tunnel info and then update our webhook URLs',
194
- },
195
- },
196
- {
197
- name: '{{agent}}',
198
- content: {
199
- text: "I'll check the current tunnel status and then update the webhook URLs.",
200
- thought:
201
- 'User needs the current tunnel URL for webhook configuration - I should get the status first, then update webhooks with the public URL.',
202
- actions: ['GET_TUNNEL_STATUS'],
203
- },
204
- },
205
- {
206
- name: '{{agent}}',
207
- content: {
208
- text: 'Tunnel is active at https://abc123.ngrok.io. Now updating webhook URLs...',
209
- thought:
210
- 'I have the current tunnel URL from the status check. I can now update the webhook configurations with this public URL.',
211
- actions: ['UPDATE_WEBHOOKS'],
212
- },
213
- },
214
- ],
215
- ] as ActionExample[][],
216
- };
217
-
218
- export default getTunnelStatusAction;