From 03621f82bcda3d95deb59f5fdbf4dfdd899eb32f Mon Sep 17 00:00:00 2001 From: Ryo Sato <97249122+mizuamedesu@users.noreply.github.com> Date: Sun, 7 Jun 2026 14:16:57 +0900 Subject: [PATCH] feat: skills --- README.md | 14 ++ install.sh | 263 +++++++++++++++++++++ skills/mdx-cli/SKILL.md | 93 ++++++++ skills/mdx-cli/agents/openai.yaml | 4 + skills/mdx-cli/references/cli-spec.md | 319 ++++++++++++++++++++++++++ 5 files changed, 693 insertions(+) create mode 100755 install.sh create mode 100644 skills/mdx-cli/SKILL.md create mode 100644 skills/mdx-cli/agents/openai.yaml create mode 100644 skills/mdx-cli/references/cli-spec.md diff --git a/README.md b/README.md index 59a7ac3..b1deb54 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,20 @@ Web ポータル (oprpl.mdx.jp) の操作をコマンドラインから実行で uv tool install . ``` +## Skill としてインストール + +Codex / Claude Code の skill として使う場合: + +```bash +curl -fsSL https://raw.githubusercontent.com/aida0710/mdx-cli/main/install.sh | sh +``` + +ローカルのこのリポジトリから入れる場合: + +```bash +sh install.sh --source . --codex-only +``` + ### アップデート ```bash diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..c5e04ef --- /dev/null +++ b/install.sh @@ -0,0 +1,263 @@ +#!/usr/bin/env sh +set -eu + +DEFAULT_REPO="aida0710/mdx-cli" +DEFAULT_REF="main" +SKILLS_SUBDIR="${SKILLS_SUBDIR:-skills}" + +REPO="${REPO:-$DEFAULT_REPO}" +REF="${REF:-$DEFAULT_REF}" +TARGETS="${INSTALL_TARGETS:-both}" +SOURCE_DIR="${SOURCE_DIR:-}" +DRY_RUN=0 +BACKUP=1 +TMP_DIR="" + +usage() { + cat <<'EOF' +Install mdx-cli skills into Codex and/or Claude Code. + +Usage: + sh install.sh [options] + +Options: + --codex-only Install only to Codex + --claude-only Install only to Claude Code + --targets LIST comma-separated: codex,claude,both + --repo OWNER/REPO GitHub repository to download when not run locally + --ref REF Git ref to download when not run locally + --source DIR Local repository directory containing skills/ + --no-backup Replace existing skills without creating .bak copies + --dry-run Show what would be installed + -h, --help Show this help + +Environment: + INSTALL_TARGETS codex, claude, or both + CODEX_SKILLS_DIR Override Codex skills destination + CLAUDE_SKILLS_DIR Override Claude Code skills destination + REPO GitHub OWNER/REPO for remote install + REF Git ref for remote install +EOF +} + +log() { + printf '%s\n' "$*" +} + +warn() { + printf 'warning: %s\n' "$*" >&2 +} + +die() { + printf 'error: %s\n' "$*" >&2 + exit 1 +} + +need_command() { + command -v "$1" >/dev/null 2>&1 || die "missing required command: $1" +} + +cleanup() { + if [ -n "$TMP_DIR" ] && [ -d "$TMP_DIR" ]; then + rm -rf "$TMP_DIR" + fi +} + +trap cleanup EXIT INT TERM + +while [ "$#" -gt 0 ]; do + case "$1" in + --codex-only) + TARGETS="codex" + ;; + --claude-only) + TARGETS="claude" + ;; + --targets) + [ "$#" -ge 2 ] || die "--targets requires a value" + TARGETS="$2" + shift + ;; + --repo) + [ "$#" -ge 2 ] || die "--repo requires a value" + REPO="$2" + shift + ;; + --ref) + [ "$#" -ge 2 ] || die "--ref requires a value" + REF="$2" + shift + ;; + --source) + [ "$#" -ge 2 ] || die "--source requires a value" + SOURCE_DIR="$2" + shift + ;; + --no-backup) + BACKUP=0 + ;; + --dry-run) + DRY_RUN=1 + ;; + -h|--help) + usage + exit 0 + ;; + *) + die "unknown option: $1" + ;; + esac + shift +done + +case "$TARGETS" in + both|all|codex,claude|claude,codex) + WANT_CODEX=1 + WANT_CLAUDE=1 + ;; + codex) + WANT_CODEX=1 + WANT_CLAUDE=0 + ;; + claude) + WANT_CODEX=0 + WANT_CLAUDE=1 + ;; + *) + die "invalid targets: $TARGETS" + ;; +esac + +CODEX_DEST="${CODEX_SKILLS_DIR:-${CODEX_HOME:-$HOME/.codex}/skills}" +CLAUDE_DEST="${CLAUDE_SKILLS_DIR:-${CLAUDE_HOME:-$HOME/.claude}/skills}" + +script_dir() { + case "$0" in + */*) dirname "$0" ;; + *) printf '.' ;; + esac +} + +resolve_source() { + if [ -n "$SOURCE_DIR" ]; then + [ -d "$SOURCE_DIR/$SKILLS_SUBDIR" ] || die "no $SKILLS_SUBDIR/ directory in $SOURCE_DIR" + printf '%s/%s\n' "$SOURCE_DIR" "$SKILLS_SUBDIR" + return + fi + + if [ -f "$0" ]; then + SCRIPT_DIR=$(cd "$(script_dir)" 2>/dev/null && pwd || printf '.') + if [ -d "$SCRIPT_DIR/$SKILLS_SUBDIR" ]; then + printf '%s/%s\n' "$SCRIPT_DIR" "$SKILLS_SUBDIR" + return + fi + fi + + need_command curl + need_command tar + TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/mdx-cli-skills.XXXXXX") + ARCHIVE="$TMP_DIR/repo.tar.gz" + URL="https://codeload.github.com/$REPO/tar.gz/$REF" + + warn "downloading $REPO@$REF" + curl -fsSL "$URL" -o "$ARCHIVE" + tar -xzf "$ARCHIVE" -C "$TMP_DIR" + ROOT_DIR=$(find "$TMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -n 1) + [ -n "$ROOT_DIR" ] || die "downloaded archive did not contain a directory" + [ -d "$ROOT_DIR/$SKILLS_SUBDIR" ] || die "downloaded archive does not contain $SKILLS_SUBDIR/" + printf '%s/%s\n' "$ROOT_DIR" "$SKILLS_SUBDIR" +} + +valid_skill_name() { + case "$1" in + ''|.*|_*|*/*|*' '*|*-|-*|*--*|*[!abcdefghijklmnopqrstuvwxyz0123456789-]*) + return 1 + ;; + *) + return 0 + ;; + esac +} + +validate_skill() { + skill_dir="$1" + skill_name="$2" + skill_md="$skill_dir/SKILL.md" + + valid_skill_name "$skill_name" || die "invalid skill directory name: $skill_name" + [ -f "$skill_md" ] || die "$skill_name is missing SKILL.md" + first_line=$(sed -n '1p' "$skill_md") + [ "$first_line" = "---" ] || die "$skill_name/SKILL.md must start with YAML frontmatter" + grep -Eq '^name:[[:space:]]*' "$skill_md" || die "$skill_name/SKILL.md is missing name:" + grep -Eq '^description:[[:space:]]*' "$skill_md" || die "$skill_name/SKILL.md is missing description:" + + if grep -Eq '^[[:space:]]*(disable-model-invocation|user-invocable|argument-hint|arguments|context|agent|hooks|paths|shell|model|effort|when_to_use):' "$skill_md"; then + warn "$skill_name uses Claude-specific frontmatter; install to Codex only if you have verified compatibility" + fi +} + +install_one() { + skill_dir="$1" + skill_name="$2" + dest_root="$3" + label="$4" + + if [ "$DRY_RUN" -eq 1 ]; then + log "Would install $skill_name to $label: $dest_root/$skill_name" + return + fi + + mkdir -p "$dest_root" + staging="$dest_root/.$skill_name.tmp.$$" + rm -rf "$staging" + mkdir -p "$staging" + (cd "$skill_dir" && tar -cf - .) | (cd "$staging" && tar -xf -) + + if [ -d "$dest_root/$skill_name" ]; then + if [ "$BACKUP" -eq 1 ]; then + backup="$dest_root/$skill_name.bak.$(date +%Y%m%d%H%M%S)" + mv "$dest_root/$skill_name" "$backup" + log "Backed up $label $skill_name to $backup" + else + rm -rf "$dest_root/$skill_name" + fi + fi + + mv "$staging" "$dest_root/$skill_name" + log "Installed $skill_name to $label: $dest_root/$skill_name" +} + +SRC=$(resolve_source) +[ -d "$SRC" ] || die "skills source not found: $SRC" + +FOUND=0 +for skill_dir in "$SRC"/*; do + [ -d "$skill_dir" ] || continue + skill_name=$(basename "$skill_dir") + case "$skill_name" in + .*|_*) continue ;; + esac + [ -f "$skill_dir/SKILL.md" ] || continue + validate_skill "$skill_dir" "$skill_name" + FOUND=$((FOUND + 1)) +done + +[ "$FOUND" -gt 0 ] || die "no installable skills found in $SRC" + +for skill_dir in "$SRC"/*; do + [ -d "$skill_dir" ] || continue + skill_name=$(basename "$skill_dir") + case "$skill_name" in + .*|_*) continue ;; + esac + [ -f "$skill_dir/SKILL.md" ] || continue + + if [ "$WANT_CODEX" -eq 1 ]; then + install_one "$skill_dir" "$skill_name" "$CODEX_DEST" "Codex" + fi + if [ "$WANT_CLAUDE" -eq 1 ]; then + install_one "$skill_dir" "$skill_name" "$CLAUDE_DEST" "Claude Code" + fi +done + +log "Done. Restart Codex or Claude Code if the new skill does not appear immediately." diff --git a/skills/mdx-cli/SKILL.md b/skills/mdx-cli/SKILL.md new file mode 100644 index 0000000..7a371b6 --- /dev/null +++ b/skills/mdx-cli/SKILL.md @@ -0,0 +1,93 @@ +--- +name: mdx-cli +description: MDX 1 cloud infrastructure CLI operations guide. Use when Codex needs to plan, explain, or execute mdx-cli commands for MDX authentication, project selection, VM inventory/deploy/lifecycle/SSH/CSV, network segments, ACL, DNAT, global IP checks, templates, tasks, JSON output, or safe bulk operations. +--- + +# MDX CLI + +## Overview + +Use this skill to operate the `mdx` command safely and accurately. The CLI controls real MDX 1 cloud resources, so prefer read-only discovery first, quote VM name patterns, and require explicit user intent before destructive or bulk changes. + +For exact command syntax, options, and behavior, read [references/cli-spec.md](references/cli-spec.md). + +## Safety Rules + +- Confirm the user is on MDX VPN or inside an MDX VM before commands that contact `oprpl.mdx.jp` or `mdxidm.mdx.jp`. +- Use `--json` for read-only discovery when the result will be parsed or filtered. +- Resolve the project explicitly with `--project-id`, `MDX_PROJECT_ID`, or `mdx project select` before project-scoped commands. +- Quote shell patterns such as `"worker-*"` and `"worker-{a-c}-{0-9}"`. +- Before `start`, `stop`, `shutdown`, `reboot`, `reset`, `destroy`, `reconfigure`, ACL/DNAT delete, or `--fix`, show the target set and get explicit user confirmation unless the user already gave that exact instruction. +- Treat `reset`, `destroy`, `network check-ip --fix`, and `network check-acl --fix` as high-risk operations. Do a read-only check first. +- For long tasks, use `--no-wait` only when the user wants asynchronous execution; then give `mdx task status ` or `mdx task wait ` as the follow-up. + +## Common Workflows + +Initial setup: + +```bash +mdx auth login +mdx project select +mdx project summary +mdx vm list +``` + +Inventory and planning: + +```bash +mdx project summary --json +mdx template list --json +mdx vm list --json +mdx network segment list --json +mdx network ips --json +``` + +Deploy VMs: + +```bash +mdx vm deploy \ + --template "Ubuntu 22.04" \ + --name "worker-{0-9}" \ + --pack-type cpu \ + --pack-num 3 \ + --disk 40 \ + --service-level spot \ + --key ~/.ssh/id_ed25519.pub \ + --power-on +``` + +Bulk VM operations: + +```bash +mdx vm list --json +mdx vm shutdown "worker-*" +mdx vm destroy "worker-*" --no-wait +``` + +Network inspection and cleanup: + +```bash +mdx network ips +mdx network check-ip +mdx network check-acl +``` + +## Command Selection + +- Use `auth` for login/logout/status. +- Use `project` to list, select, summarize, inspect storage, or list access keys. +- Use `vm` for VM inventory, deploy, lifecycle operations, reconfiguration, SSH, sync, and CSV export. +- Use `network segment` for segment list/show. +- Use `network acl` for interactive ACL list/add/edit/delete. +- Use `network dnat` for interactive DNAT list/add/edit/delete. +- Use `network ips`, `check-ip`, and `check-acl` for global IP and stale rule audits. +- Use `template` for template list/show and to choose deploy parameters. +- Use `task` for operation history and task polling. + +## Notes For Execution + +- `mdx auth login` is interactive and needs username, password, and OTP. MDX Local Auth is supported; Gakunin is not. +- Credentials and selected project are stored under `~/.config/mdx-cli/`; username/password use keyring and token/project ID are stored by the credential store. +- `--verbose` is a global flag for detailed API logs. +- The CLI auto-refreshes tokens where possible; bulk parallel operations proactively refresh every 30 VMs. +- The default API base URL is `https://oprpl.mdx.jp`. diff --git a/skills/mdx-cli/agents/openai.yaml b/skills/mdx-cli/agents/openai.yaml new file mode 100644 index 0000000..d22807d --- /dev/null +++ b/skills/mdx-cli/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "MDX CLI" + short_description: "MDX 1 CLI operations and safety guide" + default_prompt: "Use $mdx-cli to plan safe MDX VM, network, project, template, and task CLI operations." diff --git a/skills/mdx-cli/references/cli-spec.md b/skills/mdx-cli/references/cli-spec.md new file mode 100644 index 0000000..0d18221 --- /dev/null +++ b/skills/mdx-cli/references/cli-spec.md @@ -0,0 +1,319 @@ +# MDX CLI Specification + +This reference describes the `mdx` CLI implemented by `mdx_cli.main:app`. + +## Runtime + +- Package script: `mdx = mdx_cli.main:app` +- Python: 3.13+ +- Install locally: `uv tool install .` +- Update locally: `git pull && uv tool install . --force` +- Must run from MDX internal network, MDX VPN, or an MDX VM. +- API base: `https://oprpl.mdx.jp` +- SSO IdP: `mdxidm.mdx.jp` + +## Global Options + +| Option | Meaning | +|---|---| +| `--verbose`, `-v` | Enable API request/response debug logging. | +| `--install-completion` | Install shell completion through Typer. | +| `--show-completion` | Print shell completion script. | +| `--help` | Show help. | + +Project-scoped commands usually accept `--project-id`, `-p`; it also reads `MDX_PROJECT_ID`. If omitted, the saved project from `mdx project select` is used. + +## Environment And Settings + +| Environment variable | Default | Meaning | +|---|---|---| +| `MDX_BASE_URL` | `https://oprpl.mdx.jp` | API base URL. | +| `MDX_DEFAULT_PROJECT_ID` | unset | Pydantic setting, but project resolution uses CLI option, `MDX_PROJECT_ID`, or saved project. | +| `MDX_PROJECT_ID` | unset | Typer env var for project-scoped commands. | +| `MDX_REQUEST_TIMEOUT` | `120` | HTTP timeout in seconds. | +| `MDX_TASK_POLL_INTERVAL` | `3` | Task polling interval in seconds. | +| `MDX_TASK_POLL_TIMEOUT` | `600` | Task polling timeout in seconds. | + +Configuration directory: `~/.config/mdx-cli/`. + +## Output + +- Most list/show commands default to Rich table output. +- Use `--json` for JSON output and quiet API spinners. +- For automation, prefer `--json | jq ...`. + +## Name Patterns + +VM targets and deploy names support patterns: + +| Pattern | Meaning | +|---|---| +| `my-vm` | One VM. | +| `my-vm-{0-9}` | `my-vm-0` through `my-vm-9`; deploy converts single-digit numeric ranges to MDX API `[0-9]` notation. | +| `crawler-{a-g}-{0-9}` | 70 VMs; deploy batches by alphabet range and server-side numeric expansion. | +| `node-{00-05}` | Zero-padded range, client-expanded. | +| `vm-{1-99}` | Multi-digit range, client-expanded. | +| `worker-*` | Shell-style match for existing VM names. Quote this in the shell. | + +Always quote patterns containing `*`, `{}`, or spaces. + +## Authentication + +| Command | Purpose | Notes | +|---|---|---| +| `mdx auth login` | Login through Shibboleth SSO. | Interactive username/password/OTP. Saved username/password become defaults. MDX Local Auth only; Gakunin is unsupported. | +| `mdx auth status` | Show whether a token is saved. | Shows saved username when available. | +| `mdx auth logout` | Delete token and saved credentials. | Removes all stored credentials for this CLI. | + +Tokens are auto-refreshed where possible. If token refresh fails, normal API auth handling can prompt re-login. + +## Projects + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx project list [--json]` | none | List assigned projects. | +| `mdx project summary [-p ID] [--json]` | project optional | Show VM counts, disk/pack resources, and storage usage. | +| `mdx project select` | interactive | Select and save the default project. | +| `mdx project show [--json]` | required ID | Show project summary. | +| `mdx project storage [--json]` | required ID | Show storage information. | +| `mdx project keys [--json]` | required ID | List access keys. | + +`project select` flattens nested projects in organizations and saves the selected UUID. + +## VM Inventory + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx vm list [-p ID] [--json]` | project optional | List VMs. | +| `mdx vm show [target] [-p ID] [--json]` | target is UUID or exact VM name; omitted means interactive list | Show VM detail including OS, resources, disks, service networks, storage networks, and VMware Tools. | +| `mdx vm sync [-p ID]` | project optional | Request VM information sync. | +| `mdx vm csv [target] [-p ID] [-o PATH]` | target pattern optional | Export Web portal-compatible CSV. Without `-o`, writes stdout. | + +`vm csv` fetches per-VM CSV detail in parallel and emits columns: + +- `VM_NAME` +- `SERVICE_NET__IPv4` and `SERVICE_NET__IPv6` for `n = 1..8` +- `STORAGE_NET__IPv4` and `STORAGE_NET__IPv6` for `n = 1..8` + +## VM Deploy + +Command: + +```bash +mdx vm deploy [options] +``` + +Options: + +| Option | Meaning | +|---|---| +| `--project-id ID`, `-p ID` | Project ID; falls back to saved project or `MDX_PROJECT_ID`. | +| `--template TEXT`, `-t TEXT` | Template name substring, case-insensitive; first match is used. | +| `--name PATTERN`, `-n PATTERN` | VM name or batch pattern. | +| `--pack-type cpu|gpu` | CPU or GPU pack. | +| `--pack-num N` | Number of packs. CPU max is 152; GPU max is 8. | +| `--disk GB` | Disk size in GB. | +| `--service-level spot|guarantee` | Service level. | +| `--key PATH`, `-k PATH` | SSH public key path. Must be absolute or `~/...`. | +| `--power-on` | Start VM after deploy. | +| `--yes`, `-y` | Skip confirmation and nonessential prompts. If multiple segments exist and `--yes` is used, the first segment is selected. | +| `--no-wait` | Do not wait for deploy tasks. | + +Interactive defaults: + +- Template: numbered list. +- Segment: first segment automatically when only one; numbered list when multiple and not `--yes`. +- SSH public key: first of `~/.ssh/id_ed25519.pub`, `id_rsa.pub`, `id_ecdsa.pub`, then first `*.pub`. +- Pack type: `cpu` or `gpu`. +- Pack number: default `3` for CPU, `1` for GPU. +- Disk: template `lower_limit_disk`. +- Service level: `spot` or `guarantee`. +- Auto power-on: confirm unless `--power-on` or `--yes`. + +Deploy creates one or more tasks. Without `--no-wait`, it waits for all task IDs in parallel and prints final status. + +Example: + +```bash +mdx vm deploy \ + -t "Ubuntu 22.04" \ + -n "worker-{a-e}-{0-9}" \ + --pack-type cpu \ + --pack-num 3 \ + --disk 40 \ + --service-level spot \ + -k ~/.ssh/id_ed25519.pub \ + --power-on \ + -y \ + --no-wait +``` + +## VM Lifecycle + +All target arguments accept UUID, exact name, or name pattern. + +| Command | Arguments/options | Behavior | +|---|---|---| +| `mdx vm start [-p ID] [-s LEVEL]` | `--service-level`, `-s`, default `spot` | Starts VMs with the requested service level. Confirms only when multiple VMs match. | +| `mdx vm stop [-p ID]` | none | Force power off. Use `shutdown` for graceful OS shutdown. Confirms only when multiple VMs match. | +| `mdx vm shutdown [-p ID]` | none | Graceful shutdown. Confirms only when multiple VMs match. | +| `mdx vm reboot [-p ID]` | none | Reboot. Confirms only when multiple VMs match. | +| `mdx vm reset [-p ID]` | none | Hard reset. Always asks a high-risk confirmation. | +| `mdx vm destroy [-p ID] [--no-wait]` | none | Deletes VMs. If any are `PowerON`, force-stops them, waits for power-off, then destroys. Always asks a high-risk confirmation. | + +Bulk lifecycle operations run in chunks of 30 and refresh the token before each chunk. Parallel action progress is displayed. + +## VM Reconfigure + +Command: + +```bash +mdx vm reconfigure [target] [-p ID] [--no-wait] +``` + +Behavior: + +- If target is omitted, choose from a VM list. +- If target is a pattern, multiple VMs can be reconfigured together. +- Fetches full VM details before reconfiguring. +- For multiple VMs, all must have the same `pack_type` and the same disk count. +- If any target is `PowerON`, asks to shut down, sends shutdown requests, and waits for power-off. +- Prompts for new pack count and each disk capacity. +- Keeps each VM's existing disk `device_key` and network segment where possible. +- Without `--no-wait`, waits for reconfigure task completion. + +## VM SSH + +Command: + +```bash +mdx vm ssh [target] [-p ID] [-u USER] [-i KEY] [-g] +``` + +Options: + +| Option | Meaning | +|---|---| +| `--user USER`, `-u USER` | SSH username; default `mdxuser`. | +| `--identity PATH`, `-i PATH` | Private key path, `~/` supported. | +| `--global`, `-g` | Use global IP instead of private IP when available. | + +If target is omitted, it lists only `PowerON` VMs. It uses the first service network. When the user is still the default `mdxuser`, it tries to infer the template `login_username` from template metadata and VM host name. The command ends by `execvp`-ing `ssh`. + +## Network Segments + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx network segment list [-p ID] [--json]` | project optional | List segments. | +| `mdx network segment show [segment-id] [-p ID] [--json]` | segment optional | Show segment summary; if omitted, auto-selects only segment or asks from list. | + +## Global IP Checks + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx network ips [-p ID] [--json]` | project optional | List assignable global IPs. | +| `mdx network check-ip [-p ID] [--json] [--fix]` | project optional | Show global IPv4 usage across assignable IPs, DNAT, and direct VM assignment. Detects stale DNAT to dead `10.15.*` VM IPs. | +| `mdx network check-acl [-p ID] [--json] [--fix]` | project optional | Scan all segment ACLs for host rules to dead `10.15.*` VM IPs. | + +`check-ip` status values in JSON: + +- `未使用` +- `VM割当` +- `DNAT` + +`check-acl` status values in JSON: + +- `alive` +- `hole` +- `range` + +Deletion safety: + +- Without `--fix`, stale DNAT/ACL deletion is interactive. +- With `--fix`, stale rules are deleted without confirmation. +- If VM detail fetching partially fails, cleanup is skipped to prevent false deletion. + +## DNAT + +Commands live under `mdx network dnat`. + +| Command | Arguments/options | Behavior | +|---|---|---| +| `list [-p ID] [--json]` | project optional | List DNAT rules. | +| `add [-p ID]` | interactive | Pick an assignable global IP, resolve segment, enter private destination IP, confirm, create DNAT. | +| `edit [dnat-id] [-p ID]` | interactive | Pick existing rule if ID omitted, choose global IP, resolve segment, edit destination IP, confirm, update. | +| `delete [dnat-id] [-p ID] [-y]` | interactive unless ID and `-y` | Delete DNAT rule. | + +## ACL + +Commands live under `mdx network acl`. + +| Command | Arguments/options | Behavior | +|---|---|---| +| `list [segment-id] [-p ID] [--json]` | segment optional | Resolve segment and list ACLs. | +| `add [segment-id] [-p ID] [--json]` | interactive | Select protocol `TCP`/`UDP`/`ICMP`, source address/mask/port, destination address/mask/port, confirm, create ACL. | +| `edit [acl-id] [--segment-id ID] [-p ID] [--json]` | interactive | Resolve segment, find ACL, edit current values, confirm, update ACL. | +| `delete [acl-id] [--segment-id ID] [-p ID] [-y]` | interactive unless ID and `-y` | Delete ACL rule. | + +Defaults for ACL add: + +- Source address: `0.0.0.0` +- Source mask: `0.0.0.0` +- Source port: `Any` for TCP/UDP; omitted for ICMP +- Destination mask: `255.255.255.255` +- Destination port: `Any` for TCP/UDP; omitted for ICMP + +## Templates + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx template list [-p ID] [--json]` | project optional | List templates. | +| `mdx template show [template-id] [-p ID] [--json]` | template optional | Show detail; if omitted, choose from list. | + +Template detail includes UUID, template name, OS, OS type, GPU requirement, minimum disk, minimum memory, hardware version, login username, description, creator, publication date, scope, and summary URL when present. + +## Tasks And History + +| Command | Arguments/options | Purpose | +|---|---|---| +| `mdx task list [-p ID] [-n LIMIT] [-t TYPE] [--json]` | limit default `100`, max `1000` | List operation history. | +| `mdx task status [--json]` | required task ID | Show task status. | +| `mdx task wait [--json]` | required task ID | Wait for completion using configured poll interval and timeout. | + +Common `--type`, `-t` values are operation names such as `デプロイ` and `自動休止`. + +## Safe Planning Examples + +List running VMs: + +```bash +mdx vm list --json | jq '.[] | select(.status == "PowerON") | .name' +``` + +Inspect before bulk shutdown: + +```bash +mdx vm list --json | jq '.[] | select(.name | test("^worker-")) | {name, status, uuid}' +mdx vm shutdown "worker-*" +``` + +Deploy then follow tasks manually: + +```bash +mdx vm deploy -t "Ubuntu" -n "worker-{0-9}" --pack-type cpu --pack-num 3 --disk 40 --service-level spot -k ~/.ssh/id_ed25519.pub --no-wait +mdx task status +``` + +Audit network holes without deleting: + +```bash +mdx network check-ip +mdx network check-acl +``` + +Delete stale holes only after explicit approval: + +```bash +mdx network check-ip --fix +mdx network check-acl --fix +```