with one click
jdcloud-vm-ops
// Use when you need to deploy, configure, troubleshoot, or monitor JD Cloud Virtual Machine (VM) via official API/SDK or official `jdc` CLI; user mentions VM, 云主机, CVM, or tasks target VM instances.
// Use when you need to deploy, configure, troubleshoot, or monitor JD Cloud Virtual Machine (VM) via official API/SDK or official `jdc` CLI; user mentions VM, 云主机, CVM, or tasks target VM instances.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | jdcloud-vm-ops |
| description | Use when you need to deploy, configure, troubleshoot, or monitor JD Cloud Virtual Machine (VM) via official API/SDK or official `jdc` CLI; user mentions VM, 云主机, CVM, or tasks target VM instances. |
| license | MIT |
| compatibility | Official JD Cloud SDK (Python 3.10+), valid API credentials, network access to JD Cloud endpoints, and official JD Cloud CLI (`jdc`). |
| metadata | {"author":"jdcloud","version":"1.4.0","last_updated":"2026-05-07","runtime":"Harness AI Agent","api_profile":"VM API v1.0 - https://docs.jdcloud.com/cn/virtual-machines/api","cli_applicability":"jdc-first-with-fallback","cli_support_evidence":"Official `jdc` CLI supports VM operations. Verified via `jdc vm --help` and documentation at https://docs.jdcloud.com/cn/cli","environment":["JDC_ACCESS_KEY","JDC_SECRET_KEY","JDC_REGION"]} |
This skill follows the Agent Skill OpenSpec.
JD Cloud Virtual Machine (VM) is a core computing service that provides elastic, scalable, and secure cloud servers. This skill enables efficient operations, including automated deployment, real-time monitoring, and rapid troubleshooting of VM resources.
cli_applicability: jdc-first-with-fallback: Official jdc CLI supports VM operations. The Agent MUST attempt to use jdc as the primary execution path. If jdc installation or command execution fails, the Agent MUST retry up to 3 times (with exponential backoff). Only after 3 consecutive failures should the Agent fall back to SDK/API. Both paths MUST be documented.jdcloud-cloudmonitor-opsjdcloud-vpc-opsjdcloud-lb-opsjdcloud-billing-opsjdcloud-cloudmonitor-ops for metrics datajdcloud-vpc-ops, then return hereThis Skill uses structured placeholders to avoid prompt injection and parsing ambiguity:
| Placeholder | Meaning | Agent Action |
|---|---|---|
{{env.JDC_ACCESS_KEY}} | Resolved from Agent runtime environment | NEVER prompt user for this; fail if not set |
{{env.JDC_SECRET_KEY}} | Resolved from Agent runtime environment | NEVER prompt user for this; fail if not set |
{{env.JDC_REGION}} | Resolved from Agent runtime environment | Use cn-north-1 as default if unset |
{{user.region}} | Must be collected from user | Ask user once and reuse |
{{user.instance_id}} | Must be collected from user | Ask user once and reuse |
{{user.instance_name}} | Must be collected from user | Ask user once and reuse |
{{output.instance_id}} | Captured from CLI JSON output | Parse from $.result.instanceIds[0] |
Rule: Placeholders wrapped in
{{env.*}}MUST NOT be exposed to or requested from the user. Placeholders wrapped in{{user.*}}MUST be collected interactively.
--output json BEFORE the subcommand: jdc --output json vm <command> ...--no-interactive does NOT exist in jdc CLI — all commands are non-interactive by default; omit this flag.~/.jdc/config INI only (NOT from env vars). SDK uses env vars.2026-04-28T10:00:00+08:00i-[hash] for instances, img-[hash] for images, vol-[hash] for disks| Operation | JSON Path | Type | Description |
|---|---|---|---|
| Create | $.result.instanceIds[0] | string | Instance ID to track |
| Describe | $.result.instances[0].status | string | Current state (running, stopped, etc.) |
| List | $.result.instances[*].instanceId | array | All instance IDs |
| Start/Stop | $.requestId | string | Non-empty means accepted |
| Operation | Initial State | Target State | Poll Interval | Max Wait |
|---|---|---|---|---|
| Create | - | running | 5s | 300s |
| Start | stopped | running | 5s | 120s |
| Stop | running | stopped | 5s | 120s |
| Reboot | running | running | 5s | 180s |
| Delete | running/stopped | (404 on describe) | 5s | 300s |
| Version | Date | Changes |
|---|---|---|
| 1.4.0 | 2026-05-07 | Cloud Assistant support: Added cloud assistant (云助手) operations — createCommand, invokeCommand, describeCommands, deleteCommands, describeInvocations. Added new reference cloud-assistant.md. Cloud assistant uses SDK/API only (jdc CLI not supported). |
| 1.3.0 | 2026-05-06 | Critical CLI behavioral fixes: Fixed --output json positioning (must be BEFORE subcommand), removed non-existent --no-interactive flag, corrected credential docs (CLI uses ~/.jdc/config INI, NOT env vars), added sandbox config workaround |
| 1.2.0 | 2026-05-06 | jdc-first with fallback strategy: execution flows now prioritize jdc CLI (primary) with SDK/API fallback after 3 retries; Prerequisites updated to uv-based bootstrap with Phase 1 (jdc) / Phase 2 (SDK fallback); Path Preference flipped to jdc-first; pre-flight checks reordered |
| 1.1.0 | 2026-05-03 | Added dual-path execution (SDK + CLI), complete frontmatter, api-sdk-usage.md, path preference |
| 1.0.0 | 2026-04-28 | Initial version, includes basic operational guide and reference templates |
| 1.0.1 | 2026-04-28 | Added VM instance management, network configuration, and security group operations guide |
Every operation follows the pattern: Pre-flight → Execute (jdc primary / SDK fallback) → Validate → Recover. The Agent MUST NOT skip any phase.
jdc-first strategy: The Agent MUST attempt jdc CLI first (primary path). If jdc fails after 3 retries with exponential backoff, fall back to SDK/API. Documentation below lists jdc before SDK to reflect execution priority.
| Check | Command | Expected | On Failure |
|---|---|---|---|
| CLI installed | jdc --version | exit code 0 | Retry up to 3 times; then fall back to SDK |
| Credentials valid | jdc --output json vm describe-instances --region-id cn-north-1 --page-number 1 --page-size 1 | $.error == null | Prompt user to config CLI credentials (~/.jdc/config) or set SDK env vars |
| SDK available | python -c "import jdcloud_sdk" | No import error | Document install pin (fallback path) |
| Instance types available | jdc --output json vm describe-instance-types --region-id {{user.region}} | list non-empty | Suggest another region |
| Image exists | jdc --output json vm describe-images --region-id {{user.region}} --image-ids '["{{user.image_id}}"]' | returns image | Suggest available public image |
| Subnet exists | jdc --output json vpc describe-subnet --region-id {{user.region}} --subnet-id {{user.subnet_id}} | returns subnet | Suggest creating subnet first |
jdc) [Primary Path]jdc --output json vm create-instances \
--region-id {{user.region}} \
--az "{{user.az}}" \
--instance-type "{{user.instance_type}}" \
--image-id "{{user.image_id}}" \
--name "{{user.instance_name}}" \
--primary-network-interface "{\"subnetId\":\"{{user.subnet_id}}\",\"securityGroupIds\":[\"{{user.sg_id}}\"]}" \
--system-disk "{\"diskCategory\":\"cloud_ssd\",\"diskSizeGB\":{{user.disk_size}}}" \
--charge-mode "postpaid_by_duration"
import os
from jdcloud_sdk.core.credential import Credential
from jdcloud_sdk.services.vm.client import VmClient
from jdcloud_sdk.services.vm.apis.CreateInstancesRequest import CreateInstancesRequest
credential = Credential(os.environ['JDC_ACCESS_KEY'], os.environ['JDC_SECRET_KEY'])
client = VmClient(credential, os.environ.get('JDC_REGION', 'cn-north-1'))
request = CreateInstancesRequest({
"regionId": "{{user.region}}",
"az": "{{user.az}}",
"instanceType": "{{user.instance_type}}",
"imageId": "{{user.image_id}}",
"name": "{{user.instance_name}}",
"primaryNetworkInterface": {
"subnetId": "{{user.subnet_id}}",
"securityGroupIds": ["{{user.sg_id}}"]
},
"systemDisk": {
"diskCategory": "cloud_ssd",
"diskSizeGB": {{user.disk_size}}
},
"chargeMode": "postpaid_by_duration"
})
response = client.create_instances(request)
if response.error is None:
instance_id = response.result.instanceIds[0]
print(f"Created instance: {instance_id}")
else:
print(f"Error: {response.error.code} - {response.error.message}")
{{output.instance_id}} from $.result.instanceIds[0]for i in $(seq 1 60); do
STATUS=$(jdc --output json vm describe-instances \
--region-id {{user.region}} \
--instance-ids '["{{output.instance_id}}"]' | jq -r '.result.instances[0].status')
[ "$STATUS" = "running" ] && break
sleep 5
done
running → operation succeeded, report instance ID, public IP, private IP to usererror → capture error from $.result.instances[0].errorMessage, go to Failure Recovery| Exit Code | Error Pattern (regex) | Max Retries | Backoff | Agent Action |
|---|---|---|---|---|
| 1 | InvalidParameter | 1 | - | Re-check parameter format, retry with corrected params |
| 1 | QuotaExceeded | 0 | - | HALT. Inform user quota is full, suggest requesting increase |
| 1 | InsufficientBalance | 0 | - | HALT. Inform user to top up account |
| 1 | InsufficientResource | 1 | - | Suggest switching to another AZ via jdc vm describe-azs |
| 3 | InternalError | 3 | 2s, 4s, 8s | Retry with exponential backoff. After 3rd failure, report to user |
| Other | .* | 3 | 5s, 10s, 15s | Retry. On final failure, extract full error message and present to user |
jdc) [Primary Path]jdc --output json vm describe-instances \
--region-id {{env.JDC_REGION}} \
--instance-ids '["{{user.instance_id}}"]'
from jdcloud_sdk.services.vm.apis.DescribeInstancesRequest import DescribeInstancesRequest
request = DescribeInstancesRequest({
"regionId": os.environ.get('JDC_REGION', 'cn-north-1'),
"instanceIds": ["{{user.instance_id}}"]
})
response = client.describe_instances(request)
if response.error is None:
instance = response.result.instances[0]
print(f"ID: {instance.instanceId}")
print(f"Name: {instance.name}")
print(f"Status: {instance.status}")
print(f"Private IP: {instance.primaryNetworkInterface.privateIpAddress}")
else:
print(f"Error: {response.error.message}")
| Field | JSON Path | Display Format |
|---|---|---|
| ID | $.result.instances[0].instanceId | Plain text |
| Name | $.result.instances[0].name | Plain text |
| Status | $.result.instances[0].status | Badge: 🟢 running / 🟡 starting / 🔴 stopped |
| Type | $.result.instances[0].instanceType | Plain text |
| Private IP | $.result.instances[0].primaryNetworkInterface.privateIpAddress | Plain text |
| Public IP | $.result.instances[0].primaryNetworkInterface.elasticIp.publicIpAddress | - if null |
{{user.instance_name}} ({{user.instance_id}})? Running services will be interrupted."jdc) [Primary Path]jdc --output json vm stop-instance \
--region-id {{env.JDC_REGION}} \
--instance-id {{user.instance_id}}
from jdcloud_sdk.services.vm.apis.StopInstanceRequest import StopInstanceRequest
request = StopInstanceRequest({
"regionId": os.environ.get('JDC_REGION', 'cn-north-1'),
"instanceId": "{{user.instance_id}}"
})
response = client.stop_instance(request)
if response.error is None:
print(f"Stop request accepted: {response.requestId}")
else:
print(f"Error: {response.error.message}")
stopped (max 120s){{user.instance_name}} ({{user.instance_id}})? This is IRREVERSIBLE and will release all associated resources."jdc) [Primary Path]jdc --output json vm delete-instance \
--region-id {{env.JDC_REGION}} \
--instance-id {{user.instance_id}}
from jdcloud_sdk.services.vm.apis.DeleteInstanceRequest import DeleteInstanceRequest
request = DeleteInstanceRequest({
"regionId": os.environ.get('JDC_REGION', 'cn-north-1'),
"instanceId": "{{user.instance_id}}"
})
response = client.delete_instance(request)
if response.error is None:
print(f"Delete request accepted: {response.requestId}")
else:
print(f"Error: {response.error.message}")
describe-instances until HTTP 404 (max 300s)Important: Cloud assistant uses the separate endpoint
assistant.jdcloud-api.com.jdcCLI does NOT currently support cloud assistant commands. Use SDK/API only for all cloud assistant operations. See Cloud Assistant Guide for complete reference.
| Check | Command | Expected | On Failure |
|---|---|---|---|
| SDK available | python -c "import jdcloud_sdk" | No import error | Install SDK via uv pip install jdcloud_sdk |
| Region ID known | - | Non-empty | Prompt user or default to cn-north-1 |
import os
import base64
from jdcloud_sdk.core.credential import Credential
from jdcloud_sdk.services.assistant.client import AssistantClient
from jdcloud_sdk.services.assistant.apis.CreateCommandRequest import CreateCommandRequest
credential = Credential(os.environ['JDC_ACCESS_KEY'], os.environ['JDC_SECRET_KEY'])
client = AssistantClient(credential, os.environ.get('JDC_REGION', 'cn-north-1'))
command_content = base64.b64encode("{{user.command_content}}".encode()).decode()
request = CreateCommandRequest({
"regionId": "{{env.JDC_REGION}}",
"commandName": "{{user.command_name}}",
"commandType": "{{user.command_type}}",
"commandContent": command_content,
"timeout": {{user.timeout}},
"username": "{{user.username}}",
"workdir": "{{user.workdir}}",
"commandDescription": "{{user.command_description}}",
"enableParameter": {{user.enable_parameter}}
})
response = client.create_command(request)
if response.error is None:
command_id = response.result.commandId
print(f"Command created: {command_id}")
else:
print(f"Error: {response.error.code} - {response.error.message}")
{{output.command_id}} from response.result.commandIddescribeCommands with the command ID| Error Pattern (regex) | Max Retries | Backoff | Agent Action |
|---|---|---|---|
QUOTA_EXCEEDED | 0 | - | HALT. Inform user to delete unused commands |
INVALID_ARGUMENT | 1 | - | Re-check field values, retry with corrected params |
OUT_OF_RANGE | 1 | - | Adjust parameter values to valid range |
INTERNAL|UNKNOWN | 3 | 2s, 4s, 8s | Retry with exponential backoff |
{{user.command_name}} on {{user.instance_count}} VM(s)? This will run the command immediately."| Check | Command | Expected | On Failure |
|---|---|---|---|
| SDK available | python -c "import jdcloud_sdk" | No import error | Install SDK |
| Command exists | describeCommands with commandIds | Returns command | Suggest creating command first |
| VMs are running | describe-instances for each instanceId | All status == running | Skip non-running instances, warn user |
from jdcloud_sdk.services.assistant.apis.InvokeCommandRequest import InvokeCommandRequest
request = InvokeCommandRequest({
"regionId": "{{env.JDC_REGION}}",
"commandId": "{{user.command_id}}",
"instances": {{user.instance_ids}},
"timeout": {{user.timeout}},
"username": "{{user.username}}",
"workdir": "{{user.workdir}}",
"enableParameter": {{user.enable_parameter}},
"parameters": {{user.parameters}}
})
response = client.invoke_command(request)
if response.error is None:
invoke_id = response.result.invokeId
print(f"Command invoked: {invoke_id}")
else:
print(f"Error: {response.error.code} - {response.error.message}")
{{output.invoke_id}} from response.result.invokeIdimport time
from jdcloud_sdk.services.assistant.apis.DescribeInvocationsRequest import DescribeInvocationsRequest
invoke_id = "{{output.invoke_id}}"
max_wait = 300
interval = 5
for _ in range(max_wait // interval):
time.sleep(interval)
check_req = DescribeInvocationsRequest({
"regionId": "{{env.JDC_REGION}}",
"invokeIds": [invoke_id]
})
check_resp = client.describe_invocations(check_req)
if check_resp.error is not None:
break
inv = check_resp.result.invocations[0]
if inv.status in ("finished", "failed", "partial_failed"):
break
| Field | Source | Display |
|---|---|---|
| Aggregate Status | inv.status | ✅ finished / ❌ failed / ⚠️ partial_failed |
| Instance ID | inst.instanceId | Plain text |
| Per-Instance Status | inst.status | Status badge |
| Exit Code | inst.exitCode | Plain text (0 = success) |
| Output | inst.output | Truncated (max 6000B) |
| Error | inst.errorInfo | Red text if non-empty |
| Duration | inst.duration | e.g., 3s |
Environment setup follows a jdc-first with fallback strategy:
jdc CLI setup via uv (primary path)Both jdc CLI and the JD Cloud Python SDK require a Python runtime. Use uv for local, isolated, and idempotent environment management.
Install uv (system-wide, one-time per machine):
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or via Homebrew: brew install uv
# Windows (PowerShell)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
uv venv --python 3.10
source .venv/bin/activate
uv pip install jdcloud_cli jdcloud_sdk
jdc --version
python -c "import jdcloud_sdk; print('SDK OK')"
If jdc --version or any jdc command fails:
# Retry 1
uv pip install jdcloud_cli jdcloud_sdk
jdc --version && echo "OK" || echo "FAIL"
# Retry 2 (wait 2s)
sleep 2
uv pip install --force-reinstall jdcloud_cli
jdc --version && echo "OK" || echo "FAIL"
# Retry 3 (wait 4s)
sleep 4
uv pip install --force-reinstall jdcloud_cli jdcloud_sdk
jdc --version && echo "OK" || echo "FAIL"
If all 3 retries fail, proceed to Phase 2: SDK Fallback.
uv venv --python 3.10
source .venv/bin/activate
uv pip install jdcloud_sdk
python -c "import jdcloud_sdk; print('SDK OK')"
CRITICAL: The
jdcCLI reads credentials only from~/.jdc/configINI file. Environment variables (JDC_ACCESS_KEY,JDC_SECRET_KEY) are ignored by the CLI. The SDK mode reads from environment variables. Use the appropriate method below.
Method A: Configure Credentials for SDK (env vars)
export JDC_ACCESS_KEY="{{env.JDC_ACCESS_KEY}}"
export JDC_SECRET_KEY="{{env.JDC_SECRET_KEY}}"
export JDC_REGION="cn-north-1"
Method B: Configure Credentials for CLI (~/.jdc/config INI)
# For sandbox environments, redirect HOME to a writable location
export HOME=/tmp/jdc-home
mkdir -p /tmp/jdc-home/.jdc
cat > /tmp/jdc-home/.jdc/config << 'CONFIGEOF'
[default]
access_key = {{env.JDC_ACCESS_KEY}}
secret_key = {{env.JDC_SECRET_KEY}}
region_id = {{env.JDC_REGION}}
endpoint = vm.jdcloud-api.com
scheme = https
timeout = 20
CONFIGEOF
# CRITICAL: ~/.jdc/current must contain exactly "default" with NO trailing newline
printf "%s" "default" > /tmp/jdc-home/.jdc/current