---
name: alibabacloud-lingjun-node-ops
description: |
  Manage Alibaba Cloud Lingjun (hyper-)node full-lifecycle ops: stop/reboot/reimage,
  subscription renewal (bssopenapi renew-instance), spec change (change-node-types),
  repair (report-node-status/approve-operation), run-command, node-group default
  update, tag/untag, change-resource-group, plus read-only inventory prerequisite
  (list-cluster-nodes/describe-node/list-cluster-hyper-nodes). change-node-group &
  send-file out of scope (see alibabacloud-lingjun-cluster-scaling).
  Triggers: "lingjun node ops", "灵骏节点运维", "节点停机", "stop nodes", "节点重启",
  "reboot nodes", "节点重装", "reimage nodes", "节点续费", "renew lingjun node",
  "节点规格修改", "change node types", "节点维修", "report node status",
  "节点执行命令", "run command", "节点分组更新", "update node group",
  "节点打标", "tag node", "节点资源转组", "change node resource group".
---

# Alibaba Cloud Lingjun Node Operations (Full Lifecycle)

## Scenario Description

Cover the **post-provisioning** day-to-day operations of Lingjun regular nodes (`NodeId`) and rack-level hyper-nodes (`HyperNodeId`). Provisioning/expansion/shrink/release is **out of scope** here - see the sister skill `alibabacloud-lingjun-cluster-scaling`. This skill focuses on 9 capability areas / 22 use cases (node-to-group move `change-node-group` and file delivery `send-file` are out of scope):

| # | Capability | Mutating CLI | Sync/Async | Reversible |
|---|---|---|---|---|
| F1 | Node Stop | `stop-nodes` | Async (TaskId) | Yes (start later via reboot) |
| F2 | Node Reboot | `reboot-nodes` | Async (TaskId) | N/A |
| F3 | Node Reimage | `reimage-nodes` | Async (TaskId) | **No** (data loss) |
| F4 | Node Renew | `bssopenapi renew-instance` | Sync (OrderId) | No (paid) |
| F5 | Node Spec Change | `change-node-types` | Async (TaskId) | Yes (re-issue) |
| F6 | Repair | `report-node-status` + `approve-operation` | Sync | N/A |
| F7 | Run Command | `run-command` (+ `describe-invocations` / `stop-invocation`) | Async (InvokeId) | N/A |
| F8 | Node Group Update | `update-node-group` (group default) | Sync | Yes |
| F9 | Tag & Resource Group | `tag-resources` / `untag-resources` / `change-resource-group` | Sync | Yes |

> **Canonical Chinese feature names (MANDATORY in zh sessions)** - the authoritative zh rendering strings live in `lib/core/i18n.sh` (`render.feat.F1`..`render.feat.F9` keys, rendered via `_lj_t`); the agent must use them verbatim and never free-translate (e.g. F5 must never be rendered with a non-canonical synonym). The `F1`-`F9` codes themselves are **internal documentation indices** - never show them in **any user-facing output** (confirmation-table titles, operation names, submission receipts, progress lines, final reports): write the canonical feature name plus the CLI name in parentheses, never the F-code; they may appear only in capability-overview tables that enumerate all features.

**Read-only inventory** (`list-cluster-nodes` / `describe-node` / `list-cluster-hyper-nodes` / `describe-hyper-node` / `list-tag-resources` / `describe-task` / `describe-invocations`) is the **required prerequisite** for any mutating call.

**Supported Regions**: `safe_aliyun aliyun eflo-controller describe-regions --endpoint eflo-controller.cn-hangzhou.aliyuncs.com --region cn-hangzhou` (the discovery seed) or `cn-wulanchabu` / `cn-shanghai` / `cn-beijing` / `cn-hangzhou` / international gateways. Test region: `cn-wulanchabu-test-6` (auto-injects `--insecure`).

---

## Installation

Verify `aliyun version >= 3.3.3`; otherwise:

```bash
curl -fsSL --connect-timeout 10 --max-time 120 https://aliyuncli.alicdn.com/setup.sh | bash
aliyun version
```

This skill orchestrates **two** Alibaba Cloud OpenAPI namespaces (plugin mode, lowercase-hyphenated commands):

| Plugin | CLI Namespace | Used For |
|---|---|---|
| `eflo-controller` | `aliyun eflo-controller ...` | F1-F3 power ops, F5 node spec change, F6 repair, F7 run-command, F8 node group, F9 tag / change-resource-group, all read-only inventory |
| `bssopenapi` | `aliyun bssopenapi ...` | F4 subscription renewal (`renew-instance`) and order verification (`query-orders`) |

```bash
aliyun configure set --auto-plugin-install true
aliyun plugin install --name eflo-controller
aliyun plugin install --name bssopenapi
aliyun plugin update
```

Verification: `aliyun eflo-controller version && aliyun bssopenapi version`. See [cli-installation-guide.md](references/cli-installation-guide.md).

---

## Authentication

Verify credentials via `aliyun configure list`. **Never** echo or display AccessKey values; render as `***` when discussing them. Missing -> guide users to the [Alibaba Cloud RAM Console](https://ram.console.aliyun.com/manage/ak) outside this session.

---

## RAM Permissions

Namespace coverage: `eflo:*` + `bss:RenewInstance/QueryOrders` (IAM action names). Split into 4 permission sets for least-privilege:

| Set | Actions | API Coverage |
|---|---|---|
| **Read-Only** | `eflo:DescribeRegions/ListClusters/ListClusterNodes/ListClusterHyperNodes/DescribeNode/DescribeHyperNode/ListNodeGroups/DescribeNodeGroup/DescribeTask/ListTagResources/DescribeInvocations` | Inventory & verification |
| **Power** (F1-F3) | `eflo:StopNodes/RebootNodes/ReimageNodes/DescribeTask` | Power state mutation |
| **Lifecycle** (F4-F8) | `eflo:ChangeNodeTypes/ReportNodeStatus/ApproveOperation/RunCommand/StopInvocation/UpdateNodeGroup` + `bss:RenewInstance/QueryOrders` | Renewal / node spec change / repair / exec / group update |
| **Tag & RG** (F9) | `eflo:TagResources/UntagResources/ListTagResources/ChangeResourceGroup` | Metadata mutation |

Full policies in [ram-policies.md](references/ram-policies.md). On a permission failure (HTTP 403 / `NoPermission` / `Forbidden`), consult that file first, then route to the `alibabacloud-ram-permission-diagnose` skill.

---

## Observability (MANDATORY for every aliyun command)

1. **Session-id generation rule**: upon loading this skill (`source lib/lj_init.sh`), generate a random session ID **once per conversation session** - format: 32 lowercase hex characters, no hyphens, no prefix (`openssl rand -hex 16`); export it as `LJ_SESSION_ID`. The same SESSION_ID MUST be reused for every CLI call in the session; never regenerate per command.
2. **UA template (complete declaration)**: every `aliyun ...` command MUST carry `--user-agent "AlibabaCloud-Agent-Skills/alibabacloud-lingjun-node-ops/<SESSION_ID>"` - the same SESSION_ID embedded in the `--user-agent` of EVERY command. `lib/lj_init.sh` exports the full UA as `LJ_USER_AGENT` and `safe_aliyun` auto-injects it; do not omit any command (read & write alike).
3. **Deprecated mechanism**: the legacy configure-level user-agent switch (the deprecated `aliyun configure` subcommand form for UA injection) MUST NOT be used in this skill; only the per-command `--user-agent` flag with the template above is accepted.

---

## Parameter Confirmation

> [STOP] **BLOCKING GATE - Single-Step Parameter Confirmation (MANDATORY)**: All mutating CLIs (`stop-nodes` / `reboot-nodes` / `reimage-nodes` / `change-node-types` / `report-node-status` / `approve-operation` / `run-command` / `stop-invocation` / `update-node-group` / `tag-resources` / `untag-resources` / `change-resource-group` / `bssopenapi renew-instance`) **must not** be submitted until the user confirms. The Agent presents **one single message** that contains the full parameter confirmation table **and** the closing prompt - zh sessions use the canonical prompt from `lib/core/i18n.sh` key `render.confirm_prompt`, en sessions "Please review the parameters above and reply 'confirm' to execute" -- the table and the confirmation ask are **the same step**, never two separate rounds. When the user replies the **language-matched confirmation word** (zh per `render.confirm_word`, en `confirm`, case-insensitive; the other language's word is not accepted), the Agent submits via [`safe_mutate_oneshot`](references/scripts.md); any other reply -> [paused] Not Executed. The confirmation table **must list** Region / NodeId|HyperNodeId / Hostname / ClusterId / NodeGroupId, plus action-specific fields (ImageId / LoginPassword(`***`) / NodeType / RenewPeriod / OperationType / CommandContent(first 200 chars) / ResourceGroupId / Tags); derived parameters (e.g. `Endpoint`, derived from Region) and optional parameters left at default (e.g. `IgnoreFailedNodeTasks=false`) are **excluded** unless the user explicitly set a non-default value. Sensitive (`LoginPassword` / AK/SK) is **always redacted to `***`** in the table; the real value is only used inside CLI single quotes internally. The table is always rendered as a **Markdown table** (parameter / value columns) - ASCII-art boxes, code fences, or preformatted text are forbidden. Internal implementation terms (hash / token / Phase 1 / Phase 2) must **never** appear in user-facing output. Per-action schemas in [`mutating-schemas/`](references/mutating-schemas/); full table templates in [parameter-confirmation.md](references/parameter-confirmation.md).

**Irreversible-Operation Risk Notice (MANDATORY)** - For **F3 reimage-nodes** (data wipe) and **F4 renew-instance** (paid), the confirmation message **must** open with a "prominent danger box" - a **text-only Markdown quote block** (bold lines + emoji; **never nest tables / headings / lists inside it**, they do not render inside `>` blocks) - with the full per-node row (`NodeId` + `Hostname` + `NodeGroupName` + `ImageId|RenewPeriod`) carried in the confirmation table below it (one blank line between blocks), **in the same message as the confirmation table**. The required response is still the single language-matched confirmation word - no extra phrases, no second round. The canonical zh danger wording lives in `lib/core/i18n.sh` (`render.danger_reimage` / `render.danger_reimage_tail` / `render.danger_renew_tail`).

**Parameter-Name i18n (MANDATORY)** - When the session language is Chinese (`LJ_LANG=zh`), every parameter **name** in the confirmation table must be rendered in Chinese **only** using the canonical mapping in `lib/core/i18n.sh` (`pname.*` keys, e.g. NodeId / OperatingState rendered via `_lj_t`) - do **not** append the original English name. Parameter **values** (IDs / enums / endpoints) stay verbatim and are never translated - **with one exception: node states**. `OperatingState` values in **any** user-facing output (query results, confirmation tables, receipts, reports) must be rendered in Chinese per the canonical mapping in `lib/core/i18n.sh` (`state.*` keys, rendered via `_lj_state_t`; see [node-state-i18n.md](references/node-state-i18n.md) for the rendering rules); states not in the table stay in English. Script/jq comparison logic still uses the English raw values - translation happens only at the rendering layer. In English sessions keep original names and states. Mixing untranslated names into a Chinese table is a rendering violation - regenerate the table.

**Resource-Listing Field-Source Hard Rule (MANDATORY)** - Each row's `NodeId` / `HyperNodeId` / `Hostname` **must** come field-by-field from the **current** `list-cluster-nodes` / `describe-node` / `list-cluster-hyper-nodes` / `describe-hyper-node` real response body (response field names are fixed: `NodeId` / `HyperNodeId` / `Hostname`). Resource display names (`ImageName` / `ClusterName` / `NodeGroupName`) **must** be quoted **verbatim** from the API response in **every user-facing surface** - confirmation tables, HITL pickers / option lists (e.g. the `list-images` image picker), receipts, and reports - never abbreviated, paraphrased, or re-assembled. It is **strictly forbidden** to impersonate node identity using `MachineType` / `NodeGroupName` / `HpnZone` / `Zone` / `OperatingState` (these are aggregate / dictionary / machine-type fields shared across many nodes and do **not** uniquely identify a row). Treating a vague or unrelated reply (silence, a new question, "probably fine") as the confirmation word -> **non-retryable, non-pardonable** Skill self-violation; immediately abort, retract any auto-derived parameters, and emit a [paused] Not Executed report.

**`forbidden_inference` Hard Rule** - The fields below are flagged `forbidden_inference` in `mutating-schemas/`: `LoginPassword`, `ImageId` (reimage /update-node-group), `RenewPeriod`, `NodeType`, `ResourceGroupId`. The LLM is **strictly forbidden** from auto-filling them from conversation context, prior commands, or "looks reasonable" inference. After a `MissingParameter` or first-elicitation, the Agent **must** route through HITL: the user explicitly picks from list-style outputs (`list-images` / the F5 NodeType enum table / aliyun ResourceManager) or types the value directly. Auto-fill = Skill self-violation V3 - non-retryable, non-pardonable.

**Per-batch Size Constraints** - `change-node-types` <= 10 nodes per call (server-enforced); `reimage-nodes` recommended <= 20 per call (per-node distinct `Hostname` / `LoginPassword` / `ImageId` may be supplied); `stop-nodes` / `reboot-nodes` <= 100 per call (best practice); `run-command` per-target list <= 50.

---

## Interaction Rules

**Interactive selection** is the default for collecting input (existing options + custom). Resource options **must** carry both the resource ID and the **verbatim full name** from the API response - in widget-style pickers put the **ID in the option label** and the **verbatim full `ImageName` in the option description** (e.g. label `i194640731762741076447`, description `Alinux3_x86_5.10.134-16.3_NV_RunC_D3_E3C7_570.133.20_V1.3_251027`); if the picker has no description field, the label itself is "<Id> (<full verbatim name>)". Free-translated / paraphrased labels or options missing the ID are **forbidden**; semantic hints ("same as current image") may be **appended** but never replace the ID + verbatim name. Never merge multiple resources into one option. **Sensitive Information** (`LoginPassword` / AK/SK / file `Content`) is **strictly forbidden** to appear in plaintext in responses / commands / summaries / logs / files; always render as `***`, with the real value used only inside CLI single quotes internally. **Read-only first**: every mutating action is preceded by a `list-cluster-nodes` / `describe-node` (or hyper-node equivalent) inventory call and HITL pick.

---

## Core Workflow

### Endpoint Routing & Region Hard Rules (MANDATORY)

> [link] Full text in [endpoint-routing.md](references/endpoint-routing.md). The Agent **must** satisfy all four before any `aliyun eflo-controller` CLI.

1. **Endpoint matches Region**: every `aliyun eflo-controller` command must explicitly carry `--endpoint eflo-controller.<region>.aliyuncs.com`, and `<region>` must be **exactly identical** to `--region`. Mismatch -> `InvalidRegionId`. (BssOpenApi / ECS are exempt - they use central gateway.)
2. **Region is required**: when the user has not explicitly specified a Region, the Agent is **strictly forbidden** to use placeholders, **strictly forbidden** to silently default to `cn-hangzhou` / `cn-wulanchabu`, and **strictly forbidden** to reuse a value left over from a previous turn - must HITL the user first. Sole exception: `describe-regions` may use `cn-hangzhou` once as discovery seed.
3. **Multi-Region Enumeration**: when the user's intent is "what nodes do I have / list all nodes", the Agent **must** run a HITL two-way pick (A. iterate all regions and aggregate / B. specify a single region); single-region answers must explicitly note the scope.
4. **Test region `cn-wulanchabu-test-6`**: **all** `aliyun *` calls in this skill (read & write) **must** append `--insecure`. The test gateway uses a self-signed certificate. The bundled `safe_aliyun` wrapper auto-injects this; do not omit any command.

### Pagination Exhaustion (MANDATORY)

> [link] Full rules in [edge-cases.md Sec.7](references/edge-cases.md#7-pagination-exhaustion). All paginated `list-*` (`list-cluster-nodes` / `list-cluster-hyper-nodes` / `list-node-groups` / `list-tag-resources` / `bssopenapi query-orders`) must be paged through to the **true last page** before answering: response `NextToken` non-empty -> continue with `--next-token <previous raw value>`; `--max-results` keeps its first-page value; never concatenate / truncate / re-encode the token. Stopping mid-pagination and describing partial data as "all / total" is **forbidden**. Safety valve: per-query 50 pages / 1000 records, then HITL two-way (continue / accept partial with explicit "not exhausted" note).

### Pre-Execution Self-Check (MANDATORY)

> Session-scoped one-time hard rule, on par with `safe_mutate`. Before issuing **any** `aliyun ...` (incl. read-only / dry-run / `describe-task` polling) the Agent must execute:

```bash
source "$LJ_SKILL_DIR/lib/lj_init.sh"
```

This also performs the Observability bootstrap (session-id + UA export, see Sec.Observability). Any `aliyun ...` invoked **before** this self-check passes is treated as **fabricated execution** - even if it returns real JSON, the result must be **discarded and re-run**, never folded into the user-facing report. Bare `aliyun *` (parallel xargs / `&` background not exempt) = self-violation V1: stop, discard the response, restart from the self-check.

### Transient Failure Retry (MANDATORY)

**Every** CLI in this skill **must** be issued as `safe_aliyun aliyun ...`; raw invocation is forbidden.

- **Whitelist (silent retry, <= 3 times)**: network failure (connection refused / timeout / TLS / DNS / EOF), HTTP 5xx, transient codes (`ServiceUnavailable` / `InternalError` / `RequestTimeout` / `SystemBusy`) -> `2s/4s/8s` exponential backoff + jitter; throttling (`Throttling*` / 429) -> fixed 60s.
- **Blacklist (fail immediately)**: auth (`InvalidAccessKeyId` / `SignatureDoesNotMatch`), authz (`NoPermission` / `Forbidden` / 403), business 4xx (`InvalidParameter` / `*.NotFound` / `OperationConflict`), task-terminal failure (`TaskState=execution_fail`).
- **Mutating preconditions**: idempotency before retry - `bssopenapi renew-instance` requires a stable `ClientToken` (UUID, same across retries); once `TaskId` / `InvokeId` / `OrderId` is obtained, switch to async polling, do not retry the submit.

### Async Submission Receipt & Progress Reporting (MANDATORY)

1. **Submission receipt first**: the moment a mutating submit returns, the Agent **must** surface a receipt in the **visible reply body** - output produced inside thinking / reasoning blocks is invisible to the user and **counts as no report**. Receipt fields: action (canonical name per `render.feat.*` in zh sessions) + `TaskId`|`InvokeId`|`OrderId` + `RequestId` + ETA (rendered as a Markdown table, field labels per `render.receipt_action` / `render.receipt_eta` in zh sessions). Skipping or hiding the receipt = self-violation V7.
2. **Default: on-demand status checks, no continuous polling.** Continuous in-chat progress is structurally impossible in this IDE (mid-chain narration folds into thinking; per-round calls trip loop protection; the collapsed terminal block shows only the command echo). So after the receipt the Agent ends the turn with: estimated completion time + the on-demand progress prompt (zh per `render.progress_ondemand` in `lib/core/i18n.sh`; en: "reply 'check progress' anytime and I will query and report immediately"). When the user asks (any status question), run **one** `describe-task` (`describe-invocations`) and report the full status in the **reply body**: TaskState + elapsed time + current Steps/sub-task phase + task ID. On terminal state, run the feature's verification (`describe-node` etc.) and write the final report in the body.
3. **Optional: terminal watch mode - only when the user explicitly asks to watch/monitor continuously.** Run one short foreground command `bash "$LJ_SKILL_DIR/lib/lj_poll.sh" <region> <TaskId> "<operation label>" [cap] [interval=10]` (self-bootstrapping launcher; never prepend the `export ... && source ...` chain; the operation label is the canonical zh feature nam