@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.
Files changed (40) hide show
  1. package/dist/chefs/chefs.js +12 -11
  2. package/dist/commands/fs/download.d.ts +19 -4
  3. package/dist/commands/fs/download.js +75 -65
  4. package/dist/commands/fs/upload.d.ts +20 -4
  5. package/dist/commands/fs/upload.js +46 -34
  6. package/dist/commands/man/doc.js +3 -2
  7. package/dist/commands/plugins/add.js +35 -34
  8. package/dist/config/colorConfig.js +2 -1
  9. package/dist/connect/connectHandler.js +7 -6
  10. package/dist/context/contextCommand.js +3 -2
  11. package/dist/controllers/fileController.js +4 -3
  12. package/dist/controllers/pluginController.js +2 -1
  13. package/dist/feeds/feedHandler.js +19 -18
  14. package/dist/filesystem/fileGroupHandler.js +28 -27
  15. package/dist/filesystem/filesystemHandler.js +13 -11
  16. package/dist/filesystem/inodeCommand.js +2 -1
  17. package/dist/handlers/baseGroupHandler.d.ts +10 -1
  18. package/dist/handlers/baseGroupHandler.js +38 -13
  19. package/dist/index.js +3 -2
  20. package/dist/lfs/lfs.js +6 -4
  21. package/dist/man/man.js +4 -3
  22. package/dist/man/renderer.js +3 -2
  23. package/dist/pacs/pacsQueryHandler.js +14 -13
  24. package/dist/pacs/pacsRetrieveHandler.js +13 -12
  25. package/dist/path/pathCommand.js +23 -22
  26. package/dist/plugins/pluginGroupHandler.d.ts +18 -0
  27. package/dist/plugins/pluginGroupHandler.js +44 -4
  28. package/dist/plugins/pluginHandler.js +23 -22
  29. package/dist/run.d.ts +13 -0
  30. package/dist/run.js +28 -13
  31. package/dist/screen/output.d.ts +88 -0
  32. package/dist/screen/output.js +95 -0
  33. package/dist/screen/screen.d.ts +13 -0
  34. package/dist/screen/screen.js +53 -8
  35. package/dist/utils/admin_prompt.js +10 -9
  36. package/dist/utils/docker.js +10 -9
  37. package/dist/views/pluginParameters.d.ts +10 -0
  38. package/dist/views/pluginParameters.js +25 -17
  39. package/docs/pacs.adoc +3 -3
  40. 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) => console.error(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
- console.log(`Using compute resources: ${names.join(', ')}`);
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
- console.log(`Detected input format: ${detected.format}`);
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
- console.log('\n=== Phase 1: Checking current CUBE ===');
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
- console.log('Plugin not found in current CUBE.');
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
- console.log('\n=== Phase 2: Searching peer stores ===');
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
- console.log(`Found plugin in peer store: ${peerResult.storeName}`);
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
- console.log('Plugin not found in peer stores.');
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
- console.log('\n=== Phase 3: Docker extraction ===');
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
- console.error('Store URL import not yet fully supported.');
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
- console.log('Importing plugin from peer store...');
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
- console.log(`Plugin '${result.plugin?.name}' registered successfully.`);
113
+ chiliLog(`Plugin '${result.plugin?.name}' registered successfully.`);
113
114
  return true;
114
115
  }
115
116
  if (!result.requiresAuth) {
116
- console.error(result.errorMessage || 'Failed to import plugin from store.');
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
- console.error('Failed to import plugin from store (authentication failed or rejected).');
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
- console.error('Errors:');
129
- errors.forEach((e) => console.error(`- ${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
- console.error('Warnings:');
134
- warnings.forEach((e) => console.error(`- ${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
- console.log('Extracting plugin descriptor from image...');
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
- console.error(msg);
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
- console.log('Registering plugin with ChRIS CUBE...');
173
+ chiliLog('Registering plugin with ChRIS CUBE...');
173
174
  const registered = await plugin_registerWithAdmin(pluginData, computeResources, initialCreds);
174
175
  if (registered) {
175
- console.log(`Plugin '${registered.name}' registered successfully.`);
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
- console.error('Failed to register plugin.');
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
- console.log(`Trying ${method.name}...`);
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
- console.log(`Successfully extracted plugin descriptor using ${method.name}`);
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
- console.error(msg);
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
- console.log(` Running: ${cmd[0]} --json`);
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
- console.log(`Inferred plugin name: ${pluginData.name}`);
323
+ chiliLog(`Inferred plugin name: ${pluginData.name}`);
323
324
  }
324
325
  if (!pluginData.dock_image) {
325
326
  pluginData.dock_image = image;
326
- console.log(`Inferred dock_image: ${pluginData.dock_image}`);
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
- console.log(`Using provided public_repo: ${pluginData.public_repo}`);
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
- console.log(`Inferred public_repo: ${pluginData.public_repo}`);
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
- console.log('Authentication cancelled.');
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
- console.log('Authentication failed.');
370
+ chiliLog('Authentication failed.');
370
371
  }
371
372
  }
372
- console.error(`Authentication failed after ${MAX_ATTEMPTS} attempts.`);
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
- console.error(`Warning: Could not load color config: ${msg}`);
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
- console.log(login_render(success, url, user));
27
+ chiliLog(login_render(success, url, user));
27
28
  }
28
29
  catch (error) {
29
- console.log(login_render(false, url, user));
30
+ chiliLog(login_render(false, url, user));
30
31
  const msg = error instanceof Error ? error.message : String(error);
31
- console.error(chalk.red(`Error: ${msg}`));
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
- console.log(logout_render(true));
41
+ chiliLog(logout_render(true));
41
42
  }
42
43
  catch (error) {
43
- console.log(logout_render(false));
44
+ chiliLog(logout_render(false));
44
45
  const msg = error instanceof Error ? error.message : String(error);
45
- console.error(chalk.red(`Error: ${msg}`));
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
- console.log(screen.table_output(tableData, {
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
- console.log(result);
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
- console.error("Error: fileId is required for sharing.");
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
- console.error(chalk.red(`Error viewing file: ${error?.message || 'Unknown error'}`));
82
+ chiliErrLog(chalk.red(`Error viewing file: ${error?.message || 'Unknown error'}`));
82
83
  return;
83
84
  }
84
- console.log(result.value);
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
- console.error("Error parsing plugin parameters:", e);
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
- // console.log(feedList_render(feeds, selectedFields, { table: options.table, csv: options.csv }));
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
- // console.error(chalk.red(`Error listing feeds: ${msg}`));
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
- console.log(`No resource fields found for ${this.assetName}.`);
45
+ chiliLog(`No resource fields found for ${this.assetName}.`);
45
46
  }
46
47
  }
47
48
  catch (error) {
48
- console.log(errorStack.stack_search(this.assetName)[0]);
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
- console.log(`No feeds found matching: ${searchPart}`);
63
+ chiliLog(`No feeds found matching: ${searchPart}`);
63
64
  continue;
64
65
  }
65
66
  for (const feedId of feedIds) {
66
- console.log(`Sharing feed ID: ${feedId}...`);
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
- console.log(`Feed ID ${feedId} shared successfully.`);
71
+ chiliLog(`Feed ID ${feedId} shared successfully.`);
71
72
  }
72
73
  else {
73
- console.error(`Failed to share feed ID ${feedId}.`);
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
- console.log(`No feeds found matching: ${searchPart}`);
89
+ chiliLog(`No feeds found matching: ${searchPart}`);
89
90
  continue;
90
91
  }
91
92
  for (const item of items) {
92
- console.log(`Preparing to delete Feed: ID=${item.id}, Name=${item.name}`);
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
- console.log(`Deleted feed ${item.id}`);
102
+ chiliLog(`Deleted feed ${item.id}`);
102
103
  }
103
104
  else {
104
- console.error(`Failed to delete feed ${item.id}`);
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
- console.log(feedCreate_render(feed));
164
+ chiliLog(feedCreate_render(feed));
164
165
  return feed;
165
166
  }
166
- console.error(chalk.red("Feed creation returned null result."));
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
- console.error(chalk.red('Errors:'));
170
- errors.forEach(e => console.error(chalk.red(` - ${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
- console.error(chalk.red(`Error: ${message}`));
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
- console.error(`Failed to find '${this.assetName}' command. The 'new' subcommand was not added.`);
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
- console.error(`No ${this.assetName} resources found. Perhaps check your current context?`);
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
- console.log(`No ${this.assetName} found matching the criteria.`);
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
- console.log(fileList_render(uniqueResults.tableData, uniqueResults.selectedFields, { table: options.table, csv: options.csv }));
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
- console.log(errors[0]);
69
+ chiliLog(errors[0]);
69
70
  }
70
71
  else {
71
72
  const msg = error instanceof Error ? error.message : String(error);
72
- console.error(`Error: ${msg}`);
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
- console.error(chalk.yellow("\nHint: This indicates a problem on the ChRIS server (CUBE)."));
76
- console.error(chalk.yellow(" Please contact your system administrator or check the CUBE logs."));
77
- console.error(chalk.yellow(" To see full debug output, run the command again with CHILI_DEBUG=true:"));
78
- console.error(chalk.yellow(" CHILI_DEBUG=true chili ..."));
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
- console.log(`No resource fields found for ${this.assetName}.`);
94
+ chiliLog(`No resource fields found for ${this.assetName}.`);
94
95
  }
95
96
  }
96
97
  catch (error) {
97
- console.log(errorStack.stack_search(this.assetName)[0]);
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
- console.log(`No ${this.assetName} found matching: ${searchPart}`);
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
- console.error(`Cannot delete item without ID. Details: ${JSON.stringify(item)}`);
115
+ chiliErrLog(`Cannot delete item without ID. Details: ${JSON.stringify(item)}`);
115
116
  continue;
116
117
  }
117
- console.log(`Preparing to delete ${this.assetName}: ID=${item.id}, Name=${displayName}`);
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
- console.log(`Deleted ${this.assetName} ${item.id}`);
126
+ chiliLog(`Deleted ${this.assetName} ${item.id}`);
126
127
  }
127
128
  else {
128
- console.error(`Failed to delete ${this.assetName} ${item.id}`);
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
- console.log(`Sharing ${this.assetName} from ${this.controller.path_get}...`);
141
+ chiliLog(`Sharing ${this.assetName} from ${this.controller.path_get}...`);
141
142
  if (options.force) {
142
- console.log("Force sharing enabled");
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
- console.error(`Error sharing ${this.assetName}: ${error.message}`);
149
+ chiliErrLog(`Error sharing ${this.assetName}: ${error.message}`);
149
150
  }
150
151
  else {
151
- console.error(`An unknown error occurred while sharing the ${this.assetName}`);
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
- console.log(`File created successfully at: ${resolvedChRISPath}`);
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
- console.error(message);
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
- console.log("FileMemberHandler commands set up successfully");
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
- console.log(`Viewing file at ${path}`);
288
+ chiliLog(`Viewing file at ${path}`);
288
289
  const content = await files_viewContent(path);
289
290
  if (content !== null) {
290
- console.log(content);
291
+ chiliLog(content);
291
292
  }
292
293
  else {
293
- console.error("Failed to view file content (empty or error).");
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
- console.error(`Error viewing file: ${message}`);
299
+ chiliErrLog(`Error viewing file: ${message}`);
299
300
  }
300
301
  }
301
302
  }