@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
|
@@ -14,13 +14,14 @@ import { computeResources_validate, computeResourceNames_parse, computeResources
|
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { input_detectFormat, PluginInputFormat, } from '../../utils/input_format.js';
|
|
16
16
|
import { adminCredentials_prompt, } from '../../utils/admin_prompt.js';
|
|
17
|
+
import { chiliErrLog, chiliLog } from "../../screen/output.js";
|
|
17
18
|
async function computeResources_resolve(options) {
|
|
18
19
|
if (options.compute) {
|
|
19
20
|
const names = computeResourceNames_parse(options.compute);
|
|
20
21
|
const validationResult = await computeResources_validate(names);
|
|
21
22
|
if (!validationResult.ok) {
|
|
22
23
|
const errors = errorStack.allOfType_get('error');
|
|
23
|
-
errors.forEach((err) =>
|
|
24
|
+
errors.forEach((err) => chiliErrLog(err));
|
|
24
25
|
return { ok: false };
|
|
25
26
|
}
|
|
26
27
|
return { ok: true, value: names };
|
|
@@ -28,7 +29,7 @@ async function computeResources_resolve(options) {
|
|
|
28
29
|
const allResult = await computeResources_getAll();
|
|
29
30
|
if (allResult.ok && allResult.value.length > 0) {
|
|
30
31
|
const names = allResult.value.map((r) => r.name);
|
|
31
|
-
|
|
32
|
+
chiliLog(`Using compute resources: ${names.join(', ')}`);
|
|
32
33
|
return { ok: true, value: names };
|
|
33
34
|
}
|
|
34
35
|
return { ok: true, value: ['host'] };
|
|
@@ -42,13 +43,13 @@ async function computeResources_resolve(options) {
|
|
|
42
43
|
*/
|
|
43
44
|
export async function plugin_add(input, options) {
|
|
44
45
|
const detected = input_detectFormat(input);
|
|
45
|
-
|
|
46
|
+
chiliLog(`Detected input format: ${detected.format}`);
|
|
46
47
|
const resourcesResult = await computeResources_resolve(options);
|
|
47
48
|
if (!resourcesResult.ok)
|
|
48
49
|
return 'failed';
|
|
49
50
|
const computeResources = resourcesResult.value;
|
|
50
51
|
// Phase 1: Check if plugin exists in current CUBE
|
|
51
|
-
|
|
52
|
+
chiliLog('\n=== Phase 1: Checking current CUBE ===');
|
|
52
53
|
const searchTerm = detected.format === PluginInputFormat.DOCKER_IMAGE
|
|
53
54
|
? detected.pluginName || detected.value
|
|
54
55
|
: detected.value;
|
|
@@ -56,10 +57,10 @@ export async function plugin_add(input, options) {
|
|
|
56
57
|
if (existingPlugin) {
|
|
57
58
|
return 'already_exists';
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
chiliLog('Plugin not found in current CUBE.');
|
|
60
61
|
// Phase 2: Search peer stores (skip if input is store URL)
|
|
61
62
|
if (detected.format !== PluginInputFormat.STORE_URL) {
|
|
62
|
-
|
|
63
|
+
chiliLog('\n=== Phase 2: Searching peer stores ===');
|
|
63
64
|
const peerStoreUrls = options.store
|
|
64
65
|
? [options.store]
|
|
65
66
|
: ['https://cube.chrisproject.org/api/v1/'];
|
|
@@ -71,15 +72,15 @@ export async function plugin_add(input, options) {
|
|
|
71
72
|
: undefined;
|
|
72
73
|
const peerResult = await plugins_searchPeers(searchName, searchVersion, peerStoreUrls);
|
|
73
74
|
if (peerResult) {
|
|
74
|
-
|
|
75
|
+
chiliLog(`Found plugin in peer store: ${peerResult.storeName}`);
|
|
75
76
|
const ok = await pluginFromStore_register(peerResult.plugin, computeResources, options);
|
|
76
77
|
return ok ? 'installed' : 'failed';
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
chiliLog('Plugin not found in peer stores.');
|
|
79
80
|
}
|
|
80
81
|
// Phase 3: Docker extraction (only for Docker images or plugin names)
|
|
81
82
|
if (detected.format !== PluginInputFormat.STORE_URL) {
|
|
82
|
-
|
|
83
|
+
chiliLog('\n=== Phase 3: Docker extraction ===');
|
|
83
84
|
const dockerImage = detected.format === PluginInputFormat.DOCKER_IMAGE
|
|
84
85
|
? detected.value
|
|
85
86
|
: `${detected.value}:latest`; // Assume latest tag for plugin names
|
|
@@ -87,7 +88,7 @@ export async function plugin_add(input, options) {
|
|
|
87
88
|
return ok ? 'installed' : 'failed';
|
|
88
89
|
}
|
|
89
90
|
// If we get here with a store URL, we couldn't process it
|
|
90
|
-
|
|
91
|
+
chiliErrLog('Store URL import not yet fully supported.');
|
|
91
92
|
return 'failed';
|
|
92
93
|
}
|
|
93
94
|
/**
|
|
@@ -101,7 +102,7 @@ export async function plugin_add(input, options) {
|
|
|
101
102
|
* @returns Promise resolving to success boolean.
|
|
102
103
|
*/
|
|
103
104
|
async function pluginFromStore_register(pluginData, computeResources, options) {
|
|
104
|
-
|
|
105
|
+
chiliLog('Importing plugin from peer store...');
|
|
105
106
|
const initialCreds = options.adminUser && options.adminPassword
|
|
106
107
|
? { username: options.adminUser, password: options.adminPassword }
|
|
107
108
|
: undefined;
|
|
@@ -109,11 +110,11 @@ async function pluginFromStore_register(pluginData, computeResources, options) {
|
|
|
109
110
|
const result = await plugin_importFromStore('', // Store URL not needed when we have plugin data
|
|
110
111
|
pluginData, computeResources, initialCreds);
|
|
111
112
|
if (result.success) {
|
|
112
|
-
|
|
113
|
+
chiliLog(`Plugin '${result.plugin?.name}' registered successfully.`);
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
115
116
|
if (!result.requiresAuth) {
|
|
116
|
-
|
|
117
|
+
chiliErrLog(result.errorMessage || 'Failed to import plugin from store.');
|
|
117
118
|
return false;
|
|
118
119
|
}
|
|
119
120
|
// Admin auth required - try with interactive credentials
|
|
@@ -122,16 +123,16 @@ async function pluginFromStore_register(pluginData, computeResources, options) {
|
|
|
122
123
|
return retryResult.success;
|
|
123
124
|
}, options);
|
|
124
125
|
if (!retrySuccess) {
|
|
125
|
-
|
|
126
|
+
chiliErrLog('Failed to import plugin from store (authentication failed or rejected).');
|
|
126
127
|
const errors = errorStack.allOfType_get('error');
|
|
127
128
|
if (errors.length > 0) {
|
|
128
|
-
|
|
129
|
-
errors.forEach((e) =>
|
|
129
|
+
chiliErrLog('Errors:');
|
|
130
|
+
errors.forEach((e) => chiliErrLog(`- ${e}`));
|
|
130
131
|
}
|
|
131
132
|
const warnings = errorStack.allOfType_get('warning');
|
|
132
133
|
if (warnings.length > 0) {
|
|
133
|
-
|
|
134
|
-
warnings.forEach((e) =>
|
|
134
|
+
chiliErrLog('Warnings:');
|
|
135
|
+
warnings.forEach((e) => chiliErrLog(`- ${e}`));
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
138
|
return retrySuccess;
|
|
@@ -155,11 +156,11 @@ async function pluginFromDocker_register(image, computeResources, options) {
|
|
|
155
156
|
if (!pulled) {
|
|
156
157
|
return false;
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
+
chiliLog('Extracting plugin descriptor from image...');
|
|
159
160
|
const pluginData = await pluginJSON_extractFromImage(image, options);
|
|
160
161
|
if (!pluginData) {
|
|
161
162
|
const msg = 'Failed to extract plugin descriptor from image.';
|
|
162
|
-
|
|
163
|
+
chiliErrLog(msg);
|
|
163
164
|
errorStack.stack_push('error', msg);
|
|
164
165
|
return false;
|
|
165
166
|
}
|
|
@@ -169,10 +170,10 @@ async function pluginFromDocker_register(image, computeResources, options) {
|
|
|
169
170
|
? { username: options.adminUser, password: options.adminPassword }
|
|
170
171
|
: undefined;
|
|
171
172
|
// Register with admin API
|
|
172
|
-
|
|
173
|
+
chiliLog('Registering plugin with ChRIS CUBE...');
|
|
173
174
|
const registered = await plugin_registerWithAdmin(pluginData, computeResources, initialCreds);
|
|
174
175
|
if (registered) {
|
|
175
|
-
|
|
176
|
+
chiliLog(`Plugin '${registered.name}' registered successfully.`);
|
|
176
177
|
return true;
|
|
177
178
|
}
|
|
178
179
|
// Check if failure was due to admin auth
|
|
@@ -187,7 +188,7 @@ async function pluginFromDocker_register(image, computeResources, options) {
|
|
|
187
188
|
lowerE.includes('admin credentials required');
|
|
188
189
|
});
|
|
189
190
|
if (!authError) {
|
|
190
|
-
|
|
191
|
+
chiliErrLog('Failed to register plugin.');
|
|
191
192
|
return false;
|
|
192
193
|
}
|
|
193
194
|
// Retry with admin credentials
|
|
@@ -224,17 +225,17 @@ async function pluginJSON_extractFromImage(image, options) {
|
|
|
224
225
|
},
|
|
225
226
|
];
|
|
226
227
|
for (const method of methods) {
|
|
227
|
-
|
|
228
|
+
chiliLog(`Trying ${method.name}...`);
|
|
228
229
|
const jsonString = await method.fn();
|
|
229
230
|
if (jsonString && jsonString.trim() !== '') {
|
|
230
231
|
try {
|
|
231
232
|
const parsed = JSON.parse(jsonString);
|
|
232
|
-
|
|
233
|
+
chiliLog(`Successfully extracted plugin descriptor using ${method.name}`);
|
|
233
234
|
return parsed;
|
|
234
235
|
}
|
|
235
236
|
catch (e) {
|
|
236
237
|
const msg = `Failed to parse JSON from ${method.name}`;
|
|
237
|
-
|
|
238
|
+
chiliErrLog(msg);
|
|
238
239
|
errorStack.stack_push('error', msg);
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -296,7 +297,7 @@ async function pluginJSON_tryOldChrisapp(image) {
|
|
|
296
297
|
return null;
|
|
297
298
|
}
|
|
298
299
|
const command = `docker run --rm ${image} ${cmd[0]} --json`;
|
|
299
|
-
|
|
300
|
+
chiliLog(` Running: ${cmd[0]} --json`);
|
|
300
301
|
const result = await shellCommand_runWithDetails(command);
|
|
301
302
|
if (!result.success || !result.stdout || result.stdout.trim() === '') {
|
|
302
303
|
if (result.error || result.stderr) {
|
|
@@ -319,20 +320,20 @@ function pluginData_inferMissingFields(pluginData, image, options) {
|
|
|
319
320
|
if (!pluginData.name) {
|
|
320
321
|
const imageNameWithoutTag = image.split(':')[0];
|
|
321
322
|
pluginData.name = path.basename(imageNameWithoutTag);
|
|
322
|
-
|
|
323
|
+
chiliLog(`Inferred plugin name: ${pluginData.name}`);
|
|
323
324
|
}
|
|
324
325
|
if (!pluginData.dock_image) {
|
|
325
326
|
pluginData.dock_image = image;
|
|
326
|
-
|
|
327
|
+
chiliLog(`Inferred dock_image: ${pluginData.dock_image}`);
|
|
327
328
|
}
|
|
328
329
|
if (!pluginData.public_repo && options.public_repo) {
|
|
329
330
|
pluginData.public_repo = options.public_repo;
|
|
330
|
-
|
|
331
|
+
chiliLog(`Using provided public_repo: ${pluginData.public_repo}`);
|
|
331
332
|
}
|
|
332
333
|
else if (!pluginData.public_repo && image.includes('/')) {
|
|
333
334
|
const repoGuess = image.split('/')[0] + '/' + path.basename(image.split(':')[0]);
|
|
334
335
|
pluginData.public_repo = `https://github.com/${repoGuess}`;
|
|
335
|
-
|
|
336
|
+
chiliLog(`Inferred public_repo: ${pluginData.public_repo}`);
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
/**
|
|
@@ -358,7 +359,7 @@ async function registrationWithAuth_retry(registrationFn, options) {
|
|
|
358
359
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
359
360
|
const creds = await adminCredentials_prompt(attempt, MAX_ATTEMPTS);
|
|
360
361
|
if (!creds) {
|
|
361
|
-
|
|
362
|
+
chiliLog('Authentication cancelled.');
|
|
362
363
|
return false;
|
|
363
364
|
}
|
|
364
365
|
const success = await registrationFn(creds);
|
|
@@ -366,9 +367,9 @@ async function registrationWithAuth_retry(registrationFn, options) {
|
|
|
366
367
|
return true;
|
|
367
368
|
}
|
|
368
369
|
if (attempt < MAX_ATTEMPTS) {
|
|
369
|
-
|
|
370
|
+
chiliLog('Authentication failed.');
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
|
-
|
|
373
|
+
chiliErrLog(`Authentication failed after ${MAX_ATTEMPTS} attempts.`);
|
|
373
374
|
return false;
|
|
374
375
|
}
|
|
@@ -11,6 +11,7 @@ import * as fs from 'fs';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
13
|
import chalk from 'chalk';
|
|
14
|
+
import { chiliErrLog } from "../screen/output.js";
|
|
14
15
|
let cachedConfig = null;
|
|
15
16
|
/**
|
|
16
17
|
* Loads the color configuration from colors.yml.
|
|
@@ -32,7 +33,7 @@ function colorConfig_load() {
|
|
|
32
33
|
}
|
|
33
34
|
catch (e) {
|
|
34
35
|
const msg = e instanceof Error ? e.message : String(e);
|
|
35
|
-
|
|
36
|
+
chiliErrLog(`Warning: Could not load color config: ${msg}`);
|
|
36
37
|
return {
|
|
37
38
|
icons: {
|
|
38
39
|
enabled: true,
|
|
@@ -2,6 +2,7 @@ import { connect_login } from "../commands/connect/login.js";
|
|
|
2
2
|
import { connect_logout } from "../commands/connect/logout.js";
|
|
3
3
|
import { login_render, logout_render } from "../views/connect.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
5
6
|
/**
|
|
6
7
|
* Sets up the 'connect' and 'logout' commands for the CLI program.
|
|
7
8
|
*
|
|
@@ -23,12 +24,12 @@ export function connectCommand_setup(program) {
|
|
|
23
24
|
};
|
|
24
25
|
try {
|
|
25
26
|
const success = await connect_login(connectOptions);
|
|
26
|
-
|
|
27
|
+
chiliLog(login_render(success, url, user));
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
|
-
|
|
30
|
+
chiliLog(login_render(false, url, user));
|
|
30
31
|
const msg = error instanceof Error ? error.message : String(error);
|
|
31
|
-
|
|
32
|
+
chiliErrLog(chalk.red(`Error: ${msg}`));
|
|
32
33
|
}
|
|
33
34
|
});
|
|
34
35
|
program
|
|
@@ -37,12 +38,12 @@ export function connectCommand_setup(program) {
|
|
|
37
38
|
.action(async () => {
|
|
38
39
|
try {
|
|
39
40
|
await connect_logout();
|
|
40
|
-
|
|
41
|
+
chiliLog(logout_render(true));
|
|
41
42
|
}
|
|
42
43
|
catch (error) {
|
|
43
|
-
|
|
44
|
+
chiliLog(logout_render(false));
|
|
44
45
|
const msg = error instanceof Error ? error.message : String(error);
|
|
45
|
-
|
|
46
|
+
chiliErrLog(chalk.red(`Error: ${msg}`));
|
|
46
47
|
}
|
|
47
48
|
});
|
|
48
49
|
}
|
|
@@ -8,6 +8,7 @@ import { context_getFull, context_getSingle, context_set as salsa_context_set }
|
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
// import Table from "cli-table3";
|
|
10
10
|
import { screen, border_draw, table_display } from "../screen/screen.js";
|
|
11
|
+
import { chiliLog } from "../screen/output.js";
|
|
11
12
|
/**
|
|
12
13
|
* Retrieves and formats ChRIS context information based on CLI options.
|
|
13
14
|
*
|
|
@@ -94,7 +95,7 @@ async function context_displaySingle(options) {
|
|
|
94
95
|
Value: singleContext.pacsserver || "Not set",
|
|
95
96
|
},
|
|
96
97
|
];
|
|
97
|
-
|
|
98
|
+
chiliLog(screen.table_output(tableData, {
|
|
98
99
|
head: ["Context", "Value"],
|
|
99
100
|
columns: [
|
|
100
101
|
{ color: "yellow", justification: "right" },
|
|
@@ -174,7 +175,7 @@ export async function contextCommand_setup(program) {
|
|
|
174
175
|
.option("--all", "get all contexts for current session")
|
|
175
176
|
.action(async (options) => {
|
|
176
177
|
const result = await context_get(options);
|
|
177
|
-
|
|
178
|
+
chiliLog(result);
|
|
178
179
|
});
|
|
179
180
|
contextCommand
|
|
180
181
|
.command("set")
|
|
@@ -7,6 +7,7 @@ import { errorStack } from "@fnndsc/cumin";
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { BaseController } from "./baseController.js";
|
|
9
9
|
import { files_getGroup, files_getSingle, files_share as salsaFiles_share, fileContent_get } from "@fnndsc/salsa";
|
|
10
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
10
11
|
/**
|
|
11
12
|
* Controller for managing ChRIS file system resources (files, links, directories).
|
|
12
13
|
* Extends BaseController to provide file-specific functionality.
|
|
@@ -62,7 +63,7 @@ export class FileController extends BaseController {
|
|
|
62
63
|
async files_share(options) {
|
|
63
64
|
const fileId = options.fileId; // Assuming CLIoptions has a fileId property
|
|
64
65
|
if (fileId === undefined) {
|
|
65
|
-
|
|
66
|
+
chiliErrLog("Error: fileId is required for sharing.");
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
const shareOptions = { ...options }; // Pass all CLI options as share options for now
|
|
@@ -78,10 +79,10 @@ export class FileController extends BaseController {
|
|
|
78
79
|
const result = await fileContent_get(filePath);
|
|
79
80
|
if (!result.ok) {
|
|
80
81
|
const error = errorStack.stack_pop();
|
|
81
|
-
|
|
82
|
+
chiliErrLog(chalk.red(`Error viewing file: ${error?.message || 'Unknown error'}`));
|
|
82
83
|
return;
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
+
chiliLog(result.value);
|
|
85
86
|
}
|
|
86
87
|
/**
|
|
87
88
|
* Gets the current path associated with this controller.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { ChRISPluginGroup, dictionary_fromCLI } from "@fnndsc/cumin";
|
|
7
7
|
import { BaseController } from "./baseController.js";
|
|
8
8
|
import { plugin_run as salsaPlugin_run, plugins_searchableToIDs as salsaPlugins_searchableToIDs, pluginMeta_readmeContentFetch, pluginMeta_documentationUrlGet, pluginMeta_pluginIDFromSearch } from "@fnndsc/salsa";
|
|
9
|
+
import { chiliErrLog } from "../screen/output.js";
|
|
9
10
|
/**
|
|
10
11
|
* Controller for managing ChRIS plugins.
|
|
11
12
|
* Handles plugin searching, running, and group operations.
|
|
@@ -54,7 +55,7 @@ export class PluginController extends BaseController {
|
|
|
54
55
|
parsedParams = dictionary_fromCLI(params);
|
|
55
56
|
}
|
|
56
57
|
catch (e) {
|
|
57
|
-
|
|
58
|
+
chiliErrLog("Error parsing plugin parameters:", e);
|
|
58
59
|
return null;
|
|
59
60
|
}
|
|
60
61
|
return await salsaPlugin_run(searchable, parsedParams);
|
|
@@ -9,6 +9,7 @@ import { feeds_searchByTerm, feed_deleteById } from "../commands/feeds/delete.js
|
|
|
9
9
|
import { prompt_confirm } from "../utils/ui.js";
|
|
10
10
|
import { feed_create as feed_create_command } from "../commands/feed/create.js"; // Original name
|
|
11
11
|
import { feedCreate_render } from "../views/feed.js"; // Still needed for feedCreate_render
|
|
12
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
12
13
|
// import { FeedListResult } from "../commands/feeds/list.js"; // No longer needed
|
|
13
14
|
/**
|
|
14
15
|
* Handles commands related to groups of ChRIS feeds.
|
|
@@ -25,10 +26,10 @@ export class FeedGroupHandler {
|
|
|
25
26
|
// async feeds_list(options: CLIoptions): Promise<void> {
|
|
26
27
|
// try {
|
|
27
28
|
// const { feeds, selectedFields }: FeedListResult = await feeds_fetchList(options);
|
|
28
|
-
//
|
|
29
|
+
// chiliLog(feedList_render(feeds, selectedFields, { table: options.table, csv: options.csv }));
|
|
29
30
|
// } catch (error: unknown) {
|
|
30
31
|
// const msg = error instanceof Error ? error.message : String(error);
|
|
31
|
-
//
|
|
32
|
+
// chiliErrLog(chalk.red(`Error listing feeds: ${msg}`));
|
|
32
33
|
// }
|
|
33
34
|
// }
|
|
34
35
|
/**
|
|
@@ -41,11 +42,11 @@ export class FeedGroupHandler {
|
|
|
41
42
|
table_display(fields, ["fields"]);
|
|
42
43
|
}
|
|
43
44
|
else {
|
|
44
|
-
|
|
45
|
+
chiliLog(`No resource fields found for ${this.assetName}.`);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
catch (error) {
|
|
48
|
-
|
|
49
|
+
chiliLog(errorStack.stack_search(this.assetName)[0]);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
@@ -59,18 +60,18 @@ export class FeedGroupHandler {
|
|
|
59
60
|
for (const searchPart of searchParts) {
|
|
60
61
|
const feedIds = await this.baseGroupHandler.IDs_getFromSearch({ search: searchPart });
|
|
61
62
|
if (!feedIds || feedIds.length === 0) {
|
|
62
|
-
|
|
63
|
+
chiliLog(`No feeds found matching: ${searchPart}`);
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
65
66
|
for (const feedId of feedIds) {
|
|
66
|
-
|
|
67
|
+
chiliLog(`Sharing feed ID: ${feedId}...`);
|
|
67
68
|
const shareOptions = { is_public: options.is_public === true };
|
|
68
69
|
const success = await feed_shareById(Number(feedId), shareOptions);
|
|
69
70
|
if (success) {
|
|
70
|
-
|
|
71
|
+
chiliLog(`Feed ID ${feedId} shared successfully.`);
|
|
71
72
|
}
|
|
72
73
|
else {
|
|
73
|
-
|
|
74
|
+
chiliErrLog(`Failed to share feed ID ${feedId}.`);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -85,11 +86,11 @@ export class FeedGroupHandler {
|
|
|
85
86
|
for (const searchPart of searchParts) {
|
|
86
87
|
const items = await feeds_searchByTerm(searchPart);
|
|
87
88
|
if (items.length === 0) {
|
|
88
|
-
|
|
89
|
+
chiliLog(`No feeds found matching: ${searchPart}`);
|
|
89
90
|
continue;
|
|
90
91
|
}
|
|
91
92
|
for (const item of items) {
|
|
92
|
-
|
|
93
|
+
chiliLog(`Preparing to delete Feed: ID=${item.id}, Name=${item.name}`);
|
|
93
94
|
if (!options.force) {
|
|
94
95
|
const confirmed = await prompt_confirm(`Are you sure you want to delete feed ${item.name} (ID: ${item.id})?`);
|
|
95
96
|
if (!confirmed)
|
|
@@ -98,10 +99,10 @@ export class FeedGroupHandler {
|
|
|
98
99
|
const id = typeof item.id === "number" ? item.id : Number(item.id);
|
|
99
100
|
const success = await feed_deleteById(id);
|
|
100
101
|
if (success) {
|
|
101
|
-
|
|
102
|
+
chiliLog(`Deleted feed ${item.id}`);
|
|
102
103
|
}
|
|
103
104
|
else {
|
|
104
|
-
|
|
105
|
+
chiliErrLog(`Failed to delete feed ${item.id}`);
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
}
|
|
@@ -160,20 +161,20 @@ export class FeedMemberHandler {
|
|
|
160
161
|
try {
|
|
161
162
|
const feed = await feed_create_command(options);
|
|
162
163
|
if (feed) {
|
|
163
|
-
|
|
164
|
+
chiliLog(feedCreate_render(feed));
|
|
164
165
|
return feed;
|
|
165
166
|
}
|
|
166
|
-
|
|
167
|
+
chiliErrLog(chalk.red("Feed creation returned null result."));
|
|
167
168
|
const errors = errorStack.allOfType_get('error'); // Keep error reporting
|
|
168
169
|
if (errors.length > 0) {
|
|
169
|
-
|
|
170
|
-
errors.forEach(e =>
|
|
170
|
+
chiliErrLog(chalk.red('Errors:'));
|
|
171
|
+
errors.forEach(e => chiliErrLog(chalk.red(` - ${e}`)));
|
|
171
172
|
}
|
|
172
173
|
return null;
|
|
173
174
|
}
|
|
174
175
|
catch (error) {
|
|
175
176
|
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
-
|
|
177
|
+
chiliErrLog(chalk.red(`Error: ${message}`));
|
|
177
178
|
return null;
|
|
178
179
|
}
|
|
179
180
|
}
|
|
@@ -197,7 +198,7 @@ export class FeedMemberHandler {
|
|
|
197
198
|
});
|
|
198
199
|
}
|
|
199
200
|
else {
|
|
200
|
-
|
|
201
|
+
chiliErrLog(`Failed to find '${this.assetName}' command. The 'new' subcommand was not added.`);
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
204
|
}
|
|
@@ -12,6 +12,7 @@ import { files_searchByTerm, files_deleteById } from "../commands/files/delete.j
|
|
|
12
12
|
import { prompt_confirm } from "../utils/ui.js";
|
|
13
13
|
import { files_viewContent } from "../commands/file/view.js";
|
|
14
14
|
import { fileList_render } from "../views/file.js";
|
|
15
|
+
import { chiliErrLog, chiliLog } from "../screen/output.js";
|
|
15
16
|
/**
|
|
16
17
|
* Handles commands related to groups of ChRIS files, links, or directories.
|
|
17
18
|
*/
|
|
@@ -49,33 +50,33 @@ export class FileGroupHandler {
|
|
|
49
50
|
try {
|
|
50
51
|
const results = await files_fetchList(options, this.assetName, path);
|
|
51
52
|
if (!results) {
|
|
52
|
-
|
|
53
|
+
chiliErrLog(`No ${this.assetName} resources found. Perhaps check your current context?`);
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
55
56
|
if (results.tableData.length === 0) {
|
|
56
|
-
|
|
57
|
+
chiliLog(`No ${this.assetName} found matching the criteria.`);
|
|
57
58
|
}
|
|
58
59
|
else {
|
|
59
60
|
const uniqueResults = resourceColumns_removeDuplicates(results);
|
|
60
61
|
// cumin returns dynamic table rows (Record<string, unknown>[]);
|
|
61
62
|
// narrow to the FileResource view model at this boundary.
|
|
62
|
-
|
|
63
|
+
chiliLog(fileList_render(uniqueResults.tableData, uniqueResults.selectedFields, { table: options.table, csv: options.csv }));
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
catch (error) {
|
|
66
67
|
const errors = errorStack.stack_search(this.assetName);
|
|
67
68
|
if (errors.length > 0) {
|
|
68
|
-
|
|
69
|
+
chiliLog(errors[0]);
|
|
69
70
|
}
|
|
70
71
|
else {
|
|
71
72
|
const msg = error instanceof Error ? error.message : String(error);
|
|
72
|
-
|
|
73
|
+
chiliErrLog(`Error: ${msg}`);
|
|
73
74
|
const errObj = error;
|
|
74
75
|
if (msg.includes("Internal server error") || (errObj && errObj.response && errObj.response.status === 500)) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
chiliErrLog(chalk.yellow("\nHint: This indicates a problem on the ChRIS server (CUBE)."));
|
|
77
|
+
chiliErrLog(chalk.yellow(" Please contact your system administrator or check the CUBE logs."));
|
|
78
|
+
chiliErrLog(chalk.yellow(" To see full debug output, run the command again with CHILI_DEBUG=true:"));
|
|
79
|
+
chiliErrLog(chalk.yellow(" CHILI_DEBUG=true chili ..."));
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
}
|
|
@@ -90,11 +91,11 @@ export class FileGroupHandler {
|
|
|
90
91
|
table_display(fields, ["fields"]);
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
|
-
|
|
94
|
+
chiliLog(`No resource fields found for ${this.assetName}.`);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
catch (error) {
|
|
97
|
-
|
|
98
|
+
chiliLog(errorStack.stack_search(this.assetName)[0]);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
/**
|
|
@@ -105,16 +106,16 @@ export class FileGroupHandler {
|
|
|
105
106
|
for (const searchPart of searchParts) {
|
|
106
107
|
const items = await files_searchByTerm(searchPart, this.assetName);
|
|
107
108
|
if (items.length === 0) {
|
|
108
|
-
|
|
109
|
+
chiliLog(`No ${this.assetName} found matching: ${searchPart}`);
|
|
109
110
|
continue;
|
|
110
111
|
}
|
|
111
112
|
for (const item of items) {
|
|
112
113
|
const displayName = item.fname || item.path || item.id;
|
|
113
114
|
if (!item.id) {
|
|
114
|
-
|
|
115
|
+
chiliErrLog(`Cannot delete item without ID. Details: ${JSON.stringify(item)}`);
|
|
115
116
|
continue;
|
|
116
117
|
}
|
|
117
|
-
|
|
118
|
+
chiliLog(`Preparing to delete ${this.assetName}: ID=${item.id}, Name=${displayName}`);
|
|
118
119
|
if (!options.force) {
|
|
119
120
|
const confirmed = await prompt_confirm(`Are you sure you want to delete ${this.assetName} ${displayName} (ID: ${item.id})?`);
|
|
120
121
|
if (!confirmed)
|
|
@@ -122,10 +123,10 @@ export class FileGroupHandler {
|
|
|
122
123
|
}
|
|
123
124
|
const success = await files_deleteById(item.id, this.assetName);
|
|
124
125
|
if (success) {
|
|
125
|
-
|
|
126
|
+
chiliLog(`Deleted ${this.assetName} ${item.id}`);
|
|
126
127
|
}
|
|
127
128
|
else {
|
|
128
|
-
|
|
129
|
+
chiliErrLog(`Failed to delete ${this.assetName} ${item.id}`);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
}
|
|
@@ -137,18 +138,18 @@ export class FileGroupHandler {
|
|
|
137
138
|
*/
|
|
138
139
|
async files_share(options) {
|
|
139
140
|
try {
|
|
140
|
-
|
|
141
|
+
chiliLog(`Sharing ${this.assetName} from ${this.controller.path_get}...`);
|
|
141
142
|
if (options.force) {
|
|
142
|
-
|
|
143
|
+
chiliLog("Force sharing enabled");
|
|
143
144
|
}
|
|
144
145
|
await this.controller.files_share(options);
|
|
145
146
|
}
|
|
146
147
|
catch (error) {
|
|
147
148
|
if (error instanceof Error) {
|
|
148
|
-
|
|
149
|
+
chiliErrLog(`Error sharing ${this.assetName}: ${error.message}`);
|
|
149
150
|
}
|
|
150
151
|
else {
|
|
151
|
-
|
|
152
|
+
chiliErrLog(`An unknown error occurred while sharing the ${this.assetName}`);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -233,14 +234,14 @@ export class FileMemberHandler {
|
|
|
233
234
|
const success = await files_create(fileIdentifier, options);
|
|
234
235
|
if (success) {
|
|
235
236
|
const resolvedChRISPath = await path_resolveChrisFs(fileIdentifier, options);
|
|
236
|
-
|
|
237
|
+
chiliLog(`File created successfully at: ${resolvedChRISPath}`);
|
|
237
238
|
}
|
|
238
239
|
// If success is false, files_create would have thrown an error which is caught below.
|
|
239
240
|
}
|
|
240
241
|
catch (error) {
|
|
241
242
|
// Log the error from files_create
|
|
242
243
|
const message = error instanceof Error ? error.message : String(error);
|
|
243
|
-
|
|
244
|
+
chiliErrLog(message);
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
/**
|
|
@@ -274,7 +275,7 @@ export class FileMemberHandler {
|
|
|
274
275
|
.action(async (options) => {
|
|
275
276
|
await this.file_cat(options);
|
|
276
277
|
});
|
|
277
|
-
|
|
278
|
+
chiliLog("FileMemberHandler commands set up successfully");
|
|
278
279
|
}
|
|
279
280
|
/**
|
|
280
281
|
* Displays the content of the file.
|
|
@@ -284,18 +285,18 @@ export class FileMemberHandler {
|
|
|
284
285
|
async file_cat(options) {
|
|
285
286
|
try {
|
|
286
287
|
const path = this.controller.path_get;
|
|
287
|
-
|
|
288
|
+
chiliLog(`Viewing file at ${path}`);
|
|
288
289
|
const content = await files_viewContent(path);
|
|
289
290
|
if (content !== null) {
|
|
290
|
-
|
|
291
|
+
chiliLog(content);
|
|
291
292
|
}
|
|
292
293
|
else {
|
|
293
|
-
|
|
294
|
+
chiliErrLog("Failed to view file content (empty or error).");
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
catch (error) {
|
|
297
298
|
const message = error instanceof Error ? error.message : String(error);
|
|
298
|
-
|
|
299
|
+
chiliErrLog(`Error viewing file: ${message}`);
|
|
299
300
|
}
|
|
300
301
|
}
|
|
301
302
|
}
|