@fnndsc/chili 3.2.5

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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/chefs/chefs.d.ts +15 -0
  4. package/dist/chefs/chefs.js +195 -0
  5. package/dist/commands/compute/fields.d.ts +6 -0
  6. package/dist/commands/compute/fields.js +13 -0
  7. package/dist/commands/compute/list.d.ts +19 -0
  8. package/dist/commands/compute/list.js +20 -0
  9. package/dist/commands/connect/login.d.ts +16 -0
  10. package/dist/commands/connect/login.js +18 -0
  11. package/dist/commands/connect/logout.d.ts +6 -0
  12. package/dist/commands/connect/logout.js +16 -0
  13. package/dist/commands/feed/comments.d.ts +41 -0
  14. package/dist/commands/feed/comments.js +45 -0
  15. package/dist/commands/feed/create.d.ts +13 -0
  16. package/dist/commands/feed/create.js +40 -0
  17. package/dist/commands/feed/note.d.ts +21 -0
  18. package/dist/commands/feed/note.js +24 -0
  19. package/dist/commands/feeds/delete.d.ts +14 -0
  20. package/dist/commands/feeds/delete.js +34 -0
  21. package/dist/commands/feeds/fields.d.ts +6 -0
  22. package/dist/commands/feeds/fields.js +17 -0
  23. package/dist/commands/feeds/list.d.ts +17 -0
  24. package/dist/commands/feeds/list.js +38 -0
  25. package/dist/commands/feeds/share.d.ts +17 -0
  26. package/dist/commands/feeds/share.js +19 -0
  27. package/dist/commands/file/view.d.ts +8 -0
  28. package/dist/commands/file/view.js +25 -0
  29. package/dist/commands/files/delete.d.ts +16 -0
  30. package/dist/commands/files/delete.js +36 -0
  31. package/dist/commands/files/fields.d.ts +7 -0
  32. package/dist/commands/files/fields.js +18 -0
  33. package/dist/commands/files/list.d.ts +11 -0
  34. package/dist/commands/files/list.js +23 -0
  35. package/dist/commands/fs/cat.d.ts +15 -0
  36. package/dist/commands/fs/cat.js +26 -0
  37. package/dist/commands/fs/cp.d.ts +17 -0
  38. package/dist/commands/fs/cp.js +26 -0
  39. package/dist/commands/fs/create.d.ts +15 -0
  40. package/dist/commands/fs/create.js +83 -0
  41. package/dist/commands/fs/download.d.ts +25 -0
  42. package/dist/commands/fs/download.js +235 -0
  43. package/dist/commands/fs/edit.d.ts +16 -0
  44. package/dist/commands/fs/edit.js +25 -0
  45. package/dist/commands/fs/ls.d.ts +18 -0
  46. package/dist/commands/fs/ls.js +142 -0
  47. package/dist/commands/fs/mkdir.d.ts +8 -0
  48. package/dist/commands/fs/mkdir.js +21 -0
  49. package/dist/commands/fs/mv.d.ts +13 -0
  50. package/dist/commands/fs/mv.js +73 -0
  51. package/dist/commands/fs/rm.d.ts +24 -0
  52. package/dist/commands/fs/rm.js +108 -0
  53. package/dist/commands/fs/touch.d.ts +16 -0
  54. package/dist/commands/fs/touch.js +62 -0
  55. package/dist/commands/fs/upload.d.ts +55 -0
  56. package/dist/commands/fs/upload.js +221 -0
  57. package/dist/commands/groups/fields.d.ts +6 -0
  58. package/dist/commands/groups/fields.js +13 -0
  59. package/dist/commands/groups/list.d.ts +16 -0
  60. package/dist/commands/groups/list.js +26 -0
  61. package/dist/commands/man/doc.d.ts +16 -0
  62. package/dist/commands/man/doc.js +32 -0
  63. package/dist/commands/man/topics.d.ts +6 -0
  64. package/dist/commands/man/topics.js +27 -0
  65. package/dist/commands/pipeline/fields.d.ts +6 -0
  66. package/dist/commands/pipeline/fields.js +13 -0
  67. package/dist/commands/pipeline/run.d.ts +18 -0
  68. package/dist/commands/pipeline/run.js +33 -0
  69. package/dist/commands/plugin/readme.d.ts +14 -0
  70. package/dist/commands/plugin/readme.js +49 -0
  71. package/dist/commands/plugin/run.d.ts +10 -0
  72. package/dist/commands/plugin/run.js +29 -0
  73. package/dist/commands/plugin/search.d.ts +7 -0
  74. package/dist/commands/plugin/search.js +18 -0
  75. package/dist/commands/plugininstances/fields.d.ts +6 -0
  76. package/dist/commands/plugininstances/fields.js +13 -0
  77. package/dist/commands/plugininstances/list.d.ts +16 -0
  78. package/dist/commands/plugininstances/list.js +26 -0
  79. package/dist/commands/pluginmetas/fields.d.ts +6 -0
  80. package/dist/commands/pluginmetas/fields.js +13 -0
  81. package/dist/commands/pluginmetas/list.d.ts +16 -0
  82. package/dist/commands/pluginmetas/list.js +26 -0
  83. package/dist/commands/plugins/add.d.ts +34 -0
  84. package/dist/commands/plugins/add.js +374 -0
  85. package/dist/commands/plugins/delete.d.ts +14 -0
  86. package/dist/commands/plugins/delete.js +34 -0
  87. package/dist/commands/plugins/fields.d.ts +6 -0
  88. package/dist/commands/plugins/fields.js +17 -0
  89. package/dist/commands/plugins/list.d.ts +17 -0
  90. package/dist/commands/plugins/list.js +40 -0
  91. package/dist/commands/plugins/overview.d.ts +6 -0
  92. package/dist/commands/plugins/overview.js +17 -0
  93. package/dist/commands/store/list.d.ts +23 -0
  94. package/dist/commands/store/list.js +48 -0
  95. package/dist/commands/tags/fields.d.ts +6 -0
  96. package/dist/commands/tags/fields.js +13 -0
  97. package/dist/commands/tags/list.d.ts +16 -0
  98. package/dist/commands/tags/list.js +26 -0
  99. package/dist/commands/workflows/fields.d.ts +6 -0
  100. package/dist/commands/workflows/fields.js +13 -0
  101. package/dist/commands/workflows/list.d.ts +16 -0
  102. package/dist/commands/workflows/list.js +26 -0
  103. package/dist/config/colorConfig.d.ts +56 -0
  104. package/dist/config/colorConfig.js +161 -0
  105. package/dist/connect/connectHandler.d.ts +15 -0
  106. package/dist/connect/connectHandler.js +48 -0
  107. package/dist/context/contextCommand.d.ts +13 -0
  108. package/dist/context/contextCommand.js +193 -0
  109. package/dist/controllers/baseController.d.ts +43 -0
  110. package/dist/controllers/baseController.js +67 -0
  111. package/dist/controllers/feedController.d.ts +35 -0
  112. package/dist/controllers/feedController.js +53 -0
  113. package/dist/controllers/fileController.d.ts +56 -0
  114. package/dist/controllers/fileController.js +93 -0
  115. package/dist/controllers/pluginContextController.d.ts +24 -0
  116. package/dist/controllers/pluginContextController.js +49 -0
  117. package/dist/controllers/pluginController.d.ts +70 -0
  118. package/dist/controllers/pluginController.js +98 -0
  119. package/dist/controllers/pluginMetaController.d.ts +41 -0
  120. package/dist/controllers/pluginMetaController.js +52 -0
  121. package/dist/feeds/feedHandler.d.ts +64 -0
  122. package/dist/feeds/feedHandler.js +203 -0
  123. package/dist/filesystem/fileGroupHandler.d.ts +92 -0
  124. package/dist/filesystem/fileGroupHandler.js +318 -0
  125. package/dist/filesystem/filesystemHandler.d.ts +12 -0
  126. package/dist/filesystem/filesystemHandler.js +135 -0
  127. package/dist/filesystem/inodeCommand.d.ts +12 -0
  128. package/dist/filesystem/inodeCommand.js +37 -0
  129. package/dist/handlers/baseGroupHandler.d.ts +103 -0
  130. package/dist/handlers/baseGroupHandler.js +342 -0
  131. package/dist/index.d.ts +7 -0
  132. package/dist/index.js +239 -0
  133. package/dist/lfs/lfs.d.ts +25 -0
  134. package/dist/lfs/lfs.js +74 -0
  135. package/dist/man/man.d.ts +15 -0
  136. package/dist/man/man.js +31 -0
  137. package/dist/man/renderer.d.ts +26 -0
  138. package/dist/man/renderer.js +187 -0
  139. package/dist/models/feed.d.ts +18 -0
  140. package/dist/models/feed.js +8 -0
  141. package/dist/models/listing.d.ts +33 -0
  142. package/dist/models/listing.js +9 -0
  143. package/dist/models/plugin.d.ts +27 -0
  144. package/dist/models/plugin.js +1 -0
  145. package/dist/models/resource.d.ts +39 -0
  146. package/dist/models/resource.js +9 -0
  147. package/dist/pacs/pacsQueryHandler.d.ts +55 -0
  148. package/dist/pacs/pacsQueryHandler.js +196 -0
  149. package/dist/pacs/pacsQueryPayload.d.ts +19 -0
  150. package/dist/pacs/pacsQueryPayload.js +44 -0
  151. package/dist/pacs/pacsResultRender.d.ts +20 -0
  152. package/dist/pacs/pacsResultRender.js +141 -0
  153. package/dist/pacs/pacsRetrieveHandler.d.ts +87 -0
  154. package/dist/pacs/pacsRetrieveHandler.js +272 -0
  155. package/dist/pacs/pacsServerHandler.d.ts +15 -0
  156. package/dist/pacs/pacsServerHandler.js +32 -0
  157. package/dist/path/pathCommand.d.ts +64 -0
  158. package/dist/path/pathCommand.js +805 -0
  159. package/dist/path/pathMapper.d.ts +189 -0
  160. package/dist/path/pathMapper.js +382 -0
  161. package/dist/plugins/pluginGroupHandler.d.ts +42 -0
  162. package/dist/plugins/pluginGroupHandler.js +90 -0
  163. package/dist/plugins/pluginHandler.d.ts +87 -0
  164. package/dist/plugins/pluginHandler.js +303 -0
  165. package/dist/plugins/pluginMetaHandler.d.ts +21 -0
  166. package/dist/plugins/pluginMetaHandler.js +27 -0
  167. package/dist/screen/screen.d.ts +214 -0
  168. package/dist/screen/screen.js +478 -0
  169. package/dist/utils/admin_prompt.d.ts +42 -0
  170. package/dist/utils/admin_prompt.js +105 -0
  171. package/dist/utils/cli.d.ts +67 -0
  172. package/dist/utils/cli.js +112 -0
  173. package/dist/utils/docker.d.ts +74 -0
  174. package/dist/utils/docker.js +155 -0
  175. package/dist/utils/input_format.d.ts +77 -0
  176. package/dist/utils/input_format.js +135 -0
  177. package/dist/utils/sort.d.ts +29 -0
  178. package/dist/utils/sort.js +63 -0
  179. package/dist/utils/ui.d.ts +7 -0
  180. package/dist/utils/ui.js +26 -0
  181. package/dist/utils.d.ts +15 -0
  182. package/dist/utils.js +14 -0
  183. package/dist/views/compute.d.ts +17 -0
  184. package/dist/views/compute.js +40 -0
  185. package/dist/views/connect.d.ts +13 -0
  186. package/dist/views/connect.js +35 -0
  187. package/dist/views/feed.d.ts +44 -0
  188. package/dist/views/feed.js +110 -0
  189. package/dist/views/file.d.ts +25 -0
  190. package/dist/views/file.js +71 -0
  191. package/dist/views/fs.d.ts +51 -0
  192. package/dist/views/fs.js +105 -0
  193. package/dist/views/ls.d.ts +37 -0
  194. package/dist/views/ls.js +190 -0
  195. package/dist/views/plugin.d.ts +28 -0
  196. package/dist/views/plugin.js +71 -0
  197. package/dist/views/pluginParameters.d.ts +17 -0
  198. package/dist/views/pluginParameters.js +79 -0
  199. package/docs/00_intro.adoc +25 -0
  200. package/docs/01_setup.adoc +63 -0
  201. package/docs/02_context.adoc +128 -0
  202. package/docs/03_searchable.adoc +134 -0
  203. package/docs/10_everyday_examples.adoc +9 -0
  204. package/docs/11_upload_data.adoc +146 -0
  205. package/docs/12_create_a_feed.adoc +9 -0
  206. package/docs/13_run_a_plugin.adoc +271 -0
  207. package/docs/20_plugins.adoc +16 -0
  208. package/docs/21_pluginReadme.adoc +123 -0
  209. package/docs/30_filesystem.adoc +93 -0
  210. package/docs/31_fileops.adoc +51 -0
  211. package/docs/32_file_content.adoc +471 -0
  212. package/docs/_architecture.adoc +48 -0
  213. package/docs/_deepContext.adoc +84 -0
  214. package/docs/_roadmap.adoc +42 -0
  215. package/docs/_searchable.adoc +187 -0
  216. package/docs/_state.adoc +68 -0
  217. package/docs/pacs.adoc +101 -0
  218. package/docs/pathMapper.md +246 -0
  219. package/package.json +92 -0
@@ -0,0 +1,374 @@
1
+ /**
2
+ * @file Implements the logic for adding (registering) new ChRIS plugins.
3
+ *
4
+ * This module orchestrates a three-phase plugin registration strategy:
5
+ * 1. Check if plugin exists in current CUBE → assign to compute resources
6
+ * 2. Search peer stores → import from store if found
7
+ * 3. Docker extraction → pull image, extract JSON, register
8
+ *
9
+ * @module
10
+ */
11
+ import { docker_checkAvailability, shellCommand_runWithDetails, docker_pullImage, docker_getImageCmd, } from '../../utils/docker.js';
12
+ import { plugin_registerWithAdmin, plugin_checkExists, plugins_searchPeers, plugin_importFromStore, } from '@fnndsc/salsa';
13
+ import { computeResources_validate, computeResourceNames_parse, computeResources_getAll, errorStack, } from '@fnndsc/cumin';
14
+ import path from 'path';
15
+ import { input_detectFormat, PluginInputFormat, } from '../../utils/input_format.js';
16
+ import { adminCredentials_prompt, } from '../../utils/admin_prompt.js';
17
+ async function computeResources_resolve(options) {
18
+ if (options.compute) {
19
+ const names = computeResourceNames_parse(options.compute);
20
+ const validationResult = await computeResources_validate(names);
21
+ if (!validationResult.ok) {
22
+ const errors = errorStack.allOfType_get('error');
23
+ errors.forEach((err) => console.error(err));
24
+ return { ok: false };
25
+ }
26
+ return { ok: true, value: names };
27
+ }
28
+ const allResult = await computeResources_getAll();
29
+ if (allResult.ok && allResult.value.length > 0) {
30
+ const names = allResult.value.map((r) => r.name);
31
+ console.log(`Using compute resources: ${names.join(', ')}`);
32
+ return { ok: true, value: names };
33
+ }
34
+ return { ok: true, value: ['host'] };
35
+ }
36
+ /**
37
+ * Adds a plugin to the ChRIS store from the given input.
38
+ *
39
+ * @param input - Plugin source (image, URL, or path).
40
+ * @param options - Plugin add options.
41
+ * @returns The add outcome.
42
+ */
43
+ export async function plugin_add(input, options) {
44
+ const detected = input_detectFormat(input);
45
+ console.log(`Detected input format: ${detected.format}`);
46
+ const resourcesResult = await computeResources_resolve(options);
47
+ if (!resourcesResult.ok)
48
+ return 'failed';
49
+ const computeResources = resourcesResult.value;
50
+ // Phase 1: Check if plugin exists in current CUBE
51
+ console.log('\n=== Phase 1: Checking current CUBE ===');
52
+ const searchTerm = detected.format === PluginInputFormat.DOCKER_IMAGE
53
+ ? detected.pluginName || detected.value
54
+ : detected.value;
55
+ const existingPlugin = await plugin_checkExists(searchTerm);
56
+ if (existingPlugin) {
57
+ return 'already_exists';
58
+ }
59
+ console.log('Plugin not found in current CUBE.');
60
+ // Phase 2: Search peer stores (skip if input is store URL)
61
+ if (detected.format !== PluginInputFormat.STORE_URL) {
62
+ console.log('\n=== Phase 2: Searching peer stores ===');
63
+ const peerStoreUrls = options.store
64
+ ? [options.store]
65
+ : ['https://cube.chrisproject.org/api/v1/'];
66
+ const searchName = detected.format === PluginInputFormat.DOCKER_IMAGE
67
+ ? detected.pluginName // Should be present for docker images
68
+ : detected.value;
69
+ const searchVersion = detected.format === PluginInputFormat.DOCKER_IMAGE
70
+ ? detected.version
71
+ : undefined;
72
+ const peerResult = await plugins_searchPeers(searchName, searchVersion, peerStoreUrls);
73
+ if (peerResult) {
74
+ console.log(`Found plugin in peer store: ${peerResult.storeName}`);
75
+ const ok = await pluginFromStore_register(peerResult.plugin, computeResources, options);
76
+ return ok ? 'installed' : 'failed';
77
+ }
78
+ console.log('Plugin not found in peer stores.');
79
+ }
80
+ // Phase 3: Docker extraction (only for Docker images or plugin names)
81
+ if (detected.format !== PluginInputFormat.STORE_URL) {
82
+ console.log('\n=== Phase 3: Docker extraction ===');
83
+ const dockerImage = detected.format === PluginInputFormat.DOCKER_IMAGE
84
+ ? detected.value
85
+ : `${detected.value}:latest`; // Assume latest tag for plugin names
86
+ const ok = await pluginFromDocker_register(dockerImage, computeResources, options);
87
+ return ok ? 'installed' : 'failed';
88
+ }
89
+ // If we get here with a store URL, we couldn't process it
90
+ console.error('Store URL import not yet fully supported.');
91
+ return 'failed';
92
+ }
93
+ /**
94
+ * Registers a plugin from peer store data.
95
+ *
96
+ * Handles admin authentication with retry logic (up to 3 attempts).
97
+ *
98
+ * @param pluginData - Plugin data from peer store.
99
+ * @param computeResources - Compute resources to assign plugin to.
100
+ * @param options - CLI options including admin credentials.
101
+ * @returns Promise resolving to success boolean.
102
+ */
103
+ async function pluginFromStore_register(pluginData, computeResources, options) {
104
+ console.log('Importing plugin from peer store...');
105
+ const initialCreds = options.adminUser && options.adminPassword
106
+ ? { username: options.adminUser, password: options.adminPassword }
107
+ : undefined;
108
+ // Try with provided credentials (or current user)
109
+ const result = await plugin_importFromStore('', // Store URL not needed when we have plugin data
110
+ pluginData, computeResources, initialCreds);
111
+ if (result.success) {
112
+ console.log(`Plugin '${result.plugin?.name}' registered successfully.`);
113
+ return true;
114
+ }
115
+ if (!result.requiresAuth) {
116
+ console.error(result.errorMessage || 'Failed to import plugin from store.');
117
+ return false;
118
+ }
119
+ // Admin auth required - try with interactive credentials
120
+ const retrySuccess = await registrationWithAuth_retry(async (creds) => {
121
+ const retryResult = await plugin_importFromStore('', pluginData, computeResources, creds);
122
+ return retryResult.success;
123
+ }, options);
124
+ if (!retrySuccess) {
125
+ console.error('Failed to import plugin from store (authentication failed or rejected).');
126
+ const errors = errorStack.allOfType_get('error');
127
+ if (errors.length > 0) {
128
+ console.error('Errors:');
129
+ errors.forEach((e) => console.error(`- ${e}`));
130
+ }
131
+ const warnings = errorStack.allOfType_get('warning');
132
+ if (warnings.length > 0) {
133
+ console.error('Warnings:');
134
+ warnings.forEach((e) => console.error(`- ${e}`));
135
+ }
136
+ }
137
+ return retrySuccess;
138
+ }
139
+ /**
140
+ * Registers a plugin from Docker image.
141
+ *
142
+ * Pulls image (or uses local), extracts JSON descriptor, and registers with admin API.
143
+ *
144
+ * @param image - Docker image name/tag.
145
+ * @param computeResources - Compute resources to assign plugin to.
146
+ * @param options - CLI options including admin credentials.
147
+ * @returns Promise resolving to success boolean.
148
+ */
149
+ async function pluginFromDocker_register(image, computeResources, options) {
150
+ if (!await docker_checkAvailability()) {
151
+ return false;
152
+ }
153
+ // Pull Docker image (checks local first)
154
+ const pulled = await docker_pullImage(image);
155
+ if (!pulled) {
156
+ return false;
157
+ }
158
+ console.log('Extracting plugin descriptor from image...');
159
+ const pluginData = await pluginJSON_extractFromImage(image, options);
160
+ if (!pluginData) {
161
+ const msg = 'Failed to extract plugin descriptor from image.';
162
+ console.error(msg);
163
+ errorStack.stack_push('error', msg);
164
+ return false;
165
+ }
166
+ // Infer missing fields
167
+ pluginData_inferMissingFields(pluginData, image, options);
168
+ const initialCreds = options.adminUser && options.adminPassword
169
+ ? { username: options.adminUser, password: options.adminPassword }
170
+ : undefined;
171
+ // Register with admin API
172
+ console.log('Registering plugin with ChRIS CUBE...');
173
+ const registered = await plugin_registerWithAdmin(pluginData, computeResources, initialCreds);
174
+ if (registered) {
175
+ console.log(`Plugin '${registered.name}' registered successfully.`);
176
+ return true;
177
+ }
178
+ // Check if failure was due to admin auth
179
+ const errors = errorStack.allOfType_get('error');
180
+ const authError = errors.some((e) => {
181
+ const lowerE = e.toLowerCase();
182
+ return lowerE.includes('unauthorized') ||
183
+ lowerE.includes('forbidden') ||
184
+ lowerE.includes('permission denied') ||
185
+ lowerE.includes('401') ||
186
+ lowerE.includes('403') ||
187
+ lowerE.includes('admin credentials required');
188
+ });
189
+ if (!authError) {
190
+ console.error('Failed to register plugin.');
191
+ return false;
192
+ }
193
+ // Retry with admin credentials
194
+ return await registrationWithAuth_retry(async (creds) => {
195
+ const retryResult = await plugin_registerWithAdmin(pluginData, computeResources, creds);
196
+ return retryResult !== null;
197
+ }, options);
198
+ }
199
+ /**
200
+ * Extracts plugin JSON descriptor from a Docker image.
201
+ *
202
+ * Tries three methods in order (matching chrisomatic behavior):
203
+ * 1. chris_plugin_info with arguments (post 0.3.0)
204
+ * 2. chris_plugin_info without arguments (pre 0.3.0)
205
+ * 3. <CMD> --json where CMD is extracted from image (old chrisapp)
206
+ *
207
+ * @param image - Docker image name/tag.
208
+ * @param options - Plugin add options (for inferring name/repo).
209
+ * @returns Promise resolving to plugin data or null.
210
+ */
211
+ async function pluginJSON_extractFromImage(image, options) {
212
+ const methods = [
213
+ {
214
+ name: 'chris_plugin_info (post v0.3.0)',
215
+ fn: async () => await pluginJSON_tryChrisPluginInfoWithArgs(image, options),
216
+ },
217
+ {
218
+ name: 'chris_plugin_info (pre v0.3.0)',
219
+ fn: async () => await pluginJSON_tryChrisPluginInfo(image),
220
+ },
221
+ {
222
+ name: 'CMD --json (old chrisapp)',
223
+ fn: async () => await pluginJSON_tryOldChrisapp(image),
224
+ },
225
+ ];
226
+ for (const method of methods) {
227
+ console.log(`Trying ${method.name}...`);
228
+ const jsonString = await method.fn();
229
+ if (jsonString && jsonString.trim() !== '') {
230
+ try {
231
+ const parsed = JSON.parse(jsonString);
232
+ console.log(`Successfully extracted plugin descriptor using ${method.name}`);
233
+ return parsed;
234
+ }
235
+ catch (e) {
236
+ const msg = `Failed to parse JSON from ${method.name}`;
237
+ console.error(msg);
238
+ errorStack.stack_push('error', msg);
239
+ }
240
+ }
241
+ }
242
+ const msg = 'All plugin descriptor extraction methods failed';
243
+ errorStack.stack_push('error', msg);
244
+ errorStack.stack_push('error', 'This plugin may not be a ChRIS plugin, or uses an unsupported format');
245
+ return null;
246
+ }
247
+ /**
248
+ * Tries chris_plugin_info with arguments (post 0.3.0 format).
249
+ *
250
+ * @param image - Docker image name/tag.
251
+ * @param options - Plugin options for inferring name/repo.
252
+ * @returns Promise resolving to JSON string or null.
253
+ */
254
+ async function pluginJSON_tryChrisPluginInfoWithArgs(image, options) {
255
+ const args = ['chris_plugin_info', '--dock-image', image];
256
+ if (options.public_repo) {
257
+ args.push('--public-repo', options.public_repo);
258
+ }
259
+ const command = `docker run --rm ${image} ${args.join(' ')}`;
260
+ const result = await shellCommand_runWithDetails(command);
261
+ if (!result.success || !result.stdout || result.stdout.trim() === '') {
262
+ if (result.error || result.stderr) {
263
+ errorStack.stack_push('warning', `chris_plugin_info (with args) failed: ${result.error || result.stderr}`);
264
+ }
265
+ return null;
266
+ }
267
+ return result.stdout;
268
+ }
269
+ /**
270
+ * Tries chris_plugin_info without arguments (pre 0.3.0 format).
271
+ *
272
+ * @param image - Docker image name/tag.
273
+ * @returns Promise resolving to JSON string or null.
274
+ */
275
+ async function pluginJSON_tryChrisPluginInfo(image) {
276
+ const command = `docker run --rm ${image} chris_plugin_info`;
277
+ const result = await shellCommand_runWithDetails(command);
278
+ if (!result.success || !result.stdout || result.stdout.trim() === '') {
279
+ if (result.error || result.stderr) {
280
+ errorStack.stack_push('warning', `chris_plugin_info failed: ${result.error || result.stderr}`);
281
+ }
282
+ return null;
283
+ }
284
+ return result.stdout;
285
+ }
286
+ /**
287
+ * Tries old chrisapp format by extracting CMD and running with --json.
288
+ *
289
+ * @param image - Docker image name/tag.
290
+ * @returns Promise resolving to JSON string or null.
291
+ */
292
+ async function pluginJSON_tryOldChrisapp(image) {
293
+ const cmd = await docker_getImageCmd(image);
294
+ if (cmd.length === 0) {
295
+ errorStack.stack_push('warning', 'Could not extract CMD from Docker image');
296
+ return null;
297
+ }
298
+ const command = `docker run --rm ${image} ${cmd[0]} --json`;
299
+ console.log(` Running: ${cmd[0]} --json`);
300
+ const result = await shellCommand_runWithDetails(command);
301
+ if (!result.success || !result.stdout || result.stdout.trim() === '') {
302
+ if (result.error || result.stderr) {
303
+ errorStack.stack_push('warning', `${cmd[0]} --json failed: ${result.error || result.stderr}`);
304
+ }
305
+ return null;
306
+ }
307
+ return result.stdout;
308
+ }
309
+ /**
310
+ * Infers missing fields in plugin data from image name and options.
311
+ *
312
+ * Modifies pluginData in place.
313
+ *
314
+ * @param pluginData - Plugin data object to modify.
315
+ * @param image - Docker image name/tag.
316
+ * @param options - CLI options.
317
+ */
318
+ function pluginData_inferMissingFields(pluginData, image, options) {
319
+ if (!pluginData.name) {
320
+ const imageNameWithoutTag = image.split(':')[0];
321
+ pluginData.name = path.basename(imageNameWithoutTag);
322
+ console.log(`Inferred plugin name: ${pluginData.name}`);
323
+ }
324
+ if (!pluginData.dock_image) {
325
+ pluginData.dock_image = image;
326
+ console.log(`Inferred dock_image: ${pluginData.dock_image}`);
327
+ }
328
+ if (!pluginData.public_repo && options.public_repo) {
329
+ pluginData.public_repo = options.public_repo;
330
+ console.log(`Using provided public_repo: ${pluginData.public_repo}`);
331
+ }
332
+ else if (!pluginData.public_repo && image.includes('/')) {
333
+ const repoGuess = image.split('/')[0] + '/' + path.basename(image.split(':')[0]);
334
+ pluginData.public_repo = `https://github.com/${repoGuess}`;
335
+ console.log(`Inferred public_repo: ${pluginData.public_repo}`);
336
+ }
337
+ }
338
+ /**
339
+ * Retries a registration operation with admin credential prompting.
340
+ *
341
+ * Allows up to 3 attempts. Uses provided credentials or prompts interactively.
342
+ *
343
+ * @param registrationFn - Function that attempts registration with credentials.
344
+ * @param options - CLI options including admin credentials.
345
+ * @returns Promise resolving to success boolean.
346
+ */
347
+ async function registrationWithAuth_retry(registrationFn, options) {
348
+ const MAX_ATTEMPTS = 3;
349
+ // If credentials provided via flags, try once
350
+ if (options.adminUser && options.adminPassword) {
351
+ const creds = {
352
+ username: options.adminUser,
353
+ password: options.adminPassword,
354
+ };
355
+ return await registrationFn(creds);
356
+ }
357
+ // Interactive prompting with retry
358
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
359
+ const creds = await adminCredentials_prompt(attempt, MAX_ATTEMPTS);
360
+ if (!creds) {
361
+ console.log('Authentication cancelled.');
362
+ return false;
363
+ }
364
+ const success = await registrationFn(creds);
365
+ if (success) {
366
+ return true;
367
+ }
368
+ if (attempt < MAX_ATTEMPTS) {
369
+ console.log('Authentication failed.');
370
+ }
371
+ }
372
+ console.error(`Authentication failed after ${MAX_ATTEMPTS} attempts.`);
373
+ return false;
374
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolves search terms to a list of plugin items.
3
+ *
4
+ * @param searchable - The search string.
5
+ * @returns A Promise resolving to an array of plugin items (table data).
6
+ */
7
+ export declare function plugins_searchByTerm(searchable: string): Promise<Record<string, unknown>[]>;
8
+ /**
9
+ * Deletes a plugin by its ID.
10
+ *
11
+ * @param id - The plugin ID.
12
+ * @returns A Promise resolving to `true` on success, `false` otherwise.
13
+ */
14
+ export declare function plugin_deleteById(id: number): Promise<boolean>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @file Implements the logic for searching and deleting ChRIS plugins.
3
+ *
4
+ * This module provides functionality to search for plugins by name/term
5
+ * and delete them by ID using the `@fnndsc/salsa` library.
6
+ *
7
+ * @module
8
+ */
9
+ import { plugins_list as salsaPlugins_list, plugin_delete as salsaPlugin_delete } from "@fnndsc/salsa";
10
+ import { options_toParams } from "../../utils/cli.js";
11
+ /**
12
+ * Resolves search terms to a list of plugin items.
13
+ *
14
+ * @param searchable - The search string.
15
+ * @returns A Promise resolving to an array of plugin items (table data).
16
+ */
17
+ export async function plugins_searchByTerm(searchable) {
18
+ const options = { search: searchable };
19
+ const params = options_toParams(options);
20
+ const results = await salsaPlugins_list(params);
21
+ if (!results || !results.tableData) {
22
+ return [];
23
+ }
24
+ return results.tableData;
25
+ }
26
+ /**
27
+ * Deletes a plugin by its ID.
28
+ *
29
+ * @param id - The plugin ID.
30
+ * @returns A Promise resolving to `true` on success, `false` otherwise.
31
+ */
32
+ export async function plugin_deleteById(id) {
33
+ return await salsaPlugin_delete(id);
34
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Fetches the list of available fields for plugins.
3
+ *
4
+ * @returns A Promise resolving to an array of field names, or `null` if retrieval fails.
5
+ */
6
+ export declare function pluginFields_fetch(): Promise<string[] | null>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file Implements the logic for retrieving available fields for ChRIS plugins.
3
+ *
4
+ * This module provides functionality to fetch the list of valid fields
5
+ * that can be displayed or queried for registered plugins.
6
+ *
7
+ * @module
8
+ */
9
+ import { pluginFields_get as salsaPluginFields_get } from "@fnndsc/salsa";
10
+ /**
11
+ * Fetches the list of available fields for plugins.
12
+ *
13
+ * @returns A Promise resolving to an array of field names, or `null` if retrieval fails.
14
+ */
15
+ export async function pluginFields_fetch() {
16
+ return await salsaPluginFields_get();
17
+ }
@@ -0,0 +1,17 @@
1
+ import { CLIoptions } from "../../utils/cli.js";
2
+ import { Plugin } from "../../models/plugin.js";
3
+ /**
4
+ * Result structure for plugin listing.
5
+ */
6
+ export interface PluginListResult {
7
+ plugins: Plugin[];
8
+ selectedFields: string[];
9
+ totalCount?: number;
10
+ }
11
+ /**
12
+ * Fetches a list of ChRIS plugins based on options.
13
+ *
14
+ * @param options - CLI options containing filtering/pagination parameters.
15
+ * @returns A Promise resolving to a `PluginListResult` object.
16
+ */
17
+ export declare function plugins_fetchList(options: CLIoptions): Promise<PluginListResult>;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file Implements the logic for listing ChRIS plugin resources.
3
+ *
4
+ * This module provides functionality to fetch lists of plugins
5
+ * from ChRIS using the `@fnndsc/salsa` library.
6
+ *
7
+ * @module
8
+ */
9
+ import { plugins_list as salsaPlugins_list, plugins_listAll as salsaPlugins_listAll } from "@fnndsc/salsa";
10
+ import { options_toParams } from "../../utils/cli.js";
11
+ import { list_applySort } from "../../utils/sort.js";
12
+ /**
13
+ * Fetches a list of ChRIS plugins based on options.
14
+ *
15
+ * @param options - CLI options containing filtering/pagination parameters.
16
+ * @returns A Promise resolving to a `PluginListResult` object.
17
+ */
18
+ export async function plugins_fetchList(options) {
19
+ const params = options_toParams(options);
20
+ let result;
21
+ if (options.all) {
22
+ result = await salsaPlugins_listAll(params);
23
+ }
24
+ else {
25
+ result = await salsaPlugins_list(params);
26
+ }
27
+ if (result && result.tableData) {
28
+ let plugins = result.tableData;
29
+ // Apply sorting if specified
30
+ if (options.sort) {
31
+ plugins = list_applySort(plugins, options.sort, options.reverse);
32
+ }
33
+ return {
34
+ plugins,
35
+ selectedFields: result.selectedFields || [],
36
+ totalCount: result.totalCount,
37
+ };
38
+ }
39
+ return { plugins: [], selectedFields: [] };
40
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Displays an overview of registered plugins.
3
+ *
4
+ * @returns A Promise resolving to `void`.
5
+ */
6
+ export declare function pluginsOverview_display(): Promise<void>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file Implements the logic for displaying a system overview of ChRIS plugins.
3
+ *
4
+ * This module provides functionality to print a summary or overview
5
+ * of the plugins available in the ChRIS system.
6
+ *
7
+ * @module
8
+ */
9
+ import { plugins_overview as salsaPlugins_overview } from "@fnndsc/salsa";
10
+ /**
11
+ * Displays an overview of registered plugins.
12
+ *
13
+ * @returns A Promise resolving to `void`.
14
+ */
15
+ export async function pluginsOverview_display() {
16
+ return await salsaPlugins_overview();
17
+ }
@@ -0,0 +1,23 @@
1
+ import { ListingItem } from '../../models/listing.js';
2
+ import { CLIoptions } from '../../utils/cli.js';
3
+ /**
4
+ * Options for listing store plugins.
5
+ */
6
+ export interface StoreOptions extends CLIoptions {
7
+ store?: string;
8
+ }
9
+ /**
10
+ * Lists plugins from the store.
11
+ *
12
+ * @param options - CLI options.
13
+ * @returns Promise resolving to ListingItem array.
14
+ */
15
+ export declare function store_listPlugins(options: StoreOptions): Promise<ListingItem[]>;
16
+ /**
17
+ * Searches plugins in the store.
18
+ *
19
+ * @param query - Search query.
20
+ * @param options - CLI options.
21
+ * @returns Promise resolving to ListingItem array.
22
+ */
23
+ export declare function store_searchPlugins(query: string, options: StoreOptions): Promise<ListingItem[]>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @file Store commands.
3
+ *
4
+ * Commands for interacting with the peer ChRIS store.
5
+ *
6
+ * @module
7
+ */
8
+ import { store_list, store_search } from '@fnndsc/salsa';
9
+ /**
10
+ * Maps raw store plugin data to ListingItem model.
11
+ */
12
+ function storeItem_map(plugin) {
13
+ const name = plugin.name || 'unknown';
14
+ const version = plugin.version || '';
15
+ // Format name as name-vVersion to match chell's ls /bin style
16
+ // This ensures consistent colorization (green name, dim green version)
17
+ const displayName = version ? `${name}-v${version}` : name;
18
+ return {
19
+ name: displayName,
20
+ type: 'plugin', // This ensures green color
21
+ size: 0,
22
+ owner: plugin.authors || 'store',
23
+ date: plugin.creation_date || '',
24
+ // Set version to undefined to prevent grid_render from appending " (version)"
25
+ version: undefined
26
+ };
27
+ }
28
+ /**
29
+ * Lists plugins from the store.
30
+ *
31
+ * @param options - CLI options.
32
+ * @returns Promise resolving to ListingItem array.
33
+ */
34
+ export async function store_listPlugins(options) {
35
+ const plugins = await store_list(options.store);
36
+ return plugins.map(storeItem_map);
37
+ }
38
+ /**
39
+ * Searches plugins in the store.
40
+ *
41
+ * @param query - Search query.
42
+ * @param options - CLI options.
43
+ * @returns Promise resolving to ListingItem array.
44
+ */
45
+ export async function store_searchPlugins(query, options) {
46
+ const plugins = await store_search(query, options.store);
47
+ return plugins.map(storeItem_map);
48
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Fetches the available tag field names.
3
+ *
4
+ * @returns The tag field names, or null on failure.
5
+ */
6
+ export declare function tagFields_fetch(): Promise<string[] | null>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @file Fetches available fields for tag resources.
3
+ * @module
4
+ */
5
+ import { tagFields_get } from '@fnndsc/salsa';
6
+ /**
7
+ * Fetches the available tag field names.
8
+ *
9
+ * @returns The tag field names, or null on failure.
10
+ */
11
+ export async function tagFields_fetch() {
12
+ return await tagFields_get();
13
+ }
@@ -0,0 +1,16 @@
1
+ import { CLIoptions } from '../../utils/cli.js';
2
+ /**
3
+ * Result of listing tags (table data plus selected fields).
4
+ */
5
+ export interface TagListResult {
6
+ tags: Record<string, unknown>[];
7
+ selectedFields: string[];
8
+ totalCount?: number;
9
+ }
10
+ /**
11
+ * Fetches a list of tags.
12
+ *
13
+ * @param options - List and filter options.
14
+ * @returns The tags list result.
15
+ */
16
+ export declare function tags_fetchList(options: CLIoptions): Promise<TagListResult>;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file Implements listing ChRIS tags.
3
+ * @module
4
+ */
5
+ import { tags_list as salsaTags_list, tags_listAll as salsaTags_listAll } from '@fnndsc/salsa';
6
+ import { options_toParams } from '../../utils/cli.js';
7
+ import { list_applySort } from '../../utils/sort.js';
8
+ /**
9
+ * Fetches a list of tags.
10
+ *
11
+ * @param options - List and filter options.
12
+ * @returns The tags list result.
13
+ */
14
+ export async function tags_fetchList(options) {
15
+ const params = options_toParams(options);
16
+ const result = options.all
17
+ ? await salsaTags_listAll(params)
18
+ : await salsaTags_list(params);
19
+ if (result && result.tableData) {
20
+ let tags = result.tableData;
21
+ if (options.sort)
22
+ tags = list_applySort(tags, options.sort, options.reverse);
23
+ return { tags, selectedFields: result.selectedFields || [], totalCount: result.totalCount };
24
+ }
25
+ return { tags: [], selectedFields: [] };
26
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Fetches the available workflow field names.
3
+ *
4
+ * @returns The workflow field names, or null on failure.
5
+ */
6
+ export declare function workflowFields_fetch(): Promise<string[] | null>;