okai 0.0.27 → 0.0.29

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/dist/cs-ast.js CHANGED
@@ -182,12 +182,12 @@ export class CSharpAst {
182
182
  // Ignore properties with these attributes on APIs
183
183
  ignoreCreateProps = [
184
184
  'autoIncrement',
185
- 'references',
185
+ 'reference',
186
186
  'compute',
187
187
  'computed',
188
188
  ].map(x => x.toLowerCase());
189
189
  ignoreUpdateProps = [
190
- 'references',
190
+ 'reference',
191
191
  'compute',
192
192
  'computed',
193
193
  ].map(x => x.toLowerCase());
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { CSharpApiGenerator } from "./cs-apis.js";
8
8
  import { CSharpMigrationGenerator } from "./cs-migrations.js";
9
9
  import { getFileContent } from "./client.js";
10
10
  import { toTsd } from "./tsd-gen.js";
11
+ import { UiMjsGroupGenerator, UiMjsIndexGenerator } from "./ui-mjs.js";
11
12
  function normalizeSwitches(cmd) { return cmd.replace(/^-+/, '/'); }
12
13
  function parseArgs(...args) {
13
14
  const ret = {
@@ -50,6 +51,9 @@ function parseArgs(...args) {
50
51
  case "/cached":
51
52
  ret.cached = true;
52
53
  break;
54
+ case "/system":
55
+ ret.system = args[++i];
56
+ break;
53
57
  default:
54
58
  ret.unknown = ret.unknown || [];
55
59
  ret.unknown.push(arg);
@@ -94,6 +98,10 @@ function parseArgs(...args) {
94
98
  ret.accept = args[++i];
95
99
  }
96
100
  }
101
+ else if (arg == "chat") {
102
+ ret.type = "chat";
103
+ ret.chat = args[++i];
104
+ }
97
105
  else if (arg.endsWith('.d.ts')) {
98
106
  if (ret.type == "help")
99
107
  ret.type = "update";
@@ -147,6 +155,7 @@ export async function cli(cmdArgs) {
147
155
  migrationsDir: "/path/to/MyApp/Migrations",
148
156
  serviceModelDir: "/path/to/MyApp.ServiceModel",
149
157
  serviceInterfaceDir: "/path/to/MyApp.ServiceInterfaces",
158
+ uiMjsDir: "/path/to/MyApp/wwwroot/admin/sections",
150
159
  };
151
160
  fs.writeFileSync('okai.json', JSON.stringify(info, undefined, 2));
152
161
  console.log(`Added: okai.json`);
@@ -172,6 +181,8 @@ ${bin} ls models Display list of available premium LLM models
172
181
  ${bin} init Initialize okai.json with project info to override default paths
173
182
  ${bin} init <model> Create an empty <model>.d.ts file for the specified model
174
183
  ${bin} info Display current project info
184
+ ${bin} chat <prompt> Submit a new OpenAI chat request with the specified prompt
185
+ -system <prompt> Specify a system prompt
175
186
 
176
187
  Options:
177
188
  -v, -verbose Display verbose logging
@@ -239,10 +250,14 @@ Options:
239
250
  tsdAst.references.push({ path: './api.d.ts' });
240
251
  }
241
252
  tsd = toTsd(tsdAst);
253
+ let uiFileName = info.uiMjsDir
254
+ ? path.join(info.uiMjsDir, `${groupName}.mjs`)
255
+ : null;
242
256
  const tsdContent = createTsdFile(info, {
243
257
  prompt: `New ${model}`,
244
258
  apiFileName: `${groupName}.cs`,
245
259
  tsd,
260
+ uiFileName,
246
261
  });
247
262
  command.type = "update"; // let update handle the rest
248
263
  command.tsdFile = groupName + '.d.ts';
@@ -280,15 +295,10 @@ Options:
280
295
  }
281
296
  return tsdPath;
282
297
  }
283
- function resolveMigrationFile(migrationPath) {
284
- return migrationPath.startsWith('~/')
285
- ? path.join(info.slnDir, trimStart(migrationPath, '~/'))
286
- : path.join(process.cwd(), migrationPath);
287
- }
288
- function resolveApiFile(apiPath) {
289
- return apiPath.startsWith('~/')
290
- ? path.join(info.slnDir, trimStart(apiPath, '~/'))
291
- : path.join(process.cwd(), apiPath);
298
+ function resolveFile(filePath) {
299
+ return filePath.startsWith('~/')
300
+ ? path.join(info.slnDir, trimStart(filePath, '~/'))
301
+ : path.join(process.cwd(), filePath);
292
302
  }
293
303
  if (command.type === "update") {
294
304
  let tsdPath = assertTsdPath(command.tsdFile);
@@ -304,7 +314,7 @@ Options:
304
314
  const genApis = new CSharpApiGenerator();
305
315
  const csApiFiles = genApis.generate(result.csAst);
306
316
  const apiContent = replaceMyApp(csApiFiles[Object.keys(csApiFiles)[0]], info.projectName);
307
- const apiPath = resolveApiFile(header.api);
317
+ const apiPath = resolveFile(header.api);
308
318
  console.log(`${logPrefix}${apiPath}`);
309
319
  fs.writeFileSync(apiPath, apiContent, { encoding: 'utf-8' });
310
320
  if (header?.migration) {
@@ -312,10 +322,23 @@ Options:
312
322
  const getMigrations = new CSharpMigrationGenerator();
313
323
  const csMigrationFiles = getMigrations.generate(result.csAst);
314
324
  const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
315
- const migrationPath = resolveApiFile(header.migration);
325
+ const migrationPath = resolveFile(header.migration);
316
326
  console.log(`${logPrefix}${migrationPath}`);
317
327
  fs.writeFileSync(migrationPath, migrationContent, { encoding: 'utf-8' });
318
328
  }
329
+ if (header?.uiMjs) {
330
+ const uiMjsGroupPath = resolveFile(header.uiMjs);
331
+ const uiMjsGroupGen = new UiMjsGroupGenerator();
332
+ const uiMjsGroup = uiMjsGroupGen.generate(result.csAst, result.groupName);
333
+ console.log(`${logPrefix}${uiMjsGroupPath}`);
334
+ fs.writeFileSync(uiMjsGroupPath, uiMjsGroup, { encoding: 'utf-8' });
335
+ const uiMjsDir = path.dirname(uiMjsGroupPath);
336
+ const uiMjsIndexGen = new UiMjsIndexGenerator();
337
+ const uiMjsIndex = uiMjsIndexGen.generate(fs.readdirSync(uiMjsDir));
338
+ const uiMjsIndexPath = path.join(uiMjsDir, 'index.mjs');
339
+ console.log(`${logPrefix}${uiMjsIndexPath}`);
340
+ fs.writeFileSync(uiMjsIndexPath, uiMjsIndex, { encoding: 'utf-8' });
341
+ }
319
342
  console.log(`${logPrefix}${tsdPath}`);
320
343
  const newTsdContent = toTsdHeader(header) + (tsdContent.startsWith('///') ? '' : '\n\n') + tsdContent;
321
344
  fs.writeFileSync(tsdPath, newTsdContent, { encoding: 'utf-8' });
@@ -353,7 +376,7 @@ Options:
353
376
  if (command.verbose)
354
377
  console.log(JSON.stringify(header, undefined, 2));
355
378
  if (header?.migration) {
356
- const migrationPath = resolveMigrationFile(header.migration);
379
+ const migrationPath = resolveFile(header.migration);
357
380
  if (fs.existsSync(migrationPath)) {
358
381
  fs.unlinkSync(migrationPath);
359
382
  console.log(`Removed: ${migrationPath}`);
@@ -363,7 +386,7 @@ Options:
363
386
  }
364
387
  }
365
388
  if (header?.api) {
366
- const apiPath = resolveApiFile(header.api);
389
+ const apiPath = resolveFile(header.api);
367
390
  if (fs.existsSync(apiPath)) {
368
391
  fs.unlinkSync(apiPath);
369
392
  console.log(`Removed: ${apiPath}`);
@@ -372,6 +395,21 @@ Options:
372
395
  console.log(`APIs .cs file not found: ${apiPath}`);
373
396
  }
374
397
  }
398
+ if (header?.uiMjs) {
399
+ const uiMjsGroupPath = resolveFile(header.uiMjs);
400
+ if (fs.existsSync(uiMjsGroupPath)) {
401
+ fs.unlinkSync(uiMjsGroupPath);
402
+ console.log(`Removed: ${uiMjsGroupPath}`);
403
+ const uiMjsDir = path.dirname(uiMjsGroupPath);
404
+ const uiMjsIndexGen = new UiMjsIndexGenerator();
405
+ const uiMjsIndex = uiMjsIndexGen.generate(fs.readdirSync(uiMjsDir));
406
+ const uiMjsIndexPath = path.join(uiMjsDir, 'index.mjs');
407
+ fs.writeFileSync(uiMjsIndexPath, uiMjsIndex, { encoding: 'utf-8' });
408
+ }
409
+ else {
410
+ console.log(`UI .mjs file not found: ${uiMjsGroupPath}`);
411
+ }
412
+ }
375
413
  fs.unlinkSync(tsdPath);
376
414
  console.log(`Removed: ${tsdPath}`);
377
415
  const serviceModelDir = path.dirname(tsdPath);
@@ -387,6 +425,41 @@ Options:
387
425
  await acceptGist(command, command.accept);
388
426
  process.exit(0);
389
427
  }
428
+ if (command.type === "chat") {
429
+ try {
430
+ const url = new URL('/chat', command.baseUrl);
431
+ const formData = new FormData();
432
+ formData.append('prompt', command.chat);
433
+ if (command.system) {
434
+ formData.append('system', command.system);
435
+ }
436
+ if (command.models) {
437
+ formData.append('model', command.models);
438
+ if (command.license) {
439
+ formData.append('license', command.license);
440
+ }
441
+ }
442
+ if (command.verbose)
443
+ console.log(`POST: ${url}`);
444
+ const res = await fetch(url, {
445
+ method: 'POST',
446
+ body: formData,
447
+ });
448
+ if (!res.ok) {
449
+ console.log(`Failed to chat: ${res.statusText}`);
450
+ process.exit(1);
451
+ }
452
+ const response = await res.json();
453
+ if (command.verbose)
454
+ console.log(JSON.stringify(response, undefined, 2));
455
+ const content = response.choices[response.choices.length - 1]?.message?.content;
456
+ console.log(content);
457
+ }
458
+ catch (err) {
459
+ console.error(err);
460
+ }
461
+ process.exit(0);
462
+ }
390
463
  if (command.type === "prompt") {
391
464
  try {
392
465
  if (!info.serviceModelDir)
@@ -669,10 +742,22 @@ function chooseFile(ctx, info, gist, comamnd) {
669
742
  const apiTypesPath = path.join(info.slnDir, relativeServiceModelDir, `api.d.ts`);
670
743
  const apiFile = path.join(import.meta.dirname, 'api.d.ts');
671
744
  fs.writeFileSync(apiTypesPath, fs.readFileSync(apiFile, 'utf-8'));
745
+ let uiFileName = null;
746
+ if (info.uiMjsDir) {
747
+ uiFileName = `${res.groupName}.mjs`;
748
+ const uiGroupPath = path.join(info.uiMjsDir, uiFileName);
749
+ const uiVueGen = new UiMjsGroupGenerator();
750
+ const uiGroupSrc = uiVueGen.generate(res.csAst, res.groupName);
751
+ fs.writeFileSync(uiGroupPath, uiGroupSrc);
752
+ const uiIndexGen = new UiMjsIndexGenerator();
753
+ const uiIndexSrc = uiIndexGen.generate(fs.readdirSync(info.uiMjsDir));
754
+ fs.writeFileSync(path.join(info.uiMjsDir, `index.mjs`), uiIndexSrc);
755
+ }
672
756
  const tsdContent = createTsdFile(info, {
673
757
  prompt: titleBar.content.replaceAll('/*', '').replaceAll('*/', ''),
674
758
  apiFileName,
675
759
  tsd: res.tsd,
760
+ uiFileName,
676
761
  });
677
762
  const tsdFileName = `${res.groupName}.d.ts`;
678
763
  const fullTsdPath = path.join(info.slnDir, relativeServiceModelDir, tsdFileName);
@@ -680,7 +765,7 @@ function chooseFile(ctx, info, gist, comamnd) {
680
765
  const fullMigrationPath = path.join(info.slnDir, relativeMigrationDir, migrationFileName);
681
766
  if (!fs.existsSync(path.dirname(fullTsdPath))) {
682
767
  console.log(`Directory does not exist: ${path.dirname(fullTsdPath)}`);
683
- process.exit(0);
768
+ process.exit(1);
684
769
  }
685
770
  console.log(`\nSelected '${result.selectedFile}' data models`);
686
771
  fs.writeFileSync(fullTsdPath, tsdContent, { encoding: 'utf-8' });
@@ -692,6 +777,7 @@ function chooseFile(ctx, info, gist, comamnd) {
692
777
  if (fs.existsSync(path.dirname(fullMigrationPath))) {
693
778
  fs.writeFileSync(fullMigrationPath, migrationContent, { encoding: 'utf-8' });
694
779
  console.log(`Saved: ${fullMigrationPath}`);
780
+ console.log(`\nRun 'dotnet run --AppTasks=migrate' to apply the new migration and create the new tables`);
695
781
  }
696
782
  const script = path.basename(process.argv[1]);
697
783
  console.log(`\nTo regenerate classes, update '${tsdFileName}' then run:`);
@@ -706,7 +792,7 @@ function chooseFile(ctx, info, gist, comamnd) {
706
792
  .catch((err) => {
707
793
  if (comamnd.verbose)
708
794
  console.log(`ERROR: ${err.message ?? err}`);
709
- process.exit(0);
795
+ process.exit(1);
710
796
  });
711
797
  }
712
798
  else {
@@ -773,6 +859,9 @@ function createTsdFile(info, opt) {
773
859
  const relativeMigrationDir = info.migrationsDir && fs.existsSync(info.migrationsDir)
774
860
  ? trimStart(info.migrationsDir.substring(info.slnDir.length), '~/')
775
861
  : null;
862
+ const relativeUiVueDir = opt.uiFileName && info.uiMjsDir && fs.existsSync(info.uiMjsDir)
863
+ ? trimStart(info.uiMjsDir.substring(info.slnDir.length), '~/')
864
+ : null;
776
865
  const sb = [
777
866
  `/*prompt: ${opt.prompt}`,
778
867
  `api: ~/${path.join(relativeServiceModelDir, opt.apiFileName)}`,
@@ -780,41 +869,10 @@ function createTsdFile(info, opt) {
780
869
  if (relativeMigrationDir) {
781
870
  sb.push(`migration: ~/${path.join(relativeMigrationDir, migrationFileName)}`);
782
871
  }
872
+ if (relativeUiVueDir) {
873
+ sb.push(`ui.mjs: ~/${path.join(relativeUiVueDir, opt.uiFileName)}`);
874
+ }
783
875
  sb.push('*/');
784
876
  sb.push('');
785
877
  return sb.join('\n') + (opt.tsd.startsWith('///') ? '' : '\n\n') + opt.tsd;
786
878
  }
787
- function applyCSharpGist(ctx, info, gist, { accept = false, acceptAll = false, discard = false }) {
788
- const { screen, titleBar, fileList, preview, statusBar, result } = ctx;
789
- function removeSelected() {
790
- delete gist.files[result.selectedFile];
791
- fileList.removeItem(result.selectedFile);
792
- const nextFile = Object.keys(gist.files)[0];
793
- if (nextFile) {
794
- result.selectedFile = nextFile;
795
- const nextFileIndex = fileList.getItemIndex(nextFile);
796
- fileList.select(nextFileIndex);
797
- preview.setContent(gist.files[nextFile].content);
798
- }
799
- screen.render();
800
- if (Object.keys(gist.files).length === 0) {
801
- //screen.destroy()
802
- exit(screen, info, gist);
803
- }
804
- }
805
- if (discard) {
806
- const file = gist.files[result.selectedFile];
807
- removeSelected();
808
- }
809
- else if (accept) {
810
- const file = gist.files[result.selectedFile];
811
- writeFile(info, result.selectedFile, file.content);
812
- removeSelected();
813
- }
814
- else if (acceptAll) {
815
- for (const [filename, file] of Object.entries(gist.files)) {
816
- writeFile(info, filename, file.content);
817
- }
818
- exit(screen, info, gist);
819
- }
820
- }
package/dist/info.js CHANGED
@@ -53,6 +53,10 @@ export function projectInfo(cwd) {
53
53
  serviceModelDir,
54
54
  serviceInterfaceDir,
55
55
  };
56
+ const uiVueDir = path.join(hostDir, "wwwroot", "admin", "sections");
57
+ if (fs.existsSync(uiVueDir)) {
58
+ info.uiMjsDir = uiVueDir;
59
+ }
56
60
  for (const file of walk(serviceInterfaceDir, [], {
57
61
  include: (path) => path.endsWith(".cs"),
58
62
  excludeDirs: ["obj", "bin"]
package/dist/ui-mjs.js ADDED
@@ -0,0 +1,70 @@
1
+ import { getGroupName, plural, toCamelCase } from "./utils.js";
2
+ /*
3
+ export default {
4
+ group: "Bookings",
5
+ items: {
6
+ Bookings: {
7
+ type: 'Booking',
8
+ component: {
9
+ template:`<AutoQueryGrid :type="type"
10
+ selected-columns="id,name,roomType,roomNumber,bookingStartDate,bookingEndDate,cost,couponId,discount" />`,
11
+ },
12
+ },
13
+ Coupons: {
14
+ type: 'Coupon',
15
+ component: {
16
+ template:`<AutoQueryGrid :type="type"
17
+ selected-columns="id,name,description,discount,expiryDate" />`,
18
+ },
19
+ },
20
+ },
21
+ }
22
+ */
23
+ export class UiMjsGroupGenerator {
24
+ generate(ast, groupLabel) {
25
+ if (!groupLabel) {
26
+ const groupName = getGroupName(ast);
27
+ groupLabel = plural(groupName);
28
+ }
29
+ const sb = [
30
+ `export default {`,
31
+ ` group: "${groupLabel}",`,
32
+ ` items: {`,
33
+ ...ast.types.filter(x => !x.isEnum && !x.isInterface).map(x => {
34
+ return [
35
+ ` ${plural(x.name)}: {`,
36
+ ` type: '${x.name}',`,
37
+ ` component: {`,
38
+ ` template:\`<AutoQueryGrid :type="type"`,
39
+ ` selected-columns="${x.properties.map(x => toCamelCase(x.name)).join(',')}" />\`,`,
40
+ ` },`,
41
+ ` },`,
42
+ ].join('\n');
43
+ }),
44
+ ` }`,
45
+ `}`,
46
+ ];
47
+ const src = sb.join('\n');
48
+ return src;
49
+ }
50
+ }
51
+ /*
52
+ import Bookings from './Bookings.mjs'
53
+ export {
54
+ Bookings,
55
+ }
56
+ */
57
+ export class UiMjsIndexGenerator {
58
+ generate(files) {
59
+ const imports = [];
60
+ const exports = ['export {'];
61
+ files.filter(x => x != 'index.mjs').forEach(x => {
62
+ const name = x.replace('.mjs', '');
63
+ imports.push(`import ${name} from './${name}.mjs'`);
64
+ exports.push(` ${name},`);
65
+ });
66
+ exports.push('}');
67
+ const src = imports.join('\n') + '\n' + exports.join('\n');
68
+ return src;
69
+ }
70
+ }
package/dist/utils.js CHANGED
@@ -178,6 +178,9 @@ export function parseTsdHeader(tsd) {
178
178
  else if (line.startsWith('migration:')) {
179
179
  to.migration = line.replace('migration:', '').trim();
180
180
  }
181
+ else if (line.startsWith('ui.mjs:')) {
182
+ to.uiMjs = line.replace('ui.mjs:', '').trim();
183
+ }
181
184
  else if (!to.api && !to.migration && line.trim()) {
182
185
  to.prompt += line.trim() + '\n';
183
186
  }
@@ -198,6 +201,9 @@ export function toTsdHeader(header) {
198
201
  if (header.migration) {
199
202
  sb.push(`migration: ${header.migration}`);
200
203
  }
204
+ if (header.uiMjs) {
205
+ sb.push(`ui.mjs: ${header.uiMjs}`);
206
+ }
201
207
  sb.push('*/');
202
208
  sb.push('');
203
209
  return sb.join('\n');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "okai",
3
3
  "type": "module",
4
- "version": "0.0.27",
4
+ "version": "0.0.29",
5
5
  "bin": "./dist/okai.js",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",