matimo-examples 0.1.0-alpha.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.
- package/.env.example +36 -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 +128 -0
- package/edit/edit-factory.ts +120 -0
- package/edit/edit-langchain.ts +272 -0
- package/execute/execute-decorator.ts +49 -0
- package/execute/execute-factory.ts +46 -0
- package/execute/execute-langchain.ts +163 -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/package.json +58 -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 +250 -0
- package/read/read-decorator.ts +107 -0
- package/read/read-factory.ts +104 -0
- package/read/read-langchain.ts +253 -0
- package/search/search-decorator.ts +154 -0
- package/search/search-factory.ts +129 -0
- package/search/search-langchain.ts +215 -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/web/web-decorator.ts +52 -0
- package/web/web-factory.ts +70 -0
- package/web/web-langchain.ts +163 -0
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "matimo-examples",
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
|
+
"description": "Matimo SDK examples - Factory Pattern, Decorator Pattern, and LangChain integration with Slack and Gmail",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@langchain/core": "^1.1.18",
|
|
8
|
+
"@langchain/openai": "^1.2.4",
|
|
9
|
+
"langchain": "^1.2.16",
|
|
10
|
+
"dotenv": "^17.2.3",
|
|
11
|
+
"zod": "^4.3.6",
|
|
12
|
+
"matimo": "0.1.0-alpha.7",
|
|
13
|
+
"@matimo/gmail": "0.1.0-alpha.7",
|
|
14
|
+
"@matimo/postgres": "0.1.0-alpha.7",
|
|
15
|
+
"@matimo/slack": "0.1.0-alpha.7"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^25.1.0",
|
|
19
|
+
"better-sqlite3": "^12.6.2",
|
|
20
|
+
"typescript": "^5.9.3",
|
|
21
|
+
"tsx": "^4.21.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"agent:decorator": "tsx agents/decorator-pattern-agent.ts",
|
|
28
|
+
"agent:factory": "tsx agents/factory-pattern-agent.ts",
|
|
29
|
+
"agent:langchain": "tsx agents/langchain-agent.ts",
|
|
30
|
+
"gmail:factory": "tsx gmail/gmail-factory.ts",
|
|
31
|
+
"gmail:decorator": "tsx gmail/gmail-decorator.ts",
|
|
32
|
+
"gmail:langchain": "tsx gmail/gmail-langchain.ts",
|
|
33
|
+
"slack:factory": "tsx slack/slack-factory.ts",
|
|
34
|
+
"slack:decorator": "tsx slack/slack-decorator.ts",
|
|
35
|
+
"slack:langchain": "tsx slack/slack-langchain.ts",
|
|
36
|
+
"postgres:factory": "tsx postgres/postgres-factory.ts",
|
|
37
|
+
"postgres:decorator": "tsx postgres/postgres-decorator.ts",
|
|
38
|
+
"postgres:langchain": "tsx postgres/postgres-langchain.ts",
|
|
39
|
+
"postgres:approval": "tsx postgres/postgres-with-approval.ts",
|
|
40
|
+
"execute:factory": "tsx execute/execute-factory.ts",
|
|
41
|
+
"execute:decorator": "tsx execute/execute-decorator.ts",
|
|
42
|
+
"execute:langchain": "tsx execute/execute-langchain.ts",
|
|
43
|
+
"read:factory": "tsx read/read-factory.ts",
|
|
44
|
+
"read:decorator": "tsx read/read-decorator.ts",
|
|
45
|
+
"read:langchain": "tsx read/read-langchain.ts",
|
|
46
|
+
"edit:factory": "tsx edit/edit-factory.ts",
|
|
47
|
+
"edit:decorator": "tsx edit/edit-decorator.ts",
|
|
48
|
+
"edit:langchain": "tsx edit/edit-langchain.ts",
|
|
49
|
+
"search:factory": "tsx search/search-factory.ts",
|
|
50
|
+
"search:decorator": "tsx search/search-decorator.ts",
|
|
51
|
+
"search:langchain": "tsx search/search-langchain.ts",
|
|
52
|
+
"web:factory": "tsx web/web-factory.ts",
|
|
53
|
+
"web:decorator": "tsx web/web-decorator.ts",
|
|
54
|
+
"web:langchain": "tsx web/web-langchain.ts",
|
|
55
|
+
"build": "tsc",
|
|
56
|
+
"clean": "rm -rf dist"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Postgres Tool Examples
|
|
2
|
+
|
|
3
|
+
These examples demonstrate how to use the Matimo Postgres execute-sql tool with different patterns and approval flows.
|
|
4
|
+
|
|
5
|
+
## ⚡ Quick Setup
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
Before running any examples, you need a Postgres database instance. You can either:
|
|
12
|
+
|
|
13
|
+
### Option 1: Docker (Recommended - One Command)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
docker run --name postgres-matimo -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Option 2: Local Installation
|
|
20
|
+
|
|
21
|
+
**macOS with Homebrew:**
|
|
22
|
+
```bash
|
|
23
|
+
brew install postgresql
|
|
24
|
+
brew services start postgresql
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Linux (Ubuntu/Debian):**
|
|
28
|
+
```bash
|
|
29
|
+
sudo apt-get install postgresql
|
|
30
|
+
sudo systemctl start postgresql
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Configure Connection String
|
|
34
|
+
|
|
35
|
+
The examples use these environment variables from `.env`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
MATIMO_POSTGRES_HOST=localhost
|
|
39
|
+
MATIMO_POSTGRES_PORT=5432
|
|
40
|
+
MATIMO_POSTGRES_USER=your_user_name
|
|
41
|
+
MATIMO_POSTGRES_PASSWORD=your_db_password
|
|
42
|
+
MATIMO_POSTGRES_DB=your_db_name
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or use a full connection string:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
MATIMO_POSTGRES_URL="your connection string"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Running the Examples
|
|
54
|
+
|
|
55
|
+
### 1. Factory Pattern (Simple Usage)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm tsx examples/tools/postgres/postgres-factory.ts
|
|
59
|
+
or
|
|
60
|
+
pnpm postgres:factory
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Basic example showing how to initialize Matimo and execute a tool.
|
|
64
|
+
|
|
65
|
+
### 2. Decorator Pattern (Class-Based)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm tsx examples/tools/postgres/postgres-decorator.ts
|
|
69
|
+
or
|
|
70
|
+
pnpm postgres:decorator
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Example using the `@tool` decorator for a class-based approach.
|
|
74
|
+
|
|
75
|
+
### 3. LangChain Integration
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pnpm tsx examples/tools/postgres/postgres-langchain.ts
|
|
79
|
+
or
|
|
80
|
+
pnpm postgres:langchain
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Example converting Postgres tools to LangChain-compatible schemas.
|
|
84
|
+
|
|
85
|
+
### 4. Real Database with Approval Flow (Interactive)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pnpm tsx examples/tools/postgres/postgres-with-approval.ts
|
|
89
|
+
or
|
|
90
|
+
pnpm postgres:approval
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Most comprehensive example** that demonstrates:
|
|
94
|
+
- ✅ Non-destructive queries (SELECT) - no approval needed
|
|
95
|
+
- ✅ Destructive queries (INSERT, UPDATE, DELETE) - interactive approval
|
|
96
|
+
- ✅ Interactive approval callback that asks for user confirmation
|
|
97
|
+
- ✅ Auto-approval mode for CI/automated environments
|
|
98
|
+
- ✅ Error handling and proper feedback
|
|
99
|
+
|
|
100
|
+
This example **requires a real Postgres connection** and will:
|
|
101
|
+
1. Ask for permission before executing destructive SQL operations
|
|
102
|
+
2. Show actual query results
|
|
103
|
+
3. Demonstrate approval rejection workflow
|
|
104
|
+
|
|
105
|
+
**Example session:**
|
|
106
|
+
```
|
|
107
|
+
🚀 Postgres Tool Example with Approval Flow
|
|
108
|
+
==================================================
|
|
109
|
+
|
|
110
|
+
1️⃣ Non-destructive query (SELECT - no approval needed)
|
|
111
|
+
--------------------------------------------------
|
|
112
|
+
✅ Query executed successfully
|
|
113
|
+
Result: { command: 'SELECT', rowCount: 1, rows: [{...}] }
|
|
114
|
+
|
|
115
|
+
2️⃣ Destructive query (INSERT - requires approval)
|
|
116
|
+
--------------------------------------------------
|
|
117
|
+
⚠️ Approval Required for WRITE operation:
|
|
118
|
+
SQL: INSERT INTO test_table ...
|
|
119
|
+
Do you approve? (yes/no): yes
|
|
120
|
+
Result: ✅ APPROVED
|
|
121
|
+
✅ INSERT approved and executed successfully
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Approval Modes
|
|
125
|
+
|
|
126
|
+
### Interactive Mode (Default)
|
|
127
|
+
|
|
128
|
+
In interactive mode, the tool will prompt you before executing destructive SQL:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pnpm tsx examples/tools/postgres/postgres-with-approval.ts
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### CI/Automated Mode
|
|
135
|
+
|
|
136
|
+
For automated environments or CI pipelines, enable auto-approval:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
export MATIMO_SQL_AUTO_APPROVE=true
|
|
140
|
+
pnpm tsx examples/tools/postgres/postgres-with-approval.ts
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Permanent Approval Patterns
|
|
144
|
+
|
|
145
|
+
Define SQL patterns that are permanently approved:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
export MATIMO_SQL_APPROVED_PATTERNS="DELETE FROM logs|DROP TABLE test_.*|TRUNCATE.*"
|
|
149
|
+
pnpm tsx examples/tools/postgres/postgres-with-approval.ts
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Destructive SQL Detection
|
|
153
|
+
|
|
154
|
+
The Postgres tool automatically detects and requires approval for these operations:
|
|
155
|
+
- `CREATE` - Create tables, indexes, etc.
|
|
156
|
+
- `DROP` - Drop tables, indexes, etc.
|
|
157
|
+
- `ALTER` - Alter tables, columns, etc.
|
|
158
|
+
- `TRUNCATE` - Truncate tables
|
|
159
|
+
- `DELETE` - Delete rows
|
|
160
|
+
- `UPDATE` - Update rows
|
|
161
|
+
|
|
162
|
+
**SELECT, INSERT (without parameter templating), and other read-only operations are not flagged as destructive.**
|
|
163
|
+
|
|
164
|
+
## Troubleshooting
|
|
165
|
+
|
|
166
|
+
### "Connection refused" Error
|
|
167
|
+
|
|
168
|
+
Make sure Postgres is running:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Check if running
|
|
172
|
+
psql -U postgres -c "SELECT version();"
|
|
173
|
+
|
|
174
|
+
# Or start Docker container
|
|
175
|
+
docker run --name postgres-matimo -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### "Permission denied" for CREATE TABLE
|
|
179
|
+
|
|
180
|
+
Use a superuser or ensure the user has CREATE permissions:
|
|
181
|
+
|
|
182
|
+
```sql
|
|
183
|
+
ALTER USER username CREATEDB;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Questions?
|
|
187
|
+
|
|
188
|
+
Check the [Matimo documentation](https://github.com/tallclub/matimo/docs) or [GitHub issues](https://github.com/tallclub/matimo/issues).
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { MatimoInstance, setGlobalMatimoInstance, tool } from 'matimo';
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
console.info('🚀 Postgres Decorator Pattern Example');
|
|
6
|
+
console.info('='.repeat(60));
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
10
|
+
setGlobalMatimoInstance(matimo);
|
|
11
|
+
|
|
12
|
+
console.info('\n📦 Initializing Matimo with decorators...');
|
|
13
|
+
|
|
14
|
+
// Class-based database client using Matimo
|
|
15
|
+
class PostgresAgent {
|
|
16
|
+
private matimo: MatimoInstance;
|
|
17
|
+
|
|
18
|
+
constructor(matimo: MatimoInstance) {
|
|
19
|
+
this.matimo = matimo;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Sequential step 1: Discover tables
|
|
23
|
+
async discoverTables(): Promise<string[]> {
|
|
24
|
+
console.info('\n1️⃣ DISCOVER TABLES (Step 1/3)');
|
|
25
|
+
console.info('-'.repeat(60));
|
|
26
|
+
console.info('SQL: Getting all tables from information_schema...\n');
|
|
27
|
+
|
|
28
|
+
const result = await this.matimo.execute('postgres-execute-sql', {
|
|
29
|
+
sql: `
|
|
30
|
+
SELECT table_name
|
|
31
|
+
FROM information_schema.tables
|
|
32
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
33
|
+
ORDER BY table_name;
|
|
34
|
+
`,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.handleResult(result as any);
|
|
38
|
+
|
|
39
|
+
if (result && (result as any).rows && (result as any).rows.length > 0) {
|
|
40
|
+
return (result as any).rows.map((row: any) => row.table_name);
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sequential step 2: Get row counts
|
|
46
|
+
async getTableRowCounts(): Promise<void> {
|
|
47
|
+
console.info('\n2️⃣ COUNT RECORDS (Step 2/3)');
|
|
48
|
+
console.info('-'.repeat(60));
|
|
49
|
+
console.info('SQL: Getting record counts for tables...\n');
|
|
50
|
+
|
|
51
|
+
// Try pg_stat_user_tables first, fall back to manual counts
|
|
52
|
+
try {
|
|
53
|
+
const result = await this.matimo.execute('postgres-execute-sql', {
|
|
54
|
+
sql: `
|
|
55
|
+
SELECT
|
|
56
|
+
tablename,
|
|
57
|
+
n_live_tup as row_count
|
|
58
|
+
FROM pg_stat_user_tables
|
|
59
|
+
WHERE schemaname = 'public'
|
|
60
|
+
ORDER BY n_live_tup DESC;
|
|
61
|
+
`,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.handleResult(result);
|
|
65
|
+
} catch (err: any) {
|
|
66
|
+
// Fall back to information_schema method
|
|
67
|
+
console.info('⚠️ Using information_schema for row counts (pg_stat not available)');
|
|
68
|
+
const result = await this.matimo.execute('postgres-execute-sql', {
|
|
69
|
+
sql: `
|
|
70
|
+
SELECT
|
|
71
|
+
table_name,
|
|
72
|
+
(SELECT count(*) FROM information_schema.columns
|
|
73
|
+
WHERE information_schema.columns.table_name = t.table_name) as columns
|
|
74
|
+
FROM information_schema.tables t
|
|
75
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
76
|
+
ORDER BY table_name;
|
|
77
|
+
`,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this.handleResult(result);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Sequential step 3: Analyze first table structure
|
|
85
|
+
async analyzeTableStructure(tableName: string): Promise<void> {
|
|
86
|
+
console.info(`\n3️⃣ ANALYZE TABLE STRUCTURE (Step 3/3)`);
|
|
87
|
+
console.info('-'.repeat(60));
|
|
88
|
+
console.info(`SQL: Getting column structure for table "${tableName}"...\n`);
|
|
89
|
+
|
|
90
|
+
const result = await this.matimo.execute('postgres-execute-sql', {
|
|
91
|
+
sql: `
|
|
92
|
+
SELECT
|
|
93
|
+
column_name,
|
|
94
|
+
data_type,
|
|
95
|
+
is_nullable,
|
|
96
|
+
column_default
|
|
97
|
+
FROM information_schema.columns
|
|
98
|
+
WHERE table_name = $1
|
|
99
|
+
ORDER BY ordinal_position;
|
|
100
|
+
`,
|
|
101
|
+
params: [tableName],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
this.handleResult(result);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Bonus: Show database health
|
|
108
|
+
async showDatabaseHealth(): Promise<void> {
|
|
109
|
+
console.info('\n📊 DATABASE HEALTH');
|
|
110
|
+
console.info('-'.repeat(60));
|
|
111
|
+
console.info('SQL: Getting database and connection info...\n');
|
|
112
|
+
|
|
113
|
+
const result = await this.matimo.execute('postgres-execute-sql', {
|
|
114
|
+
sql: `
|
|
115
|
+
SELECT
|
|
116
|
+
current_database() as database_name,
|
|
117
|
+
version() as postgres_version,
|
|
118
|
+
now() as current_time,
|
|
119
|
+
(SELECT count(*) FROM pg_stat_activity) as active_connections;
|
|
120
|
+
`,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.handleResult(result);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private handleResult(result: any) {
|
|
127
|
+
if (
|
|
128
|
+
result &&
|
|
129
|
+
typeof result === 'object' &&
|
|
130
|
+
'success' in result &&
|
|
131
|
+
(result as any).success === false
|
|
132
|
+
) {
|
|
133
|
+
throw new Error((result as any).error || 'Query failed');
|
|
134
|
+
}
|
|
135
|
+
if (result && typeof result === 'object' && 'rows' in result) {
|
|
136
|
+
const rows = (result as any).rows;
|
|
137
|
+
if (rows && Array.isArray(rows)) {
|
|
138
|
+
console.info(`✅ Found ${rows.length} row(s):`);
|
|
139
|
+
rows.forEach((row: any, idx: number) => {
|
|
140
|
+
console.info(` [${idx + 1}] ${JSON.stringify(row)}`);
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
console.info('✅ Query executed successfully');
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
console.info('✅ Query executed successfully');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const agent = new PostgresAgent(matimo);
|
|
152
|
+
|
|
153
|
+
console.info('✅ Agent initialized\n');
|
|
154
|
+
|
|
155
|
+
// Execute sequential workflow
|
|
156
|
+
const tables = await agent.discoverTables();
|
|
157
|
+
|
|
158
|
+
await agent.getTableRowCounts();
|
|
159
|
+
|
|
160
|
+
// Analyze first discovered table if any exist
|
|
161
|
+
if (tables.length > 0) {
|
|
162
|
+
await agent.analyzeTableStructure(tables[0]);
|
|
163
|
+
} else {
|
|
164
|
+
console.info('\n3️⃣ ANALYZE TABLE STRUCTURE (Step 3/3)');
|
|
165
|
+
console.info('-'.repeat(60));
|
|
166
|
+
console.info('⚠️ No tables found to analyze\n');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Show additional database health info
|
|
170
|
+
await agent.showDatabaseHealth();
|
|
171
|
+
|
|
172
|
+
console.info('\n\n✨ Sequential discovery complete!');
|
|
173
|
+
console.info('='.repeat(60));
|
|
174
|
+
console.info('Pattern: 1) Get tables 2) Count rows 3) Analyze discovered table\n');
|
|
175
|
+
} catch (err: any) {
|
|
176
|
+
console.error('\n❌ Error:');
|
|
177
|
+
const message = err.message || String(err);
|
|
178
|
+
console.error('Message:', message);
|
|
179
|
+
|
|
180
|
+
if (message.includes('ECONNREFUSED')) {
|
|
181
|
+
console.error('\n💡 Connection refused - Postgres not running?');
|
|
182
|
+
} else if (message.includes('does not exist')) {
|
|
183
|
+
console.error('\n💡 Database or user does not exist');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.error('\nCurrent .env Postgres settings:');
|
|
187
|
+
console.error(` MATIMO_POSTGRES_HOST=${process.env.MATIMO_POSTGRES_HOST || '(not set)'}`);
|
|
188
|
+
console.error(` MATIMO_POSTGRES_USER=${process.env.MATIMO_POSTGRES_USER || '(not set)'}`);
|
|
189
|
+
console.error(` MATIMO_POSTGRES_DB=${process.env.MATIMO_POSTGRES_DB || '(not set)'}`);
|
|
190
|
+
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
main().catch((err) => {
|
|
196
|
+
console.error('Fatal error:', err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { MatimoInstance } from 'matimo';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.info('🚀 Postgres Factory Pattern Example');
|
|
11
|
+
console.info('='.repeat(60));
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Initialize Matimo with auto-discovery
|
|
15
|
+
console.info('\n📦 Initializing Matimo...');
|
|
16
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
17
|
+
|
|
18
|
+
// Get postgres tools
|
|
19
|
+
const tools = matimo.listTools();
|
|
20
|
+
const postgresTools = tools.filter((t) => t.name.startsWith('postgres'));
|
|
21
|
+
console.info(`✅ Found ${postgresTools.length} Postgres tool(s)`);
|
|
22
|
+
|
|
23
|
+
// STEP 1: Discover available tables
|
|
24
|
+
console.info('\n\n1️⃣ DISCOVER TABLES (Step 1/3)');
|
|
25
|
+
console.info('-'.repeat(60));
|
|
26
|
+
console.info('SQL: Getting all tables from information_schema...\n');
|
|
27
|
+
|
|
28
|
+
const tablesResult = await matimo.execute('postgres-execute-sql', {
|
|
29
|
+
sql: `
|
|
30
|
+
SELECT table_name,
|
|
31
|
+
(SELECT count(*) FROM information_schema.columns
|
|
32
|
+
WHERE information_schema.columns.table_name = t.table_name) as column_count
|
|
33
|
+
FROM information_schema.tables t
|
|
34
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
35
|
+
ORDER BY table_name;
|
|
36
|
+
`,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
tablesResult &&
|
|
41
|
+
typeof tablesResult === 'object' &&
|
|
42
|
+
'success' in tablesResult &&
|
|
43
|
+
(tablesResult as any).success === false
|
|
44
|
+
) {
|
|
45
|
+
throw new Error((tablesResult as any).error || 'Query failed');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let tableNames: string[] = [];
|
|
49
|
+
console.info('✅ Tables found:');
|
|
50
|
+
if (tablesResult && typeof tablesResult === 'object' && 'rows' in tablesResult) {
|
|
51
|
+
const rows = (tablesResult as any).rows;
|
|
52
|
+
if (rows && rows.length > 0) {
|
|
53
|
+
rows.forEach((row: any) => {
|
|
54
|
+
tableNames.push(row.table_name);
|
|
55
|
+
console.info(` - ${row.table_name} (${row.column_count} columns)`);
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
console.info(' (No tables in public schema)');
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
console.info(' (No tables in public schema)');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// STEP 2: Get row counts for each table
|
|
65
|
+
console.info('\n\n2️⃣ COUNT RECORDS (Step 2/3)');
|
|
66
|
+
console.info('-'.repeat(60));
|
|
67
|
+
|
|
68
|
+
if (tableNames.length > 0) {
|
|
69
|
+
console.info(`SQL: Getting record counts for ${tableNames.length} table(s)...\n`);
|
|
70
|
+
|
|
71
|
+
const countsResult = await matimo.execute('postgres-execute-sql', {
|
|
72
|
+
sql: `
|
|
73
|
+
SELECT
|
|
74
|
+
schemaname,
|
|
75
|
+
tablename,
|
|
76
|
+
n_live_tup as row_count
|
|
77
|
+
FROM pg_stat_user_tables
|
|
78
|
+
WHERE schemaname = 'public'
|
|
79
|
+
ORDER BY n_live_tup DESC;
|
|
80
|
+
`,
|
|
81
|
+
params: [],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
countsResult &&
|
|
86
|
+
typeof countsResult === 'object' &&
|
|
87
|
+
'success' in countsResult &&
|
|
88
|
+
(countsResult as any).success === false
|
|
89
|
+
) {
|
|
90
|
+
console.info('⚠️ Could not get row counts');
|
|
91
|
+
} else {
|
|
92
|
+
console.info('✅ Record counts by table:');
|
|
93
|
+
if (countsResult && typeof countsResult === 'object' && 'rows' in countsResult) {
|
|
94
|
+
const rows = (countsResult as any).rows;
|
|
95
|
+
if (rows && rows.length > 0) {
|
|
96
|
+
rows.forEach((row: any) => {
|
|
97
|
+
console.info(` - ${row.tablename}: ${row.row_count} rows`);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// STEP 3: Analyze the first discovered table
|
|
105
|
+
console.info('\n\n3️⃣ ANALYZE TABLE STRUCTURE (Step 3/3)');
|
|
106
|
+
console.info('-'.repeat(60));
|
|
107
|
+
|
|
108
|
+
if (tableNames.length > 0) {
|
|
109
|
+
const firstTable = tableNames[0];
|
|
110
|
+
console.info(`SQL: Getting column structure for table "${firstTable}"...\n`);
|
|
111
|
+
|
|
112
|
+
const columnsResult = await matimo.execute('postgres-execute-sql', {
|
|
113
|
+
sql: `
|
|
114
|
+
SELECT
|
|
115
|
+
column_name,
|
|
116
|
+
data_type,
|
|
117
|
+
is_nullable,
|
|
118
|
+
column_default
|
|
119
|
+
FROM information_schema.columns
|
|
120
|
+
WHERE table_name = $1
|
|
121
|
+
ORDER BY ordinal_position;
|
|
122
|
+
`,
|
|
123
|
+
params: [firstTable],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
columnsResult &&
|
|
128
|
+
typeof columnsResult === 'object' &&
|
|
129
|
+
'success' in columnsResult &&
|
|
130
|
+
(columnsResult as any).success === false
|
|
131
|
+
) {
|
|
132
|
+
console.info('⚠️ Could not get column structure');
|
|
133
|
+
} else {
|
|
134
|
+
console.info(`✅ Columns in "${firstTable}" table:`);
|
|
135
|
+
if (columnsResult && typeof columnsResult === 'object' && 'rows' in columnsResult) {
|
|
136
|
+
const rows = (columnsResult as any).rows;
|
|
137
|
+
if (rows && rows.length > 0) {
|
|
138
|
+
rows.forEach((row: any) => {
|
|
139
|
+
const nullable = row.is_nullable === 'YES' ? 'nullable' : 'NOT NULL';
|
|
140
|
+
const defaultVal = row.column_default ? ` = ${row.column_default}` : '';
|
|
141
|
+
console.info(` - ${row.column_name} (${row.data_type}) ${nullable}${defaultVal}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.info('\n\n✨ Sequential discovery complete!');
|
|
149
|
+
console.info('='.repeat(60));
|
|
150
|
+
console.info('Pattern: 1) Get tables 2) Count rows 3) Analyze discovered table\n');
|
|
151
|
+
} catch (err: any) {
|
|
152
|
+
console.error('\n❌ Error:');
|
|
153
|
+
|
|
154
|
+
// Try to extract helpful information
|
|
155
|
+
const message = err.message || String(err);
|
|
156
|
+
console.error('Message:', message);
|
|
157
|
+
|
|
158
|
+
// Provide specific hints based on error type
|
|
159
|
+
if (message.includes('ECONNREFUSED') || message.includes('Connection refused')) {
|
|
160
|
+
console.error('\n💡 Connection refused - Postgres not running?');
|
|
161
|
+
console.error(' Check docker-compose or your Postgres instance');
|
|
162
|
+
} else if (message.includes('does not exist')) {
|
|
163
|
+
console.error('\n💡 Database or user does not exist');
|
|
164
|
+
console.error(' Verify MATIMO_POSTGRES_* env vars in .env');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.error('\nCurrent .env Postgres settings:');
|
|
168
|
+
console.error(` MATIMO_POSTGRES_HOST=${process.env.MATIMO_POSTGRES_HOST || '(not set)'}`);
|
|
169
|
+
console.error(` MATIMO_POSTGRES_PORT=${process.env.MATIMO_POSTGRES_PORT || '(not set)'}`);
|
|
170
|
+
console.error(` MATIMO_POSTGRES_USER=${process.env.MATIMO_POSTGRES_USER || '(not set)'}`);
|
|
171
|
+
console.error(` MATIMO_POSTGRES_DB=${process.env.MATIMO_POSTGRES_DB || '(not set)'}`);
|
|
172
|
+
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
main().catch((err) => {
|
|
178
|
+
console.error('Fatal error:', err);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
});
|