huaweicloud-devkit 1.1.2-next.3 → 1.1.2-next.5
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/README.md +29 -1
- package/README.zh-CN.md +29 -1
- package/integrations/dsh/hook-plugin.mjs +11 -6
- package/integrations/opencode/hooks/skill-tracker.js +10 -9
- 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/hooks/hooks.json +2 -2
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.mjs +56 -0
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +1 -1
- package/plugins/huaweicloud-core/src/auth/credential-validator.mjs +163 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +6 -2
- package/plugins/huaweicloud-core/src/auth/project-id.mjs +68 -0
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +30 -6
- package/plugins/huaweicloud-core/src/auth/service.mjs +22 -23
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +5 -10
- package/plugins/huaweicloud-core/src/hcloud-probe.mjs +154 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +7 -0
- package/plugins/huaweicloud-core/src/safety-policy.mjs +17 -5
- package/plugins/huaweicloud-core/src/setup-cli.mjs +192 -134
- package/plugins/huaweicloud-core/src/telemetry/agent-detect.mjs +2 -2
- package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +9 -1
- package/plugins/huaweicloud-core/src/telemetry/telemetry.mjs +41 -5
- package/plugins/huaweicloud-core/src/tools.mjs +86 -38
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
readLastSync,
|
|
12
12
|
resolveCredentialsWithRuntime,
|
|
13
13
|
} from './credentials.mjs';
|
|
14
|
+
import { resolveHcloudCommand } from '../hcloud-probe.mjs';
|
|
14
15
|
import { redactSecrets } from '../safety-policy.mjs';
|
|
15
16
|
|
|
16
17
|
export { hasRuntimeCredentials };
|
|
@@ -47,6 +48,7 @@ export function readKooCliProfiles() {
|
|
|
47
48
|
return {
|
|
48
49
|
current,
|
|
49
50
|
mtimeMs,
|
|
51
|
+
configPath,
|
|
50
52
|
profiles: profiles.map((p) => ({
|
|
51
53
|
name: String(p.name || ''),
|
|
52
54
|
fingerprint: fingerprint(p.accessKeyId, p.secretAccessKey),
|
|
@@ -70,15 +72,32 @@ export function currentFingerprintFromHcloud(res) {
|
|
|
70
72
|
return cur ? cur.fingerprint : null;
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
function currentProfileFromHcloud(res) {
|
|
76
|
+
if (res.error) return null;
|
|
77
|
+
return res.profiles.find((p) => p.name === res.current) || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function s2CurrentMatchesLastDevkitSync(kooCli, s1, s1Fingerprint) {
|
|
81
|
+
const current = currentProfileFromHcloud(kooCli);
|
|
82
|
+
const lastSync = readLastSync();
|
|
83
|
+
if (!current || !lastSync?.ts || !lastSync.kooCliProfile || !lastSync.s1Fingerprint) return false;
|
|
84
|
+
if (!s1?.ak || current.accessKeyId !== s1.ak) return false;
|
|
85
|
+
if (lastSync.kooCliProfile !== kooCli.current) return false;
|
|
86
|
+
if (lastSync.s1Fingerprint !== s1Fingerprint) return false;
|
|
87
|
+
return Number(kooCli.mtimeMs || 0) <= Number(lastSync.ts) + 1000;
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
export function scanState() {
|
|
74
91
|
const s1 = readGlobalCredentials() || {};
|
|
75
92
|
const envAk = process.env.HW_ACCESS_KEY || '';
|
|
76
93
|
const envSk = process.env.HW_SECRET_KEY || '';
|
|
77
94
|
const kooCli = readKooCliProfiles();
|
|
78
|
-
const currentFp = currentFingerprintFromHcloud(kooCli);
|
|
79
95
|
const inconsistencies = [];
|
|
80
96
|
|
|
81
97
|
const s1Fingerprint = fingerprint(s1.ak, s1.sk);
|
|
98
|
+
const currentFp = currentFingerprintFromHcloud(kooCli);
|
|
99
|
+
const s2SyncedByDevkit = s1Fingerprint ? s2CurrentMatchesLastDevkitSync(kooCli, s1, s1Fingerprint) : false;
|
|
100
|
+
const effectiveCurrentFp = s2SyncedByDevkit ? s1Fingerprint : currentFp;
|
|
82
101
|
const envFingerprint = fingerprint(envAk, envSk);
|
|
83
102
|
const s3 = existsSync(obsConfigPath()) ? parseS3ObsConfig(obsConfigPath()) : null;
|
|
84
103
|
const s3Fingerprint = s3 ? fingerprint(s3.ak, s3.sk) : null;
|
|
@@ -93,12 +112,12 @@ export function scanState() {
|
|
|
93
112
|
// nothing resolvable → runtime store inactive
|
|
94
113
|
}
|
|
95
114
|
|
|
96
|
-
if (s1Fingerprint && currentFp && s1Fingerprint !==
|
|
115
|
+
if (s1Fingerprint && currentFp && s1Fingerprint !== effectiveCurrentFp) {
|
|
97
116
|
inconsistencies.push({
|
|
98
117
|
store: 'S2-current',
|
|
99
118
|
source: 'KooCLI current profile',
|
|
100
119
|
fingerprint: currentFp,
|
|
101
|
-
manualModified: isManualModified(join(baseHome(), '.hcloud', 'config.json')),
|
|
120
|
+
manualModified: isManualModified(kooCli.configPath || join(baseHome(), '.hcloud', 'config.json')),
|
|
102
121
|
});
|
|
103
122
|
}
|
|
104
123
|
if (s1Fingerprint && s3Fingerprint && s1Fingerprint !== s3Fingerprint) {
|
|
@@ -114,7 +133,7 @@ export function scanState() {
|
|
|
114
133
|
stores: {
|
|
115
134
|
s1Fingerprint,
|
|
116
135
|
envFingerprint,
|
|
117
|
-
currentFingerprint:
|
|
136
|
+
currentFingerprint: effectiveCurrentFp,
|
|
118
137
|
s3Fingerprint,
|
|
119
138
|
runtimeFingerprint,
|
|
120
139
|
},
|
|
@@ -153,7 +172,7 @@ export function exportStateForStatus(_jailed) {
|
|
|
153
172
|
}
|
|
154
173
|
|
|
155
174
|
export function runHcloudConfigure(profile, ak, sk, region) {
|
|
156
|
-
const
|
|
175
|
+
const { executable, argsPrefix } = resolveHcloudCommand();
|
|
157
176
|
const args = [
|
|
158
177
|
'configure',
|
|
159
178
|
'set',
|
|
@@ -162,7 +181,12 @@ export function runHcloudConfigure(profile, ak, sk, region) {
|
|
|
162
181
|
`--cli-secret-key=${sk}`,
|
|
163
182
|
`--cli-region=${region || ''}`,
|
|
164
183
|
];
|
|
165
|
-
const r = spawnSync(
|
|
184
|
+
const r = spawnSync(executable, [...argsPrefix, ...args], {
|
|
185
|
+
shell: false,
|
|
186
|
+
windowsHide: true,
|
|
187
|
+
stdio: 'pipe',
|
|
188
|
+
timeout: 30000,
|
|
189
|
+
});
|
|
166
190
|
return {
|
|
167
191
|
ok: r.status === 0,
|
|
168
192
|
error: redactSecrets(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
3
2
|
|
|
4
3
|
import { getAgentRegistrationStatuses } from './agent-registration.mjs';
|
|
4
|
+
import { resolveAndApplyProjectId } from './project-id.mjs';
|
|
5
5
|
import {
|
|
6
6
|
globalCredentialsPath,
|
|
7
7
|
obsConfigPath,
|
|
@@ -10,38 +10,27 @@ import {
|
|
|
10
10
|
writeObsConfig,
|
|
11
11
|
} from './credentials.mjs';
|
|
12
12
|
import {
|
|
13
|
+
fingerprint,
|
|
13
14
|
exportStateForStatus,
|
|
14
15
|
hasRuntimeCredentials,
|
|
15
16
|
resolveManagedProfile,
|
|
16
17
|
runHcloudConfigure,
|
|
17
18
|
} from './reconcile.mjs';
|
|
18
|
-
|
|
19
|
-
function hcloudInstalled() {
|
|
20
|
-
const bin = process.env.HCLOUD_BIN || 'hcloud';
|
|
21
|
-
try {
|
|
22
|
-
const r = spawnSync(`"${bin}" version`, [], {
|
|
23
|
-
shell: true,
|
|
24
|
-
windowsHide: true,
|
|
25
|
-
stdio: 'pipe',
|
|
26
|
-
timeout: 5000,
|
|
27
|
-
});
|
|
28
|
-
const out = `${r.stdout || ''}${r.stderr || ''}`;
|
|
29
|
-
return r.status === 0 && /KooCLI|Current.*version|当前KooCLI/i.test(out);
|
|
30
|
-
} catch {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
19
|
+
import { hcloudProbeNextStep, probeHcloud } from '../hcloud-probe.mjs';
|
|
34
20
|
|
|
35
21
|
export function getAuthStatus(target = 'all') {
|
|
36
22
|
const credentials = readGlobalCredentials();
|
|
37
23
|
const reconciled = { ...exportStateForStatus(), runtimeActive: hasRuntimeCredentials() };
|
|
24
|
+
const hcloud = probeHcloud();
|
|
38
25
|
return {
|
|
39
26
|
target,
|
|
40
27
|
credentialsConfigured: Boolean(credentials?.ak && credentials?.sk),
|
|
41
28
|
credentialsPath: globalCredentialsPath(),
|
|
42
29
|
obsConfigured: existsSync(obsConfigPath()),
|
|
43
30
|
obsConfigPath: obsConfigPath(),
|
|
44
|
-
kooCliInstalled:
|
|
31
|
+
kooCliInstalled: hcloud.installed,
|
|
32
|
+
kooCliStatus: hcloud.status,
|
|
33
|
+
kooCliNextStep: hcloudProbeNextStep(hcloud),
|
|
45
34
|
reconciled,
|
|
46
35
|
agents: getAgentRegistrationStatuses(target).agents,
|
|
47
36
|
};
|
|
@@ -85,11 +74,17 @@ export function syncAuth(target = 'all') {
|
|
|
85
74
|
};
|
|
86
75
|
}
|
|
87
76
|
|
|
88
|
-
|
|
77
|
+
const hcloud = probeHcloud();
|
|
78
|
+
if (!hcloud.ok) {
|
|
89
79
|
return {
|
|
90
80
|
ok: false,
|
|
91
|
-
error:
|
|
92
|
-
|
|
81
|
+
error:
|
|
82
|
+
hcloud.status === 'sandbox_home_failure'
|
|
83
|
+
? 'KooCLI detected but cannot resolve the user home directory in this agent sandbox.'
|
|
84
|
+
: hcloud.status === 'privacy_pending'
|
|
85
|
+
? 'KooCLI privacy agreement is pending.'
|
|
86
|
+
: 'KooCLI not ready.',
|
|
87
|
+
nextStep: hcloudProbeNextStep(hcloud),
|
|
93
88
|
obs: { configured: true, path: obs.path, endpoint: obs.endpoint },
|
|
94
89
|
};
|
|
95
90
|
}
|
|
@@ -104,9 +99,11 @@ export function syncAuth(target = 'all') {
|
|
|
104
99
|
};
|
|
105
100
|
}
|
|
106
101
|
|
|
107
|
-
|
|
102
|
+
const project = resolveAndApplyProjectId({ region: credentials.region, profile });
|
|
108
103
|
|
|
109
|
-
|
|
104
|
+
writeLastSync({ kooCliProfile: profile, s1Fingerprint: fingerprint(credentials.ak, credentials.sk) });
|
|
105
|
+
|
|
106
|
+
const result = {
|
|
110
107
|
ok: true,
|
|
111
108
|
profile,
|
|
112
109
|
obs: { configured: true, path: obs.path, endpoint: obs.endpoint },
|
|
@@ -115,4 +112,6 @@ export function syncAuth(target = 'all') {
|
|
|
115
112
|
agents: getAgentRegistrationStatuses(target).agents,
|
|
116
113
|
note: 'OBS credentials were synced from the global credential vault. Agent MCP registration is managed by "npx huaweicloud-devkit install --target <agent>".',
|
|
117
114
|
};
|
|
115
|
+
if (project.ok) result.projectId = project.projectId;
|
|
116
|
+
return result;
|
|
118
117
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { spawn, spawnSync } from 'node:child_process';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import {
|
|
4
|
-
import { homedir } from 'node:os';
|
|
3
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
5
4
|
import { join } from 'node:path';
|
|
6
5
|
|
|
7
6
|
import { classifyHcloudArgs, redactSecrets, assertAllowed } from './safety-policy.mjs';
|
|
8
7
|
import { getProxySettings } from './proxy/proxy-config.mjs';
|
|
8
|
+
import { findHcloudBin, resolveHcloudCommand } from './hcloud-probe.mjs';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
11
11
|
const DEFAULT_FORCE_KILL_AFTER_MS = 2_000;
|
|
@@ -67,10 +67,10 @@ function preflightSecurityGroupCheck(normalizedArgs) {
|
|
|
67
67
|
const findings = [];
|
|
68
68
|
for (const sgId of sgIds) {
|
|
69
69
|
try {
|
|
70
|
-
const
|
|
70
|
+
const { executable, argsPrefix } = resolveHcloudCommand();
|
|
71
71
|
const spawnArgs = ['VPC', 'ListSecurityGroupRules', `--security_group_id.1=${sgId}`];
|
|
72
72
|
if (regionArg) spawnArgs.push(regionArg);
|
|
73
|
-
const r = spawnSync(
|
|
73
|
+
const r = spawnSync(executable, [...argsPrefix, ...spawnArgs], {
|
|
74
74
|
shell: false,
|
|
75
75
|
windowsHide: true,
|
|
76
76
|
stdio: 'pipe',
|
|
@@ -216,12 +216,7 @@ async function runtimeCurrentMismatchWarning() {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
function discoverHcloudPath() {
|
|
219
|
-
|
|
220
|
-
const candidates =
|
|
221
|
-
process.platform === 'win32'
|
|
222
|
-
? [join(homedir(), 'hcloud', 'hcloud.exe')]
|
|
223
|
-
: [join(homedir(), '.local', 'bin', 'hcloud'), join(homedir(), 'hcloud', 'hcloud')];
|
|
224
|
-
return candidates.find((c) => existsSync(c)) || null;
|
|
219
|
+
return findHcloudBin();
|
|
225
220
|
}
|
|
226
221
|
|
|
227
222
|
function runHcloudOnce(plan, options) {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { compareVersion, getKooCliVersion, parseHcloudVersion } from './koocli-version.mjs';
|
|
7
|
+
import { redactSecrets } from './safety-policy.mjs';
|
|
8
|
+
|
|
9
|
+
const VERSION_RE = /KooCLI|Current.*version|当前KooCLI/i;
|
|
10
|
+
|
|
11
|
+
export function findHcloudBin() {
|
|
12
|
+
if (process.env.HCLOUD_BIN && existsSync(process.env.HCLOUD_BIN)) return process.env.HCLOUD_BIN;
|
|
13
|
+
const candidates =
|
|
14
|
+
process.platform === 'win32'
|
|
15
|
+
? [join(homedir(), 'hcloud', 'hcloud.exe')]
|
|
16
|
+
: [join(homedir(), '.local', 'bin', 'hcloud'), join(homedir(), 'hcloud', 'hcloud')];
|
|
17
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
18
|
+
if (found) return found;
|
|
19
|
+
|
|
20
|
+
const locator =
|
|
21
|
+
process.platform === 'win32'
|
|
22
|
+
? spawnSync('where.exe', ['hcloud'], { windowsHide: true, stdio: 'pipe', encoding: 'utf8', timeout: 3000 })
|
|
23
|
+
: spawnSync('sh', ['-c', 'command -v hcloud'], {
|
|
24
|
+
windowsHide: true,
|
|
25
|
+
stdio: 'pipe',
|
|
26
|
+
encoding: 'utf8',
|
|
27
|
+
timeout: 3000,
|
|
28
|
+
});
|
|
29
|
+
if (locator.status === 0) {
|
|
30
|
+
const first = String(locator.stdout || '')
|
|
31
|
+
.split(/\r?\n/)
|
|
32
|
+
.map((line) => line.trim())
|
|
33
|
+
.find(Boolean);
|
|
34
|
+
if (first) return first;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function envHcloudArgs() {
|
|
40
|
+
if (!process.env.HCLOUD_BIN_ARGS_JSON) return [];
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(process.env.HCLOUD_BIN_ARGS_JSON);
|
|
43
|
+
if (Array.isArray(parsed)) return parsed.map((item) => String(item));
|
|
44
|
+
} catch {}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function resolveHcloudCommand(options = {}) {
|
|
49
|
+
return {
|
|
50
|
+
executable: options.executable || findHcloudBin() || 'hcloud',
|
|
51
|
+
argsPrefix: Array.isArray(options.executableArgs) ? options.executableArgs : envHcloudArgs(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function classifyHcloudProbe(result, requiredVersion = getKooCliVersion()) {
|
|
56
|
+
const stdout = String(result.stdout || '');
|
|
57
|
+
const stderr = String(result.stderr || '');
|
|
58
|
+
const output = `${stdout}${stderr}`;
|
|
59
|
+
const installedVersion = parseHcloudVersion(output);
|
|
60
|
+
|
|
61
|
+
if (result.error?.code === 'ENOENT') {
|
|
62
|
+
return {
|
|
63
|
+
status: 'not_found',
|
|
64
|
+
installed: false,
|
|
65
|
+
ok: false,
|
|
66
|
+
errorCode: 'HCLOUD_NOT_FOUND',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (result.status === 0 && (VERSION_RE.test(output) || installedVersion)) {
|
|
71
|
+
const versionMismatch = !!(
|
|
72
|
+
requiredVersion &&
|
|
73
|
+
installedVersion &&
|
|
74
|
+
compareVersion(installedVersion, requiredVersion) !== 0
|
|
75
|
+
);
|
|
76
|
+
return {
|
|
77
|
+
status: versionMismatch ? 'version_mismatch' : 'ok',
|
|
78
|
+
installed: true,
|
|
79
|
+
ok: true,
|
|
80
|
+
installedVersion,
|
|
81
|
+
requiredVersion: requiredVersion || undefined,
|
|
82
|
+
versionMismatch,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (/获取当前用户家目录失败|home\s+directory|user\s+home|home\s+dir|homedir/i.test(output)) {
|
|
87
|
+
return {
|
|
88
|
+
status: 'sandbox_home_failure',
|
|
89
|
+
installed: true,
|
|
90
|
+
ok: false,
|
|
91
|
+
errorCode: 'HCLOUD_SANDBOX_HOME_FAILURE',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
/同意并继续使用|不同意并退出|隐私协议|隐私声明|privacy agreement|privacy statement|invalid character|无效字符/i.test(
|
|
97
|
+
output,
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'privacy_pending',
|
|
102
|
+
installed: true,
|
|
103
|
+
ok: false,
|
|
104
|
+
errorCode: 'HCLOUD_PRIVACY_PENDING',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
status: 'unavailable',
|
|
110
|
+
installed: false,
|
|
111
|
+
ok: false,
|
|
112
|
+
errorCode: 'HCLOUD_UNAVAILABLE',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function probeHcloud(options = {}) {
|
|
117
|
+
const { executable, argsPrefix } = resolveHcloudCommand(options);
|
|
118
|
+
const r = spawnSync(executable, [...argsPrefix, 'version'], {
|
|
119
|
+
shell: false,
|
|
120
|
+
windowsHide: true,
|
|
121
|
+
stdio: 'pipe',
|
|
122
|
+
encoding: 'utf8',
|
|
123
|
+
timeout: options.timeoutMs ?? 5000,
|
|
124
|
+
});
|
|
125
|
+
const classified = classifyHcloudProbe(r, options.requiredVersion);
|
|
126
|
+
const stdout = redactSecrets(String(r.stdout || '').trim());
|
|
127
|
+
const stderr = redactSecrets(String(r.stderr || r.error?.message || '').trim());
|
|
128
|
+
return {
|
|
129
|
+
executable,
|
|
130
|
+
exitCode: r.status,
|
|
131
|
+
stdout,
|
|
132
|
+
stderr,
|
|
133
|
+
output: classified.ok ? stdout : stderr || stdout,
|
|
134
|
+
...classified,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function hcloudProbeNextStep(probe) {
|
|
139
|
+
if (probe.status === 'ok')
|
|
140
|
+
return 'Use huaweicloud_show_profile_redacted to inspect the active KooCLI profile safely.';
|
|
141
|
+
if (probe.status === 'version_mismatch') {
|
|
142
|
+
return `KooCLI version mismatch: installed ${probe.installedVersion}, this plugin is paired with ${probe.requiredVersion}. Reinstall the pinned version (see huaweicloud-cli-and-auth skill) and restart the agent.`;
|
|
143
|
+
}
|
|
144
|
+
if (probe.status === 'sandbox_home_failure') {
|
|
145
|
+
return 'KooCLI was found, but this agent sandbox cannot resolve the Windows user home directory. Set HCLOUD_BIN to the full hcloud path, restart Codex, or verify from a normal terminal.';
|
|
146
|
+
}
|
|
147
|
+
if (probe.status === 'privacy_pending') {
|
|
148
|
+
return 'KooCLI requires accepting its one-time privacy agreement. Run hcloud version in a real terminal and accept the prompt, then restart the agent.';
|
|
149
|
+
}
|
|
150
|
+
if (probe.status === 'not_found') {
|
|
151
|
+
return 'hcloud executable not found. Set HCLOUD_BIN to the full hcloud path, or install KooCLI: npx huaweicloud-devkit install-hcloud. Then restart the agent.';
|
|
152
|
+
}
|
|
153
|
+
return 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.';
|
|
154
|
+
}
|
|
@@ -26,6 +26,13 @@ export async function getProxyDispatcher(targetUrl) {
|
|
|
26
26
|
return cachedDispatcher;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export async function fetchWithProxy(url, options = {}) {
|
|
30
|
+
const dispatcher = await getProxyDispatcher(url);
|
|
31
|
+
if (!dispatcher) return fetch(url, options);
|
|
32
|
+
const { fetch: undiciFetch } = await import('undici');
|
|
33
|
+
return undiciFetch(url, { ...options, dispatcher });
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
export function clearProxyDispatcherCache() {
|
|
30
37
|
cachedDispatcher = undefined;
|
|
31
38
|
cachedDispatcherProxyUrl = null;
|
|
@@ -109,6 +109,14 @@ function applyCommandRiskRules(base, normalizedArgs, options = {}) {
|
|
|
109
109
|
return mergeRiskDecision(base, risk);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
function applyRawCommandRiskRules(base, command, options = {}) {
|
|
113
|
+
if (base.decision === 'deny' || options.skipRiskRules === true) {
|
|
114
|
+
return base;
|
|
115
|
+
}
|
|
116
|
+
const risk = evaluateCommandRisk(command);
|
|
117
|
+
return mergeRiskDecision(base, risk);
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
export function classifyHcloudArgs(args, options = {}) {
|
|
113
121
|
const policy = options.policy || DEFAULT_POLICY;
|
|
114
122
|
const { service, operation, args: normalizedArgs } = commandOperation(args);
|
|
@@ -340,11 +348,15 @@ export function classifyTextCommand(command, options = {}) {
|
|
|
340
348
|
};
|
|
341
349
|
}
|
|
342
350
|
|
|
343
|
-
return
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
return applyRawCommandRiskRules(
|
|
352
|
+
{
|
|
353
|
+
decision: 'allow',
|
|
354
|
+
risk: 'not_huaweicloud',
|
|
355
|
+
reason: 'No Huawei Cloud safety rule matched.',
|
|
356
|
+
},
|
|
357
|
+
text,
|
|
358
|
+
options,
|
|
359
|
+
);
|
|
348
360
|
}
|
|
349
361
|
|
|
350
362
|
export function assertAllowed(result) {
|