matimo-examples 0.1.0-alpha.11
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 +49 -0
- package/LICENSE +21 -0
- package/README.md +525 -0
- package/agents/decorator-pattern-agent.ts +368 -0
- package/agents/factory-pattern-agent.ts +253 -0
- package/agents/langchain-agent.ts +146 -0
- package/edit/edit-decorator.ts +178 -0
- package/edit/edit-factory.ts +138 -0
- package/edit/edit-langchain.ts +292 -0
- package/execute/execute-decorator.ts +49 -0
- package/execute/execute-factory.ts +46 -0
- package/execute/execute-langchain.ts +232 -0
- package/github/github-decorator.ts +326 -0
- package/github/github-factory.ts +355 -0
- package/github/github-langchain.ts +206 -0
- package/github/github-with-approval.ts +228 -0
- package/gmail/README.md +345 -0
- package/gmail/gmail-decorator.ts +216 -0
- package/gmail/gmail-factory.ts +231 -0
- package/gmail/gmail-langchain.ts +201 -0
- package/hubspot/README.md +316 -0
- package/hubspot/hubspot-decorator.ts +180 -0
- package/hubspot/hubspot-factory.ts +188 -0
- package/hubspot/hubspot-langchain.ts +222 -0
- package/logger-example.ts +40 -0
- package/mailchimp/README.md +321 -0
- package/mailchimp/mailchimp-decorator.ts +277 -0
- package/mailchimp/mailchimp-factory.ts +187 -0
- package/mailchimp/mailchimp-langchain.ts +155 -0
- package/notion/README.md +293 -0
- package/notion/notion-decorator.ts +275 -0
- package/notion/notion-factory.ts +256 -0
- package/notion/notion-langchain.ts +237 -0
- package/package.json +79 -0
- package/postgres/README.md +188 -0
- package/postgres/postgres-decorator.ts +198 -0
- package/postgres/postgres-factory.ts +180 -0
- package/postgres/postgres-langchain.ts +213 -0
- package/postgres/postgres-with-approval.ts +344 -0
- package/read/read-decorator.ts +154 -0
- package/read/read-factory.ts +121 -0
- package/read/read-langchain.ts +273 -0
- package/search/search-decorator.ts +206 -0
- package/search/search-factory.ts +146 -0
- package/search/search-langchain.ts +255 -0
- package/slack/README.md +339 -0
- package/slack/slack-decorator.ts +245 -0
- package/slack/slack-factory.ts +226 -0
- package/slack/slack-langchain.ts +242 -0
- package/tsconfig.json +20 -0
- package/twilio/README.md +309 -0
- package/twilio/twilio-decorator.ts +288 -0
- package/twilio/twilio-factory.ts +238 -0
- package/twilio/twilio-langchain.ts +218 -0
- package/web/web-decorator.ts +52 -0
- package/web/web-factory.ts +70 -0
- package/web/web-langchain.ts +163 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* EDIT TOOL - LANGCHAIN AI AGENT EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: True AI Agent with OpenAI + LangChain
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* This is a REAL AI agent that:
|
|
10
|
+
* 1. Takes natural language user requests
|
|
11
|
+
* 2. Uses OpenAI LLM (GPT-4o-mini) to decide which edit operations to use
|
|
12
|
+
* 3. Generates appropriate parameters based on context
|
|
13
|
+
* 4. Executes tools autonomously
|
|
14
|
+
* 5. Processes results and responds naturally
|
|
15
|
+
*
|
|
16
|
+
* Use this pattern when:
|
|
17
|
+
* ✅ Building true autonomous AI agents
|
|
18
|
+
* ✅ LLM should decide which tools to use
|
|
19
|
+
* ✅ Complex workflows with LLM reasoning
|
|
20
|
+
* ✅ Multi-step agentic processes
|
|
21
|
+
* ✅ User gives high-level instructions (not low-level API calls)
|
|
22
|
+
*
|
|
23
|
+
* SETUP:
|
|
24
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
25
|
+
* 1. Create .env file in examples/tools/:
|
|
26
|
+
* OPENAI_API_KEY=sk-xxxxxxxxxxxxx
|
|
27
|
+
*
|
|
28
|
+
* 2. Install dependencies:
|
|
29
|
+
* cd examples/tools && npm install
|
|
30
|
+
*
|
|
31
|
+
* USAGE:
|
|
32
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
33
|
+
* # From root directory:
|
|
34
|
+
* pnpm edit:langchain
|
|
35
|
+
*
|
|
36
|
+
* # Or from examples/tools directory:
|
|
37
|
+
* npm run edit:langchain
|
|
38
|
+
*
|
|
39
|
+
* WHAT IT DOES:
|
|
40
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
41
|
+
* This example shows an AI agent that can:
|
|
42
|
+
* 1. Replace text in files
|
|
43
|
+
* 2. Insert new content
|
|
44
|
+
* 3. Delete lines
|
|
45
|
+
* 4. Append to files
|
|
46
|
+
* 5. Respond naturally in conversation style
|
|
47
|
+
*
|
|
48
|
+
* Example conversation:
|
|
49
|
+
* User: "Update the TODO list - mark first item as DONE and add error handling"
|
|
50
|
+
* AI Agent: "I'll help you update the TODO list..."
|
|
51
|
+
* [AI Agent calls edit tool multiple times]
|
|
52
|
+
* AI Agent: "Done! I've updated the file with your changes."
|
|
53
|
+
*
|
|
54
|
+
* ============================================================================
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import 'dotenv/config';
|
|
58
|
+
import fs from 'fs';
|
|
59
|
+
import path from 'path';
|
|
60
|
+
import { fileURLToPath } from 'url';
|
|
61
|
+
import * as readline from 'readline';
|
|
62
|
+
import { createAgent } from 'langchain';
|
|
63
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
64
|
+
import {
|
|
65
|
+
MatimoInstance,
|
|
66
|
+
convertToolsToLangChain,
|
|
67
|
+
type ToolDefinition,
|
|
68
|
+
getGlobalApprovalHandler,
|
|
69
|
+
type ApprovalRequest,
|
|
70
|
+
} from '@matimo/core';
|
|
71
|
+
|
|
72
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create an interactive approval callback for file operations
|
|
76
|
+
*/
|
|
77
|
+
function createApprovalCallback() {
|
|
78
|
+
return async (request: ApprovalRequest): Promise<boolean> => {
|
|
79
|
+
const isInteractive = process.stdin.isTTY;
|
|
80
|
+
|
|
81
|
+
console.info('\n' + '='.repeat(70));
|
|
82
|
+
console.info('🔒 APPROVAL REQUIRED FOR FILE OPERATION');
|
|
83
|
+
console.info('='.repeat(70));
|
|
84
|
+
console.info(`\n📋 Tool: ${request.toolName}`);
|
|
85
|
+
console.info(`📝 Description: ${request.description || '(no description provided)'}`);
|
|
86
|
+
console.info(`\n📄 File Operation:`);
|
|
87
|
+
console.info(` Path: ${request.params.filePath}`);
|
|
88
|
+
if (request.params.startLine) {
|
|
89
|
+
console.info(` Start Line: ${request.params.startLine}`);
|
|
90
|
+
}
|
|
91
|
+
if (request.params.endLine) {
|
|
92
|
+
console.info(` End Line: ${request.params.endLine}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!isInteractive) {
|
|
96
|
+
console.info('\n❌ REJECTED - Non-interactive environment (no terminal)');
|
|
97
|
+
console.info('\n💡 To enable auto-approval in CI/scripts:');
|
|
98
|
+
console.info(' export MATIMO_AUTO_APPROVE=true');
|
|
99
|
+
console.info('\n💡 Or approve specific patterns:');
|
|
100
|
+
console.info(' export MATIMO_APPROVED_PATTERNS="edit"');
|
|
101
|
+
console.info('\n' + '='.repeat(70) + '\n');
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Interactive mode: prompt user
|
|
106
|
+
const rl = readline.createInterface({
|
|
107
|
+
input: process.stdin,
|
|
108
|
+
output: process.stdout,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
console.info('\n❓ User Action Required');
|
|
113
|
+
const question = ' Type "yes" to approve or "no" to reject: ';
|
|
114
|
+
|
|
115
|
+
rl.question(question, (answer) => {
|
|
116
|
+
const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
|
|
117
|
+
|
|
118
|
+
if (approved) {
|
|
119
|
+
console.info(' ✅ Operation APPROVED by user');
|
|
120
|
+
} else {
|
|
121
|
+
console.info(' ❌ Operation REJECTED by user');
|
|
122
|
+
}
|
|
123
|
+
console.info('='.repeat(70) + '\n');
|
|
124
|
+
|
|
125
|
+
rl.close();
|
|
126
|
+
resolve(approved);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run AI Agent with Edit tools
|
|
134
|
+
* The agent receives natural language requests and decides which edit operations to use
|
|
135
|
+
*/
|
|
136
|
+
async function runEditAIAgent() {
|
|
137
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
138
|
+
console.info('║ Edit Tool AI Agent - LangChain + OpenAI ║');
|
|
139
|
+
console.info('║ True autonomous agent with LLM reasoning ║');
|
|
140
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
141
|
+
|
|
142
|
+
// Check required environment variables
|
|
143
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
144
|
+
if (!openaiKey) {
|
|
145
|
+
console.error('❌ Error: OPENAI_API_KEY not set in .env');
|
|
146
|
+
console.info(' Set it: export OPENAI_API_KEY="sk-..."');
|
|
147
|
+
console.info(' Get one from: https://platform.openai.com/api-keys');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.info('🤖 Using OpenAI (GPT-4o-mini) as the AI agent\n');
|
|
152
|
+
|
|
153
|
+
// Create a temp file for the agent to work with
|
|
154
|
+
const tempFile = path.join(__dirname, 'temp-agent-edit-demo.txt');
|
|
155
|
+
fs.writeFileSync(
|
|
156
|
+
tempFile,
|
|
157
|
+
`Project TODO List
|
|
158
|
+
================
|
|
159
|
+
- TODO: Implement authentication module
|
|
160
|
+
- TODO: Add database migrations
|
|
161
|
+
- TODO: Write API documentation
|
|
162
|
+
- TODO: Set up CI/CD pipeline
|
|
163
|
+
`
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Initialize Matimo with auto-discovery
|
|
168
|
+
console.info('🚀 Initializing Matimo...');
|
|
169
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
170
|
+
|
|
171
|
+
// Configure centralized approval handler
|
|
172
|
+
const approvalHandler = getGlobalApprovalHandler();
|
|
173
|
+
approvalHandler.setApprovalCallback(createApprovalCallback());
|
|
174
|
+
|
|
175
|
+
// Get edit tool
|
|
176
|
+
console.info('💬 Loading edit tool...');
|
|
177
|
+
const matimoTools = matimo.listTools();
|
|
178
|
+
const editTools = matimoTools.filter((t) => t.name === 'edit') as ToolDefinition[];
|
|
179
|
+
console.info(`✅ Loaded ${editTools.length} edit tool(s)\n`);
|
|
180
|
+
|
|
181
|
+
if (editTools.length === 0) {
|
|
182
|
+
console.error('❌ Edit tool not found');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Convert to LangChain tools using the built-in converter
|
|
187
|
+
const langchainTools = await convertToolsToLangChain(editTools, matimo);
|
|
188
|
+
|
|
189
|
+
// Initialize OpenAI LLM
|
|
190
|
+
console.info('🤖 Initializing OpenAI (GPT-4o-mini) LLM...');
|
|
191
|
+
const model = new ChatOpenAI({
|
|
192
|
+
modelName: 'gpt-4o-mini',
|
|
193
|
+
temperature: 0.7,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Create agent
|
|
197
|
+
console.info('🔧 Creating agent...\n');
|
|
198
|
+
const agent = await createAgent({
|
|
199
|
+
model,
|
|
200
|
+
tools: langchainTools as any,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Define agent tasks (natural language requests)
|
|
204
|
+
const userRequests = [
|
|
205
|
+
{
|
|
206
|
+
title: 'Example 1: Replace TODO with DONE',
|
|
207
|
+
request: `Mark the first authentication TODO as DONE in file ${tempFile}. Then describe what you did.`,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
title: 'Example 2: Insert new item',
|
|
211
|
+
request: `Add a new TODO "Set up error logging" after line 5 in file ${tempFile}. Confirm the action and tell me how many lines were affected.`,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
title: 'Example 3: List file content',
|
|
215
|
+
request: `Show me the current content of file ${tempFile} (use read tool if available, or describe the final state after your edits)`,
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
console.info('🧪 Running AI Agent Tasks');
|
|
220
|
+
console.info('═'.repeat(60));
|
|
221
|
+
console.info(`📁 Working with: ${tempFile}\n`);
|
|
222
|
+
|
|
223
|
+
// Show initial file content
|
|
224
|
+
console.info('📄 Initial file content:');
|
|
225
|
+
console.info('─'.repeat(60));
|
|
226
|
+
console.info(fs.readFileSync(tempFile, 'utf-8'));
|
|
227
|
+
console.info('─'.repeat(60) + '\n');
|
|
228
|
+
|
|
229
|
+
// Run each task through the agent
|
|
230
|
+
for (const task of userRequests) {
|
|
231
|
+
console.info(`${task.title}`);
|
|
232
|
+
console.info('─'.repeat(60));
|
|
233
|
+
console.info(`👤 User: "${task.request}"\n`);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const response = await agent.invoke({
|
|
237
|
+
messages: [
|
|
238
|
+
{
|
|
239
|
+
role: 'user',
|
|
240
|
+
content: task.request,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Get the last message from the agent
|
|
246
|
+
const lastMessage = response.messages[response.messages.length - 1];
|
|
247
|
+
if (lastMessage) {
|
|
248
|
+
const content =
|
|
249
|
+
typeof lastMessage.content === 'string'
|
|
250
|
+
? lastMessage.content
|
|
251
|
+
: String(lastMessage.content);
|
|
252
|
+
|
|
253
|
+
console.info(`🤖 Agent: ${content || '(Tool executed successfully)'}\n`);
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
257
|
+
console.info(`⚠️ Agent error: ${errorMsg}\n`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.info('═'.repeat(60));
|
|
262
|
+
console.info('\n📄 Final file content:');
|
|
263
|
+
console.info('─'.repeat(60));
|
|
264
|
+
console.info(fs.readFileSync(tempFile, 'utf-8'));
|
|
265
|
+
console.info('─'.repeat(60));
|
|
266
|
+
|
|
267
|
+
console.info('\n✨ AI Agent Examples Complete!\n');
|
|
268
|
+
console.info('Key Features:');
|
|
269
|
+
console.info(' ✅ Real LLM (OpenAI) decides which tools to use');
|
|
270
|
+
console.info(' ✅ Natural language requests, not API calls');
|
|
271
|
+
console.info(' ✅ LLM generates tool parameters based on context');
|
|
272
|
+
console.info(' ✅ Agentic reasoning and decision-making\n');
|
|
273
|
+
console.info('⚠️ Note on agent responses:');
|
|
274
|
+
console.info(' The agent DOES call the real LLM to decide which tool to use.');
|
|
275
|
+
console.info(' However, LangChain agents generate minimal responses when tools succeed.');
|
|
276
|
+
console.info(' The agent autonomously executes tools without verbose narration.\n');
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
279
|
+
if (error instanceof Error && error.stack) {
|
|
280
|
+
console.error('Stack:', error.stack);
|
|
281
|
+
}
|
|
282
|
+
process.exit(1);
|
|
283
|
+
} finally {
|
|
284
|
+
// Clean up
|
|
285
|
+
if (fs.existsSync(tempFile)) {
|
|
286
|
+
fs.unlinkSync(tempFile);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Run the AI agent
|
|
292
|
+
runEditAIAgent().catch(console.error);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MatimoInstance, setGlobalMatimoInstance, tool } from '@matimo/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Execute tool using @tool decorator pattern
|
|
5
|
+
* Demonstrates class-based tool execution with automatic decoration
|
|
6
|
+
*/
|
|
7
|
+
class CommandExecutor {
|
|
8
|
+
@tool('execute')
|
|
9
|
+
async runCommand(command: string, timeout?: number): Promise<unknown> {
|
|
10
|
+
// Decorator automatically intercepts and executes via Matimo
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@tool('execute')
|
|
15
|
+
async listDirectory(): Promise<unknown> {
|
|
16
|
+
// Decorator automatically intercepts and executes via Matimo
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function decoratorExample() {
|
|
22
|
+
// Set up decorator support with autoDiscover
|
|
23
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
24
|
+
setGlobalMatimoInstance(matimo);
|
|
25
|
+
|
|
26
|
+
console.info('=== Execute Tool - Decorator Pattern ===\n');
|
|
27
|
+
|
|
28
|
+
const executor = new CommandExecutor();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Example 1: Run command through decorated method
|
|
32
|
+
console.info('1. Running command: echo "Hello from decorator"\n');
|
|
33
|
+
const result1 = await executor.runCommand('echo "Hello from decorator"');
|
|
34
|
+
console.info('Success:', (result1 as any).success);
|
|
35
|
+
console.info('Output:', (result1 as any).stdout);
|
|
36
|
+
console.info('---\n');
|
|
37
|
+
|
|
38
|
+
// Example 2: List directory
|
|
39
|
+
console.info('2. Running command: pwd\n');
|
|
40
|
+
const result2 = await executor.listDirectory();
|
|
41
|
+
console.info('Success:', (result2 as any).success);
|
|
42
|
+
console.info('Output:', (result2 as any).stdout);
|
|
43
|
+
console.info('---\n');
|
|
44
|
+
} catch (error: any) {
|
|
45
|
+
console.error('Error:', error.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
decoratorExample();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { MatimoInstance } from '@matimo/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Execute tool using factory pattern
|
|
5
|
+
* Demonstrates running shell commands and capturing output
|
|
6
|
+
*/
|
|
7
|
+
async function executeExample() {
|
|
8
|
+
// Initialize Matimo with autoDiscover to find all tools (core + providers)
|
|
9
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
10
|
+
|
|
11
|
+
console.info('=== Execute Tool - Factory Pattern ===\n');
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Example 1: List files in current directory
|
|
15
|
+
console.info('1. Running: ls\n');
|
|
16
|
+
const lsResult = await matimo.execute('execute', {
|
|
17
|
+
command: 'ls',
|
|
18
|
+
timeout: 10000,
|
|
19
|
+
});
|
|
20
|
+
console.info('Success:', (lsResult as any).success);
|
|
21
|
+
console.info('Output:', (lsResult as any).stdout?.substring(0, 200));
|
|
22
|
+
console.info('---\n');
|
|
23
|
+
|
|
24
|
+
// Example 2: Get current working directory
|
|
25
|
+
console.info('2. Running: pwd\n');
|
|
26
|
+
const pwdResult = await matimo.execute('execute', {
|
|
27
|
+
command: 'pwd',
|
|
28
|
+
});
|
|
29
|
+
console.info('Success:', (pwdResult as any).success);
|
|
30
|
+
console.info('Output:', (pwdResult as any).stdout);
|
|
31
|
+
console.info('---\n');
|
|
32
|
+
|
|
33
|
+
// Example 3: Echo command
|
|
34
|
+
console.info('3. Running: echo "Hello from Matimo"\n');
|
|
35
|
+
const echoResult = await matimo.execute('execute', {
|
|
36
|
+
command: 'echo "Hello from Matimo"',
|
|
37
|
+
});
|
|
38
|
+
console.info('Success:', (echoResult as any).success);
|
|
39
|
+
console.info('Output:', (echoResult as any).stdout);
|
|
40
|
+
console.info('---\n');
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
console.error('Error executing command:', error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
executeExample();
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* EXECUTE TOOL - LANGCHAIN AI AGENT EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: True AI Agent with OpenAI + LangChain
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* This is a REAL AI agent that:
|
|
10
|
+
* 1. Takes natural language user requests
|
|
11
|
+
* 2. Uses OpenAI LLM (GPT-4o-mini) to decide when to execute commands
|
|
12
|
+
* 3. Generates appropriate command parameters based on context
|
|
13
|
+
* 4. Executes tools autonomously
|
|
14
|
+
* 5. Processes results and responds naturally
|
|
15
|
+
*
|
|
16
|
+
* SETUP:
|
|
17
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
18
|
+
* 1. Create .env file:
|
|
19
|
+
* OPENAI_API_KEY=sk-xxxxxxxxxxxxx
|
|
20
|
+
*
|
|
21
|
+
* 2. Install dependencies:
|
|
22
|
+
* npm install
|
|
23
|
+
*
|
|
24
|
+
* USAGE:
|
|
25
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
26
|
+
* export OPENAI_API_KEY=sk-xxxx
|
|
27
|
+
* npm run execute:langchain
|
|
28
|
+
*
|
|
29
|
+
* ============================================================================
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import 'dotenv/config';
|
|
33
|
+
import * as readline from 'readline';
|
|
34
|
+
import { createAgent } from 'langchain';
|
|
35
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
36
|
+
import {
|
|
37
|
+
MatimoInstance,
|
|
38
|
+
convertToolsToLangChain,
|
|
39
|
+
ToolDefinition,
|
|
40
|
+
getGlobalApprovalHandler,
|
|
41
|
+
type ApprovalRequest,
|
|
42
|
+
} from '@matimo/core';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create an interactive approval callback for command execution
|
|
46
|
+
*/
|
|
47
|
+
function createApprovalCallback() {
|
|
48
|
+
return async (request: ApprovalRequest): Promise<boolean> => {
|
|
49
|
+
// Check both process.stdin.isTTY and if stdin is readable
|
|
50
|
+
const isInteractive = process.stdin.isTTY && process.stdin.readable;
|
|
51
|
+
|
|
52
|
+
console.info('\n' + '='.repeat(70));
|
|
53
|
+
console.info('🔒 APPROVAL REQUIRED FOR COMMAND EXECUTION');
|
|
54
|
+
console.info('='.repeat(70));
|
|
55
|
+
console.info(`\n📋 Tool: ${request.toolName}`);
|
|
56
|
+
console.info(`📝 Description: ${request.description || '(no description provided)'}`);
|
|
57
|
+
console.info(`\n💻 Command Operation:`);
|
|
58
|
+
console.info(` Command: ${request.params.command}`);
|
|
59
|
+
if (request.params.cwd) {
|
|
60
|
+
console.info(` Working Dir: ${request.params.cwd}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!isInteractive) {
|
|
64
|
+
console.info('\n❌ REJECTED - Non-interactive environment (stdin not available)');
|
|
65
|
+
console.info('\n💡 To auto-approve in non-interactive environments:');
|
|
66
|
+
console.info(' export MATIMO_AUTO_APPROVE=true');
|
|
67
|
+
console.info('\n💡 Or pre-approve specific patterns:');
|
|
68
|
+
console.info(' export MATIMO_APPROVED_PATTERNS="execute"');
|
|
69
|
+
console.info('\n💡 To test interactively, run from command line:');
|
|
70
|
+
console.info(' npm run execute:langchain');
|
|
71
|
+
console.info('\n' + '='.repeat(70) + '\n');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Interactive mode: prompt user
|
|
76
|
+
const rl = readline.createInterface({
|
|
77
|
+
input: process.stdin,
|
|
78
|
+
output: process.stdout,
|
|
79
|
+
terminal: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
console.info('\n❓ User Action Required');
|
|
84
|
+
const question = ' Type "yes" to approve or "no" to reject: ';
|
|
85
|
+
|
|
86
|
+
rl.question(question, (answer) => {
|
|
87
|
+
const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
|
|
88
|
+
|
|
89
|
+
if (approved) {
|
|
90
|
+
console.info(' ✅ Operation APPROVED by user');
|
|
91
|
+
} else {
|
|
92
|
+
console.info(' ❌ Operation REJECTED by user');
|
|
93
|
+
}
|
|
94
|
+
console.info('='.repeat(70) + '\n');
|
|
95
|
+
|
|
96
|
+
rl.close();
|
|
97
|
+
resolve(approved);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The agent receives natural language requests and decides which commands to execute
|
|
105
|
+
*/
|
|
106
|
+
async function runExecuteAIAgent() {
|
|
107
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
108
|
+
console.info('║ Execute Tool AI Agent - LangChain + OpenAI ║');
|
|
109
|
+
console.info('║ True autonomous agent with LLM reasoning ║');
|
|
110
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
111
|
+
|
|
112
|
+
// Check required environment variables
|
|
113
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
114
|
+
if (!openaiKey) {
|
|
115
|
+
console.error('❌ Error: OPENAI_API_KEY not set in .env');
|
|
116
|
+
console.info(' Set it: export OPENAI_API_KEY="sk-..."');
|
|
117
|
+
console.info(' Get one from: https://platform.openai.com/api-keys');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.info('🤖 Using OpenAI (GPT-4o-mini) as the AI agent\n');
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// Initialize Matimo with auto-discovery
|
|
125
|
+
console.info('🚀 Initializing Matimo...');
|
|
126
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
127
|
+
|
|
128
|
+
// Get execute tool
|
|
129
|
+
console.info('💬 Loading execute tool...');
|
|
130
|
+
const matimoTools = matimo.listTools();
|
|
131
|
+
const executeTools = matimoTools.filter((t) => t.name === 'execute');
|
|
132
|
+
console.info(`✅ Loaded ${executeTools.length} execute tool(s)\n`);
|
|
133
|
+
|
|
134
|
+
if (executeTools.length === 0) {
|
|
135
|
+
console.error('❌ Execute tool not found');
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Convert to LangChain tools using the built-in converter
|
|
140
|
+
const langchainTools = await convertToolsToLangChain(executeTools as ToolDefinition[], matimo);
|
|
141
|
+
|
|
142
|
+
// Set up approval callback for destructive commands
|
|
143
|
+
const approvalHandler = getGlobalApprovalHandler();
|
|
144
|
+
approvalHandler.setApprovalCallback(createApprovalCallback());
|
|
145
|
+
|
|
146
|
+
// Initialize OpenAI LLM
|
|
147
|
+
console.info('🤖 Initializing OpenAI (GPT-4o-mini) LLM...');
|
|
148
|
+
const model = new ChatOpenAI({
|
|
149
|
+
modelName: 'gpt-4o-mini',
|
|
150
|
+
temperature: 0.7,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Create agent
|
|
154
|
+
console.info('🔧 Creating agent...\n');
|
|
155
|
+
const agent = await createAgent({
|
|
156
|
+
model,
|
|
157
|
+
tools: langchainTools as any,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Define agent tasks (natural language requests)
|
|
161
|
+
const userRequests = [
|
|
162
|
+
{
|
|
163
|
+
title: 'Example 1: Get system information',
|
|
164
|
+
request: 'Show me the current working directory using pwd command',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
title: 'Example 2: List files',
|
|
168
|
+
request: 'List files and directories in the current folder (use ls -la)',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
title: 'Example 3: Create a test file',
|
|
172
|
+
request: 'Create a simple test file named test.txt in /tmp with content "Hello from Agent"',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
console.info('🧪 Running AI Agent Tasks');
|
|
177
|
+
console.info('═'.repeat(60) + '\n');
|
|
178
|
+
|
|
179
|
+
// Run each task through the agent
|
|
180
|
+
for (const task of userRequests) {
|
|
181
|
+
console.info(`${task.title}`);
|
|
182
|
+
console.info('─'.repeat(60));
|
|
183
|
+
console.info(`👤 User: "${task.request}"\n`);
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const response = await agent.invoke({
|
|
187
|
+
messages: [
|
|
188
|
+
{
|
|
189
|
+
role: 'user',
|
|
190
|
+
content: task.request,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Get the last message from the agent
|
|
196
|
+
const lastMessage = response.messages[response.messages.length - 1];
|
|
197
|
+
if (lastMessage) {
|
|
198
|
+
const content =
|
|
199
|
+
typeof lastMessage.content === 'string'
|
|
200
|
+
? lastMessage.content
|
|
201
|
+
: String(lastMessage.content);
|
|
202
|
+
|
|
203
|
+
if (content && content.trim()) {
|
|
204
|
+
console.info(`🤖 Agent: ${content}\n`);
|
|
205
|
+
} else {
|
|
206
|
+
console.info('🤖 Agent: (Command executed successfully)\n');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
211
|
+
console.info(`⚠️ Agent error: ${errorMsg}\n`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.info('═'.repeat(60));
|
|
216
|
+
console.info('\n✨ AI Agent Examples Complete!\n');
|
|
217
|
+
console.info('Key Features:');
|
|
218
|
+
console.info(' ✅ Real LLM (OpenAI) decides which tools to use');
|
|
219
|
+
console.info(' ✅ Natural language requests, not API calls');
|
|
220
|
+
console.info(' ✅ LLM generates command parameters based on context');
|
|
221
|
+
console.info(' ✅ Agentic reasoning and decision-making\n');
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
224
|
+
if (error instanceof Error && error.stack) {
|
|
225
|
+
console.error('Stack:', error.stack);
|
|
226
|
+
}
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Run the AI agent
|
|
232
|
+
runExecuteAIAgent().catch(console.error);
|