@fnndsc/chili 3.2.5 → 3.4.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/index.js CHANGED
@@ -2,62 +2,18 @@
2
2
  /**
3
3
  * @file Command-line entry point for the chili (ChILI) CLI.
4
4
  *
5
- * @module
6
- */
7
- /**
8
- * Suppress DEP0169 warning from axios/proxy-from-env dependency.
9
- *
10
- * This warning originates from the `proxy-from-env` package (version 1.1.0)
11
- * used by axios for proxy configuration. The package uses the legacy `url.parse()`
12
- * API which is deprecated in favor of the WHATWG URL API.
5
+ * This is a thin wrapper: all command wiring and execution live in {@link run}
6
+ * (./run.js) so the same logic can be driven in-process by a host such as chell
7
+ * without spawning a separate `node` subprocess. This file only adds the
8
+ * bin-only concerns shell completion and fatal-exit handling.
13
9
  *
14
- * Why we suppress it:
15
- * - It's a transitive dependency (axios → proxy-from-env) we don't control
16
- * - proxy-from-env 1.1.0 is the latest version and hasn't been updated
17
- * - According to the warning: "CVEs are not issued for url.parse() vulnerabilities"
18
- * - The warning doesn't affect functionality, only console output
19
- * - This is a well-known issue in the Node.js ecosystem
20
- *
21
- * This can be removed once:
22
- * - proxy-from-env migrates to WHATWG URL API, OR
23
- * - axios replaces proxy-from-env with an alternative
24
- *
25
- * Tracking: https://github.com/Rob--W/proxy-from-env/issues/51
10
+ * @module
26
11
  */
27
- const originalEmitWarning = process.emitWarning;
28
- process.emitWarning = function (warning, ...args) {
29
- // Suppress only the specific DEP0169 warning
30
- if (typeof warning === 'string' &&
31
- (warning.includes('DEP0169') || warning.includes('url.parse()'))) {
32
- return;
33
- }
34
- // Allow all other warnings through
35
- return originalEmitWarning.call(process, warning, ...args);
36
- };
37
- import { Command } from "commander";
38
- import figlet from "figlet";
39
12
  import omelette from "omelette";
40
- import { connectCommand_setup } from "./connect/connectHandler.js";
41
- import { FeedGroupHandler, FeedMemberHandler } from "./feeds/feedHandler.js";
42
- import { PluginGroupHandler, PluginMemberHandler, } from "./plugins/pluginHandler.js";
43
- import { PluginMetaGroupHandler } from "./plugins/pluginMetaHandler.js";
44
- import { PluginContextGroupHandler } from "./plugins/pluginGroupHandler.js";
45
- import { inodeCommand_setup } from "./filesystem/inodeCommand.js";
46
- import { contextCommand_setup } from "./context/contextCommand.js";
47
- import { pathCommand_setup } from "./path/pathCommand.js";
48
- import { fileBrowserCommand_setup } from "./filesystem/filesystemHandler.js";
49
- import { manCommand_setup } from "./man/man.js";
50
- import { chefsCommand_setup } from "./chefs/chefs.js";
51
- import { PACSServerGroupHandler } from "./pacs/pacsServerHandler.js";
52
- import { PACSQueryGroupHandler } from "./pacs/pacsQueryHandler.js";
53
- import { PACSRetrieveGroupHandler } from "./pacs/pacsRetrieveHandler.js";
54
- import { chrisConnection_init, NodeStorageProvider, errorStack_getAllOfType } from "@fnndsc/cumin";
55
- import { FileGroupHandler } from "./filesystem/fileGroupHandler.js";
56
- const program = new Command();
57
- program.version("1.0.1").description("A CLI for ChRIS");
13
+ import { run } from "./run.js";
58
14
  /**
59
- * Sets up command completion for the ChILI CLI using omelette.
60
- * This provides auto-completion suggestions in the shell.
15
+ * Sets up shell command completion for the standalone chili CLI using omelette.
16
+ * This is a bin-only concern; in-process hosts provide their own completion.
61
17
  */
62
18
  function commandCompletion_setup() {
63
19
  const completion = omelette(`chili|chili`);
@@ -95,7 +51,6 @@ function commandCompletion_setup() {
95
51
  });
96
52
  completion.on("man.doc", ({ reply }) => {
97
53
  console.log("Autocomplete triggered for man doc command");
98
- // We'll implement actual topic retrieval here later
99
54
  reply([]);
100
55
  });
101
56
  completion.init();
@@ -104,136 +59,15 @@ function commandCompletion_setup() {
104
59
  }
105
60
  }
106
61
  /**
107
- * Initializes and sets up all command handlers for the ChILI CLI.
108
- *
109
- * @param program - The Commander.js program instance.
110
- */
111
- async function handlers_initialize() {
112
- // const client = await chrisConnection.client_get();
113
- // We don't enforce connection here to allow --help to work.
114
- // Commands will fail individually if not connected.
115
- fileBrowserCommand_setup(program);
116
- contextCommand_setup(program);
117
- pathCommand_setup(program);
118
- manCommand_setup(program);
119
- chefsCommand_setup(program);
120
- const pacsServerHandler = new PACSServerGroupHandler();
121
- pacsServerHandler.pacsServerCommand_setup(program);
122
- const pacsQueryHandler = new PACSQueryGroupHandler();
123
- pacsQueryHandler.pacsQueryCommand_setup(program);
124
- const pacsRetrieveHandler = new PACSRetrieveGroupHandler();
125
- pacsRetrieveHandler.pacsRetrieveCommand_setup(program);
126
- await inodeCommand_setup(program);
127
- const pluginGroupHandler = new PluginGroupHandler();
128
- pluginGroupHandler.pluginGroupCommand_setup(program);
129
- const pluginMetaGroupHandler = new PluginMetaGroupHandler();
130
- pluginMetaGroupHandler.pluginMetaGroupCommand_setup(program);
131
- const feedGroupHandler = new FeedGroupHandler();
132
- feedGroupHandler.feedGroupCommand_setup(program);
133
- const feedMemberHandler = new FeedMemberHandler();
134
- feedMemberHandler.feedCommand_setup(program);
135
- const pluginMemberHandler = new PluginMemberHandler();
136
- pluginMemberHandler.pluginCommand_setup(program);
137
- try {
138
- const filesGroupHandler = await FileGroupHandler.handler_create("files");
139
- filesGroupHandler.fileGroupCommand_setup(program);
140
- const linksGroupHandler = await FileGroupHandler.handler_create("links");
141
- linksGroupHandler.fileGroupCommand_setup(program);
142
- const dirsGroupHandler = await FileGroupHandler.handler_create("dirs");
143
- dirsGroupHandler.fileGroupCommand_setup(program);
144
- }
145
- catch (e) {
146
- const err = e instanceof Error ? e.message : String(e);
147
- const errors = errorStack_getAllOfType("error");
148
- const warnings = errorStack_getAllOfType("warning");
149
- console.log(`Note: File group commands (files, dirs, links) are unavailable. Reason: ${err}`);
150
- if (errors.length > 0) {
151
- console.log("Errors:");
152
- errors.forEach(msg => console.log(` - ${msg}`));
153
- }
154
- if (warnings.length > 0) {
155
- console.log("Warnings:");
156
- warnings.forEach(msg => console.log(` - ${msg}`));
157
- }
158
- }
159
- try {
160
- const computesOfPluginHandler = await PluginContextGroupHandler.handler_create("computesofplugin");
161
- computesOfPluginHandler.pluginContextGroupCommand_setup(program);
162
- const pluginInstancesHandler = await PluginContextGroupHandler.handler_create("instancesofplugin");
163
- pluginInstancesHandler.pluginContextGroupCommand_setup(program);
164
- const pluginParametersHandler = await PluginContextGroupHandler.handler_create("parametersofplugin");
165
- pluginParametersHandler.pluginContextGroupCommand_setup(program);
166
- }
167
- catch (e) {
168
- const err = e instanceof Error ? e.message : String(e);
169
- const errors = errorStack_getAllOfType("error");
170
- const warnings = errorStack_getAllOfType("warning");
171
- console.log(`Note: Plugin context commands are unavailable. Reason: ${err}`);
172
- if (errors.length > 0) {
173
- console.log("Errors:");
174
- errors.forEach(msg => console.log(` - ${msg}`));
175
- }
176
- if (warnings.length > 0) {
177
- console.log("Warnings:");
178
- warnings.forEach(msg => console.log(` - ${msg}`));
179
- }
180
- }
181
- commandCompletion_setup();
182
- }
183
- /**
184
- * Parses the command line arguments to extract context information.
185
- *
186
- * @param args - The command line arguments array (process.argv).
187
- * @returns A tuple containing the context string (if found) and the remaining arguments.
188
- */
189
- function context_parse(args) {
190
- // Find the first argument that looks like a context string (contains "=" but doesn't start with "-")
191
- for (let i = 2; i < args.length; i++) {
192
- const arg = args[i];
193
- if (arg.includes("=") && !arg.startsWith("-")) {
194
- const context = arg;
195
- // Reconstruct args without the context string
196
- const newArgs = [...args.slice(0, i), ...args.slice(i + 1)];
197
- return [context, newArgs];
198
- }
199
- }
200
- return [undefined, args];
201
- }
202
- /**
203
- * Main function to run the ChILI CLI.
204
- * Handles initialization, context parsing, and command execution.
62
+ * Bin entry: register shell completion, then run the requested chili command.
205
63
  */
206
64
  async function main() {
207
- const nodeStorageProvider = new NodeStorageProvider();
208
- const connection = await chrisConnection_init(nodeStorageProvider);
209
- const [context, newArgs] = context_parse(process.argv);
210
- if (context) {
211
- const contextSetSuccess = await connection.context_set(context);
212
- if (!contextSetSuccess) {
213
- console.error("Failed to set context. Exiting.");
214
- process.exit(1);
215
- }
216
- process.argv = newArgs;
217
- }
218
- program
219
- .name("chili")
220
- .description("ChILI handles Intelligent Line Interactions")
221
- .option("-v, --verbose", "enable verbose output")
222
- .option("-c, --config <path>", "path to config file")
223
- .option("-s, --nosplash", "disable splash screen");
224
- connectCommand_setup(program);
225
- await handlers_initialize(); // Call handlers_initialize here
226
- program.parseOptions(process.argv);
227
- const options = program.opts();
228
- if (!options.nosplash) {
229
- console.log(figlet.textSync("ChILI"));
230
- console.log("ChILI handles Intelligent Line Interactions");
231
- }
232
- program.parse(process.argv);
65
+ commandCompletion_setup();
66
+ await run(process.argv.slice(2));
233
67
  }
234
68
  main().catch((error) => {
235
69
  console.error("An error occurred:", error);
236
70
  process.exit(1);
237
71
  });
238
- // Export utilities for use by other packages (e.g., chell)
239
- export { logical_toPhysical } from './utils/cli.js';
72
+ // Re-export library utilities for consumers that import the package root.
73
+ export { logical_toPhysical } from "./utils/cli.js";
@@ -23,4 +23,4 @@ export declare function asciidoc_render(content: string, style: "figlet" | "asci
23
23
  *
24
24
  * @param filePath - The path to the file to open.
25
25
  */
26
- export declare function browser_open(filePath: string): void;
26
+ export declare function browser_open(filePath: string): Promise<void>;
@@ -6,7 +6,7 @@
6
6
  import fs from "fs";
7
7
  import path from "path";
8
8
  import chalk from "chalk";
9
- import asciidoctor from "asciidoctor";
9
+ import { convert as adoc_convert } from "asciidoctor";
10
10
  import { exec } from "child_process";
11
11
  import os from "os";
12
12
  const headingStyles = [
@@ -63,9 +63,8 @@ export function projectDir_get() {
63
63
  * @param content - The AsciiDoc content string.
64
64
  * @returns The converted HTML string.
65
65
  */
66
- function adoc_htmlConvert(content) {
67
- const ascii = asciidoctor();
68
- let result = ascii.convert(content, {
66
+ async function adoc_htmlConvert(content) {
67
+ let result = await adoc_convert(content, {
69
68
  standalone: false,
70
69
  attributes: {
71
70
  showtitle: "",
@@ -107,7 +106,7 @@ function text_wrap(text, width) {
107
106
  * @returns A Promise resolving to the formatted string.
108
107
  */
109
108
  export async function asciidoc_render(content, style, width) {
110
- let result = adoc_htmlConvert(content);
109
+ let result = await adoc_htmlConvert(content);
111
110
  function ASCII_create(text, style) {
112
111
  const transformedText = style.textTransform(text);
113
112
  return style.color(transformedText);
@@ -159,12 +158,11 @@ export async function asciidoc_render(content, style, width) {
159
158
  *
160
159
  * @param filePath - The path to the file to open.
161
160
  */
162
- export function browser_open(filePath) {
163
- const ascii = asciidoctor();
161
+ export async function browser_open(filePath) {
164
162
  const tempHtmlPath = path.join(os.tmpdir(), path.basename(filePath).replace(".adoc", ".html"));
165
163
  try {
166
164
  const content = fs.readFileSync(filePath, "utf-8");
167
- const html = ascii.convert(content, {
165
+ const html = await adoc_convert(content, {
168
166
  safe: "safe",
169
167
  standalone: true,
170
168
  attributes: { showtitle: true },
@@ -13,7 +13,7 @@ import { table_display, border_draw } from "../screen/screen.js";
13
13
  import { options_toParams } from "../utils/cli.js";
14
14
  import archy from "archy";
15
15
  import open from "open";
16
- import { exec } from "child_process";
16
+ import { execFile } from "child_process";
17
17
  import os from "os";
18
18
  import { files_downloadWithProgress } from "../commands/fs/download.js";
19
19
  /**
@@ -160,10 +160,13 @@ async function mermaid_renderServerSide(mermaidDefinition, outputFile) {
160
160
  const tempDir = os.tmpdir();
161
161
  const inputFile = path.join(tempDir, "input.mmd");
162
162
  fs.writeFileSync(inputFile, mermaidDefinition);
163
+ // Pass args as an array (execFile, no shell) so a user-supplied --save path
164
+ // cannot inject shell commands. Requires `mmdc` (@mermaid-js/mermaid-cli),
165
+ // which is fetched on demand via npx and is not a hard dependency.
163
166
  return new Promise((resolve, reject) => {
164
- exec(`npx mmdc -i ${inputFile} -o ${outputFile}`, (error, stdout, stderr) => {
167
+ execFile("npx", ["mmdc", "-i", inputFile, "-o", outputFile], (error) => {
165
168
  if (error) {
166
- console.error(`exec error: ${error}`);
169
+ console.error(`Mermaid render failed. Ensure @mermaid-js/mermaid-cli is available (npx mmdc): ${error.message}`);
167
170
  reject(error);
168
171
  return;
169
172
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Command } from "commander";
7
7
  import { CLIoptions } from "../utils/cli.js";
8
+ import { type CommandEnvelope } from "@fnndsc/cumin";
8
9
  /**
9
10
  * Handles commands related to groups of plugin contexts (computes, instances, parameters).
10
11
  */
@@ -33,6 +34,23 @@ export declare class PluginContextGroupHandler {
33
34
  * Lists available fields for the current plugin context resource.
34
35
  */
35
36
  parameters_fieldsList(): Promise<void>;
37
+ /**
38
+ * Renders plugin parameters in "man page" style as an envelope.
39
+ *
40
+ * Sans-I/O counterpart to {@link parameters_listMan}.
41
+ *
42
+ * @param options - CLI options for filtering.
43
+ * @returns An envelope carrying the rendered parameters or an error.
44
+ */
45
+ parameters_listManRender(options: CLIoptions): Promise<CommandEnvelope>;
46
+ /**
47
+ * Renders available fields for the current plugin context resource as an envelope.
48
+ *
49
+ * Sans-I/O counterpart to {@link parameters_fieldsList}.
50
+ *
51
+ * @returns An envelope carrying the field listing.
52
+ */
53
+ parameters_fieldsRender(): Promise<CommandEnvelope>;
36
54
  /**
37
55
  * Sets up the Commander.js commands for plugin context group operations.
38
56
  *
@@ -6,7 +6,8 @@
6
6
  import { BaseGroupHandler } from "../handlers/baseGroupHandler.js";
7
7
  import { options_toParams } from "../utils/cli.js";
8
8
  import { PluginContextController } from "../controllers/pluginContextController.js";
9
- import { pluginParameters_renderMan } from "../views/pluginParameters.js";
9
+ import { pluginParameters_renderMan, pluginParameters_manRender } from "../views/pluginParameters.js";
10
+ import { envelope_ok, envelope_error } from "@fnndsc/cumin";
10
11
  class InitializationError extends Error {
11
12
  constructor(message) {
12
13
  super(message);
@@ -76,6 +77,44 @@ export class PluginContextGroupHandler {
76
77
  await this.baseGroupHandler.resourceFields_list();
77
78
  }
78
79
  }
80
+ /**
81
+ * Renders plugin parameters in "man page" style as an envelope.
82
+ *
83
+ * Sans-I/O counterpart to {@link parameters_listMan}.
84
+ *
85
+ * @param options - CLI options for filtering.
86
+ * @returns An envelope carrying the rendered parameters or an error.
87
+ */
88
+ async parameters_listManRender(options) {
89
+ try {
90
+ const asset = this.controller.chrisObject.asset;
91
+ if (!asset || typeof asset.resources_listAndFilterByOptions !== 'function') {
92
+ return envelope_error('', undefined, "Underlying resource does not support listing.\n");
93
+ }
94
+ const params = options_toParams(options);
95
+ const results = await asset.resources_listAndFilterByOptions(params);
96
+ if (results) {
97
+ return envelope_ok(`${pluginParameters_manRender(results)}\n`);
98
+ }
99
+ return envelope_ok("No parameters found.\n");
100
+ }
101
+ catch (error) {
102
+ return envelope_error('', undefined, `Error listing parameters: ${error}\n`);
103
+ }
104
+ }
105
+ /**
106
+ * Renders available fields for the current plugin context resource as an envelope.
107
+ *
108
+ * Sans-I/O counterpart to {@link parameters_fieldsList}.
109
+ *
110
+ * @returns An envelope carrying the field listing.
111
+ */
112
+ async parameters_fieldsRender() {
113
+ if (this.baseGroupHandler) {
114
+ return this.baseGroupHandler.resourceFields_render();
115
+ }
116
+ return envelope_ok('');
117
+ }
79
118
  /**
80
119
  * Sets up the Commander.js commands for plugin context group operations.
81
120
  *
package/dist/run.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @file In-process library entry for the chili (ChILI) CLI.
3
+ *
4
+ * Exposes {@link run}, which builds a fresh Commander program and executes a
5
+ * single chili command, returning when it completes. Unlike the bin entry
6
+ * (`index.ts`), importing this module has no side effect of running the CLI and
7
+ * never calls `process.exit`, so a host process such as chell can drive chili
8
+ * in-process instead of spawning a separate `node` subprocess.
9
+ *
10
+ * @module
11
+ */
12
+ /**
13
+ * Executes a single chili command in the current process.
14
+ *
15
+ * Builds a fresh Commander program (so repeated calls do not share parser
16
+ * state), wires all command handlers, and parses the supplied arguments. Help,
17
+ * version and usage errors are handled gracefully (Commander's message is
18
+ * already written) instead of terminating the process; genuine errors thrown by
19
+ * a command action propagate to the caller.
20
+ *
21
+ * @param argv - The chili arguments, e.g. `["feeds", "list", "-s"]` (without
22
+ * the leading `node`/script entries).
23
+ */
24
+ export declare function run(argv: string[]): Promise<void>;
package/dist/run.js ADDED
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @file In-process library entry for the chili (ChILI) CLI.
3
+ *
4
+ * Exposes {@link run}, which builds a fresh Commander program and executes a
5
+ * single chili command, returning when it completes. Unlike the bin entry
6
+ * (`index.ts`), importing this module has no side effect of running the CLI and
7
+ * never calls `process.exit`, so a host process such as chell can drive chili
8
+ * in-process instead of spawning a separate `node` subprocess.
9
+ *
10
+ * @module
11
+ */
12
+ import { Command, CommanderError } from "commander";
13
+ import figlet from "figlet";
14
+ import { connectCommand_setup } from "./connect/connectHandler.js";
15
+ import { FeedGroupHandler, FeedMemberHandler } from "./feeds/feedHandler.js";
16
+ import { PluginGroupHandler, PluginMemberHandler, } from "./plugins/pluginHandler.js";
17
+ import { PluginMetaGroupHandler } from "./plugins/pluginMetaHandler.js";
18
+ import { PluginContextGroupHandler } from "./plugins/pluginGroupHandler.js";
19
+ import { inodeCommand_setup } from "./filesystem/inodeCommand.js";
20
+ import { contextCommand_setup } from "./context/contextCommand.js";
21
+ import { pathCommand_setup } from "./path/pathCommand.js";
22
+ import { fileBrowserCommand_setup } from "./filesystem/filesystemHandler.js";
23
+ import { manCommand_setup } from "./man/man.js";
24
+ import { chefsCommand_setup } from "./chefs/chefs.js";
25
+ import { PACSServerGroupHandler } from "./pacs/pacsServerHandler.js";
26
+ import { PACSQueryGroupHandler } from "./pacs/pacsQueryHandler.js";
27
+ import { PACSRetrieveGroupHandler } from "./pacs/pacsRetrieveHandler.js";
28
+ import { chrisConnection_init, NodeStorageProvider, errorStack_getAllOfType, } from "@fnndsc/cumin";
29
+ import { FileGroupHandler } from "./filesystem/fileGroupHandler.js";
30
+ /**
31
+ * Suppress the DEP0169 (`url.parse()`) warning emitted transitively by
32
+ * axios -> proxy-from-env, which we do not control. Installed once at import.
33
+ * See the tracking note in the original bin entry for context.
34
+ */
35
+ const originalEmitWarning = process.emitWarning;
36
+ process.emitWarning = function (warning, ...args) {
37
+ if (typeof warning === "string" &&
38
+ (warning.includes("DEP0169") || warning.includes("url.parse()"))) {
39
+ return;
40
+ }
41
+ return originalEmitWarning.call(process, warning, ...args);
42
+ };
43
+ /**
44
+ * Registers every chili command handler on the supplied program. The handler
45
+ * setup functions are program-parameterised, so a fresh program can be wired on
46
+ * each call without sharing Commander state across invocations.
47
+ *
48
+ * @param program - The Commander program to register commands on.
49
+ */
50
+ async function handlers_initialize(program) {
51
+ // Connection is intentionally not enforced here so that `--help` works; each
52
+ // command fails individually if it needs a connection that is not present.
53
+ fileBrowserCommand_setup(program);
54
+ contextCommand_setup(program);
55
+ pathCommand_setup(program);
56
+ manCommand_setup(program);
57
+ chefsCommand_setup(program);
58
+ const pacsServerHandler = new PACSServerGroupHandler();
59
+ pacsServerHandler.pacsServerCommand_setup(program);
60
+ const pacsQueryHandler = new PACSQueryGroupHandler();
61
+ pacsQueryHandler.pacsQueryCommand_setup(program);
62
+ const pacsRetrieveHandler = new PACSRetrieveGroupHandler();
63
+ pacsRetrieveHandler.pacsRetrieveCommand_setup(program);
64
+ await inodeCommand_setup(program);
65
+ const pluginGroupHandler = new PluginGroupHandler();
66
+ pluginGroupHandler.pluginGroupCommand_setup(program);
67
+ const pluginMetaGroupHandler = new PluginMetaGroupHandler();
68
+ pluginMetaGroupHandler.pluginMetaGroupCommand_setup(program);
69
+ const feedGroupHandler = new FeedGroupHandler();
70
+ feedGroupHandler.feedGroupCommand_setup(program);
71
+ const feedMemberHandler = new FeedMemberHandler();
72
+ feedMemberHandler.feedCommand_setup(program);
73
+ const pluginMemberHandler = new PluginMemberHandler();
74
+ pluginMemberHandler.pluginCommand_setup(program);
75
+ try {
76
+ const filesGroupHandler = await FileGroupHandler.handler_create("files");
77
+ filesGroupHandler.fileGroupCommand_setup(program);
78
+ const linksGroupHandler = await FileGroupHandler.handler_create("links");
79
+ linksGroupHandler.fileGroupCommand_setup(program);
80
+ const dirsGroupHandler = await FileGroupHandler.handler_create("dirs");
81
+ dirsGroupHandler.fileGroupCommand_setup(program);
82
+ }
83
+ catch (e) {
84
+ const err = e instanceof Error ? e.message : String(e);
85
+ const errors = errorStack_getAllOfType("error");
86
+ const warnings = errorStack_getAllOfType("warning");
87
+ console.log(`Note: File group commands (files, dirs, links) are unavailable. Reason: ${err}`);
88
+ if (errors.length > 0) {
89
+ console.log("Errors:");
90
+ errors.forEach((msg) => console.log(` - ${msg}`));
91
+ }
92
+ if (warnings.length > 0) {
93
+ console.log("Warnings:");
94
+ warnings.forEach((msg) => console.log(` - ${msg}`));
95
+ }
96
+ }
97
+ try {
98
+ const computesOfPluginHandler = await PluginContextGroupHandler.handler_create("computesofplugin");
99
+ computesOfPluginHandler.pluginContextGroupCommand_setup(program);
100
+ const pluginInstancesHandler = await PluginContextGroupHandler.handler_create("instancesofplugin");
101
+ pluginInstancesHandler.pluginContextGroupCommand_setup(program);
102
+ const pluginParametersHandler = await PluginContextGroupHandler.handler_create("parametersofplugin");
103
+ pluginParametersHandler.pluginContextGroupCommand_setup(program);
104
+ }
105
+ catch (e) {
106
+ const err = e instanceof Error ? e.message : String(e);
107
+ const errors = errorStack_getAllOfType("error");
108
+ const warnings = errorStack_getAllOfType("warning");
109
+ console.log(`Note: Plugin context commands are unavailable. Reason: ${err}`);
110
+ if (errors.length > 0) {
111
+ console.log("Errors:");
112
+ errors.forEach((msg) => console.log(` - ${msg}`));
113
+ }
114
+ if (warnings.length > 0) {
115
+ console.log("Warnings:");
116
+ warnings.forEach((msg) => console.log(` - ${msg}`));
117
+ }
118
+ }
119
+ }
120
+ /**
121
+ * Extracts a leading `key=value` context string from an argv array.
122
+ *
123
+ * @param args - An argv array shaped like `process.argv` (exec, script, ...).
124
+ * @returns A tuple of the context string (if any) and argv with it removed.
125
+ */
126
+ function context_parse(args) {
127
+ for (let i = 2; i < args.length; i++) {
128
+ const arg = args[i];
129
+ if (arg.includes("=") && !arg.startsWith("-")) {
130
+ const context = arg;
131
+ const newArgs = [...args.slice(0, i), ...args.slice(i + 1)];
132
+ return [context, newArgs];
133
+ }
134
+ }
135
+ return [undefined, args];
136
+ }
137
+ /**
138
+ * Executes a single chili command in the current process.
139
+ *
140
+ * Builds a fresh Commander program (so repeated calls do not share parser
141
+ * state), wires all command handlers, and parses the supplied arguments. Help,
142
+ * version and usage errors are handled gracefully (Commander's message is
143
+ * already written) instead of terminating the process; genuine errors thrown by
144
+ * a command action propagate to the caller.
145
+ *
146
+ * @param argv - The chili arguments, e.g. `["feeds", "list", "-s"]` (without
147
+ * the leading `node`/script entries).
148
+ */
149
+ export async function run(argv) {
150
+ const nodeStorageProvider = new NodeStorageProvider();
151
+ const connection = await chrisConnection_init(nodeStorageProvider);
152
+ // Commander's default parsing expects argv[0]/argv[1] to be exec/script.
153
+ let fullArgv = ["node", "chili", ...argv];
154
+ const [context, newArgs] = context_parse(fullArgv);
155
+ if (context) {
156
+ const contextSetSuccess = await connection.context_set(context);
157
+ if (!contextSetSuccess) {
158
+ console.error("Failed to set context.");
159
+ return;
160
+ }
161
+ fullArgv = newArgs;
162
+ }
163
+ const program = new Command();
164
+ program
165
+ .name("chili")
166
+ .description("ChILI handles Intelligent Line Interactions")
167
+ .version("1.0.1")
168
+ .option("-v, --verbose", "enable verbose output")
169
+ .option("-c, --config <path>", "path to config file")
170
+ .option("-s, --nosplash", "disable splash screen")
171
+ .exitOverride();
172
+ connectCommand_setup(program);
173
+ await handlers_initialize(program);
174
+ program.parseOptions(fullArgv);
175
+ const options = program.opts();
176
+ if (!options.nosplash) {
177
+ console.log(figlet.textSync("ChILI"));
178
+ console.log("ChILI handles Intelligent Line Interactions");
179
+ }
180
+ try {
181
+ await program.parseAsync(fullArgv);
182
+ }
183
+ catch (err) {
184
+ // Under exitOverride, --help/--version/usage errors throw a CommanderError
185
+ // whose message Commander has already written. Swallow these so the host
186
+ // (e.g. a chell REPL) is not torn down; rethrow anything else.
187
+ if (err instanceof CommanderError) {
188
+ return;
189
+ }
190
+ throw err;
191
+ }
192
+ }
@@ -75,6 +75,19 @@ export declare function border_draw(text: string, borders?: Borders): string;
75
75
  * @param options - Optional table display options.
76
76
  * @returns The `TableContent` object or null on error.
77
77
  */
78
+ /**
79
+ * Renders a table to a string, the string form of {@link table_display}.
80
+ *
81
+ * Envelope-returning callers use this to return their output rather than
82
+ * printing it; table_display remains the printing form for callers not yet
83
+ * converted. Returns an empty string when there is no table to render.
84
+ *
85
+ * @param tableData - The rows to render.
86
+ * @param headers - Column headers.
87
+ * @param options - Table formatting options.
88
+ * @returns The rendered table (with a trailing newline), or an empty string.
89
+ */
90
+ export declare function table_render(tableData: TableDataRow[] | string[][] | string[], headers: string[] | string, options?: TableOptions): string;
78
91
  export declare function table_display(tableData: TableDataRow[] | string[][] | string[], headers: string[] | string, options?: TableOptions): TableContent | null;
79
92
  /**
80
93
  * Provides screen utilities for logging and table output.