Skip to content

Commit b540ee4

Browse files
committed
Initial commit: minimax-cli
CLI for the MiniMax API Platform (Token Plan). Supports 16 commands across 10 resource groups: text chat, speech synthesis, image generation, video generation, music generation, web search, image understanding, quota management, auth, and config. Features: - Resource-verb grammar (minimax <resource> <command>) - Auto-detects API region (global/cn) on first run - Structured output (text, json, yaml) - SSE streaming for chat - Async polling for video generation - Cross-compiles to standalone binaries via Bun - Works with Node 18+ via npm install
0 parents  commit b540ee4

83 files changed

Lines changed: 4935 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CI
2+
on: [push, pull_request]
3+
4+
jobs:
5+
check:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@v4
9+
- uses: oven-sh/setup-bun@v2
10+
- run: bun install
11+
- run: bun run typecheck
12+
- run: bun test
13+
- name: Smoke test
14+
run: |
15+
bun build src/main.ts --compile --outfile dist/minimax
16+
./dist/minimax --version

.github/workflows/release.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Release
2+
on:
3+
push:
4+
tags: ['v*']
5+
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
- uses: oven-sh/setup-bun@v2
12+
- run: bun install
13+
14+
- name: Build all targets
15+
run: VERSION=${GITHUB_REF_NAME} bun run build.ts
16+
17+
- name: Create GitHub Release
18+
uses: softprops/action-gh-release@v2
19+
with:
20+
files: dist/minimax-*
21+
generate_release_notes: true

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
dist/
3+
*.exe
4+
.env
5+
.env.*
6+
coverage/
7+
*.tgz
8+
.DS_Store

bin/minimax

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bun
2+
import '../src/main.ts';

build.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { $ } from 'bun';
2+
3+
const VERSION = process.env.VERSION ?? 'dev';
4+
5+
const targets = [
6+
{ target: 'bun-linux-x64', output: 'minimax-linux-x64' },
7+
{ target: 'bun-linux-arm64', output: 'minimax-linux-arm64' },
8+
{ target: 'bun-darwin-x64', output: 'minimax-darwin-x64' },
9+
{ target: 'bun-darwin-arm64', output: 'minimax-darwin-arm64' },
10+
{ target: 'bun-windows-x64', output: 'minimax-windows-x64.exe' },
11+
];
12+
13+
console.log(`Building minimax-cli ${VERSION}...\n`);
14+
15+
for (const { target, output } of targets) {
16+
console.log(` Building ${output}...`);
17+
await $`bun build src/main.ts \
18+
--compile \
19+
--target ${target} \
20+
--outfile dist/${output} \
21+
--define "process.env.CLI_VERSION='${VERSION}'"`.quiet();
22+
console.log(` ✓ dist/${output}`);
23+
}
24+
25+
console.log('\nDone. Binaries are in dist/');

bun.lock

Lines changed: 203 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli-design.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# minimax CLI Design
2+
3+
## Command Grammar
4+
5+
All commands follow `resource + verb`:
6+
7+
```
8+
minimax <resource> <verb> [flags]
9+
```
10+
11+
## Command Tree
12+
13+
```
14+
minimax
15+
├── auth
16+
│ ├── login Authenticate via OAuth or API key
17+
│ ├── status Show current authentication state
18+
│ ├── refresh Manually refresh OAuth token
19+
│ └── logout Revoke tokens and clear stored credentials
20+
├── text
21+
│ └── chat Send a chat completion (M2.7 / M2.7-highspeed)
22+
├── speech
23+
│ └── synthesize Synchronous TTS, ≤10k chars
24+
├── image
25+
│ └── generate Generate images (image-01)
26+
├── video
27+
│ ├── generate Create a video generation task
28+
│ ├── task
29+
│ │ └── get Query video task status
30+
│ └── download Download a completed video by file ID
31+
├── music
32+
│ └── generate Generate a song (music-2.5)
33+
├── quota
34+
│ └── show Display Token Plan usage and remaining quotas
35+
└── config
36+
├── show Display current configuration
37+
└── set Set a config value
38+
```
39+
40+
## Exit Codes
41+
42+
| Code | Meaning |
43+
|------|----------------------------------|
44+
| 0 | Success |
45+
| 1 | General / server error |
46+
| 2 | Usage error (bad flags) |
47+
| 3 | Authentication error |
48+
| 4 | Rate limit or quota exceeded |
49+
| 5 | Timeout |
50+
| 10 | Content sensitivity filter |
51+
52+
## Authentication
53+
54+
Credential resolution order:
55+
1. `--api-key` flag
56+
2. `$MINIMAX_API_KEY` env var
57+
3. `~/.minimax/credentials.json` (OAuth)
58+
4. `api_key` in `~/.minimax/config.yaml`
59+
60+
## Configuration
61+
62+
Config precedence: flag > env var > config file > default.
63+
64+
Config file: `~/.minimax/config.yaml`

install.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
set -e
3+
4+
REPO="MiniMax-AI-Dev/minimax-cli"
5+
INSTALL_DIR="${MINIMAX_INSTALL_DIR:-/usr/local/bin}"
6+
7+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
8+
ARCH=$(uname -m)
9+
case "$ARCH" in
10+
x86_64) ARCH="x64" ;;
11+
aarch64|arm64) ARCH="arm64" ;;
12+
*) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;;
13+
esac
14+
15+
BINARY="minimax-${OS}-${ARCH}"
16+
URL="https://github.com/${REPO}/releases/latest/download/${BINARY}"
17+
18+
echo "Downloading ${BINARY}..."
19+
curl -fsSL "$URL" -o "${INSTALL_DIR}/minimax"
20+
chmod +x "${INSTALL_DIR}/minimax"
21+
echo "Installed minimax to ${INSTALL_DIR}/minimax"
22+
"${INSTALL_DIR}/minimax" --version

package.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "minimax-cli",
3+
"version": "0.1.0",
4+
"private": true,
5+
"description": "CLI for the MiniMax API Platform (Token Plan)",
6+
"type": "module",
7+
"bin": {
8+
"minimax": "./dist/minimax.mjs"
9+
},
10+
"files": [
11+
"dist/minimax.mjs"
12+
],
13+
"scripts": {
14+
"dev": "bun run src/main.ts",
15+
"build": "bun run build.ts",
16+
"lint": "eslint src/ test/",
17+
"typecheck": "tsc --noEmit",
18+
"test": "bun test",
19+
"test:watch": "bun test --watch",
20+
"build:npm": "bun build src/main.ts --outfile dist/minimax.mjs --target node --define \"process.env.CLI_VERSION='$npm_package_version'\" && printf '#!/usr/bin/env node\\n' | cat - dist/minimax.mjs > dist/tmp && mv dist/tmp dist/minimax.mjs && chmod +x dist/minimax.mjs",
21+
"prepublishOnly": "bun run build:npm"
22+
},
23+
"dependencies": {
24+
"yaml": "^2.7.1",
25+
"zod": "^3.24.4"
26+
},
27+
"devDependencies": {
28+
"typescript": "^5.8.3",
29+
"@types/bun": "latest",
30+
"eslint": "^9.24.0"
31+
}
32+
}

src/args.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { GlobalFlags } from './types/flags';
2+
3+
export interface ParsedArgs {
4+
commandPath: string[];
5+
flags: GlobalFlags;
6+
}
7+
8+
export function parseArgs(argv: string[]): ParsedArgs {
9+
const commandPath: string[] = [];
10+
const flags: GlobalFlags = {
11+
quiet: false,
12+
verbose: false,
13+
noColor: false,
14+
yes: false,
15+
dryRun: false,
16+
help: false,
17+
};
18+
19+
let i = 0;
20+
while (i < argv.length) {
21+
const arg = argv[i]!;
22+
23+
if (arg === '--help' || arg === '-h') {
24+
flags.help = true;
25+
i++;
26+
continue;
27+
}
28+
29+
if (arg === '--') {
30+
i++;
31+
break;
32+
}
33+
34+
if (arg.startsWith('--')) {
35+
const eqIndex = arg.indexOf('=');
36+
let key: string;
37+
let value: string | undefined;
38+
39+
if (eqIndex !== -1) {
40+
key = arg.slice(2, eqIndex);
41+
value = arg.slice(eqIndex + 1);
42+
} else {
43+
key = arg.slice(2);
44+
}
45+
46+
const camelKey = kebabToCamel(key);
47+
48+
// Boolean flags
49+
if (['quiet', 'verbose', 'noColor', 'yes', 'dryRun', 'help', 'stream',
50+
'subtitles', 'autoLyrics', 'wait', 'noBrowser'].includes(camelKey)) {
51+
(flags as Record<string, unknown>)[camelKey] = true;
52+
i++;
53+
continue;
54+
}
55+
56+
// Value flags
57+
if (value === undefined) {
58+
i++;
59+
value = argv[i];
60+
}
61+
62+
if (value === undefined) {
63+
throw new Error(`Flag --${key} requires a value.`);
64+
}
65+
66+
// Repeatable flags
67+
if (['message', 'tool', 'pronunciation'].includes(camelKey)) {
68+
const arr = (flags as Record<string, unknown>)[camelKey] as string[] | undefined;
69+
if (arr) {
70+
arr.push(value);
71+
} else {
72+
(flags as Record<string, unknown>)[camelKey] = [value];
73+
}
74+
} else if (['maxTokens', 'temperature', 'topP', 'speed', 'volume',
75+
'pitch', 'sampleRate', 'bitrate', 'channels', 'n',
76+
'timeout', 'pollInterval'].includes(camelKey)) {
77+
(flags as Record<string, unknown>)[camelKey] = Number(value);
78+
} else {
79+
(flags as Record<string, unknown>)[camelKey] = value;
80+
}
81+
i++;
82+
continue;
83+
}
84+
85+
// Positional argument — part of command path
86+
commandPath.push(arg);
87+
i++;
88+
}
89+
90+
return { commandPath, flags };
91+
}
92+
93+
function kebabToCamel(str: string): string {
94+
return str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
95+
}

0 commit comments

Comments
 (0)