dolphin-server-modules 2.11.0 → 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 (100) hide show
  1. package/TUTORIAL_NEPALI.md +181 -0
  2. package/dist/adapters/mongoose/index.d.ts +4 -4
  3. package/dist/adapters/mongoose/index.js.map +1 -1
  4. package/dist/adapters/mongoose/index.test.d.ts +1 -0
  5. package/dist/adapters/mongoose/index.test.js +145 -0
  6. package/dist/adapters/mongoose/index.test.js.map +1 -0
  7. package/dist/adapters/mongoose/integration.test.d.ts +5 -0
  8. package/dist/adapters/mongoose/integration.test.js +217 -0
  9. package/dist/adapters/mongoose/integration.test.js.map +1 -0
  10. package/dist/ai/dolphin-agent/agent.d.ts +5 -0
  11. package/dist/ai/dolphin-agent/agent.js +93 -35
  12. package/dist/ai/dolphin-agent/agent.js.map +1 -1
  13. package/dist/ai/dolphin-agent/config.js +30 -37
  14. package/dist/ai/dolphin-agent/config.js.map +1 -1
  15. package/dist/auth/auth.test.d.ts +1 -0
  16. package/dist/auth/auth.test.js +286 -0
  17. package/dist/auth/auth.test.js.map +1 -0
  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 -165
  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/middleware/zod.test.d.ts +1 -0
  45. package/dist/middleware/zod.test.js +74 -0
  46. package/dist/middleware/zod.test.js.map +1 -0
  47. package/dist/performance-test.d.ts +1 -0
  48. package/dist/performance-test.js +92 -0
  49. package/dist/performance-test.js.map +1 -0
  50. package/dist/real-test-mongoose.d.ts +1 -0
  51. package/dist/real-test-mongoose.js +104 -0
  52. package/dist/real-test-mongoose.js.map +1 -0
  53. package/dist/realtime/camera.d.ts +119 -0
  54. package/dist/realtime/camera.js +299 -0
  55. package/dist/realtime/camera.js.map +1 -0
  56. package/dist/realtime/camera.test.d.ts +1 -0
  57. package/dist/realtime/camera.test.js +345 -0
  58. package/dist/realtime/camera.test.js.map +1 -0
  59. package/dist/realtime/index.d.ts +2 -0
  60. package/dist/realtime/index.js +2 -0
  61. package/dist/realtime/index.js.map +1 -1
  62. package/dist/realtime/realtime.test.d.ts +1 -0
  63. package/dist/realtime/realtime.test.js +623 -0
  64. package/dist/realtime/realtime.test.js.map +1 -0
  65. package/dist/realtime/rtsp.d.ts +65 -0
  66. package/dist/realtime/rtsp.js +410 -0
  67. package/dist/realtime/rtsp.js.map +1 -0
  68. package/dist/realtime/rtsp.test.d.ts +1 -0
  69. package/dist/realtime/rtsp.test.js +361 -0
  70. package/dist/realtime/rtsp.test.js.map +1 -0
  71. package/dist/router/router.test.d.ts +1 -0
  72. package/dist/router/router.test.js +45 -0
  73. package/dist/router/router.test.js.map +1 -0
  74. package/dist/server/server.test.d.ts +1 -0
  75. package/dist/server/server.test.js +299 -0
  76. package/dist/server/server.test.js.map +1 -0
  77. package/dist/services/ai-service.js +22 -11
  78. package/dist/services/ai-service.js.map +1 -1
  79. package/dist/signaling/signaling.test.d.ts +1 -0
  80. package/dist/signaling/signaling.test.js +112 -0
  81. package/dist/signaling/signaling.test.js.map +1 -0
  82. package/dist/swagger/swagger.js +31 -31
  83. package/dist/swagger/swagger.test.d.ts +1 -0
  84. package/dist/swagger/swagger.test.js +38 -0
  85. package/dist/swagger/swagger.test.js.map +1 -0
  86. package/dist/templates/index.d.ts +6 -0
  87. package/dist/templates/index.js +282 -105
  88. package/dist/templates/index.js.map +1 -1
  89. package/dist/test-2fa-real.d.ts +1 -0
  90. package/dist/test-2fa-real.js +105 -0
  91. package/dist/test-2fa-real.js.map +1 -0
  92. package/dist/test-dolphin.d.ts +1 -0
  93. package/dist/test-dolphin.js +98 -0
  94. package/dist/test-dolphin.js.map +1 -0
  95. package/dist/utils/ctx.d.ts +50 -0
  96. package/dist/utils/ctx.js +82 -0
  97. package/dist/utils/ctx.js.map +1 -0
  98. package/package.json +155 -45
  99. package/scripts/client.js +838 -0
  100. package/scripts/dolphin-persist.js +211 -0
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,202 +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 'import' NOT 'require'. This is an ES Module project.
70
- - Use 'dolphin-server-modules/server' for createDolphinServer()
71
- - Use 'dolphin-server-modules/adapters/mongoose' for createMongooseAdapter()
72
- - Use 'dolphin-server-modules/auth-controller' for createDolphinAuthController()
73
- - Use 'dolphin-server-modules/crud' for createCrudController(dbAdapter, modelName)
74
- Example usage:
75
- const app = createDolphinServer();
76
- const db = createMongooseAdapter(uri);
77
- const crud = createCrudController(db, "Task");
78
- app.get("/tasks", crud.getAll);
79
- app.post("/tasks", crud.create);
80
- Project Structure:
81
- - package.json (MUST include "type": "module")
82
- - index.js (main server)
83
- - models/ (Mongoose models)
84
- - .env (env vars)
85
-
86
- CRITICAL: Return ONLY a valid JSON object.
87
- IMPORTANT: All code content inside JSON strings MUST have escaped newlines (use \\n).
88
- Example: { "file.js": "import x from 'y';\\nconsole.log(x);" }
89
- No markdown. No conversational text.`;
90
- const response = await ai.request(fullPrompt, systemPrompt);
91
- fs.writeFileSync('debug-ai-response.txt', response);
92
- // Robust JSON extraction using stack-based matching
93
- let cleaned = "";
94
- let i = response.indexOf('{');
95
- if (i !== -1) {
96
- let stack = 0;
97
- let inString = false;
98
- let escaped = false;
99
- let start = i;
100
- for (let j = i; j < response.length; j++) {
101
- const char = response[j];
102
- if (char === '"' && !escaped)
103
- inString = !inString;
104
- if (!inString) {
105
- if (char === '{')
106
- stack++;
107
- if (char === '}')
108
- stack--;
109
- if (stack === 0) {
110
- cleaned = response.substring(start, j + 1);
111
- break;
112
- }
113
- }
114
- if (char === '\\' && !escaped)
115
- escaped = true;
116
- else
117
- escaped = false;
118
- }
119
- }
120
- if (!cleaned)
121
- cleaned = response.replace(/```json|```/g, '').trim();
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);
285
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
286
+ const cleaned = jsonMatch ? jsonMatch[0] : response.replace(/```json|```/g, '').trim();
122
287
  let files;
123
288
  try {
124
289
  files = JSON.parse(cleaned);
125
290
  }
126
- catch (err) {
127
- // Try to fix unescaped newlines in JSON strings
128
- // This is a common AI error where it puts real newlines inside "..."
129
- const fixed = cleaned.replace(/"([^"]*)"/g, (match, content) => {
130
- return '"' + content.replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"';
131
- });
132
- files = JSON.parse(fixed);
291
+ catch {
292
+ const sanitized = cleaned.replace(/[\x00-\x1F\x7F-\x9F]/g, (c) => c === '\n' ? '\\n' : c === '\r' ? '\\r' : c === '\t' ? '\\t' : '');
293
+ files = JSON.parse(sanitized);
133
294
  }
134
295
  Object.entries(files).forEach(([fPath, content]) => {
135
296
  const fullPath = path.join(process.cwd(), fPath);
136
- // Don't overwrite existing .env files to protect API keys
137
297
  if (fPath === '.env' && fs.existsSync(fullPath)) {
138
- console.log(` ⚠️ Skipped: .env (file already exists)`);
298
+ console.log(' ⚠️ Skipped: .env');
139
299
  return;
140
300
  }
141
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
301
+ ensureDir(path.dirname(fullPath));
142
302
  fs.writeFileSync(fullPath, content);
143
303
  console.log(` 📄 Created: ${fPath}`);
144
304
  });
145
- // Ensure .env file exists with defaults
146
- const envPath = path.join(process.cwd(), '.env');
147
- if (!fs.existsSync(envPath)) {
148
- const defaultEnv = `MONGO_URI=mongodb://localhost:27017/dolphin_db
149
- JWT_SECRET=your_ultra_secret_jwt_key_here
150
- GEMINI_API_KEY=your_gemini_api_key_here
151
- GROQ_API_KEY=your_groq_api_key_here
152
- OPENAI_API_KEY=your_openai_api_key_here
153
- `;
154
- fs.writeFileSync(envPath, defaultEnv);
155
- console.log(` 📄 Created: .env (with defaults)`);
305
+ const dotEnv = path.join(process.cwd(), '.env');
306
+ if (!fs.existsSync(dotEnv)) {
307
+ fs.writeFileSync(dotEnv, TEMPLATES.env);
308
+ console.log(' 📄 Created: .env');
156
309
  }
157
- CLIUI.stopSpinner(true, 'Project architected successfully!');
310
+ CLIUI.stopSpinner(true, 'Project architected!');
158
311
  }
159
312
  catch (e) {
160
313
  CLIUI.stopSpinner(false, e.message);
161
314
  }
162
315
  break;
163
- case 'chat':
164
- import('../ai/dolphin-agent/index.js').then(agentModule => {
165
- const agent = agentModule.initAgentHook({ framework: 'dolphin', autoGenerateEnv: false, ...aiConfig });
166
- agent.interactiveChat();
167
- }).catch(err => {
168
- CLIUI.error(`[Dolphin-Agent] Failed to start chat: ${err.message}`);
169
- });
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}`));
170
322
  break;
323
+ }
324
+ // ── init / init-prod ─────────────────────────────────────────────────
171
325
  case 'init':
172
- case 'init-prod':
173
- CLIUI.heading('Scaffolding Production Project');
326
+ case 'init-prod': {
327
+ CLIUI.heading('Production Project Scaffold');
174
328
  const dirs = ['models', 'controllers', 'routes', 'middleware', 'services', 'config'];
175
- dirs.forEach(dir => {
176
- const p = path.join(process.cwd(), dir);
177
- if (!fs.existsSync(p))
178
- fs.mkdirSync(p, { recursive: true });
179
- });
180
- fs.writeFileSync(path.join(process.cwd(), 'app.js'), TEMPLATES.app);
181
- CLIUI.success('Folders and app.js created.');
182
- import('../ai/dolphin-agent/index.js').then(agentModule => {
183
- agentModule.initAgentHook({ framework: 'dolphin', autoGenerateEnv: true, ...aiConfig });
184
- }).catch(err => {
185
- 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
+ }
186
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(() => { });
187
351
  break;
188
- case 'add':
189
- const subCommand = args[1];
190
- if (subCommand === 'adapter') {
191
- const type = args[2];
192
- if (type === 'mongoose') {
193
- const configDir = path.join(process.cwd(), 'config');
194
- if (!fs.existsSync(configDir))
195
- fs.mkdirSync(configDir, { recursive: true });
196
- fs.writeFileSync(path.join(configDir, 'db.js'), TEMPLATES.mongoose);
197
- 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\';');
198
378
  }
199
379
  else {
200
- CLIUI.error('Unsupported adapter. Try: dolphin add adapter mongoose');
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');
201
385
  }
386
+ // add auth
202
387
  }
203
- else if (subCommand === 'auth') {
204
- const controllerDir = path.join(process.cwd(), 'controllers');
205
- const modelDir = path.join(process.cwd(), 'models');
206
- if (!fs.existsSync(controllerDir))
207
- fs.mkdirSync(controllerDir, { recursive: true });
208
- if (!fs.existsSync(modelDir))
209
- fs.mkdirSync(modelDir, { recursive: true });
210
- fs.writeFileSync(path.join(controllerDir, 'auth.js'), TEMPLATES.auth);
211
- fs.writeFileSync(path.join(modelDir, 'User.js'), TEMPLATES.authModel);
212
- CLIUI.success('Auth controller and User model added.');
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
213
397
  }
214
- else if (subCommand === 'crud') {
215
- const name = args[2];
398
+ else if (sub === 'crud') {
216
399
  if (!name)
217
400
  return CLIUI.error('Usage: dolphin add crud <ModelName>');
218
- const controllerDir = path.join(process.cwd(), 'controllers');
219
- const modelDir = path.join(process.cwd(), 'models');
220
- if (!fs.existsSync(controllerDir))
221
- fs.mkdirSync(controllerDir, { recursive: true });
222
- if (!fs.existsSync(modelDir))
223
- fs.mkdirSync(modelDir, { recursive: true });
224
- fs.writeFileSync(path.join(controllerDir, `${name.toLowerCase()}.js`), TEMPLATES.crud(name));
225
- fs.writeFileSync(path.join(modelDir, `${name}.js`), TEMPLATES.crudModel(name));
226
- CLIUI.success(`CRUD Controller and Model for ${name} generated successfully!`);
227
- console.log(` 📄 Created: controllers/${name.toLowerCase()}.js`);
228
- 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!`);
229
452
  }
230
453
  else {
231
- 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');
232
463
  }
233
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 मा छैन)'}`);
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
+ `);
538
+ break;
539
+ }
540
+ // ── version ──────────────────────────────────────────────────────────
234
541
  case '-v':
235
542
  case '--version':
236
- console.log(`🐬 Dolphin CLI v2.9.9`);
543
+ case 'version':
544
+ console.log(`🐬 Dolphin CLI v${VERSION}`);
237
545
  break;
546
+ // ── help ─────────────────────────────────────────────────────────────
238
547
  case 'help':
239
548
  default:
240
- CLIUI.heading('Dolphin Framework CLI');
241
- console.log(`
242
- Commands:
243
- serve Start development server
244
- generate <prompt> Quick AI code generation
245
- generate-full <p> Full project architecture
246
- chat Autonomous AI Agent Mode (Full Access)
247
- init-prod Scaffold production folder structure
248
- add adapter <t> Add Mongoose/Sequelize adapter
249
- add auth Add pre-built Auth controller
250
- add crud <Name> Add CRUD controller for a model
251
- -v, --version Show CLI version
252
- `);
253
- break;
549
+ CLIUI.heading(`Dolphin Framework CLI v${VERSION}`);
550
+ console.log(`
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
+ `);
254
582
  }
255
583
  }
256
584
  run().catch(err => {
257
585
  CLIUI.error(err.message);
586
+ process.exit(1);
258
587
  });
259
588
  //# sourceMappingURL=cli.js.map