huaweicloud-devkit 1.1.4-next.0 → 1.1.4-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
3
  "mcpName": "io.github.huaweicloud/huaweicloud-devkit",
4
- "version": "1.1.4-next.0",
4
+ "version": "1.1.4-next.1",
5
5
  "kooCliVersion": "7.2.12",
6
6
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
7
7
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.4-next.0",
20
+ "version": "1.1.4-next.1",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -20,7 +20,7 @@
20
20
  "mcpServers": "./.mcp.json",
21
21
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
22
22
  "skills": "./skills/",
23
- "version": "1.1.4-next.0",
23
+ "version": "1.1.4-next.1",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.4-next.0",
20
+ "version": "1.1.4-next.1",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.4-next.0",
3
+ "version": "1.1.4-next.1",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.4-next.0",
3
+ "version": "1.1.4-next.1",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -2,7 +2,7 @@
2
2
  "name": "huaweicloud-devkit",
3
3
  "id": "huaweicloud-devkit",
4
4
  "displayName": "HuaweiCloud DevKit",
5
- "version": "1.1.4-next.0",
5
+ "version": "1.1.4-next.1",
6
6
  "family": "bundle-plugin",
7
7
  "bundleFormat": "codex",
8
8
  "description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
@@ -58,7 +58,7 @@ Flavor families are **region-dependent**. Always run `hcloud ECS ListFlavors --c
58
58
  | Create instance | hcloud ECS CreateServers --server.name=<n> --server.flavorRef=<id> --server.imageRef=<id> --server.nics.1.subnet_id=<id> --server.availability_zone=<az> | references/create-instance.md |
59
59
  | Find by name | See "How to search for instances" below | |
60
60
  | Bind EIP | hcloud EIP AssociatePublicips --publicip_id=<id> --publicip.associate_instance_id=<port-id> --publicip.associate_instance_type=PORT | Get port ID from `hcloud ECS ListServersDetails --server_id=<id>` → `OS-EXT-IPS:port_id` |
61
- | Security group rule | hcloud VPC CreateSecurityGroupRule --security_group_id=<id> --direction=<direction> --protocol=<protocol> | references/sg.md |
61
+ | Security group rule | hcloud VPC CreateSecurityGroupRule --security_group_id=<id> --direction=<direction> --protocol=<protocol> | see huawei-vpc |
62
62
  | Attach disk | hcloud ECS AttachServerVolume --server_id=<id> --volumeAttachment.volumeId=<id> | references/evs.md |
63
63
  | Delete instance | hcloud ECS DeleteServers --servers.1.id=<id> --delete_publicip=true --delete_volume=true | references/create-instance.md |
64
64
  | Reboot instance | hcloud ECS BatchRebootServers --reboot.servers.1.id=<id> --reboot.type=SOFT | NOT `RebootServer` — that operation does not exist |
@@ -45,12 +45,12 @@ Domain expertise for Huawei Cloud Identity and Access Management (IAM). Covers u
45
45
 
46
46
  | Task | Command | Steps |
47
47
  | ------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
48
- | List users | hcloud IAM KeystoneListUsers --cli-region=<r> | references/iam-ops.md |
49
- | Create group | hcloud IAM CreateGroupV5 --group_name=<name> | references/iam-ops.md |
48
+ | List users | hcloud IAM KeystoneListUsers --cli-region=<r> | references/quick-ref.md |
49
+ | Create group | hcloud IAM CreateGroupV5 --group_name=<name> | references/quick-ref.md |
50
50
  | Create custom policy | hcloud IAM CreateCloudServiceCustomPolicy --role.display_name=<n> --role.type=<type> --role.policy.Statement.1.Effect=Allow | references/policy-examples.md |
51
- | Create agency | hcloud IAM CreateAgency --agency.name=<n> --agency.domain_id=<id> --agency.trust_domain_name=<domain> | references/agency.md |
52
- | Get temporary credential | hcloud STS AssumeAgency --agency_urn=<urn> --agency_session_name=<n> --duration_seconds=3600 | references/sts.md |
53
- | Attach policy to group | hcloud IAM AttachGroupPolicyV5 --group_id=<id> --policy_id=<id> | references/iam-ops.md |
51
+ | Create agency | hcloud IAM CreateAgency --agency.name=<n> --agency.domain_id=<id> --agency.trust_domain_name=<domain> | references/quick-ref.md |
52
+ | Get temporary credential | hcloud STS AssumeAgency --agency_urn=<urn> --agency_session_name=<n> --duration_seconds=3600 | references/quick-ref.md |
53
+ | Attach policy to group | hcloud IAM AttachGroupPolicyV5 --group_id=<id> --policy_id=<id> | references/quick-ref.md |
54
54
 
55
55
  ## Policy Examples (Least Privilege)
56
56
 
@@ -109,4 +109,4 @@ Common roles for FunctionGraph: VPC Administrator, RDS Administrator, DCS Admini
109
109
 
110
110
  - IAM Docs: https://support.huaweicloud.com/iam/
111
111
  - Policy examples: references/policy-examples.md
112
- - Agency setup: references/agency.md
112
+ - Agency setup: references/quick-ref.md
@@ -7,11 +7,46 @@ hcloud IAM KeystoneListProjects # list projects
7
7
  hcloud IAM KeystoneListUsers # list users
8
8
  hcloud IAM KeystoneListGroups # list groups
9
9
  hcloud IAM ListCustomPolicies # list custom policies
10
- hcloud IAM ListAgencies # list agencies
10
+ hcloud IAM ListAgencies --domain_id=<domain-id> # list agencies
11
11
  hcloud IAM KeystoneListAuthDomains # list domains
12
12
  hcloud IAM KeystoneShowUser # show user detail
13
13
  ```
14
14
 
15
+ > **Group lifecycle uses the V5 API, not Keystone.** `CreateGroupV5` / `AttachGroupPolicyV5` take `group_id`/`policy_id`; `KeystoneListGroups` is the older read API. The V5 group `description` forbids `@ # % & < > \ $ ^ *`.
16
+
17
+ ```bash
18
+ hcloud IAM CreateGroupV5 --group_name=<name>
19
+ hcloud IAM AttachGroupPolicyV5 --group_id=<id> --policy_id=<id>
20
+ ```
21
+
22
+ ## Agencies & Temporary Credentials (STS)
23
+
24
+ An agency + `STS AssumeAgency` is the only safe way to mint temporary credentials (never store long-term AK/SK):
25
+
26
+ 1. Create the delegation:
27
+
28
+ ```bash
29
+ hcloud IAM CreateAgency --agency.name=<name> --agency.domain_id=<domain-id> --agency.trust_domain_name=<trust-domain>
30
+ ```
31
+
32
+ `--agency.trust_domain_name` and `--agency.trust_domain_id` are two ways to name the entrusted account — provide at least one; when both are present, `trust_domain_name` wins validation.
33
+
34
+ 2. Grant a role to the agency (optional):
35
+
36
+ ```bash
37
+ hcloud IAM AssociateAgencyWithDomainPermission --agency_id=<id> --role_id=<role-id>
38
+ ```
39
+
40
+ 3. Exchange for temporary credentials:
41
+
42
+ ```bash
43
+ hcloud STS AssumeAgency --agency_urn=<urn> --agency_session_name=<name> --duration_seconds=3600
44
+ ```
45
+
46
+ - `--agency_urn` is the agency **URN**, not its name or id — query it with `hcloud IAM ListAgencies --domain_id=<domain-id>` first.
47
+ - `--duration_seconds` range is `[900, 43200]`; it must not exceed the agency's own session duration, and is capped at `3600` when `--X-Security-Token` is present.
48
+ - The returned credential includes a `security_token` — pass it as `--X-Security-Token` for subsequent calls.
49
+
15
50
  ## Credential Management
16
51
 
17
52
  ```bash
@@ -131,4 +131,3 @@ See `references/single-file-share.md` for the full workflow to host one file and
131
131
  - Static website: references/static-website.md
132
132
  - Single-file share: references/single-file-share.md
133
133
  - Lifecycle: references/bucket-lifecycle.md
134
- - Replication: references/replication.md
@@ -43,8 +43,8 @@ Domain expertise for Huawei Cloud Virtual Private Cloud (VPC). Covers VPC/subnet
43
43
  | SG rule | hcloud VPC CreateSecurityGroupRule --security_group_rule.security_group_id=<id> --security_group_rule.direction=<direction> --security_group_rule.protocol=<protocol> --security_group_rule.multiport=<port> --security_group_rule.remote_ip_prefix=<cidr> | references/security-group.md |
44
44
  | Create EIP | hcloud EIP CreatePublicip --publicip.type=<type> --bandwidth.size=<size> --bandwidth.share_type=<share-type> --bandwidth.name=<name> | Run `hcloud EIP CreatePublicip --help` to confirm valid type values per region |
45
45
  | Bind EIP to ECS | hcloud EIP AssociatePublicips --publicip_id=<id> --publicip.associate_instance_id=<port-id> --publicip.associate_instance_type=PORT | Get port ID from `hcloud ECS ListServersDetails --server_id=<id>` → `OS-EXT-IPS:port_id` |
46
- | Unbind EIP | hcloud EIP DisassociatePublicips --publicip_id=<id> | references/eip.md |
47
- | Delete EIP | hcloud EIP DeletePublicip --publicip_id=<id> | references/eip.md |
46
+ | Unbind EIP | hcloud EIP DisassociatePublicips --publicip_id=<id> | references/network.md |
47
+ | Delete EIP | hcloud EIP DeletePublicip --publicip_id=<id> | references/network.md |
48
48
  | List EIPs | hcloud EIP ListPublicips | |
49
49
  | NAT gateway | hcloud NAT CreateNatGateway --nat.name=<name> --nat.spec=<spec> --router_id=<vpc-id> --internal_network_id=<subnet-id> | Run `hcloud NAT CreateNatGateway --help` for available spec values |
50
50
 
@@ -57,10 +57,13 @@ export function syncAuth(target = 'all') {
57
57
  try {
58
58
  obs = writeObsConfig(credentials);
59
59
  } catch (error) {
60
+ const missingRegion = !String(credentials?.region || '').trim();
60
61
  return {
61
62
  ok: false,
62
63
  error: error.message,
63
- nextStep: 'Run "npx huaweicloud-devkit auth init" to refresh credentials and region.',
64
+ nextStep: missingRegion
65
+ ? 'Credential region is missing — run huaweicloud_auth_switch action=persist with --region, or add "region" to creds-import.json and re-import.'
66
+ : 'Run "npx huaweicloud-devkit auth init" to refresh credentials and region.',
64
67
  };
65
68
  }
66
69
 
@@ -961,9 +961,23 @@ function readImportFile() {
961
961
  region: String(data.region || ''),
962
962
  };
963
963
  } catch {
964
+ // Malformed/undecodable import file is un-replayable — wipe it. A VALID
965
+ // file is kept so a rejected persist can be replayed (see #502).
966
+ try {
967
+ rmSync(path, { force: true });
968
+ } catch {
969
+ // ignore
970
+ }
964
971
  return null;
965
- } finally {
972
+ }
973
+ }
974
+
975
+ function clearImportFile() {
976
+ const path = join(dirname(globalCredentialsPath()), 'creds-import.json');
977
+ try {
966
978
  rmSync(path, { force: true });
979
+ } catch {
980
+ // best-effort: an absent or locked file is not an error
967
981
  }
968
982
  }
969
983
 
@@ -996,7 +1010,7 @@ function persistCredentials(ak, sk, securityToken, region) {
996
1010
  writeLastSync({ kooCliProfile: profile, s1Fingerprint: fingerprint(ak, sk) });
997
1011
  }
998
1012
  return {
999
- status: 'ok',
1013
+ status: obs.ok && hcloud.ok ? 'ok' : 'partial',
1000
1014
  scope: 'persist',
1001
1015
  backedUp: Boolean(before),
1002
1016
  obs: obs.ok ? { configured: true } : { configured: false, error: obs.error },
@@ -1124,10 +1138,14 @@ export async function callTool(name, rawArgs = {}) {
1124
1138
  let securityToken = args.securityToken || '';
1125
1139
  let region = args.region || '';
1126
1140
  const sourceChannel = args.mode || 'memory';
1141
+ let importedFromFile = false;
1127
1142
 
1128
1143
  if (sourceChannel === 'import' && (!ak || !sk)) {
1129
1144
  const imported = readImportFile();
1130
- if (imported) ({ ak, sk, securityToken, region } = imported);
1145
+ if (imported) {
1146
+ ({ ak, sk, securityToken, region } = imported);
1147
+ importedFromFile = true;
1148
+ }
1131
1149
  }
1132
1150
  if (sourceChannel === 'mcp-config' && (!ak || !sk)) {
1133
1151
  const cc = readCodeArtsCredentials();
@@ -1143,9 +1161,19 @@ export async function callTool(name, rawArgs = {}) {
1143
1161
  throw new Error('ak and sk are required (or provide creds-import.json for mode=import).');
1144
1162
  }
1145
1163
 
1164
+ if (action === 'persist' && !String(region || '').trim()) {
1165
+ return {
1166
+ status: 'error',
1167
+ scope: 'invalid_region',
1168
+ error:
1169
+ 'region is required to persist credentials. Pass --region, or include "region" in creds-import.json (mode=import).',
1170
+ };
1171
+ }
1172
+
1146
1173
  if (action === 'temporary') {
1147
1174
  setRuntimeCredentials(ak, sk, securityToken || undefined, region);
1148
1175
  refreshUserHashAfterAuthChange();
1176
+ if (importedFromFile) clearImportFile();
1149
1177
  return {
1150
1178
  status: 'ok',
1151
1179
  scope: 'temporary',
@@ -1166,6 +1194,7 @@ export async function callTool(name, rawArgs = {}) {
1166
1194
  newRegion: region,
1167
1195
  oldFingerprint: fingerprint(prev.ak, prev.sk),
1168
1196
  newFingerprint: fingerprint(ak, sk),
1197
+ fromImport: importedFromFile,
1169
1198
  });
1170
1199
  return {
1171
1200
  status: 'needs_confirmation',
@@ -1178,6 +1207,10 @@ export async function callTool(name, rawArgs = {}) {
1178
1207
  }
1179
1208
 
1180
1209
  const persisted = persistCredentials(ak, sk, securityToken, region);
1210
+ // Clear the import file for non-replayable outcomes (success, or an
1211
+ // unfixable rejection such as STS R3). Keep it only for a retryable
1212
+ // 'partial' (S1 written but a mirror failed).
1213
+ if (importedFromFile && persisted.status !== 'partial') clearImportFile();
1181
1214
  refreshUserHashAfterAuthChange();
1182
1215
  return persisted;
1183
1216
  }
@@ -1189,6 +1222,7 @@ export async function callTool(name, rawArgs = {}) {
1189
1222
  return { status: 'ok', outcome: 'aborted', message: '保持 S1 现有账号,未覆盖。' };
1190
1223
  }
1191
1224
  const confirmed = persistCredentials(pending.newAk, pending.newSk, pending.newSecurityToken, pending.newRegion);
1225
+ if (pending.fromImport && confirmed.status !== 'partial') clearImportFile();
1192
1226
  refreshUserHashAfterAuthChange();
1193
1227
  return confirmed;
1194
1228
  }