@cli4ai/mongodb 1.0.7 → 1.0.9

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/cli4ai.json CHANGED
@@ -1,33 +1,214 @@
1
1
  {
2
2
  "name": "mongodb",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "MongoDB queries",
5
5
  "author": "cliforai",
6
- "license": "MIT",
7
- "entry": "run.ts",
6
+ "license": "BUSL-1.1",
7
+ "entry": "dist/run.js",
8
8
  "runtime": "node",
9
- "keywords": ["mongodb", "database", "nosql", "query"],
9
+ "keywords": [
10
+ "mongodb",
11
+ "database",
12
+ "nosql",
13
+ "query"
14
+ ],
10
15
  "commands": {
11
- "databases": { "description": "List all databases" },
12
- "collections": { "description": "List collections", "args": [{ "name": "db", "required": true }] },
13
- "schema": { "description": "Infer schema", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "samples", "required": false }] },
14
- "find": { "description": "Find documents", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }, { "name": "limit", "required": false }] },
15
- "one": { "description": "Single document", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] },
16
- "aggregate": { "description": "Run aggregation pipeline", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "pipeline", "required": true }] },
17
- "count": { "description": "Count documents", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] },
18
- "indexes": { "description": "List indexes", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }] },
19
- "stats": { "description": "Collection statistics", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }] },
20
- "distinct": { "description": "Distinct values", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "field", "required": true }, { "name": "query", "required": false }] },
21
- "sample": { "description": "Random sample", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "size", "required": false }] },
22
- "explain": { "description": "Query explanation", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] }
16
+ "databases": {
17
+ "description": "List all databases"
18
+ },
19
+ "collections": {
20
+ "description": "List collections",
21
+ "args": [
22
+ {
23
+ "name": "db",
24
+ "required": true
25
+ }
26
+ ]
27
+ },
28
+ "schema": {
29
+ "description": "Infer schema",
30
+ "args": [
31
+ {
32
+ "name": "db",
33
+ "required": true
34
+ },
35
+ {
36
+ "name": "collection",
37
+ "required": true
38
+ },
39
+ {
40
+ "name": "samples",
41
+ "required": false
42
+ }
43
+ ]
44
+ },
45
+ "find": {
46
+ "description": "Find documents",
47
+ "args": [
48
+ {
49
+ "name": "db",
50
+ "required": true
51
+ },
52
+ {
53
+ "name": "collection",
54
+ "required": true
55
+ },
56
+ {
57
+ "name": "query",
58
+ "required": false
59
+ },
60
+ {
61
+ "name": "limit",
62
+ "required": false
63
+ }
64
+ ]
65
+ },
66
+ "one": {
67
+ "description": "Single document",
68
+ "args": [
69
+ {
70
+ "name": "db",
71
+ "required": true
72
+ },
73
+ {
74
+ "name": "collection",
75
+ "required": true
76
+ },
77
+ {
78
+ "name": "query",
79
+ "required": false
80
+ }
81
+ ]
82
+ },
83
+ "aggregate": {
84
+ "description": "Run aggregation pipeline",
85
+ "args": [
86
+ {
87
+ "name": "db",
88
+ "required": true
89
+ },
90
+ {
91
+ "name": "collection",
92
+ "required": true
93
+ },
94
+ {
95
+ "name": "pipeline",
96
+ "required": true
97
+ }
98
+ ]
99
+ },
100
+ "count": {
101
+ "description": "Count documents",
102
+ "args": [
103
+ {
104
+ "name": "db",
105
+ "required": true
106
+ },
107
+ {
108
+ "name": "collection",
109
+ "required": true
110
+ },
111
+ {
112
+ "name": "query",
113
+ "required": false
114
+ }
115
+ ]
116
+ },
117
+ "indexes": {
118
+ "description": "List indexes",
119
+ "args": [
120
+ {
121
+ "name": "db",
122
+ "required": true
123
+ },
124
+ {
125
+ "name": "collection",
126
+ "required": true
127
+ }
128
+ ]
129
+ },
130
+ "stats": {
131
+ "description": "Collection statistics",
132
+ "args": [
133
+ {
134
+ "name": "db",
135
+ "required": true
136
+ },
137
+ {
138
+ "name": "collection",
139
+ "required": true
140
+ }
141
+ ]
142
+ },
143
+ "distinct": {
144
+ "description": "Distinct values",
145
+ "args": [
146
+ {
147
+ "name": "db",
148
+ "required": true
149
+ },
150
+ {
151
+ "name": "collection",
152
+ "required": true
153
+ },
154
+ {
155
+ "name": "field",
156
+ "required": true
157
+ },
158
+ {
159
+ "name": "query",
160
+ "required": false
161
+ }
162
+ ]
163
+ },
164
+ "sample": {
165
+ "description": "Random sample",
166
+ "args": [
167
+ {
168
+ "name": "db",
169
+ "required": true
170
+ },
171
+ {
172
+ "name": "collection",
173
+ "required": true
174
+ },
175
+ {
176
+ "name": "size",
177
+ "required": false
178
+ }
179
+ ]
180
+ },
181
+ "explain": {
182
+ "description": "Query explanation",
183
+ "args": [
184
+ {
185
+ "name": "db",
186
+ "required": true
187
+ },
188
+ {
189
+ "name": "collection",
190
+ "required": true
191
+ },
192
+ {
193
+ "name": "query",
194
+ "required": false
195
+ }
196
+ ]
197
+ }
23
198
  },
24
199
  "env": {
25
- "MONGO_URI": { "required": false, "description": "MongoDB connection URI (default: localhost:27017)" }
200
+ "MONGO_URI": {
201
+ "required": false,
202
+ "description": "MongoDB connection URI (default: localhost:27017)"
203
+ }
26
204
  },
27
205
  "dependencies": {
28
206
  "@cli4ai/lib": "^1.0.0",
29
207
  "mongodb": "^6.0.0",
30
208
  "commander": "^14.0.0"
31
209
  },
32
- "mcp": { "enabled": true, "transport": "stdio" }
210
+ "mcp": {
211
+ "enabled": true,
212
+ "transport": "stdio"
213
+ }
33
214
  }
package/dist/run.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/run.js ADDED
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { MongoClient } from 'mongodb';
3
+ import { cli, loadEnv, output, withErrorHandling, parseJson } from '@cli4ai/lib';
4
+ loadEnv();
5
+ const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017';
6
+ async function withClient(fn) {
7
+ const client = new MongoClient(MONGO_URI);
8
+ try {
9
+ await client.connect();
10
+ return await fn(client);
11
+ }
12
+ finally {
13
+ await client.close();
14
+ }
15
+ }
16
+ // Infer schema from documents by analyzing field types
17
+ function inferSchema(docs) {
18
+ const schema = {};
19
+ function getType(value) {
20
+ if (value === null)
21
+ return 'null';
22
+ if (Array.isArray(value))
23
+ return 'array';
24
+ if (value instanceof Date)
25
+ return 'date';
26
+ if (typeof value === 'object' && value.constructor?.name === 'ObjectId')
27
+ return 'ObjectId';
28
+ if (typeof value === 'object' && value.constructor?.name === 'Binary')
29
+ return 'Binary';
30
+ return typeof value;
31
+ }
32
+ function processDoc(doc, prefix = '') {
33
+ for (const [key, value] of Object.entries(doc)) {
34
+ const path = prefix ? `${prefix}.${key}` : key;
35
+ const type = getType(value);
36
+ if (!schema[path]) {
37
+ schema[path] = { types: new Set(), sample: value, count: 0 };
38
+ }
39
+ schema[path].types.add(type);
40
+ schema[path].count++;
41
+ // Recurse into objects (but not arrays or special types)
42
+ if (type === 'object' && value !== null) {
43
+ processDoc(value, path);
44
+ }
45
+ }
46
+ }
47
+ docs.forEach(doc => processDoc(doc));
48
+ // Convert to output format
49
+ const result = {};
50
+ for (const [path, info] of Object.entries(schema)) {
51
+ result[path] = {
52
+ types: Array.from(info.types),
53
+ sample: info.sample,
54
+ frequency: `${info.count}/${docs.length}`
55
+ };
56
+ }
57
+ return result;
58
+ }
59
+ const program = cli('mongodb', '1.0.0', 'MongoDB queries');
60
+ program
61
+ .command('databases')
62
+ .description('List all databases')
63
+ .action(withErrorHandling(async () => {
64
+ await withClient(async (client) => {
65
+ const admin = client.db().admin();
66
+ const result = await admin.listDatabases();
67
+ output(result.databases.map(db => ({
68
+ name: db.name,
69
+ sizeOnDisk: db.sizeOnDisk,
70
+ sizeMB: db.sizeOnDisk ? Math.round(db.sizeOnDisk / 1024 / 1024 * 100) / 100 : 0
71
+ })));
72
+ });
73
+ }));
74
+ program
75
+ .command('collections <db>')
76
+ .description('List collections')
77
+ .action(withErrorHandling(async (dbName) => {
78
+ await withClient(async (client) => {
79
+ const db = client.db(dbName);
80
+ const collections = await db.listCollections().toArray();
81
+ output(collections.map(c => ({ name: c.name, type: c.type })));
82
+ });
83
+ }));
84
+ program
85
+ .command('schema <db> <collection> [samples]')
86
+ .description('Infer schema (default: 100 samples)')
87
+ .action(withErrorHandling(async (dbName, collName, sampleSize = '100') => {
88
+ await withClient(async (client) => {
89
+ const collection = client.db(dbName).collection(collName);
90
+ const docs = await collection.find({}).limit(parseInt(sampleSize)).toArray();
91
+ const schema = inferSchema(docs);
92
+ output({
93
+ collection: `${dbName}.${collName}`,
94
+ sampledDocs: docs.length,
95
+ fields: schema
96
+ });
97
+ });
98
+ }));
99
+ program
100
+ .command('find <db> <collection> [query] [limit]')
101
+ .description('Find documents (default: {}, 10)')
102
+ .action(withErrorHandling(async (dbName, collName, query = '{}', limit = '10') => {
103
+ await withClient(async (client) => {
104
+ const collection = client.db(dbName).collection(collName);
105
+ const docs = await collection.find(parseJson(query, 'query')).limit(parseInt(limit)).toArray();
106
+ output(docs);
107
+ });
108
+ }));
109
+ program
110
+ .command('one <db> <collection> [query]')
111
+ .description('Single document')
112
+ .action(withErrorHandling(async (dbName, collName, query = '{}') => {
113
+ await withClient(async (client) => {
114
+ const collection = client.db(dbName).collection(collName);
115
+ const doc = await collection.findOne(parseJson(query, 'query'));
116
+ output(doc);
117
+ });
118
+ }));
119
+ program
120
+ .command('aggregate <db> <collection> <pipeline>')
121
+ .description('Run aggregation pipeline')
122
+ .action(withErrorHandling(async (dbName, collName, pipeline) => {
123
+ await withClient(async (client) => {
124
+ const collection = client.db(dbName).collection(collName);
125
+ const docs = await collection.aggregate(parseJson(pipeline, 'pipeline')).toArray();
126
+ output(docs);
127
+ });
128
+ }));
129
+ program
130
+ .command('count <db> <collection> [query]')
131
+ .description('Count documents')
132
+ .action(withErrorHandling(async (dbName, collName, query = '{}') => {
133
+ await withClient(async (client) => {
134
+ const collection = client.db(dbName).collection(collName);
135
+ const count = await collection.countDocuments(parseJson(query, 'query'));
136
+ output({ collection: `${dbName}.${collName}`, count });
137
+ });
138
+ }));
139
+ program
140
+ .command('indexes <db> <collection>')
141
+ .description('List indexes')
142
+ .action(withErrorHandling(async (dbName, collName) => {
143
+ await withClient(async (client) => {
144
+ const collection = client.db(dbName).collection(collName);
145
+ const indexes = await collection.indexes();
146
+ output(indexes);
147
+ });
148
+ }));
149
+ program
150
+ .command('stats <db> <collection>')
151
+ .description('Collection statistics')
152
+ .action(withErrorHandling(async (dbName, collName) => {
153
+ await withClient(async (client) => {
154
+ const db = client.db(dbName);
155
+ const stats = await db.command({ collStats: collName });
156
+ output({
157
+ ns: stats.ns,
158
+ count: stats.count,
159
+ size: stats.size,
160
+ sizeMB: Math.round(stats.size / 1024 / 1024 * 100) / 100,
161
+ avgObjSize: stats.avgObjSize,
162
+ storageSize: stats.storageSize,
163
+ indexes: stats.nindexes,
164
+ indexSize: stats.totalIndexSize
165
+ });
166
+ });
167
+ }));
168
+ program
169
+ .command('distinct <db> <collection> <field> [query]')
170
+ .description('Distinct values')
171
+ .action(withErrorHandling(async (dbName, collName, field, query = '{}') => {
172
+ await withClient(async (client) => {
173
+ const collection = client.db(dbName).collection(collName);
174
+ const values = await collection.distinct(field, parseJson(query, 'query'));
175
+ output({ field, count: values.length, values });
176
+ });
177
+ }));
178
+ program
179
+ .command('sample <db> <collection> [size]')
180
+ .description('Random sample (default: 5)')
181
+ .action(withErrorHandling(async (dbName, collName, size = '5') => {
182
+ await withClient(async (client) => {
183
+ const collection = client.db(dbName).collection(collName);
184
+ const docs = await collection.aggregate([{ $sample: { size: parseInt(size) } }]).toArray();
185
+ output(docs);
186
+ });
187
+ }));
188
+ program
189
+ .command('explain <db> <collection> [query]')
190
+ .description('Query explanation')
191
+ .action(withErrorHandling(async (dbName, collName, query = '{}') => {
192
+ await withClient(async (client) => {
193
+ const collection = client.db(dbName).collection(collName);
194
+ const explanation = await collection.find(parseJson(query, 'query')).explain('executionStats');
195
+ output(explanation);
196
+ });
197
+ }));
198
+ program.parse();
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@cli4ai/mongodb",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "MongoDB queries",
5
5
  "author": "cliforai",
6
- "license": "MIT",
7
- "main": "run.ts",
6
+ "license": "BUSL-1.1",
7
+ "main": "dist/run.js",
8
8
  "bin": {
9
- "mongodb": "./run.ts"
9
+ "mongodb": "./dist/run.js"
10
10
  },
11
11
  "type": "module",
12
12
  "keywords": [
@@ -33,12 +33,20 @@
33
33
  "commander": "^14.0.0"
34
34
  },
35
35
  "files": [
36
- "run.ts",
36
+ "dist",
37
37
  "cli4ai.json",
38
38
  "README.md",
39
39
  "LICENSE"
40
40
  ],
41
41
  "publishConfig": {
42
42
  "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "prepublishOnly": "npm run build"
47
+ },
48
+ "devDependencies": {
49
+ "typescript": "^5.0.0",
50
+ "@types/node": "^22.0.0"
43
51
  }
44
52
  }
package/run.ts DELETED
@@ -1,216 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- import { MongoClient, Document } from 'mongodb';
3
- import { cli, loadEnv, output, outputError, withErrorHandling, parseJson } from '@cli4ai/lib/cli.ts';
4
-
5
- loadEnv();
6
-
7
- const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017';
8
-
9
- async function withClient<T>(fn: (client: MongoClient) => Promise<T>): Promise<T> {
10
- const client = new MongoClient(MONGO_URI);
11
- try {
12
- await client.connect();
13
- return await fn(client);
14
- } finally {
15
- await client.close();
16
- }
17
- }
18
-
19
- // Infer schema from documents by analyzing field types
20
- function inferSchema(docs: Document[]): Document {
21
- const schema: Record<string, { types: Set<string>; sample: unknown; count: number }> = {};
22
-
23
- function getType(value: unknown): string {
24
- if (value === null) return 'null';
25
- if (Array.isArray(value)) return 'array';
26
- if (value instanceof Date) return 'date';
27
- if (typeof value === 'object' && (value as object).constructor?.name === 'ObjectId') return 'ObjectId';
28
- if (typeof value === 'object' && (value as object).constructor?.name === 'Binary') return 'Binary';
29
- return typeof value;
30
- }
31
-
32
- function processDoc(doc: Document, prefix = ''): void {
33
- for (const [key, value] of Object.entries(doc)) {
34
- const path = prefix ? `${prefix}.${key}` : key;
35
- const type = getType(value);
36
-
37
- if (!schema[path]) {
38
- schema[path] = { types: new Set(), sample: value, count: 0 };
39
- }
40
- schema[path].types.add(type);
41
- schema[path].count++;
42
-
43
- // Recurse into objects (but not arrays or special types)
44
- if (type === 'object' && value !== null) {
45
- processDoc(value as Document, path);
46
- }
47
- }
48
- }
49
-
50
- docs.forEach(doc => processDoc(doc));
51
-
52
- // Convert to output format
53
- const result: Record<string, { types: string[]; sample: unknown; frequency: string }> = {};
54
- for (const [path, info] of Object.entries(schema)) {
55
- result[path] = {
56
- types: Array.from(info.types),
57
- sample: info.sample,
58
- frequency: `${info.count}/${docs.length}`
59
- };
60
- }
61
- return result;
62
- }
63
-
64
- const program = cli('mongodb', '1.0.0', 'MongoDB queries');
65
-
66
- program
67
- .command('databases')
68
- .description('List all databases')
69
- .action(withErrorHandling(async () => {
70
- await withClient(async (client) => {
71
- const admin = client.db().admin();
72
- const result = await admin.listDatabases();
73
- output(result.databases.map(db => ({
74
- name: db.name,
75
- sizeOnDisk: db.sizeOnDisk,
76
- sizeMB: db.sizeOnDisk ? Math.round(db.sizeOnDisk / 1024 / 1024 * 100) / 100 : 0
77
- })));
78
- });
79
- }));
80
-
81
- program
82
- .command('collections <db>')
83
- .description('List collections')
84
- .action(withErrorHandling(async (dbName: string) => {
85
- await withClient(async (client) => {
86
- const db = client.db(dbName);
87
- const collections = await db.listCollections().toArray();
88
- output(collections.map(c => ({ name: c.name, type: c.type })));
89
- });
90
- }));
91
-
92
- program
93
- .command('schema <db> <collection> [samples]')
94
- .description('Infer schema (default: 100 samples)')
95
- .action(withErrorHandling(async (dbName: string, collName: string, sampleSize = '100') => {
96
- await withClient(async (client) => {
97
- const collection = client.db(dbName).collection(collName);
98
- const docs = await collection.find({}).limit(parseInt(sampleSize)).toArray();
99
- const schema = inferSchema(docs);
100
- output({
101
- collection: `${dbName}.${collName}`,
102
- sampledDocs: docs.length,
103
- fields: schema
104
- });
105
- });
106
- }));
107
-
108
- program
109
- .command('find <db> <collection> [query] [limit]')
110
- .description('Find documents (default: {}, 10)')
111
- .action(withErrorHandling(async (dbName: string, collName: string, query = '{}', limit = '10') => {
112
- await withClient(async (client) => {
113
- const collection = client.db(dbName).collection(collName);
114
- const docs = await collection.find(parseJson(query, 'query')).limit(parseInt(limit)).toArray();
115
- output(docs);
116
- });
117
- }));
118
-
119
- program
120
- .command('one <db> <collection> [query]')
121
- .description('Single document')
122
- .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
123
- await withClient(async (client) => {
124
- const collection = client.db(dbName).collection(collName);
125
- const doc = await collection.findOne(parseJson(query, 'query'));
126
- output(doc);
127
- });
128
- }));
129
-
130
- program
131
- .command('aggregate <db> <collection> <pipeline>')
132
- .description('Run aggregation pipeline')
133
- .action(withErrorHandling(async (dbName: string, collName: string, pipeline: string) => {
134
- await withClient(async (client) => {
135
- const collection = client.db(dbName).collection(collName);
136
- const docs = await collection.aggregate(parseJson(pipeline, 'pipeline')).toArray();
137
- output(docs);
138
- });
139
- }));
140
-
141
- program
142
- .command('count <db> <collection> [query]')
143
- .description('Count documents')
144
- .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
145
- await withClient(async (client) => {
146
- const collection = client.db(dbName).collection(collName);
147
- const count = await collection.countDocuments(parseJson(query, 'query'));
148
- output({ collection: `${dbName}.${collName}`, count });
149
- });
150
- }));
151
-
152
- program
153
- .command('indexes <db> <collection>')
154
- .description('List indexes')
155
- .action(withErrorHandling(async (dbName: string, collName: string) => {
156
- await withClient(async (client) => {
157
- const collection = client.db(dbName).collection(collName);
158
- const indexes = await collection.indexes();
159
- output(indexes);
160
- });
161
- }));
162
-
163
- program
164
- .command('stats <db> <collection>')
165
- .description('Collection statistics')
166
- .action(withErrorHandling(async (dbName: string, collName: string) => {
167
- await withClient(async (client) => {
168
- const db = client.db(dbName);
169
- const stats = await db.command({ collStats: collName });
170
- output({
171
- ns: stats.ns,
172
- count: stats.count,
173
- size: stats.size,
174
- sizeMB: Math.round(stats.size / 1024 / 1024 * 100) / 100,
175
- avgObjSize: stats.avgObjSize,
176
- storageSize: stats.storageSize,
177
- indexes: stats.nindexes,
178
- indexSize: stats.totalIndexSize
179
- });
180
- });
181
- }));
182
-
183
- program
184
- .command('distinct <db> <collection> <field> [query]')
185
- .description('Distinct values')
186
- .action(withErrorHandling(async (dbName: string, collName: string, field: string, query = '{}') => {
187
- await withClient(async (client) => {
188
- const collection = client.db(dbName).collection(collName);
189
- const values = await collection.distinct(field, parseJson(query, 'query'));
190
- output({ field, count: values.length, values });
191
- });
192
- }));
193
-
194
- program
195
- .command('sample <db> <collection> [size]')
196
- .description('Random sample (default: 5)')
197
- .action(withErrorHandling(async (dbName: string, collName: string, size = '5') => {
198
- await withClient(async (client) => {
199
- const collection = client.db(dbName).collection(collName);
200
- const docs = await collection.aggregate([{ $sample: { size: parseInt(size) } }]).toArray();
201
- output(docs);
202
- });
203
- }));
204
-
205
- program
206
- .command('explain <db> <collection> [query]')
207
- .description('Query explanation')
208
- .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
209
- await withClient(async (client) => {
210
- const collection = client.db(dbName).collection(collName);
211
- const explanation = await collection.find(parseJson(query, 'query')).explain('executionStats');
212
- output(explanation);
213
- });
214
- }));
215
-
216
- program.parse();