huaweicloud-devkit 1.0.2-dev.0 → 1.0.2-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.0.2-dev.0",
3
+ "version": "1.0.2-dev.1",
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": [
@@ -5,6 +5,7 @@
5
5
  "args": [
6
6
  "./src/mcp-server.mjs"
7
7
  ],
8
+ "cwd": ".",
8
9
  "env": {
9
10
  "HUAWEICLOUD_AGENT_TOOLKIT_MODE": "local"
10
11
  }
@@ -79,6 +79,13 @@ Build → Create bucket → Upload → Set bucket ACL → Set object ACL → Con
79
79
 
80
80
  > KooCLI OBS does NOT support `SetBucketWebsite`. Configure static website hosting via REST API (`PUT /?website`) or the Huawei Cloud console.
81
81
 
82
+ ## Single-File Quick Share
83
+
84
+ See `references/single-file-share.md` for the full workflow to host one file and get a shareable link in seconds:
85
+
86
+ - **Private, time-limited**: `hcloud OBS sign obs://<bucket>/<key> -e=<seconds>` (max 7 days)
87
+ - **Public, permanent**: `hcloud OBS cp <file> obs://<bucket>/<key> -f` + `hcloud OBS chattri obs://<bucket>/<key> -acl=public-read`, then share `https://<bucket>.obs.<region>.myhuaweicloud.com/<key>`
88
+
82
89
  ## Storage Classes
83
90
 
84
91
  | Class | Use Case | Min Storage | Retrieval Fee |
@@ -115,5 +122,6 @@ Build → Create bucket → Upload → Set bucket ACL → Set object ACL → Con
115
122
 
116
123
  - OBS Docs: https://support.huaweicloud.com/obs/
117
124
  - Static website: references/static-website.md
125
+ - Single-file share: references/single-file-share.md
118
126
  - Lifecycle: references/bucket-lifecycle.md
119
127
  - Replication: references/replication.md
@@ -0,0 +1,40 @@
1
+ # OBS Single-File Quick Share
2
+
3
+ Host one file and get a shareable link in seconds. Two options:
4
+
5
+ | Option | Link type | Bucket/object visibility | Expiry |
6
+ |--------|-----------|--------------------------|--------|
7
+ | Presigned URL | Time-limited private link | Keep private | `-e=<seconds>`, max 7 days |
8
+ | Public URL | Permanent public link | Set object `-acl=public-read` | None |
9
+
10
+ ## Option 1: Presigned URL (private, time-limited)
11
+
12
+ No ACL change needed. The object stays private; the link grants temporary access.
13
+
14
+ ```bash
15
+ hcloud OBS cp <file> obs://<bucket>/<key> -f
16
+ hcloud OBS sign obs://<bucket>/<key> -e=3600 # valid 1 hour, max 604800 (7 days)
17
+ ```
18
+
19
+ The `sign` command prints the full presigned URL directly — share it as-is.
20
+
21
+ ## Option 2: Public URL (permanent, public-read)
22
+
23
+ ```bash
24
+ hcloud OBS cp <file> obs://<bucket>/<key> -f
25
+ hcloud OBS chattri obs://<bucket>/<key> -acl=public-read
26
+ ```
27
+
28
+ The shareable public URL follows the OBS endpoint format (NOT the `obs-website` static-site endpoint):
29
+
30
+ ```
31
+ https://<bucket>.obs.<region>.myhuaweicloud.com/<key>
32
+ ```
33
+
34
+ ## Key Gotchas
35
+
36
+ - **Bucket ACL does NOT cascade**: `chattri obs://<bucket> -acl=public-read` alone does not make objects public. Set the object-level ACL explicitly.
37
+ - **`-f` is mandatory**: `cp` without `-f` prompts "Please input (y/n)" on overwrite → agent hangs (TIMEOUT).
38
+ - **Public-read means anyone with the link can access**: use presigned URLs when the file is sensitive.
39
+ - **Presigned URL max expiry is 7 days** (`-e=604800`). For longer-lived public access, use the public-read option.
40
+ - **Credential setup**: OBS uses a separate config (`~/.obsutilconfig`). Call `huaweicloud_setup_obs_config` before first use.
@@ -40,7 +40,7 @@ function opencodeConfigFile() {
40
40
  function codexDesktopSkillsDir() { return join(homedir(), '.agents', 'skills'); }
41
41
  function codexDesktopCommandsDir() { return join(homedir(), '.agents', 'commands'); }
42
42
  function codexDesktopPluginsDir() { return join(homedir(), '.agents', 'huaweicloud-plugins'); }
43
- function codexDesktopConfigFile() { return join(homedir(), '.agents', 'opencode.json'); }
43
+ function codexConfigToml() { return join(homedir(), '.codex', 'config.toml'); }
44
44
 
45
45
  function codeartsSkillsDir() { return join(homedir(), '.codeartsdoer', 'skills'); }
46
46
  function codeartsMcpSettingsDir() { return join(homedir(), '.codeartsdoer', 'mcp'); }
@@ -230,6 +230,14 @@ function uninstallCodex() {
230
230
  shell: true, windowsHide: true, stdio: 'pipe',
231
231
  });
232
232
  console.log(` ${r.stdout ? r.stdout.toString().trim() : r.stderr.toString().trim()}`);
233
+
234
+ for (const name of new Set([marketplaceName, 'HuaweiCloud-Devkit'])) {
235
+ console.log(` Removing Codex marketplace: ${name}`);
236
+ const r2 = spawnSync(`codex plugin marketplace remove "${name}"`, [], {
237
+ shell: true, windowsHide: true, stdio: 'pipe',
238
+ });
239
+ console.log(` ${r2.stdout ? r2.stdout.toString().trim() : r2.stderr.toString().trim()}`);
240
+ }
233
241
  }
234
242
 
235
243
  function codexStatus() {
@@ -326,18 +334,27 @@ async function installCodexDesktop() {
326
334
  }
327
335
 
328
336
  const mcpPath = join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
329
- const configPath = codexDesktopConfigFile();
330
- let config = {};
337
+ const configPath = codexConfigToml();
338
+ const section = [
339
+ '[mcp_servers.huaweicloud-devkit]',
340
+ 'command = "node"',
341
+ `args = ["${mcpPath}"]`,
342
+ '',
343
+ '[mcp_servers.huaweicloud-devkit.env]',
344
+ 'HUAWEICLOUD_AGENT_TOOLKIT_MODE = "local"',
345
+ '',
346
+ ].join('\n');
347
+ let existing = '';
331
348
  if (existsSync(configPath)) {
332
- try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
349
+ try { existing = readFileSync(configPath, 'utf8'); } catch {}
350
+ if (existing.includes('[mcp_servers.huaweicloud-devkit]')) {
351
+ console.log(` Config already configured: ${configPath}`);
352
+ return;
353
+ }
333
354
  }
334
- config.mcp = config.mcp || {};
335
- config.mcp['huaweicloud-devkit'] = {
336
- type: 'local',
337
- command: ['node', mcpPath],
338
- enabled: true,
339
- };
340
- writeFileSync(configPath, JSON.stringify(config, null, 2));
355
+ mkdirSync(dirname(configPath), { recursive: true });
356
+ if (existing && !existing.endsWith('\n')) existing += '\n';
357
+ writeFileSync(configPath, existing + section);
341
358
  console.log(` Config updated: ${configPath}`);
342
359
  }
343
360
 
@@ -369,16 +386,19 @@ function uninstallCodexDesktop() {
369
386
  if (removeIfExists(codexDesktopPluginsDir())) {
370
387
  console.log(' Removed MCP server and safety policy');
371
388
  }
372
- const configPath = codexDesktopConfigFile();
389
+ const configPath = codexConfigToml();
373
390
  if (existsSync(configPath)) {
374
- let config = {};
375
- try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
376
- if (config.mcp?.['huaweicloud-devkit']) {
377
- delete config.mcp['huaweicloud-devkit'];
378
- if (Object.keys(config.mcp).length === 0) delete config.mcp;
379
- writeFileSync(configPath, JSON.stringify(config, null, 2));
380
- console.log(' Config cleaned');
391
+ const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
392
+ const out = [];
393
+ let skip = false;
394
+ for (const line of lines) {
395
+ if (/^\[mcp_servers\.huaweicloud-devkit(\]|\.)/.test(line)) { skip = true; continue; }
396
+ if (skip && line.startsWith('[')) skip = false;
397
+ if (!skip) out.push(line);
381
398
  }
399
+ while (out.length > 0 && out[out.length - 1].trim() === '') out.pop();
400
+ writeFileSync(configPath, out.join('\n') + (out.length > 0 ? '\n' : ''));
401
+ console.log(' Config cleaned');
382
402
  }
383
403
  }
384
404
 
@@ -663,7 +683,9 @@ async function cmdInstall() {
663
683
  // Write install marker for doctor to detect
664
684
  const markerDir = target === 'codearts' ? codeartsPluginsDir()
665
685
  : target === 'workbuddy' ? workbuddyPluginsDir()
686
+ : target === 'codex-desktop' ? codexDesktopPluginsDir()
666
687
  : opencodePluginsDir();
688
+ mkdirSync(markerDir, { recursive: true });
667
689
  writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
668
690
  if (target === 'opencode' || target === 'all') {
669
691
  console.log('Or describe your Huawei Cloud task in OpenCode');
@@ -696,13 +718,15 @@ async function cmdUninstall() {
696
718
  console.log('\n[WorkBuddy]');
697
719
  uninstallWorkBuddy();
698
720
  }
699
- if (target === 'codex' || target === 'all') {
721
+ if (target === 'codex-desktop' || target === 'codex' || target === 'all') {
700
722
  console.log('\n[Codex]');
701
723
  uninstallCodexDesktop();
702
- if (!hasCodexCLI()) {
703
- console.log(' \x1b[33mCodex CLI not found. Run "npm uninstall -g codex" to fully remove.\x1b[0m');
704
- } else {
705
- uninstallCodex();
724
+ if (target === 'codex' || target === 'all') {
725
+ if (!hasCodexCLI()) {
726
+ console.log(' \x1b[33mCodex CLI not found. Run "npm uninstall -g codex" to fully remove.\x1b[0m');
727
+ } else {
728
+ uninstallCodex();
729
+ }
706
730
  }
707
731
  }
708
732
  console.log(`\n\x1b[32mUninstall complete.\x1b[0m`);
@@ -783,11 +807,11 @@ async function cmdDoctor() {
783
807
  if (cfg.mcp && cfg.mcp['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'OpenCode'; }
784
808
  } catch {}
785
809
  }
786
- const codexCfg = codexDesktopConfigFile();
810
+ const codexCfg = codexConfigToml();
787
811
  if (!mcpConfigured && existsSync(codexCfg)) {
788
812
  try {
789
- const cfg = JSON.parse(readFileSync(codexCfg, 'utf8'));
790
- if (cfg.mcp && cfg.mcp['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'Codex Desktop'; }
813
+ const cfg = readFileSync(codexCfg, 'utf8');
814
+ if (cfg.includes('[mcp_servers.huaweicloud-devkit]')) { mcpConfigured = true; mcpCfgTarget = 'Codex Desktop'; }
791
815
  } catch {}
792
816
  }
793
817
  const workbuddyCfg = workbuddyMcpConfigFile();
@@ -860,6 +884,7 @@ async function cmdDoctor() {
860
884
  // Detect "installed but not restarted" — check all supported agents
861
885
  const installedMarkers = [
862
886
  { path: join(opencodePluginsDir(), '.installed'), name: 'OpenCode' },
887
+ { path: join(codexDesktopPluginsDir(), '.installed'), name: 'Codex Desktop' },
863
888
  { path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
864
889
  { path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
865
890
  ];