Merge branch 'main' into accurate-cli-version

This commit is contained in:
Ola Hungerford 2025-09-22 20:46:14 -07:00 • committed by GitHub
commit 32ea112051
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -19,6 +19,17 @@ import {
} from "./client/index.js";
import { handleError } from "./error-handler.js";
import { createTransport, TransportOptions } from "./transport.js";
import { awaitableLog } from "./utils/awaitable-log.js";
// JSON value type for CLI arguments
type JsonValue =
| string
| number
| boolean
| null
| undefined
| JsonValue[]
| { [key: string]: JsonValue };
import packageJson from "../package.json" with { type: "json" };
@ -26,17 +37,19 @@ type Args = {
target: string[];
method?: string;
promptName?: string;
promptArgs?: Record<string, string>;
promptArgs?: Record<string, JsonValue>;
uri?: string;
logLevel?: LogLevel;
toolName?: string;
toolArg?: Record<string, string>;
toolArg?: Record<string, JsonValue>;
transport?: "sse" | "stdio" | "http";
headers?: Record<string, string>;
};
function createTransportOptions(
target: string[],
transport?: "sse" | "stdio" | "http",
headers?: Record<string, string>,
): TransportOptions {
if (target.length === 0) {
throw new Error(
@ -83,11 +96,16 @@ function createTransportOptions(
command: isUrl ? undefined : command,
args: isUrl ? undefined : commandArgs,
url: isUrl ? command : undefined,
headers,
};
}
async function callMethod(args: Args): Promise<void> {
const transportOptions = createTransportOptions(args.target, args.transport);
const transportOptions = createTransportOptions(
args.target,
args.transport,
args.headers,
);
const transport = createTransport(transportOptions);
const [, name = packageJson.name] = packageJson.name.split("/");
@ -154,7 +172,7 @@ async function callMethod(args: Args): Promise<void> {
);
}
console.log(JSON.stringify(result, null, 2));
await awaitableLog(JSON.stringify(result, null, 2));
} finally {
try {
await disconnect(transport);
@ -166,8 +184,8 @@ async function callMethod(args: Args): Promise<void> {
function parseKeyValuePair(
value: string,
previous: Record<string, string> = {},
): Record<string, string> {
previous: Record<string, JsonValue> = {},
): Record<string, JsonValue> {
const parts = value.split("=");
const key = parts[0];
const val = parts.slice(1).join("=");
@ -178,7 +196,40 @@ function parseKeyValuePair(
);
}
return { ...previous, [key as string]: val };
// Try to parse as JSON first
let parsedValue: JsonValue;
try {
parsedValue = JSON.parse(val) as JsonValue;
} catch {
// If JSON parsing fails, keep as string
parsedValue = val;
}
return { ...previous, [key as string]: parsedValue };
}
function parseHeaderPair(
value: string,
previous: Record<string, string> = {},
): Record<string, string> {
const colonIndex = value.indexOf(":");
if (colonIndex === -1) {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}
const key = value.slice(0, colonIndex).trim();
const val = value.slice(colonIndex + 1).trim();
if (key === "" || val === "") {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}
return { ...previous, [key]: val };
}
function parseArgs(): Args {
@ -260,12 +311,24 @@ function parseArgs(): Args {
}
return value as "sse" | "http" | "stdio";
},
)
//
// HTTP headers
//
.option(
"--header <headers...>",
'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)',
parseHeaderPair,
{},
);
// Parse only the arguments before --
program.parse(preArgs);
const options = program.opts() as Omit<Args, "target">;
const options = program.opts() as Omit<Args, "target"> & {
header?: Record<string, string>;
};
let remainingArgs = program.args;
// Add back any arguments that came after --
@ -280,6 +343,7 @@ function parseArgs(): Args {
return {
target: finalArgs,
...options,
headers: options.header, // commander.js uses 'header' field, map to 'headers'
};
}
@ -291,6 +355,7 @@ async function main(): Promise<void> {
try {
const args = parseArgs();
await callMethod(args);
// Explicitly exit to ensure process terminates in CI
process.exit(0);
} catch (error) {