diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cd6d2c..0ca5d17 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,25 +12,3 @@ jobs: - run: bun install - run: bun run typecheck - run: bun test - - release: - needs: test - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun install - - - name: Build all targets - run: VERSION=${GITHUB_REF_NAME} bun run build.ts - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - files: | - dist/minimax-* - dist/minimax.mjs - dist/manifest.json - generate_release_notes: true diff --git a/README.md b/README.md index ef5fd70..913558d 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,10 @@ Supports **Global** (`api.minimax.io`) and **CN** (`api.minimaxi.com`) with auto ## Install +Requires [Node.js](https://nodejs.org) 18+. + ```bash -curl -fsSL https://raw.githubusercontent.com/MiniMax-AI-Dev/cli/main/install.sh | sh +npm install -g minimax-cli ``` --- diff --git a/README_CN.md b/README_CN.md index 9983576..0e46d0f 100644 --- a/README_CN.md +++ b/README_CN.md @@ -10,8 +10,10 @@ ## 安装 +需要 [Node.js](https://nodejs.org) 18+。 + ```bash -curl -fsSL https://raw.githubusercontent.com/MiniMax-AI-Dev/cli/main/install.sh | sh +npm install -g minimax-cli ``` --- diff --git a/build.ts b/build.ts index 0d03ea5..b611dd4 100644 --- a/build.ts +++ b/build.ts @@ -1,63 +1,19 @@ -import { $ } from 'bun'; -import { createHash } from 'crypto'; import { readFileSync, writeFileSync } from 'fs'; const VERSION = process.env.VERSION ?? 'dev'; - -const targets = [ - { bunTarget: 'bun-linux-x64', platform: 'linux-x64', output: 'minimax-linux-x64' }, - { bunTarget: 'bun-linux-x64-musl', platform: 'linux-x64-musl', output: 'minimax-linux-x64-musl' }, - { bunTarget: 'bun-linux-arm64', platform: 'linux-arm64', output: 'minimax-linux-arm64' }, - { bunTarget: 'bun-linux-arm64-musl', platform: 'linux-arm64-musl', output: 'minimax-linux-arm64-musl' }, - { bunTarget: 'bun-darwin-x64', platform: 'darwin-x64', output: 'minimax-darwin-x64' }, - { bunTarget: 'bun-darwin-arm64', platform: 'darwin-arm64', output: 'minimax-darwin-arm64' }, - { bunTarget: 'bun-windows-x64', platform: 'windows-x64', output: 'minimax-windows-x64.exe' }, -]; - -function sha256(path: string): string { - return createHash('sha256').update(readFileSync(path)).digest('hex'); -} - -console.log(`Building minimax-cli ${VERSION}...\n`); - -const manifest: { - version: string; - platforms: Record; -} = { version: VERSION, platforms: {} }; - -// Platform standalones -for (const { bunTarget, platform, output } of targets) { - const outPath = `dist/${output}`; - process.stdout.write(` ${output}...`); - - await $`bun build src/main.ts \ - --compile \ - --minify \ - --target ${bunTarget} \ - --outfile ${outPath} \ - --define "process.env.CLI_VERSION='${VERSION}'"`.quiet(); - - manifest.platforms[platform] = { file: output, checksum: sha256(outPath) }; - console.log(' ✓'); -} - -// Node.js .mjs bundle (much smaller, requires node >= 18) -process.stdout.write(' minimax.mjs...'); -const mjsPath = 'dist/minimax.mjs'; - -await $`bun build src/main.ts \ - --outfile ${mjsPath} \ - --target node \ - --minify \ - --define "process.env.CLI_VERSION='${VERSION}'"`.quiet(); - -// Prepend shebang so the file is directly executable -const mjsContent = readFileSync(mjsPath); -writeFileSync(mjsPath, Buffer.concat([Buffer.from('#!/usr/bin/env node\n'), mjsContent])); - -manifest.platforms['node'] = { file: 'minimax.mjs', checksum: sha256(mjsPath) }; -console.log(' ✓'); - -writeFileSync('dist/manifest.json', JSON.stringify(manifest, null, 2)); -console.log(' manifest.json ✓'); -console.log(`\nDone. ${targets.length + 1} builds in dist/`); +const OUT = 'dist/minimax.mjs'; + +await Bun.build({ + entrypoints: ['src/main.ts'], + outdir: 'dist', + naming: 'minimax.mjs', + target: 'node', + minify: true, + define: { 'process.env.CLI_VERSION': JSON.stringify(VERSION) }, +}); + +const content = readFileSync(OUT); +writeFileSync(OUT, Buffer.concat([Buffer.from('#!/usr/bin/env node\n'), content])); + +const size = (content.length / 1024).toFixed(0); +console.log(`dist/minimax.mjs ${size}KB`); diff --git a/install.sh b/install.sh deleted file mode 100644 index 745163e..0000000 --- a/install.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/sh -set -e - -# Usage: install.sh [stable|latest|VERSION] -CHANNEL="${1:-stable}" - -case "$CHANNEL" in - stable|latest) ;; - v*|[0-9]*) ;; - *) - echo "Usage: $0 [stable|latest|VERSION]" >&2 - exit 1 - ;; -esac - -REPO="MiniMax-AI-Dev/cli" -INSTALL_DIR="${MINIMAX_INSTALL_DIR:-$HOME/.local/bin}" - -# Dependency check: curl or wget -if command -v curl >/dev/null 2>&1; then - download() { curl -fsSL "$1"; } - download_to() { curl -fsSL -o "$2" "$1"; } -elif command -v wget >/dev/null 2>&1; then - download() { wget -qO- "$1"; } - download_to() { wget -qO "$2" "$1"; } -else - echo "curl or wget is required." >&2; exit 1 -fi - -# Prefer Node.js >= 18 (much smaller download, ~200KB vs ~57MB) -USE_NODE=0 -if command -v node >/dev/null 2>&1; then - NODE_MAJOR=$(node --version 2>/dev/null | sed 's/v//' | cut -d. -f1) - if [ -n "$NODE_MAJOR" ] && [ "$NODE_MAJOR" -ge 18 ] 2>/dev/null; then - USE_NODE=1 - fi -fi - -if [ "$USE_NODE" = "0" ]; then - # Detect OS - case "$(uname -s)" in - Darwin) OS="darwin" ;; - Linux) OS="linux" ;; - *) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;; - esac - - # Detect architecture - case "$(uname -m)" in - x86_64|amd64) ARCH="x64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; - esac - - # Rosetta 2: x64 shell on ARM Mac → use native arm64 binary - if [ "$OS" = "darwin" ] && [ "$ARCH" = "x64" ]; then - if [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = "1" ]; then - ARCH="arm64" - fi - fi - - # musl detection on Linux - PLATFORM="${OS}-${ARCH}" - if [ "$OS" = "linux" ]; then - if [ -f /lib/libc.musl-x86_64.so.1 ] || \ - [ -f /lib/libc.musl-aarch64.so.1 ] || \ - ldd /bin/ls 2>&1 | grep -q musl; then - PLATFORM="${OS}-${ARCH}-musl" - fi - fi -fi - -# Resolve version from channel -GH_API="https://api.github.com/repos/${REPO}" -case "$CHANNEL" in - stable) - VERSION=$(download "${GH_API}/releases/latest" \ - | grep '"tag_name"' | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/') - ;; - latest) - VERSION=$(download "${GH_API}/releases?per_page=1" \ - | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/') - ;; - *) - case "$CHANNEL" in v*) VERSION="$CHANNEL" ;; *) VERSION="v${CHANNEL}" ;; esac - ;; -esac - -if [ -z "$VERSION" ]; then - echo "Failed to resolve version." >&2; exit 1 -fi - -BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" - -# Fetch manifest and extract checksum -MANIFEST=$(download "${BASE_URL}/manifest.json") || { - echo "Failed to fetch manifest.json" >&2; exit 1 -} - -if [ "$USE_NODE" = "1" ]; then - PLATFORM="node" - DOWNLOAD_FILE="minimax.mjs" - echo "Installing minimax ${VERSION} (Node.js)..." -else - DOWNLOAD_FILE="minimax-${PLATFORM}" - echo "Installing minimax ${VERSION} for ${PLATFORM}..." -fi - -CHECKSUM=$(printf '%s' "$MANIFEST" | tr -d '\n' | \ - sed "s/.*\"${PLATFORM}\"[^}]*\"checksum\" *: *\"\([a-f0-9]*\)\".*/\1/") - -if [ -z "$CHECKSUM" ] || [ "${#CHECKSUM}" -ne 64 ]; then - echo "Platform '${PLATFORM}' not found in manifest." >&2; exit 1 -fi - -# Download to temp file -TMP=$(mktemp) -trap 'rm -f "$TMP"' EXIT - -download_to "${BASE_URL}/${DOWNLOAD_FILE}" "$TMP" || { - echo "Download failed." >&2; exit 1 -} - -# Verify SHA256 -if command -v shasum >/dev/null 2>&1; then - ACTUAL=$(shasum -a 256 "$TMP" | cut -d' ' -f1) -elif command -v sha256sum >/dev/null 2>&1; then - ACTUAL=$(sha256sum "$TMP" | cut -d' ' -f1) -else - echo "shasum or sha256sum is required." >&2; exit 1 -fi - -if [ "$ACTUAL" != "$CHECKSUM" ]; then - echo "Checksum verification failed." >&2; exit 1 -fi - -chmod +x "$TMP" -mkdir -p "$INSTALL_DIR" -mv "$TMP" "${INSTALL_DIR}/minimax" - -echo "Installed minimax ${VERSION} to ${INSTALL_DIR}/minimax" - -# Warn if install dir is not in PATH -case ":${PATH}:" in - *":${INSTALL_DIR}:"*) ;; - *) - printf '\nNote: %s is not in PATH. Add to your shell profile:\n' "$INSTALL_DIR" - printf ' export PATH="%s:$PATH"\n\n' "$INSTALL_DIR" - ;; -esac diff --git a/package.json b/package.json index 3bd394b..ef62cbf 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,20 @@ { "name": "minimax-cli", "version": "0.3.1", - "private": true, - "description": "CLI for the MiniMax API Platform (Token Plan)", + "description": "CLI for the MiniMax AI Platform", "type": "module", - "bin": { - "minimax": "./dist/minimax.mjs" - }, - "files": [ - "dist/minimax.mjs" - ], + "engines": { "node": ">=18" }, + "bin": { "minimax": "./dist/minimax.mjs" }, + "files": ["dist/minimax.mjs"], "scripts": { "dev": "bun run src/main.ts", "build": "bun run build.ts", - "build:dev": "bun build src/main.ts --compile --minify --outfile dist/minimax --define \"process.env.CLI_VERSION='$(node -p \"require('./package.json').version\")'\"", + "build:dev": "bun build src/main.ts --outfile dist/minimax.mjs --target node --minify --define \"process.env.CLI_VERSION='0.3.1'\" && printf '#!/usr/bin/env node\\n' | cat - dist/minimax.mjs > dist/tmp && mv dist/tmp dist/minimax.mjs && chmod +x dist/minimax.mjs", + "prepublishOnly": "bun run build", "lint": "eslint src/ test/", "typecheck": "tsc --noEmit", "test": "bun test", - "test:watch": "bun test --watch", - "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", - "prepublishOnly": "bun run build:npm" + "test:watch": "bun test --watch" }, "dependencies": { "@clack/prompts": "^0.7.0" diff --git a/src/commands/update.ts b/src/commands/update.ts index 527a305..2b4ef8b 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -1,57 +1,17 @@ import { defineCommand } from '../command'; -import { CLIError } from '../errors/base'; -import { ExitCode } from '../errors/codes'; -import { resolveUpdateTarget, applySelfUpdate, type Channel } from '../update/self-update'; -const CLI_VERSION = process.env.CLI_VERSION ?? '0.1.0'; +const CLI_VERSION = process.env.CLI_VERSION ?? '0.0.0'; export default defineCommand({ name: 'update', - description: 'Update minimax to a newer version', - usage: 'minimax update [stable|latest|VERSION]', - options: [ - { flag: '[channel]', description: 'Target: stable (default), latest, or a version like 0.2.0' }, - ], + description: 'Update minimax to the latest version', + usage: 'minimax update', examples: [ 'minimax update', - 'minimax update latest', - 'minimax update 0.2.0', ], - async run(config, flags) { - // Detect current binary path - const currentBin = process.execPath; - if (currentBin.endsWith('bun') || currentBin.endsWith('node')) { - throw new CLIError( - 'Self-update is only supported for standalone binary installs.\n' + - 'For npm installs, run: npm update -g minimax-cli', - ExitCode.USAGE, - ); - } - - const rawChannel = (flags._positional as string[] | undefined)?.[0] ?? 'stable'; - const validChannels = new Set(['stable', 'latest']); - const channel: Channel = validChannels.has(rawChannel) || /^\d/.test(rawChannel) || rawChannel.startsWith('v') - ? rawChannel - : (() => { throw new CLIError(`Unknown channel: ${rawChannel}`, ExitCode.USAGE); })(); - - process.stderr.write(`Checking for updates (channel: ${channel})...\n`); - const target = await resolveUpdateTarget(channel); - - if (target.version === `v${CLI_VERSION}`) { - process.stderr.write(`Already up to date (${CLI_VERSION}).\n`); - return; - } - - process.stderr.write(`Update available: v${CLI_VERSION} → ${target.version}\n`); - - if (config.dryRun) { - process.stderr.write(`Would replace: ${currentBin}\n`); - return; - } - - await applySelfUpdate(target, currentBin); - - process.stderr.write(`\nUpdated to ${target.version}.\n`); - process.stderr.write(`https://github.com/MiniMax-AI-Dev/minimax-cli/releases/tag/${target.version}\n`); + async run() { + process.stderr.write(`Current version: ${CLI_VERSION}\n\n`); + process.stderr.write('Run:\n'); + process.stderr.write(' npm update -g minimax-cli\n\n'); }, }); diff --git a/src/main.ts b/src/main.ts index 266a544..d97d403 100644 --- a/src/main.ts +++ b/src/main.ts @@ -70,8 +70,8 @@ async function main() { await updateCheckPromise; const newVersion = getPendingUpdateNotification(); if (newVersion && !config.quiet) { - process.stderr.write(`\n Update available: v${CLI_VERSION} → ${newVersion}\n`); - process.stderr.write(` Run 'minimax update' to upgrade.\n\n`); + process.stderr.write(`\n Update available: ${newVersion}\n`); + process.stderr.write(` npm update -g minimax-cli\n\n`); } }