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.
|
|
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": [
|
|
@@ -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
|
|
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 =
|
|
330
|
-
|
|
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 {
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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 =
|
|
389
|
+
const configPath = codexConfigToml();
|
|
373
390
|
if (existsSync(configPath)) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
|
|
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 (
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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 =
|
|
810
|
+
const codexCfg = codexConfigToml();
|
|
787
811
|
if (!mcpConfigured && existsSync(codexCfg)) {
|
|
788
812
|
try {
|
|
789
|
-
const cfg =
|
|
790
|
-
if (cfg.
|
|
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
|
];
|