huaweicloud-devkit 1.0.2-dev.9 → 1.1.2-next.0
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 -4
- package/README.zh-CN.md +27 -4
- package/bin/setup.cjs +0 -0
- package/package.json +3 -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/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +6 -0
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +13 -8
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +18 -3
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +19 -1
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +155 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +87 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +7 -3
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +12 -3
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +7 -0
- package/plugins/huaweicloud-core/src/search-market.mjs +7 -2
- package/plugins/huaweicloud-core/src/setup-cli.mjs +433 -10
- package/plugins/huaweicloud-core/src/tools.mjs +21 -6
|
@@ -8,6 +8,7 @@ import { createInterface } from 'node:readline';
|
|
|
8
8
|
import { spawnSync } from 'node:child_process';
|
|
9
9
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
10
10
|
import { globalCredentialsPath, readGlobalCredentials, writeGlobalCredentials, writeObsConfig } from './auth/credentials.mjs';
|
|
11
|
+
import { proxyConfigPath, readProxyConfig, writeProxyConfig, clearProxyConfig, getProxySettings } from './proxy/proxy-config.mjs';
|
|
11
12
|
|
|
12
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const PLUGIN_ROOT = resolve(__dirname, '..');
|
|
@@ -56,6 +57,15 @@ function workbuddySkillsDir() { return join(homedir(), '.workbuddy', 'skills');
|
|
|
56
57
|
function workbuddyMcpConfigFile() { return join(homedir(), '.workbuddy', 'mcp.json'); }
|
|
57
58
|
function workbuddyPluginsDir() { return join(homedir(), '.workbuddy', 'huaweicloud-plugins'); }
|
|
58
59
|
|
|
60
|
+
function dshRoot() { return process.env.DSH_HOME || join(homedir(), '.dsh'); }
|
|
61
|
+
function dshSkillsDir() { return join(dshRoot(), 'skills'); }
|
|
62
|
+
function dshProfileDir() { return join(dshRoot(), 'profiles', 'web'); }
|
|
63
|
+
function dshPatchFile() { return join(dshProfileDir(), 'cordis.patch.yml'); }
|
|
64
|
+
function dshPluginsDir() { return join(dshRoot(), 'huaweicloud-plugins'); }
|
|
65
|
+
|
|
66
|
+
const DSH_MCP_PATCH_START = '# HuaweiCloud DevKit DSH integration start';
|
|
67
|
+
const DSH_MCP_PATCH_END = '# HuaweiCloud DevKit DSH integration end';
|
|
68
|
+
|
|
59
69
|
// Detect CodeArts sandbox mode (bash_mode in permission config).
|
|
60
70
|
function detectCodeartsSandbox() {
|
|
61
71
|
try {
|
|
@@ -758,6 +768,261 @@ function workbuddyStatus() {
|
|
|
758
768
|
}
|
|
759
769
|
}
|
|
760
770
|
|
|
771
|
+
function dshMcpServerPath() {
|
|
772
|
+
return join(dshPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function dshPatchBlock() {
|
|
776
|
+
const hcloudBin = findHcloudBin();
|
|
777
|
+
const envLines = [
|
|
778
|
+
' HUAWEICLOUD_AGENT_TOOLKIT_MODE: local',
|
|
779
|
+
" HDKITSERVICE_ENDPOINT: ''",
|
|
780
|
+
];
|
|
781
|
+
if (hcloudBin) {
|
|
782
|
+
envLines.push(` HCLOUD_BIN: '${hcloudBin.replace(/\\/g, '/').replace(/'/g, "''")}'`);
|
|
783
|
+
}
|
|
784
|
+
return [
|
|
785
|
+
DSH_MCP_PATCH_START,
|
|
786
|
+
'- insert:',
|
|
787
|
+
' - id: mcp-huaweicloud',
|
|
788
|
+
" name: '@deepseek-ai/dsh-mcp-client'",
|
|
789
|
+
' config:',
|
|
790
|
+
' serverName: huaweicloud',
|
|
791
|
+
' transport: stdio',
|
|
792
|
+
' command: node',
|
|
793
|
+
' args:',
|
|
794
|
+
` - '${dshMcpServerPath().replace(/'/g, "''")}'`,
|
|
795
|
+
' env:',
|
|
796
|
+
...envLines,
|
|
797
|
+
' failOnStartupError: false',
|
|
798
|
+
DSH_MCP_PATCH_END,
|
|
799
|
+
].join('\n');
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function escapeRegExp(value) {
|
|
803
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function removeManagedDshPatchBlock(content) {
|
|
807
|
+
const pattern = new RegExp(`\\n?${escapeRegExp(DSH_MCP_PATCH_START)}[\\s\\S]*?${escapeRegExp(DSH_MCP_PATCH_END)}\\s*`, 'g');
|
|
808
|
+
return String(content || '').replace(pattern, '\n').replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function dshPatchHasOnlyCommentsOrEmptyList(content) {
|
|
812
|
+
const meaningful = String(content || '')
|
|
813
|
+
.split(/\r?\n/)
|
|
814
|
+
.map((line) => line.trim())
|
|
815
|
+
.filter((line) => line && !line.startsWith('#'));
|
|
816
|
+
return meaningful.length === 0 || (meaningful.length === 1 && meaningful[0] === '[]');
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function ensureDshMcpPatch() {
|
|
820
|
+
const patchFile = dshPatchFile();
|
|
821
|
+
const existing = existsSync(patchFile) ? readFileSync(patchFile, 'utf8') : '';
|
|
822
|
+
const cleaned = removeManagedDshPatchBlock(existing);
|
|
823
|
+
const block = dshPatchBlock();
|
|
824
|
+
let next;
|
|
825
|
+
if (dshPatchHasOnlyCommentsOrEmptyList(cleaned)) {
|
|
826
|
+
const prefix = cleaned
|
|
827
|
+
.split(/\r?\n/)
|
|
828
|
+
.filter((line) => line.trim() !== '[]')
|
|
829
|
+
.join('\n')
|
|
830
|
+
.trimEnd();
|
|
831
|
+
next = `${prefix ? `${prefix}\n` : ''}${block}\n`;
|
|
832
|
+
} else {
|
|
833
|
+
next = `${cleaned}\n\n${block}\n`;
|
|
834
|
+
}
|
|
835
|
+
if (existing.replace(/\r\n/g, '\n') === next) {
|
|
836
|
+
console.log(` DSH patch unchanged: ${patchFile}`);
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
mkdirSync(dirname(patchFile), { recursive: true });
|
|
840
|
+
writeFileSync(patchFile, next);
|
|
841
|
+
console.log(` DSH patch updated: ${patchFile}`);
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function removeDshMcpPatch() {
|
|
846
|
+
const patchFile = dshPatchFile();
|
|
847
|
+
if (!existsSync(patchFile)) return false;
|
|
848
|
+
const existing = readFileSync(patchFile, 'utf8');
|
|
849
|
+
const cleaned = removeManagedDshPatchBlock(existing);
|
|
850
|
+
if (cleaned === existing.trimEnd()) return false;
|
|
851
|
+
const prefix = cleaned
|
|
852
|
+
.split(/\r?\n/)
|
|
853
|
+
.filter((line) => line.trim() !== '[]')
|
|
854
|
+
.join('\n')
|
|
855
|
+
.trimEnd();
|
|
856
|
+
const next = dshPatchHasOnlyCommentsOrEmptyList(cleaned)
|
|
857
|
+
? `${prefix ? `${prefix}\n` : ''}[]\n`
|
|
858
|
+
: `${cleaned}\n`;
|
|
859
|
+
writeFileSync(patchFile, next);
|
|
860
|
+
console.log(` DSH patch cleaned: ${patchFile}`);
|
|
861
|
+
return true;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function dshPatchConfigured() {
|
|
865
|
+
const patchFile = dshPatchFile();
|
|
866
|
+
if (!existsSync(patchFile)) return false;
|
|
867
|
+
try {
|
|
868
|
+
const patch = readFileSync(patchFile, 'utf8');
|
|
869
|
+
return patch.includes('id: mcp-huaweicloud')
|
|
870
|
+
&& patch.includes("@deepseek-ai/dsh-mcp-client")
|
|
871
|
+
&& patch.includes('serverName: huaweicloud');
|
|
872
|
+
} catch {
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function commandAvailable(command, args = ['--version']) {
|
|
878
|
+
try {
|
|
879
|
+
const r = spawnSync(command, args, { shell: false, windowsHide: true, stdio: 'pipe', timeout: 10000 });
|
|
880
|
+
if (r.status === 0) return true;
|
|
881
|
+
} catch {}
|
|
882
|
+
if (process.platform === 'win32') {
|
|
883
|
+
try {
|
|
884
|
+
const w = spawnSync('where.exe', [command], { windowsHide: true, stdio: 'pipe', timeout: 10000 });
|
|
885
|
+
return w.status === 0 && w.stdout.toString().trim().length > 0;
|
|
886
|
+
} catch {}
|
|
887
|
+
}
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function dshMcpClientAvailable() {
|
|
892
|
+
const modulePath = join('node_modules', '@deepseek-ai', 'dsh-mcp-client', 'package.json');
|
|
893
|
+
const candidates = [
|
|
894
|
+
join(dshProfileDir(), modulePath),
|
|
895
|
+
join(dshRoot(), 'profiles', modulePath),
|
|
896
|
+
join(dshRoot(), modulePath),
|
|
897
|
+
];
|
|
898
|
+
if (candidates.some((p) => existsSync(p))) return true;
|
|
899
|
+
const pkgPath = join(dshProfileDir(), 'package.json');
|
|
900
|
+
if (!existsSync(pkgPath)) return false;
|
|
901
|
+
try {
|
|
902
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
903
|
+
return Boolean(pkg.dependencies?.['@deepseek-ai/dsh-mcp-client']
|
|
904
|
+
|| pkg.devDependencies?.['@deepseek-ai/dsh-mcp-client']);
|
|
905
|
+
} catch {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function tryInstallDshMcpClient() {
|
|
911
|
+
if (process.env.HUAWEICLOUD_DEVKIT_SKIP_DSH_PLUGIN_INSTALL === '1') {
|
|
912
|
+
console.log(' DSH MCP client install skipped by environment');
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
if (dshMcpClientAvailable()) {
|
|
916
|
+
console.log(' DSH MCP client package detected');
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
if (commandAvailable('dsh')) {
|
|
920
|
+
const r = spawnSync('dsh', ['plugin', '--profile', 'web', 'add', '@deepseek-ai/dsh-mcp-client'], {
|
|
921
|
+
env: { ...process.env, DSH_HOME: dshRoot() },
|
|
922
|
+
windowsHide: true,
|
|
923
|
+
stdio: 'pipe',
|
|
924
|
+
timeout: 60000,
|
|
925
|
+
});
|
|
926
|
+
if (r.status === 0) {
|
|
927
|
+
console.log(' DSH MCP client package installed via dsh');
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
const err = `${r.stderr || ''}${r.stdout || ''}`.trim().split(/\r?\n/).slice(-2).join(' ');
|
|
931
|
+
console.log(` \x1b[33m[WARN]\x1b[0m DSH MCP client auto-install failed${err ? `: ${err}` : ''}`);
|
|
932
|
+
}
|
|
933
|
+
if (commandAvailable('pnpm') && existsSync(join(dshProfileDir(), 'package.json'))) {
|
|
934
|
+
const r = spawnSync('pnpm', ['--dir', dshProfileDir(), 'add', '@deepseek-ai/dsh-mcp-client'], {
|
|
935
|
+
windowsHide: true,
|
|
936
|
+
stdio: 'pipe',
|
|
937
|
+
timeout: 60000,
|
|
938
|
+
});
|
|
939
|
+
if (r.status === 0) {
|
|
940
|
+
console.log(' DSH MCP client package installed via pnpm');
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
console.log(' \x1b[33m[WARN]\x1b[0m DSH MCP client package not detected.');
|
|
945
|
+
console.log(' Manual: npx @deepseek-ai/dsh plugin --profile web add @deepseek-ai/dsh-mcp-client');
|
|
946
|
+
console.log(' If pnpm is missing, run: corepack enable pnpm');
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
async function installDsh() {
|
|
951
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
952
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
953
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
954
|
+
const pluginDest = dshPluginsDir();
|
|
955
|
+
|
|
956
|
+
copyDir(skillsSrc, dshSkillsDir());
|
|
957
|
+
console.log(` Skills -> ${dshSkillsDir()}`);
|
|
958
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
959
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
960
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
961
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
962
|
+
ensureDshMcpPatch();
|
|
963
|
+
tryInstallDshMcpClient();
|
|
964
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
965
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
async function updateDsh() {
|
|
969
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
970
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
971
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
972
|
+
const pluginDest = dshPluginsDir();
|
|
973
|
+
|
|
974
|
+
copyDir(skillsSrc, dshSkillsDir());
|
|
975
|
+
const stale = pruneStale(dshSkillsDir(), skillsSrc);
|
|
976
|
+
console.log(` Skills updated -> ${dshSkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
977
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
978
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
979
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
980
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
981
|
+
ensureDshMcpPatch();
|
|
982
|
+
tryInstallDshMcpClient();
|
|
983
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
984
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function uninstallDsh() {
|
|
988
|
+
const skillsDir = dshSkillsDir();
|
|
989
|
+
let removed = 0;
|
|
990
|
+
if (existsSync(skillsDir)) {
|
|
991
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
992
|
+
if (entry.name.startsWith('huawei')) {
|
|
993
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
994
|
+
removed++;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
998
|
+
try {
|
|
999
|
+
if (readdirSync(skillsDir).length === 0) {
|
|
1000
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
1001
|
+
console.log(` Removed empty skills directory: ${skillsDir}`);
|
|
1002
|
+
}
|
|
1003
|
+
} catch {}
|
|
1004
|
+
}
|
|
1005
|
+
if (removeIfExists(dshPluginsDir())) {
|
|
1006
|
+
console.log(' Removed MCP server and safety policy');
|
|
1007
|
+
}
|
|
1008
|
+
removeDshMcpPatch();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function dshStatus() {
|
|
1012
|
+
const pluginDir = dshPluginsDir();
|
|
1013
|
+
const skillsDir = dshSkillsDir();
|
|
1014
|
+
console.log(` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
1015
|
+
console.log(` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
1016
|
+
let skillCount = 0;
|
|
1017
|
+
if (existsSync(skillsDir)) {
|
|
1018
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
1019
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
|
|
1020
|
+
}
|
|
1021
|
+
console.log(` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`);
|
|
1022
|
+
console.log(` DSH patch: ${dshPatchConfigured() ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
|
|
1023
|
+
console.log(` DSH MCP client package: ${dshMcpClientAvailable() ? '\x1b[32mDetected\x1b[0m' : '\x1b[33mCheck DSH profile\x1b[0m'}`);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
761
1026
|
function opencodeStatus() {
|
|
762
1027
|
const pluginDir = opencodePluginsDir();
|
|
763
1028
|
const skillsDir = opencodeSkillsDir();
|
|
@@ -788,6 +1053,7 @@ function parseTarget() {
|
|
|
788
1053
|
if (val === 'codex-desktop') return 'codex-desktop';
|
|
789
1054
|
if (val === 'codearts') return 'codearts';
|
|
790
1055
|
if (val === 'workbuddy') return 'workbuddy';
|
|
1056
|
+
if (val === 'dsh') return 'dsh';
|
|
791
1057
|
if (val === 'all') return 'all';
|
|
792
1058
|
return 'opencode';
|
|
793
1059
|
}
|
|
@@ -814,6 +1080,10 @@ async function cmdInstall() {
|
|
|
814
1080
|
console.log('\n[WorkBuddy]');
|
|
815
1081
|
await installWorkBuddy();
|
|
816
1082
|
}
|
|
1083
|
+
if (target === 'dsh' || target === 'all') {
|
|
1084
|
+
console.log('\n[DSH]');
|
|
1085
|
+
await installDsh();
|
|
1086
|
+
}
|
|
817
1087
|
if (target === 'codex' || target === 'all') {
|
|
818
1088
|
console.log('\n[Codex]');
|
|
819
1089
|
if (!hasCodexCLI()) {
|
|
@@ -836,11 +1106,13 @@ async function cmdInstall() {
|
|
|
836
1106
|
} else {
|
|
837
1107
|
installCodex();
|
|
838
1108
|
}
|
|
839
|
-
}
|
|
1109
|
+
}
|
|
1110
|
+
console.log(`\n\x1b[32mInstallation complete!\x1b[0m`);
|
|
840
1111
|
const appName = target === 'codearts' ? 'CodeArts'
|
|
841
1112
|
: target === 'codex-desktop' ? 'Codex Desktop'
|
|
842
1113
|
: target === 'codex' ? 'Codex'
|
|
843
1114
|
: target === 'workbuddy' ? 'WorkBuddy'
|
|
1115
|
+
: target === 'dsh' ? 'DSH'
|
|
844
1116
|
: 'OpenCode';
|
|
845
1117
|
const pad = ' '.repeat(24 - appName.length);
|
|
846
1118
|
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
@@ -863,7 +1135,8 @@ async function cmdInstall() {
|
|
|
863
1135
|
console.log(` 3. 运行自检:npx huaweicloud-devkit doctor`);
|
|
864
1136
|
|
|
865
1137
|
// Write install marker for doctor to detect
|
|
866
|
-
const markerDir = target === '
|
|
1138
|
+
const markerDir = target === 'dsh' ? dshPluginsDir()
|
|
1139
|
+
: target === 'codearts' ? codeartsPluginsDir()
|
|
867
1140
|
: target === 'workbuddy' ? workbuddyPluginsDir()
|
|
868
1141
|
: target === 'codex-desktop' ? codexDesktopPluginsDir()
|
|
869
1142
|
: opencodePluginsDir();
|
|
@@ -881,6 +1154,9 @@ async function cmdInstall() {
|
|
|
881
1154
|
if (target === 'workbuddy' || target === 'all') {
|
|
882
1155
|
console.log('Or describe your Huawei Cloud task in WorkBuddy');
|
|
883
1156
|
}
|
|
1157
|
+
if (target === 'dsh' || target === 'all') {
|
|
1158
|
+
console.log('Or describe your Huawei Cloud task in DSH');
|
|
1159
|
+
}
|
|
884
1160
|
}
|
|
885
1161
|
|
|
886
1162
|
async function cmdUninstall() {
|
|
@@ -900,6 +1176,10 @@ async function cmdUninstall() {
|
|
|
900
1176
|
console.log('\n[WorkBuddy]');
|
|
901
1177
|
uninstallWorkBuddy();
|
|
902
1178
|
}
|
|
1179
|
+
if (target === 'dsh' || target === 'all') {
|
|
1180
|
+
console.log('\n[DSH]');
|
|
1181
|
+
uninstallDsh();
|
|
1182
|
+
}
|
|
903
1183
|
if (target === 'codex-desktop' || target === 'codex' || target === 'all') {
|
|
904
1184
|
console.log('\n[Codex]');
|
|
905
1185
|
uninstallCodexDesktop();
|
|
@@ -944,6 +1224,10 @@ async function cmdStatus() {
|
|
|
944
1224
|
console.log('\n[WorkBuddy]');
|
|
945
1225
|
workbuddyStatus();
|
|
946
1226
|
}
|
|
1227
|
+
if (target === 'dsh' || target === 'all') {
|
|
1228
|
+
console.log('\n[DSH]');
|
|
1229
|
+
dshStatus();
|
|
1230
|
+
}
|
|
947
1231
|
if (target === 'codex' || target === 'all') {
|
|
948
1232
|
console.log('\n[Codex]');
|
|
949
1233
|
if (!hasCodexCLI()) {
|
|
@@ -971,16 +1255,22 @@ async function cmdDoctor() {
|
|
|
971
1255
|
// Node.js
|
|
972
1256
|
check('Node.js >= 20', process.versions.node.split('.')[0] >= 20, 'Run: nvm install 20 && nvm use 20');
|
|
973
1257
|
|
|
974
|
-
// MCP server — check OpenCode, Codex Desktop, CodeArts, and
|
|
1258
|
+
// MCP server — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH paths
|
|
975
1259
|
const opencodePluginDir = opencodePluginsDir();
|
|
976
1260
|
const codexPluginDir = codexDesktopPluginsDir();
|
|
1261
|
+
const codeartsPluginDir = codeartsPluginsDir();
|
|
977
1262
|
const workbuddyPluginDir = workbuddyPluginsDir();
|
|
1263
|
+
const dshPluginDir = dshPluginsDir();
|
|
978
1264
|
const mcpOk = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs'))
|
|
979
1265
|
|| existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
|
|
980
|
-
|| existsSync(join(
|
|
1266
|
+
|| existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs'))
|
|
1267
|
+
|| existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs'))
|
|
1268
|
+
|| existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs'));
|
|
981
1269
|
const mcpTarget = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ? 'OpenCode'
|
|
982
1270
|
: existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ? 'Codex Desktop'
|
|
983
|
-
: existsSync(join(
|
|
1271
|
+
: existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs')) ? 'CodeArts'
|
|
1272
|
+
: existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ? 'WorkBuddy'
|
|
1273
|
+
: existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs')) ? 'DSH' : '';
|
|
984
1274
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
985
1275
|
|
|
986
1276
|
if (mcpOk) {
|
|
@@ -989,10 +1279,12 @@ async function cmdDoctor() {
|
|
|
989
1279
|
|
|
990
1280
|
const safetyOk = existsSync(join(opencodePluginDir, 'safety', 'policy.json'))
|
|
991
1281
|
|| existsSync(join(codexPluginDir, 'safety', 'policy.json'))
|
|
992
|
-
|| existsSync(join(
|
|
1282
|
+
|| existsSync(join(codeartsPluginDir, 'safety', 'policy.json'))
|
|
1283
|
+
|| existsSync(join(workbuddyPluginDir, 'safety', 'policy.json'))
|
|
1284
|
+
|| existsSync(join(dshPluginDir, 'safety', 'policy.json'));
|
|
993
1285
|
check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
|
|
994
1286
|
|
|
995
|
-
// MCP config — check OpenCode, Codex Desktop, and
|
|
1287
|
+
// MCP config — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH
|
|
996
1288
|
let mcpConfigured = false;
|
|
997
1289
|
let mcpCfgTarget = '';
|
|
998
1290
|
const opencodeCfg = opencodeConfigFile();
|
|
@@ -1009,6 +1301,13 @@ async function cmdDoctor() {
|
|
|
1009
1301
|
if (cfg.includes('[mcp_servers.huaweicloud-devkit]')) { mcpConfigured = true; mcpCfgTarget = 'Codex Desktop'; }
|
|
1010
1302
|
} catch {}
|
|
1011
1303
|
}
|
|
1304
|
+
const codeartsCfg = codeartsMcpSettingsFile();
|
|
1305
|
+
if (!mcpConfigured && existsSync(codeartsCfg)) {
|
|
1306
|
+
try {
|
|
1307
|
+
const cfg = JSON.parse(readFileSync(codeartsCfg, 'utf8'));
|
|
1308
|
+
if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'CodeArts'; }
|
|
1309
|
+
} catch {}
|
|
1310
|
+
}
|
|
1012
1311
|
const workbuddyCfg = workbuddyMcpConfigFile();
|
|
1013
1312
|
if (!mcpConfigured && existsSync(workbuddyCfg)) {
|
|
1014
1313
|
try {
|
|
@@ -1016,6 +1315,10 @@ async function cmdDoctor() {
|
|
|
1016
1315
|
if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'WorkBuddy'; }
|
|
1017
1316
|
} catch {}
|
|
1018
1317
|
}
|
|
1318
|
+
if (!mcpConfigured && dshPatchConfigured()) {
|
|
1319
|
+
mcpConfigured = true;
|
|
1320
|
+
mcpCfgTarget = 'DSH';
|
|
1321
|
+
}
|
|
1019
1322
|
check('MCP configured', mcpConfigured, mcpCfgTarget ? `Found in ${mcpCfgTarget} config` : 'Run: npx huaweicloud-devkit install');
|
|
1020
1323
|
|
|
1021
1324
|
// hcloud CLI
|
|
@@ -1045,7 +1348,7 @@ async function cmdDoctor() {
|
|
|
1045
1348
|
}
|
|
1046
1349
|
|
|
1047
1350
|
// Skills
|
|
1048
|
-
const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir(), workbuddySkillsDir()];
|
|
1351
|
+
const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir(), workbuddySkillsDir(), dshSkillsDir()];
|
|
1049
1352
|
let skillCount = 0, skillsDir = '', missingSkills = [];
|
|
1050
1353
|
for (const dir of skillsOptions) {
|
|
1051
1354
|
if (!existsSync(dir)) continue;
|
|
@@ -1064,6 +1367,14 @@ async function cmdDoctor() {
|
|
|
1064
1367
|
warn++;
|
|
1065
1368
|
}
|
|
1066
1369
|
|
|
1370
|
+
const proxyConfig = readProxyConfig();
|
|
1371
|
+
const proxyEnv = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
|
|
1372
|
+
if (proxyConfig || proxyEnv) {
|
|
1373
|
+
const source = proxyEnv ? 'env' : 'file';
|
|
1374
|
+
const proxyUrl = proxyEnv || proxyConfig.https_proxy || proxyConfig.http_proxy;
|
|
1375
|
+
console.log(` \x1b[36m[INFO]\x1b[0m Proxy configured (${source}): ${proxyUrl}`);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1067
1378
|
console.log(`\nResults: ${pass} pass, ${warn} warn, ${fail} fail`);
|
|
1068
1379
|
|
|
1069
1380
|
if (mcpConfigured && !hcloudOk) {
|
|
@@ -1082,6 +1393,7 @@ async function cmdDoctor() {
|
|
|
1082
1393
|
{ path: join(codexDesktopPluginsDir(), '.installed'), name: 'Codex Desktop' },
|
|
1083
1394
|
{ path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
|
|
1084
1395
|
{ path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
|
|
1396
|
+
{ path: join(dshPluginsDir(), '.installed'), name: 'DSH' },
|
|
1085
1397
|
];
|
|
1086
1398
|
for (const marker of installedMarkers) {
|
|
1087
1399
|
if (existsSync(marker.path)) {
|
|
@@ -1163,6 +1475,18 @@ async function cmdUpdate() {
|
|
|
1163
1475
|
return;
|
|
1164
1476
|
}
|
|
1165
1477
|
|
|
1478
|
+
if (target === 'dsh') {
|
|
1479
|
+
if (!existsSync(join(dshPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1480
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
console.log('[DSH]');
|
|
1484
|
+
await updateDsh();
|
|
1485
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1486
|
+
console.log(`\x1b[33mRestart the DSH session for changes to take effect.\x1b[0m`);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1166
1490
|
if (target === 'all') {
|
|
1167
1491
|
let updatedAny = false;
|
|
1168
1492
|
if (existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
@@ -1185,6 +1509,11 @@ async function cmdUpdate() {
|
|
|
1185
1509
|
await updateWorkBuddy();
|
|
1186
1510
|
updatedAny = true;
|
|
1187
1511
|
}
|
|
1512
|
+
if (existsSync(join(dshPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1513
|
+
console.log('\n[DSH]');
|
|
1514
|
+
await updateDsh();
|
|
1515
|
+
updatedAny = true;
|
|
1516
|
+
}
|
|
1188
1517
|
if (codexStatus()) {
|
|
1189
1518
|
console.log('\n[Codex]');
|
|
1190
1519
|
installCodex();
|
|
@@ -1524,6 +1853,93 @@ async function cmdAuth() {
|
|
|
1524
1853
|
return cmdAuthStatus();
|
|
1525
1854
|
}
|
|
1526
1855
|
|
|
1856
|
+
async function cmdProxyInit() {
|
|
1857
|
+
console.log(BANNER);
|
|
1858
|
+
console.log('HuaweiCloud DevKit Proxy Configuration\n');
|
|
1859
|
+
console.log('Configure HTTP/HTTPS proxy for connections to Huawei Cloud services.');
|
|
1860
|
+
console.log('Proxy settings are saved to ~/.config/huaweicloud/proxy.json\n');
|
|
1861
|
+
|
|
1862
|
+
const existing = readProxyConfig() || {};
|
|
1863
|
+
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
1864
|
+
|
|
1865
|
+
if (!interactive) {
|
|
1866
|
+
console.error('\x1b[31mNon-interactive session. Set proxy via environment variables:\x1b[0m');
|
|
1867
|
+
console.error(' HTTPS_PROXY=http://proxy:port');
|
|
1868
|
+
console.error(' HTTP_PROXY=http://proxy:port');
|
|
1869
|
+
console.error(' NO_PROXY=localhost,127.0.0.1');
|
|
1870
|
+
console.error('\nOr run "npx huaweicloud-devkit proxy init" in a real terminal.');
|
|
1871
|
+
process.exitCode = 1;
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
const httpsProxy = await readLineQuestion(`HTTPS proxy [${existing.https_proxy || 'none'}]: `);
|
|
1876
|
+
const httpProxy = await readLineQuestion(`HTTP proxy [${existing.http_proxy || 'none'}]: `);
|
|
1877
|
+
const noProxy = await readLineQuestion(`NO_PROXY hosts [${existing.no_proxy || 'localhost,127.0.0.1'}]: `);
|
|
1878
|
+
|
|
1879
|
+
const config = {
|
|
1880
|
+
https_proxy: httpsProxy || existing.https_proxy || '',
|
|
1881
|
+
http_proxy: httpProxy || existing.http_proxy || '',
|
|
1882
|
+
no_proxy: noProxy || existing.no_proxy || 'localhost,127.0.0.1',
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
const path = writeProxyConfig(config);
|
|
1886
|
+
console.log(`\nProxy configuration saved to ${path}`);
|
|
1887
|
+
console.log('\nEffective settings:');
|
|
1888
|
+
console.log(` HTTPS_PROXY: ${config.https_proxy || '(none)'}`);
|
|
1889
|
+
console.log(` HTTP_PROXY: ${config.http_proxy || '(none)'}`);
|
|
1890
|
+
console.log(` NO_PROXY: ${config.no_proxy || '(none)'}`);
|
|
1891
|
+
console.log('\nEnvironment variables (HTTPS_PROXY, HTTP_PROXY, NO_PROXY) override file settings.');
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
async function cmdProxyShow() {
|
|
1895
|
+
console.log(BANNER);
|
|
1896
|
+
console.log('HuaweiCloud DevKit Proxy Configuration\n');
|
|
1897
|
+
|
|
1898
|
+
const config = readProxyConfig();
|
|
1899
|
+
const configPath = proxyConfigPath();
|
|
1900
|
+
|
|
1901
|
+
console.log(`Config file: ${configPath}`);
|
|
1902
|
+
console.log(`File exists: ${config ? 'yes' : 'no'}\n`);
|
|
1903
|
+
|
|
1904
|
+
if (config) {
|
|
1905
|
+
console.log('File settings:');
|
|
1906
|
+
console.log(` https_proxy: ${config.https_proxy || '(empty)'}`);
|
|
1907
|
+
console.log(` http_proxy: ${config.http_proxy || '(empty)'}`);
|
|
1908
|
+
console.log(` no_proxy: ${config.no_proxy || '(empty)'}`);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
console.log('\nEnvironment variables:');
|
|
1912
|
+
console.log(` HTTPS_PROXY: ${process.env.HTTPS_PROXY || process.env.https_proxy || '(not set)'}`);
|
|
1913
|
+
console.log(` HTTP_PROXY: ${process.env.HTTP_PROXY || process.env.http_proxy || '(not set)'}`);
|
|
1914
|
+
console.log(` NO_PROXY: ${process.env.NO_PROXY || process.env.no_proxy || '(not set)'}`);
|
|
1915
|
+
|
|
1916
|
+
const effective = getProxySettings();
|
|
1917
|
+
console.log('\nEffective (env > file):');
|
|
1918
|
+
if (effective) {
|
|
1919
|
+
console.log(` https_proxy: ${effective.https_proxy || '(none)'}`);
|
|
1920
|
+
console.log(` http_proxy: ${effective.http_proxy || '(none)'}`);
|
|
1921
|
+
console.log(` no_proxy: ${effective.no_proxy || '(none)'}`);
|
|
1922
|
+
} else {
|
|
1923
|
+
console.log(' (no proxy configured)');
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
async function cmdProxyClear() {
|
|
1928
|
+
const removed = clearProxyConfig();
|
|
1929
|
+
if (removed) {
|
|
1930
|
+
console.log('Proxy configuration removed.');
|
|
1931
|
+
} else {
|
|
1932
|
+
console.log('No proxy configuration file found.');
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
async function cmdProxy() {
|
|
1937
|
+
const sub = (process.argv[3] || 'show').toLowerCase();
|
|
1938
|
+
if (sub === 'init' || sub === 'setup') return cmdProxyInit();
|
|
1939
|
+
if (sub === 'clear' || sub === 'remove' || sub === 'reset') return cmdProxyClear();
|
|
1940
|
+
return cmdProxyShow();
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1527
1943
|
async function main() {
|
|
1528
1944
|
const cmd = process.argv[2] || 'help';
|
|
1529
1945
|
|
|
@@ -1557,12 +1973,15 @@ async function main() {
|
|
|
1557
1973
|
case 'auth':
|
|
1558
1974
|
await cmdAuth();
|
|
1559
1975
|
break;
|
|
1976
|
+
case 'proxy':
|
|
1977
|
+
await cmdProxy();
|
|
1978
|
+
break;
|
|
1560
1979
|
case 'help':
|
|
1561
1980
|
case '--help':
|
|
1562
1981
|
case '-h':
|
|
1563
1982
|
default:
|
|
1564
1983
|
console.log(BANNER);
|
|
1565
|
-
console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|all>]\n');
|
|
1984
|
+
console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|dsh|all>]\n');
|
|
1566
1985
|
console.log('Commands:');
|
|
1567
1986
|
console.log(' install Install skills, MCP server, safety policy');
|
|
1568
1987
|
console.log(' uninstall Remove installed files');
|
|
@@ -1572,18 +1991,22 @@ async function main() {
|
|
|
1572
1991
|
console.log(' doctor Self-check: hcloud, MCP, skills, auth');
|
|
1573
1992
|
console.log(' install-hcloud Show KooCLI install commands for your OS');
|
|
1574
1993
|
console.log(' auth Manage unified auth: init | sync | status');
|
|
1994
|
+
console.log(' proxy Manage proxy config: init | show | clear');
|
|
1575
1995
|
console.log(' help Show this help');
|
|
1576
1996
|
console.log('\nOptions:');
|
|
1577
|
-
console.log(' --target Target agent: opencode (default), codex, codearts, workbuddy, all');
|
|
1997
|
+
console.log(' --target Target agent: opencode (default), codex, codearts, workbuddy, dsh, all');
|
|
1578
1998
|
console.log('\nExamples:');
|
|
1579
1999
|
console.log(' npx huaweicloud-devkit install');
|
|
1580
2000
|
console.log(' npx huaweicloud-devkit install --target codex');
|
|
1581
2001
|
console.log(' npx huaweicloud-devkit install --target codearts');
|
|
1582
2002
|
console.log(' npx huaweicloud-devkit install --target workbuddy');
|
|
2003
|
+
console.log(' npx huaweicloud-devkit install --target dsh');
|
|
1583
2004
|
console.log(' npx huaweicloud-devkit install --target all');
|
|
1584
2005
|
console.log(' npx huaweicloud-devkit auth init');
|
|
1585
2006
|
console.log(' npx huaweicloud-devkit auth sync --target all');
|
|
1586
2007
|
console.log(' npx huaweicloud-devkit auth status --target all');
|
|
2008
|
+
console.log(' npx huaweicloud-devkit proxy init');
|
|
2009
|
+
console.log(' npx huaweicloud-devkit proxy show');
|
|
1587
2010
|
break;
|
|
1588
2011
|
}
|
|
1589
2012
|
}
|