huaweicloud-devkit 1.1.2-next.0 → 1.1.2-next.2
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/.agents/plugins/marketplace.json +18 -18
- package/README.md +182 -38
- package/README.zh-CN.md +180 -36
- package/bin/dsh-postinstall.cjs +77 -0
- package/bin/setup.cjs +7 -4
- package/cordis.patch.yml +13 -0
- package/integrations/atomcode/hooks/huaweicloud-telemetry.js +181 -0
- package/integrations/atomcode/hooks.json +9 -0
- package/integrations/dsh/hook-plugin.mjs +148 -0
- package/integrations/hermes/hooks/huaweicloud-telemetry.py +238 -0
- package/integrations/hermes/manifest.yaml +43 -0
- package/integrations/opencode/hooks/skill-tracker.js +115 -0
- package/integrations/opencode/opencode.json +1 -4
- package/integrations/workbuddy/hooks/telemetry-tracker.py +226 -0
- package/package.json +74 -43
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +23 -33
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +2 -5
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/assets/icon.png +0 -0
- package/plugins/huaweicloud-core/assets/logo-dark.png +0 -0
- package/plugins/huaweicloud-core/assets/logo.png +0 -0
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +104 -28
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/safety/policy.json +75 -99
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +196 -35
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +38 -34
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +38 -32
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +40 -31
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +45 -37
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +57 -6
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +44 -39
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
- package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +93 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/architectures.md +83 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +107 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/state-file.md +104 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +33 -25
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +46 -45
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +71 -61
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +776 -44
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +84 -0
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +100 -0
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +36 -29
- package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +57 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +22 -15
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +23 -20
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +21 -2
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +30 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +68 -54
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +36 -34
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +142 -5
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +217 -5
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +174 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +57 -2
- package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
- package/plugins/huaweicloud-core/src/detect-framework.mjs +366 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +203 -16
- package/plugins/huaweicloud-core/src/icon-library.mjs +176 -0
- package/plugins/huaweicloud-core/src/mcp-protocol.mjs +88 -0
- package/plugins/huaweicloud-core/src/mcp-server-remote.mjs +107 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +154 -99
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
- package/plugins/huaweicloud-core/src/safety-policy.mjs +123 -68
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +48 -12
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +25 -26
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +943 -10
- package/plugins/huaweicloud-core/src/sandbox/uninstall-cleanup.mjs +50 -0
- package/plugins/huaweicloud-core/src/search-market.mjs +49 -14
- package/plugins/huaweicloud-core/src/setup-cli.mjs +3091 -498
- package/plugins/huaweicloud-core/src/telemetry/agent-detect.mjs +40 -0
- package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +328 -0
- package/plugins/huaweicloud-core/src/telemetry/telemetry.mjs +378 -0
- package/plugins/huaweicloud-core/src/tools.mjs +1272 -146
- package/plugins/huaweicloud-core/src/update-check.mjs +383 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +13 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +268 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +40 -13
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
|
@@ -11,12 +11,12 @@ Use evidence before fixes. Do not guess service behavior when request IDs, regio
|
|
|
11
11
|
|
|
12
12
|
## Critical Warnings
|
|
13
13
|
|
|
14
|
-
| Trap
|
|
15
|
-
|
|
16
|
-
| Error in response body, not HTTP status | Many APIs return HTTP 200 with error in JSON body. Always check `error_code` and `error_msg`
|
|
17
|
-
| Missing `project_id` is common
|
|
18
|
-
| Sandbox may block hcloud
|
|
19
|
-
| KooCLI param prefix varies
|
|
14
|
+
| Trap | Why |
|
|
15
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| Error in response body, not HTTP status | Many APIs return HTTP 200 with error in JSON body. Always check `error_code` and `error_msg` |
|
|
17
|
+
| Missing `project_id` is common | New users often omit project_id from API path or CLI. Get it from `IAM KeystoneListProjects` |
|
|
18
|
+
| Sandbox may block hcloud | OpenCode/Codex sandbox can block hcloud metadata files in `~/.hcloud/`. Use `dangerouslyDisableSandbox` or Bash tool |
|
|
19
|
+
| KooCLI param prefix varies | VPC params need `--vpc.x`, ECS params need `--server.x`, ALWAYS check `--help` first |
|
|
20
20
|
|
|
21
21
|
## Workflow
|
|
22
22
|
|
|
@@ -36,42 +36,44 @@ Use evidence before fixes. Do not guess service behavior when request IDs, regio
|
|
|
36
36
|
|
|
37
37
|
## Common Checks
|
|
38
38
|
|
|
39
|
-
| Check
|
|
40
|
-
|
|
41
|
-
| Authentication
|
|
42
|
-
| Region
|
|
43
|
-
| Project ID
|
|
44
|
-
| Pagination
|
|
45
|
-
| Quota
|
|
46
|
-
| IAM Permission
|
|
47
|
-
| KooCLI version
|
|
39
|
+
| Check | Command |
|
|
40
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| Authentication | `hcloud configure list` (redacted) |
|
|
42
|
+
| Region | Match CLI region, endpoint, and resource region |
|
|
43
|
+
| Project ID | `hcloud IAM KeystoneListProjects` |
|
|
44
|
+
| Pagination | Add `--limit=100` and check `marker` |
|
|
45
|
+
| Quota | Check service quotas in console or API |
|
|
46
|
+
| IAM Permission | Verify user/role has required action — see `huawei-iam` skill |
|
|
47
|
+
| KooCLI version | `hcloud version` |
|
|
48
|
+
| Public IP (outbound) | **必须绕过系统代理 + 多源交叉验证**:PowerShell `Invoke-WebRequest` 会走系统代理返回代理出口 IP(实测 167.x vs 真实 117.x,SSH 白名单放错 IP)。正确姿势:`node -e "fetch('https://cip.cc').then(r=>r.text()).then(console.log)"`(node 直连国内源,不继承 IE 代理)+ `curl --noproxy '*' ifconfig.me` 交叉比对;不一致即告警 |
|
|
49
|
+
| SSH 超时但安全组规则"正确" | 先怀疑探测到的公网 IP 走了代理 → 按上行方法重取真实出口 IP,删旧规则加新规则 |
|
|
48
50
|
|
|
49
51
|
## Common Error Codes
|
|
50
52
|
|
|
51
|
-
| Error
|
|
52
|
-
|
|
53
|
-
| AuthFailure / 401
|
|
54
|
-
| AccessDenied / 403
|
|
55
|
-
| NoSuchKey / 404
|
|
56
|
-
| QuotaExceeded
|
|
57
|
-
| [USE_ERROR] 不正确的参数 | Wrong param name
|
|
58
|
-
| Ecs.0005
|
|
59
|
-
| FSS.0400
|
|
60
|
-
| FSS.1417
|
|
61
|
-
| APIC.7201
|
|
62
|
-
| [NETWORK_ERROR]
|
|
53
|
+
| Error | Likely Cause | Fix |
|
|
54
|
+
| ------------------------ | ---------------------------------- | ------------------------------------------------------------ |
|
|
55
|
+
| AuthFailure / 401 | AK/SK invalid or expired | Regenerate AK/SK, re-run `npx huaweicloud-devkit auth init` |
|
|
56
|
+
| AccessDenied / 403 | IAM permission missing | Check `huawei-iam` skill, add required policy action |
|
|
57
|
+
| NoSuchKey / 404 | Resource not found | Verify resource ID, region, and project_id |
|
|
58
|
+
| QuotaExceeded | Account limit reached | Request quota increase in console |
|
|
59
|
+
| [USE_ERROR] 不正确的参数 | Wrong param name | Run `--help`, check `--param=value` format and nested prefix |
|
|
60
|
+
| Ecs.0005 | Flavor-image mismatch | Check image `__support_*` against flavor virtualization type |
|
|
61
|
+
| FSS.0400 | FunctionGraph latest version error | Strip `:latest` from function URN |
|
|
62
|
+
| FSS.1417 | DEDICATEDGATEWAY missing params | Add instance_id, group_id, protocol, env_name, env_id |
|
|
63
|
+
| APIC.7201 | Missing security_group_id | Add `--security_group_id` param for APIG CreateInstanceV2 |
|
|
64
|
+
| [NETWORK_ERROR] | Transient network failure | Retry with `maxRetries` param or wait and retry |
|
|
63
65
|
|
|
64
66
|
## KooCLI Error Types
|
|
65
67
|
|
|
66
68
|
KooCLI classifies errors into 5 types. The error type prefix in the message guides initial diagnosis:
|
|
67
69
|
|
|
68
|
-
| Type
|
|
69
|
-
|
|
70
|
-
| `[NETWORK_ERROR]` | HTTP request exception
|
|
71
|
-
| `[CLI_ERROR]`
|
|
72
|
-
| `[USE_ERROR]`
|
|
73
|
-
| `[OPENAPI_ERROR]` | Cloud service API error
|
|
74
|
-
| `[APIE_ERROR]`
|
|
70
|
+
| Type | Meaning | First Check |
|
|
71
|
+
| ----------------- | ---------------------------- | ----------------------------------------------------- |
|
|
72
|
+
| `[NETWORK_ERROR]` | HTTP request exception | Network connectivity, firewall, endpoint reachability |
|
|
73
|
+
| `[CLI_ERROR]` | KooCLI internal error | Contact KooCLI support |
|
|
74
|
+
| `[USE_ERROR]` | Incorrect command parameters | Run `--help`, check param names and format |
|
|
75
|
+
| `[OPENAPI_ERROR]` | Cloud service API error | Check service docs, contact service support |
|
|
76
|
+
| `[APIE_ERROR]` | API Explorer metadata error | Contact API Explorer support |
|
|
75
77
|
|
|
76
78
|
Enable debug with `--cli-debug=true` to see the underlying HTTP request/response and pinpoint network or endpoint issues.
|
|
77
79
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
|
-
display_name:
|
|
3
|
-
short_description:
|
|
4
|
-
default_prompt:
|
|
2
|
+
display_name: 'Huawei Cloud Troubleshooting'
|
|
3
|
+
short_description: 'Diagnose Huawei Cloud errors using evidence and read-only checks.'
|
|
4
|
+
default_prompt: 'Use $huaweicloud-troubleshooting to diagnose this Huawei Cloud error.'
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
5
6
|
|
|
6
|
-
export const SUPPORTED_AGENT_TARGETS = [
|
|
7
|
+
export const SUPPORTED_AGENT_TARGETS = [
|
|
8
|
+
'opencode',
|
|
9
|
+
'codex',
|
|
10
|
+
'codex-desktop',
|
|
11
|
+
'codearts',
|
|
12
|
+
'codearts-work',
|
|
13
|
+
'workbuddy',
|
|
14
|
+
'dsh',
|
|
15
|
+
'officeace',
|
|
16
|
+
'hermes',
|
|
17
|
+
'openclaw',
|
|
18
|
+
'atomcode',
|
|
19
|
+
];
|
|
7
20
|
|
|
8
21
|
function baseHome() {
|
|
9
22
|
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
@@ -66,6 +79,12 @@ function codeartsRegistered() {
|
|
|
66
79
|
});
|
|
67
80
|
}
|
|
68
81
|
|
|
82
|
+
function codeartsWorkRegistered() {
|
|
83
|
+
const path = join(baseHome(), '.codeartswork', 'mcp', 'mcp_settings.json');
|
|
84
|
+
const cfg = readJsonSafe(path);
|
|
85
|
+
return Boolean(cfg?.mcp?.['huaweicloud-devkit']);
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
function workbuddyRegistered() {
|
|
70
89
|
const cfg = readJsonSafe(join(baseHome(), '.workbuddy', 'mcp.json'));
|
|
71
90
|
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
@@ -80,9 +99,122 @@ function dshRegistered() {
|
|
|
80
99
|
if (!existsSync(patchPath)) return false;
|
|
81
100
|
try {
|
|
82
101
|
const patch = readFileSync(patchPath, 'utf8');
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
return (
|
|
103
|
+
(patch.includes('id: huaweicloud-devkit') || patch.includes('id: mcp-huaweicloud')) &&
|
|
104
|
+
patch.includes('@deepseek-ai/dsh-mcp-client') &&
|
|
105
|
+
patch.includes('serverName: huaweicloud')
|
|
106
|
+
);
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function readOfficeaceRegistryInstallDir() {
|
|
113
|
+
if (process.platform !== 'win32') return null;
|
|
114
|
+
try {
|
|
115
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
116
|
+
encoding: 'utf8',
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
timeout: 5000,
|
|
119
|
+
});
|
|
120
|
+
if (r.status === 0) {
|
|
121
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
122
|
+
if (m) return m[1].trim();
|
|
123
|
+
}
|
|
124
|
+
} catch {}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function officeaceCapabilitiesDir() {
|
|
129
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
130
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return configRoot;
|
|
131
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
132
|
+
if (regDir) {
|
|
133
|
+
const dir = join(regDir, '.office-claw');
|
|
134
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
135
|
+
}
|
|
136
|
+
if (process.platform === 'win32') {
|
|
137
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
138
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
139
|
+
for (const base of bases) {
|
|
140
|
+
if (!base) continue;
|
|
141
|
+
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
142
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function officeaceCapabilitiesDirSafe() {
|
|
149
|
+
return officeaceCapabilitiesDir() || join(baseHome(), '.office-claw');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function officeaceSqlitePath() {
|
|
153
|
+
const capDir = officeaceCapabilitiesDirSafe();
|
|
154
|
+
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function officeaceRegistered() {
|
|
158
|
+
let hasMcp = false;
|
|
159
|
+
const dbPath = officeaceSqlitePath();
|
|
160
|
+
if (existsSync(dbPath)) {
|
|
161
|
+
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
162
|
+
if (nodeMajor >= 22) {
|
|
163
|
+
try {
|
|
164
|
+
const { DatabaseSync } = createRequire(import.meta.url)('node:sqlite');
|
|
165
|
+
const db = new DatabaseSync(dbPath, { readonly: true });
|
|
166
|
+
const row = db.prepare("SELECT enabled FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").get();
|
|
167
|
+
db.close();
|
|
168
|
+
hasMcp = Boolean(row?.enabled);
|
|
169
|
+
} catch {}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const capFile = join(officeaceCapabilitiesDirSafe(), 'capabilities.json');
|
|
174
|
+
const cfg = readJsonSafe(capFile);
|
|
175
|
+
const hasSkills = cfg?.capabilities?.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill') ?? false;
|
|
176
|
+
|
|
177
|
+
return hasMcp || hasSkills;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function atomcodeHome() {
|
|
181
|
+
return process.env.ATOMCODE_HOME || join(baseHome(), '.atomcode');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function atomcodeRegistered() {
|
|
185
|
+
const cfg = readJsonSafe(join(atomcodeHome(), 'mcp.json'));
|
|
186
|
+
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function openclawMcpConfigured(cfg) {
|
|
190
|
+
if (!cfg) return false;
|
|
191
|
+
if (cfg.mcpServers?.['huaweicloud-devkit']) return true;
|
|
192
|
+
if (cfg.mcp?.servers?.['huaweicloud-devkit']) return true;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function openclawRegistered() {
|
|
197
|
+
const pluginCfg = readJsonSafe(join(baseHome(), '.agents', 'huaweicloud-plugins', '.mcp.json'));
|
|
198
|
+
if (openclawMcpConfigured(pluginCfg)) return true;
|
|
199
|
+
const nativeCfg = readJsonSafe(join(baseHome(), '.openclaw', 'openclaw.json'));
|
|
200
|
+
return openclawMcpConfigured(nativeCfg);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function hermesHome() {
|
|
204
|
+
if (process.env.HERMES_HOME) return process.env.HERMES_HOME;
|
|
205
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
206
|
+
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
|
|
207
|
+
return join(process.env.LOCALAPPDATA, 'hermes');
|
|
208
|
+
}
|
|
209
|
+
return join(baseHome(), '.hermes');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function hermesRegistered() {
|
|
213
|
+
const configPath = join(hermesHome(), 'config.yaml');
|
|
214
|
+
if (!existsSync(configPath)) return false;
|
|
215
|
+
try {
|
|
216
|
+
const content = readFileSync(configPath, 'utf8');
|
|
217
|
+
return content.includes('mcp_servers:') && content.includes('huaweicloud-devkit');
|
|
86
218
|
} catch {
|
|
87
219
|
return false;
|
|
88
220
|
}
|
|
@@ -97,8 +229,13 @@ export function getAgentRegistrationStatuses(target = 'all') {
|
|
|
97
229
|
if (agent === 'codex-desktop') configured = codexDesktopRegistered();
|
|
98
230
|
if (agent === 'codex') configured = codexCliRegistered();
|
|
99
231
|
if (agent === 'codearts') configured = codeartsRegistered();
|
|
232
|
+
if (agent === 'codearts-work') configured = codeartsWorkRegistered();
|
|
100
233
|
if (agent === 'workbuddy') configured = workbuddyRegistered();
|
|
101
234
|
if (agent === 'dsh') configured = dshRegistered();
|
|
235
|
+
if (agent === 'officeace') configured = officeaceRegistered();
|
|
236
|
+
if (agent === 'hermes') configured = hermesRegistered();
|
|
237
|
+
if (agent === 'openclaw') configured = openclawRegistered();
|
|
238
|
+
if (agent === 'atomcode') configured = atomcodeRegistered();
|
|
102
239
|
result.agents[agent] = { configured };
|
|
103
240
|
}
|
|
104
241
|
return result;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
copyFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
readlinkSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from 'node:fs';
|
|
2
11
|
import { dirname, join } from 'node:path';
|
|
3
12
|
import { homedir } from 'node:os';
|
|
4
13
|
|
|
@@ -7,14 +16,20 @@ import { homedir } from 'node:os';
|
|
|
7
16
|
// Native Windows has no POSIX modes (statSync always reports 0666), so skip the check there.
|
|
8
17
|
function ensurePrivateMode(path) {
|
|
9
18
|
if (process.platform === 'win32') return;
|
|
10
|
-
try {
|
|
19
|
+
try {
|
|
20
|
+
chmodSync(path, 0o600);
|
|
21
|
+
} catch {}
|
|
11
22
|
try {
|
|
12
23
|
const mode = statSync(path).mode & 0o777;
|
|
13
24
|
if (mode !== 0o600) {
|
|
14
|
-
console.warn(
|
|
25
|
+
console.warn(
|
|
26
|
+
`\x1b[33m[WARN]\x1b[0m Could not set 0600 on ${path} (current mode ${mode.toString(8)}). Credentials may be readable by other users.`,
|
|
27
|
+
);
|
|
15
28
|
console.warn(`\x1b[33m If running under WSL, move the credential home to the Linux filesystem:\x1b[0m`);
|
|
16
29
|
console.warn(`\x1b[33m export HUAWEICLOUD_HOME=$HOME (then re-run auth init)\x1b[0m`);
|
|
17
|
-
console.warn(
|
|
30
|
+
console.warn(
|
|
31
|
+
`\x1b[33m Or skip file storage entirely with HW_ACCESS_KEY/HW_SECRET_KEY environment variables.\x1b[0m`,
|
|
32
|
+
);
|
|
18
33
|
}
|
|
19
34
|
} catch {}
|
|
20
35
|
}
|
|
@@ -49,12 +64,21 @@ export function writeGlobalCredentials(credentials = {}) {
|
|
|
49
64
|
sk: String(credentials.sk || ''),
|
|
50
65
|
securityToken: String(credentials.securityToken || ''),
|
|
51
66
|
region: String(credentials.region || ''),
|
|
67
|
+
...(credentials.configuredBySession === undefined
|
|
68
|
+
? {}
|
|
69
|
+
: { configuredBySession: Boolean(credentials.configuredBySession) }),
|
|
52
70
|
};
|
|
53
71
|
writeFileSync(path, JSON.stringify(payload, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
54
72
|
ensurePrivateMode(path);
|
|
55
73
|
return path;
|
|
56
74
|
}
|
|
57
75
|
|
|
76
|
+
export function setConfiguredBySession(flag) {
|
|
77
|
+
const stored = readGlobalCredentials();
|
|
78
|
+
const next = { ...(stored || {}), configuredBySession: Boolean(flag) };
|
|
79
|
+
writeGlobalCredentials(next);
|
|
80
|
+
}
|
|
81
|
+
|
|
58
82
|
export function writeObsConfig(credentials = {}) {
|
|
59
83
|
const region = String(credentials.region || '');
|
|
60
84
|
const ak = String(credentials.ak || '');
|
|
@@ -78,6 +102,13 @@ export function resolveCredentials(options = {}) {
|
|
|
78
102
|
let securityToken = process.env.HW_SECURITY_TOKEN;
|
|
79
103
|
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
80
104
|
|
|
105
|
+
const codeartsCreds = isCodeArtsContext() ? readCodeArtsCredentials() : null;
|
|
106
|
+
if (codeartsCreds) {
|
|
107
|
+
if (!ak && codeartsCreds.ak) ak = codeartsCreds.ak;
|
|
108
|
+
if (!sk && codeartsCreds.sk) sk = codeartsCreds.sk;
|
|
109
|
+
if (!region && codeartsCreds.region) region = codeartsCreds.region;
|
|
110
|
+
}
|
|
111
|
+
|
|
81
112
|
const stored = readGlobalCredentials();
|
|
82
113
|
if (stored) {
|
|
83
114
|
if (!ak && stored.ak) ak = stored.ak;
|
|
@@ -86,10 +117,191 @@ export function resolveCredentials(options = {}) {
|
|
|
86
117
|
if (!region && stored.region) region = stored.region;
|
|
87
118
|
}
|
|
88
119
|
|
|
120
|
+
// R9: S1 written by `auth_switch persist` (configuredBySession) is the session's
|
|
121
|
+
// source of truth and wins over env-injected defaults. Plain S1 (`auth init`)
|
|
122
|
+
// still yields to env so devspace-style env injection keeps working.
|
|
123
|
+
if (stored && stored.configuredBySession === true && stored.ak && stored.sk) {
|
|
124
|
+
ak = stored.ak;
|
|
125
|
+
sk = stored.sk;
|
|
126
|
+
if (!securityToken) securityToken = stored.securityToken || '';
|
|
127
|
+
if (!region) region = stored.region || '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Sandbox/platform-injected temporary STS credentials (env vars carrying a
|
|
131
|
+
// security token) must not shadow the user's explicit permanent credentials
|
|
132
|
+
// from `auth init`. Prefer the stored file when both exist.
|
|
133
|
+
if (
|
|
134
|
+
process.env.HW_ACCESS_KEY &&
|
|
135
|
+
process.env.HW_SECRET_KEY &&
|
|
136
|
+
process.env.HW_SECURITY_TOKEN &&
|
|
137
|
+
stored &&
|
|
138
|
+
stored.ak &&
|
|
139
|
+
stored.sk
|
|
140
|
+
) {
|
|
141
|
+
ak = stored.ak;
|
|
142
|
+
sk = stored.sk;
|
|
143
|
+
securityToken = stored.securityToken || '';
|
|
144
|
+
region = stored.region || region;
|
|
145
|
+
}
|
|
146
|
+
|
|
89
147
|
if (!ak || !sk) {
|
|
90
148
|
if (options.allowMissing) return null;
|
|
91
|
-
throw new Error(
|
|
149
|
+
throw new Error(
|
|
150
|
+
'Huawei Cloud credentials are not configured. Run "npx huaweicloud-devkit auth init" or set HW_ACCESS_KEY/HW_SECRET_KEY.',
|
|
151
|
+
);
|
|
92
152
|
}
|
|
93
153
|
|
|
94
154
|
return { ak, sk, securityToken, region };
|
|
95
155
|
}
|
|
156
|
+
|
|
157
|
+
let _parentCwd = undefined;
|
|
158
|
+
|
|
159
|
+
export function getParentCwd() {
|
|
160
|
+
if (_parentCwd !== undefined) return _parentCwd;
|
|
161
|
+
try {
|
|
162
|
+
_parentCwd = readlinkSync(`/proc/${process.ppid}/cwd`);
|
|
163
|
+
return _parentCwd;
|
|
164
|
+
} catch {
|
|
165
|
+
_parentCwd = null;
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isCodeArtsContext() {
|
|
171
|
+
return (
|
|
172
|
+
existsSync(join(process.cwd(), '.codeartsdoer')) ||
|
|
173
|
+
existsSync(join(homedir(), '.codeartsdoer')) ||
|
|
174
|
+
existsSync(join(homedir(), '.codeartswork'))
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function readCodeArtsCredentials() {
|
|
179
|
+
const parentCwd = getParentCwd();
|
|
180
|
+
const searchDirs = [process.env.CODEARTS_PROJECT_DIR, parentCwd, process.cwd(), homedir()];
|
|
181
|
+
|
|
182
|
+
for (const dir of searchDirs) {
|
|
183
|
+
if (!dir) continue;
|
|
184
|
+
const path = join(dir, '.codeartsdoer', 'mcp', 'mcp_settings.json');
|
|
185
|
+
try {
|
|
186
|
+
if (!existsSync(path)) continue;
|
|
187
|
+
const config = JSON.parse(readFileSync(path, 'utf8'));
|
|
188
|
+
const server = config?.mcpServers?.['huaweicloud-devkit'];
|
|
189
|
+
if (!server?.env) continue;
|
|
190
|
+
|
|
191
|
+
const ak = server.env.HW_ACCESS_KEY;
|
|
192
|
+
const sk = server.env.HW_SECRET_KEY;
|
|
193
|
+
if (ak && sk) {
|
|
194
|
+
return {
|
|
195
|
+
ak,
|
|
196
|
+
sk,
|
|
197
|
+
securityToken: server.env.HW_SECURITY_TOKEN || '',
|
|
198
|
+
region: server.env.HW_REGION || server.env.HUAWEICLOUD_REGION || '',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
// mcp_settings.json missing or invalid — skip
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// CodeArts Work — user-level only
|
|
207
|
+
{
|
|
208
|
+
const path = join(homedir(), '.codeartswork', 'mcp', 'mcp_settings.json');
|
|
209
|
+
try {
|
|
210
|
+
if (existsSync(path)) {
|
|
211
|
+
const config = JSON.parse(readFileSync(path, 'utf8'));
|
|
212
|
+
const server = config?.mcp?.['huaweicloud-devkit'];
|
|
213
|
+
if (server?.environment) {
|
|
214
|
+
const ak = server.environment.HW_ACCESS_KEY;
|
|
215
|
+
const sk = server.environment.HW_SECRET_KEY;
|
|
216
|
+
if (ak && sk) {
|
|
217
|
+
return {
|
|
218
|
+
ak,
|
|
219
|
+
sk,
|
|
220
|
+
securityToken: server.environment.HW_SECURITY_TOKEN || '',
|
|
221
|
+
region: server.environment.HW_REGION || server.environment.HUAWEICLOUD_REGION || '',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} catch {
|
|
227
|
+
// mcp_settings.json missing or invalid — skip
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let runtimeCredentials = null;
|
|
235
|
+
|
|
236
|
+
export function setRuntimeCredentials(ak, sk, securityToken, region) {
|
|
237
|
+
runtimeCredentials = { ak, sk, securityToken: securityToken || '', region: region || '' };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function clearRuntimeCredentials() {
|
|
241
|
+
runtimeCredentials = null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function hasRuntimeCredentials() {
|
|
245
|
+
return runtimeCredentials !== null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function resolveCredentialsWithRuntime(options = {}) {
|
|
249
|
+
if (runtimeCredentials) {
|
|
250
|
+
return {
|
|
251
|
+
ak: runtimeCredentials.ak,
|
|
252
|
+
sk: runtimeCredentials.sk,
|
|
253
|
+
securityToken: runtimeCredentials.securityToken,
|
|
254
|
+
region: runtimeCredentials.region,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return resolveCredentials(options);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function lastSyncPath() {
|
|
262
|
+
return join(baseHome(), '.config', 'huaweicloud', '.last_sync');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function readLastSync() {
|
|
266
|
+
const path = lastSyncPath();
|
|
267
|
+
if (!existsSync(path)) return null;
|
|
268
|
+
try {
|
|
269
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
270
|
+
} catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function writeLastSync() {
|
|
276
|
+
const path = lastSyncPath();
|
|
277
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
278
|
+
const payload = { ts: Date.now() };
|
|
279
|
+
writeFileSync(path, JSON.stringify(payload), { encoding: 'utf8', mode: 0o600 });
|
|
280
|
+
ensurePrivateMode(path);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function backupGlobalCredentials() {
|
|
284
|
+
const path = globalCredentialsPath();
|
|
285
|
+
if (!existsSync(path)) return null;
|
|
286
|
+
const bakPath = `${path}.bak`;
|
|
287
|
+
try {
|
|
288
|
+
copyFileSync(path, bakPath);
|
|
289
|
+
ensurePrivateMode(bakPath);
|
|
290
|
+
return bakPath;
|
|
291
|
+
} catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function restoreGlobalCredentialsBackup() {
|
|
297
|
+
const path = globalCredentialsPath();
|
|
298
|
+
const bakPath = `${path}.bak`;
|
|
299
|
+
if (!existsSync(bakPath)) return false;
|
|
300
|
+
try {
|
|
301
|
+
copyFileSync(bakPath, path);
|
|
302
|
+
ensurePrivateMode(path);
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|