@havesomecode/kibana-mcp-server 1.2.1 → 1.3.1

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/README.md CHANGED
@@ -104,7 +104,7 @@ npm run build
104
104
  5. Open the plugin directory in Codex and install `Kibana Log Investigation` from the repo marketplace.
105
105
  - if the current model cannot complete that install itself, do the Codex UI click manually and let the agent continue with configuration afterward
106
106
  6. Restart Codex if the new MCP server does not appear immediately.
107
- 7. Run `node dist/src/index.js bootstrap --client none` to verify the connection and save an empty profile. The repo plugin already supplies the client registration.
107
+ 7. Run `node dist/src/bin/kibana-mcp-server.js bootstrap --client none` to verify the connection and save an empty profile. The repo plugin already supplies the client registration.
108
108
  8. In a fresh thread, call `discover`, ask the user for the exact index or pattern, then call `configure_index` only after they answer.
109
109
 
110
110
  Repo-scoped plugin files:
@@ -134,7 +134,7 @@ The preferred path is deterministic connection-only bootstrap:
134
134
 
135
135
  Guided setup remains available for hand-authored source catalogs:
136
136
 
137
- - `npm run setup` or `node dist/src/index.js setup`
137
+ - `npm run setup` or `node dist/src/bin/kibana-mcp-server.js setup`
138
138
  - saves non-secret profile metadata in the user’s machine-level app config directory
139
139
  - saves credentials in the platform credential store
140
140
  - macOS Keychain
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./run_cli.js";
3
+ process.exitCode = await runCli(process.argv);
@@ -0,0 +1,49 @@
1
+ import { Command, CommanderError } from "commander";
2
+ import registerServeCommand from "../commands/serve.js";
3
+ import registerSetupCommands from "../commands/setup.js";
4
+ import { startMcpServer } from "../mcp_runtime.js";
5
+ import { createCliIo } from "../utils/cli_io.js";
6
+ import { createCliUi } from "../utils/cli_ui.js";
7
+ export async function runCli(argv, overrides = {}, dependencies = {}) {
8
+ const io = createCliIo(overrides);
9
+ let exitCode = 0;
10
+ const context = {
11
+ argv,
12
+ io,
13
+ ui: createCliUi(io),
14
+ dependencies,
15
+ setExitCode: (code) => {
16
+ exitCode = code;
17
+ },
18
+ };
19
+ const program = new Command()
20
+ .name("kibana-mcp-server")
21
+ .description("Kibana Log Investigation")
22
+ .showHelpAfterError()
23
+ .exitOverride()
24
+ .configureOutput({
25
+ writeOut: (message) => io.stdout(message.trimEnd()),
26
+ writeErr: (message) => io.stderr(message.trimEnd()),
27
+ })
28
+ .action(async () => {
29
+ try {
30
+ await (dependencies.startMcpServerFn ?? startMcpServer)(io.env);
31
+ }
32
+ catch (error) {
33
+ context.ui.error(error instanceof Error ? error.message : String(error));
34
+ exitCode = 1;
35
+ }
36
+ });
37
+ registerSetupCommands(program, context);
38
+ registerServeCommand(program, context);
39
+ try {
40
+ await program.parseAsync(argv);
41
+ }
42
+ catch (error) {
43
+ if (error instanceof CommanderError)
44
+ return error.exitCode;
45
+ io.stderr(error instanceof Error ? error.message : String(error));
46
+ return 1;
47
+ }
48
+ return exitCode;
49
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { PROFILE_NAME_ENV } from "../config.js";
2
+ import { startMcpServer } from "../mcp_runtime.js";
3
+ export default (program, context) => {
4
+ program
5
+ .command("serve")
6
+ .description("Start the stdio MCP server")
7
+ .option("--profile <name>", "Saved profile name")
8
+ .action(async (options) => {
9
+ try {
10
+ await (context.dependencies.startMcpServerFn ?? startMcpServer)(options.profile
11
+ ? { ...context.io.env, [PROFILE_NAME_ENV]: options.profile }
12
+ : context.io.env);
13
+ }
14
+ catch (error) {
15
+ context.ui.error(error instanceof Error ? error.message : String(error));
16
+ context.setExitCode(1);
17
+ }
18
+ });
19
+ };
@@ -0,0 +1,132 @@
1
+ import { InvalidArgumentError, Option } from "commander";
2
+ import { runBootstrap as runBootstrapFlow } from "../bootstrap.js";
3
+ import { runSetupFlow } from "../setup_flow.js";
4
+ import { PromptCancelledError, createPromptIo } from "../utils/cli_prompts.js";
5
+ export default function registerSetup(program, context) {
6
+ const setupCommand = addOptions(program
7
+ .command("setup")
8
+ .alias("bootstrap")
9
+ .description("Run guided machine setup and connection bootstrap")
10
+ .addHelpText("after", "\nBootstrap is an alias for setup. Without options, setup is guided; with options, it verifies and saves only the Kibana connection and does not inspect or configure indexes.\n\nConnection options use KIBANA_BASE_URL, KIBANA_USERNAME, and KIBANA_PASSWORD as fallbacks. Passwords are accepted only through --password-stdin, --password-env <NAME>, or KIBANA_PASSWORD."));
11
+ setupCommand.action(async () => {
12
+ const options = readOptions(context.argv);
13
+ if (context.argv.some((argument) => argument.startsWith("-")))
14
+ return runBootstrap(options, context);
15
+ const promptIo = await createPromptIo(context.io);
16
+ try {
17
+ const result = await (context.dependencies.runSetupFlowFn ?? runSetupFlow)(promptIo);
18
+ context.ui.success(`Saved ${result.profiles.length} environment${result.profiles.length === 1 ? "" : "s"}. Default environment: ${result.defaultProfileName}.`);
19
+ }
20
+ catch (error) {
21
+ if (error instanceof PromptCancelledError)
22
+ context.setExitCode(130);
23
+ else
24
+ throw error;
25
+ }
26
+ finally {
27
+ await promptIo.close();
28
+ }
29
+ });
30
+ }
31
+ function readOptions(argv) {
32
+ const value = (flag) => {
33
+ const index = argv.indexOf(flag);
34
+ return index === -1 ? undefined : argv[index + 1];
35
+ };
36
+ return {
37
+ profile: value("--profile"),
38
+ url: value("--url"),
39
+ username: value("--username"),
40
+ passwordStdin: argv.includes("--password-stdin"),
41
+ passwordEnv: value("--password-env"),
42
+ timeout: value("--timeout") === undefined ? undefined : Number(value("--timeout")),
43
+ client: value("--client"),
44
+ package: value("--package"),
45
+ mcpName: value("--mcp-name"),
46
+ replace: argv.includes("--replace"),
47
+ default: !argv.includes("--no-default"),
48
+ };
49
+ }
50
+ function addOptions(command) {
51
+ return command
52
+ .option("--profile <name>", "Saved profile name")
53
+ .option("--url <url>", "Kibana base URL")
54
+ .option("--username <name>", "Kibana basic-auth username")
55
+ .addOption(new Option("--password-stdin", "Read the password from stdin").conflicts("passwordEnv"))
56
+ .option("--password-env <name>", "Read the password from an environment variable")
57
+ .addOption(new Option("--timeout <milliseconds>", "Kibana request timeout").argParser(parseTimeout))
58
+ .addOption(new Option("--client <codex|none>", "Client registration mode").choices(["codex", "none"]))
59
+ .option("--package <specifier>", "Package specifier for client registration")
60
+ .option("--mcp-name <name>", "MCP registration name")
61
+ .option("--replace", "Replace an existing catalog")
62
+ .option("--no-default", "Do not make this profile the default");
63
+ }
64
+ async function runBootstrap(options, context) {
65
+ const needsPrompt = !options.url ||
66
+ !options.username ||
67
+ (!options.passwordStdin && options.passwordEnv === undefined);
68
+ const promptIo = needsPrompt ? await createPromptIo(context.io) : undefined;
69
+ try {
70
+ const result = await (context.dependencies.runBootstrapFn ?? runBootstrapFlow)(await resolveBootstrapOptions(options, context, promptIo));
71
+ context.ui.success(renderBootstrapResult(result));
72
+ }
73
+ catch (error) {
74
+ context.ui.error(error instanceof Error ? error.message : String(error));
75
+ context.setExitCode(1);
76
+ }
77
+ finally {
78
+ await promptIo?.close();
79
+ }
80
+ }
81
+ async function resolveBootstrapOptions(values, context, prompter) {
82
+ const { env, stdin } = context.io;
83
+ const baseUrl = values.url?.trim() || (await prompter?.prompt("Kibana base URL"))?.trim();
84
+ if (!baseUrl)
85
+ throw new Error("Kibana base URL is required via --url or the associated prompt.");
86
+ const username = values.username?.trim() || (await prompter?.prompt("Kibana username"))?.trim();
87
+ if (!username)
88
+ throw new Error("Kibana username is required via --username or the associated prompt.");
89
+ const password = values.passwordStdin
90
+ ? (await readAllInput(stdin)).replace(/\r?\n$/, "")
91
+ : values.passwordEnv
92
+ ? env[values.passwordEnv]
93
+ : (await prompter?.prompt("Kibana password", { secret: true }))?.trim();
94
+ if (!password)
95
+ throw new Error("Kibana password is required via an option or the associated prompt.");
96
+ return {
97
+ profileName: values.profile?.trim() || env.KIBANA_PROFILE?.trim() || "default",
98
+ baseUrl,
99
+ username,
100
+ password,
101
+ client: values.client ?? "codex",
102
+ packageSpecifier: values.package,
103
+ mcpName: values.mcpName,
104
+ makeDefault: values.default,
105
+ replaceExisting: values.replace ?? false,
106
+ timeoutMs: values.timeout,
107
+ };
108
+ }
109
+ function parseTimeout(value) {
110
+ const timeout = Number(value);
111
+ if (!Number.isFinite(timeout)) {
112
+ throw new InvalidArgumentError("--timeout must be a number of milliseconds.");
113
+ }
114
+ return timeout;
115
+ }
116
+ function renderBootstrapResult(result) {
117
+ return [
118
+ `Bootstrap verified for profile '${result.profileName}'.`,
119
+ result.sourceCount === 0
120
+ ? "Source catalog is empty. No Kibana indexes were inspected or configured. Ask the user which index or index pattern to configure before querying logs."
121
+ : `Preserved ${result.sourceCount} explicitly configured source${result.sourceCount === 1 ? "" : "s"}; bootstrap did not inspect them.`,
122
+ result.registered
123
+ ? `Codex MCP registration is installed and verified for profile '${result.profileName}'.`
124
+ : "Client registration was skipped by request.",
125
+ ].join("\n");
126
+ }
127
+ async function readAllInput(stdin) {
128
+ let raw = "";
129
+ for await (const chunk of stdin)
130
+ raw += typeof chunk === "string" ? chunk : chunk.toString("utf8");
131
+ return raw;
132
+ }
@@ -0,0 +1,11 @@
1
+ export function createCliIo(overrides = {}) {
2
+ return {
3
+ stdout: overrides.stdout ?? ((text) => process.stdout.write(`${text}\n`)),
4
+ stderr: overrides.stderr ?? ((text) => process.stderr.write(`${text}\n`)),
5
+ stdin: overrides.stdin ?? process.stdin,
6
+ stdoutStream: overrides.stdoutStream ?? process.stdout,
7
+ stdinIsTTY: overrides.stdinIsTTY,
8
+ env: overrides.env ?? process.env,
9
+ isStdoutOverridden: overrides.stdout !== undefined,
10
+ };
11
+ }
@@ -0,0 +1,67 @@
1
+ import { cancel, confirm, isCancel, log, password, text } from "@clack/prompts";
2
+ export class PromptCancelledError extends Error {
3
+ constructor() {
4
+ super("Setup cancelled.");
5
+ }
6
+ }
7
+ export async function createPromptIo(io) {
8
+ const stdinIsTTY = io.stdinIsTTY ?? Boolean(io.stdin.isTTY);
9
+ if (!stdinIsTTY)
10
+ return createQueuedPromptIo(io.stdout, await readQueuedAnswers(io.stdin));
11
+ return {
12
+ info: (message) => log.info(message, { output: io.stdoutStream }),
13
+ async prompt(message, options = {}) {
14
+ return unwrap(await (options.secret ? password : text)({
15
+ message,
16
+ defaultValue: options.defaultValue,
17
+ input: io.stdin,
18
+ output: io.stdoutStream,
19
+ }), io.stdoutStream);
20
+ },
21
+ async confirm(message, defaultValue = false) {
22
+ return unwrap(await confirm({
23
+ message,
24
+ initialValue: defaultValue,
25
+ input: io.stdin,
26
+ output: io.stdoutStream,
27
+ }), io.stdoutStream);
28
+ },
29
+ async close() { },
30
+ };
31
+ }
32
+ function unwrap(answer, output) {
33
+ if (isCancel(answer)) {
34
+ cancel("Setup cancelled.", { output });
35
+ throw new PromptCancelledError();
36
+ }
37
+ return answer;
38
+ }
39
+ function createQueuedPromptIo(stdout, answers) {
40
+ let index = 0;
41
+ const consume = (label) => {
42
+ const answer = answers[index++];
43
+ if (answer === undefined)
44
+ throw new Error(`No stdin answer available for prompt '${label}'.`);
45
+ return answer;
46
+ };
47
+ return {
48
+ info: stdout,
49
+ async prompt(message, options = {}) {
50
+ const suffix = options.defaultValue ? ` [${options.defaultValue}]` : "";
51
+ stdout(`${message}${suffix}:`);
52
+ return consume(message) || options.defaultValue || "";
53
+ },
54
+ async confirm(message, defaultValue = false) {
55
+ stdout(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"}:`);
56
+ const answer = consume(message).trim().toLowerCase();
57
+ return answer ? answer === "y" || answer === "yes" : defaultValue;
58
+ },
59
+ async close() { },
60
+ };
61
+ }
62
+ async function readQueuedAnswers(stdin) {
63
+ let raw = "";
64
+ for await (const chunk of stdin)
65
+ raw += typeof chunk === "string" ? chunk : chunk.toString("utf8");
66
+ return raw.replaceAll("\r\n", "\n").split("\n");
67
+ }
@@ -0,0 +1,19 @@
1
+ import { log } from "@clack/prompts";
2
+ export function createCliUi(io) {
3
+ return {
4
+ success(message) {
5
+ if (io.isStdoutOverridden) {
6
+ io.stdout(message);
7
+ return;
8
+ }
9
+ log.success(message, { output: io.stdoutStream });
10
+ },
11
+ error(message) {
12
+ if (io.isStdoutOverridden) {
13
+ io.stderr(message);
14
+ return;
15
+ }
16
+ log.error(message, { output: io.stdoutStream });
17
+ },
18
+ };
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@havesomecode/kibana-mcp-server",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "license": "MIT",
5
5
  "description": "Read-only MCP server for Kibana-backed log investigation and schema-aware querying",
6
6
  "homepage": "https://havesomecode.github.io/kibana-mcp-server/",
@@ -11,7 +11,7 @@
11
11
  "type": "module",
12
12
  "main": "dist/src/mcp_entry.js",
13
13
  "bin": {
14
- "kibana-mcp-server": "dist/src/index.js"
14
+ "kibana-mcp-server": "dist/src/bin/kibana-mcp-server.js"
15
15
  },
16
16
  "publishConfig": {
17
17
  "access": "public",
@@ -40,7 +40,7 @@
40
40
  "dev": "tsx src/mcp_entry.ts",
41
41
  "release": "semantic-release",
42
42
  "release:dry-run": "semantic-release --dry-run",
43
- "setup": "node dist/src/index.js setup",
43
+ "setup": "node dist/src/bin/kibana-mcp-server.js setup",
44
44
  "start": "node dist/src/mcp_entry.js",
45
45
  "test": "vitest run",
46
46
  "verify:release-contract": "node scripts/verify-release-contract.mjs",
@@ -50,7 +50,9 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@cfworker/json-schema": "^4.1.1",
53
+ "@clack/prompts": "^1.8.0",
53
54
  "@modelcontextprotocol/server": "2.0.0",
55
+ "commander": "^14.0.3",
54
56
  "zod": "^4.0.0"
55
57
  },
56
58
  "devDependencies": {
@@ -60,7 +62,7 @@
60
62
  "@types/node": "^25.5.2",
61
63
  "semantic-release": "^25.0.1",
62
64
  "tsx": "^4.0.0",
63
- "typescript": "^5.7.0",
65
+ "typescript": "^7.0.2",
64
66
  "vitest": "^4.1.3"
65
67
  }
66
68
  }
package/dist/src/cli.js DELETED
@@ -1,323 +0,0 @@
1
- import { createInterface } from "node:readline/promises";
2
- import { Writable } from "node:stream";
3
- import { parseArgs } from "node:util";
4
- import { runBootstrap } from "./bootstrap.js";
5
- import { PROFILE_NAME_ENV } from "./config.js";
6
- import { startMcpServer } from "./mcp_runtime.js";
7
- import { runSetupFlow } from "./setup_flow.js";
8
- function renderHelp() {
9
- return [
10
- "Kibana Log Investigation",
11
- "",
12
- "Usage:",
13
- " kibana-mcp-server [command]",
14
- "",
15
- "Commands:",
16
- " setup Run guided machine setup",
17
- " bootstrap Verify, save, and register a profile without prompts",
18
- " serve Start the stdio MCP server",
19
- " help Show this help output",
20
- ].join("\n");
21
- }
22
- function renderBootstrapHelp() {
23
- return [
24
- "Usage:",
25
- " kibana-mcp-server bootstrap [options]",
26
- " kibana-mcp-server setup [options] # compatible non-interactive alias",
27
- "",
28
- "Bootstrap verifies and saves only the Kibana connection; it does not inspect or configure indexes.",
29
- "After installation, ask the agent to configure a specific index or index pattern.",
30
- "",
31
- "Connection options use KIBANA_BASE_URL, KIBANA_USERNAME, and KIBANA_PASSWORD as fallbacks.",
32
- "Passwords are accepted only through --password-stdin, --password-env <NAME>, or KIBANA_PASSWORD.",
33
- "",
34
- "Options:",
35
- " --profile <name> Saved profile name (default: KIBANA_PROFILE or default)",
36
- " --url <url> Kibana base URL",
37
- " --username <name> Kibana basic-auth username",
38
- " --password-stdin Read the password from stdin",
39
- " --password-env <name> Read the password from a named environment variable",
40
- " --client <codex|none> Register Codex or skip client registration (default: codex)",
41
- " --package <specifier> Exact package specifier used by the Codex registration",
42
- " --mcp-name <name> Base name for the transport-hashed Codex registration",
43
- " --replace Explicitly replace an existing catalog with an empty managed catalog",
44
- " --no-default Do not make this profile the default",
45
- ].join("\n");
46
- }
47
- export async function runCli(argv = process.argv.slice(2), io = {}, dependencies = {}) {
48
- const stdout = io.stdout ?? ((text) => process.stdout.write(`${text}\n`));
49
- const stderr = io.stderr ?? ((text) => process.stderr.write(`${text}\n`));
50
- const stdin = io.stdin ?? process.stdin;
51
- const stdoutStream = io.stdoutStream ?? process.stdout;
52
- const env = io.env ?? process.env;
53
- const [command, ...rest] = argv;
54
- switch (command) {
55
- case undefined: {
56
- try {
57
- await (dependencies.startMcpServerFn ?? startMcpServer)(env);
58
- return 0;
59
- }
60
- catch (error) {
61
- stderr(error instanceof Error ? error.message : String(error));
62
- return 1;
63
- }
64
- }
65
- case "setup": {
66
- if (rest.length > 0) {
67
- if (rest.includes("--help") || rest.includes("-h")) {
68
- stdout(renderBootstrapHelp());
69
- return 0;
70
- }
71
- try {
72
- const options = await parseBootstrapOptions(rest, stdin, env);
73
- const result = await (dependencies.runBootstrapFn ?? runBootstrap)(options);
74
- stdout(renderBootstrapResult(result));
75
- return 0;
76
- }
77
- catch (error) {
78
- stderr(error instanceof Error ? error.message : String(error));
79
- return 1;
80
- }
81
- }
82
- const promptIo = await createPromptIo(stdout, {
83
- stdin,
84
- stdoutStream,
85
- stdinIsTTY: io.stdinIsTTY,
86
- });
87
- try {
88
- const result = await (dependencies.runSetupFlowFn ?? runSetupFlow)(promptIo);
89
- stdout(`Saved ${result.profiles.length} environment${result.profiles.length === 1 ? "" : "s"}. Default environment: ${result.defaultProfileName}.`);
90
- return 0;
91
- }
92
- finally {
93
- await promptIo.close();
94
- }
95
- }
96
- case "help":
97
- case "--help":
98
- case "-h": {
99
- stdout(renderHelp());
100
- return 0;
101
- }
102
- case "bootstrap": {
103
- if (rest.includes("--help") || rest.includes("-h")) {
104
- stdout(renderBootstrapHelp());
105
- return 0;
106
- }
107
- try {
108
- const options = await parseBootstrapOptions(rest, stdin, env);
109
- const result = await (dependencies.runBootstrapFn ?? runBootstrap)(options);
110
- stdout(renderBootstrapResult(result));
111
- return 0;
112
- }
113
- catch (error) {
114
- stderr(error instanceof Error ? error.message : String(error));
115
- return 1;
116
- }
117
- }
118
- case "serve": {
119
- try {
120
- const { values } = parseArgs({
121
- args: rest,
122
- allowPositionals: false,
123
- strict: true,
124
- options: {
125
- profile: { type: "string" },
126
- },
127
- });
128
- const serveEnv = values.profile ? { ...env, [PROFILE_NAME_ENV]: values.profile } : env;
129
- await (dependencies.startMcpServerFn ?? startMcpServer)(serveEnv);
130
- }
131
- catch (error) {
132
- stderr(error instanceof Error ? error.message : String(error));
133
- return 1;
134
- }
135
- return 0;
136
- }
137
- default: {
138
- stderr(`Unknown command: ${command}`);
139
- stderr(renderHelp());
140
- return 1;
141
- }
142
- }
143
- }
144
- async function parseBootstrapOptions(args, stdin, env) {
145
- const { values } = parseArgs({
146
- args,
147
- allowPositionals: false,
148
- strict: true,
149
- options: {
150
- profile: { type: "string" },
151
- url: { type: "string" },
152
- username: { type: "string" },
153
- "password-stdin": { type: "boolean" },
154
- "password-env": { type: "string" },
155
- timeout: { type: "string" },
156
- client: { type: "string" },
157
- package: { type: "string" },
158
- "mcp-name": { type: "string" },
159
- replace: { type: "boolean" },
160
- "no-default": { type: "boolean" },
161
- },
162
- });
163
- const baseUrl = values.url?.trim() || env.KIBANA_BASE_URL?.trim();
164
- if (!baseUrl) {
165
- throw new Error("Kibana base URL is required via --url or KIBANA_BASE_URL.");
166
- }
167
- const username = values.username?.trim() || env.KIBANA_USERNAME?.trim();
168
- if (!username) {
169
- throw new Error("Kibana username is required via --username or KIBANA_USERNAME.");
170
- }
171
- if (values["password-stdin"] && values["password-env"]) {
172
- throw new Error("Use only one of --password-stdin or --password-env.");
173
- }
174
- let password;
175
- if (values["password-stdin"]) {
176
- password = (await readAllInput(stdin)).replace(/\r?\n$/, "");
177
- }
178
- else if (values["password-env"]) {
179
- password = env[values["password-env"]];
180
- if (password === undefined) {
181
- throw new Error(`Password environment variable '${values["password-env"]}' is not set.`);
182
- }
183
- }
184
- else {
185
- password = env.KIBANA_PASSWORD;
186
- }
187
- if (!password) {
188
- throw new Error("Kibana password is required via --password-stdin, --password-env, or KIBANA_PASSWORD.");
189
- }
190
- const client = values.client ?? "codex";
191
- if (client !== "codex" && client !== "none") {
192
- throw new Error("--client must be either 'codex' or 'none'.");
193
- }
194
- const timeoutMs = values.timeout === undefined ? undefined : Number(values.timeout);
195
- if (timeoutMs !== undefined && !Number.isFinite(timeoutMs)) {
196
- throw new Error("--timeout must be a number of milliseconds.");
197
- }
198
- return {
199
- profileName: values.profile?.trim() || env[PROFILE_NAME_ENV]?.trim() || "default",
200
- baseUrl,
201
- username,
202
- password,
203
- client,
204
- packageSpecifier: values.package,
205
- mcpName: values["mcp-name"],
206
- makeDefault: !values["no-default"],
207
- replaceExisting: values.replace ?? false,
208
- timeoutMs,
209
- };
210
- }
211
- function renderBootstrapResult(result) {
212
- return [
213
- `Bootstrap verified for profile '${result.profileName}'.`,
214
- result.sourceCount === 0
215
- ? "Source catalog is empty. No Kibana indexes were inspected or configured. Ask the user which index or index pattern to configure before querying logs."
216
- : `Preserved ${result.sourceCount} explicitly configured source${result.sourceCount === 1 ? "" : "s"}; bootstrap did not inspect them.`,
217
- result.registered
218
- ? `Codex MCP registration is installed and verified for profile '${result.profileName}'.`
219
- : "Client registration was skipped by request.",
220
- ].join("\n");
221
- }
222
- async function readAllInput(stdin) {
223
- let rawInput = "";
224
- for await (const chunk of stdin) {
225
- rawInput += typeof chunk === "string" ? chunk : chunk.toString("utf8");
226
- }
227
- return rawInput;
228
- }
229
- export async function createPromptIo(stdout, options = {}) {
230
- const stdin = options.stdin ?? process.stdin;
231
- const stdoutStream = options.stdoutStream ?? process.stdout;
232
- const stdinIsTTY = options.stdinIsTTY ?? Boolean(stdin.isTTY);
233
- if (!stdinIsTTY) {
234
- return createQueuedPromptIo(stdout, await readQueuedAnswers(stdin));
235
- }
236
- const maskingOutput = new MaskingWritable(stdoutStream);
237
- const readline = createInterface({
238
- input: stdin,
239
- output: maskingOutput,
240
- });
241
- return {
242
- info(message) {
243
- stdout(message);
244
- },
245
- async prompt(message, options = {}) {
246
- const suffix = options.defaultValue ? ` [${options.defaultValue}]` : "";
247
- if (options.secret) {
248
- maskingOutput.muted = false;
249
- stdoutStream.write(`${message}: `);
250
- maskingOutput.muted = true;
251
- const answer = await readline.question("");
252
- maskingOutput.muted = false;
253
- stdoutStream.write("\n");
254
- return answer || options.defaultValue || "";
255
- }
256
- const answer = await readline.question(`${message}${suffix}: `);
257
- return answer || options.defaultValue || "";
258
- },
259
- async confirm(message, defaultValue = false) {
260
- const answer = await readline.question(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"}: `);
261
- const normalized = answer.trim().toLowerCase();
262
- if (!normalized) {
263
- return defaultValue;
264
- }
265
- return normalized === "y" || normalized === "yes";
266
- },
267
- async close() {
268
- await readline.close();
269
- },
270
- };
271
- }
272
- function createQueuedPromptIo(stdout, answers) {
273
- let answerIndex = 0;
274
- function consumeAnswer(promptLabel) {
275
- const answer = answers[answerIndex];
276
- answerIndex += 1;
277
- if (answer === undefined) {
278
- throw new Error(`No stdin answer available for prompt '${promptLabel}'.`);
279
- }
280
- return answer;
281
- }
282
- return {
283
- info(message) {
284
- stdout(message);
285
- },
286
- async prompt(message, options = {}) {
287
- const suffix = options.defaultValue ? ` [${options.defaultValue}]` : "";
288
- stdout(`${message}${suffix}:`);
289
- const answer = consumeAnswer(message);
290
- return answer || options.defaultValue || "";
291
- },
292
- async confirm(message, defaultValue = false) {
293
- stdout(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"}:`);
294
- const normalized = consumeAnswer(message).trim().toLowerCase();
295
- if (!normalized) {
296
- return defaultValue;
297
- }
298
- return normalized === "y" || normalized === "yes";
299
- },
300
- async close() { },
301
- };
302
- }
303
- async function readQueuedAnswers(stdin) {
304
- let rawInput = "";
305
- for await (const chunk of stdin) {
306
- rawInput += typeof chunk === "string" ? chunk : chunk.toString("utf8");
307
- }
308
- return rawInput.replaceAll("\r\n", "\n").split("\n");
309
- }
310
- class MaskingWritable extends Writable {
311
- target;
312
- muted = false;
313
- constructor(target) {
314
- super();
315
- this.target = target;
316
- }
317
- _write(chunk, encoding, callback) {
318
- if (!this.muted) {
319
- this.target.write(chunk, encoding);
320
- }
321
- callback();
322
- }
323
- }
package/dist/src/index.js DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runCli } from "./cli.js";
3
- runCli()
4
- .then((exitCode) => {
5
- process.exitCode = exitCode;
6
- })
7
- .catch((error) => {
8
- console.error(error);
9
- process.exitCode = 1;
10
- });