Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ await Bun.build({
naming: 'mmx.mjs',
target: 'node',
minify: !DEV_BUILD,
external: ['undici'],
define: { 'process.env.CLI_VERSION': JSON.stringify(VERSION) },
});

Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"dependencies": {
"@clack/prompts": "^0.7.0",
"bun-plugin-dts": "^0.4.0",
"es-toolkit": "^1.46.1"
"es-toolkit": "^1.46.1",
"undici": "^6.21.1"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
Expand Down
19 changes: 17 additions & 2 deletions src/commands/config/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { readConfigFile, writeConfigFile } from '../../config/loader';
import type { Config } from '../../config/schema';
import type { GlobalFlags } from '../../types/flags';

const VALID_KEYS = ['region', 'base_url', 'output', 'timeout', 'api_key', 'default_text_model', 'default_speech_model', 'default_video_model', 'default_music_model'];
const VALID_KEYS = ['region', 'base_url', 'output', 'timeout', 'api_key', 'proxy', 'default_text_model', 'default_speech_model', 'default_video_model', 'default_music_model'];

// Allow hyphen-style keys (e.g. default-text-model → default_text_model)
const KEY_ALIASES: Record<string, string> = {
Expand All @@ -21,12 +21,13 @@ export default defineCommand({
description: 'Set a config value',
usage: 'mmx config set --key <key> --value <value>',
options: [
{ flag: '--key <key>', description: 'Config key (region, base_url, output, timeout, api_key, default_text_model, default_speech_model, default_video_model, default_music_model)' },
{ flag: '--key <key>', description: 'Config key (region, base_url, output, timeout, api_key, proxy, default_text_model, default_speech_model, default_video_model, default_music_model)' },
{ flag: '--value <value>', description: 'Value to set' },
],
examples: [
'mmx config set --key output --value json',
'mmx config set --key timeout --value 600',
'mmx config set --key proxy --value http://127.0.0.1:7890',
'mmx config set --key base_url --value https://api-uw.minimax.io',
],
async run(config: Config, flags: GlobalFlags) {
Expand Down Expand Up @@ -67,6 +68,20 @@ export default defineCommand({
);
}

if (resolvedKey === 'base_url' && !value.startsWith('http')) {
throw new CLIError(
`Invalid base_url "${value}". Must start with http.`,
ExitCode.USAGE,
);
}

if (resolvedKey === 'proxy' && !value.startsWith('http')) {
throw new CLIError(
`Invalid proxy "${value}". Must be a URL starting with http (e.g. http://127.0.0.1:7890).`,
ExitCode.USAGE,
);
}

if (resolvedKey === 'timeout') {
const num = Number(value);
if (isNaN(num) || num <= 0) {
Expand Down
1 change: 1 addition & 0 deletions src/commands/config/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default defineCommand({
if (file.default_speech_model) result.default_speech_model = file.default_speech_model;
if (file.default_video_model) result.default_video_model = file.default_video_model;
if (file.default_music_model) result.default_music_model = file.default_music_model;
if (file.proxy) result.proxy = file.proxy;

console.log(formatOutput(result, format));
},
Expand Down
2 changes: 2 additions & 0 deletions src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ConfigFile {
base_url?: string;
output?: 'text' | 'json';
timeout?: number;
proxy?: string;
default_text_model?: string;
default_speech_model?: string;
default_video_model?: string;
Expand All @@ -35,6 +36,7 @@ export function parseConfigFile(raw: unknown): ConfigFile {
if (typeof obj.base_url === 'string' && obj.base_url.startsWith('http')) out.base_url = obj.base_url;
if (typeof obj.output === 'string' && VALID_OUTPUTS.has(obj.output)) out.output = obj.output as ConfigFile['output'];
if (typeof obj.timeout === 'number' && obj.timeout > 0) out.timeout = obj.timeout;
if (typeof obj.proxy === 'string' && obj.proxy.startsWith('http')) out.proxy = obj.proxy;
if (typeof obj.default_text_model === 'string' && obj.default_text_model.length > 0) out.default_text_model = obj.default_text_model;
if (typeof obj.default_speech_model === 'string' && obj.default_speech_model.length > 0) out.default_speech_model = obj.default_speech_model;
if (typeof obj.default_video_model === 'string' && obj.default_video_model.length > 0) out.default_video_model = obj.default_video_model;
Expand Down
10 changes: 7 additions & 3 deletions src/errors/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export function handleError(err: unknown): never {
const networkErr = new CLIError(
"Network request failed.",
ExitCode.NETWORK,
"Check your network connection and proxy settings. Also verify MINIMAX_BASE_URL is a valid URL.",
"Check your network connection.\n" +
"To use a proxy: set HTTPS_PROXY env var, or run: mmx config set --key proxy --value http://HOST:PORT",
);
return handleError(networkErr);
}
Expand All @@ -63,10 +64,13 @@ export function handleError(err: unknown): never {
msg.includes("eai_AGAIN");

if (isNetworkError) {
let hint = "Check your network connection and proxy settings.";
let hint =
"Check your network connection.\n" +
"To use a proxy: set HTTPS_PROXY env var, or run: mmx config set --key proxy --value http://HOST:PORT";
if (msg.includes("proxy")) {
hint =
"Proxy error — check HTTP_PROXY / HTTPS_PROXY environment variables and proxy authentication.";
"Proxy connection failed — verify your proxy URL and authentication.\n" +
"Check: HTTPS_PROXY / HTTP_PROXY env vars, or mmx config show for configured proxy.";
}
const networkErr = new CLIError(
"Network request failed.",
Expand Down
13 changes: 12 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { checkForUpdate, getPendingUpdateNotification } from './update/checker';
import { loadCredentials } from './auth/credentials';
import { ensureApiKey } from './auth/setup';
import { CLI_VERSION } from './version';
import { ProxyAgent, setGlobalDispatcher } from 'undici';

// Handle Ctrl+C gracefully
process.on('SIGINT', () => {
Expand Down Expand Up @@ -42,9 +43,19 @@ async function main() {

const commandPath = scanCommandPath(argv, GLOBAL_OPTIONS);

// Proxy: env vars take precedence over config file
const rawConfig = readConfigFile();
const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy
|| process.env.HTTP_PROXY || process.env.http_proxy
|| process.env.ALL_PROXY || process.env.all_proxy
|| rawConfig.proxy;
if (proxyUrl) {
setGlobalDispatcher(new ProxyAgent(proxyUrl));
}

if (argv.includes('--help') || argv.includes('-h')) {
const ri = argv.indexOf('--region');
const region = ((ri >= 0 && argv[ri + 1]) || process.env.MINIMAX_REGION || readConfigFile().region || 'global') as Region;
const region = ((ri >= 0 && argv[ri + 1]) || process.env.MINIMAX_REGION || rawConfig.region || 'global') as Region;
registry.printHelp(commandPath, process.stderr, region);
process.exit(0);
}
Expand Down
Loading