@fnndsc/chili 3.3.0 → 3.5.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/dist/chefs/chefs.js +12 -11
- package/dist/commands/fs/download.d.ts +19 -4
- package/dist/commands/fs/download.js +75 -65
- package/dist/commands/fs/upload.d.ts +20 -4
- package/dist/commands/fs/upload.js +46 -34
- package/dist/commands/man/doc.js +3 -2
- package/dist/commands/plugins/add.js +35 -34
- package/dist/config/colorConfig.js +2 -1
- package/dist/connect/connectHandler.js +7 -6
- package/dist/context/contextCommand.js +3 -2
- package/dist/controllers/fileController.js +4 -3
- package/dist/controllers/pluginController.js +2 -1
- package/dist/feeds/feedHandler.js +19 -18
- package/dist/filesystem/fileGroupHandler.js +28 -27
- package/dist/filesystem/filesystemHandler.js +13 -11
- package/dist/filesystem/inodeCommand.js +2 -1
- package/dist/handlers/baseGroupHandler.d.ts +10 -1
- package/dist/handlers/baseGroupHandler.js +38 -13
- package/dist/index.js +3 -2
- package/dist/lfs/lfs.js +6 -4
- package/dist/man/man.js +4 -3
- package/dist/man/renderer.js +3 -2
- package/dist/pacs/pacsQueryHandler.js +14 -13
- package/dist/pacs/pacsRetrieveHandler.js +13 -12
- package/dist/path/pathCommand.js +23 -22
- package/dist/plugins/pluginGroupHandler.d.ts +18 -0
- package/dist/plugins/pluginGroupHandler.js +44 -4
- package/dist/plugins/pluginHandler.js +23 -22
- package/dist/run.d.ts +13 -0
- package/dist/run.js +28 -13
- package/dist/screen/output.d.ts +88 -0
- package/dist/screen/output.js +95 -0
- package/dist/screen/screen.d.ts +13 -0
- package/dist/screen/screen.js +53 -8
- package/dist/utils/admin_prompt.js +10 -9
- package/dist/utils/docker.js +10 -9
- package/dist/views/pluginParameters.d.ts +10 -0
- package/dist/views/pluginParameters.js +25 -17
- package/docs/pacs.adoc +3 -3
- package/package.json +1 -1
package/dist/path/pathCommand.js
CHANGED
|
@@ -16,6 +16,7 @@ import open from "open";
|
|
|
16
16
|
import { execFile } from "child_process";
|
|
17
17
|
import os from "os";
|
|
18
18
|
import { files_downloadWithProgress } from "../commands/fs/download.js";
|
|
19
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
19
20
|
/**
|
|
20
21
|
* Creates a summary table for file transfer details.
|
|
21
22
|
*
|
|
@@ -166,7 +167,7 @@ async function mermaid_renderServerSide(mermaidDefinition, outputFile) {
|
|
|
166
167
|
return new Promise((resolve, reject) => {
|
|
167
168
|
execFile("npx", ["mmdc", "-i", inputFile, "-o", outputFile], (error) => {
|
|
168
169
|
if (error) {
|
|
169
|
-
|
|
170
|
+
chiliErrLog(`Mermaid render failed. Ensure @mermaid-js/mermaid-cli is available (npx mmdc): ${error.message}`);
|
|
170
171
|
reject(error);
|
|
171
172
|
return;
|
|
172
173
|
}
|
|
@@ -412,10 +413,10 @@ async function scanResult_render(scanResult, options) {
|
|
|
412
413
|
try {
|
|
413
414
|
const outputFile = path.resolve(options.save);
|
|
414
415
|
const savedFilePath = await mermaid_renderServerSide(mermaidDefinition, outputFile);
|
|
415
|
-
|
|
416
|
+
chiliLog(`Mermaid diagram saved to: ${savedFilePath}`);
|
|
416
417
|
}
|
|
417
418
|
catch (error) {
|
|
418
|
-
|
|
419
|
+
chiliErrLog(`Failed to save Mermaid diagram: ${error}`);
|
|
419
420
|
}
|
|
420
421
|
}
|
|
421
422
|
else {
|
|
@@ -424,16 +425,16 @@ async function scanResult_render(scanResult, options) {
|
|
|
424
425
|
return;
|
|
425
426
|
}
|
|
426
427
|
if (options.tree) {
|
|
427
|
-
|
|
428
|
+
chiliLog(archyTree_create(scanResult.fileInfo));
|
|
428
429
|
return;
|
|
429
430
|
}
|
|
430
431
|
if (!options.silent) {
|
|
431
432
|
for (const file of scanResult.fileInfo) {
|
|
432
433
|
if (file.isLink && !options.follow) {
|
|
433
|
-
|
|
434
|
+
chiliLog(`${file.chrisPath} -> ${file.linkTarget}`);
|
|
434
435
|
}
|
|
435
436
|
else {
|
|
436
|
-
|
|
437
|
+
chiliLog(`${file.chrisPath}`);
|
|
437
438
|
}
|
|
438
439
|
}
|
|
439
440
|
}
|
|
@@ -447,24 +448,24 @@ async function scanResult_render(scanResult, options) {
|
|
|
447
448
|
export async function scan_do(options) {
|
|
448
449
|
const chrisFolder = await chrisContext.current_get(Context.ChRISfolder);
|
|
449
450
|
if (!chrisFolder) {
|
|
450
|
-
|
|
451
|
+
chiliErrLog(chalk.red("No ChRIS folder context set. Use 'folder=' to set a context."));
|
|
451
452
|
return null;
|
|
452
453
|
}
|
|
453
454
|
if (!options.silent) {
|
|
454
|
-
|
|
455
|
+
chiliLog(chalk.cyan(`Scanning for ${options.dirsOnly ? "directories" : "all files"} recursively from ${chrisFolder}`));
|
|
455
456
|
}
|
|
456
457
|
const hostBasePath = options.hostpath || process.cwd();
|
|
457
458
|
const scanResult = await chrisFS_scan(chrisFolder, hostBasePath, options.follow, options.dirsOnly);
|
|
458
459
|
if (!scanResult) {
|
|
459
|
-
|
|
460
|
+
chiliErrLog(chalk.red("Failed to scan ChRIS filesystem."));
|
|
460
461
|
return null;
|
|
461
462
|
}
|
|
462
463
|
const filtered = scanResult_filter(scanResult, options);
|
|
463
464
|
await scanResult_render(filtered, options);
|
|
464
465
|
if (!options.silent) {
|
|
465
|
-
|
|
466
|
+
chiliLog(chalk.green(`Total size: ${bytes_format(filtered.totalSize)}`));
|
|
466
467
|
if (options.filter || options.endsWith) {
|
|
467
|
-
|
|
468
|
+
chiliLog(chalk.cyan(`Filtered results: ${filtered.fileInfo.length} items`));
|
|
468
469
|
}
|
|
469
470
|
}
|
|
470
471
|
return filtered;
|
|
@@ -517,25 +518,25 @@ async function localFS_scan(options) {
|
|
|
517
518
|
await chrisContext.currentContext_update();
|
|
518
519
|
const folder = chrisContext.singleContext.folder;
|
|
519
520
|
if (!folder) {
|
|
520
|
-
|
|
521
|
+
chiliErrLog("ChRIS folder context is undefined, cannot initialize chrisIO.");
|
|
521
522
|
return null;
|
|
522
523
|
}
|
|
523
524
|
// Validate that the folder context exists in CUBE before proceeding
|
|
524
525
|
try {
|
|
525
526
|
const testGroup = await objContext_create("ChRISDirsContext", `folder:${folder}`);
|
|
526
527
|
if (!testGroup) {
|
|
527
|
-
|
|
528
|
+
chiliErrLog(chalk.red(`Folder context '${folder}' does not exist in CUBE. Please specify an existing directory.`));
|
|
528
529
|
return null;
|
|
529
530
|
}
|
|
530
531
|
}
|
|
531
532
|
catch (error) {
|
|
532
|
-
|
|
533
|
+
chiliErrLog(chalk.red(`Folder context '${folder}' does not exist in CUBE. Please specify an existing directory.`));
|
|
533
534
|
return null;
|
|
534
535
|
}
|
|
535
536
|
// Folder exists, so just set chrisFolder without calling init()
|
|
536
537
|
// (init() tries to CREATE the folder which will fail if it already exists)
|
|
537
538
|
chrisIO.chrisFolder = folder;
|
|
538
|
-
|
|
539
|
+
chiliLog(border_draw(chalk.cyan("Scanning files to upload...")));
|
|
539
540
|
const filesToUpload = await filesToUpload_get(options.hostpath, folder);
|
|
540
541
|
let totalSize = 0;
|
|
541
542
|
for (const file of filesToUpload) {
|
|
@@ -643,12 +644,12 @@ async function chris_pull(scanRecord, progressBar) {
|
|
|
643
644
|
}
|
|
644
645
|
else {
|
|
645
646
|
summary.failedCount++;
|
|
646
|
-
|
|
647
|
+
chiliLog(chalk.yellow(`Failed to download: ${file.hostPath}`));
|
|
647
648
|
}
|
|
648
649
|
}
|
|
649
650
|
catch (error) {
|
|
650
651
|
summary.failedCount++;
|
|
651
|
-
|
|
652
|
+
chiliLog(chalk.red(`Error downloading ${file.hostPath}: ${error instanceof Error ? error.message : String(error)}`));
|
|
652
653
|
}
|
|
653
654
|
progressBar.update(index + 1, {
|
|
654
655
|
bytes: bytes_format(summary.transferSize), // Renamed
|
|
@@ -685,12 +686,12 @@ async function chris_push(scanRecord, progressBar) {
|
|
|
685
686
|
}
|
|
686
687
|
else {
|
|
687
688
|
summary.failedCount++;
|
|
688
|
-
|
|
689
|
+
chiliLog(chalk.yellow(`Failed to upload: ${file.hostPath}`));
|
|
689
690
|
}
|
|
690
691
|
}
|
|
691
692
|
catch (error) {
|
|
692
693
|
summary.failedCount++;
|
|
693
|
-
|
|
694
|
+
chiliLog(chalk.red(`Error uploading ${file.hostPath}: ${error instanceof Error ? error.message : String(error)}`));
|
|
694
695
|
}
|
|
695
696
|
progressBar.update(index + 1, {
|
|
696
697
|
bytes: bytes_format(summary.transferSize), // Renamed
|
|
@@ -713,7 +714,7 @@ async function download_handle(options) {
|
|
|
713
714
|
await chrisContext.currentContext_update();
|
|
714
715
|
const folder = chrisContext.singleContext.folder;
|
|
715
716
|
if (!folder) {
|
|
716
|
-
|
|
717
|
+
chiliErrLog(chalk.red("No ChRIS folder context set. Use 'connect' to establish a session."));
|
|
717
718
|
return false;
|
|
718
719
|
}
|
|
719
720
|
const localTarget = path.resolve(options.hostpath || process.cwd());
|
|
@@ -728,7 +729,7 @@ async function download_handle(options) {
|
|
|
728
729
|
}
|
|
729
730
|
catch (error) {
|
|
730
731
|
const msg = error instanceof Error ? error.message : String(error);
|
|
731
|
-
|
|
732
|
+
chiliErrLog(chalk.red(`Download failed: ${msg}`));
|
|
732
733
|
return false;
|
|
733
734
|
}
|
|
734
735
|
}
|
|
@@ -788,7 +789,7 @@ export async function pathCommand_setup(program) {
|
|
|
788
789
|
hostpath: hostpath || process.cwd(),
|
|
789
790
|
force: options.force,
|
|
790
791
|
});
|
|
791
|
-
|
|
792
|
+
chiliLog(result);
|
|
792
793
|
});
|
|
793
794
|
pathCommand
|
|
794
795
|
.command("scan")
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { CLIoptions } from "../utils/cli.js";
|
|
8
|
+
import { type CommandEnvelope } from "@fnndsc/cumin";
|
|
8
9
|
/**
|
|
9
10
|
* Handles commands related to groups of plugin contexts (computes, instances, parameters).
|
|
10
11
|
*/
|
|
@@ -33,6 +34,23 @@ export declare class PluginContextGroupHandler {
|
|
|
33
34
|
* Lists available fields for the current plugin context resource.
|
|
34
35
|
*/
|
|
35
36
|
parameters_fieldsList(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Renders plugin parameters in "man page" style as an envelope.
|
|
39
|
+
*
|
|
40
|
+
* Sans-I/O counterpart to {@link parameters_listMan}.
|
|
41
|
+
*
|
|
42
|
+
* @param options - CLI options for filtering.
|
|
43
|
+
* @returns An envelope carrying the rendered parameters or an error.
|
|
44
|
+
*/
|
|
45
|
+
parameters_listManRender(options: CLIoptions): Promise<CommandEnvelope>;
|
|
46
|
+
/**
|
|
47
|
+
* Renders available fields for the current plugin context resource as an envelope.
|
|
48
|
+
*
|
|
49
|
+
* Sans-I/O counterpart to {@link parameters_fieldsList}.
|
|
50
|
+
*
|
|
51
|
+
* @returns An envelope carrying the field listing.
|
|
52
|
+
*/
|
|
53
|
+
parameters_fieldsRender(): Promise<CommandEnvelope>;
|
|
36
54
|
/**
|
|
37
55
|
* Sets up the Commander.js commands for plugin context group operations.
|
|
38
56
|
*
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
import { BaseGroupHandler } from "../handlers/baseGroupHandler.js";
|
|
7
7
|
import { options_toParams } from "../utils/cli.js";
|
|
8
8
|
import { PluginContextController } from "../controllers/pluginContextController.js";
|
|
9
|
-
import { pluginParameters_renderMan } from "../views/pluginParameters.js";
|
|
9
|
+
import { pluginParameters_renderMan, pluginParameters_manRender } from "../views/pluginParameters.js";
|
|
10
|
+
import { envelope_ok, envelope_error } from "@fnndsc/cumin";
|
|
11
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
10
12
|
class InitializationError extends Error {
|
|
11
13
|
constructor(message) {
|
|
12
14
|
super(message);
|
|
@@ -52,7 +54,7 @@ export class PluginContextGroupHandler {
|
|
|
52
54
|
// We access the asset directly from the controller
|
|
53
55
|
const asset = this.controller.chrisObject.asset;
|
|
54
56
|
if (!asset || typeof asset.resources_listAndFilterByOptions !== 'function') {
|
|
55
|
-
|
|
57
|
+
chiliErrLog("Underlying resource does not support listing.");
|
|
56
58
|
return;
|
|
57
59
|
}
|
|
58
60
|
const params = options_toParams(options);
|
|
@@ -61,11 +63,11 @@ export class PluginContextGroupHandler {
|
|
|
61
63
|
pluginParameters_renderMan(results);
|
|
62
64
|
}
|
|
63
65
|
else {
|
|
64
|
-
|
|
66
|
+
chiliLog("No parameters found.");
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
catch (error) {
|
|
68
|
-
|
|
70
|
+
chiliErrLog(`Error listing parameters: ${error}`);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
/**
|
|
@@ -76,6 +78,44 @@ export class PluginContextGroupHandler {
|
|
|
76
78
|
await this.baseGroupHandler.resourceFields_list();
|
|
77
79
|
}
|
|
78
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Renders plugin parameters in "man page" style as an envelope.
|
|
83
|
+
*
|
|
84
|
+
* Sans-I/O counterpart to {@link parameters_listMan}.
|
|
85
|
+
*
|
|
86
|
+
* @param options - CLI options for filtering.
|
|
87
|
+
* @returns An envelope carrying the rendered parameters or an error.
|
|
88
|
+
*/
|
|
89
|
+
async parameters_listManRender(options) {
|
|
90
|
+
try {
|
|
91
|
+
const asset = this.controller.chrisObject.asset;
|
|
92
|
+
if (!asset || typeof asset.resources_listAndFilterByOptions !== 'function') {
|
|
93
|
+
return envelope_error('', undefined, "Underlying resource does not support listing.\n");
|
|
94
|
+
}
|
|
95
|
+
const params = options_toParams(options);
|
|
96
|
+
const results = await asset.resources_listAndFilterByOptions(params);
|
|
97
|
+
if (results) {
|
|
98
|
+
return envelope_ok(`${pluginParameters_manRender(results)}\n`);
|
|
99
|
+
}
|
|
100
|
+
return envelope_ok("No parameters found.\n");
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return envelope_error('', undefined, `Error listing parameters: ${error}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Renders available fields for the current plugin context resource as an envelope.
|
|
108
|
+
*
|
|
109
|
+
* Sans-I/O counterpart to {@link parameters_fieldsList}.
|
|
110
|
+
*
|
|
111
|
+
* @returns An envelope carrying the field listing.
|
|
112
|
+
*/
|
|
113
|
+
async parameters_fieldsRender() {
|
|
114
|
+
if (this.baseGroupHandler) {
|
|
115
|
+
return this.baseGroupHandler.resourceFields_render();
|
|
116
|
+
}
|
|
117
|
+
return envelope_ok('');
|
|
118
|
+
}
|
|
79
119
|
/**
|
|
80
120
|
* Sets up the Commander.js commands for plugin context group operations.
|
|
81
121
|
*
|
|
@@ -13,6 +13,7 @@ import { pluginIds_resolve } from "../commands/plugin/search.js";
|
|
|
13
13
|
import { pluginRun_render } from "../views/plugin.js"; // Still needed for pluginRun_render
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
import { pluginReadme_render } from "../commands/plugin/readme.js";
|
|
16
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
16
17
|
/**
|
|
17
18
|
* Handles commands related to groups of ChRIS plugins.
|
|
18
19
|
*/
|
|
@@ -34,10 +35,10 @@ export class PluginGroupHandler {
|
|
|
34
35
|
// async plugins_list(options: CLIoptions): Promise<void> {
|
|
35
36
|
// try {
|
|
36
37
|
// const { plugins, selectedFields } = await plugins_fetchList(options);
|
|
37
|
-
//
|
|
38
|
+
// chiliLog(pluginList_render(plugins, selectedFields, { table: options.table, csv: options.csv }));
|
|
38
39
|
// } catch (error: unknown) {
|
|
39
40
|
// const msg = error instanceof Error ? error.message : String(error);
|
|
40
|
-
//
|
|
41
|
+
// chiliErrLog(msg);
|
|
41
42
|
// }
|
|
42
43
|
// }
|
|
43
44
|
/**
|
|
@@ -50,11 +51,11 @@ export class PluginGroupHandler {
|
|
|
50
51
|
table_display(fields, ["fields"]);
|
|
51
52
|
}
|
|
52
53
|
else {
|
|
53
|
-
|
|
54
|
+
chiliLog(`No resource fields found for ${this.assetName}.`);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
catch (error) {
|
|
57
|
-
|
|
58
|
+
chiliLog(errorStack.stack_search(this.assetName)[0]);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
@@ -67,12 +68,12 @@ export class PluginGroupHandler {
|
|
|
67
68
|
for (const search of searchables) {
|
|
68
69
|
const items = await plugins_searchByTerm(search.raw);
|
|
69
70
|
if (items.length === 0) {
|
|
70
|
-
|
|
71
|
+
chiliLog(`No plugins found matching: ${search.raw}`);
|
|
71
72
|
continue;
|
|
72
73
|
}
|
|
73
74
|
for (const item of items) {
|
|
74
75
|
// Show item info - reusing table_display for single item details if possible, or simple log
|
|
75
|
-
|
|
76
|
+
chiliLog(`Preparing to delete Plugin: ID=${item.id}, Name=${item.name}, Version=${item.version}`);
|
|
76
77
|
if (!options.force) {
|
|
77
78
|
const confirmed = await prompt_confirm(`Are you sure you want to delete plugin ${item.name} (ID: ${item.id})?`);
|
|
78
79
|
if (!confirmed)
|
|
@@ -80,10 +81,10 @@ export class PluginGroupHandler {
|
|
|
80
81
|
}
|
|
81
82
|
const success = await plugin_deleteById(item.id);
|
|
82
83
|
if (success) {
|
|
83
|
-
|
|
84
|
+
chiliLog(`Deleted plugin ${item.id}`);
|
|
84
85
|
}
|
|
85
86
|
else {
|
|
86
|
-
|
|
87
|
+
chiliErrLog(`Failed to delete plugin ${item.id}`);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
}
|
|
@@ -161,11 +162,11 @@ export class PluginMemberHandler {
|
|
|
161
162
|
async readme_print(repoUrl) {
|
|
162
163
|
const content = await this.controller.readmeContent_fetch(repoUrl);
|
|
163
164
|
if (content) {
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
chiliLog(chalk.green.bold("\nREADME Content:"));
|
|
166
|
+
chiliLog(pluginReadme_render(content));
|
|
166
167
|
}
|
|
167
168
|
else {
|
|
168
|
-
|
|
169
|
+
chiliLog(chalk.red("README not found in the repository."));
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
/**
|
|
@@ -176,21 +177,21 @@ export class PluginMemberHandler {
|
|
|
176
177
|
*/
|
|
177
178
|
async plugin_readme(pluginId) {
|
|
178
179
|
try {
|
|
179
|
-
|
|
180
|
+
chiliLog(`Fetching info for plugin with ID: ${pluginId}`);
|
|
180
181
|
const documentation = await this.controller.documentationUrl_get(pluginId);
|
|
181
182
|
if (!documentation) {
|
|
182
183
|
return null;
|
|
183
184
|
}
|
|
184
|
-
|
|
185
|
+
chiliLog(documentation);
|
|
185
186
|
await this.readme_print(documentation);
|
|
186
187
|
return documentation;
|
|
187
188
|
}
|
|
188
189
|
catch (error) {
|
|
189
190
|
if (error instanceof Error) {
|
|
190
|
-
|
|
191
|
+
chiliErrLog(`Error fetching plugin info: ${error.message}`);
|
|
191
192
|
}
|
|
192
193
|
else {
|
|
193
|
-
|
|
194
|
+
chiliErrLog("An unknown error occurred while fetching plugin info");
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
return null;
|
|
@@ -214,15 +215,15 @@ export class PluginMemberHandler {
|
|
|
214
215
|
try {
|
|
215
216
|
const instance = await plugin_execute(searchable, params);
|
|
216
217
|
if (!instance) {
|
|
217
|
-
|
|
218
|
+
chiliLog(errorStack.messagesOfType_search("error", "plugin"));
|
|
218
219
|
return null;
|
|
219
220
|
}
|
|
220
|
-
|
|
221
|
+
chiliLog(pluginRun_render(instance));
|
|
221
222
|
return instance.id;
|
|
222
223
|
}
|
|
223
224
|
catch (e) {
|
|
224
225
|
const message = e instanceof Error ? e.message : String(e);
|
|
225
|
-
|
|
226
|
+
chiliErrLog(message);
|
|
226
227
|
return null;
|
|
227
228
|
}
|
|
228
229
|
}
|
|
@@ -234,7 +235,7 @@ export class PluginMemberHandler {
|
|
|
234
235
|
if (!hits) {
|
|
235
236
|
return null;
|
|
236
237
|
}
|
|
237
|
-
|
|
238
|
+
chiliLog(hits);
|
|
238
239
|
return hits;
|
|
239
240
|
}
|
|
240
241
|
/**
|
|
@@ -270,10 +271,10 @@ export class PluginMemberHandler {
|
|
|
270
271
|
else {
|
|
271
272
|
const warnings = errorStack_getAllOfType('warning');
|
|
272
273
|
if (warnings && warnings.length > 0) {
|
|
273
|
-
warnings.forEach(warning =>
|
|
274
|
+
warnings.forEach(warning => chiliErrLog(chalk.yellow(warning)));
|
|
274
275
|
}
|
|
275
276
|
else {
|
|
276
|
-
|
|
277
|
+
chiliErrLog(chalk.red("Plugin not found."));
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
});
|
|
@@ -297,7 +298,7 @@ export class PluginMemberHandler {
|
|
|
297
298
|
});
|
|
298
299
|
}
|
|
299
300
|
else {
|
|
300
|
-
|
|
301
|
+
chiliErrLog(`Failed to find '${this.assetName}' command. The 'readme' subcommand was not added.`);
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
304
|
}
|
package/dist/run.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
|
+
import { type ChiliCaptured } from "./screen/output.js";
|
|
12
13
|
/**
|
|
13
14
|
* Executes a single chili command in the current process.
|
|
14
15
|
*
|
|
@@ -22,3 +23,15 @@
|
|
|
22
23
|
* the leading `node`/script entries).
|
|
23
24
|
*/
|
|
24
25
|
export declare function run(argv: string[]): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Executes a single chili command with its output captured instead of printed.
|
|
28
|
+
*
|
|
29
|
+
* Runs {@link run} inside {@link chili_capture}, so everything the command
|
|
30
|
+
* would have written to the output and error channels is collected and
|
|
31
|
+
* returned. This is how an in-process host (the brasa engine) drives chili
|
|
32
|
+
* headless and carries the result in an envelope, with no console monkeypatch.
|
|
33
|
+
*
|
|
34
|
+
* @param argv - The chili arguments, e.g. `["feeds", "list", "-s"]`.
|
|
35
|
+
* @returns The captured output and error text.
|
|
36
|
+
*/
|
|
37
|
+
export declare function run_capture(argv: string[]): Promise<ChiliCaptured>;
|
package/dist/run.js
CHANGED
|
@@ -27,6 +27,7 @@ import { PACSQueryGroupHandler } from "./pacs/pacsQueryHandler.js";
|
|
|
27
27
|
import { PACSRetrieveGroupHandler } from "./pacs/pacsRetrieveHandler.js";
|
|
28
28
|
import { chrisConnection_init, NodeStorageProvider, errorStack_getAllOfType, } from "@fnndsc/cumin";
|
|
29
29
|
import { FileGroupHandler } from "./filesystem/fileGroupHandler.js";
|
|
30
|
+
import { chiliErrLog, chiliLog, chili_capture } from "./screen/output.js";
|
|
30
31
|
/**
|
|
31
32
|
* Suppress the DEP0169 (`url.parse()`) warning emitted transitively by
|
|
32
33
|
* axios -> proxy-from-env, which we do not control. Installed once at import.
|
|
@@ -84,14 +85,14 @@ async function handlers_initialize(program) {
|
|
|
84
85
|
const err = e instanceof Error ? e.message : String(e);
|
|
85
86
|
const errors = errorStack_getAllOfType("error");
|
|
86
87
|
const warnings = errorStack_getAllOfType("warning");
|
|
87
|
-
|
|
88
|
+
chiliLog(`Note: File group commands (files, dirs, links) are unavailable. Reason: ${err}`);
|
|
88
89
|
if (errors.length > 0) {
|
|
89
|
-
|
|
90
|
-
errors.forEach((msg) =>
|
|
90
|
+
chiliLog("Errors:");
|
|
91
|
+
errors.forEach((msg) => chiliLog(` - ${msg}`));
|
|
91
92
|
}
|
|
92
93
|
if (warnings.length > 0) {
|
|
93
|
-
|
|
94
|
-
warnings.forEach((msg) =>
|
|
94
|
+
chiliLog("Warnings:");
|
|
95
|
+
warnings.forEach((msg) => chiliLog(` - ${msg}`));
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
try {
|
|
@@ -106,14 +107,14 @@ async function handlers_initialize(program) {
|
|
|
106
107
|
const err = e instanceof Error ? e.message : String(e);
|
|
107
108
|
const errors = errorStack_getAllOfType("error");
|
|
108
109
|
const warnings = errorStack_getAllOfType("warning");
|
|
109
|
-
|
|
110
|
+
chiliLog(`Note: Plugin context commands are unavailable. Reason: ${err}`);
|
|
110
111
|
if (errors.length > 0) {
|
|
111
|
-
|
|
112
|
-
errors.forEach((msg) =>
|
|
112
|
+
chiliLog("Errors:");
|
|
113
|
+
errors.forEach((msg) => chiliLog(` - ${msg}`));
|
|
113
114
|
}
|
|
114
115
|
if (warnings.length > 0) {
|
|
115
|
-
|
|
116
|
-
warnings.forEach((msg) =>
|
|
116
|
+
chiliLog("Warnings:");
|
|
117
|
+
warnings.forEach((msg) => chiliLog(` - ${msg}`));
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
}
|
|
@@ -155,7 +156,7 @@ export async function run(argv) {
|
|
|
155
156
|
if (context) {
|
|
156
157
|
const contextSetSuccess = await connection.context_set(context);
|
|
157
158
|
if (!contextSetSuccess) {
|
|
158
|
-
|
|
159
|
+
chiliErrLog("Failed to set context.");
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
161
162
|
fullArgv = newArgs;
|
|
@@ -174,8 +175,8 @@ export async function run(argv) {
|
|
|
174
175
|
program.parseOptions(fullArgv);
|
|
175
176
|
const options = program.opts();
|
|
176
177
|
if (!options.nosplash) {
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
chiliLog(figlet.textSync("ChILI"));
|
|
179
|
+
chiliLog("ChILI handles Intelligent Line Interactions");
|
|
179
180
|
}
|
|
180
181
|
try {
|
|
181
182
|
await program.parseAsync(fullArgv);
|
|
@@ -190,3 +191,17 @@ export async function run(argv) {
|
|
|
190
191
|
throw err;
|
|
191
192
|
}
|
|
192
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Executes a single chili command with its output captured instead of printed.
|
|
196
|
+
*
|
|
197
|
+
* Runs {@link run} inside {@link chili_capture}, so everything the command
|
|
198
|
+
* would have written to the output and error channels is collected and
|
|
199
|
+
* returned. This is how an in-process host (the brasa engine) drives chili
|
|
200
|
+
* headless and carries the result in an envelope, with no console monkeypatch.
|
|
201
|
+
*
|
|
202
|
+
* @param argv - The chili arguments, e.g. `["feeds", "list", "-s"]`.
|
|
203
|
+
* @returns The captured output and error text.
|
|
204
|
+
*/
|
|
205
|
+
export async function run_capture(argv) {
|
|
206
|
+
return chili_capture(() => run(argv));
|
|
207
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file chili's output seam.
|
|
3
|
+
*
|
|
4
|
+
* chili historically printed straight to `console.log`/`console.error`, which
|
|
5
|
+
* fused its command layer to a terminal and forced any in-process host (the
|
|
6
|
+
* brasa engine, which drives chili headless) to capture output with a console
|
|
7
|
+
* monkeypatch. This seam inverts that: command output is written to whatever
|
|
8
|
+
* writer is installed, and by default that writer simply delegates to the
|
|
9
|
+
* process console — so chili's standalone CLI behaves exactly as it always has.
|
|
10
|
+
*
|
|
11
|
+
* A host captures a run's output with {@link chili_capture}, which swaps in a
|
|
12
|
+
* buffering writer for the duration of a call and returns the collected text.
|
|
13
|
+
* This is explicit dependency injection through a module seam, not a runtime
|
|
14
|
+
* override of the global `console`: the default writer calls `console.log`
|
|
15
|
+
* normally, and capture bypasses the console entirely.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Destination for chili's command output.
|
|
21
|
+
*/
|
|
22
|
+
export interface ChiliWriter {
|
|
23
|
+
/**
|
|
24
|
+
* Writes to the output channel with `console.log` semantics.
|
|
25
|
+
*
|
|
26
|
+
* @param args - The values to format, exactly as passed to `console.log`.
|
|
27
|
+
*/
|
|
28
|
+
log(...args: unknown[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Writes to the error channel with `console.error` semantics.
|
|
31
|
+
*
|
|
32
|
+
* @param args - The values to format, exactly as passed to `console.error`.
|
|
33
|
+
*/
|
|
34
|
+
errLog(...args: unknown[]): void;
|
|
35
|
+
/**
|
|
36
|
+
* Writes raw text or bytes to the output channel without adding a newline.
|
|
37
|
+
*
|
|
38
|
+
* @param chunk - The text or bytes to write.
|
|
39
|
+
*/
|
|
40
|
+
write(chunk: string | Buffer): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Installs a writer as the active output destination.
|
|
44
|
+
*
|
|
45
|
+
* @param writer - The writer to install.
|
|
46
|
+
* @returns The previously installed writer, so callers can restore it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function chiliWriter_set(writer: ChiliWriter): ChiliWriter;
|
|
49
|
+
/**
|
|
50
|
+
* Writes to the output channel with `console.log` semantics.
|
|
51
|
+
*
|
|
52
|
+
* @param args - The values to format, exactly as passed to `console.log`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function chiliLog(...args: unknown[]): void;
|
|
55
|
+
/**
|
|
56
|
+
* Writes to the error channel with `console.error` semantics.
|
|
57
|
+
*
|
|
58
|
+
* @param args - The values to format, exactly as passed to `console.error`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function chiliErrLog(...args: unknown[]): void;
|
|
61
|
+
/**
|
|
62
|
+
* Writes raw text or bytes to the output channel without adding a newline —
|
|
63
|
+
* the replacement for direct `process.stdout.write` calls.
|
|
64
|
+
*
|
|
65
|
+
* @param chunk - The text or bytes to write.
|
|
66
|
+
*/
|
|
67
|
+
export declare function chiliWrite(chunk: string | Buffer): void;
|
|
68
|
+
/**
|
|
69
|
+
* The text captured from a run, one string per channel.
|
|
70
|
+
*/
|
|
71
|
+
export interface ChiliCaptured {
|
|
72
|
+
/** Everything written to the output channel. */
|
|
73
|
+
out: string;
|
|
74
|
+
/** Everything written to the error channel. */
|
|
75
|
+
err: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Runs `fn` with both channels captured into memory and returns the collected
|
|
79
|
+
* text, restoring the previous writer afterwards even if `fn` throws.
|
|
80
|
+
*
|
|
81
|
+
* Formatting matches `console.log`: arguments are rendered with `util.format`
|
|
82
|
+
* and each `log`/`errLog` call contributes a trailing newline, exactly as the
|
|
83
|
+
* console would have written.
|
|
84
|
+
*
|
|
85
|
+
* @param fn - The work whose output should be captured.
|
|
86
|
+
* @returns The captured output and error text.
|
|
87
|
+
*/
|
|
88
|
+
export declare function chili_capture(fn: () => Promise<void>): Promise<ChiliCaptured>;
|