@objectql/cli 1.8.4 ā 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/commands/database-push.d.ts +5 -0
- package/dist/commands/database-push.js +15 -0
- package/dist/commands/database-push.js.map +1 -0
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.js +94 -6
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +37 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.js +31 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/serve.js +122 -46
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/start.d.ts +1 -0
- package/dist/commands/start.js +15 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/index.js +173 -210
- package/dist/index.js.map +1 -1
- package/package.json +13 -7
- package/templates/hello-world/.vscode/extensions.json +7 -0
- package/templates/hello-world/CHANGELOG.md +41 -0
- package/templates/hello-world/README.md +29 -0
- package/templates/hello-world/package.json +24 -0
- package/templates/hello-world/src/index.ts +58 -0
- package/templates/hello-world/tsconfig.json +10 -0
- package/templates/starter/.vscode/extensions.json +7 -0
- package/{CHANGELOG.md ā templates/starter/CHANGELOG.md} +36 -42
- package/templates/starter/README.md +17 -0
- package/templates/starter/__tests__/projects-hooks-actions.test.ts +490 -0
- package/templates/starter/jest.config.js +16 -0
- package/templates/starter/package.json +52 -0
- package/templates/starter/src/README.pages.md +110 -0
- package/templates/starter/src/demo.app.yml +4 -0
- package/templates/starter/src/i18n/zh-CN/projects.json +22 -0
- package/templates/starter/src/modules/kitchen-sink/kitchen_sink.data.yml +18 -0
- package/templates/starter/src/modules/kitchen-sink/kitchen_sink.object.yml +156 -0
- package/templates/starter/src/modules/projects/project_approval.workflow.yml +51 -0
- package/templates/starter/src/modules/projects/projects.action.ts +472 -0
- package/templates/starter/src/modules/projects/projects.data.yml +13 -0
- package/templates/starter/src/modules/projects/projects.hook.ts +339 -0
- package/templates/starter/src/modules/projects/projects.object.yml +148 -0
- package/templates/starter/src/modules/projects/projects.permission.yml +141 -0
- package/templates/starter/src/modules/projects/projects.validation.yml +37 -0
- package/templates/starter/src/modules/tasks/tasks.data.yml +23 -0
- package/templates/starter/src/modules/tasks/tasks.object.yml +34 -0
- package/templates/starter/src/modules/tasks/tasks.permission.yml +167 -0
- package/templates/starter/src/seed.ts +55 -0
- package/templates/starter/src/types/index.ts +3 -0
- package/templates/starter/src/types/kitchen_sink.ts +101 -0
- package/templates/starter/src/types/projects.ts +49 -0
- package/templates/starter/src/types/tasks.ts +33 -0
- package/templates/starter/tsconfig.json +11 -0
- package/templates/starter/tsconfig.tsbuildinfo +1 -0
- package/AI_EXAMPLES.md +0 -154
- package/AI_IMPLEMENTATION_SUMMARY.md +0 -509
- package/AI_TUTORIAL.md +0 -144
- package/IMPLEMENTATION_SUMMARY.md +0 -437
- package/USAGE_EXAMPLES.md +0 -951
- package/__tests__/commands.test.ts +0 -426
- package/jest.config.js +0 -19
- package/src/commands/ai.ts +0 -509
- package/src/commands/build.ts +0 -98
- package/src/commands/dev.ts +0 -23
- package/src/commands/format.ts +0 -110
- package/src/commands/generate.ts +0 -135
- package/src/commands/i18n.ts +0 -303
- package/src/commands/init.ts +0 -191
- package/src/commands/lint.ts +0 -98
- package/src/commands/migrate.ts +0 -314
- package/src/commands/new.ts +0 -221
- package/src/commands/repl.ts +0 -120
- package/src/commands/serve.ts +0 -96
- package/src/commands/start.ts +0 -100
- package/src/commands/sync.ts +0 -328
- package/src/commands/test.ts +0 -98
- package/src/index.ts +0 -356
- package/tsconfig.json +0 -15
- package/tsconfig.tsbuildinfo +0 -1
package/src/commands/ai.ts
DELETED
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as yaml from 'js-yaml';
|
|
4
|
-
import * as readline from 'readline';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import OpenAI from 'openai';
|
|
7
|
-
import { Validator, ObjectQLAgent } from '@objectql/core';
|
|
8
|
-
import { glob } from 'fast-glob';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Create an ObjectQL AI agent instance
|
|
12
|
-
*/
|
|
13
|
-
export function createAgent(apiKey: string): ObjectQLAgent {
|
|
14
|
-
return new ObjectQLAgent({ apiKey });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface GenerateOptions {
|
|
18
|
-
description: string;
|
|
19
|
-
output?: string;
|
|
20
|
-
type?: 'basic' | 'complete' | 'custom';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ValidateOptions {
|
|
24
|
-
path: string;
|
|
25
|
-
fix?: boolean;
|
|
26
|
-
verbose?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface ChatOptions {
|
|
30
|
-
initialPrompt?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface ConversationalOptions {
|
|
34
|
-
output?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Conversational generation with step-by-step refinement
|
|
39
|
-
*/
|
|
40
|
-
export async function aiConversational(options: ConversationalOptions): Promise<void> {
|
|
41
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
42
|
-
if (!apiKey) {
|
|
43
|
-
console.error(chalk.red('Error: OPENAI_API_KEY environment variable is not set.'));
|
|
44
|
-
console.log(chalk.yellow('\nPlease set your OpenAI API key:'));
|
|
45
|
-
console.log(chalk.cyan(' export OPENAI_API_KEY=your-api-key-here'));
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const outputDir = options.output || './src';
|
|
50
|
-
const agent = createAgent(apiKey);
|
|
51
|
-
|
|
52
|
-
console.log(chalk.blue('š¬ ObjectQL Conversational Generator\n'));
|
|
53
|
-
console.log(chalk.gray('Build your application step by step through conversation.'));
|
|
54
|
-
console.log(chalk.gray('Type "done" to finish and save, "exit" to quit without saving.\n'));
|
|
55
|
-
|
|
56
|
-
const rl = readline.createInterface({
|
|
57
|
-
input: process.stdin,
|
|
58
|
-
output: process.stdout,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
let conversationHistory: any[] = [];
|
|
62
|
-
let currentApp: any = null;
|
|
63
|
-
let fileCount = 0;
|
|
64
|
-
|
|
65
|
-
const askQuestion = () => {
|
|
66
|
-
const prompt = currentApp
|
|
67
|
-
? chalk.cyan('\nWhat would you like to change or add? ')
|
|
68
|
-
: chalk.cyan('Describe your application: ');
|
|
69
|
-
|
|
70
|
-
rl.question(prompt, async (input: string) => {
|
|
71
|
-
if (input.toLowerCase() === 'exit') {
|
|
72
|
-
console.log(chalk.blue('\nš Goodbye! No files were saved.'));
|
|
73
|
-
rl.close();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (input.toLowerCase() === 'done') {
|
|
78
|
-
if (!currentApp || !currentApp.files || currentApp.files.length === 0) {
|
|
79
|
-
console.log(chalk.yellow('\nā ļø No application generated yet. Continue the conversation or type "exit" to quit.'));
|
|
80
|
-
askQuestion();
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Save files
|
|
85
|
-
console.log(chalk.yellow('\nš¾ Saving files...'));
|
|
86
|
-
|
|
87
|
-
if (!fs.existsSync(outputDir)) {
|
|
88
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
for (const file of currentApp.files) {
|
|
92
|
-
const filePath = path.join(outputDir, file.filename);
|
|
93
|
-
const fileDir = path.dirname(filePath);
|
|
94
|
-
|
|
95
|
-
if (!fs.existsSync(fileDir)) {
|
|
96
|
-
fs.mkdirSync(fileDir, { recursive: true });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fs.writeFileSync(filePath, file.content);
|
|
100
|
-
console.log(chalk.green(` ā ${file.filename}`));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
console.log(chalk.blue(`\nā
Application saved to: ${outputDir}`));
|
|
104
|
-
console.log(chalk.gray('\nNext steps:'));
|
|
105
|
-
console.log(chalk.cyan(' 1. Review the generated files'));
|
|
106
|
-
console.log(chalk.cyan(' 2. Run: objectql ai validate ' + outputDir));
|
|
107
|
-
console.log(chalk.cyan(' 3. Test with: objectql serve --dir ' + outputDir));
|
|
108
|
-
|
|
109
|
-
rl.close();
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!input.trim()) {
|
|
114
|
-
askQuestion();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
console.log(chalk.yellow('\nā³ Generating...'));
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const result = await agent.generateConversational({
|
|
122
|
-
message: input,
|
|
123
|
-
conversationHistory,
|
|
124
|
-
currentApp,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (!result.success) {
|
|
128
|
-
console.error(chalk.red('\nā Error:'), result.errors?.join(', ') || 'Unknown error');
|
|
129
|
-
askQuestion();
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
conversationHistory = result.conversationHistory;
|
|
134
|
-
currentApp = result;
|
|
135
|
-
fileCount = result.files.length;
|
|
136
|
-
|
|
137
|
-
console.log(chalk.green(`\nā
Generated/Updated ${fileCount} file(s):`));
|
|
138
|
-
|
|
139
|
-
// Group files by type
|
|
140
|
-
const filesByType: Record<string, string[]> = {};
|
|
141
|
-
result.files.forEach(f => {
|
|
142
|
-
if (!filesByType[f.type]) filesByType[f.type] = [];
|
|
143
|
-
filesByType[f.type].push(f.filename);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
Object.entries(filesByType).forEach(([type, files]) => {
|
|
147
|
-
console.log(chalk.cyan(` ${type}:`), files.join(', '));
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Show suggestions
|
|
151
|
-
if (result.suggestions && result.suggestions.length > 0) {
|
|
152
|
-
console.log(chalk.blue('\nš” Suggestions:'));
|
|
153
|
-
result.suggestions.forEach(s => console.log(chalk.gray(` ⢠${s}`)));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
console.log(chalk.gray('\nYou can now:'));
|
|
157
|
-
console.log(chalk.gray(' ⢠Request changes (e.g., "Add email validation to user")'));
|
|
158
|
-
console.log(chalk.gray(' ⢠Add features (e.g., "Add a workflow for approval")'));
|
|
159
|
-
console.log(chalk.gray(' ⢠Type "done" to save files'));
|
|
160
|
-
console.log(chalk.gray(' ⢠Type "exit" to quit without saving'));
|
|
161
|
-
|
|
162
|
-
} catch (error) {
|
|
163
|
-
console.error(chalk.red('\nā Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
askQuestion();
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
askQuestion();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Generate application metadata using AI
|
|
175
|
-
*/
|
|
176
|
-
export async function aiGenerate(options: GenerateOptions): Promise<void> {
|
|
177
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
178
|
-
if (!apiKey) {
|
|
179
|
-
console.error(chalk.red('Error: OPENAI_API_KEY environment variable is not set.'));
|
|
180
|
-
console.log(chalk.yellow('\nPlease set your OpenAI API key:'));
|
|
181
|
-
console.log(chalk.cyan(' export OPENAI_API_KEY=your-api-key-here'));
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const outputDir = options.output || './src';
|
|
186
|
-
|
|
187
|
-
console.log(chalk.blue('š¤ ObjectQL AI Generator\n'));
|
|
188
|
-
console.log(chalk.gray(`Description: ${options.description}`));
|
|
189
|
-
console.log(chalk.gray(`Output directory: ${outputDir}\n`));
|
|
190
|
-
|
|
191
|
-
console.log(chalk.yellow('ā³ Generating metadata...'));
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const agent = createAgent(apiKey);
|
|
195
|
-
const result = await agent.generateApp({
|
|
196
|
-
description: options.description,
|
|
197
|
-
type: options.type || 'custom',
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
if (!result.success || result.files.length === 0) {
|
|
201
|
-
console.log(chalk.yellow('\nā ļø No valid metadata files generated.'));
|
|
202
|
-
if (result.errors) {
|
|
203
|
-
result.errors.forEach(err => console.error(chalk.red(` Error: ${err}`)));
|
|
204
|
-
}
|
|
205
|
-
if (result.rawResponse) {
|
|
206
|
-
console.log(chalk.gray('\nResponse:'));
|
|
207
|
-
console.log(result.rawResponse);
|
|
208
|
-
}
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Create output directory if it doesn't exist
|
|
213
|
-
if (!fs.existsSync(outputDir)) {
|
|
214
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Write files
|
|
218
|
-
console.log(chalk.green('\nā
Generated files:'));
|
|
219
|
-
for (const file of result.files) {
|
|
220
|
-
const filePath = path.join(outputDir, file.filename);
|
|
221
|
-
const fileDir = path.dirname(filePath);
|
|
222
|
-
|
|
223
|
-
if (!fs.existsSync(fileDir)) {
|
|
224
|
-
fs.mkdirSync(fileDir, { recursive: true });
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
fs.writeFileSync(filePath, file.content);
|
|
228
|
-
console.log(chalk.green(` ā ${file.filename} (${file.type})`));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
console.log(chalk.blue(`\nš Files written to: ${outputDir}`));
|
|
232
|
-
console.log(chalk.gray('\nNext steps:'));
|
|
233
|
-
console.log(chalk.cyan(' 1. Review the generated files'));
|
|
234
|
-
console.log(chalk.cyan(' 2. Run: objectql ai validate <path>'));
|
|
235
|
-
console.log(chalk.cyan(' 3. Test with: objectql serve'));
|
|
236
|
-
|
|
237
|
-
} catch (error) {
|
|
238
|
-
console.error(chalk.red('\nā Error generating metadata:'));
|
|
239
|
-
if (error instanceof Error) {
|
|
240
|
-
console.error(chalk.red(error.message));
|
|
241
|
-
}
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Validate metadata files using AI
|
|
248
|
-
*/
|
|
249
|
-
export async function aiValidate(options: ValidateOptions): Promise<void> {
|
|
250
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
251
|
-
|
|
252
|
-
if (!apiKey) {
|
|
253
|
-
console.error(chalk.red('Error: OPENAI_API_KEY environment variable is not set.'));
|
|
254
|
-
console.log(chalk.yellow('\nNote: AI validation requires OpenAI API key.'));
|
|
255
|
-
console.log(chalk.yellow('Falling back to basic validation...\n'));
|
|
256
|
-
await basicValidate(options);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
console.log(chalk.blue('š ObjectQL AI Validator\n'));
|
|
261
|
-
|
|
262
|
-
// Find all metadata files
|
|
263
|
-
const patterns = [
|
|
264
|
-
'**/*.object.yml',
|
|
265
|
-
'**/*.validation.yml',
|
|
266
|
-
'**/*.action.yml',
|
|
267
|
-
'**/*.hook.yml',
|
|
268
|
-
'**/*.permission.yml',
|
|
269
|
-
'**/*.workflow.yml',
|
|
270
|
-
'**/*.data.yml',
|
|
271
|
-
];
|
|
272
|
-
|
|
273
|
-
const files = await glob(patterns, {
|
|
274
|
-
cwd: options.path,
|
|
275
|
-
absolute: true,
|
|
276
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
if (files.length === 0) {
|
|
280
|
-
console.log(chalk.yellow('No metadata files found.'));
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
console.log(chalk.gray(`Found ${files.length} metadata file(s)\n`));
|
|
285
|
-
|
|
286
|
-
const agent = createAgent(apiKey);
|
|
287
|
-
let errorCount = 0;
|
|
288
|
-
let warningCount = 0;
|
|
289
|
-
|
|
290
|
-
for (const filePath of files) {
|
|
291
|
-
const relativePath = path.relative(options.path, filePath);
|
|
292
|
-
console.log(chalk.cyan(`\nš ${relativePath}`));
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
296
|
-
|
|
297
|
-
// Validate using AI agent
|
|
298
|
-
const result = await agent.validateMetadata({
|
|
299
|
-
metadata: content,
|
|
300
|
-
filename: relativePath,
|
|
301
|
-
checkBusinessLogic: true,
|
|
302
|
-
checkPerformance: true,
|
|
303
|
-
checkSecurity: true,
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Display results
|
|
307
|
-
if (result.errors.length > 0) {
|
|
308
|
-
result.errors.forEach(error => {
|
|
309
|
-
console.log(chalk.red(` ā ERROR: ${error.message}`));
|
|
310
|
-
if (error.location) {
|
|
311
|
-
console.log(chalk.gray(` Location: ${error.location}`));
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
errorCount += result.errors.length;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (result.warnings.length > 0) {
|
|
318
|
-
result.warnings.forEach(warning => {
|
|
319
|
-
console.log(chalk.yellow(` ā ļø WARNING: ${warning.message}`));
|
|
320
|
-
if (warning.suggestion) {
|
|
321
|
-
console.log(chalk.gray(` Suggestion: ${warning.suggestion}`));
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
warningCount += result.warnings.length;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (options.verbose && result.info.length > 0) {
|
|
328
|
-
result.info.forEach(info => {
|
|
329
|
-
console.log(chalk.blue(` ā¹ļø INFO: ${info.message}`));
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (result.valid && result.warnings.length === 0) {
|
|
334
|
-
console.log(chalk.green(' ā No issues found'));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
} catch (error) {
|
|
338
|
-
console.log(chalk.red(` ā Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
339
|
-
errorCount++;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Summary
|
|
344
|
-
console.log(chalk.blue('\n' + '='.repeat(60)));
|
|
345
|
-
console.log(chalk.blue('Validation Summary:'));
|
|
346
|
-
console.log(chalk.gray(` Files checked: ${files.length}`));
|
|
347
|
-
|
|
348
|
-
if (errorCount > 0) {
|
|
349
|
-
console.log(chalk.red(` Errors: ${errorCount}`));
|
|
350
|
-
}
|
|
351
|
-
if (warningCount > 0) {
|
|
352
|
-
console.log(chalk.yellow(` Warnings: ${warningCount}`));
|
|
353
|
-
}
|
|
354
|
-
if (errorCount === 0 && warningCount === 0) {
|
|
355
|
-
console.log(chalk.green(' ā All files validated successfully!'));
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (errorCount > 0) {
|
|
359
|
-
process.exit(1);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Basic validation without AI (fallback)
|
|
365
|
-
*/
|
|
366
|
-
async function basicValidate(options: ValidateOptions): Promise<void> {
|
|
367
|
-
const patterns = [
|
|
368
|
-
'**/*.object.yml',
|
|
369
|
-
'**/*.validation.yml',
|
|
370
|
-
];
|
|
371
|
-
|
|
372
|
-
const files = await glob(patterns, {
|
|
373
|
-
cwd: options.path,
|
|
374
|
-
absolute: true,
|
|
375
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
if (files.length === 0) {
|
|
379
|
-
console.log(chalk.yellow('No metadata files found.'));
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
console.log(chalk.gray(`Found ${files.length} metadata file(s)\n`));
|
|
384
|
-
|
|
385
|
-
let errorCount = 0;
|
|
386
|
-
const validator = new Validator({ language: 'en' });
|
|
387
|
-
|
|
388
|
-
for (const filePath of files) {
|
|
389
|
-
const relativePath = path.relative(options.path, filePath);
|
|
390
|
-
console.log(chalk.cyan(`š ${relativePath}`));
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
394
|
-
const data = yaml.load(content) as any;
|
|
395
|
-
|
|
396
|
-
// Validate YAML structure
|
|
397
|
-
if (!data || typeof data !== 'object') {
|
|
398
|
-
console.log(chalk.red(' ā Invalid YAML structure'));
|
|
399
|
-
errorCount++;
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Validate based on file type
|
|
404
|
-
if (filePath.endsWith('.validation.yml')) {
|
|
405
|
-
if (!data.rules || !Array.isArray(data.rules)) {
|
|
406
|
-
console.log(chalk.yellow(' ā ļø No validation rules found'));
|
|
407
|
-
} else {
|
|
408
|
-
console.log(chalk.green(` ā ${data.rules.length} validation rule(s) found`));
|
|
409
|
-
}
|
|
410
|
-
} else if (filePath.endsWith('.object.yml')) {
|
|
411
|
-
if (!data.fields || typeof data.fields !== 'object') {
|
|
412
|
-
console.log(chalk.red(' ā No fields defined'));
|
|
413
|
-
errorCount++;
|
|
414
|
-
} else {
|
|
415
|
-
const fieldCount = Object.keys(data.fields).length;
|
|
416
|
-
console.log(chalk.green(` ā ${fieldCount} field(s) defined`));
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
} catch (error) {
|
|
421
|
-
console.log(chalk.red(` ā Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
422
|
-
errorCount++;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
console.log(chalk.blue('\n' + '='.repeat(60)));
|
|
427
|
-
if (errorCount === 0) {
|
|
428
|
-
console.log(chalk.green('ā Basic validation passed'));
|
|
429
|
-
} else {
|
|
430
|
-
console.log(chalk.red(`ā Found ${errorCount} error(s)`));
|
|
431
|
-
process.exit(1);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Interactive AI chat for metadata assistance
|
|
437
|
-
*/
|
|
438
|
-
export async function aiChat(options: ChatOptions): Promise<void> {
|
|
439
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
440
|
-
if (!apiKey) {
|
|
441
|
-
console.error(chalk.red('Error: OPENAI_API_KEY environment variable is not set.'));
|
|
442
|
-
process.exit(1);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const openai = new OpenAI({ apiKey });
|
|
446
|
-
|
|
447
|
-
console.log(chalk.blue('š¬ ObjectQL AI Assistant\n'));
|
|
448
|
-
console.log(chalk.gray('Ask me anything about ObjectQL metadata, data modeling, or best practices.'));
|
|
449
|
-
console.log(chalk.gray('Type "exit" to quit.\n'));
|
|
450
|
-
|
|
451
|
-
const rl = readline.createInterface({
|
|
452
|
-
input: process.stdin,
|
|
453
|
-
output: process.stdout,
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
const systemPrompt = `You are an expert ObjectQL architect and consultant. Help users with:
|
|
457
|
-
- ObjectQL metadata specifications
|
|
458
|
-
- Data modeling best practices
|
|
459
|
-
- Validation rules and business logic
|
|
460
|
-
- Relationships and field types
|
|
461
|
-
- Application architecture
|
|
462
|
-
- Performance and security considerations
|
|
463
|
-
|
|
464
|
-
Provide clear, actionable advice with examples when appropriate.`;
|
|
465
|
-
|
|
466
|
-
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
|
467
|
-
{ role: 'system', content: systemPrompt }
|
|
468
|
-
];
|
|
469
|
-
|
|
470
|
-
if (options.initialPrompt) {
|
|
471
|
-
messages.push({ role: 'user', content: options.initialPrompt });
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const askQuestion = () => {
|
|
475
|
-
rl.question(chalk.cyan('You: '), async (input: string) => {
|
|
476
|
-
if (input.toLowerCase() === 'exit') {
|
|
477
|
-
console.log(chalk.blue('\nGoodbye! š'));
|
|
478
|
-
rl.close();
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (!input.trim()) {
|
|
483
|
-
askQuestion();
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
messages.push({ role: 'user', content: input });
|
|
488
|
-
|
|
489
|
-
try {
|
|
490
|
-
const completion = await openai.chat.completions.create({
|
|
491
|
-
model: process.env.OPENAI_MODEL || 'gpt-4',
|
|
492
|
-
messages: messages,
|
|
493
|
-
temperature: 0.7,
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
const response = completion.choices[0]?.message?.content || 'No response';
|
|
497
|
-
messages.push({ role: 'assistant', content: response });
|
|
498
|
-
|
|
499
|
-
console.log(chalk.green('\nAssistant: ') + response + '\n');
|
|
500
|
-
} catch (error) {
|
|
501
|
-
console.error(chalk.red('\nError: ') + (error instanceof Error ? error.message : 'Unknown error') + '\n');
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
askQuestion();
|
|
505
|
-
});
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
askQuestion();
|
|
509
|
-
}
|
package/src/commands/build.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { ObjectQL } from '@objectql/core';
|
|
2
|
-
import { ObjectLoader } from '@objectql/platform-node';
|
|
3
|
-
import { generateTypes } from './generate';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
import glob from 'fast-glob';
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
|
|
9
|
-
interface BuildOptions {
|
|
10
|
-
dir?: string;
|
|
11
|
-
output?: string;
|
|
12
|
-
types?: boolean;
|
|
13
|
-
validate?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Build command - validates metadata and generates TypeScript types
|
|
18
|
-
* Prepares the project for production deployment
|
|
19
|
-
*/
|
|
20
|
-
export async function build(options: BuildOptions) {
|
|
21
|
-
console.log(chalk.blue('šØ Building ObjectQL project...\n'));
|
|
22
|
-
|
|
23
|
-
const rootDir = path.resolve(process.cwd(), options.dir || '.');
|
|
24
|
-
const outputDir = path.resolve(process.cwd(), options.output || './dist');
|
|
25
|
-
|
|
26
|
-
// Step 1: Validate metadata
|
|
27
|
-
if (options.validate !== false) {
|
|
28
|
-
console.log(chalk.cyan('1ļøā£ Validating metadata files...'));
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const app = new ObjectQL({ datasources: {} });
|
|
32
|
-
const loader = new ObjectLoader(app.metadata);
|
|
33
|
-
loader.load(rootDir);
|
|
34
|
-
console.log(chalk.green(' ā
Metadata validation passed\n'));
|
|
35
|
-
} catch (e: any) {
|
|
36
|
-
console.error(chalk.red(' ā Metadata validation failed:'), e.message);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Step 2: Generate TypeScript types
|
|
42
|
-
if (options.types !== false) {
|
|
43
|
-
console.log(chalk.cyan('2ļøā£ Generating TypeScript types...'));
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const typesOutput = path.join(outputDir, 'types');
|
|
47
|
-
await generateTypes(rootDir, typesOutput);
|
|
48
|
-
console.log(chalk.green(` ā
Types generated at ${typesOutput}\n`));
|
|
49
|
-
} catch (e: any) {
|
|
50
|
-
console.error(chalk.red(' ā Type generation failed:'), e.message);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Step 3: Copy metadata files to dist
|
|
56
|
-
console.log(chalk.cyan('3ļøā£ Copying metadata files...'));
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
// Ensure output directory exists
|
|
60
|
-
if (!fs.existsSync(outputDir)) {
|
|
61
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Copy .yml files
|
|
65
|
-
const metadataPatterns = [
|
|
66
|
-
'**/*.object.yml',
|
|
67
|
-
'**/*.validation.yml',
|
|
68
|
-
'**/*.permission.yml',
|
|
69
|
-
'**/*.hook.yml',
|
|
70
|
-
'**/*.action.yml',
|
|
71
|
-
'**/*.app.yml'
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
let fileCount = 0;
|
|
75
|
-
const files = await glob(metadataPatterns, { cwd: rootDir });
|
|
76
|
-
|
|
77
|
-
for (const file of files) {
|
|
78
|
-
const srcPath = path.join(rootDir, file);
|
|
79
|
-
const destPath = path.join(outputDir, file);
|
|
80
|
-
const destDir = path.dirname(destPath);
|
|
81
|
-
|
|
82
|
-
if (!fs.existsSync(destDir)) {
|
|
83
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
fs.copyFileSync(srcPath, destPath);
|
|
87
|
-
fileCount++;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
console.log(chalk.green(` ā
Copied ${fileCount} metadata files\n`));
|
|
91
|
-
} catch (e: any) {
|
|
92
|
-
console.error(chalk.red(' ā Failed to copy metadata files:'), e.message);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
console.log(chalk.green.bold('⨠Build completed successfully!\n'));
|
|
97
|
-
console.log(chalk.gray(`Output directory: ${outputDir}`));
|
|
98
|
-
}
|
package/src/commands/dev.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { serve } from './serve';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Start development server with hot reload
|
|
6
|
-
* This is an enhanced version of the serve command for development workflow
|
|
7
|
-
*/
|
|
8
|
-
export async function dev(options: {
|
|
9
|
-
port: number;
|
|
10
|
-
dir: string;
|
|
11
|
-
watch?: boolean;
|
|
12
|
-
}) {
|
|
13
|
-
console.log(chalk.cyan('š Starting ObjectQL Development Server...\n'));
|
|
14
|
-
|
|
15
|
-
// For now, delegate to serve command
|
|
16
|
-
// In future, can add file watching and auto-reload
|
|
17
|
-
await serve({ port: options.port, dir: options.dir });
|
|
18
|
-
|
|
19
|
-
if (options.watch !== false) {
|
|
20
|
-
console.log(chalk.yellow('\nš Watching for file changes... (Not yet implemented)'));
|
|
21
|
-
console.log(chalk.gray(' Tip: Use --no-watch to disable file watching'));
|
|
22
|
-
}
|
|
23
|
-
}
|