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.
Files changed (57) hide show
  1. package/.env.example +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +525 -0
  4. package/agents/decorator-pattern-agent.ts +368 -0
  5. package/agents/factory-pattern-agent.ts +253 -0
  6. package/agents/langchain-agent.ts +146 -0
  7. package/edit/edit-decorator.ts +178 -0
  8. package/edit/edit-factory.ts +138 -0
  9. package/edit/edit-langchain.ts +292 -0
  10. package/execute/execute-decorator.ts +49 -0
  11. package/execute/execute-factory.ts +46 -0
  12. package/execute/execute-langchain.ts +232 -0
  13. package/github/github-decorator.ts +326 -0
  14. package/github/github-factory.ts +355 -0
  15. package/github/github-langchain.ts +206 -0
  16. package/github/github-with-approval.ts +228 -0
  17. package/gmail/README.md +345 -0
  18. package/gmail/gmail-decorator.ts +216 -0
  19. package/gmail/gmail-factory.ts +231 -0
  20. package/gmail/gmail-langchain.ts +201 -0
  21. package/hubspot/README.md +316 -0
  22. package/hubspot/hubspot-decorator.ts +180 -0
  23. package/hubspot/hubspot-factory.ts +188 -0
  24. package/hubspot/hubspot-langchain.ts +222 -0
  25. package/logger-example.ts +40 -0
  26. package/mailchimp/README.md +321 -0
  27. package/mailchimp/mailchimp-decorator.ts +277 -0
  28. package/mailchimp/mailchimp-factory.ts +187 -0
  29. package/mailchimp/mailchimp-langchain.ts +155 -0
  30. package/notion/README.md +293 -0
  31. package/notion/notion-decorator.ts +275 -0
  32. package/notion/notion-factory.ts +256 -0
  33. package/notion/notion-langchain.ts +237 -0
  34. package/package.json +79 -0
  35. package/postgres/README.md +188 -0
  36. package/postgres/postgres-decorator.ts +198 -0
  37. package/postgres/postgres-factory.ts +180 -0
  38. package/postgres/postgres-langchain.ts +213 -0
  39. package/postgres/postgres-with-approval.ts +344 -0
  40. package/read/read-decorator.ts +154 -0
  41. package/read/read-factory.ts +121 -0
  42. package/read/read-langchain.ts +273 -0
  43. package/search/search-decorator.ts +206 -0
  44. package/search/search-factory.ts +146 -0
  45. package/search/search-langchain.ts +255 -0
  46. package/slack/README.md +339 -0
  47. package/slack/slack-decorator.ts +245 -0
  48. package/slack/slack-factory.ts +226 -0
  49. package/slack/slack-langchain.ts +242 -0
  50. package/tsconfig.json +20 -0
  51. package/twilio/README.md +309 -0
  52. package/twilio/twilio-decorator.ts +288 -0
  53. package/twilio/twilio-factory.ts +238 -0
  54. package/twilio/twilio-langchain.ts +218 -0
  55. package/web/web-decorator.ts +52 -0
  56. package/web/web-factory.ts +70 -0
  57. package/web/web-langchain.ts +163 -0
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * POSTGRES TOOLS - 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 Postgres tools to use
12
+ * 3. Generates appropriate SQL based on context
13
+ * 4. Executes queries autonomously
14
+ * 5. Processes results and responds naturally
15
+ *
16
+ * CRITICAL: Database credentials are NEVER exposed to the LLM
17
+ * - LLM only sees tool descriptions and results
18
+ * - Matimo handles all credential loading from environment variables
19
+ * - Tool execution is secure and isolated
20
+ *
21
+ * Use this pattern when:
22
+ * ✅ Building true autonomous AI agents
23
+ * ✅ LLM should decide which tools to use
24
+ * ✅ Complex workflows with LLM reasoning
25
+ * ✅ Multi-step agentic processes
26
+ * ✅ User gives high-level instructions (not SQL queries)
27
+ *
28
+ * SETUP:
29
+ * ─────────────────────────────────────────────────────────────────────────
30
+ * 1. Make sure Postgres is running (see POSTGRES_EXAMPLE_SETUP.md)
31
+ * 2. Create .env file with database credentials:
32
+ * MATIMO_POSTGRES_HOST=localhost
33
+ * MATIMO_POSTGRES_PORT=5432
34
+ * MATIMO_POSTGRES_USER=matimo
35
+ * MATIMO_POSTGRES_PASSWORD=development
36
+ * MATIMO_POSTGRES_DB=matimo
37
+ * OPENAI_API_KEY=sk-xxxxxxxxxxxxx
38
+ *
39
+ * USAGE:
40
+ * ─────────────────────────────────────────────────────────────────────────
41
+ * pnpm postgres:langchain
42
+ *
43
+ * WHAT IT DOES:
44
+ * ─────────────────────────────────────────────────────────────────────────
45
+ * This example shows an AI agent that can:
46
+ * 1. Explore database tables and schema
47
+ * 2. Execute SELECT queries to analyze data
48
+ * 3. Respond naturally in conversation style
49
+ * 4. Multi-step reasoning without exposing credentials
50
+ *
51
+ * Example:
52
+ * User: "What tables exist in matimo?"
53
+ * AI Agent: "Let me query the database to see what tables are available..."
54
+ * [Agent calls postgres-execute-sql tool with SELECT query]
55
+ * AI Agent: "The matimo database contains these tables: ..."
56
+ *
57
+ * ============================================================================
58
+ */
59
+
60
+ import 'dotenv/config';
61
+ import { MatimoInstance, convertToolsToLangChain, ToolDefinition } from 'matimo';
62
+ import { createAgent } from 'langchain';
63
+ import { ChatOpenAI } from '@langchain/openai';
64
+
65
+ /**
66
+ * Run AI Agent with Postgres tools
67
+ * The agent receives natural language requests and decides which Postgres tools to use
68
+ */
69
+ async function runPostgresAIAgent() {
70
+ console.info('\n╔════════════════════════════════════════════════════════╗');
71
+ console.info('║ Postgres AI Agent - LangChain + OpenAI ║');
72
+ console.info('║ True autonomous agent with LLM reasoning ║');
73
+ console.info('║ Database credentials: NEVER exposed to LLM ║');
74
+ console.info('╚════════════════════════════════════════════════════════╝\n');
75
+
76
+ // Check required environment variables
77
+ const pgConnected =
78
+ process.env.MATIMO_POSTGRES_HOST &&
79
+ process.env.MATIMO_POSTGRES_USER &&
80
+ process.env.MATIMO_POSTGRES_PASSWORD &&
81
+ process.env.MATIMO_POSTGRES_DB;
82
+
83
+ if (!pgConnected) {
84
+ console.error('❌ Error: Postgres connection not configured in .env');
85
+ console.info(' Required environment variables:');
86
+ console.info(' MATIMO_POSTGRES_HOST');
87
+ console.info(' MATIMO_POSTGRES_USER');
88
+ console.info(' MATIMO_POSTGRES_PASSWORD');
89
+ console.info(' MATIMO_POSTGRES_DB');
90
+ process.exit(1);
91
+ }
92
+
93
+ const openaiKey = process.env.OPENAI_API_KEY;
94
+ if (!openaiKey) {
95
+ console.error('❌ Error: OPENAI_API_KEY not set in .env');
96
+ console.info(' Set it: export OPENAI_API_KEY="sk-..."');
97
+ process.exit(1);
98
+ }
99
+
100
+ console.info(
101
+ `📍 Postgres: ${process.env.MATIMO_POSTGRES_DB}@${process.env.MATIMO_POSTGRES_HOST}`
102
+ );
103
+ console.info(`🤖 Using OpenAI (GPT-4o-mini) as the AI agent\n`);
104
+
105
+ try {
106
+ // Initialize Matimo with auto-discovery
107
+ console.info('🚀 Initializing Matimo...');
108
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
109
+
110
+ // Get Postgres tools and convert to LangChain format
111
+ console.info('💬 Loading Postgres tools...');
112
+ const matimoTools = matimo.listTools();
113
+ const postgresTools = matimoTools.filter((t) => t.name.startsWith('postgres'));
114
+ console.info(`✅ Loaded ${postgresTools.length} Postgres tool(s)\n`);
115
+
116
+ // ✅ Convert Matimo tools to LangChain format using the built-in integration
117
+ const langchainTools = await convertToolsToLangChain(
118
+ postgresTools as ToolDefinition[],
119
+ matimo,
120
+ {
121
+ // Pass database credentials so LLM tool calls can execute securely
122
+ // The LLM will NEVER see these values - only Matimo tool execution will use them
123
+ }
124
+ );
125
+
126
+ // Initialize OpenAI LLM
127
+ console.info('🤖 Initializing OpenAI (GPT-4o-mini) LLM...');
128
+ const model = new ChatOpenAI({
129
+ modelName: 'gpt-4o-mini',
130
+ temperature: 0.7,
131
+ });
132
+
133
+ // Create agent
134
+ console.info('🔧 Creating agent...\n');
135
+ const agent = await createAgent({
136
+ model,
137
+ tools: langchainTools as any[], // Type casting for LangChain tools
138
+ });
139
+
140
+ // Define agent tasks (sequential - each task uses results from previous)
141
+ const userRequests = [
142
+ {
143
+ title: 'Step 1: Discover tables',
144
+ request:
145
+ 'Get a list of all tables in the matimo database. Query the information_schema to show me all tables and their row counts.',
146
+ },
147
+ {
148
+ title: 'Step 2: Analyze data volume',
149
+ request:
150
+ 'Now that you know which tables exist, tell me the total number of records across all tables and which tables have the most data.',
151
+ },
152
+ {
153
+ title: 'Step 3: Show schema for main table',
154
+ request:
155
+ 'For the table with the most records, show me the column structure (column names and data types) so I understand what data is stored there.',
156
+ },
157
+ ];
158
+
159
+ console.info('🧪 Running AI Agent Tasks (Sequential Workflow)');
160
+ console.info('═'.repeat(60));
161
+
162
+ // Run each task through the agent sequentially
163
+ // Each task builds on previous results instead of making assumptions
164
+ for (const task of userRequests) {
165
+ console.info(`\n${task.title}`);
166
+ console.info('─'.repeat(60));
167
+ console.info(`👤 User: "${task.request}"\n`);
168
+
169
+ try {
170
+ const response = await agent.invoke({
171
+ messages: [
172
+ {
173
+ role: 'user',
174
+ content: task.request,
175
+ },
176
+ ],
177
+ });
178
+
179
+ // Get the last message from the agent
180
+ const lastMessage = response.messages[response.messages.length - 1];
181
+ if (lastMessage) {
182
+ if (typeof lastMessage.content === 'string') {
183
+ console.info(`🤖 Agent: ${lastMessage.content}\n`);
184
+ } else {
185
+ console.info(`🤖 Agent:`, lastMessage.content, '\n');
186
+ }
187
+ }
188
+ } catch (error) {
189
+ const errorMsg = error instanceof Error ? error.message : String(error);
190
+ console.info(`⚠️ Agent error: ${errorMsg}\n`);
191
+ }
192
+ }
193
+
194
+ console.info('═'.repeat(60));
195
+ console.info('✨ AI Agent Examples Complete!\n');
196
+ console.info('Key Features:');
197
+ console.info(' ✅ Real LLM (OpenAI) decides which tools to use');
198
+ console.info(' ✅ Natural language requests, not SQL queries');
199
+ console.info(' ✅ LLM generates SQL based on context');
200
+ console.info(' ✅ Database credentials NEVER exposed to LLM');
201
+ console.info(' ✅ Matimo handles secure credential injection');
202
+ console.info(' ✅ Agentic reasoning and decision-making\n');
203
+ } catch (error) {
204
+ console.error('❌ Error:', error instanceof Error ? error.message : String(error));
205
+ if (error instanceof Error && error.stack) {
206
+ console.error('Stack:', error.stack);
207
+ }
208
+ process.exit(1);
209
+ }
210
+ }
211
+
212
+ // Run the AI agent
213
+ runPostgresAIAgent().catch(console.error);
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Postgres Tool Example with Real Database and Approval Flow
3
+ *
4
+ * This example demonstrates:
5
+ * 1. Connecting to a real Postgres database
6
+ * 2. Executing non-destructive queries (SELECT) - no approval needed
7
+ * 3. Executing destructive queries - approval required and handled
8
+ * 4. Setting up a single generic approval callback
9
+ *
10
+ * Approval Flow (Generic for ALL tools):
11
+ * - Tool declares requires_approval in YAML OR contains destructive keywords
12
+ * - Check MATIMO_AUTO_APPROVE=true (approve all)
13
+ * - Check MATIMO_APPROVED_PATTERNS="pattern*" (pre-approved patterns)
14
+ * - Call single approval callback for user/policy approval
15
+ * - No per-tool, per-provider custom logic needed
16
+ *
17
+ * Setup Instructions:
18
+ * 1. Make sure you have a Postgres instance running locally or accessible
19
+ * 2. Set the connection string via environment variable:
20
+ * export MATIMO_POSTGRES_URL="postgresql://user:password@localhost:5432/dbname"
21
+ * OR set individual components:
22
+ * export MATIMO_POSTGRES_HOST="localhost"
23
+ * export MATIMO_POSTGRES_PORT="5432"
24
+ * export MATIMO_POSTGRES_USER="user"
25
+ * export MATIMO_POSTGRES_PASSWORD="password"
26
+ * export MATIMO_POSTGRES_DB="dbname"
27
+ *
28
+ * 3. Run the example:
29
+ * pnpm postgres:approval
30
+ */
31
+
32
+ import 'dotenv/config';
33
+ import { MatimoInstance, getGlobalApprovalHandler, type ApprovalRequest } from '@matimo/core';
34
+ import * as readline from 'readline';
35
+
36
+ // Interactive approval callback - prompts user when MATIMO_AUTO_APPROVE is not set
37
+ function createApprovalCallback() {
38
+ return async (request: ApprovalRequest): Promise<boolean> => {
39
+ const isInteractive = process.stdin.isTTY;
40
+
41
+ console.info('\n' + '='.repeat(70));
42
+ console.info('🔒 APPROVAL REQUIRED FOR DESTRUCTIVE OPERATION');
43
+ console.info('='.repeat(70));
44
+ console.info(`\n📋 Tool: ${request.toolName}`);
45
+ console.info(`📝 Description: ${request.description || '(no description provided)'}`);
46
+
47
+ // Show SQL content if available
48
+ if (request.params && typeof request.params.sql === 'string') {
49
+ console.info(`\n📄 SQL to execute:`);
50
+ console.info(' ' + request.params.sql.split('\n').join('\n '));
51
+ }
52
+
53
+ if (!isInteractive) {
54
+ console.info('\n❌ REJECTED - Non-interactive environment (no terminal)');
55
+ console.info('\n💡 To enable auto-approval in CI/scripts:');
56
+ console.info(' export MATIMO_AUTO_APPROVE=true');
57
+ console.info('\n💡 Or approve specific patterns:');
58
+ console.info(' export MATIMO_APPROVED_PATTERNS="postgres-execute-sql"');
59
+ console.info('\n' + '='.repeat(70) + '\n');
60
+ return false;
61
+ }
62
+
63
+ // Interactive mode: prompt user
64
+ const rl = readline.createInterface({
65
+ input: process.stdin,
66
+ output: process.stdout,
67
+ });
68
+
69
+ return new Promise((resolve) => {
70
+ console.info('\n❓ User Action Required');
71
+ const question = ' Type "yes" to approve or "no" to reject: ';
72
+
73
+ rl.question(question, (answer) => {
74
+ const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
75
+
76
+ if (approved) {
77
+ console.info(' ✅ Operation APPROVED by user');
78
+ } else {
79
+ console.info(' ❌ Operation REJECTED by user');
80
+ }
81
+ console.info('='.repeat(70) + '\n');
82
+
83
+ rl.close();
84
+ resolve(approved);
85
+ });
86
+ });
87
+ };
88
+ }
89
+
90
+ async function main() {
91
+ // Check if database connection is available
92
+ if (
93
+ !process.env.MATIMO_POSTGRES_URL &&
94
+ (!process.env.MATIMO_POSTGRES_HOST ||
95
+ !process.env.MATIMO_POSTGRES_USER ||
96
+ !process.env.MATIMO_POSTGRES_PASSWORD ||
97
+ !process.env.MATIMO_POSTGRES_DB)
98
+ ) {
99
+ console.error('❌ Database connection not configured.');
100
+ console.error('Please set one of:');
101
+ console.error(' - MATIMO_POSTGRES_URL="postgresql://user:password@host:port/db"');
102
+ console.error(
103
+ ' - MATIMO_POSTGRES_HOST, MATIMO_POSTGRES_PORT, MATIMO_POSTGRES_USER, MATIMO_POSTGRES_PASSWORD, MATIMO_POSTGRES_DB'
104
+ );
105
+ process.exit(1);
106
+ }
107
+
108
+ // Initialize Matimo with postgres tools
109
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
110
+
111
+ console.info('\n' + '='.repeat(70));
112
+ console.info('🚀 Postgres Tool Example with Approval Flow');
113
+ console.info('='.repeat(70));
114
+
115
+ // Configure centralized approval handler
116
+ const approvalHandler = getGlobalApprovalHandler();
117
+ approvalHandler.setApprovalCallback(createApprovalCallback());
118
+
119
+ // Show current approval mode
120
+ const autoApproveEnabled = process.env.MATIMO_AUTO_APPROVE === 'true';
121
+ const approvedPatterns = process.env.MATIMO_APPROVED_PATTERNS;
122
+
123
+ console.info('\n🔐 APPROVAL CONFIGURATION:');
124
+ if (autoApproveEnabled) {
125
+ console.info(' ✅ MATIMO_AUTO_APPROVE=true');
126
+ console.info(' → All destructive operations will be AUTO-APPROVED');
127
+ } else if (approvedPatterns) {
128
+ console.info(` ✅ MATIMO_APPROVED_PATTERNS="${approvedPatterns}"`);
129
+ console.info(' → Matching operations will be auto-approved');
130
+ } else {
131
+ console.info(' ⚠️ INTERACTIVE MODE ENABLED');
132
+ console.info(' → You will be prompted to approve destructive operations');
133
+ console.info(' → Type "yes" or "no" when prompted');
134
+ }
135
+
136
+ try {
137
+ // 1. List available tools
138
+ const tools = matimo.listTools();
139
+ const postgresTools = tools.filter((t) => t.name.startsWith('postgres'));
140
+ console.info(`\n📋 Available Postgres tools: ${postgresTools.map((t) => t.name).join(', ')}`);
141
+
142
+ // Sequential discovery workflow
143
+
144
+ // STEP 1: Discover tables (SELECT - no approval needed)
145
+ console.info('\n\n1️⃣ DISCOVER TABLES (Step 1/3 - No approval needed)');
146
+ console.info('-'.repeat(70));
147
+
148
+ let discoveredTables: string[] = [];
149
+ try {
150
+ console.info('Executing: SELECT table_name FROM information_schema.tables...');
151
+ console.info('(This is a SELECT - no approval will be requested)\n');
152
+ const tablesResult = await matimo.execute('postgres-execute-sql', {
153
+ sql: `
154
+ SELECT table_name
155
+ FROM information_schema.tables
156
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
157
+ ORDER BY table_name;
158
+ `,
159
+ });
160
+ if (
161
+ tablesResult &&
162
+ typeof tablesResult === 'object' &&
163
+ 'success' in tablesResult &&
164
+ (tablesResult as any).success === false
165
+ ) {
166
+ throw new Error((tablesResult as any).error || 'Query failed');
167
+ }
168
+ console.info('✅ Query executed successfully (no approval required for SELECT)');
169
+ if (tablesResult && typeof tablesResult === 'object' && 'rows' in tablesResult) {
170
+ const rows = (tablesResult as any).rows;
171
+ if (rows && rows.length > 0) {
172
+ console.info('Tables found:');
173
+ rows.forEach((row: any) => {
174
+ console.info(` - ${row.table_name}`);
175
+ discoveredTables.push(row.table_name);
176
+ });
177
+ } else {
178
+ console.info('(No tables found in public schema)');
179
+ }
180
+ } else {
181
+ console.info('(No tables found in public schema)');
182
+ }
183
+ } catch (err: any) {
184
+ console.error('❌ Error:', err.message);
185
+ }
186
+
187
+ // STEP 2: Get row counts (SELECT - no approval needed)
188
+ console.info('\n\n2️⃣ COUNT RECORDS (Step 2/3 - No approval needed)');
189
+ console.info('-'.repeat(60));
190
+ console.info('SQL: SELECT tablename, n_live_tup FROM pg_stat_user_tables\n');
191
+
192
+ try {
193
+ const countsResult = await matimo.execute('postgres-execute-sql', {
194
+ sql: `
195
+ SELECT
196
+ table_name,
197
+ (SELECT count(*) FROM information_schema.columns
198
+ WHERE information_schema.columns.table_name = t.table_name) as columns
199
+ FROM information_schema.tables t
200
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
201
+ ORDER BY table_name;
202
+ `,
203
+ });
204
+ if (
205
+ countsResult &&
206
+ typeof countsResult === 'object' &&
207
+ 'success' in countsResult &&
208
+ (countsResult as any).success === false
209
+ ) {
210
+ throw new Error((countsResult as any).error || 'Query failed');
211
+ }
212
+ console.info('✅ Query executed successfully (no approval needed for SELECT)');
213
+ if (countsResult && typeof countsResult === 'object' && 'rows' in countsResult) {
214
+ const rows = (countsResult as any).rows;
215
+ if (rows && rows.length > 0) {
216
+ console.info('Tables found:');
217
+ rows.forEach((row: any) => {
218
+ console.info(` - ${row.table_name} (${row.columns} columns)`);
219
+ });
220
+ } else {
221
+ console.info('(No tables found in public schema)');
222
+ }
223
+ } else {
224
+ console.info('(No tables found in public schema)');
225
+ }
226
+ } catch (err: any) {
227
+ console.error('❌ Error:', err.message);
228
+ }
229
+
230
+ // STEP 3: Try a destructive operation that requires approval
231
+ console.info('\n\n3️⃣ DESTRUCTIVE OPERATION (Step 3/3 - Requires approval)');
232
+ console.info('-'.repeat(70));
233
+ console.info('Attempting DELETE operation - this WILL trigger approval...\n');
234
+
235
+ if (discoveredTables.length > 0) {
236
+ const tableName = discoveredTables[0];
237
+ const deleteSql = `DELETE FROM ${tableName} WHERE 1=0;`;
238
+ console.info(`📄 SQL Query: ${deleteSql}`);
239
+ console.info('⚠️ NOTE: This is a DESTRUCTIVE operation (DELETE keyword detected)');
240
+ console.info('⚠️ NOTE: WHERE 1=0 matches no rows, so it is SAFE\n');
241
+
242
+ if (autoApproveEnabled) {
243
+ console.info('ℹ️ MATIMO_AUTO_APPROVE=true → Will auto-approve this operation\n');
244
+ } else if (approvedPatterns) {
245
+ console.info(`ℹ️ MATIMO_APPROVED_PATTERNS set → Checking pattern matching...\n`);
246
+ } else {
247
+ console.info('ℹ️ INTERACTIVE MODE → You will be prompted to approve/reject\n');
248
+ }
249
+
250
+ try {
251
+ // Safe DELETE that matches no rows (WHERE 1=0)
252
+ const deleteResult = await matimo.execute('postgres-execute-sql', {
253
+ sql: deleteSql,
254
+ });
255
+
256
+ // Check if operation was rejected by user approval
257
+ if (
258
+ deleteResult &&
259
+ typeof deleteResult === 'object' &&
260
+ 'approved' in deleteResult &&
261
+ (deleteResult as any).approved === false
262
+ ) {
263
+ console.info('\n⚠️ DELETE was REJECTED by user approval');
264
+ console.info(` Reason: ${(deleteResult as any).reason}`);
265
+ console.info(' The operation was cancelled due to approval denial\n');
266
+ } else {
267
+ console.info('\n✅ DELETE APPROVED AND EXECUTED');
268
+ console.info(' Operation was approved (or auto-approved)');
269
+ console.info(' (0 rows deleted - safe WHERE clause)\n');
270
+ }
271
+ } catch (err: any) {
272
+ const errorMsg = err?.message || String(err);
273
+
274
+ if (
275
+ errorMsg.includes('Operation rejected') ||
276
+ errorMsg.includes('not approved') ||
277
+ errorMsg.includes('User or policy rejected')
278
+ ) {
279
+ console.info('\n⚠️ DELETE was REJECTED by user');
280
+ console.info(` Reason: ${errorMsg}`);
281
+ console.info(' You chose to reject this destructive operation\n');
282
+ } else if (errorMsg.includes('approval') && errorMsg.includes('required')) {
283
+ console.info('\n⚠️ DELETE REQUIRES APPROVAL');
284
+ console.info(` Error: ${errorMsg}`);
285
+ console.info(' Set MATIMO_AUTO_APPROVE=true to auto-approve, or run interactively\n');
286
+ } else {
287
+ console.error(`\n❌ Unexpected error: ${errorMsg}\n`);
288
+ }
289
+ }
290
+ } else {
291
+ console.info('\n⚠️ No tables found to demonstrate destructive operation');
292
+ console.info(' (Would require approval when attempting DELETE/UPDATE/CREATE/DROP/ALTER)\n');
293
+ }
294
+
295
+ // STEP 4: Show approval configuration and summary
296
+ console.info('\n\n4️⃣ APPROVAL SYSTEM SUMMARY');
297
+ console.info('='.repeat(70));
298
+
299
+ console.info('\n📋 Current Settings:');
300
+ if (autoApproveEnabled) {
301
+ console.info(' ✅ MATIMO_AUTO_APPROVE=true → All destructive ops AUTO-APPROVED');
302
+ } else if (approvedPatterns) {
303
+ console.info(` ✅ MATIMO_APPROVED_PATTERNS="${approvedPatterns}"`);
304
+ console.info(' → Matching patterns AUTO-APPROVED, others require user input');
305
+ } else {
306
+ console.info(' ⚠️ Interactive Mode (RECOMMENDED FOR HUMANS)');
307
+ console.info(' → All destructive operations require your "yes/no" input');
308
+ }
309
+
310
+ console.info('\n💡 How to Use:');
311
+ console.info(' 1. Interactive (default): pnpm postgres:approval');
312
+ console.info(
313
+ ' 2. Auto-approve in CI: MATIMO_AUTO_APPROVE=true pnpm postgres:approval'
314
+ );
315
+ console.info(
316
+ ' 3. Pre-approved patterns: MATIMO_APPROVED_PATTERNS="postgres*" pnpm postgres:approval'
317
+ );
318
+
319
+ console.info('\n🔐 Supported Approval Modes:');
320
+ console.info(' • MATIMO_AUTO_APPROVE=true → Approve all destructive operations');
321
+ console.info(
322
+ ' • MATIMO_APPROVED_PATTERNS → Approve only matching tool names (glob pattern)'
323
+ );
324
+ console.info(' • Interactive (no env vars) → Prompt user for each operation');
325
+
326
+ console.info('\n✨ Workflow Complete! You tested:');
327
+ console.info(' 1. ✅ SELECT (non-destructive) → No approval needed');
328
+ console.info(' 2. ✅ SELECT (non-destructive) → No approval needed');
329
+ console.info(' 3. 🔒 DELETE (destructive) → Approval required/tested');
330
+
331
+ console.info('\n' + '='.repeat(70) + '\n');
332
+ } catch (err: any) {
333
+ console.error('\n❌ Error:', err.message);
334
+ if (err.code === 'ECONNREFUSED') {
335
+ console.error('\nDatabase connection refused. Make sure your Postgres instance is running.');
336
+ }
337
+ process.exit(1);
338
+ }
339
+ }
340
+
341
+ main().catch((err) => {
342
+ console.error('Fatal error:', err);
343
+ process.exit(1);
344
+ });