dolphin-server-modules 2.7.0 → 2.8.1

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 (88) hide show
  1. package/README.md +2 -1
  2. package/dist/adapters/mongoose/index.js +1 -4
  3. package/dist/adapters/mongoose/index.js.map +1 -1
  4. package/dist/adapters/mongoose/index.test.js +2 -4
  5. package/dist/adapters/mongoose/index.test.js.map +1 -1
  6. package/dist/adapters/mongoose/integration.test.js +19 -54
  7. package/dist/adapters/mongoose/integration.test.js.map +1 -1
  8. package/dist/auth/auth.js +23 -29
  9. package/dist/auth/auth.js.map +1 -1
  10. package/dist/auth/auth.test.js +5 -10
  11. package/dist/auth/auth.test.js.map +1 -1
  12. package/dist/authController/authController.js +8 -48
  13. package/dist/authController/authController.js.map +1 -1
  14. package/dist/authController/authController.test.js +121 -123
  15. package/dist/authController/authController.test.js.map +1 -1
  16. package/dist/bin/cli.js +149 -596
  17. package/dist/bin/cli.js.map +1 -1
  18. package/dist/controller/controller.js +4 -11
  19. package/dist/controller/controller.js.map +1 -1
  20. package/dist/controller/controller.test.js +4 -6
  21. package/dist/controller/controller.test.js.map +1 -1
  22. package/dist/curd/crud.js +4 -11
  23. package/dist/curd/crud.js.map +1 -1
  24. package/dist/curd/crud.test.js +2 -4
  25. package/dist/curd/crud.test.js.map +1 -1
  26. package/dist/demo-server.js +21 -59
  27. package/dist/demo-server.js.map +1 -1
  28. package/dist/djson/djson.js +10 -22
  29. package/dist/djson/djson.js.map +1 -1
  30. package/dist/djson/djson.test.js +149 -184
  31. package/dist/djson/djson.test.js.map +1 -1
  32. package/dist/dolphin-bench.js +4 -9
  33. package/dist/dolphin-bench.js.map +1 -1
  34. package/dist/hard-performance-test.js +9 -14
  35. package/dist/hard-performance-test.js.map +1 -1
  36. package/dist/index.js +4 -20
  37. package/dist/index.js.map +1 -1
  38. package/dist/middleware/zod.js +9 -15
  39. package/dist/middleware/zod.js.map +1 -1
  40. package/dist/middleware/zod.test.js +12 -14
  41. package/dist/middleware/zod.test.js.map +1 -1
  42. package/dist/performance-test.js +9 -14
  43. package/dist/performance-test.js.map +1 -1
  44. package/dist/real-test-mongoose.js +17 -22
  45. package/dist/real-test-mongoose.js.map +1 -1
  46. package/dist/realtime/codec.js +3 -8
  47. package/dist/realtime/codec.js.map +1 -1
  48. package/dist/realtime/core.js +20 -64
  49. package/dist/realtime/core.js.map +1 -1
  50. package/dist/realtime/index.js +4 -20
  51. package/dist/realtime/index.js.map +1 -1
  52. package/dist/realtime/plugins.js +6 -12
  53. package/dist/realtime/plugins.js.map +1 -1
  54. package/dist/realtime/realtime.test.js +10 -45
  55. package/dist/realtime/realtime.test.js.map +1 -1
  56. package/dist/realtime/trie.js +1 -5
  57. package/dist/realtime/trie.js.map +1 -1
  58. package/dist/router/router.js +1 -4
  59. package/dist/router/router.js.map +1 -1
  60. package/dist/router/router.test.js +9 -11
  61. package/dist/router/router.test.js.map +1 -1
  62. package/dist/server/server.js +12 -18
  63. package/dist/server/server.js.map +1 -1
  64. package/dist/server/server.test.js +21 -26
  65. package/dist/server/server.test.js.map +1 -1
  66. package/dist/services/ai-service.d.ts +16 -0
  67. package/dist/services/ai-service.js +141 -0
  68. package/dist/services/ai-service.js.map +1 -0
  69. package/dist/signaling/index.js +4 -9
  70. package/dist/signaling/index.js.map +1 -1
  71. package/dist/signaling/signaling.test.js +13 -15
  72. package/dist/signaling/signaling.test.js.map +1 -1
  73. package/dist/swagger/swagger.js +3 -8
  74. package/dist/swagger/swagger.js.map +1 -1
  75. package/dist/swagger/swagger.test.js +10 -12
  76. package/dist/swagger/swagger.test.js.map +1 -1
  77. package/dist/templates/index.d.ts +9 -0
  78. package/dist/templates/index.js +112 -0
  79. package/dist/templates/index.js.map +1 -0
  80. package/dist/test-2fa-real.js +6 -11
  81. package/dist/test-2fa-real.js.map +1 -1
  82. package/dist/test-dolphin.js +6 -8
  83. package/dist/test-dolphin.js.map +1 -1
  84. package/dist/utils/ui.d.ts +10 -0
  85. package/dist/utils/ui.js +36 -0
  86. package/dist/utils/ui.js.map +1 -0
  87. package/package.json +2 -1
  88. package/scripts/client.js +3 -1
package/dist/bin/cli.js CHANGED
@@ -1,636 +1,189 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
36
- var __importDefault = (this && this.__importDefault) || function (mod) {
37
- return (mod && mod.__esModule) ? mod : { "default": mod };
38
- };
39
- Object.defineProperty(exports, "__esModule", { value: true });
40
- const fs_1 = __importDefault(require("fs"));
41
- const path_1 = __importDefault(require("path"));
42
- const https_1 = __importDefault(require("https"));
43
- const server_1 = require("../server/server");
44
- const core_1 = require("../realtime/core");
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { createServer } from 'http';
5
+ import { AIService } from '../services/ai-service.js';
6
+ import { CLIUI } from '../utils/ui.js';
7
+ import { TEMPLATES } from '../templates/index.js';
45
8
  const args = process.argv.slice(2);
46
- const command = args[0];
47
- const TEMPLATES = {
48
- app: `import { createDolphinServer } from 'dolphin-server-modules/server';
49
- const app = createDolphinServer();
50
-
51
- app.get('/', (ctx) => ctx.json({ message: 'Dolphin Server is running!' }));
52
-
53
- app.listen(3000, () => console.log('🐬 Dolphin swimming on port 3000'));`,
54
- mongoose: `import mongoose from 'mongoose';
55
- import { createMongooseAdapter } from 'dolphin-server-modules/adapters/mongoose';
56
-
57
- export async function connectDB(models = {}) {
58
- const uri = process.env.MONGO_URI || 'mongodb://localhost:27017/dolphin_db';
59
- await mongoose.connect(uri);
60
- console.log('✅ MongoDB Connected');
61
-
62
- return createMongooseAdapter({
63
- models: { ...models }
9
+ 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
+ }
64
19
  });
65
- }`,
66
- sequelize: `import { Sequelize } from 'sequelize';
67
- // Note: This is a skeleton for Dolphin Sequelize Adapter
68
- export async function connectDB() {
69
- const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, {
70
- host: process.env.DB_HOST,
71
- dialect: 'mysql' // or 'postgres', 'sqlite'
72
- });
73
-
74
- try {
75
- await sequelize.authenticate();
76
- console.log('✅ SQL Database Connected');
77
- } catch (error) {
78
- console.error('❌ Unable to connect to the database:', error);
79
- }
80
-
81
- return sequelize;
82
- }`,
83
- auth: `import { createDolphinAuthController } from 'dolphin-server-modules/auth-controller';
84
- import { createDolphinRouter } from 'dolphin-server-modules/router';
85
-
86
- export function setupAuth(dbAdapter, config) {
87
- const router = createDolphinRouter();
88
- const auth = createDolphinAuthController(dbAdapter, config);
89
-
90
- router.post('/register', auth.register);
91
- router.post('/login', auth.login);
92
- router.post('/refresh', auth.refresh);
93
- router.get('/me', auth.requireAuth, (ctx) => ctx.json(ctx.req.user));
94
-
95
- return router;
96
- }`,
97
- crud: (name) => `import { createCRUD } from 'dolphin-server-modules/crud';
98
-
99
- export function setup${name}CRUD(dbAdapter) {
100
- const service = createCRUD(dbAdapter, { enforceOwnership: false });
101
- const COLLECTION = '${name}';
102
-
103
- return {
104
- getAll: async (ctx) => ctx.json(await service.read(COLLECTION, ctx.query)),
105
- getOne: async (ctx) => ctx.json(await service.readOne(COLLECTION, ctx.params.id)),
106
- create: async (ctx) => ctx.json(await service.create(COLLECTION, ctx.body)),
107
- update: async (ctx) => ctx.json(await service.updateOne(COLLECTION, ctx.params.id, ctx.body)),
108
- delete: async (ctx) => ctx.json(await service.deleteOne(COLLECTION, ctx.params.id))
109
- };
110
- }`,
111
- authModel: `import mongoose from 'mongoose';
112
-
113
- const UserSchema = new mongoose.Schema({
114
- email: { type: String, required: true, unique: true },
115
- password: { type: String, required: true },
116
- name: { type: String },
117
- role: { type: String, default: 'user' },
118
- is2FAEnabled: { type: Boolean, default: false },
119
- twoFASecret: { type: String },
120
- recoveryCodes: [{ type: String }],
121
- createdAt: { type: Date, default: Date.now }
122
- });
123
-
124
- const RefreshTokenSchema = new mongoose.Schema({
125
- userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
126
- token: { type: String, required: true },
127
- expiresAt: { type: Date, required: true },
128
- createdAt: { type: Date, default: Date.now }
129
- });
130
-
131
- export const User = mongoose.model('User', UserSchema);
132
- export const RefreshToken = mongoose.model('RefreshToken', RefreshTokenSchema);`
20
+ }
21
+ const aiConfig = {
22
+ apiKey: (process.env.DOLPHIN_AI_KEY || process.env.GEMINI_API_KEY || '').trim(),
23
+ baseUrl: process.env.DOLPHIN_AI_BASE_URL,
24
+ model: process.env.DOLPHIN_AI_MODEL
133
25
  };
134
- function loadEnv() {
135
- const envPath = path_1.default.join(process.cwd(), '.env');
136
- if (fs_1.default.existsSync(envPath)) {
137
- const content = fs_1.default.readFileSync(envPath, 'utf8');
138
- content.split(/\r?\n/).forEach(line => {
139
- const trimmedLine = line.trim();
140
- if (!trimmedLine || trimmedLine.startsWith('#'))
141
- return;
142
- const [key, ...valueParts] = trimmedLine.split('=');
143
- if (key && valueParts.length > 0) {
144
- const value = valueParts.join('=').trim().replace(/^["']|["']$/g, '');
145
- process.env[key.trim()] = value;
146
- }
147
- });
148
- }
26
+ if (!aiConfig.apiKey && ['generate', 'generate-full', 'chat'].includes(command)) {
27
+ CLIUI.error('API Key not found! Please set DOLPHIN_AI_KEY or GEMINI_API_KEY in your .env file.');
28
+ process.exit(1);
149
29
  }
30
+ const ai = new AIService(aiConfig);
150
31
  async function run() {
151
- loadEnv();
152
32
  switch (command) {
33
+ case 'serve':
34
+ const port = parseInt(args.find(arg => arg.startsWith('--port='))?.split('=')[1] || '3000');
35
+ CLIUI.heading(`Dolphin Dev Server starting on port ${port}`);
36
+ const server = createServer((req, res) => {
37
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
38
+ res.end('Dolphin Server is swimming!\n');
39
+ });
40
+ server.listen(port, () => {
41
+ CLIUI.success(`Server is live at http://localhost:${port}`);
42
+ });
43
+ break;
153
44
  case 'generate':
154
- const requestedModel = args.find(arg => arg.startsWith('--model='))?.split('=')[1] || 'gemini-flash-latest';
155
- const prompt = args.filter(arg => !arg.startsWith('--')).slice(1).join(' ');
156
- const apiKey = (process.env.GEMINI_API_KEY || '').trim();
157
- if (!apiKey) {
158
- console.log('❌ Error: GEMINI_API_KEY environment variable is not set.');
159
- console.log('💡 Get a free key at: https://aistudio.google.com/app/apikey');
160
- break;
161
- }
162
- if (!prompt) {
163
- console.log('❌ Please provide a prompt. Example: dolphin generate "a library management system"');
164
- break;
45
+ const prompt = args.slice(1).join(' ');
46
+ if (!prompt)
47
+ return CLIUI.error('Usage: dolphin generate "your prompt"');
48
+ CLIUI.startSpinner('AI is generating code');
49
+ try {
50
+ const response = await ai.request(prompt, "Return ONLY raw JavaScript code. No markdown.");
51
+ const cleanCode = response.replace(/```javascript|```js|```/g, '').trim();
52
+ fs.writeFileSync(path.join(process.cwd(), 'ai-generated.js'), cleanCode);
53
+ CLIUI.stopSpinner(true, 'File generated: ai-generated.js');
54
+ }
55
+ catch (e) {
56
+ CLIUI.stopSpinner(false, e.message);
165
57
  }
166
- const fallbackModels = ['gemini-flash-latest', 'gemini-2.0-flash', 'gemini-2.5-flash', 'gemini-pro-latest'];
167
- const modelsToTry = [requestedModel, ...fallbackModels.filter(m => m !== requestedModel)];
168
- const versions = ['v1beta', 'v1'];
169
- const tryGenerate = (modelIndex, versionIndex) => {
170
- if (modelIndex >= modelsToTry.length) {
171
- console.log('❌ All AI models failed. Please ensure Generative Language API is enabled in Google AI Studio for your key.');
172
- return;
173
- }
174
- if (versionIndex >= versions.length) {
175
- return tryGenerate(modelIndex + 1, 0);
176
- }
177
- const currentModel = modelsToTry[modelIndex];
178
- const currentVersion = versions[versionIndex];
179
- console.log(`🤖 AI (${currentModel} via ${currentVersion}) is swimming with Dolphin...`);
180
- const aiData = JSON.stringify({
181
- contents: [{ parts: [{ text: `Generate a production-ready Node.js file using dolphin-server-modules.
182
- Rules:
183
- 1. Use ESM 'import' instead of 'require'.
184
- 2. Use 'const app = createDolphinServer();' from 'dolphin-server-modules/server'.
185
- 3. Use unified context '(ctx) => { ... }' instead of '(req, res)'.
186
- 4. Return objects directly for JSON response.
187
- 5. No markdown backticks, no explanations.
188
- Context: ${prompt}. Return ONLY raw JS code.` }] }]
189
- });
190
- const options = {
191
- hostname: 'generativelanguage.googleapis.com',
192
- path: `/${currentVersion}/models/${currentModel}:generateContent?key=${encodeURIComponent(apiKey)}`,
193
- method: 'POST',
194
- headers: { 'Content-Type': 'application/json' }
195
- };
196
- const req = https_1.default.request(options, (res) => {
197
- let body = '';
198
- res.on('data', (d) => body += d);
199
- res.on('end', () => {
200
- try {
201
- const json = JSON.parse(body);
202
- if (json.error) {
203
- console.log(`⚠️ ${currentModel} (${currentVersion}) unavailable. Error: ${json.error.status}. Trying next...`);
204
- tryGenerate(modelIndex, versionIndex + 1);
205
- return;
206
- }
207
- const rawCode = json.candidates?.[0]?.content?.parts?.[0]?.text || '';
208
- if (rawCode) {
209
- const codeMatch = rawCode.match(/(\/\*[\s\S]*?\*\/|\/\/.*|[\s\S])*?(import|const|let|var|app|function)[\s\S]*/);
210
- const code = (codeMatch ? codeMatch[0] : rawCode).replace(/```javascript|```js|```/g, '').trim();
211
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'ai-generated-app.js'), code);
212
- console.log(`✅ Success! Code generated using ${currentModel} in ai-generated-app.js`);
213
- }
214
- else {
215
- tryGenerate(modelIndex, versionIndex + 1);
216
- }
217
- }
218
- catch (e) {
219
- tryGenerate(modelIndex, versionIndex + 1);
220
- }
221
- });
222
- });
223
- req.on('error', () => tryGenerate(modelIndex, versionIndex + 1));
224
- req.write(aiData);
225
- req.end();
226
- };
227
- tryGenerate(0, 0);
228
58
  break;
229
59
  case 'generate-full':
230
- const fullRequestedModel = args.find(arg => arg.startsWith('--model='))?.split('=')[1] || 'gemini-1.5-flash';
231
- const fullPrompt = args.filter(arg => !arg.startsWith('--')).slice(1).join(' ');
232
- const fullApiKey = (process.env.GEMINI_API_KEY || '').trim();
233
- if (!fullApiKey) {
234
- console.log('❌ Error: GEMINI_API_KEY environment variable is not set.');
235
- break;
236
- }
237
- if (!fullPrompt) {
238
- console.log('❌ Please provide a prompt. Example: dolphin generate-full "a laundry management system"');
239
- break;
240
- }
241
- console.log(`🤖 AI is architecting a full production-grade system...`);
242
- const tryGenerateFull = (modelIndex, versionIndex) => {
243
- const modelsToTry = ['gemini-1.5-flash-latest', 'gemini-flash-latest', 'gemini-1.5-pro-latest', 'gemini-pro-latest'];
244
- const versions = ['v1beta', 'v1'];
245
- if (modelIndex >= modelsToTry.length) {
246
- console.log('❌ All AI models failed for full architecture. Please check your prompt or API limits.');
247
- return;
248
- }
249
- if (versionIndex >= versions.length) {
250
- return tryGenerateFull(modelIndex + 1, 0);
251
- }
252
- const currentModel = modelsToTry[modelIndex];
253
- const currentVersion = versions[versionIndex];
254
- console.log(`🤖 AI (${currentModel} via ${currentVersion}) is architecting...`);
255
- const aiData = JSON.stringify({
256
- contents: [{ parts: [{ text: `Generate a full production-ready modular Node.js project structure using dolphin-server-modules.
257
- Rules:
258
- 1. Return ONLY a valid JSON object.
259
- 2. Keys are file paths (e.g., "routes/user.js", "models/User.js", "app.js").
260
- 3. Values are the file contents as strings.
261
- 4. Use ESM 'import'.
262
- 5. Use 'const app = createDolphinServer();' from 'dolphin-server-modules/server' to initialize the app.
263
- 6. Use 'createMongooseAdapter' from 'dolphin-server-modules/adapters/mongoose' for DB connectivity.
264
- 7. Use 'createDolphinAuthController' from 'dolphin-server-modules/auth-controller' for all authentication logic.
265
- 8. Use 'createCRUD' from 'dolphin-server-modules/crud' to generate standard API services.
266
- 9. Create REAL Mongoose schemas in the models/ folder and export them to be used by the adapter.
267
- 10. Use Dolphin unified context '(ctx) => { ... }' for all route handlers.
268
- 11. Return objects directly for JSON response (e.g., return { success: true, data }).
269
- 12. Include folders: models, controllers, routes, middleware, config, services.
270
- 13. Ensure 'app.js' connects everything: DB -> Adapter -> Auth/CRUD -> Router -> Server.
271
- Context: ${fullPrompt}.
272
- Return ONLY the JSON. No markdown backticks, no text before or after.` }] }]
273
- });
274
- const options = {
275
- hostname: 'generativelanguage.googleapis.com',
276
- path: `/${currentVersion}/models/${currentModel}:generateContent?key=${encodeURIComponent(fullApiKey)}`,
277
- method: 'POST',
278
- headers: { 'Content-Type': 'application/json' }
279
- };
280
- const req = https_1.default.request(options, (res) => {
281
- let body = '';
282
- res.on('data', (d) => body += d);
283
- res.on('end', () => {
284
- try {
285
- const json = JSON.parse(body);
286
- if (json.error) {
287
- console.log(`⚠️ ${currentModel} (${currentVersion}) error: ${json.error.message}. Trying next...`);
288
- return tryGenerateFull(modelIndex, versionIndex + 1);
289
- }
290
- const rawJson = json.candidates?.[0]?.content?.parts?.[0]?.text || '';
291
- const cleanJson = rawJson.replace(/```json|```/g, '').trim();
292
- const jsonMatch = cleanJson.match(/\{[\s\S]*\}/);
293
- if (jsonMatch) {
294
- try {
295
- const files = JSON.parse(jsonMatch[0]);
296
- Object.keys(files).forEach(filePath => {
297
- const fullPath = path_1.default.join(process.cwd(), filePath);
298
- const dirPath = path_1.default.dirname(fullPath);
299
- if (!fs_1.default.existsSync(dirPath)) {
300
- fs_1.default.mkdirSync(dirPath, { recursive: true });
301
- }
302
- fs_1.default.writeFileSync(fullPath, files[filePath]);
303
- console.log(`📄 Generated: ${filePath}`);
304
- });
305
- console.log('✅ Full project architected successfully! 🐬');
306
- }
307
- catch (e) {
308
- console.log('❌ Error parsing project JSON. Trying next model...');
309
- tryGenerateFull(modelIndex, versionIndex + 1);
310
- }
311
- }
312
- else {
313
- console.log('❌ AI failed to return valid JSON. Trying next model...');
314
- tryGenerateFull(modelIndex, versionIndex + 1);
315
- }
316
- }
317
- catch (e) {
318
- console.log('❌ Unexpected error. Trying next model...');
319
- tryGenerateFull(modelIndex, versionIndex + 1);
320
- }
321
- });
60
+ const fullPrompt = args.slice(1).join(' ');
61
+ if (!fullPrompt)
62
+ return CLIUI.error('Usage: dolphin generate-full "project description"');
63
+ CLIUI.startSpinner('Architecting full project structure');
64
+ try {
65
+ const systemPrompt = `You are a Dolphin Framework expert. Generate a production-ready backend project.
66
+ Dolphin Patterns:
67
+ - controllers/ (Logic)
68
+ - models/ (Schemas: can be Mongoose or Sequelize)
69
+ - config/db.js (DB connection logic)
70
+ - .env (Environment variables)
71
+ Dolphin supports both Mongoose (MongoDB) and Sequelize (MySQL/PostgreSQL).
72
+ Choose the appropriate adapter based on the user's request.
73
+ Return ONLY a JSON object where keys are file paths and values are code content. No markdown.`;
74
+ const response = await ai.request(fullPrompt, systemPrompt);
75
+ const files = JSON.parse(response.replace(/```json|```/g, '').trim());
76
+ Object.entries(files).forEach(([fPath, content]) => {
77
+ const fullPath = path.join(process.cwd(), fPath);
78
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
79
+ fs.writeFileSync(fullPath, content);
80
+ console.log(` 📄 Created: ${fPath}`);
322
81
  });
323
- req.on('error', () => tryGenerateFull(modelIndex, versionIndex + 1));
324
- req.write(aiData);
325
- req.end();
326
- };
327
- tryGenerateFull(0, 0);
328
- break;
329
- case 'modify':
330
- const fileToModify = args[1];
331
- const modifyPrompt = args.slice(2).join(' ');
332
- const modifyApiKey = (process.env.GEMINI_API_KEY || '').trim();
333
- if (!modifyApiKey || !fileToModify || !modifyPrompt) {
334
- console.log('❌ Usage: dolphin modify <filename> "your instructions"');
335
- break;
82
+ CLIUI.stopSpinner(true, 'Project architected successfully!');
336
83
  }
337
- const filePath = path_1.default.join(process.cwd(), fileToModify);
338
- if (!fs_1.default.existsSync(filePath)) {
339
- console.log(`❌ File not found: ${fileToModify}`);
340
- break;
84
+ catch (e) {
85
+ CLIUI.stopSpinner(false, e.message);
341
86
  }
342
- const currentCode = fs_1.default.readFileSync(filePath, 'utf8');
343
- console.log(`🛠️ Modifying ${fileToModify}...`);
344
- const tryModify = (modelIndex, versionIndex) => {
345
- const models = ['gemini-1.5-flash-latest', 'gemini-flash-latest', 'gemini-1.5-pro-latest'];
346
- const versions = ['v1beta', 'v1'];
347
- if (modelIndex >= models.length) {
348
- console.log('❌ Modification failed after trying all models.');
349
- return;
350
- }
351
- if (versionIndex >= versions.length) {
352
- return tryModify(modelIndex + 1, 0);
353
- }
354
- const model = models[modelIndex];
355
- const version = versions[versionIndex];
356
- const aiData = JSON.stringify({
357
- contents: [{ parts: [{ text: `You are an expert Dolphin Framework developer.
358
- Task: Modify the following code based on the instructions.
359
- Instructions: ${modifyPrompt}
360
- Current Code:
361
- ${currentCode}
362
-
363
- Rules:
364
- 1. Return ONLY the updated code.
365
- 2. No markdown, no explanations.
366
- 3. Maintain Dolphin Framework standards (ctx usage, createDolphinServer, etc.).` }] }]
367
- });
368
- const options = {
369
- hostname: 'generativelanguage.googleapis.com',
370
- path: `/${version}/models/${model}:generateContent?key=${encodeURIComponent(modifyApiKey)}`,
371
- method: 'POST',
372
- headers: { 'Content-Type': 'application/json' }
373
- };
374
- const req = https_1.default.request(options, (res) => {
375
- let body = '';
376
- res.on('data', (d) => body += d);
377
- res.on('end', () => {
378
- try {
379
- const json = JSON.parse(body);
380
- if (json.error) {
381
- return tryModify(modelIndex, versionIndex + 1);
382
- }
383
- const rawCode = json.candidates?.[0]?.content?.parts?.[0]?.text || '';
384
- if (rawCode) {
385
- const newCode = rawCode.replace(/```javascript|```js|```/g, '').trim();
386
- fs_1.default.writeFileSync(filePath, newCode);
387
- console.log(`✅ ${fileToModify} updated successfully!`);
388
- }
389
- else {
390
- tryModify(modelIndex, versionIndex + 1);
391
- }
392
- }
393
- catch (e) {
394
- tryModify(modelIndex, versionIndex + 1);
395
- }
396
- });
397
- });
398
- req.on('error', () => tryModify(modelIndex, versionIndex + 1));
399
- req.write(aiData);
400
- req.end();
401
- };
402
- tryModify(0, 0);
403
87
  break;
404
88
  case 'chat':
405
- console.log('🐬 Welcome to Dolphin AI Agent! I can write files and run commands.');
406
- console.log('💡 Try: "Create a new model for Blog" or "Show me current files"');
407
- console.log('💬 Type "exit" to quit.\n');
408
- const agentApiKey = (process.env.GEMINI_API_KEY || '').trim();
409
- if (!agentApiKey) {
410
- console.log('❌ GEMINI_API_KEY not set.');
411
- break;
412
- }
413
- Promise.resolve().then(() => __importStar(require('readline'))).then(readline => {
414
- const rl = readline.createInterface({
415
- input: process.stdin,
416
- output: process.stdout
89
+ CLIUI.heading('Dolphin Agentic AI Chat');
90
+ const readline = await import('readline');
91
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
92
+ const ask = () => {
93
+ rl.question('💬 You: ', async (input) => {
94
+ if (input.toLowerCase() === 'exit')
95
+ return rl.close();
96
+ CLIUI.startSpinner('Thinking');
97
+ try {
98
+ const reply = await ai.request(input, "You are a helpful Dolphin Framework AI Agent.");
99
+ CLIUI.stopSpinner(true, 'Dolphin Agent');
100
+ console.log(`🤖 ${reply}\n`);
101
+ ask();
102
+ }
103
+ catch (e) {
104
+ CLIUI.stopSpinner(false, e.message);
105
+ ask();
106
+ }
417
107
  });
418
- const ask = () => {
419
- rl.question('💬 You: ', (input) => {
420
- if (input.toLowerCase() === 'exit')
421
- return rl.close();
422
- const systemPrompt = `You are the Dolphin Framework AI Agent.
423
- You have access to the user's filesystem and shell.
424
- If you need to create or modify a file, start your response with:
425
- FILE_WRITE: <path>
426
- <content>
427
- END_FILE
428
-
429
- If you need to run a command, start with:
430
- SHELL_RUN: <command>
431
-
432
- Current working directory: ${process.cwd()}
433
- Always use Dolphin Framework standards.`;
434
- const aiData = JSON.stringify({
435
- contents: [{ parts: [{ text: systemPrompt + "\nUser: " + input }] }]
436
- });
437
- const options = {
438
- hostname: 'generativelanguage.googleapis.com',
439
- path: `/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${encodeURIComponent(agentApiKey)}`,
440
- method: 'POST',
441
- headers: { 'Content-Type': 'application/json' }
442
- };
443
- const req = https_1.default.request(options, (res) => {
444
- let body = '';
445
- res.on('data', (d) => body += d);
446
- res.on('end', () => {
447
- try {
448
- const json = JSON.parse(body);
449
- const reply = json.candidates?.[0]?.content?.parts?.[0]?.text || '';
450
- // Handle FILE_WRITE
451
- if (reply.includes('FILE_WRITE:')) {
452
- const match = reply.match(/FILE_WRITE: (.*?)\n([\s\S]*?)END_FILE/);
453
- if (match) {
454
- const fPath = match[1].trim();
455
- const content = match[2].trim().replace(/```javascript|```js|```/g, '');
456
- const fullPath = path_1.default.join(process.cwd(), fPath);
457
- const dir = path_1.default.dirname(fullPath);
458
- if (!fs_1.default.existsSync(dir))
459
- fs_1.default.mkdirSync(dir, { recursive: true });
460
- fs_1.default.writeFileSync(fullPath, content);
461
- console.log(`🤖 Dolphin Agent: Created/Updated file [${fPath}]`);
462
- }
463
- }
464
- // Handle SHELL_RUN
465
- if (reply.includes('SHELL_RUN:')) {
466
- const cmdMatch = reply.match(/SHELL_RUN: (.*)/);
467
- if (cmdMatch) {
468
- const cmd = cmdMatch[1].trim();
469
- console.log(`🤖 Dolphin Agent wants to run: [${cmd}]`);
470
- // For safety, we just log it or we could use execSync
471
- // execSync(cmd, { stdio: 'inherit' });
472
- }
473
- }
474
- console.log(`🤖 Dolphin: ${reply.split('FILE_WRITE:')[0].split('SHELL_RUN:')[0].trim()}\n`);
475
- ask();
476
- }
477
- catch (e) {
478
- console.log('❌ Error parsing AI response.');
479
- ask();
480
- }
481
- });
482
- });
483
- req.write(aiData);
484
- req.end();
485
- });
486
- };
487
- ask();
488
- });
489
- break;
490
- case 'clean':
491
- const filesToClean = ['ai-generated-app.js', 'ai-test-output.js'];
492
- filesToClean.forEach(file => {
493
- const filePath = path_1.default.join(process.cwd(), file);
494
- if (fs_1.default.existsSync(filePath)) {
495
- fs_1.default.unlinkSync(filePath);
496
- console.log(`🗑️ Deleted: ${file}`);
497
- }
498
- });
499
- console.log('✅ Cleanup complete.');
500
- break;
501
- case 'serve':
502
- let portStr = args.find(arg => arg.startsWith('--port='))?.split('=')[1];
503
- if (!portStr) {
504
- const portIdx = args.indexOf('--port');
505
- if (portIdx !== -1 && args[portIdx + 1])
506
- portStr = args[portIdx + 1];
507
- }
508
- const port = parseInt(portStr || '3000');
509
- const rt = new core_1.RealtimeCore({ debug: true });
510
- const server = (0, server_1.createDolphinServer)({ realtime: rt });
511
- server.get('/', (ctx) => ctx.html('<h1>Dolphin CLI Static Server</h1>'));
512
- server.listen(port, () => console.log(`✅ Dolphin Server running at http://localhost:${port}`));
108
+ };
109
+ ask();
513
110
  break;
514
111
  case 'init':
515
- console.log('🏗️ Initializing Dolphin Project...');
516
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'app.js'), TEMPLATES.app);
517
- if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), 'package.json'))) {
518
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'package.json'), JSON.stringify({
519
- name: path_1.default.basename(process.cwd()),
520
- version: '1.0.0',
521
- main: 'app.js',
522
- type: 'module',
523
- dependencies: {
524
- "dolphin-server-modules": "^2.2.2",
525
- "mongoose": "^8.0.0",
526
- "zod": "^3.22.0"
527
- }
528
- }, null, 2));
529
- console.log('✅ Created package.json with core dependencies.');
530
- }
531
- else {
532
- console.log('⚠️ package.json already exists. Skipping...');
533
- }
534
- if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), '.gitignore'))) {
535
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), '.gitignore'), '.env\nnode_modules\ndist\n.DS_Store');
536
- console.log('✅ Created .gitignore');
537
- }
112
+ case 'init-prod':
113
+ CLIUI.heading('Scaffolding Production Project');
114
+ const dirs = ['models', 'controllers', 'routes', 'middleware', 'services', 'config'];
115
+ dirs.forEach(dir => {
116
+ const p = path.join(process.cwd(), dir);
117
+ if (!fs.existsSync(p))
118
+ fs.mkdirSync(p, { recursive: true });
119
+ });
120
+ fs.writeFileSync(path.join(process.cwd(), 'app.js'), TEMPLATES.app);
121
+ CLIUI.success('Folders and app.js created.');
538
122
  break;
539
123
  case 'add':
540
- const type = args[1]; // adapter, auth, crud
541
- const name = args[2] || 'Default';
542
- if (type === 'adapter') {
543
- if (name === 'mongoose') {
544
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'db.js'), TEMPLATES.mongoose);
545
- console.log('✅ Added Mongoose Adapter template to db.js');
546
- }
547
- else if (name === 'sequelize') {
548
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'db-sql.js'), TEMPLATES.sequelize);
549
- console.log('✅ Added Sequelize (SQL) Adapter template to db-sql.js');
124
+ const subCommand = args[1];
125
+ if (subCommand === 'adapter') {
126
+ const type = args[2];
127
+ if (type === 'mongoose') {
128
+ const configDir = path.join(process.cwd(), 'config');
129
+ if (!fs.existsSync(configDir))
130
+ fs.mkdirSync(configDir, { recursive: true });
131
+ fs.writeFileSync(path.join(configDir, 'db.js'), TEMPLATES.mongoose);
132
+ CLIUI.success('Mongoose adapter added to config/db.js');
550
133
  }
551
134
  else {
552
- console.log(' Unknown adapter. Use "mongoose" or "sequelize".');
135
+ CLIUI.error('Unsupported adapter. Try: dolphin add adapter mongoose');
553
136
  }
554
137
  }
555
- else if (type === 'auth') {
556
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'auth-router.js'), TEMPLATES.auth);
557
- console.log('✅ Added Auth Controller template to auth-router.js');
558
- }
559
- else if (type === 'crud') {
560
- const crudTemplate = typeof TEMPLATES.crud === 'function' ? TEMPLATES.crud(name) : '';
561
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), `${name.toLowerCase()}-crud.js`), crudTemplate);
562
- console.log(`✅ Added CRUD Controller template for "${name}" to ${name.toLowerCase()}-crud.js`);
138
+ else if (subCommand === 'auth') {
139
+ const controllerDir = path.join(process.cwd(), 'controllers');
140
+ const modelDir = path.join(process.cwd(), 'models');
141
+ if (!fs.existsSync(controllerDir))
142
+ fs.mkdirSync(controllerDir, { recursive: true });
143
+ if (!fs.existsSync(modelDir))
144
+ fs.mkdirSync(modelDir, { recursive: true });
145
+ fs.writeFileSync(path.join(controllerDir, 'auth.js'), TEMPLATES.auth);
146
+ fs.writeFileSync(path.join(modelDir, 'User.js'), TEMPLATES.authModel);
147
+ CLIUI.success('Auth controller and User model added.');
148
+ }
149
+ else if (subCommand === 'crud') {
150
+ const name = args[2];
151
+ if (!name)
152
+ return CLIUI.error('Usage: dolphin add crud <ModelName>');
153
+ const controllerDir = path.join(process.cwd(), 'controllers');
154
+ const modelDir = path.join(process.cwd(), 'models');
155
+ if (!fs.existsSync(controllerDir))
156
+ fs.mkdirSync(controllerDir, { recursive: true });
157
+ if (!fs.existsSync(modelDir))
158
+ fs.mkdirSync(modelDir, { recursive: true });
159
+ fs.writeFileSync(path.join(controllerDir, `${name.toLowerCase()}.js`), TEMPLATES.crud(name));
160
+ fs.writeFileSync(path.join(modelDir, `${name}.js`), TEMPLATES.crudModel(name));
161
+ CLIUI.success(`CRUD Controller and Model for ${name} generated successfully!`);
162
+ console.log(` 📄 Created: controllers/${name.toLowerCase()}.js`);
163
+ console.log(` 📄 Created: models/${name}.js`);
563
164
  }
564
165
  else {
565
- console.log(' Unknown type. Use "adapter", "auth", or "crud".');
566
- }
567
- break;
568
- case 'mongo-auth-model':
569
- const modelsDir = path_1.default.join(process.cwd(), 'models');
570
- if (!fs_1.default.existsSync(modelsDir)) {
571
- fs_1.default.mkdirSync(modelsDir);
572
- }
573
- fs_1.default.writeFileSync(path_1.default.join(modelsDir, 'auth-models.js'), TEMPLATES.authModel);
574
- console.log('✅ Generated fixed Auth models (User, RefreshToken) in models/auth-models.js');
575
- break;
576
- case 'init-prod':
577
- console.log('🚀 Scaffolding Production-Ready Dolphin Project...');
578
- const dirs = ['models', 'controllers', 'routes', 'middleware', 'services', 'config'];
579
- dirs.forEach(dir => {
580
- const dirPath = path_1.default.join(process.cwd(), dir);
581
- if (!fs_1.default.existsSync(dirPath)) {
582
- fs_1.default.mkdirSync(dirPath);
583
- console.log(`📁 Created: /${dir}`);
584
- }
585
- });
586
- // Create basic files in the structure
587
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'app.js'), TEMPLATES.app);
588
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'config', 'db.js'), TEMPLATES.mongoose);
589
- if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), 'package.json'))) {
590
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'package.json'), JSON.stringify({
591
- name: path_1.default.basename(process.cwd()),
592
- version: '1.0.0',
593
- main: 'app.js',
594
- type: 'module',
595
- dependencies: {
596
- "dolphin-server-modules": "^2.2.4",
597
- "mongoose": "^8.0.0",
598
- "zod": "^3.22.0"
599
- }
600
- }, null, 2));
601
- }
602
- if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), '.gitignore'))) {
603
- fs_1.default.writeFileSync(path_1.default.join(process.cwd(), '.gitignore'), '.env\nnode_modules\ndist\n.DS_Store');
604
- console.log('✅ Created .gitignore');
166
+ CLIUI.error('Usage: dolphin add <adapter|auth|crud>');
605
167
  }
606
- console.log('✅ Production scaffolding complete. Start swimming! 🐬');
607
168
  break;
608
169
  case 'help':
609
170
  default:
171
+ CLIUI.heading('Dolphin Framework CLI');
610
172
  console.log(`
611
- 🐬 Dolphin Framework CLI
612
173
  Commands:
613
- serve Start a basic development server
614
- init Bootstrap a basic Dolphin project
615
- init-prod Scaffold a production-grade folder structure
616
- generate <prompt> AI-powered single-file API generation
617
- generate-full <prompt> AI-powered full project architecture (folders + files)
618
- modify <file> <prompt> AI-powered file modification/update
619
- chat Interactive AI chat for coding help
620
- clean Remove AI generated files
621
- add adapter <type> Add a database adapter (mongoose, sequelize)
622
- add auth Add a pre-configured Auth controller
623
- add crud <Name> Add a pre-configured CRUD controller
624
- mongo-auth-model Generate fixed Mongoose models for Auth (User, RefreshToken)
625
- generate "prompt" --model=gemini-1.5-pro (Custom AI model support)
626
-
627
- Options:
628
- --port=3000 Specify port for serve command
174
+ serve Start development server
175
+ generate <prompt> Quick AI code generation
176
+ generate-full <p> Full project architecture
177
+ chat Interactive AI coding assistant
178
+ init-prod Scaffold production folder structure
179
+ add adapter <t> Add Mongoose/Sequelize adapter
180
+ add auth Add pre-built Auth controller
181
+ add crud <Name> Add CRUD controller for a model
629
182
  `);
630
183
  break;
631
184
  }
632
185
  }
633
186
  run().catch(err => {
634
- console.error('❌ CLI Error:', err);
187
+ CLIUI.error(err.message);
635
188
  });
636
189
  //# sourceMappingURL=cli.js.map