okai 0.0.5 → 0.0.7
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/client.js +39 -0
- package/dist/cs-apis.js +123 -0
- package/dist/cs-ast.js +685 -0
- package/dist/cs-gen.js +123 -0
- package/dist/cs-migrations.js +102 -0
- package/dist/icons.js +84 -0
- package/dist/index.js +419 -52
- package/dist/info.js +1 -1
- package/dist/okai.js +1 -1
- package/dist/openai.js +203 -14
- package/dist/ts-ast.js +26 -0
- package/dist/ts-parser.js +170 -0
- package/dist/tsd-gen.js +109 -0
- package/dist/types.js +8 -1
- package/dist/utils.js +178 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
@@ -1,63 +1,348 @@
|
|
1
1
|
import fs from "fs";
|
2
2
|
import path from "path";
|
3
3
|
import blessed from 'blessed';
|
4
|
-
import { projectInfo } from './info
|
5
|
-
import { replaceMyApp } from "./utils
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
import { projectInfo } from './info';
|
5
|
+
import { getGroupName, leftPart, replaceMyApp, trimStart } from "./utils";
|
6
|
+
import { toAst } from "./ts-ast";
|
7
|
+
import { toMetadataTypes } from "./cs-ast";
|
8
|
+
import { CSharpApiGenerator } from "./cs-apis";
|
9
|
+
import { CSharpMigrationGenerator } from "./cs-migrations";
|
10
|
+
import { TsdDataModelGenerator } from "./tsd-gen";
|
11
|
+
import { parseTsdHeader, toTsdHeader } from "./client";
|
12
|
+
function normalizeSwitches(cmd) { return cmd.replace(/^-+/, '/'); }
|
13
|
+
function parseArgs(...args) {
|
14
|
+
const ret = {
|
15
|
+
type: "help",
|
16
|
+
baseUrl: process.env.OKAI_URL || "https://okai.servicestack.com",
|
17
|
+
script: path.basename(process.argv[1]) || "okai",
|
18
|
+
};
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
20
|
+
const arg = args[i];
|
21
|
+
const opt = normalizeSwitches(arg);
|
22
|
+
if (opt.startsWith('/')) {
|
23
|
+
switch (opt) {
|
24
|
+
case "/?":
|
25
|
+
case "/h":
|
26
|
+
case "/help":
|
27
|
+
ret.type = "help";
|
28
|
+
break;
|
29
|
+
case "/v":
|
30
|
+
case "/verbose":
|
31
|
+
ret.verbose = true;
|
32
|
+
break;
|
33
|
+
case "/D":
|
34
|
+
ret.verbose = ret.debug = true;
|
35
|
+
break;
|
36
|
+
case "/w":
|
37
|
+
case "/watch":
|
38
|
+
ret.watch = true;
|
39
|
+
break;
|
40
|
+
case "/m":
|
41
|
+
case "/models":
|
42
|
+
ret.models = args[++i];
|
43
|
+
break;
|
44
|
+
case "/l":
|
45
|
+
case "/license":
|
46
|
+
ret.license = args[++i];
|
47
|
+
break;
|
48
|
+
default:
|
49
|
+
ret.unknown = ret.unknown || [];
|
50
|
+
ret.unknown.push(arg);
|
51
|
+
break;
|
52
|
+
}
|
12
53
|
}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
54
|
+
else if (ret.type === "help" && ["help", "info", "init", "ls", "rm"].includes(arg)) {
|
55
|
+
if (arg == "help")
|
56
|
+
ret.type = "help";
|
57
|
+
else if (arg == "info")
|
58
|
+
ret.type = "info";
|
59
|
+
else if (arg == "init")
|
60
|
+
ret.type = "init";
|
61
|
+
else if (arg == "rm")
|
62
|
+
ret.type = "remove";
|
63
|
+
else if (arg == "ls") {
|
64
|
+
ret.type = "list";
|
65
|
+
ret.list = args[++i];
|
66
|
+
}
|
67
|
+
}
|
68
|
+
else if (arg.endsWith('.d.ts')) {
|
69
|
+
if (ret.type == "help")
|
70
|
+
ret.type = "update";
|
71
|
+
ret.tsdFile = arg;
|
72
|
+
}
|
73
|
+
else {
|
74
|
+
ret.type = "prompt";
|
75
|
+
if (ret.prompt) {
|
76
|
+
ret.prompt += ' ';
|
77
|
+
}
|
78
|
+
ret.prompt = (ret.prompt ?? '') + arg;
|
23
79
|
}
|
80
|
+
}
|
81
|
+
if (ret.type === "prompt") {
|
82
|
+
if (!ret.models && process.env.OKAI_MODELS) {
|
83
|
+
ret.models = process.env.OKAI_MODELS;
|
84
|
+
}
|
85
|
+
if (ret.models) {
|
86
|
+
if (!ret.license && process.env.SERVICESTACK_CERTIFICATE) {
|
87
|
+
ret.license = process.env.SERVICESTACK_CERTIFICATE;
|
88
|
+
}
|
89
|
+
if (!ret.license && process.env.SERVICESTACK_LICENSE) {
|
90
|
+
ret.license = process.env.SERVICESTACK_LICENSE;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
return ret;
|
95
|
+
}
|
96
|
+
export async function cli(cmdArgs) {
|
97
|
+
const command = parseArgs(...cmdArgs);
|
98
|
+
const script = command.script;
|
99
|
+
if (command.verbose) {
|
100
|
+
console.log(`Command: ${JSON.stringify(command, undefined, 2)}`);
|
101
|
+
}
|
102
|
+
if (command.debug) {
|
103
|
+
console.log(`Environment:`);
|
104
|
+
Object.keys(process.env).forEach(k => {
|
105
|
+
console.log(`${k}: ${process.env[k]}`);
|
106
|
+
});
|
107
|
+
process.exit(0);
|
108
|
+
return;
|
109
|
+
}
|
110
|
+
if (command.type === "init") {
|
111
|
+
let info = projectInfo(process.cwd()) ?? {
|
112
|
+
projectName: "<MyApp>",
|
113
|
+
slnDir: "/path/to/.sln/folder",
|
114
|
+
hostDir: "/path/to/MyApp",
|
115
|
+
migrationsDir: "/path/to/MyApp/Migrations",
|
116
|
+
serviceModelDir: "/path/to/MyApp.ServiceModel",
|
117
|
+
serviceInterfaceDir: "/path/to/MyApp.ServiceInterfaces",
|
118
|
+
};
|
24
119
|
fs.writeFileSync('okai.json', JSON.stringify(info, undefined, 2));
|
25
120
|
process.exit(0);
|
121
|
+
return;
|
26
122
|
}
|
27
|
-
if (
|
123
|
+
if (command.type === "help" || command.unknown?.length) {
|
124
|
+
const exitCode = command.unknown?.length ? 1 : 0;
|
125
|
+
if (command.unknown?.length) {
|
126
|
+
console.log(`Unknown Command: ${command.script} ${command.unknown.join(' ')}\n`);
|
127
|
+
}
|
28
128
|
console.log(`Usage:
|
29
|
-
|
30
|
-
|
129
|
+
${script} <prompt> Generate new TypeScript Data Models, C# APIs and Migrations from prompt
|
130
|
+
-m, -models <model,> Specify up to 5 LLM models to generate .d.ts Data Models
|
131
|
+
-l, -license <LC-xxx> Specify valid license certificate or key to use premium models
|
132
|
+
|
133
|
+
${script} <models>.d.ts Regenerate C# *.cs files for Data Models defined in the TypeScript .d.ts file
|
134
|
+
-w, -watch Watch for changes to <models>.d.ts and regenerate *.cs on save
|
135
|
+
|
136
|
+
${script} rm <models>.d.ts Remove <models>.d.ts and its generated *.cs files
|
137
|
+
${script} ls models Display list of available premium LLM models
|
138
|
+
${script} init Initialize okai.json with project info to override default paths
|
139
|
+
${script} info Display current project info
|
140
|
+
|
141
|
+
Options:
|
142
|
+
-v, -verbose Display verbose logging
|
143
|
+
--ignore-ssl-errors Ignore SSL Errors`);
|
144
|
+
process.exit(exitCode);
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
const info = command.info = projectInfo(process.cwd());
|
148
|
+
if (!info) {
|
149
|
+
if (!info) {
|
150
|
+
console.log(`No .sln file found`);
|
151
|
+
console.log(`okai needs to be run within a ServiceStack App that contains a ServiceModel project`);
|
152
|
+
console.log(`To use with an external or custom project, create an okai.json config file with:`);
|
153
|
+
console.log(`$ ${script} init`);
|
154
|
+
process.exit(1);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
if (command.type === "info") {
|
158
|
+
try {
|
159
|
+
console.log(JSON.stringify(command.info, undefined, 2));
|
160
|
+
}
|
161
|
+
catch (err) {
|
162
|
+
console.error(err.message ?? `${err}`);
|
163
|
+
}
|
31
164
|
process.exit(0);
|
32
165
|
return;
|
33
166
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
167
|
+
if (command.ignoreSsl) {
|
168
|
+
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
|
169
|
+
}
|
170
|
+
if (command.type === "list") {
|
171
|
+
if (command.list == "models") {
|
172
|
+
const url = new URL('/models/list', command.baseUrl);
|
173
|
+
if (command.verbose)
|
174
|
+
console.log(`GET: ${url}`);
|
175
|
+
const res = await fetch(url);
|
176
|
+
if (!res.ok) {
|
177
|
+
console.log(`Failed to fetch models: ${res.statusText}`);
|
178
|
+
process.exit(1);
|
179
|
+
}
|
180
|
+
const models = await res.text();
|
181
|
+
console.log(models);
|
182
|
+
process.exit(0);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
function assertTsdPath(tsdFile) {
|
186
|
+
const tryPaths = [
|
187
|
+
path.join(process.cwd(), tsdFile),
|
188
|
+
];
|
189
|
+
if (info?.serviceModelDir) {
|
190
|
+
tryPaths.push(path.join(info.serviceModelDir, tsdFile));
|
191
|
+
}
|
192
|
+
const tsdPath = tryPaths.find(fs.existsSync);
|
193
|
+
if (!tsdPath) {
|
194
|
+
console.log(`Could not find: ${command.tsdFile}, tried:\n${tryPaths.join('\n')}`);
|
195
|
+
process.exit(1);
|
196
|
+
}
|
197
|
+
return tsdPath;
|
198
|
+
}
|
199
|
+
function resolveMigrationFile(migrationPath) {
|
200
|
+
return migrationPath.startsWith('~/')
|
201
|
+
? path.join(info.slnDir, trimStart(migrationPath, '~/'))
|
202
|
+
: path.join(process.cwd(), migrationPath);
|
203
|
+
}
|
204
|
+
function resolveApiFile(apiPath) {
|
205
|
+
return apiPath.startsWith('~/')
|
206
|
+
? path.join(info.slnDir, trimStart(apiPath, '~/'))
|
207
|
+
: path.join(process.cwd(), apiPath);
|
208
|
+
}
|
209
|
+
if (command.type === "update") {
|
210
|
+
let tsdPath = assertTsdPath(command.tsdFile);
|
211
|
+
if (command.verbose)
|
212
|
+
console.log(`Updating: ${tsdPath}...`);
|
213
|
+
let tsdContent = fs.readFileSync(tsdPath, 'utf-8');
|
214
|
+
const header = parseTsdHeader(tsdContent);
|
215
|
+
if (command.verbose)
|
216
|
+
console.log(JSON.stringify(header, undefined, 2));
|
217
|
+
function regenerate(header, tsdContent, logPrefix = '') {
|
218
|
+
const tsdAst = toAst(tsdContent);
|
219
|
+
const tsdGenerator = new TsdDataModelGenerator();
|
220
|
+
tsdContent = tsdGenerator.generate(tsdAst);
|
221
|
+
const csAst = toMetadataTypes(tsdAst);
|
222
|
+
// const groupName = path.basename(command.tsdFile, '.d.ts')
|
223
|
+
// console.log('groupName', groupName)
|
224
|
+
const genApis = new CSharpApiGenerator();
|
225
|
+
const csApiFiles = genApis.generate(csAst);
|
226
|
+
const apiContent = replaceMyApp(csApiFiles[Object.keys(csApiFiles)[0]], info.projectName);
|
227
|
+
const apiPath = resolveApiFile(header.api);
|
228
|
+
console.log(`${logPrefix}${apiPath}`);
|
229
|
+
fs.writeFileSync(apiPath, apiContent, { encoding: 'utf-8' });
|
230
|
+
if (header?.migration) {
|
231
|
+
const migrationCls = leftPart(path.basename(header.migration), '.');
|
232
|
+
const getMigrations = new CSharpMigrationGenerator();
|
233
|
+
const csMigrationFiles = getMigrations.generate(csAst);
|
234
|
+
const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
|
235
|
+
const migrationPath = resolveApiFile(header.migration);
|
236
|
+
console.log(`${logPrefix}${migrationPath}`);
|
237
|
+
fs.writeFileSync(migrationPath, migrationContent, { encoding: 'utf-8' });
|
238
|
+
}
|
239
|
+
console.log(`${logPrefix}${tsdPath}`);
|
240
|
+
const newTsdContent = toTsdHeader(header) + '\n\n' + tsdContent;
|
241
|
+
fs.writeFileSync(tsdPath, newTsdContent, { encoding: 'utf-8' });
|
242
|
+
return newTsdContent;
|
243
|
+
}
|
244
|
+
if (command.watch) {
|
245
|
+
let lastTsdContent = tsdContent;
|
246
|
+
console.log(`watching ${tsdPath} ...`);
|
247
|
+
let i = 0;
|
248
|
+
fs.watchFile(tsdPath, { interval: 100 }, (curr, prev) => {
|
249
|
+
let tsdContent = fs.readFileSync(tsdPath, 'utf-8');
|
250
|
+
if (tsdContent == lastTsdContent) {
|
251
|
+
if (command.verbose)
|
252
|
+
console.log(`No change detected`);
|
253
|
+
return;
|
254
|
+
}
|
255
|
+
console.log(`\n${++i}. ${leftPart(new Date().toTimeString(), ' ')} regenerating files:`);
|
256
|
+
lastTsdContent = regenerate(header, tsdContent);
|
257
|
+
});
|
258
|
+
return;
|
259
|
+
}
|
260
|
+
else {
|
261
|
+
regenerate(header, tsdContent, 'saved: ');
|
262
|
+
console.log(`\nLast migration can be rerun with 'npm run rerun:last' or:`);
|
263
|
+
console.log(`$ dotnet run --AppTasks=migrate.rerun:last`);
|
264
|
+
process.exit(0);
|
265
|
+
}
|
266
|
+
}
|
267
|
+
if (command.type === 'remove') {
|
268
|
+
let tsdPath = assertTsdPath(command.tsdFile);
|
269
|
+
if (command.verbose)
|
270
|
+
console.log(`Removing: ${tsdPath}...`);
|
271
|
+
const tsdContent = fs.readFileSync(tsdPath, 'utf-8');
|
272
|
+
const header = parseTsdHeader(tsdContent);
|
273
|
+
if (command.verbose)
|
274
|
+
console.log(JSON.stringify(header, undefined, 2));
|
275
|
+
if (header?.migration) {
|
276
|
+
const migrationPath = resolveMigrationFile(header.migration);
|
277
|
+
if (fs.existsSync(migrationPath)) {
|
278
|
+
fs.unlinkSync(migrationPath);
|
279
|
+
console.log(`Removed: ${migrationPath}`);
|
280
|
+
}
|
281
|
+
else {
|
282
|
+
console.log(`Migration .cs file not found: ${migrationPath}`);
|
283
|
+
}
|
284
|
+
}
|
285
|
+
if (header?.api) {
|
286
|
+
const apiPath = resolveApiFile(header.api);
|
287
|
+
if (fs.existsSync(apiPath)) {
|
288
|
+
fs.unlinkSync(apiPath);
|
289
|
+
console.log(`Removed: ${apiPath}`);
|
290
|
+
}
|
291
|
+
else {
|
292
|
+
console.log(`APIs .cs file not found: ${apiPath}`);
|
293
|
+
}
|
294
|
+
}
|
295
|
+
fs.unlinkSync(tsdPath);
|
296
|
+
console.log(`Removed: ${tsdPath}`);
|
297
|
+
process.exit(0);
|
298
|
+
}
|
299
|
+
if (command.type === 'prompt') {
|
300
|
+
try {
|
301
|
+
if (!info.serviceModelDir)
|
302
|
+
throw new Error("Could not find ServiceModel directory");
|
303
|
+
console.log(`Generating new APIs and Tables for: ${command.prompt}...`);
|
304
|
+
const gist = await fetchGistFiles(command);
|
305
|
+
// const projectGist = convertToProjectGist(info, gist)
|
306
|
+
const ctx = await createGistPreview(command.prompt, gist);
|
307
|
+
ctx.screen.key('a', () => chooseFile(ctx, info, gist));
|
308
|
+
// ctx.screen.key('a', () => applyCSharpGist(ctx, info, gist, { accept: true }))
|
309
|
+
// ctx.screen.key('d', () => applyCSharpGist(ctx, info, gist, { discard: true }))
|
310
|
+
// ctx.screen.key('S-a', () => applyCSharpGist(ctx, info, gist, { acceptAll: true }))
|
311
|
+
ctx.screen.render();
|
312
|
+
}
|
313
|
+
catch (err) {
|
314
|
+
console.error(err);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
else {
|
318
|
+
console.log(`Unknown command: ${command.type}`);
|
319
|
+
process.exit(1);
|
50
320
|
}
|
51
321
|
}
|
52
|
-
async function fetchGistFiles(
|
53
|
-
const url = new URL('/gist', baseUrl);
|
322
|
+
async function fetchGistFiles(command) {
|
323
|
+
const url = new URL('/models/gist', command.baseUrl);
|
54
324
|
if (process.env.OKAI_CACHED) {
|
55
325
|
url.searchParams.append('cached', `1`);
|
56
326
|
}
|
57
|
-
url.searchParams.append('
|
327
|
+
url.searchParams.append('prompt', command.prompt);
|
328
|
+
if (command.models) {
|
329
|
+
url.searchParams.append('models', command.models);
|
330
|
+
if (command.license) {
|
331
|
+
url.searchParams.append('license', command.license);
|
332
|
+
}
|
333
|
+
}
|
334
|
+
if (command.verbose)
|
335
|
+
console.log(`GET: ${url}`);
|
58
336
|
const res = await fetch(url);
|
59
337
|
if (!res.ok) {
|
60
|
-
|
338
|
+
try {
|
339
|
+
const errorResponse = await res.json();
|
340
|
+
console.error(errorResponse?.responseStatus?.message ?? errorResponse.message ?? errorResponse);
|
341
|
+
}
|
342
|
+
catch (err) {
|
343
|
+
console.log(`Failed to generate data models: ${res.statusText}`);
|
344
|
+
}
|
345
|
+
process.exit(1);
|
61
346
|
}
|
62
347
|
const gist = await res.json();
|
63
348
|
const files = gist.files;
|
@@ -66,33 +351,44 @@ async function fetchGistFiles(baseUrl, text) {
|
|
66
351
|
}
|
67
352
|
return gist;
|
68
353
|
}
|
354
|
+
// When writing to disk, replace MyApp with the project name
|
69
355
|
function convertToProjectGist(info, gist) {
|
70
356
|
const to = Object.assign({}, gist, { files: {} });
|
71
357
|
const cwd = process.cwd();
|
72
|
-
for (const [
|
73
|
-
|
74
|
-
|
358
|
+
for (const [displayName, file] of Object.entries(gist.files)) {
|
359
|
+
const writeFileName = file.filename;
|
360
|
+
const type = `text/csharp`;
|
361
|
+
const content = replaceMyApp(file.content, info.projectName);
|
362
|
+
const size = content.length;
|
363
|
+
if (writeFileName.startsWith('MyApp.ServiceModel/') && info.serviceModelDir) {
|
364
|
+
const fullPath = path.join(info.serviceModelDir, writeFileName.substring('MyApp.ServiceModel/'.length));
|
75
365
|
const relativePath = path.relative(cwd, fullPath);
|
76
366
|
to.files[relativePath] = {
|
77
367
|
filename: path.basename(fullPath),
|
78
|
-
content
|
368
|
+
content,
|
369
|
+
type,
|
370
|
+
size,
|
79
371
|
};
|
80
372
|
}
|
81
|
-
else if (
|
82
|
-
const fullPath = path.join(info.migrationsDir,
|
373
|
+
else if (writeFileName.startsWith('MyApp/Migrations/') && info.migrationsDir) {
|
374
|
+
const fullPath = path.join(info.migrationsDir, writeFileName.substring('MyApp.Migrations/'.length));
|
83
375
|
const relativePath = path.relative(cwd, fullPath);
|
84
376
|
to.files[relativePath] = Object.assign({}, file, {
|
85
377
|
filename: path.basename(fullPath),
|
86
|
-
content
|
378
|
+
content,
|
379
|
+
type,
|
380
|
+
size,
|
87
381
|
});
|
88
382
|
}
|
89
383
|
else {
|
90
|
-
const fullPath = path.join(info.slnDir,
|
384
|
+
const fullPath = path.join(info.slnDir, writeFileName);
|
91
385
|
const relativePath = path.relative(cwd, fullPath);
|
92
386
|
const toFilename = replaceMyApp(relativePath, info.projectName);
|
93
387
|
to.files[relativePath] = Object.assign({}, file, {
|
94
388
|
filename: path.basename(toFilename),
|
95
|
-
content
|
389
|
+
content,
|
390
|
+
type,
|
391
|
+
size,
|
96
392
|
});
|
97
393
|
}
|
98
394
|
}
|
@@ -159,7 +455,7 @@ async function createGistPreview(title, gist) {
|
|
159
455
|
left: 0,
|
160
456
|
width: '100%',
|
161
457
|
height: 1,
|
162
|
-
content: 'Press (a) accept (
|
458
|
+
content: 'Press (a) accept (q) quit',
|
163
459
|
style: {
|
164
460
|
fg: 'white',
|
165
461
|
bg: 'blue'
|
@@ -190,6 +486,58 @@ async function createGistPreview(title, gist) {
|
|
190
486
|
// Render screen
|
191
487
|
return { screen, titleBar, fileList, preview, statusBar, result };
|
192
488
|
}
|
489
|
+
function chooseFile(ctx, info, gist) {
|
490
|
+
const { screen, titleBar, fileList, preview, statusBar, result } = ctx;
|
491
|
+
const file = gist.files[result.selectedFile];
|
492
|
+
screen.destroy();
|
493
|
+
const tsd = file.content;
|
494
|
+
const tsdAst = toAst(tsd);
|
495
|
+
const csAst = toMetadataTypes(tsdAst);
|
496
|
+
const groupName = getGroupName(csAst);
|
497
|
+
const genApis = new CSharpApiGenerator();
|
498
|
+
const csApiFiles = genApis.generate(csAst);
|
499
|
+
const getMigrations = new CSharpMigrationGenerator();
|
500
|
+
const csMigrationFiles = getMigrations.generate(csAst);
|
501
|
+
const relativeServiceModelDir = trimStart(info.serviceModelDir.substring(info.slnDir.length), '~/');
|
502
|
+
const relativeMigrationDir = trimStart(info.migrationsDir.substring(info.slnDir.length), '~/');
|
503
|
+
const apiFileName = `${groupName}.cs`;
|
504
|
+
const apiContent = replaceMyApp(csApiFiles[Object.keys(csApiFiles)[0]], info.projectName);
|
505
|
+
const migrationPath = resolveMigrationFile(path.join(info.migrationsDir, `Migration1000.cs`));
|
506
|
+
const migrationFileName = path.basename(migrationPath);
|
507
|
+
const migrationCls = leftPart(migrationFileName, '.');
|
508
|
+
const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
|
509
|
+
const sb = [];
|
510
|
+
sb.push(`/*prompt: ${titleBar.content.replaceAll('/*', '').replaceAll('*/', '')}`);
|
511
|
+
sb.push(`api: ~/${path.join(relativeServiceModelDir, apiFileName)}`);
|
512
|
+
sb.push(`migration: ~/${path.join(relativeMigrationDir, migrationFileName)}`);
|
513
|
+
sb.push(`*/`);
|
514
|
+
sb.push('');
|
515
|
+
sb.push(tsd);
|
516
|
+
const tsdContent = sb.join('\n');
|
517
|
+
const tsdFileName = `${groupName}.d.ts`;
|
518
|
+
const fullTsdPath = path.join(info.slnDir, relativeServiceModelDir, tsdFileName);
|
519
|
+
const fullApiPath = path.join(info.slnDir, relativeServiceModelDir, apiFileName);
|
520
|
+
const fullMigrationPath = path.join(info.slnDir, relativeMigrationDir, migrationFileName);
|
521
|
+
if (!fs.existsSync(path.dirname(fullTsdPath))) {
|
522
|
+
console.log(`Directory does not exist: ${path.dirname(fullTsdPath)}`);
|
523
|
+
process.exit(0);
|
524
|
+
}
|
525
|
+
console.log(`\nSelected '${result.selectedFile}' data models`);
|
526
|
+
fs.writeFileSync(fullTsdPath, tsdContent, { encoding: 'utf-8' });
|
527
|
+
console.log(`\nSaved: ${fullTsdPath}`);
|
528
|
+
if (fs.existsSync(path.dirname(fullApiPath))) {
|
529
|
+
fs.writeFileSync(fullApiPath, apiContent, { encoding: 'utf-8' });
|
530
|
+
console.log(`Saved: ${fullApiPath}`);
|
531
|
+
}
|
532
|
+
if (fs.existsSync(path.dirname(fullMigrationPath))) {
|
533
|
+
fs.writeFileSync(fullMigrationPath, migrationContent, { encoding: 'utf-8' });
|
534
|
+
console.log(`Saved: ${fullMigrationPath}`);
|
535
|
+
}
|
536
|
+
const script = path.basename(process.argv[1]);
|
537
|
+
console.log(`\nTo regenerate classes, update '${tsdFileName}' then run:`);
|
538
|
+
console.log(`$ ${script} ${tsdFileName}`);
|
539
|
+
process.exit(0);
|
540
|
+
}
|
193
541
|
function writeFile(info, filename, content) {
|
194
542
|
let fullPath = path.join(process.cwd(), filename);
|
195
543
|
const dir = path.dirname(fullPath);
|
@@ -217,6 +565,25 @@ function writeFile(info, filename, content) {
|
|
217
565
|
}
|
218
566
|
fs.writeFileSync(fullPath, content);
|
219
567
|
}
|
568
|
+
function resolveMigrationFile(fullPath) {
|
569
|
+
const dir = path.dirname(fullPath);
|
570
|
+
const filename = path.basename(fullPath);
|
571
|
+
const ext = path.extname(filename);
|
572
|
+
const baseName = path.basename(filename, ext);
|
573
|
+
// filename: Migration1000.cs, baseName: Migration1000, ext: .cs
|
574
|
+
// console.log(`File already exists: ${fullPath}`, { filename, baseName, ext })
|
575
|
+
const numberedFile = baseName.match(/(\d+)$/);
|
576
|
+
if (numberedFile) {
|
577
|
+
let nextNumber = parseInt(numberedFile[1]);
|
578
|
+
while (fs.existsSync(fullPath)) {
|
579
|
+
if (numberedFile) {
|
580
|
+
nextNumber += 1;
|
581
|
+
fullPath = path.join(dir, `${baseName.replace(/\d+$/, '')}${nextNumber}${ext}`);
|
582
|
+
}
|
583
|
+
}
|
584
|
+
}
|
585
|
+
return fullPath;
|
586
|
+
}
|
220
587
|
function exit(screen, info, gist) {
|
221
588
|
screen.destroy();
|
222
589
|
if (info.migrationsDir) {
|
@@ -224,7 +591,7 @@ function exit(screen, info, gist) {
|
|
224
591
|
}
|
225
592
|
process.exit(0);
|
226
593
|
}
|
227
|
-
function
|
594
|
+
function applyCSharpGist(ctx, info, gist, { accept = false, acceptAll = false, discard = false }) {
|
228
595
|
const { screen, titleBar, fileList, preview, statusBar, result } = ctx;
|
229
596
|
function removeSelected() {
|
230
597
|
delete gist.files[result.selectedFile];
|
package/dist/info.js
CHANGED
package/dist/okai.js
CHANGED