huaweicloud-devkit 1.1.4-next.3 → 1.1.4-next.4
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 +1 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +48 -23
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +9 -2
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.
|
|
4
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
20
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
23
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
20
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
3
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
3
|
+
"version": "1.1.4-next.4",
|
|
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.
|
|
5
|
+
"version": "1.1.4-next.4",
|
|
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,6 +58,33 @@ export function readGlobalCredentials() {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// R11: Placeholder / masked credential values are treated as "not configured".
|
|
62
|
+
// A vendor/IDE marketplace that pre-fills mcp_settings env with template text
|
|
63
|
+
// (e.g. "<HW_ACCESS_KEY>" or "${SECRET_KEY}") must not shadow the user's real
|
|
64
|
+
// S1 vault. Real AK/SK are >=20 char alphanumeric strings, so none of the
|
|
65
|
+
// template/masked patterns below can match a genuine credential.
|
|
66
|
+
export function isPlaceholder(value) {
|
|
67
|
+
if (typeof value !== 'string' || value.length === 0) return false;
|
|
68
|
+
// <HW_ACCESS_KEY> / <your-ak> / <...>
|
|
69
|
+
if (/^<[^>]*>$/.test(value)) return true;
|
|
70
|
+
// ${HW_ACCESS_KEY}
|
|
71
|
+
if (/^\$\{[^}]*\}$/.test(value)) return true;
|
|
72
|
+
// YOUR_AK / ACCESS_KEY / SECRET_KEY / SECURITY_TOKEN / REGION (bare keyword)
|
|
73
|
+
if (/^(YOUR_)?(AK|SK|ACCESS[_-]?KEY|SECRET[_-]?KEY|TOKEN|SECURITY[_-]?TOKEN|REGION)$/i.test(value)) return true;
|
|
74
|
+
// placeholder / replace_me / change.me
|
|
75
|
+
if (/\b(placeholder|replace[._-]?me|change[._-]?me)\b/i.test(value)) return true;
|
|
76
|
+
// masked value: short alnum prefix + asterisk run (abc**** / token****) — the
|
|
77
|
+
// S4 mirror writes the SK slot this way so the secret never lands in plaintext.
|
|
78
|
+
if (/^[A-Za-z0-9]{3,8}\*+$/.test(value)) return true;
|
|
79
|
+
// bare asterisks
|
|
80
|
+
if (/^\*+$/.test(value)) return true;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function present(v) {
|
|
85
|
+
return typeof v === 'string' && v.length > 0 && !isPlaceholder(v);
|
|
86
|
+
}
|
|
87
|
+
|
|
61
88
|
export function writeGlobalCredentials(credentials = {}) {
|
|
62
89
|
const path = globalCredentialsPath();
|
|
63
90
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -99,9 +126,10 @@ export function writeObsConfig(credentials = {}) {
|
|
|
99
126
|
}
|
|
100
127
|
|
|
101
128
|
export function resolveCredentials(options = {}) {
|
|
102
|
-
|
|
103
|
-
let
|
|
104
|
-
let
|
|
129
|
+
// R11: placeholder/masked env values are "not configured", never credentials.
|
|
130
|
+
let ak = present(process.env.HW_ACCESS_KEY) ? process.env.HW_ACCESS_KEY : '';
|
|
131
|
+
let sk = present(process.env.HW_SECRET_KEY) ? process.env.HW_SECRET_KEY : '';
|
|
132
|
+
let securityToken = present(process.env.HW_SECURITY_TOKEN) ? process.env.HW_SECURITY_TOKEN : '';
|
|
105
133
|
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
106
134
|
|
|
107
135
|
const codeartsCreds = isCodeArtsContext() ? readCodeArtsCredentials() : null;
|
|
@@ -113,36 +141,33 @@ export function resolveCredentials(options = {}) {
|
|
|
113
141
|
|
|
114
142
|
const stored = readGlobalCredentials();
|
|
115
143
|
if (stored) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!
|
|
144
|
+
// R11: skip placeholder/masked values from the vault too (templates must
|
|
145
|
+
// not win the truthiness check and shadow another store).
|
|
146
|
+
if (!ak && present(stored.ak)) ak = stored.ak;
|
|
147
|
+
if (!sk && present(stored.sk)) sk = stored.sk;
|
|
148
|
+
if (!securityToken && present(stored.securityToken)) securityToken = stored.securityToken;
|
|
119
149
|
if (!region && stored.region) region = stored.region;
|
|
120
150
|
}
|
|
121
151
|
|
|
122
152
|
// R9: S1 written by `auth_switch persist` (configuredBySession) is the session's
|
|
123
153
|
// source of truth and wins over env-injected defaults. Plain S1 (`auth init`)
|
|
124
154
|
// still yields to env so devspace-style env injection keeps working.
|
|
125
|
-
if (stored && stored.configuredBySession === true && stored.ak && stored.sk) {
|
|
155
|
+
if (stored && stored.configuredBySession === true && present(stored.ak) && present(stored.sk)) {
|
|
126
156
|
ak = stored.ak;
|
|
127
157
|
sk = stored.sk;
|
|
128
|
-
if (!securityToken) securityToken = stored.securityToken
|
|
158
|
+
if (!securityToken) securityToken = present(stored.securityToken) ? stored.securityToken : '';
|
|
129
159
|
if (!region) region = stored.region || '';
|
|
130
160
|
}
|
|
131
161
|
|
|
132
162
|
// Sandbox/platform-injected temporary STS credentials (env vars carrying a
|
|
133
163
|
// security token) must not shadow the user's explicit permanent credentials
|
|
134
164
|
// from `auth init`. Prefer the stored file when both exist.
|
|
135
|
-
|
|
136
|
-
process.env.HW_ACCESS_KEY &&
|
|
137
|
-
|
|
138
|
-
process.env.HW_SECURITY_TOKEN &&
|
|
139
|
-
stored &&
|
|
140
|
-
stored.ak &&
|
|
141
|
-
stored.sk
|
|
142
|
-
) {
|
|
165
|
+
const envHasFullTriplet =
|
|
166
|
+
present(process.env.HW_ACCESS_KEY) && present(process.env.HW_SECRET_KEY) && present(process.env.HW_SECURITY_TOKEN);
|
|
167
|
+
if (envHasFullTriplet && stored && present(stored.ak) && present(stored.sk)) {
|
|
143
168
|
ak = stored.ak;
|
|
144
169
|
sk = stored.sk;
|
|
145
|
-
securityToken = stored.securityToken
|
|
170
|
+
securityToken = present(stored.securityToken) ? stored.securityToken : '';
|
|
146
171
|
region = stored.region || region;
|
|
147
172
|
}
|
|
148
173
|
|
|
@@ -189,16 +214,16 @@ export function readCodeArtsCredentials() {
|
|
|
189
214
|
try {
|
|
190
215
|
if (!existsSync(path)) continue;
|
|
191
216
|
const config = JSON.parse(readFileSync(path, 'utf8'));
|
|
192
|
-
const server = config?.mcpServers?.['huaweicloud-devkit'];
|
|
217
|
+
const server = config?.mcpServers?.['huaweicloud-devkit'] || config?.mcpServers?.['HuaweiCloud DevKit'];
|
|
193
218
|
if (!server?.env) continue;
|
|
194
219
|
|
|
195
220
|
const ak = server.env.HW_ACCESS_KEY;
|
|
196
221
|
const sk = server.env.HW_SECRET_KEY;
|
|
197
|
-
if (ak && sk) {
|
|
222
|
+
if (present(ak) && present(sk)) {
|
|
198
223
|
return {
|
|
199
224
|
ak,
|
|
200
225
|
sk,
|
|
201
|
-
securityToken: server.env.HW_SECURITY_TOKEN
|
|
226
|
+
securityToken: present(server.env.HW_SECURITY_TOKEN) ? server.env.HW_SECURITY_TOKEN : '',
|
|
202
227
|
region: server.env.HW_REGION || server.env.HUAWEICLOUD_REGION || '',
|
|
203
228
|
};
|
|
204
229
|
}
|
|
@@ -213,15 +238,15 @@ export function readCodeArtsCredentials() {
|
|
|
213
238
|
try {
|
|
214
239
|
if (existsSync(path)) {
|
|
215
240
|
const config = JSON.parse(readFileSync(path, 'utf8'));
|
|
216
|
-
const server = config?.mcp?.['huaweicloud-devkit'];
|
|
241
|
+
const server = config?.mcp?.['huaweicloud-devkit'] || config?.mcp?.['HuaweiCloud DevKit'];
|
|
217
242
|
if (server?.environment) {
|
|
218
243
|
const ak = server.environment.HW_ACCESS_KEY;
|
|
219
244
|
const sk = server.environment.HW_SECRET_KEY;
|
|
220
|
-
if (ak && sk) {
|
|
245
|
+
if (present(ak) && present(sk)) {
|
|
221
246
|
return {
|
|
222
247
|
ak,
|
|
223
248
|
sk,
|
|
224
|
-
securityToken: server.environment.HW_SECURITY_TOKEN
|
|
249
|
+
securityToken: present(server.environment.HW_SECURITY_TOKEN) ? server.environment.HW_SECURITY_TOKEN : '',
|
|
225
250
|
region: server.environment.HW_REGION || server.environment.HUAWEICLOUD_REGION || '',
|
|
226
251
|
};
|
|
227
252
|
}
|
|
@@ -6,6 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
hasRuntimeCredentials,
|
|
9
|
+
isPlaceholder,
|
|
9
10
|
obsConfigPath,
|
|
10
11
|
readGlobalCredentials,
|
|
11
12
|
readLastSync,
|
|
@@ -104,10 +105,13 @@ function s2CurrentMatchesLastDevkitSync(kooCli, s1, s1Fingerprint) {
|
|
|
104
105
|
|
|
105
106
|
export function scanState() {
|
|
106
107
|
const s1 = readGlobalCredentials() || {};
|
|
107
|
-
const envAk = process.env.HW_ACCESS_KEY || '';
|
|
108
|
-
const envSk = process.env.HW_SECRET_KEY || '';
|
|
108
|
+
const envAk = isPlaceholder(process.env.HW_ACCESS_KEY) ? '' : process.env.HW_ACCESS_KEY || '';
|
|
109
|
+
const envSk = isPlaceholder(process.env.HW_SECRET_KEY) ? '' : process.env.HW_SECRET_KEY || '';
|
|
109
110
|
const kooCli = readKooCliProfiles();
|
|
110
111
|
const inconsistencies = [];
|
|
112
|
+
const envIsPlaceholder =
|
|
113
|
+
Boolean(process.env.HW_ACCESS_KEY && isPlaceholder(process.env.HW_ACCESS_KEY)) ||
|
|
114
|
+
Boolean(process.env.HW_SECRET_KEY && isPlaceholder(process.env.HW_SECRET_KEY));
|
|
111
115
|
|
|
112
116
|
const s1Fingerprint = fingerprint(s1.ak, s1.sk);
|
|
113
117
|
const currentFp = currentFingerprintFromHcloud(kooCli);
|
|
@@ -153,6 +157,9 @@ export function scanState() {
|
|
|
153
157
|
runtimeFingerprint,
|
|
154
158
|
},
|
|
155
159
|
kooCliCurrent: kooCli.error ? null : kooCli.current,
|
|
160
|
+
// R11: env carries placeholder/masked values → surface that explicitly so
|
|
161
|
+
// diagnostics don't show a bogus fingerprint derived from template text.
|
|
162
|
+
envIsPlaceholder,
|
|
156
163
|
// S2 is encrypted storage (authEncrypt) → its fingerprint is unavailable,
|
|
157
164
|
// so the S2-current drift check is intentionally skipped (#533).
|
|
158
165
|
s2Encrypted: Boolean(!kooCli.error && kooCli.authEncrypt),
|