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 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.6",
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-next.6",
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-next.6",
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-next.6",
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-next.6",
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-next.6",
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-next.6",
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.",
@@ -36,6 +36,7 @@
36
36
  "Stop",
37
37
  "Start",
38
38
  "Restart",
39
+ "Apply",
39
40
  "Authorize",
40
41
  "Revoke",
41
42
  "Add",
@@ -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
  }