huaweicloud-devkit 1.1.4-next.6 → 1.1.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 +2 -2
- 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/safety/policy.json +1 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +34 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +135 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +12 -1
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
|
|
4
|
+
"version": "1.1.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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"test": "node --test \"test/*.test.mjs\"",
|
|
33
|
+
"test": "node --experimental-test-module-mocks --test \"test/*.test.mjs\"",
|
|
34
34
|
"format": "prettier --write .",
|
|
35
35
|
"format:check": "prettier --check .",
|
|
36
36
|
"lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.git\" \"#test/**\" \"#docs/**\" \"#.superpowers/**\"",
|
|
@@ -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
|
|
20
|
+
"version": "1.1.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
|
|
23
|
+
"version": "1.1.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
|
|
20
|
+
"version": "1.1.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
|
|
3
|
+
"version": "1.1.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
|
|
3
|
+
"version": "1.1.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
|
|
5
|
+
"version": "1.1.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.",
|
|
@@ -177,6 +177,40 @@ export function resolveCredentials(options = {}) {
|
|
|
177
177
|
'Huawei Cloud credentials are not configured. Run "npx huaweicloud-devkit auth init" or set HW_ACCESS_KEY/HW_SECRET_KEY.',
|
|
178
178
|
);
|
|
179
179
|
err.code = 'HDKIT_CRED_MISSING';
|
|
180
|
+
// Lightweight onboarding hint. HDKIT_CRED_MISSING fires when no credential
|
|
181
|
+
// resolved: with env empty/masked and S1 absent, the scenario is 3 by
|
|
182
|
+
// default. Scenario 4 (import a config-provided account) is only reachable
|
|
183
|
+
// in the narrow case where CodeArts S4 offers real creds that the resolver
|
|
184
|
+
// still couldn't adopt (e.g. CODEARTS_PROJECT_DIR set but the marker dir is
|
|
185
|
+
// absent). Full guidance comes from getAuthStatus(); keep this branch cheap.
|
|
186
|
+
const codeartsCreds = readCodeArtsCredentials();
|
|
187
|
+
const injectedAvailable = present(codeartsCreds?.ak) && present(codeartsCreds?.sk);
|
|
188
|
+
err.onboarding = injectedAvailable
|
|
189
|
+
? {
|
|
190
|
+
scenario: 4,
|
|
191
|
+
reason: 'import-injected',
|
|
192
|
+
message: '检测到配置中已有有效账号,可导入为正式凭证。',
|
|
193
|
+
steps: isCodeArtsContext()
|
|
194
|
+
? [{ action: 'auth_switch', args: { mode: 'mcp-config', action: 'persist' }, label: '导入配置中的账号' }]
|
|
195
|
+
: [
|
|
196
|
+
{
|
|
197
|
+
action: 'auth_switch',
|
|
198
|
+
args: { mode: 'import', action: 'persist' },
|
|
199
|
+
label: '通过 creds-import.json 导入',
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
}
|
|
203
|
+
: {
|
|
204
|
+
scenario: 3,
|
|
205
|
+
reason: 's1-missing',
|
|
206
|
+
message: '未配置华为云凭证,需要完成登录后才能使用云能力。',
|
|
207
|
+
steps: isCodeArtsContext()
|
|
208
|
+
? [
|
|
209
|
+
{ action: 'write-import', target: join(homedir(), '.config', 'huaweicloud', 'creds-import.json') },
|
|
210
|
+
{ action: 'auth_switch', args: { mode: 'import', action: 'persist' } },
|
|
211
|
+
]
|
|
212
|
+
: [{ action: 'auth-init', args: {} }],
|
|
213
|
+
};
|
|
180
214
|
throw err;
|
|
181
215
|
}
|
|
182
216
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
|
|
3
5
|
import { getAgentRegistrationStatuses } from './agent-registration.mjs';
|
|
4
6
|
import { resolveAndApplyProjectId } from './project-id.mjs';
|
|
5
7
|
import {
|
|
6
8
|
globalCredentialsPath,
|
|
9
|
+
isPlaceholder,
|
|
7
10
|
obsConfigPath,
|
|
8
11
|
readGlobalCredentials,
|
|
9
12
|
writeLastSync,
|
|
@@ -18,10 +21,141 @@ import {
|
|
|
18
21
|
} from './reconcile.mjs';
|
|
19
22
|
import { hcloudProbeNextStep, probeHcloud } from '../hcloud-probe.mjs';
|
|
20
23
|
|
|
24
|
+
function isCodeArtsHome() {
|
|
25
|
+
return (
|
|
26
|
+
existsSync(join(process.cwd(), '.codeartsdoer')) ||
|
|
27
|
+
existsSync(join(homedir(), '.codeartsdoer')) ||
|
|
28
|
+
existsSync(join(homedir(), '.codeartswork'))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function envHasRealTriplet() {
|
|
33
|
+
return (
|
|
34
|
+
!isPlaceholder(process.env.HW_ACCESS_KEY) &&
|
|
35
|
+
Boolean(process.env.HW_ACCESS_KEY) &&
|
|
36
|
+
!isPlaceholder(process.env.HW_SECRET_KEY) &&
|
|
37
|
+
Boolean(process.env.HW_SECRET_KEY) &&
|
|
38
|
+
!isPlaceholder(process.env.HW_SECURITY_TOKEN) &&
|
|
39
|
+
Boolean(process.env.HW_SECURITY_TOKEN)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Onboarding guidance (scenarios 1-4) for credential setup.
|
|
44
|
+
// scenario: 1=S1 exists, no env/…; 2=conflict (S1 vs injected); 3=everything
|
|
45
|
+
// missing (fresh user); 4=S1 empty but injected creds exist (import candidate).
|
|
46
|
+
export function computeOnboarding({ credentials, reconciled } = {}) {
|
|
47
|
+
const creds = credentials ?? readGlobalCredentials();
|
|
48
|
+
const scan = reconciled ?? exportStateForStatus();
|
|
49
|
+
const s1Has = Boolean(creds?.ak && creds?.sk && !isPlaceholder(creds.ak) && !isPlaceholder(creds.sk));
|
|
50
|
+
const injected = envHasRealTriplet();
|
|
51
|
+
// env has real non-triplet creds that are NOT placeholders (e.g. devspace AK/SK w/o token)
|
|
52
|
+
const envRealAk = !isPlaceholder(process.env.HW_ACCESS_KEY) && Boolean(process.env.HW_ACCESS_KEY);
|
|
53
|
+
const envRealSk = !isPlaceholder(process.env.HW_SECRET_KEY) && Boolean(process.env.HW_SECRET_KEY);
|
|
54
|
+
const envHasCreds = envRealAk && envRealSk;
|
|
55
|
+
const codeArts = isCodeArtsHome();
|
|
56
|
+
const accountHint = s1Has ? fingerprint(creds.ak, creds.sk) : null;
|
|
57
|
+
|
|
58
|
+
let scenario;
|
|
59
|
+
let reason;
|
|
60
|
+
let message;
|
|
61
|
+
let steps;
|
|
62
|
+
|
|
63
|
+
if (scan.hasRuntime) {
|
|
64
|
+
scenario = 0;
|
|
65
|
+
reason = 'runtime-active';
|
|
66
|
+
message = 'Runtime credentials are active; no setup needed.';
|
|
67
|
+
return { needsSetup: false, scenario, reason, message, steps: [], accountHint };
|
|
68
|
+
}
|
|
69
|
+
if (envHasRealTriplet() && !s1Has) {
|
|
70
|
+
scenario = 0;
|
|
71
|
+
reason = 'platform-injected';
|
|
72
|
+
message = 'Platform credentials are active; nothing to configure.';
|
|
73
|
+
return { needsSetup: false, scenario, reason, message, steps: [], accountHint };
|
|
74
|
+
}
|
|
75
|
+
if (s1Has && !envHasCreds) {
|
|
76
|
+
scenario = 1;
|
|
77
|
+
reason = 's1-only';
|
|
78
|
+
message = `已保存账号(指纹 ${accountHint})可直接使用。`;
|
|
79
|
+
steps = [
|
|
80
|
+
{ order: 1, action: 'use-s1', args: {}, label: '直接使用已保存账号' },
|
|
81
|
+
{ order: 2, action: 'switch-new', args: { mode: 'import', action: 'persist' }, label: '改用新账号(导入)' },
|
|
82
|
+
];
|
|
83
|
+
} else if (s1Has && envHasCreds) {
|
|
84
|
+
// Covers both plain env creds (conflict) AND platform triplet when S1
|
|
85
|
+
// exists. resolveCredentials' STS gate keeps S1 in front of a triplet,
|
|
86
|
+
// so the effective account is S1 → scenario 1 semantics plus a note.
|
|
87
|
+
if (injected) {
|
|
88
|
+
scenario = 1;
|
|
89
|
+
reason = 's1-with-platform-injected';
|
|
90
|
+
message = `已保存账号(指纹 ${accountHint})优先于平台注入,可直接使用。`;
|
|
91
|
+
steps = [
|
|
92
|
+
{ order: 1, action: 'use-s1', args: {}, label: '使用已保存账号' },
|
|
93
|
+
{
|
|
94
|
+
order: 2,
|
|
95
|
+
action: 'switch-platform',
|
|
96
|
+
args: { mode: 'mcp-config', action: 'persist' },
|
|
97
|
+
label: '改用平台注入账号',
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
} else {
|
|
101
|
+
scenario = 2;
|
|
102
|
+
reason = 'conflict';
|
|
103
|
+
message = '检测到两套账号:已保存 与 环境注入,请选择其一。';
|
|
104
|
+
steps = [
|
|
105
|
+
{
|
|
106
|
+
order: 1,
|
|
107
|
+
action: 'switch-persist',
|
|
108
|
+
args: { mode: 'memory', action: 'persist' },
|
|
109
|
+
label: '使用已保存账号覆盖',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
order: 2,
|
|
113
|
+
action: 'switch-env',
|
|
114
|
+
args: { mode: 'mcp-config', action: 'persist' },
|
|
115
|
+
label: '使用环境注入账号导入',
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
} else if (!s1Has && envHasCreds && !injected) {
|
|
120
|
+
scenario = 4;
|
|
121
|
+
reason = 'import-injected';
|
|
122
|
+
message = '检测到配置中已有有效账号,可导入为正式凭证。';
|
|
123
|
+
steps = [
|
|
124
|
+
{
|
|
125
|
+
order: 1,
|
|
126
|
+
action: 'switch-persist',
|
|
127
|
+
args: { mode: codeArts ? 'mcp-config' : 'import', action: 'persist' },
|
|
128
|
+
label: '导入该账号',
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
} else {
|
|
132
|
+
scenario = 3;
|
|
133
|
+
reason = 's1-missing';
|
|
134
|
+
message = '未配置华为云凭证,需要完成登录后才能使用云能力。';
|
|
135
|
+
steps = [
|
|
136
|
+
{ order: 1, action: 'obtain-aksk', target: 'console', label: '获取 AK/SK(华为云控制台)' },
|
|
137
|
+
...(codeArts
|
|
138
|
+
? [
|
|
139
|
+
{
|
|
140
|
+
order: 2,
|
|
141
|
+
action: 'write-import',
|
|
142
|
+
target: join(homedir(), '.config', 'huaweicloud', 'creds-import.json'),
|
|
143
|
+
label: '把 AK/SK 写入 creds-import.json',
|
|
144
|
+
},
|
|
145
|
+
{ order: 3, action: 'auth_switch', args: { mode: 'import', action: 'persist' }, label: '执行导入完成配置' },
|
|
146
|
+
]
|
|
147
|
+
: [{ order: 2, action: 'auth-init', args: {}, label: '运行 npx huaweicloud-devkit auth init' }]),
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { needsSetup: true, scenario, reason, message, steps, accountHint };
|
|
152
|
+
}
|
|
153
|
+
|
|
21
154
|
export function getAuthStatus(target = 'all') {
|
|
22
155
|
const credentials = readGlobalCredentials();
|
|
23
156
|
const reconciled = { ...exportStateForStatus(), runtimeActive: hasRuntimeCredentials() };
|
|
24
157
|
const hcloud = probeHcloud();
|
|
158
|
+
const onboarding = computeOnboarding({ credentials, reconciled });
|
|
25
159
|
return {
|
|
26
160
|
target,
|
|
27
161
|
credentialsConfigured: Boolean(credentials?.ak && credentials?.sk),
|
|
@@ -32,6 +166,7 @@ export function getAuthStatus(target = 'all') {
|
|
|
32
166
|
kooCliStatus: hcloud.status,
|
|
33
167
|
kooCliNextStep: hcloudProbeNextStep(hcloud),
|
|
34
168
|
reconciled,
|
|
169
|
+
onboarding,
|
|
35
170
|
agents: getAgentRegistrationStatuses(target).agents,
|
|
36
171
|
};
|
|
37
172
|
}
|
|
@@ -3,6 +3,17 @@ import { getProxyUrlForTarget } from './proxy-config.mjs';
|
|
|
3
3
|
let cachedDispatcher = undefined;
|
|
4
4
|
let cachedDispatcherProxyUrl = null;
|
|
5
5
|
|
|
6
|
+
// Enterprise MITM proxies re-sign TLS on BOTH legs: the proxy's own certificate
|
|
7
|
+
// (`proxyTls`) and the tunneled connection to the target server (`requestTls`).
|
|
8
|
+
// Both must be relaxed for such environments. Note `requestTls` is NOT the proxy
|
|
9
|
+
// cert — it is the target-server cert seen through the tunnel. This relaxation is
|
|
10
|
+
// scoped to proxied connections only; direct connections keep strict certificate
|
|
11
|
+
// verification (see the non-proxy branch of `fetchWithProxy`).
|
|
12
|
+
export const PROXY_TLS_OPTIONS = {
|
|
13
|
+
proxyTls: { rejectUnauthorized: false },
|
|
14
|
+
requestTls: { rejectUnauthorized: false },
|
|
15
|
+
};
|
|
16
|
+
|
|
6
17
|
async function importUndici() {
|
|
7
18
|
try {
|
|
8
19
|
/* eslint-disable-next-line n/no-missing-import */
|
|
@@ -21,7 +32,7 @@ export async function getProxyDispatcher(targetUrl) {
|
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
const { ProxyAgent } = await importUndici();
|
|
24
|
-
cachedDispatcher = new ProxyAgent(proxyUrl);
|
|
35
|
+
cachedDispatcher = new ProxyAgent({ uri: proxyUrl, ...PROXY_TLS_OPTIONS });
|
|
25
36
|
cachedDispatcherProxyUrl = proxyUrl;
|
|
26
37
|
return cachedDispatcher;
|
|
27
38
|
}
|