dsh-cloudq 0.1.2 → 0.1.4
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 +47 -45
- package/lib/client.js +25 -7
- package/lib/index.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,67 +1,69 @@
|
|
|
1
1
|
# dsh-cloudq
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](README-zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
CloudQ integration for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It adds a CloudQ mode to the Web profile, bundles the `cloudq` skill, and provides CloudQ usage and architecture views, local credential setup, and plugin management.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Screenshots
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
|
-
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
12
14
|
|
|
13
15
|
- Node.js `>=22.19.0`
|
|
14
|
-
- DeepSeek Harness `0.1.1-rc.2`
|
|
15
|
-
- `dsh plugin`
|
|
16
|
-
-
|
|
17
|
-
- macOS
|
|
16
|
+
- DeepSeek Harness `0.1.1-rc.2` or a compatible newer `0.1.x` release
|
|
17
|
+
- `pnpm` available to the `dsh plugin` command
|
|
18
|
+
- Python 3 available as `python3`
|
|
19
|
+
- macOS or Linux
|
|
18
20
|
|
|
19
|
-
##
|
|
21
|
+
## Install
|
|
20
22
|
|
|
21
23
|
```sh
|
|
22
24
|
dsh plugin --profile web add dsh-cloudq
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Restart the Web profile after installation:
|
|
26
28
|
|
|
27
29
|
```sh
|
|
28
30
|
dsh --profile web
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
Open the URL printed by DSH. The conversation input area and sidebar will expose the CloudQ entry. You can also invoke the bundled skill explicitly with `/cloudq`.
|
|
32
34
|
|
|
33
|
-
##
|
|
35
|
+
## Upgrade and remove
|
|
34
36
|
|
|
35
37
|
```sh
|
|
36
38
|
dsh plugin --profile web update dsh-cloudq
|
|
37
39
|
dsh plugin --profile web remove dsh-cloudq
|
|
38
40
|
```
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
Restart the Web profile after changing the installed package set.
|
|
41
43
|
|
|
42
|
-
##
|
|
44
|
+
## Credentials
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
The settings card accepts a Tencent Cloud `SecretId`/`SecretKey` pair.
|
|
45
47
|
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
48
|
+
- Credentials are stored locally at `~/.tencent-cloudq/credential.json` with owner-only permissions.
|
|
49
|
+
- Secret values are sent to Python helpers through standard input, never command-line arguments.
|
|
50
|
+
- Browser APIs return only credential state and masked identifiers; local credential paths and secret values are not returned.
|
|
51
|
+
- Use the logout action to remove the stored credential.
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
Follow least-privilege: grant only the permissions required for the CloudQ operations you intend to run. The bundled skill can invoke read and write cloud-management operations; review each action before approving it.
|
|
52
54
|
|
|
53
|
-
##
|
|
55
|
+
## Security model
|
|
54
56
|
|
|
55
|
-
- Host
|
|
56
|
-
- JSON
|
|
57
|
-
- Python
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
- npm
|
|
57
|
+
- Host APIs accept only loopback, same-origin requests.
|
|
58
|
+
- JSON request bodies are limited to 64 KiB.
|
|
59
|
+
- Python helper output is bounded, and unexpected internal errors are not returned to the browser.
|
|
60
|
+
- Remote values are inserted with DOM text nodes rather than HTML injection.
|
|
61
|
+
- Download links require HTTPS.
|
|
62
|
+
- No credentials, tokens, or local environment files are included in the npm package.
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
Report security issues through [GitHub Issues](https://github.com/TencentCloud/cloudq-for-dsh/issues) without including live credentials.
|
|
63
65
|
|
|
64
|
-
##
|
|
66
|
+
## Development
|
|
65
67
|
|
|
66
68
|
```sh
|
|
67
69
|
pnpm install
|
|
@@ -73,28 +75,28 @@ pnpm run check:client
|
|
|
73
75
|
npm pack --dry-run --registry=https://registry.npmjs.org/
|
|
74
76
|
```
|
|
75
77
|
|
|
76
|
-
npm
|
|
78
|
+
The npm package ships prebuilt Host and Web client artifacts, the bundle patch, the runtime logo, and the bundled skill. Registry installation runs no build scripts.
|
|
77
79
|
|
|
78
|
-
##
|
|
80
|
+
## Repository layout
|
|
79
81
|
|
|
80
82
|
```text
|
|
81
|
-
src/ Host
|
|
82
|
-
skills/cloudq/
|
|
83
|
-
assets/cloudq.png Host
|
|
84
|
-
scripts/
|
|
85
|
-
tests/
|
|
86
|
-
cordis.patch.yml DSH
|
|
83
|
+
src/ Host and Web client source
|
|
84
|
+
skills/cloudq/ Bundled CloudQ skill and Python helpers
|
|
85
|
+
assets/cloudq.png Runtime logo served by the Host
|
|
86
|
+
scripts/ Build and release checks
|
|
87
|
+
tests/ Unit, integration, and package-contract tests
|
|
88
|
+
cordis.patch.yml DSH bundle layer
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
##
|
|
91
|
+
## Release
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
Source is reviewed and versioned in [TencentCloud/cloudq-for-dsh](https://github.com/TencentCloud/cloudq-for-dsh). An npm version is published to the official registry only after the repository checks and the package-install smoke test pass.
|
|
92
94
|
|
|
93
|
-
##
|
|
95
|
+
## More documentation
|
|
94
96
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
+
- Changelog: [`CHANGELOG.md`](CHANGELOG.md)
|
|
98
|
+
- Development & maintenance guide: [`DEVELOPMENT.md`](DEVELOPMENT.md)
|
|
97
99
|
|
|
98
|
-
##
|
|
100
|
+
## License
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
MIT. See [`LICENSE`](LICENSE).
|
package/lib/client.js
CHANGED
|
@@ -2134,7 +2134,7 @@ display: none;
|
|
|
2134
2134
|
hint.className = "dsh-cloudq-panel__hint";
|
|
2135
2135
|
hint.textContent = "正在加载用量…";
|
|
2136
2136
|
tableWrap.appendChild(hint);
|
|
2137
|
-
cloudqRequest(`${API_USAGE}?start=${encodeURIComponent(range.start)}&end=${encodeURIComponent(range.end)}`).then((data) => {
|
|
2137
|
+
requireCloudqCredential().then(() => cloudqRequest(`${API_USAGE}?start=${encodeURIComponent(range.start)}&end=${encodeURIComponent(range.end)}`)).then((data) => {
|
|
2138
2138
|
if (panelView !== "usage" || !panelExpanded) return;
|
|
2139
2139
|
value.textContent = formatCredits(data.overview?.TotalCredits);
|
|
2140
2140
|
const rows = Array.isArray(data.detail) ? data.detail : [];
|
|
@@ -2300,7 +2300,7 @@ display: none;
|
|
|
2300
2300
|
hint.className = "dsh-cloudq-panel__hint";
|
|
2301
2301
|
hint.textContent = "正在加载灵感…";
|
|
2302
2302
|
list.appendChild(hint);
|
|
2303
|
-
cloudqRequest(API_INSPIRATIONS).then((data) => {
|
|
2303
|
+
requireCloudqCredential().then(() => cloudqRequest(API_INSPIRATIONS)).then((data) => {
|
|
2304
2304
|
inspirationCache = Array.isArray(data.inspirations) ? data.inspirations : [];
|
|
2305
2305
|
paint(inspirationCache);
|
|
2306
2306
|
}).catch((error) => {
|
|
@@ -2442,7 +2442,7 @@ display: none;
|
|
|
2442
2442
|
hint.className = "dsh-cloudq-panel__hint";
|
|
2443
2443
|
hint.textContent = "正在加载制品…";
|
|
2444
2444
|
panelBody.appendChild(hint);
|
|
2445
|
-
cloudqRequest(API_ARTIFACTS).then((data) => {
|
|
2445
|
+
requireCloudqCredential().then(() => cloudqRequest(API_ARTIFACTS)).then((data) => {
|
|
2446
2446
|
artifactCache = Array.isArray(data?.sessions) ? data.sessions : [];
|
|
2447
2447
|
artifactTotal = Number(data?.total) || artifactCache.length;
|
|
2448
2448
|
paint(artifactCache, artifactTotal);
|
|
@@ -2742,7 +2742,7 @@ display: none;
|
|
|
2742
2742
|
paintDirectories(architectureDirectoriesCache, architectureFirstFolderId);
|
|
2743
2743
|
return;
|
|
2744
2744
|
}
|
|
2745
|
-
cloudqRequest(API_ARCH_DIRECTORIES).then((data) => {
|
|
2745
|
+
requireCloudqCredential().then(() => cloudqRequest(API_ARCH_DIRECTORIES)).then((data) => {
|
|
2746
2746
|
architectureDirectoriesCache = Array.isArray(data?.folders) ? data.folders : [];
|
|
2747
2747
|
architectureFirstFolderId = Number(data?.firstFolderId) || null;
|
|
2748
2748
|
paintDirectories(architectureDirectoriesCache, architectureFirstFolderId);
|
|
@@ -3041,14 +3041,32 @@ display: none;
|
|
|
3041
3041
|
this.code = code;
|
|
3042
3042
|
}
|
|
3043
3043
|
};
|
|
3044
|
+
const CLOUDQ_AUTH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3045
|
+
"NeedAuth",
|
|
3046
|
+
"CredentialExpired",
|
|
3047
|
+
"AuthFailure"
|
|
3048
|
+
]);
|
|
3044
3049
|
/**
|
|
3045
|
-
* Panel-facing error text.
|
|
3046
|
-
*
|
|
3050
|
+
* Panel-facing error text. Credential problems carry an actionable Host
|
|
3051
|
+
* message and are shown directly instead of a raw load failure prefix.
|
|
3047
3052
|
*/
|
|
3048
3053
|
function cloudqPanelErrorText(error, prefix) {
|
|
3049
|
-
if (error instanceof CloudQApiError && error.code
|
|
3054
|
+
if (error instanceof CloudQApiError && CLOUDQ_AUTH_ERROR_CODES.has(error.code)) return error.message;
|
|
3050
3055
|
return `${prefix}:${error instanceof Error ? error.message : "未知错误"}`;
|
|
3051
3056
|
}
|
|
3057
|
+
/**
|
|
3058
|
+
* Reject with an actionable NeedAuth error when no credential is configured,
|
|
3059
|
+
* so data views show the settings guide without calling the CloudQ APIs.
|
|
3060
|
+
* Returns undefined when the status check itself fails, letting the data
|
|
3061
|
+
* request surface its own error.
|
|
3062
|
+
*/
|
|
3063
|
+
function requireCloudqCredential() {
|
|
3064
|
+
return cloudqRequest(API_CREDENTIAL).then((response) => {
|
|
3065
|
+
if (response.status?.logged_in !== true) throw new CloudQApiError("尚未配置 AK/SK,请前往「设置 → 插件 → CloudQ」完成配置后重试。", "NeedAuth");
|
|
3066
|
+
}).catch((error) => {
|
|
3067
|
+
if (error instanceof CloudQApiError && error.code === "NeedAuth") throw error;
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3052
3070
|
async function cloudqRequest(url, init, timeoutMs = 2e4) {
|
|
3053
3071
|
const controller = new AbortController();
|
|
3054
3072
|
const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
|
package/lib/index.js
CHANGED
|
@@ -424,7 +424,11 @@ async function runScript(scriptName, args, options) {
|
|
|
424
424
|
try {
|
|
425
425
|
return await runScript$1(resolve(skillDirectory(), "scripts"), scriptName, args, options);
|
|
426
426
|
} catch (error) {
|
|
427
|
-
if (error instanceof HttpError
|
|
427
|
+
if (error instanceof HttpError) {
|
|
428
|
+
if (error.code === "NeedAuth") throw new HttpError(401, "NeedAuth", "尚未配置 AK/SK,请前往「设置 → 插件 → CloudQ」完成配置。");
|
|
429
|
+
if (error.code === "CredentialExpired") throw new HttpError(401, "CredentialExpired", "凭证已过期,请前往「设置 → 插件 → CloudQ」重新配置。");
|
|
430
|
+
if (typeof error.code === "string" && error.code.startsWith("AuthFailure")) throw new HttpError(401, "AuthFailure", "AK/SK 无效或权限不足,请前往「设置 → 插件 → CloudQ」检查配置。");
|
|
431
|
+
}
|
|
428
432
|
throw error;
|
|
429
433
|
}
|
|
430
434
|
}
|