casualos 3.5.3-alpha.16326443512 ā 3.5.3
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/cli.js +473 -39
- package/cli.js.map +1 -1
- package/dist/cli.js +637 -88
- package/package.json +4 -4
package/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ import repl from 'node:repl';
|
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
25
25
|
// @ts-ignore
|
|
26
26
|
import Conf from 'conf';
|
|
27
|
-
import { getBotsStateFromStoredAux, getSessionKeyExpiration, isExpired, parseSessionKey, willExpire, } from '@casual-simulation/aux-common';
|
|
27
|
+
import { calculateStringTagValue, createBot, DATE_TAG_PREFIX, DNA_TAG_PREFIX, getBotsStateFromStoredAux, getSessionKeyExpiration, getUploadState, hasValue, isExpired, LIBRARY_SCRIPT_PREFIX, merge, NUMBER_TAG_PREFIX, parseSessionKey, ROTATION_TAG_PREFIX, STRING_TAG_PREFIX, VECTOR_TAG_PREFIX, willExpire, } from '@casual-simulation/aux-common';
|
|
28
28
|
import { serverConfigSchema } from '@casual-simulation/aux-records';
|
|
29
29
|
import { PassThrough } from 'node:stream';
|
|
30
30
|
import { getSchemaMetadata } from '@casual-simulation/aux-common';
|
|
@@ -32,8 +32,10 @@ import path from 'path';
|
|
|
32
32
|
import { readFile } from 'fs/promises';
|
|
33
33
|
import { setupInfraCommands } from 'infra';
|
|
34
34
|
import { z } from 'zod';
|
|
35
|
-
import { existsSync,
|
|
36
|
-
import { writeFile } from 'node:fs/promises';
|
|
35
|
+
import { existsSync, statSync } from 'node:fs';
|
|
36
|
+
import { mkdir, readdir, stat, writeFile } from 'node:fs/promises';
|
|
37
|
+
import { v4 as uuid } from 'uuid';
|
|
38
|
+
import fastJsonStableStringify from '../fast-json-stable-stringify';
|
|
37
39
|
const REFRESH_LIFETIME_MS = 1000 * 60 * 60 * 24 * 7; // 1 week
|
|
38
40
|
const config = new Conf({
|
|
39
41
|
projectName: 'casualos-cli',
|
|
@@ -195,6 +197,53 @@ program
|
|
|
195
197
|
break;
|
|
196
198
|
}
|
|
197
199
|
});
|
|
200
|
+
program
|
|
201
|
+
.command('unpack-aux')
|
|
202
|
+
.argument('[input]', 'The aux file/directory to convert to a file system.')
|
|
203
|
+
.argument('[dir]', 'The directory to write the file system to.')
|
|
204
|
+
.option('-o, --overwrite', 'Overwrite existing files.')
|
|
205
|
+
.option('-r, --recursive', 'Recursively convert aux files in a directory.')
|
|
206
|
+
.option('--write-systemless-bots', "Write bots that don't have a system tag. By default, these bots are written to the extra.aux file.")
|
|
207
|
+
.option('--omit-extra-bots', 'Prevent writing extra.aux files.')
|
|
208
|
+
.description('Generate a folder from an AUX file.')
|
|
209
|
+
.action(async (input, dir, options) => {
|
|
210
|
+
if (options.overwrite) {
|
|
211
|
+
console.log('Overwriting existing files.');
|
|
212
|
+
}
|
|
213
|
+
if (options.recursive) {
|
|
214
|
+
console.log('Recursively converting aux files in input directory.');
|
|
215
|
+
}
|
|
216
|
+
if (options.writeSystemlessBots) {
|
|
217
|
+
console.log('Writing systemless bots. All bots will be written to the file system.');
|
|
218
|
+
}
|
|
219
|
+
if (options.omitExtraBots) {
|
|
220
|
+
console.log('Omitting extra bots. No extra.aux file(s) will be written.');
|
|
221
|
+
}
|
|
222
|
+
await auxGenFs(input, dir, options);
|
|
223
|
+
});
|
|
224
|
+
program
|
|
225
|
+
.command('pack-aux')
|
|
226
|
+
.argument('[dir]', 'The directory to read the file system from. If the directory does not contain an extra.aux file, then each directory will be read as a separate aux file.')
|
|
227
|
+
.argument('[output]', 'The output file to write the aux file to. This should be the folder that each aux should be written to if the input directory contains multiple aux filesystems.')
|
|
228
|
+
.option('-o, --overwrite', 'Overwrite existing files.')
|
|
229
|
+
.option('-f, --filter', 'The bot filter to apply to the bots being read.')
|
|
230
|
+
.option('--allow-duplicates', 'Whether to allow duplicate bots. If a duplicate is encoutered, then a new bot ID will be generated for the duplicate.')
|
|
231
|
+
.description('Generate an AUX file from a folder.')
|
|
232
|
+
.action(async (dir, output, options) => {
|
|
233
|
+
if (options.overwrite) {
|
|
234
|
+
console.log('Overwriting existing files.');
|
|
235
|
+
}
|
|
236
|
+
if (options.merge) {
|
|
237
|
+
console.log('Merging output AUX file.');
|
|
238
|
+
}
|
|
239
|
+
if (options.recursive) {
|
|
240
|
+
console.log('Recursively reading aux files in directory.');
|
|
241
|
+
}
|
|
242
|
+
if (options.allowDuplicates) {
|
|
243
|
+
console.log('Allowing duplicate bots.');
|
|
244
|
+
}
|
|
245
|
+
await auxReadFs(dir, output, options);
|
|
246
|
+
});
|
|
198
247
|
program
|
|
199
248
|
.command('generate-server-config')
|
|
200
249
|
.option('-p, --pretty', 'Pretty print the output.')
|
|
@@ -241,59 +290,444 @@ program
|
|
|
241
290
|
}
|
|
242
291
|
});
|
|
243
292
|
setupInfraCommands(program.command('infra'), config);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Validates the given file is a proper aux file.
|
|
295
|
+
* This function checks if the file exists, is a file, and has the correct extension.
|
|
296
|
+
* If the file is valid, it returns the parsed bot state from the aux file.
|
|
297
|
+
* @param filePath The path to the file whose to be validated.
|
|
298
|
+
* @param opts Optional options to skip parsing or contents validation.
|
|
299
|
+
*/
|
|
300
|
+
async function loadAuxFile(filePath) {
|
|
301
|
+
const targetStat = await stat(filePath);
|
|
302
|
+
if (!targetStat.isFile()) {
|
|
303
|
+
return { success: false, error: 'Path is not a file.' };
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const contents = JSON.parse(await readFile(filePath, { encoding: 'utf-8' }));
|
|
307
|
+
const botsState = getBotsStateFromStoredAux(contents);
|
|
308
|
+
if (!botsState) {
|
|
309
|
+
return {
|
|
310
|
+
success: false,
|
|
311
|
+
error: `Aux file at ${filePath} is not a valid (or supported) aux file.`,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return { success: true, botsState };
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
return {
|
|
318
|
+
success: false,
|
|
319
|
+
error: `Could not read or parse aux file at ${filePath}.\n\n${err}`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async function requestFiles(opts) {
|
|
324
|
+
opts = {
|
|
325
|
+
query: 'target file or directory containing files (path)',
|
|
326
|
+
allowedExtensions: new Set(['.aux']),
|
|
327
|
+
...opts,
|
|
328
|
+
};
|
|
329
|
+
const targetFD = sanitizePath(await askForInputs(getSchemaMetadata(z.string().min(1)), opts.query));
|
|
330
|
+
if (!existsSync(targetFD))
|
|
331
|
+
return { directory: null, files: [] };
|
|
332
|
+
const targetStat = statSync(targetFD);
|
|
333
|
+
const files = [];
|
|
334
|
+
if (targetStat.isDirectory()) {
|
|
335
|
+
for (let file of await readdir(targetFD)) {
|
|
336
|
+
if (opts.allowedExtensions.has(path.extname(file).toLowerCase())) {
|
|
337
|
+
files.push(file);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else if (targetStat.isFile()) {
|
|
342
|
+
if (opts.allowedExtensions.has(path.extname(targetFD).toLowerCase())) {
|
|
343
|
+
files.push(path.basename(targetFD));
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
console.warn(`Invalid file type provided.\nExpected one of ${Array.from(opts.allowedExtensions).join(' | ')}.\nGot: ${path.extname(targetFD).toLowerCase()}`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
console.error('Unknown item at path.');
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
return { directory: getDir(targetFD), files };
|
|
355
|
+
}
|
|
356
|
+
async function requestOutputDirectory(query = 'output directory to write files to') {
|
|
357
|
+
const outDir = sanitizePath(await askForInputs(getSchemaMetadata(z.string().min(1)), query));
|
|
358
|
+
if (existsSync(outDir) && statSync(outDir).isDirectory())
|
|
359
|
+
return outDir;
|
|
360
|
+
console.error(`Directory does not exist or is not a directory.`);
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const fileTagPrefixes = [
|
|
364
|
+
['@', '.tsx'],
|
|
365
|
+
[LIBRARY_SCRIPT_PREFIX, '.tsm'],
|
|
366
|
+
[DNA_TAG_PREFIX, '.json'],
|
|
367
|
+
[DATE_TAG_PREFIX, '.date.text'],
|
|
368
|
+
[STRING_TAG_PREFIX, '.text'],
|
|
369
|
+
[NUMBER_TAG_PREFIX, '.number.text'],
|
|
370
|
+
[VECTOR_TAG_PREFIX, '.vector.text'],
|
|
371
|
+
[ROTATION_TAG_PREFIX, '.rotation.text'],
|
|
372
|
+
];
|
|
373
|
+
const fileExtensions = [
|
|
374
|
+
['.tsx', '@'],
|
|
375
|
+
['.tsm', LIBRARY_SCRIPT_PREFIX],
|
|
376
|
+
['.json', DNA_TAG_PREFIX],
|
|
377
|
+
['.date.text', DATE_TAG_PREFIX],
|
|
378
|
+
['.txt', ''],
|
|
379
|
+
['.text', STRING_TAG_PREFIX],
|
|
380
|
+
['.number.text', NUMBER_TAG_PREFIX],
|
|
381
|
+
['.vector.text', VECTOR_TAG_PREFIX],
|
|
382
|
+
['.rotation.text', ROTATION_TAG_PREFIX],
|
|
383
|
+
];
|
|
384
|
+
async function auxGenFs(input, output, options) {
|
|
385
|
+
var _a;
|
|
386
|
+
if (!input) {
|
|
387
|
+
input = await askForInputs(getSchemaMetadata(z.string().min(1)), 'The path to the AUX file to convert to a file system');
|
|
388
|
+
}
|
|
389
|
+
input = path.resolve(input);
|
|
390
|
+
if (!existsSync(input)) {
|
|
391
|
+
throw new Error(`The provided path does not exist: ${input}`);
|
|
392
|
+
}
|
|
393
|
+
let files = [];
|
|
394
|
+
let extraDirectories = [];
|
|
395
|
+
const inputStat = await stat(input);
|
|
396
|
+
if (inputStat.isDirectory()) {
|
|
397
|
+
const paths = await readdir(input);
|
|
398
|
+
for (let fileOrFolder of paths) {
|
|
399
|
+
const fileOrFolderPath = path.resolve(input, fileOrFolder);
|
|
400
|
+
const stats = await stat(fileOrFolderPath);
|
|
401
|
+
if (stats.isFile() && fileOrFolder.endsWith('.aux')) {
|
|
402
|
+
files.push(fileOrFolderPath);
|
|
403
|
+
}
|
|
404
|
+
else if (options.recursive &&
|
|
405
|
+
!fileOrFolder.startsWith('.') &&
|
|
406
|
+
stats.isDirectory()) {
|
|
407
|
+
extraDirectories.push(fileOrFolderPath);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
files.push(input);
|
|
413
|
+
}
|
|
414
|
+
if (!output) {
|
|
415
|
+
output = await askForInputs(getSchemaMetadata(z.string().min(1)), 'The directory to write the file system to');
|
|
416
|
+
}
|
|
417
|
+
output = path.resolve(output);
|
|
418
|
+
// make the directory if it doesn't exist
|
|
419
|
+
await mkdir(output, {
|
|
420
|
+
recursive: true,
|
|
421
|
+
});
|
|
422
|
+
const flag = options.overwrite ? 'w' : 'wx';
|
|
423
|
+
for (const file of files) {
|
|
424
|
+
const fileData = await loadAuxFile(file);
|
|
425
|
+
if (!fileData.success) {
|
|
426
|
+
throw new Error(`Invalid aux file: ${file}.\n\n${fileData.error}`);
|
|
427
|
+
}
|
|
428
|
+
const auxName = path.parse(file).name;
|
|
429
|
+
const botsState = fileData.botsState;
|
|
430
|
+
const extraBotsState = {};
|
|
431
|
+
for (let id in botsState) {
|
|
432
|
+
const bot = botsState[id];
|
|
433
|
+
if (!options.writeSystemlessBots && !hasValue(bot.tags.system)) {
|
|
434
|
+
console.warn(`Adding ${id} to extra.aux.`);
|
|
435
|
+
extraBotsState[id] = bot;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const system = (_a = bot.tags.system) !== null && _a !== void 0 ? _a : id;
|
|
439
|
+
const dirName = system.replace(/\./g, path.sep);
|
|
440
|
+
const dir = path.resolve(output, auxName, dirName);
|
|
441
|
+
const botJson = {
|
|
442
|
+
id,
|
|
443
|
+
tags: {},
|
|
444
|
+
};
|
|
445
|
+
if (hasValue(bot.space)) {
|
|
446
|
+
botJson.space = bot.space;
|
|
447
|
+
}
|
|
448
|
+
if (hasValue(bot.tags.system)) {
|
|
449
|
+
botJson.tags.system = bot.tags.system;
|
|
450
|
+
}
|
|
451
|
+
// make the directory if it doesn't exist
|
|
452
|
+
await mkdir(dir, {
|
|
453
|
+
recursive: true,
|
|
454
|
+
});
|
|
455
|
+
// Don't track tag masks
|
|
456
|
+
for (const tag of Object.keys(bot.tags)) {
|
|
457
|
+
const value = calculateStringTagValue(null, bot, tag, null);
|
|
458
|
+
let written = false;
|
|
459
|
+
if (hasValue(value)) {
|
|
460
|
+
for (let [prefix, ext] of fileTagPrefixes) {
|
|
461
|
+
if (value.startsWith(prefix)) {
|
|
462
|
+
// write the tag value to its own file
|
|
463
|
+
const filePath = path.resolve(dir, `${tag}${ext}`);
|
|
464
|
+
const fileContent = value.slice(prefix.length);
|
|
465
|
+
try {
|
|
466
|
+
await writeFile(filePath, fileContent, {
|
|
467
|
+
encoding: 'utf-8',
|
|
468
|
+
flag,
|
|
469
|
+
});
|
|
470
|
+
written = true;
|
|
471
|
+
}
|
|
472
|
+
catch (err) {
|
|
473
|
+
console.error(`Could not write file: ${filePath}.\n\n${err}\n`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (!written && value.indexOf('\n') >= 0) {
|
|
478
|
+
// string has a newline, so write it to a text file
|
|
479
|
+
const filePath = path.resolve(dir, `${tag}.txt`);
|
|
480
|
+
try {
|
|
481
|
+
await writeFile(filePath, value, {
|
|
482
|
+
encoding: 'utf-8',
|
|
483
|
+
flag,
|
|
484
|
+
});
|
|
485
|
+
written = true;
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
console.error(`Could not write file: ${filePath}.\n\n${err}\n`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (!written) {
|
|
493
|
+
botJson.tags[tag] = bot.tags[tag];
|
|
253
494
|
}
|
|
254
495
|
}
|
|
496
|
+
// write the bot.json file
|
|
497
|
+
const botAuxName = `${system}.bot.aux`;
|
|
498
|
+
const botJsonPath = path.resolve(dir, botAuxName);
|
|
499
|
+
try {
|
|
500
|
+
const botAux = {
|
|
501
|
+
version: 1,
|
|
502
|
+
state: {
|
|
503
|
+
[id]: botJson,
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
await writeFile(botJsonPath, fastJsonStableStringify(botAux, {
|
|
507
|
+
space: 2,
|
|
508
|
+
}), {
|
|
509
|
+
encoding: 'utf-8',
|
|
510
|
+
flag,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
catch (err) {
|
|
514
|
+
console.error(`Could not write ${botAuxName} file: ${botJsonPath}.\n\n${err}\n`);
|
|
515
|
+
}
|
|
516
|
+
console.log(`Created: ${system}`);
|
|
517
|
+
}
|
|
518
|
+
// Always write the extra bots file so that we can do the reverse operation
|
|
519
|
+
// and produce the original aux file.
|
|
520
|
+
if (!options.omitExtraBots) {
|
|
521
|
+
// write a aux file for the extra bots to the output directory
|
|
522
|
+
const extraBotsFilePath = path.resolve(output, auxName, `extra.aux`);
|
|
523
|
+
try {
|
|
524
|
+
const aux = {
|
|
525
|
+
version: 1,
|
|
526
|
+
state: extraBotsState,
|
|
527
|
+
};
|
|
528
|
+
await writeFile(extraBotsFilePath, JSON.stringify(aux, null, 2), {
|
|
529
|
+
encoding: 'utf-8',
|
|
530
|
+
flag,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
catch (err) {
|
|
534
|
+
console.error(`Could not write extra.aux file: ${extraBotsFilePath}.\n\n${err}\n`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
for (let extraDir of extraDirectories) {
|
|
539
|
+
// Only allow one level of recursion
|
|
540
|
+
await auxGenFs(extraDir, output, {
|
|
541
|
+
...options,
|
|
542
|
+
recursive: false,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
async function auxReadFs(input, output, options) {
|
|
547
|
+
const { overwrite } = options;
|
|
548
|
+
const failOnDuplicate = !options.allowDuplicates;
|
|
549
|
+
if (!input) {
|
|
550
|
+
input = await askForInputs(getSchemaMetadata(z.string().min(1)), 'The path to the directory to read into an AUX file.');
|
|
551
|
+
}
|
|
552
|
+
input = path.resolve(input);
|
|
553
|
+
if (!existsSync(input)) {
|
|
554
|
+
console.error(`The provided path does not exist: ${input}`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (!output) {
|
|
558
|
+
output = await askForInputs(getSchemaMetadata(z.string().min(1)), 'The path to the output AUX file.');
|
|
559
|
+
}
|
|
560
|
+
output = path.resolve(output);
|
|
561
|
+
let filterFunc = null;
|
|
562
|
+
if (options.filter) {
|
|
563
|
+
filterFunc = Function('$', options.filter);
|
|
564
|
+
}
|
|
565
|
+
const inputFiles = await readdir(input);
|
|
566
|
+
const hasExtra = inputFiles.includes('extra.aux');
|
|
567
|
+
if (!hasExtra) {
|
|
568
|
+
await mkdir(output, {
|
|
569
|
+
recursive: true,
|
|
570
|
+
});
|
|
571
|
+
for (let file of inputFiles) {
|
|
572
|
+
const filePath = path.resolve(input, file);
|
|
573
|
+
const outputPath = path.resolve(output, `${file}.aux`);
|
|
574
|
+
const stats = await stat(filePath);
|
|
575
|
+
if (stats.isDirectory()) {
|
|
576
|
+
await auxReadFs(filePath, outputPath, options);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
console.log('Reading aux files from directory:', input);
|
|
582
|
+
console.log('Output will be written to:', output);
|
|
583
|
+
// folder represents a single aux
|
|
584
|
+
const botsState = await auxReadFsCore(input, filterFunc, failOnDuplicate);
|
|
585
|
+
const storedAux = {
|
|
586
|
+
version: 1,
|
|
587
|
+
state: botsState,
|
|
588
|
+
};
|
|
589
|
+
await writeFile(output, fastJsonStableStringify(storedAux, {
|
|
590
|
+
space: 2,
|
|
591
|
+
}), { encoding: 'utf-8', flag: overwrite ? 'w' : 'wx' });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function readAuxFile(filePath) {
|
|
595
|
+
const targetStat = await stat(filePath);
|
|
596
|
+
if (!targetStat.isFile()) {
|
|
597
|
+
throw new Error(`Path is not a file: ${filePath}`);
|
|
598
|
+
}
|
|
599
|
+
const contents = JSON.parse(await readFile(filePath, { encoding: 'utf-8' }));
|
|
600
|
+
const botsState = getBotsStateFromStoredAux(contents);
|
|
601
|
+
if (!botsState) {
|
|
602
|
+
throw new Error(`Aux file at ${filePath} is not a valid (or supported) aux file.`);
|
|
603
|
+
}
|
|
604
|
+
return getUploadState(botsState);
|
|
605
|
+
}
|
|
606
|
+
async function assignBots(state, added, failOnDuplicate) {
|
|
607
|
+
for (let id in added) {
|
|
608
|
+
const b = added[id];
|
|
609
|
+
if (!hasValue(b)) {
|
|
610
|
+
continue;
|
|
255
611
|
}
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
|
|
612
|
+
if (id in state && hasValue(state[id])) {
|
|
613
|
+
if (failOnDuplicate) {
|
|
614
|
+
throw new Error(`Bot ${id} already exists in the bots state.`);
|
|
259
615
|
}
|
|
260
616
|
else {
|
|
261
|
-
console.warn(`
|
|
262
|
-
|
|
617
|
+
console.warn(`Bot ${id} already exists in the bots state. Generating new ID.`);
|
|
618
|
+
id = uuid();
|
|
619
|
+
b.id = id;
|
|
263
620
|
}
|
|
264
621
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
622
|
+
state[id] = b;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
async function auxReadFsCore(input, filter, failOnDuplicate) {
|
|
626
|
+
const botsState = {};
|
|
627
|
+
console.log('Reading directory:', input);
|
|
628
|
+
const inputFiles = await readdir(input);
|
|
629
|
+
let tags = {};
|
|
630
|
+
let hasBot = false;
|
|
631
|
+
let botId = null;
|
|
632
|
+
let botState = {};
|
|
633
|
+
for (let file of inputFiles) {
|
|
634
|
+
const filePath = path.join(input, file);
|
|
635
|
+
const fileStat = await stat(filePath);
|
|
636
|
+
if (fileStat.isDirectory()) {
|
|
637
|
+
// If the file is a directory, we need to read its contents recursively
|
|
638
|
+
const subState = await auxReadFsCore(filePath, filter, failOnDuplicate);
|
|
639
|
+
assignBots(botsState, subState, failOnDuplicate);
|
|
268
640
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
641
|
+
else {
|
|
642
|
+
if (file.endsWith('.aux')) {
|
|
643
|
+
console.log(`Reading aux: ${file}`);
|
|
644
|
+
const isSystemBotFile = file.endsWith('.bot.aux');
|
|
645
|
+
const auxBotsState = await readAuxFile(filePath);
|
|
646
|
+
// Get the first bot Id from the aux file
|
|
647
|
+
if (isSystemBotFile && !botId) {
|
|
648
|
+
for (let id in auxBotsState) {
|
|
649
|
+
if (hasValue(id)) {
|
|
650
|
+
console.log(`Found bot ID: ${id}`);
|
|
651
|
+
botId = id;
|
|
652
|
+
hasBot = true;
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
279
656
|
}
|
|
280
|
-
|
|
281
|
-
console.
|
|
657
|
+
else if (!isSystemBotFile) {
|
|
658
|
+
console.log('Reading extra aux file.\n\n');
|
|
282
659
|
}
|
|
660
|
+
assignBots(botState, auxBotsState, failOnDuplicate);
|
|
283
661
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
662
|
+
else {
|
|
663
|
+
for (let [ext, prefix] of fileExtensions) {
|
|
664
|
+
if (file.endsWith(ext)) {
|
|
665
|
+
const tagName = file.slice(0, -ext.length);
|
|
666
|
+
console.log(`Reading tag: ${tagName}`);
|
|
667
|
+
// If the file has a known extension, we can read it and add its contents to the bots state
|
|
668
|
+
const fileContents = prefix +
|
|
669
|
+
(await readFile(filePath, { encoding: 'utf-8' }));
|
|
670
|
+
tags[tagName] = fileContents;
|
|
671
|
+
hasBot = true;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (!botId && hasBot) {
|
|
678
|
+
console.warn('No bot ID found for folder:', input);
|
|
679
|
+
console.warn('Generating a random bot ID.');
|
|
680
|
+
botId = uuid();
|
|
681
|
+
}
|
|
682
|
+
if (botId) {
|
|
683
|
+
const existingBot = botState[botId];
|
|
684
|
+
if (existingBot) {
|
|
685
|
+
existingBot.tags = merge(existingBot.tags, tags);
|
|
287
686
|
}
|
|
288
687
|
else {
|
|
289
|
-
|
|
290
|
-
|
|
688
|
+
// If the bot does not exist, we create a new bot with the tags
|
|
689
|
+
botState[botId] = createBot(botId, tags);
|
|
291
690
|
}
|
|
292
691
|
}
|
|
293
|
-
|
|
294
|
-
|
|
692
|
+
if (filter) {
|
|
693
|
+
for (let id in botState) {
|
|
694
|
+
const b = botState[id];
|
|
695
|
+
if (!filter(b)) {
|
|
696
|
+
console.log(`Bot ${id} does not match filter, skipping.`);
|
|
697
|
+
delete botState[id];
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
assignBots(botsState, botState, failOnDuplicate);
|
|
702
|
+
return botsState;
|
|
703
|
+
}
|
|
704
|
+
async function auxConvert() {
|
|
705
|
+
const { directory: targetFD, files: auxFiles } = await requestFiles({
|
|
706
|
+
allowedExtensions: new Set(['.aux']),
|
|
707
|
+
});
|
|
708
|
+
if (auxFiles.length < 1) {
|
|
709
|
+
console.error(`No aux file found at/in the provided path.`);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const outDir = await requestOutputDirectory();
|
|
713
|
+
if (!outDir) {
|
|
714
|
+
console.error(`Invalid output directory provided.`);
|
|
295
715
|
return;
|
|
296
716
|
}
|
|
717
|
+
let converted = 0;
|
|
718
|
+
const prefix = outDir === targetFD ? '_' : '';
|
|
719
|
+
for (let file of auxFiles) {
|
|
720
|
+
try {
|
|
721
|
+
await writeFile(path.join(outDir, `${prefix}${file}`), JSON.stringify(getBotsStateFromStoredAux(JSON.parse(await readFile(replaceWithBasename(targetFD, file), { encoding: 'utf-8' })))));
|
|
722
|
+
converted++;
|
|
723
|
+
}
|
|
724
|
+
catch (err) {
|
|
725
|
+
console.error(`Could not convert: ${file}.\n\n${err}\n`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
console.log(`\nšµ Converted ${converted}/${auxFiles.length} Files.\n--------------------------\n${auxFiles
|
|
729
|
+
.map((f) => `|āļø | ${f}`)
|
|
730
|
+
.join('\n')}\n`);
|
|
297
731
|
}
|
|
298
732
|
async function query(client, procedure, input, shouldConfirm = true, isJavaScriptInput = false, repl = null) {
|
|
299
733
|
const availableOperations = await client.listProcedures({});
|