dolphin-server-modules 2.11.1 → 2.11.2

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 (115) hide show
  1. package/TUTORIAL_NEPALI.md +181 -0
  2. package/dist/adapters/mongoose/index.test.d.ts +1 -0
  3. package/dist/adapters/mongoose/index.test.js +145 -0
  4. package/dist/adapters/mongoose/index.test.js.map +1 -0
  5. package/dist/adapters/mongoose/integration.test.d.ts +5 -0
  6. package/dist/adapters/mongoose/integration.test.js +217 -0
  7. package/dist/adapters/mongoose/integration.test.js.map +1 -0
  8. package/dist/ai/dolphin-agent/agent.d.ts +5 -0
  9. package/dist/ai/dolphin-agent/agent.js +93 -46
  10. package/dist/ai/dolphin-agent/agent.js.map +1 -1
  11. package/dist/ai/dolphin-agent/config.js +19 -23
  12. package/dist/ai/dolphin-agent/config.js.map +1 -1
  13. package/dist/auth/auth.test.d.ts +1 -0
  14. package/dist/auth/auth.test.js +286 -0
  15. package/dist/auth/auth.test.js.map +1 -0
  16. package/dist/authController/authController.js +1 -1
  17. package/dist/authController/authController.js.map +1 -1
  18. package/dist/authController/authController.test.d.ts +1 -0
  19. package/dist/authController/authController.test.js +359 -0
  20. package/dist/authController/authController.test.js.map +1 -0
  21. package/dist/bin/cli.js +494 -131
  22. package/dist/bin/cli.js.map +1 -1
  23. package/dist/client.test.d.ts +22 -0
  24. package/dist/client.test.js +573 -0
  25. package/dist/client.test.js.map +1 -0
  26. package/dist/controller/controller.test.d.ts +1 -0
  27. package/dist/controller/controller.test.js +37 -0
  28. package/dist/controller/controller.test.js.map +1 -0
  29. package/dist/curd/crud.test.d.ts +1 -0
  30. package/dist/curd/crud.test.js +104 -0
  31. package/dist/curd/crud.test.js.map +1 -0
  32. package/dist/demo-server.d.ts +1 -0
  33. package/dist/demo-server.js +191 -0
  34. package/dist/demo-server.js.map +1 -0
  35. package/dist/djson/djson.test.d.ts +1 -0
  36. package/dist/djson/djson.test.js +200 -0
  37. package/dist/djson/djson.test.js.map +1 -0
  38. package/dist/dolphin-bench.d.ts +1 -0
  39. package/dist/dolphin-bench.js +63 -0
  40. package/dist/dolphin-bench.js.map +1 -0
  41. package/dist/hard-performance-test.d.ts +1 -0
  42. package/dist/hard-performance-test.js +97 -0
  43. package/dist/hard-performance-test.js.map +1 -0
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.js +4 -4
  46. package/dist/index.js.map +1 -1
  47. package/dist/middleware/zod.test.d.ts +1 -0
  48. package/dist/middleware/zod.test.js +74 -0
  49. package/dist/middleware/zod.test.js.map +1 -0
  50. package/dist/performance-test.d.ts +1 -0
  51. package/dist/performance-test.js +92 -0
  52. package/dist/performance-test.js.map +1 -0
  53. package/dist/real-test-mongoose.d.ts +1 -0
  54. package/dist/real-test-mongoose.js +104 -0
  55. package/dist/real-test-mongoose.js.map +1 -0
  56. package/dist/realtime/camera.d.ts +119 -0
  57. package/dist/realtime/camera.js +299 -0
  58. package/dist/realtime/camera.js.map +1 -0
  59. package/dist/realtime/camera.test.d.ts +1 -0
  60. package/dist/realtime/camera.test.js +345 -0
  61. package/dist/realtime/camera.test.js.map +1 -0
  62. package/dist/realtime/core.d.ts +5 -5
  63. package/dist/realtime/core.js +6 -6
  64. package/dist/realtime/core.js.map +1 -1
  65. package/dist/realtime/index.d.ts +6 -4
  66. package/dist/realtime/index.js +6 -4
  67. package/dist/realtime/index.js.map +1 -1
  68. package/dist/realtime/realtime.test.d.ts +1 -0
  69. package/dist/realtime/realtime.test.js +623 -0
  70. package/dist/realtime/realtime.test.js.map +1 -0
  71. package/dist/realtime/rtsp.d.ts +65 -0
  72. package/dist/realtime/rtsp.js +410 -0
  73. package/dist/realtime/rtsp.js.map +1 -0
  74. package/dist/realtime/rtsp.test.d.ts +1 -0
  75. package/dist/realtime/rtsp.test.js +361 -0
  76. package/dist/realtime/rtsp.test.js.map +1 -0
  77. package/dist/router/router.test.d.ts +1 -0
  78. package/dist/router/router.test.js +45 -0
  79. package/dist/router/router.test.js.map +1 -0
  80. package/dist/server/server.d.ts +8 -8
  81. package/dist/server/server.js +2 -11
  82. package/dist/server/server.js.map +1 -1
  83. package/dist/server/server.test.d.ts +1 -0
  84. package/dist/server/server.test.js +299 -0
  85. package/dist/server/server.test.js.map +1 -0
  86. package/dist/services/ai-service.js +22 -11
  87. package/dist/services/ai-service.js.map +1 -1
  88. package/dist/signaling/index.d.ts +1 -1
  89. package/dist/signaling/signaling.test.d.ts +1 -0
  90. package/dist/signaling/signaling.test.js +112 -0
  91. package/dist/signaling/signaling.test.js.map +1 -0
  92. package/dist/swagger/swagger.test.d.ts +1 -0
  93. package/dist/swagger/swagger.test.js +38 -0
  94. package/dist/swagger/swagger.test.js.map +1 -0
  95. package/dist/templates/index.d.ts +6 -0
  96. package/dist/templates/index.js +247 -70
  97. package/dist/templates/index.js.map +1 -1
  98. package/dist/test-2fa-real.d.ts +1 -0
  99. package/dist/test-2fa-real.js +105 -0
  100. package/dist/test-2fa-real.js.map +1 -0
  101. package/dist/test-dolphin.d.ts +1 -0
  102. package/dist/test-dolphin.js +98 -0
  103. package/dist/test-dolphin.js.map +1 -0
  104. package/dist/utils/ctx.d.ts +50 -0
  105. package/dist/utils/ctx.js +82 -0
  106. package/dist/utils/ctx.js.map +1 -0
  107. package/package.json +156 -65
  108. package/scripts/client.js +838 -703
  109. package/scripts/benchmark.js +0 -12
  110. package/scripts/benchmark.ts +0 -12
  111. package/scripts/list-models.js +0 -34
  112. package/scripts/run-real-ai-test.js +0 -79
  113. package/scripts/test-ai-logic.js +0 -44
  114. package/scripts/test-client.js +0 -105
  115. package/scripts/test-dolphin.js +0 -36
package/dist/bin/cli.js CHANGED
@@ -2,54 +2,266 @@
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { createServer } from 'http';
5
+ import net from 'net';
6
+ import { fileURLToPath } from 'url';
5
7
  import { AIService } from '../services/ai-service.js';
6
8
  import { CLIUI } from '../utils/ui.js';
7
9
  import { TEMPLATES } from '../templates/index.js';
8
10
  const args = process.argv.slice(2);
9
11
  const command = args[0] || 'help';
10
- // Simple .env loader
11
- const envPath = path.join(process.cwd(), '.env');
12
- if (fs.existsSync(envPath)) {
13
- const envContent = fs.readFileSync(envPath, 'utf8');
14
- envContent.split('\n').forEach(line => {
15
- const [key, ...valueParts] = line.split('=');
16
- if (key && valueParts.length > 0) {
17
- process.env[key.trim()] = valueParts.join('=').trim();
18
- }
12
+ // Version — package.json बाट dynamic पढ्ने
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8'));
16
+ const VERSION = pkg.version;
17
+ // .env loader
18
+ const envFilePath = path.join(process.cwd(), '.env');
19
+ if (fs.existsSync(envFilePath)) {
20
+ fs.readFileSync(envFilePath, 'utf8').split('\n').forEach(line => {
21
+ const trimmed = line.trim();
22
+ if (!trimmed || trimmed.startsWith('#'))
23
+ return;
24
+ const eqIdx = trimmed.indexOf('=');
25
+ if (eqIdx === -1)
26
+ return;
27
+ const key = trimmed.slice(0, eqIdx).trim();
28
+ const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
29
+ if (key && !process.env[key])
30
+ process.env[key] = value;
19
31
  });
20
32
  }
21
33
  const aiConfig = {
22
34
  apiKey: (process.env.DOLPHIN_AI_KEY || process.env.GEMINI_API_KEY || '').trim(),
23
35
  baseUrl: process.env.DOLPHIN_AI_BASE_URL,
24
- model: process.env.DOLPHIN_AI_MODEL
36
+ model: process.env.DOLPHIN_AI_MODEL,
25
37
  };
26
38
  const useOllama = process.env.USE_OLLAMA === 'true';
27
39
  const hasAnyKey = aiConfig.apiKey || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY;
28
40
  if (!hasAnyKey && !useOllama && ['generate', 'generate-full', 'chat'].includes(command)) {
29
- CLIUI.error('API Key not found! Please set DOLPHIN_AI_KEY, GEMINI_API_KEY, or GROQ_API_KEY in your .env file.');
41
+ CLIUI.error('API Key फेला परेन! .env मा DOLPHIN_AI_KEY, GEMINI_API_KEY, वा GROQ_API_KEY राख्नुहोस्।');
30
42
  process.exit(1);
31
43
  }
32
44
  const ai = new AIService(aiConfig);
45
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
46
+ function ensureDir(dir) {
47
+ if (!fs.existsSync(dir))
48
+ fs.mkdirSync(dir, { recursive: true });
49
+ }
50
+ function writeFile(filePath, content, label) {
51
+ ensureDir(path.dirname(filePath));
52
+ fs.writeFileSync(filePath, content);
53
+ console.log(` 📄 Created: ${label || path.relative(process.cwd(), filePath)}`);
54
+ }
55
+ function capitalize(s) {
56
+ return s.charAt(0).toUpperCase() + s.slice(1);
57
+ }
58
+ /** TCP port check — live connection test गर्ने */
59
+ function checkTcpPort(host, port, timeoutMs = 4000) {
60
+ return new Promise((resolve) => {
61
+ const socket = new net.Socket();
62
+ socket.setTimeout(timeoutMs);
63
+ socket.once('connect', () => { socket.destroy(); resolve(true); });
64
+ socket.once('error', () => { socket.destroy(); resolve(false); });
65
+ socket.once('timeout', () => { socket.destroy(); resolve(false); });
66
+ socket.connect(port, host);
67
+ });
68
+ }
69
+ /** URI बाट host र port निकाल्ने */
70
+ function parseUri(uri) {
71
+ try {
72
+ const u = new URL(uri);
73
+ const defaults = {
74
+ 'mongodb:': 27017, 'mongodb+srv:': 27017,
75
+ 'redis:': 6379, 'rediss:': 6380,
76
+ };
77
+ return {
78
+ host: u.hostname || 'localhost',
79
+ port: parseInt(u.port || '') || defaults[u.protocol] || 3000,
80
+ };
81
+ }
82
+ catch {
83
+ return { host: 'localhost', port: 27017 };
84
+ }
85
+ }
86
+ /**
87
+ * Automatically registers the newly created Mongoose model in the entry file (e.g. app.js)
88
+ * where createMongooseAdapter is called, by adding the model import and inserting it into
89
+ * the adapter initialization parameters.
90
+ */
91
+ function registerModelInAdapter(modelName) {
92
+ const entryFiles = ['app.js', 'app.ts', 'server.js', 'server.ts', 'index.js', 'index.ts'];
93
+ let foundFile = '';
94
+ let content = '';
95
+ for (const file of entryFiles) {
96
+ const fullPath = path.join(process.cwd(), file);
97
+ if (fs.existsSync(fullPath)) {
98
+ const txt = fs.readFileSync(fullPath, 'utf8');
99
+ if (txt.includes('createMongooseAdapter')) {
100
+ foundFile = fullPath;
101
+ content = txt;
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ if (!foundFile) {
107
+ console.log(` ⚠️ Could not find any file calling createMongooseAdapter to register ${modelName}.`);
108
+ return;
109
+ }
110
+ // 1. Add Import
111
+ if (content.includes(`models/${modelName}.js`) || content.includes(`models/${modelName}`)) {
112
+ console.log(` ⚠️ ${modelName} is already imported in ${path.basename(foundFile)}`);
113
+ }
114
+ else {
115
+ const importLine = `import { ${modelName} } from './models/${modelName}.js';\n`;
116
+ const modelImportRegex = /import\s+{[^}]+}\s+from\s+['"].\/models\/[^'"]+['"];/g;
117
+ const matches = [...content.matchAll(modelImportRegex)];
118
+ if (matches.length > 0) {
119
+ const lastMatch = matches[matches.length - 1];
120
+ const insertPos = lastMatch.index + lastMatch[0].length;
121
+ content = content.slice(0, insertPos) + '\n' + importLine + content.slice(insertPos);
122
+ }
123
+ else {
124
+ const firstImportIdx = content.indexOf('import ');
125
+ if (firstImportIdx !== -1) {
126
+ content = content.slice(0, firstImportIdx) + importLine + content.slice(firstImportIdx);
127
+ }
128
+ else {
129
+ content = importLine + content;
130
+ }
131
+ }
132
+ }
133
+ // 2. Add to createMongooseAdapter arguments
134
+ const adapterCallRegex = /createMongooseAdapter\s*\(\s*\{([\s\S]*?)\}\s*\)/;
135
+ const match = content.match(adapterCallRegex);
136
+ if (match) {
137
+ const innerContent = match[1];
138
+ if (innerContent.includes(modelName)) {
139
+ console.log(` ⚠️ ${modelName} already registered in createMongooseAdapter`);
140
+ }
141
+ else {
142
+ let updatedInner = '';
143
+ if (innerContent.includes('models:')) {
144
+ const modelsRegex = /models\s*:\s*\{\s*([\s\S]*?)\s*\}/;
145
+ const modelsMatch = innerContent.match(modelsRegex);
146
+ if (modelsMatch) {
147
+ const existingModels = modelsMatch[1].trim();
148
+ const updatedModels = existingModels ? `${modelName}, ${existingModels}` : modelName;
149
+ updatedInner = innerContent.replace(modelsRegex, `models: { ${updatedModels} }`);
150
+ }
151
+ else {
152
+ updatedInner = innerContent.trim() + `, models: { ${modelName} }`;
153
+ }
154
+ }
155
+ else {
156
+ const trimmedInner = innerContent.trim();
157
+ updatedInner = trimmedInner ? `${trimmedInner}, models: { ${modelName} }` : `models: { ${modelName} }`;
158
+ }
159
+ content = content.replace(adapterCallRegex, `createMongooseAdapter({ ${updatedInner} })`);
160
+ fs.writeFileSync(foundFile, content);
161
+ console.log(` ✅ Registered: ${modelName} registered in ${path.relative(process.cwd(), foundFile)}`);
162
+ }
163
+ }
164
+ else {
165
+ console.log(` ⚠️ Could not parse createMongooseAdapter call in ${path.basename(foundFile)}`);
166
+ }
167
+ }
168
+ // ─── Commands ─────────────────────────────────────────────────────────────────
33
169
  async function run() {
34
170
  switch (command) {
35
- case 'serve':
36
- const port = parseInt(args.find(arg => arg.startsWith('--port='))?.split('=')[1] || '3000');
37
- CLIUI.heading(`Dolphin Dev Server starting on port ${port}`);
171
+ // ── serve ────────────────────────────────────────────────────────────
172
+ case 'serve': {
173
+ const port = parseInt(args.find(a => a.startsWith('--port='))?.split('=')[1] || '3000');
174
+ CLIUI.heading(`Dolphin Dev Server — port ${port}`);
38
175
  const server = createServer((req, res) => {
39
176
  res.writeHead(200, { 'Content-Type': 'text/plain' });
40
- res.end('Dolphin Server is swimming!\n');
177
+ res.end('🐬 Dolphin Server is swimming!\n');
41
178
  });
42
179
  server.listen(port, () => {
43
180
  CLIUI.success(`Server is live at http://localhost:${port}`);
181
+ console.log(' Ctrl+C थिचेर रोक्नुहोस्।');
44
182
  });
45
183
  break;
46
- case 'generate':
184
+ }
185
+ // ── connect mongoose|redis ───────────────────────────────────────────
186
+ case 'connect': {
187
+ const dbType = args[1];
188
+ const uriArg = args[2];
189
+ if (dbType === 'mongoose' || dbType === 'mongo' || dbType === 'mongodb') {
190
+ const uri = uriArg || process.env.MONGO_URI || 'mongodb://localhost:27017';
191
+ CLIUI.heading('MongoDB Connection Test');
192
+ console.log(` URI: ${uri.replace(/:\/\/[^@]+@/, '://***@')}`);
193
+ // Step 1: TCP check
194
+ CLIUI.startSpinner('TCP ping...');
195
+ const { host, port } = parseUri(uri);
196
+ const tcpOk = await checkTcpPort(host, port);
197
+ CLIUI.stopSpinner(tcpOk, tcpOk ? `Host reachable: ${host}:${port}` : `Cannot reach ${host}:${port}`);
198
+ if (!tcpOk) {
199
+ console.log('\n \x1b[33mFix — MongoDB start गर्नुहोस्:\x1b[0m');
200
+ console.log(' brew services start mongodb-community (macOS)');
201
+ console.log(' sudo systemctl start mongod (Linux)');
202
+ console.log(' mongod (manual)');
203
+ process.exit(1);
204
+ }
205
+ // Step 2: Mongoose check
206
+ CLIUI.startSpinner('Mongoose handshake...');
207
+ try {
208
+ const mongoose = await import('mongoose');
209
+ await mongoose.default.connect(uri, { serverSelectionTimeoutMS: 5000 });
210
+ const dbHost = mongoose.default.connection.host;
211
+ await mongoose.default.disconnect();
212
+ CLIUI.stopSpinner(true, `Connected! Host: ${dbHost}`);
213
+ CLIUI.success('MongoDB तयार छ। MONGO_URI .env मा राख्नुहोस्।');
214
+ }
215
+ catch (e) {
216
+ CLIUI.stopSpinner(false, e.message);
217
+ CLIUI.error('Mongoose connection failed. URI सही छ?');
218
+ process.exit(1);
219
+ }
220
+ }
221
+ else if (dbType === 'redis') {
222
+ const uri = uriArg || process.env.REDIS_URL || 'redis://localhost:6379';
223
+ CLIUI.heading('Redis Connection Test');
224
+ console.log(` URI: ${uri}`);
225
+ CLIUI.startSpinner('TCP ping...');
226
+ const { host, port } = parseUri(uri);
227
+ const tcpOk = await checkTcpPort(host, port);
228
+ CLIUI.stopSpinner(tcpOk, tcpOk ? `Host reachable: ${host}:${port}` : `Cannot reach ${host}:${port}`);
229
+ if (!tcpOk) {
230
+ console.log('\n \x1b[33mFix — Redis start गर्नुहोस्:\x1b[0m');
231
+ console.log(' brew services start redis (macOS)');
232
+ console.log(' sudo systemctl start redis (Linux)');
233
+ console.log(' redis-server (manual)');
234
+ process.exit(1);
235
+ }
236
+ CLIUI.startSpinner('Redis PING...');
237
+ try {
238
+ const { createClient } = await import('redis');
239
+ const client = createClient({ url: uri });
240
+ await client.connect();
241
+ const pong = await client.ping();
242
+ await client.quit();
243
+ CLIUI.stopSpinner(pong === 'PONG', `Redis replied: ${pong}`);
244
+ CLIUI.success('Redis तयार छ!');
245
+ }
246
+ catch {
247
+ CLIUI.stopSpinner(true, 'TCP OK (redis package: npm install redis)');
248
+ }
249
+ }
250
+ else {
251
+ CLIUI.error('Usage:');
252
+ console.log(' dolphin connect mongoose [uri]');
253
+ console.log(' dolphin connect redis [uri]');
254
+ }
255
+ break;
256
+ }
257
+ // ── generate ─────────────────────────────────────────────────────────
258
+ case 'generate': {
47
259
  const prompt = args.slice(1).join(' ');
48
260
  if (!prompt)
49
261
  return CLIUI.error('Usage: dolphin generate "your prompt"');
50
262
  CLIUI.startSpinner('AI is generating code');
51
263
  try {
52
- const response = await ai.request(prompt, "Return ONLY raw JavaScript code. No markdown.");
264
+ const response = await ai.request(prompt, 'Return ONLY raw JavaScript code. No markdown.');
53
265
  const cleanCode = response.replace(/```javascript|```js|```/g, '').trim();
54
266
  fs.writeFileSync(path.join(process.cwd(), 'ai-generated.js'), cleanCode);
55
267
  CLIUI.stopSpinner(true, 'File generated: ai-generated.js');
@@ -58,168 +270,319 @@ async function run() {
58
270
  CLIUI.stopSpinner(false, e.message);
59
271
  }
60
272
  break;
61
- case 'generate-full':
273
+ }
274
+ // ── generate-full ────────────────────────────────────────────────────
275
+ case 'generate-full': {
62
276
  const fullPrompt = args.slice(1).join(' ');
63
277
  if (!fullPrompt)
64
278
  return CLIUI.error('Usage: dolphin generate-full "project description"');
65
279
  CLIUI.startSpinner('Architecting full project structure');
66
280
  try {
67
- const systemPrompt = `You are a Dolphin Framework expert. Generate a production-ready backend project using ONLY Dolphin Server modules.
68
- Dolphin Components:
69
- - Use 'dolphin-server-modules/server' for createDolphinServer()
70
- - Use 'dolphin-server-modules/adapters/mongoose' for createMongooseAdapter()
71
- - Use 'dolphin-server-modules/auth-controller' for createDolphinAuthController()
72
- - Use 'dolphin-server-modules/crud' for createCrudController()
73
- - Models: Mongoose schemas only
74
- - Controllers: Use Dolphin CRUD controllers
75
- - Main app: Use createDolphinServer(), not Express
76
- - DB: Use createMongooseAdapter() for MongoDB
77
- - Auth: Use createDolphinAuthController()
78
- - Routes: Direct on app, no separate route files
79
- - index.js: Main entry point using Dolphin server
80
- Project Structure:
81
- - index.js (main server)
82
- - models/ (Mongoose models)
83
- - controllers/ (Dolphin controllers)
84
- - config/db.js (DB connection)
85
- - .env (env vars)
86
- Return ONLY a JSON object where keys are file paths and values are code content. No markdown.`;
87
- const response = await ai.request(fullPrompt, systemPrompt);
88
- // Extract only the part between { and } to avoid AI's conversational chatter
281
+ const sysPrompt = `You are a Dolphin Framework expert. Generate a production-ready backend project.
282
+ Use: createDolphinServer(), createMongooseAdapter({User,RefreshToken}), createDolphinAuthController(), createCrudController().
283
+ Return ONLY a JSON object: {filePath: fileContent}. No markdown.`;
284
+ const response = await ai.request(fullPrompt, sysPrompt);
89
285
  const jsonMatch = response.match(/\{[\s\S]*\}/);
90
286
  const cleaned = jsonMatch ? jsonMatch[0] : response.replace(/```json|```/g, '').trim();
91
287
  let files;
92
288
  try {
93
289
  files = JSON.parse(cleaned);
94
290
  }
95
- catch (err) {
96
- // AI le pathayeko "raw newlines" ra illegal control characters lai escape garne
97
- const sanitized = cleaned.replace(/[\x00-\x1F\x7F-\x9F]/g, (c) => c === '\n' ? '\\n' : (c === '\r' ? '\\r' : (c === '\t' ? '\\t' : '')));
291
+ catch {
292
+ const sanitized = cleaned.replace(/[\x00-\x1F\x7F-\x9F]/g, (c) => c === '\n' ? '\\n' : c === '\r' ? '\\r' : c === '\t' ? '\\t' : '');
98
293
  files = JSON.parse(sanitized);
99
294
  }
100
295
  Object.entries(files).forEach(([fPath, content]) => {
101
296
  const fullPath = path.join(process.cwd(), fPath);
102
- // Don't overwrite existing .env files to protect API keys
103
297
  if (fPath === '.env' && fs.existsSync(fullPath)) {
104
- console.log(` ⚠️ Skipped: .env (file already exists)`);
298
+ console.log(' ⚠️ Skipped: .env');
105
299
  return;
106
300
  }
107
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
301
+ ensureDir(path.dirname(fullPath));
108
302
  fs.writeFileSync(fullPath, content);
109
303
  console.log(` 📄 Created: ${fPath}`);
110
304
  });
111
- // Ensure .env file exists with defaults
112
- const envPath = path.join(process.cwd(), '.env');
113
- if (!fs.existsSync(envPath)) {
114
- const defaultEnv = `MONGO_URI=mongodb://localhost:27017/dolphin_db
115
- JWT_SECRET=your_ultra_secret_jwt_key_here
116
- GEMINI_API_KEY=your_gemini_api_key_here
117
- GROQ_API_KEY=your_groq_api_key_here
118
- OPENAI_API_KEY=your_openai_api_key_here
119
- `;
120
- fs.writeFileSync(envPath, defaultEnv);
121
- console.log(` 📄 Created: .env (with defaults)`);
122
- }
123
- CLIUI.stopSpinner(true, 'Project architected successfully!');
305
+ const dotEnv = path.join(process.cwd(), '.env');
306
+ if (!fs.existsSync(dotEnv)) {
307
+ fs.writeFileSync(dotEnv, TEMPLATES.env);
308
+ console.log(' 📄 Created: .env');
309
+ }
310
+ CLIUI.stopSpinner(true, 'Project architected!');
124
311
  }
125
312
  catch (e) {
126
313
  CLIUI.stopSpinner(false, e.message);
127
314
  }
128
315
  break;
129
- case 'chat':
130
- import('../ai/dolphin-agent/index.js').then(agentModule => {
131
- const agent = agentModule.initAgentHook({ framework: 'dolphin', autoGenerateEnv: false });
132
- agent.interactiveChat();
133
- }).catch(err => {
134
- CLIUI.error(`[Dolphin-Agent] Failed to start chat: ${err.message}`);
135
- });
316
+ }
317
+ // ── chat ─────────────────────────────────────────────────────────────
318
+ case 'chat': {
319
+ import('../ai/dolphin-agent/index.js')
320
+ .then(m => { const a = m.initAgentHook({ framework: 'dolphin', autoGenerateEnv: false }); a.interactiveChat(); })
321
+ .catch(err => CLIUI.error(`[Dolphin-Agent] ${err.message}`));
136
322
  break;
323
+ }
324
+ // ── init / init-prod ─────────────────────────────────────────────────
137
325
  case 'init':
138
- case 'init-prod':
139
- CLIUI.heading('Scaffolding Production Project');
326
+ case 'init-prod': {
327
+ CLIUI.heading('Production Project Scaffold');
140
328
  const dirs = ['models', 'controllers', 'routes', 'middleware', 'services', 'config'];
141
- dirs.forEach(dir => {
142
- const p = path.join(process.cwd(), dir);
143
- if (!fs.existsSync(p))
144
- fs.mkdirSync(p, { recursive: true });
145
- });
146
- fs.writeFileSync(path.join(process.cwd(), 'app.js'), TEMPLATES.app);
147
- CLIUI.success('Folders and app.js created.');
148
- import('../ai/dolphin-agent/index.js').then(agentModule => {
149
- agentModule.initAgentHook({ framework: 'dolphin', autoGenerateEnv: true });
150
- }).catch(err => {
151
- CLIUI.error(`[Dolphin-Agent] Initialization failed: ${err.message}`);
329
+ dirs.forEach(d => { ensureDir(path.join(process.cwd(), d)); console.log(` 📁 ${d}/`); });
330
+ const files = [
331
+ ['app.js', TEMPLATES.app],
332
+ ['config/db.js', TEMPLATES.mongoose],
333
+ ['.env', TEMPLATES.env],
334
+ ['.gitignore', 'node_modules/\ndist/\n.env\n*.log\n'],
335
+ ];
336
+ files.forEach(([rel, content]) => {
337
+ const full = path.join(process.cwd(), rel);
338
+ if (!fs.existsSync(full)) {
339
+ fs.writeFileSync(full, content);
340
+ console.log(` 📄 Created: ${rel}`);
341
+ }
152
342
  });
343
+ CLIUI.success('Structure ready!');
344
+ console.log('\n Next steps:');
345
+ console.log(' 1. npm install dolphin-server-modules mongoose');
346
+ console.log(' 2. dolphin connect mongoose — DB test');
347
+ console.log(' 3. node app.js — Server start');
348
+ import('../ai/dolphin-agent/index.js')
349
+ .then(m => m.initAgentHook({ framework: 'dolphin', autoGenerateEnv: true }))
350
+ .catch(() => { });
153
351
  break;
154
- case 'add':
155
- const subCommand = args[1];
156
- if (subCommand === 'adapter') {
157
- const type = args[2];
158
- if (type === 'mongoose') {
159
- const configDir = path.join(process.cwd(), 'config');
160
- if (!fs.existsSync(configDir))
161
- fs.mkdirSync(configDir, { recursive: true });
162
- fs.writeFileSync(path.join(configDir, 'db.js'), TEMPLATES.mongoose);
163
- CLIUI.success('Mongoose adapter added to config/db.js');
352
+ }
353
+ // ── add ──────────────────────────────────────────────────────────────
354
+ case 'add': {
355
+ const sub = args[1];
356
+ const name = args[2];
357
+ // add adapter
358
+ if (sub === 'adapter') {
359
+ const type = name || '';
360
+ const configDir = path.join(process.cwd(), 'config');
361
+ if (['mongoose', 'mongo', 'mongodb'].includes(type)) {
362
+ writeFile(path.join(configDir, 'db.js'), TEMPLATES.mongoose, 'config/db.js');
363
+ CLIUI.success('Mongoose adapter added!');
364
+ console.log('\n Usage: import { connectDB } from \'./config/db.js\';');
365
+ console.log(' connectDB(process.env.MONGO_URI);');
366
+ }
367
+ else if (['sequelize', 'mysql', 'postgres', 'sql'].includes(type)) {
368
+ writeFile(path.join(configDir, 'db.js'), TEMPLATES.sequelize, 'config/db.js');
369
+ CLIUI.success('Sequelize adapter added!');
370
+ console.log('\n MySQL: npm install sequelize mysql2');
371
+ console.log(' PostgreSQL: npm install sequelize pg pg-hstore');
372
+ }
373
+ else if (type === 'redis') {
374
+ writeFile(path.join(configDir, 'redis.js'), TEMPLATES.redis, 'config/redis.js');
375
+ CLIUI.success('Redis adapter added!');
376
+ console.log('\n Install: npm install redis');
377
+ console.log(' Usage: import { connectRedis, cache } from \'./config/redis.js\';');
164
378
  }
165
379
  else {
166
- CLIUI.error('Unsupported adapter. Try: dolphin add adapter mongoose');
167
- }
168
- }
169
- else if (subCommand === 'auth') {
170
- const controllerDir = path.join(process.cwd(), 'controllers');
171
- const modelDir = path.join(process.cwd(), 'models');
172
- if (!fs.existsSync(controllerDir))
173
- fs.mkdirSync(controllerDir, { recursive: true });
174
- if (!fs.existsSync(modelDir))
175
- fs.mkdirSync(modelDir, { recursive: true });
176
- fs.writeFileSync(path.join(controllerDir, 'auth.js'), TEMPLATES.auth);
177
- fs.writeFileSync(path.join(modelDir, 'User.js'), TEMPLATES.authModel);
178
- CLIUI.success('Auth controller and User model added.');
179
- }
180
- else if (subCommand === 'crud') {
181
- const name = args[2];
380
+ CLIUI.error('Unknown adapter. Available: mongoose | sequelize | redis');
381
+ console.log(' Examples:');
382
+ console.log(' dolphin add adapter mongoose');
383
+ console.log(' dolphin add adapter sequelize');
384
+ console.log(' dolphin add adapter redis');
385
+ }
386
+ // add auth
387
+ }
388
+ else if (sub === 'auth') {
389
+ writeFile(path.join(process.cwd(), 'controllers', 'auth.js'), TEMPLATES.auth, 'controllers/auth.js');
390
+ writeFile(path.join(process.cwd(), 'models', 'User.js'), TEMPLATES.authModel, 'models/User.js');
391
+ CLIUI.success('Auth controller + User model added!');
392
+ console.log('\n app.js मा थप्नुहोस्:');
393
+ console.log(' import { auth } from \'./controllers/auth.js\';');
394
+ console.log(' app.post(\'/api/auth/register\', auth.register);');
395
+ console.log(' app.post(\'/api/auth/login\', auth.login);');
396
+ // add crud
397
+ }
398
+ else if (sub === 'crud') {
182
399
  if (!name)
183
400
  return CLIUI.error('Usage: dolphin add crud <ModelName>');
184
- const controllerDir = path.join(process.cwd(), 'controllers');
185
- const modelDir = path.join(process.cwd(), 'models');
186
- if (!fs.existsSync(controllerDir))
187
- fs.mkdirSync(controllerDir, { recursive: true });
188
- if (!fs.existsSync(modelDir))
189
- fs.mkdirSync(modelDir, { recursive: true });
190
- fs.writeFileSync(path.join(controllerDir, `${name.toLowerCase()}.js`), TEMPLATES.crud(name));
191
- fs.writeFileSync(path.join(modelDir, `${name}.js`), TEMPLATES.crudModel(name));
192
- CLIUI.success(`CRUD Controller and Model for ${name} generated successfully!`);
193
- console.log(` 📄 Created: controllers/${name.toLowerCase()}.js`);
194
- console.log(` 📄 Created: models/${name}.js`);
401
+ const N = capitalize(name);
402
+ writeFile(path.join(process.cwd(), 'controllers', `${N.toLowerCase()}.js`), TEMPLATES.crud(N), `controllers/${N.toLowerCase()}.js`);
403
+ writeFile(path.join(process.cwd(), 'models', `${N}.js`), TEMPLATES.crudModel(N), `models/${N}.js`);
404
+ CLIUI.success(`CRUD for ${N} generated!`);
405
+ // add model
406
+ }
407
+ else if (sub === 'model') {
408
+ if (!name)
409
+ return CLIUI.error('Usage: dolphin add model <ModelName> [--adapter]');
410
+ const N = capitalize(name);
411
+ writeFile(path.join(process.cwd(), 'models', `${N}.js`), TEMPLATES.model(N), `models/${N}.js`);
412
+ CLIUI.success(`${N} model added!`);
413
+ if (args.includes('--adapter') || args.includes('-a')) {
414
+ registerModelInAdapter(N);
415
+ }
416
+ // add model-adapter
417
+ }
418
+ else if (sub === 'model-adapter') {
419
+ if (!name)
420
+ return CLIUI.error('Usage: dolphin add model-adapter <ModelName>');
421
+ const N = capitalize(name);
422
+ writeFile(path.join(process.cwd(), 'models', `${N}.js`), TEMPLATES.model(N), `models/${N}.js`);
423
+ CLIUI.success(`${N} model added!`);
424
+ registerModelInAdapter(N);
425
+ // add middleware
426
+ }
427
+ else if (sub === 'middleware' || sub === 'mw') {
428
+ if (!name)
429
+ return CLIUI.error('Usage: dolphin add middleware <Name>');
430
+ const N = capitalize(name);
431
+ writeFile(path.join(process.cwd(), 'middleware', `${name.toLowerCase()}.js`), TEMPLATES.middleware(N), `middleware/${name.toLowerCase()}.js`);
432
+ CLIUI.success(`${N} middleware added!`);
433
+ console.log(`\n app.use() मा थप्नुहोस्: import { ${name.toLowerCase()}Middleware } from './middleware/${name.toLowerCase()}.js';`);
434
+ // add route
435
+ }
436
+ else if (sub === 'route' || sub === 'router') {
437
+ if (!name)
438
+ return CLIUI.error('Usage: dolphin add route <Name>');
439
+ const N = capitalize(name);
440
+ writeFile(path.join(process.cwd(), 'routes', `${name.toLowerCase()}.js`), TEMPLATES.route(N), `routes/${name.toLowerCase()}.js`);
441
+ CLIUI.success(`${N} route added!`);
442
+ console.log(`\n import { ${name.toLowerCase()}Router } from './routes/${name.toLowerCase()}.js';`);
443
+ console.log(` app.use('', ${name.toLowerCase()}Router);`);
444
+ // add service
445
+ }
446
+ else if (sub === 'service' || sub === 'svc') {
447
+ if (!name)
448
+ return CLIUI.error('Usage: dolphin add service <Name>');
449
+ const N = capitalize(name);
450
+ writeFile(path.join(process.cwd(), 'services', `${N}.service.js`), TEMPLATES.service(N), `services/${N}.service.js`);
451
+ CLIUI.success(`${N}Service added!`);
195
452
  }
196
453
  else {
197
- CLIUI.error('Usage: dolphin add <adapter|auth|crud>');
454
+ CLIUI.error('Usage: dolphin add <subcommand>');
455
+ console.log('\n adapter <mongoose|sequelize|redis> DB adapter');
456
+ console.log(' auth Auth system');
457
+ console.log(' crud <ModelName> CRUD controller + model');
458
+ console.log(' model <ModelName> [--adapter] Mongoose model (optionally registers it)');
459
+ console.log(' model-adapter <ModelName> Mongoose model & registers in adapter');
460
+ console.log(' middleware <Name> Middleware');
461
+ console.log(' route <Name> Route file');
462
+ console.log(' service <Name> Service class');
463
+ }
464
+ break;
465
+ }
466
+ // ── status ───────────────────────────────────────────────────────────
467
+ case 'status': {
468
+ CLIUI.heading('Dolphin Project Status');
469
+ const cwd = process.cwd();
470
+ const checks = [
471
+ { label: 'package.json', file: 'package.json' },
472
+ { label: '.env', file: '.env' },
473
+ { label: 'app.js / app.ts', file: 'app.js', alt: 'app.ts' },
474
+ { label: 'config/db.js', file: 'config/db.js', alt: 'config/db.ts' },
475
+ { label: 'models/', dir: 'models' },
476
+ { label: 'controllers/', dir: 'controllers' },
477
+ { label: 'routes/', dir: 'routes' },
478
+ { label: 'middleware/', dir: 'middleware' },
479
+ { label: 'services/', dir: 'services' },
480
+ ];
481
+ checks.forEach(({ label, file, alt, dir }) => {
482
+ let exists = false;
483
+ if (dir)
484
+ exists = fs.existsSync(path.join(cwd, dir));
485
+ else
486
+ exists = fs.existsSync(path.join(cwd, file)) || (alt ? fs.existsSync(path.join(cwd, alt)) : false);
487
+ const icon = exists ? '\x1b[32m✔\x1b[0m' : '\x1b[33m–\x1b[0m';
488
+ console.log(` ${icon} ${label}`);
489
+ });
490
+ const projPkg = path.join(cwd, 'package.json');
491
+ if (fs.existsSync(projPkg)) {
492
+ const p = JSON.parse(fs.readFileSync(projPkg, 'utf8'));
493
+ const dsm = p.dependencies?.['dolphin-server-modules'] || p.devDependencies?.['dolphin-server-modules'];
494
+ console.log(`\n dolphin-server-modules: ${dsm || '(package.json मा छैन)'}`);
198
495
  }
496
+ const dotEnv = path.join(cwd, '.env');
497
+ if (fs.existsSync(dotEnv)) {
498
+ const envText = fs.readFileSync(dotEnv, 'utf8');
499
+ console.log('\n .env keys:');
500
+ ['MONGO_URI', 'JWT_SECRET', 'REDIS_URL', 'PORT'].forEach(k => {
501
+ const set = envText.includes(k + '=') && !envText.match(new RegExp(k + '=your_'));
502
+ console.log(` ${set ? '\x1b[32m✔\x1b[0m' : '\x1b[33m–\x1b[0m'} ${k}`);
503
+ });
504
+ }
505
+ console.log(`\n 🐬 Dolphin CLI v${VERSION}`);
506
+ break;
507
+ }
508
+ // ── deploy ───────────────────────────────────────────────────────────
509
+ case 'deploy': {
510
+ CLIUI.heading('Production Deployment (PM2)');
511
+ const entry = fs.existsSync(path.join(process.cwd(), 'dist/index.js')) ? 'dist/index.js'
512
+ : fs.existsSync(path.join(process.cwd(), 'app.js')) ? 'app.js' : 'index.js';
513
+ console.log(`
514
+ \x1b[1mStep 1\x1b[0m — PM2 install:
515
+ \x1b[36mnpm install -g pm2\x1b[0m
516
+
517
+ \x1b[1mStep 2\x1b[0m — Build (TypeScript भए):
518
+ \x1b[36mnpm run build\x1b[0m
519
+
520
+ \x1b[1mStep 3\x1b[0m — Start:
521
+ \x1b[36mpm2 start ${entry} --name "dolphin-app" --env production\x1b[0m
522
+
523
+ \x1b[1mStep 4\x1b[0m — Auto-start on reboot:
524
+ \x1b[36mpm2 startup && pm2 save\x1b[0m
525
+
526
+ \x1b[1mStep 5\x1b[0m — Logs:
527
+ \x1b[36mpm2 logs dolphin-app\x1b[0m
528
+
529
+ ─── ecosystem.config.js (cluster mode) ────────────────
530
+ module.exports = {
531
+ apps: [{ name: 'dolphin-app', script: '${entry}',
532
+ instances: 'max', exec_mode: 'cluster',
533
+ env_production: { NODE_ENV: 'production', PORT: 3000 }
534
+ }]
535
+ };
536
+ \x1b[36mpm2 start ecosystem.config.js --env production\x1b[0m
537
+ `);
199
538
  break;
539
+ }
540
+ // ── version ──────────────────────────────────────────────────────────
200
541
  case '-v':
201
542
  case '--version':
202
- console.log(`🐬 Dolphin CLI v2.9.3`);
543
+ case 'version':
544
+ console.log(`🐬 Dolphin CLI v${VERSION}`);
203
545
  break;
546
+ // ── help ─────────────────────────────────────────────────────────────
204
547
  case 'help':
205
548
  default:
206
- CLIUI.heading('Dolphin Framework CLI');
549
+ CLIUI.heading(`Dolphin Framework CLI v${VERSION}`);
207
550
  console.log(`
208
- Commands:
209
- serve Start development server
210
- generate <prompt> Quick AI code generation
211
- generate-full <p> Full project architecture
212
- chat Autonomous AI Agent Mode (Full Access)
213
- init-prod Scaffold production folder structure
214
- add adapter <t> Add Mongoose/Sequelize adapter
215
- add auth Add pre-built Auth controller
216
- add crud <Name> Add CRUD controller for a model
217
- -v, --version Show CLI version
218
- `);
219
- break;
551
+ \x1b[1mServer\x1b[0m
552
+ serve [--port=N] Dev server start गर्ने
553
+
554
+ \x1b[1mScaffolding\x1b[0m
555
+ init / init-prod Production project scaffold
556
+ add adapter <type> DB adapter (mongoose|sequelize|redis)
557
+ add auth Auth controller + User model
558
+ add crud <ModelName> CRUD controller + Model
559
+ add model <ModelName> Mongoose model मात्र (use --adapter or model-adapter to register)
560
+ add model-adapter <ModelName> Mongoose model + registers in adapter
561
+ add middleware <Name> Middleware file
562
+ add route <Name> Route file
563
+ add service <Name> Service class
564
+
565
+ \x1b[1mDatabase\x1b[0m
566
+ connect mongoose [uri] MongoDB connection test
567
+ connect redis [uri] Redis connection test
568
+
569
+ \x1b[1mAI (API key चाहिन्छ)\x1b[0m
570
+ generate <prompt> Quick AI code generation
571
+ generate-full <prompt> Full project AI architecture
572
+ chat Autonomous AI Agent (Cursor mode)
573
+
574
+ \x1b[1mProject\x1b[0m
575
+ status Project health check
576
+ deploy PM2 deployment guide
577
+
578
+ \x1b[1mInfo\x1b[0m
579
+ -v / --version Version
580
+ help यो help
581
+ `);
220
582
  }
221
583
  }
222
584
  run().catch(err => {
223
585
  CLIUI.error(err.message);
586
+ process.exit(1);
224
587
  });
225
588
  //# sourceMappingURL=cli.js.map