huaweicloud-devkit 1.0.2-next.19 → 1.0.2-next.20
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 +2 -1
- package/README.zh-CN.md +2 -1
- 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 +30 -0
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -2
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +11 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +26 -24
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +12 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +56 -8
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +1 -1
- package/plugins/huaweicloud-core/src/setup-cli.mjs +417 -33
- package/plugins/huaweicloud-core/src/tools.mjs +45 -11
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +9 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Supports OpenCode, CodeArts Agent, WorkBuddy, DeepSeek Harness (DSH), and Office
|
|
|
12
12
|
|
|
13
13
|
## Prerequisites
|
|
14
14
|
|
|
15
|
-
- Node.js >=
|
|
15
|
+
- Node.js >= 22
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
@@ -31,6 +31,7 @@ npx --yes huaweicloud-devkit doctor --target opencode
|
|
|
31
31
|
npx --yes huaweicloud-devkit status --target opencode
|
|
32
32
|
npx --yes huaweicloud-devkit update --target opencode
|
|
33
33
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
34
|
+
rm -rf ~/.npm/_npx/ # Linux/macOS: clear npx cache to remove old version residue
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
### CodeArts Agent
|
package/README.zh-CN.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## 前置条件
|
|
14
14
|
|
|
15
|
-
- Node.js >=
|
|
15
|
+
- Node.js >= 22
|
|
16
16
|
|
|
17
17
|
## 快速开始
|
|
18
18
|
|
|
@@ -31,6 +31,7 @@ npx --yes huaweicloud-devkit doctor --target opencode
|
|
|
31
31
|
npx --yes huaweicloud-devkit status --target opencode
|
|
32
32
|
npx --yes huaweicloud-devkit update --target opencode
|
|
33
33
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
34
|
+
rm -rf ~/.npm/_npx/ # Linux/macOS: 清理 npx 缓存避免旧版残留
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
### CodeArts Agent(码道)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.20",
|
|
4
4
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
],
|
|
59
59
|
"license": "Apache-2.0",
|
|
60
60
|
"engines": {
|
|
61
|
-
"node": ">=
|
|
61
|
+
"node": ">=22"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -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.0.2-next.
|
|
20
|
+
"version": "1.0.2-next.20",
|
|
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-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.20",
|
|
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",
|
|
@@ -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.0.2-next.
|
|
20
|
+
"version": "1.0.2-next.20",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "HuaweiCloud Mate",
|
|
23
23
|
"url": "https://github.com/huaweicloud"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "huaweicloud-core",
|
|
3
|
+
"version": "1.0.2-next.20",
|
|
4
|
+
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "HuaweiCloud Mate",
|
|
7
|
+
"url": "https://github.com/huaweicloud"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
10
|
+
"repository": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills"],
|
|
13
|
+
"skills": "./skills/",
|
|
14
|
+
"mcpServers": "./.mcp.json",
|
|
15
|
+
"interface": {
|
|
16
|
+
"displayName": "HuaweiCloud Devkit",
|
|
17
|
+
"shortDescription": "HuaweiCloud Devkit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
18
|
+
"longDescription": "HuaweiCloud Devkit helps coding agents choose and use Huawei Cloud Skills, KooCLI, APIs, SDKs, and MCP tools with less context, safer command execution, and more accurate cloud implementation decisions.",
|
|
19
|
+
"developerName": "HuaweiCloud Mate",
|
|
20
|
+
"category": "Cloud",
|
|
21
|
+
"capabilities": ["Read", "Interactive"],
|
|
22
|
+
"websiteURL": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
23
|
+
"brandColor": "#C7000B",
|
|
24
|
+
"defaultPrompt": [
|
|
25
|
+
"Help me choose the right Huawei Cloud capability.",
|
|
26
|
+
"Check my Huawei Cloud CLI setup safely.",
|
|
27
|
+
"Plan this Huawei Cloud API or SDK task."
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.20",
|
|
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",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills"],
|
|
13
13
|
"skills": "./skills/",
|
|
14
14
|
"mcpServers": "./.mcp.json",
|
|
15
|
-
"hooks": "./hooks/",
|
|
16
15
|
"interface": {
|
|
17
16
|
"displayName": "HuaweiCloud Devkit",
|
|
18
17
|
"shortDescription": "HuaweiCloud Devkit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
@@ -37,7 +37,7 @@ This skill enables users to:
|
|
|
37
37
|
|
|
38
38
|
## Prerequisites
|
|
39
39
|
|
|
40
|
-
- **Node.js >=
|
|
40
|
+
- **Node.js >= 22** must be installed
|
|
41
41
|
- **Network access** to `gitcode.com` (API v5 for index) and `github.com` / `raw.githubusercontent.com` (for skill details)
|
|
42
42
|
|
|
43
43
|
### Step 0: Check Python Environment
|
|
@@ -22,6 +22,17 @@ Use this skill to turn vague developer intent into a precise Huawei Cloud capabi
|
|
|
22
22
|
9. When no built-in devkit skill matches, browse the community skill marketplace at https://github.com/huaweicloud/huaweicloud-skills. Fetch the index from https://raw.githubusercontent.com/huaweicloud/huaweicloud-skills/master/skills-index/index.json.
|
|
23
23
|
10. When the deliverable is a PPT, architecture diagram (draw.io), or frontend page that needs official Huawei Cloud service logos, use the `huaweicloud_get_service_icon` MCP tool to get logo URLs from the official Icons library instead of guessing or hotlinking unofficial images.
|
|
24
24
|
|
|
25
|
+
## Scenario Routing
|
|
26
|
+
|
|
27
|
+
Match the scenario before picking a service. For "deploy a web app" requests, layer the recommendation instead of defaulting to a production service:
|
|
28
|
+
|
|
29
|
+
| Scenario | Recommended path |
|
|
30
|
+
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
31
|
+
| hello world / prototype / demo / temporary preview (free, quick try) | `huawei-sandbox` first — temporary runtime + public URL, ~8h validity, zero billed resources |
|
|
32
|
+
| production / long-term / custom domain / high availability | `huawei-functiongraph` / `huawei-ecs` (or `huawei-cce` for containers) |
|
|
33
|
+
|
|
34
|
+
Route to the sandbox when the developer signals free/quick preview intent ("免费", "快速", "预览", "hello world", "原型", "演示"); route to production services only when production-grade hosting is explicitly required.
|
|
35
|
+
|
|
25
36
|
## Official Service Logos
|
|
26
37
|
|
|
27
38
|
- Use `huaweicloud_get_service_icon` with the service name, alias, or Chinese name (e.g. `ecs`, `obs`, `modelarts`, `对象存储`). It returns the official CDN logo URL (`logo.source_url`), category, and product page link from https://open.huaweicloud.com/openplatform/icons.html.
|
|
@@ -46,30 +46,32 @@ Do not rely on training data for facts. Huawei Cloud services, pricing, quotas,
|
|
|
46
46
|
|
|
47
47
|
## Service Map
|
|
48
48
|
|
|
49
|
-
| Workload
|
|
50
|
-
|
|
|
51
|
-
| Web application hosting
|
|
52
|
-
| Containerized microservices
|
|
53
|
-
| Static file storage / CDN (long-term)
|
|
54
|
-
| Relational database (MySQL/PG)
|
|
55
|
-
| Distributed SQL database
|
|
56
|
-
| Document database (MongoDB API)
|
|
57
|
-
| In-memory cache
|
|
58
|
-
| Serverless functions
|
|
59
|
-
| API management
|
|
60
|
-
| AI model training/inference
|
|
61
|
-
| Message queuing
|
|
62
|
-
| Push notifications
|
|
63
|
-
| Secret management
|
|
64
|
-
| Key management
|
|
65
|
-
| DDoS protection
|
|
66
|
-
| Web firewall
|
|
67
|
-
| Monitoring dashboards
|
|
68
|
-
| Audit trails
|
|
69
|
-
| Backup / disaster recovery
|
|
70
|
-
| CI/CD pipeline
|
|
71
|
-
| Temporary runtime / web app preview
|
|
72
|
-
| Getting started
|
|
49
|
+
| Workload | Primary Service | Skill |
|
|
50
|
+
| ------------------------------------------------ | -------------------- | ------------------------ |
|
|
51
|
+
| Web application hosting (production / long-term) | ECS | huawei-ecs |
|
|
52
|
+
| Containerized microservices | CCE | huawei-cce |
|
|
53
|
+
| Static file storage / CDN (long-term) | OBS | huawei-obs |
|
|
54
|
+
| Relational database (MySQL/PG) | RDS | huawei-rds |
|
|
55
|
+
| Distributed SQL database | GaussDB | huawei-gaussdb |
|
|
56
|
+
| Document database (MongoDB API) | DDS | huawei-dds-dcs |
|
|
57
|
+
| In-memory cache | DCS (Redis) | huawei-dds-dcs |
|
|
58
|
+
| Serverless functions | FunctionGraph | huawei-functiongraph |
|
|
59
|
+
| API management | APIG | huawei-apig |
|
|
60
|
+
| AI model training/inference | ModelArts | huawei-modelarts |
|
|
61
|
+
| Message queuing | DMS (Kafka/RabbitMQ) | huawei-smn-dms |
|
|
62
|
+
| Push notifications | SMN | huawei-smn-dms |
|
|
63
|
+
| Secret management | DEW (CSMS) | huawei-dew |
|
|
64
|
+
| Key management | DEW (KMS) | huawei-dew |
|
|
65
|
+
| DDoS protection | AAD | huawei-waf-aad |
|
|
66
|
+
| Web firewall | WAF | huawei-waf-aad |
|
|
67
|
+
| Monitoring dashboards | Cloud Eye (CES) | huawei-cloud-eye |
|
|
68
|
+
| Audit trails | CTS | huawei-cts |
|
|
69
|
+
| Backup / disaster recovery | CBR | huawei-cbr |
|
|
70
|
+
| CI/CD pipeline | CloudDeploy | huawei-deployment |
|
|
71
|
+
| Temporary runtime / web app preview | Sandbox (DevStation) | huawei-sandbox |
|
|
72
|
+
| Getting started | Account setup | huaweicloud-cli-and-auth |
|
|
73
|
+
|
|
74
|
+
**Web app scenario layering**: for "deploy a web app", prefer `huawei-sandbox` for free/quick try (hello world, prototype, demo, temporary preview — temporary runtime + public URL, ~8h validity, zero billed resources); route to `huawei-functiongraph` / `huawei-ecs` (or `huawei-cce`) for production / long-term / custom-domain / high-availability hosting.
|
|
73
75
|
|
|
74
76
|
## Deployment Target Options
|
|
75
77
|
|
|
@@ -28,3 +28,15 @@ Route based on answers:
|
|
|
28
28
|
- SQL + massive scale / distributed -> huawei-gaussdb
|
|
29
29
|
- Document model (MongoDB-compatible) -> huawei-dds-dcs
|
|
30
30
|
- Cache / key-value -> huawei-dds-dcs (DCS)
|
|
31
|
+
|
|
32
|
+
## Example: I need to deploy a web app
|
|
33
|
+
|
|
34
|
+
Ask:
|
|
35
|
+
|
|
36
|
+
- Is this a quick try / prototype / demo / temporary preview (free)?
|
|
37
|
+
- Or production (long-term, custom domain, high availability)?
|
|
38
|
+
|
|
39
|
+
Route by scenario:
|
|
40
|
+
|
|
41
|
+
- Free / quick try / prototype / demo / temporary preview -> huawei-sandbox (temporary runtime + public URL, ~8h validity, zero billed resources)
|
|
42
|
+
- Production / long-term / custom domain / high availability -> huawei-functiongraph / huawei-ecs (or huawei-cce for containers)
|
|
@@ -12,6 +12,8 @@ export const SUPPORTED_AGENT_TARGETS = [
|
|
|
12
12
|
'workbuddy',
|
|
13
13
|
'dsh',
|
|
14
14
|
'officeace',
|
|
15
|
+
'hermes',
|
|
16
|
+
'openclaw',
|
|
15
17
|
];
|
|
16
18
|
|
|
17
19
|
function baseHome() {
|
|
@@ -99,23 +101,48 @@ function dshRegistered() {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
function readOfficeaceRegistryInstallDir() {
|
|
105
|
+
if (process.platform !== 'win32') return null;
|
|
106
|
+
try {
|
|
107
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
windowsHide: true,
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
});
|
|
112
|
+
if (r.status === 0) {
|
|
113
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
114
|
+
if (m) return m[1].trim();
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
function officeaceCapabilitiesDir() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
121
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
122
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return configRoot;
|
|
123
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
124
|
+
if (regDir) {
|
|
125
|
+
const dir = join(regDir, '.office-claw');
|
|
126
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
127
|
+
}
|
|
107
128
|
if (process.platform === 'win32') {
|
|
108
|
-
|
|
129
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
130
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
131
|
+
for (const base of bases) {
|
|
109
132
|
if (!base) continue;
|
|
110
133
|
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
111
134
|
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
112
135
|
}
|
|
113
136
|
}
|
|
114
|
-
return
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function officeaceCapabilitiesDirSafe() {
|
|
141
|
+
return officeaceCapabilitiesDir() || join(baseHome(), '.office-claw');
|
|
115
142
|
}
|
|
116
143
|
|
|
117
144
|
function officeaceSqlitePath() {
|
|
118
|
-
const capDir =
|
|
145
|
+
const capDir = officeaceCapabilitiesDirSafe();
|
|
119
146
|
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
120
147
|
}
|
|
121
148
|
|
|
@@ -135,13 +162,33 @@ function officeaceRegistered() {
|
|
|
135
162
|
}
|
|
136
163
|
}
|
|
137
164
|
|
|
138
|
-
const capFile = join(
|
|
165
|
+
const capFile = join(officeaceCapabilitiesDirSafe(), 'capabilities.json');
|
|
139
166
|
const cfg = readJsonSafe(capFile);
|
|
140
167
|
const hasSkills = cfg?.capabilities?.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill') ?? false;
|
|
141
168
|
|
|
142
169
|
return hasMcp || hasSkills;
|
|
143
170
|
}
|
|
144
171
|
|
|
172
|
+
function hermesHome() {
|
|
173
|
+
if (process.env.HERMES_HOME) return process.env.HERMES_HOME;
|
|
174
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
175
|
+
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
|
|
176
|
+
return join(process.env.LOCALAPPDATA, 'hermes');
|
|
177
|
+
}
|
|
178
|
+
return join(baseHome(), '.hermes');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function hermesRegistered() {
|
|
182
|
+
const configPath = join(hermesHome(), 'config.yaml');
|
|
183
|
+
if (!existsSync(configPath)) return false;
|
|
184
|
+
try {
|
|
185
|
+
const content = readFileSync(configPath, 'utf8');
|
|
186
|
+
return content.includes('mcp_servers:') && content.includes('huaweicloud-devkit');
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
145
192
|
export function getAgentRegistrationStatuses(target = 'all') {
|
|
146
193
|
const requested = target === 'all' ? SUPPORTED_AGENT_TARGETS : [target];
|
|
147
194
|
const result = { target, agents: {} };
|
|
@@ -154,6 +201,7 @@ export function getAgentRegistrationStatuses(target = 'all') {
|
|
|
154
201
|
if (agent === 'workbuddy') configured = workbuddyRegistered();
|
|
155
202
|
if (agent === 'dsh') configured = dshRegistered();
|
|
156
203
|
if (agent === 'officeace') configured = officeaceRegistered();
|
|
204
|
+
if (agent === 'hermes') configured = hermesRegistered();
|
|
157
205
|
result.agents[agent] = { configured };
|
|
158
206
|
}
|
|
159
207
|
return result;
|
|
@@ -128,32 +128,57 @@ function dshPluginsDir() {
|
|
|
128
128
|
return join(dshRoot(), 'huaweicloud-plugins');
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
function readOfficeaceRegistryInstallDir() {
|
|
132
|
+
if (platform() !== 'win32') return null;
|
|
133
|
+
try {
|
|
134
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
windowsHide: true,
|
|
137
|
+
timeout: 5000,
|
|
138
|
+
});
|
|
139
|
+
if (r.status === 0) {
|
|
140
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
141
|
+
if (m) return m[1].trim();
|
|
142
|
+
}
|
|
143
|
+
} catch {}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
131
147
|
function officeaceCapabilitiesDir() {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
148
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
149
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return configRoot;
|
|
150
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
151
|
+
if (regDir) {
|
|
152
|
+
const dir = join(regDir, '.office-claw');
|
|
153
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
154
|
+
}
|
|
136
155
|
if (platform() === 'win32') {
|
|
137
|
-
|
|
156
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
157
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
158
|
+
for (const base of bases) {
|
|
138
159
|
if (!base) continue;
|
|
139
160
|
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
140
161
|
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
141
162
|
}
|
|
142
163
|
}
|
|
143
|
-
return
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function officeaceCapabilitiesDirSafe() {
|
|
168
|
+
return officeaceCapabilitiesDir() || join(homedir(), '.office-claw');
|
|
144
169
|
}
|
|
145
170
|
function officeaceCapabilitiesFile() {
|
|
146
|
-
return join(
|
|
171
|
+
return join(officeaceCapabilitiesDirSafe(), 'capabilities.json');
|
|
147
172
|
}
|
|
148
173
|
function officeaceSkillsDir() {
|
|
149
|
-
return join(
|
|
174
|
+
return join(officeaceCapabilitiesDirSafe(), 'skills');
|
|
150
175
|
}
|
|
151
176
|
function officeacePluginsDir() {
|
|
152
|
-
return join(
|
|
177
|
+
return join(officeaceCapabilitiesDirSafe(), 'huaweicloud-plugins');
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
function officeaceSqlitePath() {
|
|
156
|
-
const capDir =
|
|
181
|
+
const capDir = officeaceCapabilitiesDirSafe();
|
|
157
182
|
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
158
183
|
}
|
|
159
184
|
|
|
@@ -269,7 +294,7 @@ function readCapabilitiesJson() {
|
|
|
269
294
|
}
|
|
270
295
|
|
|
271
296
|
function writeCapabilitiesJson(config) {
|
|
272
|
-
mkdirSync(
|
|
297
|
+
mkdirSync(officeaceCapabilitiesDirSafe(), { recursive: true });
|
|
273
298
|
writeFileSync(officeaceCapabilitiesFile(), JSON.stringify(config, null, 2));
|
|
274
299
|
}
|
|
275
300
|
|
|
@@ -383,8 +408,8 @@ function printSandboxWarning(reason) {
|
|
|
383
408
|
|
|
384
409
|
function checkNode() {
|
|
385
410
|
const v = process.versions.node.split('.').map(Number);
|
|
386
|
-
if (v[0] <
|
|
387
|
-
console.error(`\x1b[31mNode.js >=
|
|
411
|
+
if (v[0] < 22) {
|
|
412
|
+
console.error(`\x1b[31mNode.js >= 22 required (current: ${process.version})\x1b[0m`);
|
|
388
413
|
process.exit(1);
|
|
389
414
|
}
|
|
390
415
|
console.log(` Node.js ${process.version} \x1b[32mOK\x1b[0m`);
|
|
@@ -406,7 +431,7 @@ function copyDir(src, dest) {
|
|
|
406
431
|
|
|
407
432
|
function installRuntimeDeps(pluginsDir) {
|
|
408
433
|
const pkgJson = {
|
|
409
|
-
name: 'huaweicloud-
|
|
434
|
+
name: 'huaweicloud-devkit',
|
|
410
435
|
version: pkgVersion,
|
|
411
436
|
type: 'module',
|
|
412
437
|
dependencies: { undici: '^8.10.0' },
|
|
@@ -465,7 +490,8 @@ function updateOpenCodeConfig(pluginDir) {
|
|
|
465
490
|
existing.type === 'local' &&
|
|
466
491
|
Array.isArray(existing.command) &&
|
|
467
492
|
existing.command[0] === 'node' &&
|
|
468
|
-
existing.command[1] === mcpPath
|
|
493
|
+
existing.command[1] === mcpPath &&
|
|
494
|
+
existing.timeout === 300000
|
|
469
495
|
) {
|
|
470
496
|
console.log(` OpenCode MCP config unchanged: ${configPath}`);
|
|
471
497
|
return;
|
|
@@ -476,6 +502,7 @@ function updateOpenCodeConfig(pluginDir) {
|
|
|
476
502
|
type: 'local',
|
|
477
503
|
command: ['node', mcpPath],
|
|
478
504
|
enabled: true,
|
|
505
|
+
timeout: 300000,
|
|
479
506
|
};
|
|
480
507
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
481
508
|
console.log(` OpenCode MCP config updated: ${configPath}`);
|
|
@@ -891,7 +918,13 @@ function registerCodeartsMcp(configPath) {
|
|
|
891
918
|
return;
|
|
892
919
|
}
|
|
893
920
|
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
894
|
-
if (
|
|
921
|
+
if (
|
|
922
|
+
existing &&
|
|
923
|
+
existing.command === 'node' &&
|
|
924
|
+
Array.isArray(existing.args) &&
|
|
925
|
+
existing.args[0] === mcpPath &&
|
|
926
|
+
existing.timeout === 300000
|
|
927
|
+
) {
|
|
895
928
|
console.log(` MCP config unchanged: ${configPath}`);
|
|
896
929
|
return;
|
|
897
930
|
}
|
|
@@ -902,6 +935,7 @@ function registerCodeartsMcp(configPath) {
|
|
|
902
935
|
args: [mcpPath],
|
|
903
936
|
env,
|
|
904
937
|
enabled: true,
|
|
938
|
+
timeout: 300000,
|
|
905
939
|
};
|
|
906
940
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
907
941
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -1030,7 +1064,13 @@ function ensureWorkbuddyMcpConfig() {
|
|
|
1030
1064
|
return false;
|
|
1031
1065
|
}
|
|
1032
1066
|
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
1033
|
-
if (
|
|
1067
|
+
if (
|
|
1068
|
+
existing &&
|
|
1069
|
+
existing.command === 'node' &&
|
|
1070
|
+
Array.isArray(existing.args) &&
|
|
1071
|
+
existing.args[0] === mcpPath &&
|
|
1072
|
+
existing.timeout === 300000
|
|
1073
|
+
) {
|
|
1034
1074
|
console.log(` MCP config unchanged: ${configPath}`);
|
|
1035
1075
|
return false;
|
|
1036
1076
|
}
|
|
@@ -1040,6 +1080,7 @@ function ensureWorkbuddyMcpConfig() {
|
|
|
1040
1080
|
command: 'node',
|
|
1041
1081
|
args: [mcpPath],
|
|
1042
1082
|
env,
|
|
1083
|
+
timeout: 300000,
|
|
1043
1084
|
};
|
|
1044
1085
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
1045
1086
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -1418,7 +1459,39 @@ function dshStatus() {
|
|
|
1418
1459
|
);
|
|
1419
1460
|
}
|
|
1420
1461
|
|
|
1462
|
+
async function promptOfficeaceInstallDir() {
|
|
1463
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1464
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
1465
|
+
while (true) {
|
|
1466
|
+
const path = (await ask(' Install directory: ')).trim();
|
|
1467
|
+
if (!path) {
|
|
1468
|
+
console.log(' \x1b[33mPath cannot be empty.\x1b[0m');
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
const capFile = join(path, '.office-claw', 'capabilities.json');
|
|
1472
|
+
if (existsSync(capFile)) {
|
|
1473
|
+
rl.close();
|
|
1474
|
+
return path;
|
|
1475
|
+
}
|
|
1476
|
+
console.log(` \x1b[33mcapabilities.json not found at: ${capFile}\x1b[0m`);
|
|
1477
|
+
console.log(' Please verify the path and try again.');
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1421
1481
|
async function installOfficeAce() {
|
|
1482
|
+
if (!officeaceCapabilitiesDir()) {
|
|
1483
|
+
if (process.stdin.isTTY) {
|
|
1484
|
+
console.log(' \x1b[33mOfficeAce install directory not found automatically.\x1b[0m');
|
|
1485
|
+
console.log(' Please enter the OfficeAce install directory.');
|
|
1486
|
+
const entered = await promptOfficeaceInstallDir();
|
|
1487
|
+
process.env.OFFICE_CLAW_CONFIG_ROOT = join(entered, '.office-claw');
|
|
1488
|
+
} else {
|
|
1489
|
+
console.log(
|
|
1490
|
+
' \x1b[31mOfficeAce install directory not found. Please set OFFICE_CLAW_CONFIG_ROOT env var and retry.\x1b[0m',
|
|
1491
|
+
);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1422
1495
|
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1423
1496
|
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1424
1497
|
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
@@ -1521,6 +1594,200 @@ function officeaceStatus() {
|
|
|
1521
1594
|
}
|
|
1522
1595
|
}
|
|
1523
1596
|
|
|
1597
|
+
// ── Hermes Agent ──
|
|
1598
|
+
|
|
1599
|
+
function hermesHomeDir() {
|
|
1600
|
+
if (process.env.HERMES_HOME) return process.env.HERMES_HOME;
|
|
1601
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
1602
|
+
if (platform() === 'win32' && process.env.LOCALAPPDATA) {
|
|
1603
|
+
return join(process.env.LOCALAPPDATA, 'hermes');
|
|
1604
|
+
}
|
|
1605
|
+
return join(homedir(), '.hermes');
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
function hermesSkillsDir() {
|
|
1609
|
+
return join(hermesHomeDir(), 'skills');
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function hermesPluginsDir() {
|
|
1613
|
+
return join(hermesHomeDir(), 'huaweicloud-plugins');
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
function hermesConfigFile() {
|
|
1617
|
+
return join(hermesHomeDir(), 'config.yaml');
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
1621
|
+
function ensureHermesMcpConfig() {
|
|
1622
|
+
const configPath = hermesConfigFile();
|
|
1623
|
+
const mcpPath = join(hermesPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
1624
|
+
const hcloudBin = findHcloudBin();
|
|
1625
|
+
|
|
1626
|
+
const blockLines = [
|
|
1627
|
+
'mcp_servers:',
|
|
1628
|
+
' huaweicloud-devkit:',
|
|
1629
|
+
' command: "node"',
|
|
1630
|
+
` args: ["${mcpPath}"]`,
|
|
1631
|
+
' env:',
|
|
1632
|
+
' HUAWEICLOUD_AGENT_TOOLKIT_MODE: "local"',
|
|
1633
|
+
];
|
|
1634
|
+
if (hcloudBin) {
|
|
1635
|
+
blockLines.push(` HCLOUD_BIN: "${hcloudBin.replace(/\\/g, '/')}"`);
|
|
1636
|
+
}
|
|
1637
|
+
const block = blockLines.join('\n');
|
|
1638
|
+
|
|
1639
|
+
let existing = '';
|
|
1640
|
+
if (existsSync(configPath)) {
|
|
1641
|
+
try {
|
|
1642
|
+
existing = readFileSync(configPath, 'utf8');
|
|
1643
|
+
} catch {}
|
|
1644
|
+
if (existing.includes('mcp_servers:') && existing.includes('huaweicloud-devkit')) {
|
|
1645
|
+
if (existing.includes(`args: ["${mcpPath}"]`)) {
|
|
1646
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
removeHermesMcpConfigBlock();
|
|
1650
|
+
existing = '';
|
|
1651
|
+
if (existsSync(configPath)) {
|
|
1652
|
+
try {
|
|
1653
|
+
existing = readFileSync(configPath, 'utf8');
|
|
1654
|
+
} catch {}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
1659
|
+
const newContent = existing ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`;
|
|
1660
|
+
writeFileSync(configPath, newContent);
|
|
1661
|
+
console.log(` MCP config updated: ${configPath}`);
|
|
1662
|
+
return true;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
function removeHermesMcpConfigBlock() {
|
|
1666
|
+
const configPath = hermesConfigFile();
|
|
1667
|
+
if (!existsSync(configPath)) return;
|
|
1668
|
+
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
1669
|
+
const out = [];
|
|
1670
|
+
let skip = false;
|
|
1671
|
+
for (const line of lines) {
|
|
1672
|
+
if (/^\s*huaweicloud-devkit\s*:/.test(line)) {
|
|
1673
|
+
skip = true;
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
if (skip) {
|
|
1677
|
+
// Continue skipping indented lines (the server config block)
|
|
1678
|
+
if (/^\s{2,}/.test(line) && line.trim() !== '') continue;
|
|
1679
|
+
// Stop skipping at a top-level key or empty line
|
|
1680
|
+
skip = false;
|
|
1681
|
+
}
|
|
1682
|
+
// If we just skipped the only server under mcp_servers, remove the key too
|
|
1683
|
+
if (line.trim() === 'mcp_servers:') {
|
|
1684
|
+
// Peek ahead to check if this mcp_servers block only has huaweicloud-devkit
|
|
1685
|
+
const remaining = out.join('\n');
|
|
1686
|
+
// Remove trailing mcp_servers: if it's the last meaningful line
|
|
1687
|
+
continue; // skip the mcp_servers key for now, re-add if other servers exist
|
|
1688
|
+
}
|
|
1689
|
+
out.push(line);
|
|
1690
|
+
}
|
|
1691
|
+
// Reconstruct: add mcp_servers back if there are other servers
|
|
1692
|
+
const cleaned = out.join('\n').trimEnd();
|
|
1693
|
+
writeFileSync(configPath, cleaned ? `${cleaned}\n` : '');
|
|
1694
|
+
console.log(' MCP config cleaned');
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
async function installHermes() {
|
|
1698
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1699
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1700
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1701
|
+
const pluginDest = hermesPluginsDir();
|
|
1702
|
+
|
|
1703
|
+
copyDir(skillsSrc, hermesSkillsDir());
|
|
1704
|
+
console.log(` Skills -> ${hermesSkillsDir()}`);
|
|
1705
|
+
|
|
1706
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1707
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
1708
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1709
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
1710
|
+
|
|
1711
|
+
ensureHermesMcpConfig();
|
|
1712
|
+
installRuntimeDeps(pluginDest);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
async function updateHermes() {
|
|
1716
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1717
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1718
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1719
|
+
const pluginDest = hermesPluginsDir();
|
|
1720
|
+
|
|
1721
|
+
copyDir(skillsSrc, hermesSkillsDir());
|
|
1722
|
+
const stale = pruneStale(hermesSkillsDir(), skillsSrc);
|
|
1723
|
+
console.log(` Skills updated -> ${hermesSkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
1724
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1725
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
1726
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1727
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
1728
|
+
ensureHermesMcpConfig();
|
|
1729
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
1730
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1731
|
+
installRuntimeDeps(pluginDest);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
function uninstallHermes() {
|
|
1735
|
+
const skillsDir = hermesSkillsDir();
|
|
1736
|
+
let removed = 0;
|
|
1737
|
+
if (existsSync(skillsDir)) {
|
|
1738
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
1739
|
+
if (entry.name.startsWith('huawei')) {
|
|
1740
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
1741
|
+
removed++;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
1745
|
+
try {
|
|
1746
|
+
if (readdirSync(skillsDir).length === 0) {
|
|
1747
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
1748
|
+
console.log(` Removed empty skills directory: ${skillsDir}`);
|
|
1749
|
+
}
|
|
1750
|
+
} catch {}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
if (removeIfExists(hermesPluginsDir())) {
|
|
1754
|
+
console.log(' Removed MCP server and safety policy');
|
|
1755
|
+
}
|
|
1756
|
+
removeHermesMcpConfigBlock();
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
function hermesStatus() {
|
|
1760
|
+
const pluginDir = hermesPluginsDir();
|
|
1761
|
+
const skillsDir = hermesSkillsDir();
|
|
1762
|
+
console.log(
|
|
1763
|
+
` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1764
|
+
);
|
|
1765
|
+
console.log(
|
|
1766
|
+
` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1767
|
+
);
|
|
1768
|
+
let skillCount = 0;
|
|
1769
|
+
if (existsSync(skillsDir)) {
|
|
1770
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1771
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
1772
|
+
).length;
|
|
1773
|
+
}
|
|
1774
|
+
console.log(
|
|
1775
|
+
` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1776
|
+
);
|
|
1777
|
+
const configPath = hermesConfigFile();
|
|
1778
|
+
if (existsSync(configPath)) {
|
|
1779
|
+
try {
|
|
1780
|
+
const config = readFileSync(configPath, 'utf8');
|
|
1781
|
+
const configured = config.includes('mcp_servers:') && config.includes('huaweicloud-devkit');
|
|
1782
|
+
console.log(` MCP config: ${configured ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
|
|
1783
|
+
} catch {
|
|
1784
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
1785
|
+
}
|
|
1786
|
+
} else {
|
|
1787
|
+
console.log(` MCP config: \x1b[31mNot found\x1b[0m`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1524
1791
|
function opencodeStatus() {
|
|
1525
1792
|
const pluginDir = opencodePluginsDir();
|
|
1526
1793
|
const skillsDir = opencodeSkillsDir();
|
|
@@ -1566,6 +1833,8 @@ function autoDetectTarget() {
|
|
|
1566
1833
|
},
|
|
1567
1834
|
],
|
|
1568
1835
|
['officeace', () => existsSync(officeaceCapabilitiesDir())],
|
|
1836
|
+
['hermes', () => existsSync(hermesHomeDir())],
|
|
1837
|
+
['openclaw', () => existsSync(join(homedir(), '.openclaw'))],
|
|
1569
1838
|
];
|
|
1570
1839
|
const detected = checks.filter(([, check]) => check()).map(([name]) => name);
|
|
1571
1840
|
if (detected.length === 0) {
|
|
@@ -1618,6 +1887,14 @@ async function cmdInstall() {
|
|
|
1618
1887
|
console.log('\n[OfficeAce]');
|
|
1619
1888
|
await installOfficeAce();
|
|
1620
1889
|
}
|
|
1890
|
+
if (target === 'hermes' || target === 'all') {
|
|
1891
|
+
console.log('\n[Hermes Agent]');
|
|
1892
|
+
await installHermes();
|
|
1893
|
+
}
|
|
1894
|
+
if (target === 'openclaw' || target === 'all') {
|
|
1895
|
+
console.log('\n[OpenClaw]');
|
|
1896
|
+
await installCodexDesktop();
|
|
1897
|
+
}
|
|
1621
1898
|
if (target === 'codex' || target === 'all') {
|
|
1622
1899
|
console.log('\n[Codex]');
|
|
1623
1900
|
if (!hasCodexCLI()) {
|
|
@@ -1655,13 +1932,24 @@ async function cmdInstall() {
|
|
|
1655
1932
|
? 'DSH'
|
|
1656
1933
|
: target === 'officeace'
|
|
1657
1934
|
? 'OfficeAce'
|
|
1658
|
-
: '
|
|
1935
|
+
: target === 'hermes'
|
|
1936
|
+
? 'Hermes Agent'
|
|
1937
|
+
: target === 'openclaw'
|
|
1938
|
+
? 'OpenClaw'
|
|
1939
|
+
: '当前 agent';
|
|
1659
1940
|
const pad = ' '.repeat(24 - appName.length);
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1941
|
+
if (target === 'officeace') {
|
|
1942
|
+
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
1943
|
+
console.log(`\x1b[1m\x1b[33m║ 打开连接器 → 我的连接器 → huaweicloud-devkit ║`);
|
|
1944
|
+
console.log(`\x1b[1m\x1b[33m║ → 连接 → 回到对话 → 输入框开启连接器 ║`);
|
|
1945
|
+
console.log(`\x1b[1m\x1b[33m╚══════════════════════════════════════════════════════╝\x1b[0m`);
|
|
1946
|
+
} else {
|
|
1947
|
+
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
1948
|
+
console.log(`\x1b[1m\x1b[33m║ MCP 工具在重启 ${appName} 会话后才生效${pad}║`);
|
|
1949
|
+
console.log(`\x1b[1m\x1b[33m║ 关闭当前会话 → 重新打开,直接描述华为云任务即可 ║`);
|
|
1950
|
+
console.log(`\x1b[1m\x1b[33m║ 重启前请勿执行 hcloud 命令,避免 AK/SK 泄露 ║`);
|
|
1951
|
+
console.log(`\x1b[1m\x1b[33m╚══════════════════════════════════════════════════════╝\x1b[0m`);
|
|
1952
|
+
}
|
|
1665
1953
|
|
|
1666
1954
|
const hcloudOk = checkHcloud();
|
|
1667
1955
|
if (!hcloudOk) {
|
|
@@ -1674,7 +1962,11 @@ async function cmdInstall() {
|
|
|
1674
1962
|
console.log(`\n\x1b[1m下一步:\x1b[0m`);
|
|
1675
1963
|
console.log(` 1. 配置统一凭据:npx huaweicloud-devkit auth init`);
|
|
1676
1964
|
console.log(` 2. 配置代理(企业内网):npx huaweicloud-devkit proxy init`);
|
|
1677
|
-
|
|
1965
|
+
if (target === 'officeace') {
|
|
1966
|
+
console.log(` 3. 打开连接器 → 我的连接器 → huaweicloud-devkit → 连接 → 回到对话 → 输入框开启连接器`);
|
|
1967
|
+
} else {
|
|
1968
|
+
console.log(` 3. 重启 ${appName} 会话(MCP 工具重启后生效)`);
|
|
1969
|
+
}
|
|
1678
1970
|
console.log(` 4. 运行自检:npx huaweicloud-devkit doctor`);
|
|
1679
1971
|
|
|
1680
1972
|
// Write install marker for doctor to detect
|
|
@@ -1687,9 +1979,11 @@ async function cmdInstall() {
|
|
|
1687
1979
|
? workbuddyPluginsDir()
|
|
1688
1980
|
: target === 'officeace'
|
|
1689
1981
|
? officeacePluginsDir()
|
|
1690
|
-
: target === '
|
|
1982
|
+
: target === 'openclaw'
|
|
1691
1983
|
? codexDesktopPluginsDir()
|
|
1692
|
-
:
|
|
1984
|
+
: target === 'codex-desktop'
|
|
1985
|
+
? codexDesktopPluginsDir()
|
|
1986
|
+
: opencodePluginsDir();
|
|
1693
1987
|
mkdirSync(markerDir, { recursive: true });
|
|
1694
1988
|
writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
|
|
1695
1989
|
if (target === 'opencode' || target === 'all') {
|
|
@@ -1710,6 +2004,9 @@ async function cmdInstall() {
|
|
|
1710
2004
|
if (target === 'officeace' || target === 'all') {
|
|
1711
2005
|
console.log('Or describe your Huawei Cloud task in OfficeAce');
|
|
1712
2006
|
}
|
|
2007
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2008
|
+
console.log('Or describe your Huawei Cloud task in OpenClaw');
|
|
2009
|
+
}
|
|
1713
2010
|
}
|
|
1714
2011
|
|
|
1715
2012
|
async function cmdUninstall() {
|
|
@@ -1737,6 +2034,14 @@ async function cmdUninstall() {
|
|
|
1737
2034
|
console.log('\n[OfficeAce]');
|
|
1738
2035
|
uninstallOfficeAce();
|
|
1739
2036
|
}
|
|
2037
|
+
if (target === 'hermes' || target === 'all') {
|
|
2038
|
+
console.log('\n[Hermes Agent]');
|
|
2039
|
+
uninstallHermes();
|
|
2040
|
+
}
|
|
2041
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2042
|
+
console.log('\n[OpenClaw]');
|
|
2043
|
+
uninstallCodexDesktop();
|
|
2044
|
+
}
|
|
1740
2045
|
if (target === 'codex-desktop' || target === 'codex' || target === 'all') {
|
|
1741
2046
|
console.log('\n[Codex]');
|
|
1742
2047
|
uninstallCodexDesktop();
|
|
@@ -1789,6 +2094,30 @@ async function cmdStatus() {
|
|
|
1789
2094
|
console.log('\n[OfficeAce]');
|
|
1790
2095
|
officeaceStatus();
|
|
1791
2096
|
}
|
|
2097
|
+
if (target === 'hermes' || target === 'all') {
|
|
2098
|
+
console.log('\n[Hermes Agent]');
|
|
2099
|
+
hermesStatus();
|
|
2100
|
+
}
|
|
2101
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2102
|
+
console.log('\n[OpenClaw]');
|
|
2103
|
+
const cdPluginDir = codexDesktopPluginsDir();
|
|
2104
|
+
const cdSkillsDir = codexDesktopSkillsDir();
|
|
2105
|
+
console.log(
|
|
2106
|
+
` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2107
|
+
);
|
|
2108
|
+
console.log(
|
|
2109
|
+
` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2110
|
+
);
|
|
2111
|
+
let cdSkillCount = 0;
|
|
2112
|
+
if (existsSync(cdSkillsDir)) {
|
|
2113
|
+
cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
|
|
2114
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
2115
|
+
).length;
|
|
2116
|
+
}
|
|
2117
|
+
console.log(
|
|
2118
|
+
` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
1792
2121
|
if (target === 'codex' || target === 'all') {
|
|
1793
2122
|
console.log('\n[Codex]');
|
|
1794
2123
|
if (!hasCodexCLI()) {
|
|
@@ -1821,7 +2150,7 @@ async function cmdDoctor() {
|
|
|
1821
2150
|
}
|
|
1822
2151
|
|
|
1823
2152
|
// Node.js
|
|
1824
|
-
check('Node.js >=
|
|
2153
|
+
check('Node.js >= 22', process.versions.node.split('.')[0] >= 22, 'Run: nvm install 22 && nvm use 22');
|
|
1825
2154
|
|
|
1826
2155
|
// MCP server — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH paths
|
|
1827
2156
|
const opencodePluginDir = opencodePluginsDir();
|
|
@@ -1830,13 +2159,15 @@ async function cmdDoctor() {
|
|
|
1830
2159
|
const workbuddyPluginDir = workbuddyPluginsDir();
|
|
1831
2160
|
const dshPluginDir = dshPluginsDir();
|
|
1832
2161
|
const officeacePluginDir = officeacePluginsDir();
|
|
2162
|
+
const hermesPluginDir = hermesPluginsDir();
|
|
1833
2163
|
const mcpOk =
|
|
1834
2164
|
existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1835
2165
|
existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1836
2166
|
existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1837
2167
|
existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1838
2168
|
existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1839
|
-
existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs'))
|
|
2169
|
+
existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2170
|
+
existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'));
|
|
1840
2171
|
const mcpTarget = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs'))
|
|
1841
2172
|
? 'OpenCode'
|
|
1842
2173
|
: existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
|
|
@@ -1849,7 +2180,9 @@ async function cmdDoctor() {
|
|
|
1849
2180
|
? 'DSH'
|
|
1850
2181
|
: existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs'))
|
|
1851
2182
|
? 'OfficeAce'
|
|
1852
|
-
: ''
|
|
2183
|
+
: existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'))
|
|
2184
|
+
? 'Hermes Agent'
|
|
2185
|
+
: '';
|
|
1853
2186
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
1854
2187
|
|
|
1855
2188
|
if (mcpOk) {
|
|
@@ -1862,7 +2195,8 @@ async function cmdDoctor() {
|
|
|
1862
2195
|
existsSync(join(codeartsPluginDir, 'safety', 'policy.json')) ||
|
|
1863
2196
|
existsSync(join(workbuddyPluginDir, 'safety', 'policy.json')) ||
|
|
1864
2197
|
existsSync(join(dshPluginDir, 'safety', 'policy.json')) ||
|
|
1865
|
-
existsSync(join(officeacePluginDir, 'safety', 'policy.json'))
|
|
2198
|
+
existsSync(join(officeacePluginDir, 'safety', 'policy.json')) ||
|
|
2199
|
+
existsSync(join(hermesPluginDir, 'safety', 'policy.json'));
|
|
1866
2200
|
check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
|
|
1867
2201
|
|
|
1868
2202
|
// MCP config — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH
|
|
@@ -1927,6 +2261,18 @@ async function cmdDoctor() {
|
|
|
1927
2261
|
} catch {}
|
|
1928
2262
|
}
|
|
1929
2263
|
}
|
|
2264
|
+
if (!mcpConfigured) {
|
|
2265
|
+
const hermesCfg = hermesConfigFile();
|
|
2266
|
+
if (existsSync(hermesCfg)) {
|
|
2267
|
+
try {
|
|
2268
|
+
const cfg = readFileSync(hermesCfg, 'utf8');
|
|
2269
|
+
if (cfg.includes('mcp_servers:') && cfg.includes('huaweicloud-devkit')) {
|
|
2270
|
+
mcpConfigured = true;
|
|
2271
|
+
mcpCfgTarget = 'Hermes Agent';
|
|
2272
|
+
}
|
|
2273
|
+
} catch {}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
1930
2276
|
check(
|
|
1931
2277
|
'MCP configured',
|
|
1932
2278
|
mcpConfigured,
|
|
@@ -1977,6 +2323,7 @@ async function cmdDoctor() {
|
|
|
1977
2323
|
workbuddySkillsDir(),
|
|
1978
2324
|
dshSkillsDir(),
|
|
1979
2325
|
officeaceSkillsDir(),
|
|
2326
|
+
hermesSkillsDir(),
|
|
1980
2327
|
];
|
|
1981
2328
|
let skillCount = 0;
|
|
1982
2329
|
const missingSkills = [];
|
|
@@ -2134,7 +2481,31 @@ async function cmdUpdate() {
|
|
|
2134
2481
|
console.log('[OfficeAce]');
|
|
2135
2482
|
await updateOfficeAce();
|
|
2136
2483
|
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2137
|
-
console.log(`\x1b[
|
|
2484
|
+
console.log(`\x1b[33m打开连接器 → 我的连接器 → huaweicloud-devkit → 连接 → 回到对话 → 输入框开启连接器\x1b[0m`);
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
if (target === 'hermes') {
|
|
2489
|
+
if (!existsSync(join(hermesPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2490
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
console.log('[Hermes Agent]');
|
|
2494
|
+
await updateHermes();
|
|
2495
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2496
|
+
console.log(`\x1b[33mRestart Hermes Agent for changes to take effect.\x1b[0m`);
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
if (target === 'openclaw') {
|
|
2501
|
+
if (!existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2502
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
console.log('[OpenClaw]');
|
|
2506
|
+
await updateCodexDesktop();
|
|
2507
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2508
|
+
console.log(`\x1b[33mRestart OpenClaw for changes to take effect.\x1b[0m`);
|
|
2138
2509
|
return;
|
|
2139
2510
|
}
|
|
2140
2511
|
|
|
@@ -2170,6 +2541,16 @@ async function cmdUpdate() {
|
|
|
2170
2541
|
await updateOfficeAce();
|
|
2171
2542
|
updatedAny = true;
|
|
2172
2543
|
}
|
|
2544
|
+
if (existsSync(join(hermesPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2545
|
+
console.log('\n[Hermes Agent]');
|
|
2546
|
+
await updateHermes();
|
|
2547
|
+
updatedAny = true;
|
|
2548
|
+
}
|
|
2549
|
+
if (existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2550
|
+
console.log('\n[OpenClaw]');
|
|
2551
|
+
await updateCodexDesktop();
|
|
2552
|
+
updatedAny = true;
|
|
2553
|
+
}
|
|
2173
2554
|
if (codexStatus()) {
|
|
2174
2555
|
console.log('\n[Codex]');
|
|
2175
2556
|
installCodex();
|
|
@@ -2665,7 +3046,7 @@ async function main() {
|
|
|
2665
3046
|
default:
|
|
2666
3047
|
console.log(BANNER);
|
|
2667
3048
|
console.log(
|
|
2668
|
-
'Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|dsh|officeace|all>]\n',
|
|
3049
|
+
'Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|dsh|officeace|hermes|all>]\n',
|
|
2669
3050
|
);
|
|
2670
3051
|
console.log('Commands:');
|
|
2671
3052
|
console.log(' install Install skills, MCP server, safety policy');
|
|
@@ -2679,7 +3060,9 @@ async function main() {
|
|
|
2679
3060
|
console.log(' proxy Manage proxy config: init | show | clear');
|
|
2680
3061
|
console.log(' help Show this help');
|
|
2681
3062
|
console.log('\nOptions:');
|
|
2682
|
-
console.log(
|
|
3063
|
+
console.log(
|
|
3064
|
+
' --target Target agent: opencode (default), codex, codearts, workbuddy, dsh, officeace, hermes, all',
|
|
3065
|
+
);
|
|
2683
3066
|
console.log('\nExamples:');
|
|
2684
3067
|
console.log(' npx huaweicloud-devkit install');
|
|
2685
3068
|
console.log(' npx huaweicloud-devkit install --target codex');
|
|
@@ -2687,6 +3070,7 @@ async function main() {
|
|
|
2687
3070
|
console.log(' npx huaweicloud-devkit install --target workbuddy');
|
|
2688
3071
|
console.log(' npx huaweicloud-devkit install --target dsh');
|
|
2689
3072
|
console.log(' npx huaweicloud-devkit install --target officeace');
|
|
3073
|
+
console.log(' npx huaweicloud-devkit install --target hermes');
|
|
2690
3074
|
console.log(' npx huaweicloud-devkit install --target all');
|
|
2691
3075
|
console.log(' npx huaweicloud-devkit auth init');
|
|
2692
3076
|
console.log(' npx huaweicloud-devkit auth sync --target all');
|
|
@@ -5,6 +5,7 @@ import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs';
|
|
|
5
5
|
import { join, dirname } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
8
9
|
import { searchMarketplace } from './search-market.mjs';
|
|
9
10
|
import { getServiceIcon } from './icon-library.mjs';
|
|
10
11
|
import {
|
|
@@ -42,20 +43,52 @@ function dshSkillsDir() {
|
|
|
42
43
|
const home = process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
43
44
|
return join(home, 'skills');
|
|
44
45
|
}
|
|
46
|
+
function readOfficeaceRegistryInstallDir() {
|
|
47
|
+
if (process.platform !== 'win32') return null;
|
|
48
|
+
try {
|
|
49
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
50
|
+
encoding: 'utf8',
|
|
51
|
+
windowsHide: true,
|
|
52
|
+
timeout: 5000,
|
|
53
|
+
});
|
|
54
|
+
if (r.status === 0) {
|
|
55
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
56
|
+
if (m) return m[1].trim();
|
|
57
|
+
}
|
|
58
|
+
} catch {}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
function officeaceSkillsRoot() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
63
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
64
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return join(configRoot, 'skills');
|
|
65
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
66
|
+
if (regDir) {
|
|
67
|
+
const dir = join(regDir, '.office-claw', 'skills');
|
|
68
|
+
if (existsSync(dir)) return dir;
|
|
69
|
+
}
|
|
50
70
|
if (process.platform === 'win32') {
|
|
51
|
-
|
|
71
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
72
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
73
|
+
for (const base of bases) {
|
|
52
74
|
if (!base) continue;
|
|
53
|
-
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
54
|
-
if (existsSync(
|
|
75
|
+
const dir = join(base, 'OfficeAce', '.office-claw', 'skills');
|
|
76
|
+
if (existsSync(dir)) return dir;
|
|
55
77
|
}
|
|
56
78
|
}
|
|
57
|
-
return
|
|
79
|
+
return null;
|
|
58
80
|
}
|
|
81
|
+
|
|
82
|
+
function hermesSkillsDir() {
|
|
83
|
+
if (process.env.HERMES_HOME) return join(process.env.HERMES_HOME, 'skills');
|
|
84
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
85
|
+
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
|
|
86
|
+
return join(process.env.LOCALAPPDATA, 'hermes', 'skills');
|
|
87
|
+
}
|
|
88
|
+
const home = homedir();
|
|
89
|
+
return join(home, '.hermes', 'skills');
|
|
90
|
+
}
|
|
91
|
+
|
|
59
92
|
export function listSkillDirs(root) {
|
|
60
93
|
if (!existsSync(root)) return [];
|
|
61
94
|
try {
|
|
@@ -83,6 +116,7 @@ function resolveSkillsRoot() {
|
|
|
83
116
|
opencodeSkillsDir(),
|
|
84
117
|
workbuddySkillsDir(),
|
|
85
118
|
officeaceSkillsRoot(),
|
|
119
|
+
hermesSkillsDir(),
|
|
86
120
|
]) || SKILLS_ROOT_DEV
|
|
87
121
|
);
|
|
88
122
|
}
|
|
@@ -390,7 +424,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
390
424
|
target: {
|
|
391
425
|
type: 'string',
|
|
392
426
|
description:
|
|
393
|
-
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, or all (default).',
|
|
427
|
+
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
394
428
|
},
|
|
395
429
|
},
|
|
396
430
|
},
|
|
@@ -405,7 +439,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
405
439
|
target: {
|
|
406
440
|
type: 'string',
|
|
407
441
|
description:
|
|
408
|
-
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, or all (default).',
|
|
442
|
+
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
409
443
|
},
|
|
410
444
|
},
|
|
411
445
|
},
|
|
@@ -506,7 +540,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
506
540
|
type: 'boolean',
|
|
507
541
|
description: 'Extract tar.gz on sandbox after upload (default: true)',
|
|
508
542
|
},
|
|
509
|
-
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default:
|
|
543
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 300000)' },
|
|
510
544
|
},
|
|
511
545
|
},
|
|
512
546
|
},
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file contains code derived from hwlink.
|
|
3
|
+
*
|
|
4
|
+
* Source:
|
|
5
|
+
* https://gitcode.com/huawei-developers/hwlink
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the ISC License.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
const DEFAULT_HIGH_WATERMARK = 8 * 1024 * 1024;
|
|
2
11
|
const DEFAULT_LOW_WATERMARK = 2 * 1024 * 1024;
|
|
3
12
|
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file contains code derived from hwlink.
|
|
3
|
+
*
|
|
4
|
+
* Source:
|
|
5
|
+
* https://gitcode.com/huawei-developers/hwlink
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the ISC License.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { Buffer } from 'node:buffer';
|
|
2
11
|
|
|
3
12
|
import { FairQueue } from './hwlink-fair-queue.js';
|