dsh-cloudq 0.1.0 → 0.1.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/README.md CHANGED
@@ -35,7 +35,7 @@ dsh plugin --profile web remove dsh-cloudq
35
35
 
36
36
  ## 凭证配置
37
37
 
38
- 设置卡片支持 CloudQ OAuth 流程,也支持手动配置腾讯云 `SecretId` 和 `SecretKey`。
38
+ 设置卡片支持配置腾讯云 `SecretId` 和 `SecretKey` 凭证。
39
39
 
40
40
  - 凭证保存在本机 `~/.tencent-cloudq/credential.json`,文件权限仅允许当前用户访问。
41
41
  - 敏感凭证通过标准输入传递给 Python 辅助脚本,不会放入命令行参数。
package/lib/client.js CHANGED
@@ -100,6 +100,11 @@ window.__ModuleLoader__.load({
100
100
  background: var(--dsw-alias-bg-layer-3, #fff);
101
101
  color: var(--dsw-alias-label-primary, #17191c);
102
102
  }
103
+ .dsh-cloudq-settings-card *,
104
+ .dsh-cloudq-settings-card *::before,
105
+ .dsh-cloudq-settings-card *::after {
106
+ box-sizing: border-box;
107
+ }
103
108
  .dsh-cloudq-settings-card__summary {
104
109
  padding: 15px 16px;
105
110
  }
@@ -215,10 +220,11 @@ window.__ModuleLoader__.load({
215
220
  flex-wrap: wrap;
216
221
  }
217
222
  .dsh-cloudq-settings-card__actions--stacked {
218
- flex-direction: column-reverse;
223
+ flex-direction: column;
224
+ align-items: stretch;
219
225
  }
220
226
  .dsh-cloudq-settings-card__actions--stacked button {
221
- width: 100%;
227
+ align-self: flex-end;
222
228
  }
223
229
  .dsh-cloudq-settings-card__btn {
224
230
  display: inline-flex;
@@ -1906,7 +1912,7 @@ display: none;
1906
1912
  panelBody.textContent = "";
1907
1913
  const err = document.createElement("div");
1908
1914
  err.className = "dsh-cloudq-panel__error";
1909
- err.textContent = `用量加载失败:${error.message}`;
1915
+ err.textContent = cloudqPanelErrorText(error, "用量加载失败");
1910
1916
  panelBody.appendChild(err);
1911
1917
  });
1912
1918
  }
@@ -2009,7 +2015,7 @@ display: none;
2009
2015
  list.textContent = "";
2010
2016
  const err = document.createElement("div");
2011
2017
  err.className = "dsh-cloudq-panel__error";
2012
- err.textContent = `灵感加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2018
+ err.textContent = cloudqPanelErrorText(error, "灵感加载失败");
2013
2019
  list.appendChild(err);
2014
2020
  });
2015
2021
  }
@@ -2152,7 +2158,7 @@ display: none;
2152
2158
  panelBody.textContent = "";
2153
2159
  const err = document.createElement("div");
2154
2160
  err.className = "dsh-cloudq-panel__error";
2155
- err.textContent = `制品加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2161
+ err.textContent = cloudqPanelErrorText(error, "制品加载失败");
2156
2162
  panelBody.appendChild(err);
2157
2163
  });
2158
2164
  }
@@ -2433,7 +2439,7 @@ display: none;
2433
2439
  summary.textContent = "加载失败";
2434
2440
  const err = document.createElement("div");
2435
2441
  err.className = "dsh-cloudq-panel__error";
2436
- err.textContent = `架构图加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2442
+ err.textContent = cloudqPanelErrorText(error, "架构图加载失败");
2437
2443
  grid.appendChild(err);
2438
2444
  });
2439
2445
  };
@@ -2452,7 +2458,7 @@ display: none;
2452
2458
  panelBody.textContent = "";
2453
2459
  const err = document.createElement("div");
2454
2460
  err.className = "dsh-cloudq-panel__error";
2455
- err.textContent = `架构目录加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2461
+ err.textContent = cloudqPanelErrorText(error, "架构目录加载失败");
2456
2462
  panelBody.appendChild(err);
2457
2463
  });
2458
2464
  }
@@ -2732,8 +2738,6 @@ display: none;
2732
2738
  };
2733
2739
  }
2734
2740
  const API_CREDENTIAL = "/api/dsh-cloudq/credential";
2735
- const API_LOGIN_URL = "/api/dsh-cloudq/login/url";
2736
- const API_LOGIN_SAVE = "/api/dsh-cloudq/login/save";
2737
2741
  const API_LOGOUT = "/api/dsh-cloudq/logout";
2738
2742
  const API_CREDENTIAL_TEST = "/api/dsh-cloudq/credential/test";
2739
2743
  const API_CREDENTIAL_SAVE = "/api/dsh-cloudq/credential/save";
@@ -2744,6 +2748,14 @@ display: none;
2744
2748
  this.code = code;
2745
2749
  }
2746
2750
  };
2751
+ /**
2752
+ * Panel-facing error text. A missing credential gets an actionable pointer to
2753
+ * the settings card instead of a raw load failure message.
2754
+ */
2755
+ function cloudqPanelErrorText(error, prefix) {
2756
+ if (error instanceof CloudQApiError && error.code === "NeedAuth") return "尚未配置 AK/SK,请前往「设置 → 插件 → CloudQ」完成配置后重试。";
2757
+ return `${prefix}:${error instanceof Error ? error.message : "未知错误"}`;
2758
+ }
2747
2759
  async function cloudqRequest(url, init, timeoutMs = 2e4) {
2748
2760
  const controller = new AbortController();
2749
2761
  const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
@@ -2788,9 +2800,8 @@ display: none;
2788
2800
  const [loading, setLoading] = react.useState(true);
2789
2801
  const [busy, setBusy] = react.useState();
2790
2802
  const [status, setStatus] = react.useState();
2791
- const [authorizeUrl, setAuthorizeUrl] = react.useState("");
2792
- const [code, setCode] = react.useState("");
2793
2803
  const [feedback, setFeedback] = react.useState();
2804
+ const [validated, setValidated] = react.useState(false);
2794
2805
  const [secretId, setSecretId] = react.useState("");
2795
2806
  const [secretKey, setSecretKey] = react.useState("");
2796
2807
  const refresh = react.useCallback(async () => {
@@ -2810,61 +2821,13 @@ display: none;
2810
2821
  react.useEffect(() => {
2811
2822
  refresh();
2812
2823
  }, [refresh]);
2813
- const handleGetAuthorizeUrl = async () => {
2814
- setBusy("url");
2815
- setFeedback(void 0);
2816
- try {
2817
- const response = await cloudqRequest(API_LOGIN_URL, { method: "POST" });
2818
- setAuthorizeUrl(response.authorize_url);
2819
- if (response.authorize_url) window.open(response.authorize_url, "_blank", "noopener");
2820
- } catch (error) {
2821
- setFeedback({
2822
- kind: "error",
2823
- text: error.message
2824
- });
2825
- } finally {
2826
- setBusy(void 0);
2827
- }
2828
- };
2829
- const handleSaveCode = async () => {
2830
- const trimmed = code.trim();
2831
- if (!trimmed) {
2832
- setFeedback({
2833
- kind: "error",
2834
- text: "请先在浏览器中完成授权,然后粘贴授权码。"
2835
- });
2836
- return;
2837
- }
2838
- setBusy("save");
2839
- setFeedback(void 0);
2840
- try {
2841
- const response = await cloudqRequest(API_LOGIN_SAVE, {
2842
- method: "POST",
2843
- body: JSON.stringify({ code: trimmed })
2844
- });
2845
- setStatus(response.status);
2846
- setCode("");
2847
- setFeedback({
2848
- kind: "success",
2849
- text: "登录成功,凭证已保存。"
2850
- });
2851
- } catch (error) {
2852
- setFeedback({
2853
- kind: "error",
2854
- text: error.message
2855
- });
2856
- } finally {
2857
- setBusy(void 0);
2858
- }
2859
- };
2860
2824
  const handleLogout = async () => {
2861
2825
  setBusy("logout");
2862
2826
  setFeedback(void 0);
2863
2827
  try {
2864
2828
  await cloudqRequest(API_LOGOUT, { method: "POST" });
2865
2829
  setStatus({ logged_in: false });
2866
- setAuthorizeUrl("");
2867
- setCode("");
2830
+ setValidated(false);
2868
2831
  setFeedback({
2869
2832
  kind: "success",
2870
2833
  text: "已退出登录。"
@@ -2903,26 +2866,27 @@ display: none;
2903
2866
  setSecretId("");
2904
2867
  setSecretKey("");
2905
2868
  }
2869
+ setValidated(true);
2906
2870
  setFeedback({
2907
2871
  kind: "success",
2908
2872
  text: successText
2909
2873
  });
2910
2874
  } catch (error) {
2875
+ setValidated(false);
2876
+ const invalid = error instanceof CloudQApiError && error.code !== "network-error" && error.code !== "invalid-response";
2911
2877
  setFeedback({
2912
2878
  kind: "error",
2913
- text: error.message
2879
+ text: invalid ? "AKSK 无效,请检查后重新配置。" : error.message
2914
2880
  });
2915
2881
  } finally {
2916
2882
  setBusy(void 0);
2917
2883
  }
2918
2884
  };
2919
- const handleTestConnection = () => submitAccessKey(API_CREDENTIAL_TEST, "test", "连接成功,该密钥可用于 CloudQ。");
2920
- const handleSaveAccessKey = () => submitAccessKey(API_CREDENTIAL_SAVE, "save-ak", "配置已保存,密钥已安全存放在 DSH Host 端。");
2885
+ const handleTestConnection = () => submitAccessKey(API_CREDENTIAL_TEST, "test", "连接成功,AKSK 有效。");
2886
+ const handleSaveAccessKey = () => submitAccessKey(API_CREDENTIAL_SAVE, "save-ak", "配置已保存,AKSK 有效。");
2921
2887
  const loggedIn = status?.logged_in === true;
2922
- const expired = status?.expired === true;
2923
- const badgeClass = expired ? "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--warn" : loggedIn ? "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--ok" : "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--off";
2924
- const badgeText = expired ? "凭证已过期" : loggedIn ? "已登录" : "未登录";
2925
- const summarySubtitle = loggedIn ? "腾讯云 CloudQ 凭证已激活,可直接使用 CloudQ 模式。" : "用于在 CloudQ 模式下调用腾讯云 SSE API 与 CLI 工具。";
2888
+ const akskValid = validated || loggedIn;
2889
+ const summarySubtitle = akskValid ? "腾讯云 CloudQ 凭证已激活,可直接使用 CloudQ 模式。" : "用于在 CloudQ 模式下调用腾讯云 SSE API CLI 工具。";
2926
2890
  const disabled = loading || busy !== void 0;
2927
2891
  return react_jsx_runtime.jsx("section", {
2928
2892
  className: "dsh-cloudq-settings-card",
@@ -2938,9 +2902,9 @@ display: none;
2938
2902
  children: [react_jsx_runtime.jsx("strong", { children: "CloudQ" }), react_jsx_runtime.jsx("small", { children: summarySubtitle })]
2939
2903
  }), react_jsx_runtime.jsx("span", {
2940
2904
  className: "dsh-cloudq-settings-card__summary-right",
2941
- children: [react_jsx_runtime.jsx("span", {
2942
- className: badgeClass,
2943
- children: [react_jsx_runtime.jsx("span", { className: "dsh-cloudq-settings-card__badge-dot" }), badgeText]
2905
+ children: [akskValid && react_jsx_runtime.jsx("span", {
2906
+ className: "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--ok",
2907
+ children: [react_jsx_runtime.jsx("span", { className: "dsh-cloudq-settings-card__badge-dot" }), "AKSK有效"]
2944
2908
  }), react_jsx_runtime.jsx("span", {
2945
2909
  className: `dsh-cloudq-settings-card__chevron${expanded ? " is-open" : ""}`,
2946
2910
  children: "▾"
@@ -2952,20 +2916,10 @@ display: none;
2952
2916
  children: [
2953
2917
  loading ? react_jsx_runtime.jsx("div", {
2954
2918
  className: "dsh-cloudq-settings-card__hint",
2955
- children: "正在检查 CloudQ 登录状态…"
2919
+ children: "正在检查凭证状态…"
2956
2920
  }) : react_jsx_runtime.jsx("div", {
2957
2921
  className: "dsh-cloudq-settings-card__rows",
2958
2922
  children: [
2959
- react_jsx_runtime.jsx("div", {
2960
- className: "dsh-cloudq-settings-card__row",
2961
- children: [react_jsx_runtime.jsx("span", {
2962
- className: "dsh-cloudq-settings-card__row-key",
2963
- children: "状态"
2964
- }), react_jsx_runtime.jsx("span", {
2965
- className: "dsh-cloudq-settings-card__row-val",
2966
- children: badgeText
2967
- })]
2968
- }),
2969
2923
  status?.credential_file ? react_jsx_runtime.jsx("div", {
2970
2924
  className: "dsh-cloudq-settings-card__row",
2971
2925
  children: [react_jsx_runtime.jsx("span", {
@@ -3023,35 +2977,6 @@ display: none;
3023
2977
  children: feedback.text
3024
2978
  }) : null,
3025
2979
  !loggedIn && react_jsx_runtime.jsx("div", { children: [
3026
- react_jsx_runtime.jsx("div", {
3027
- className: "dsh-cloudq-settings-card__actions",
3028
- children: [react_jsx_runtime.jsx("button", {
3029
- type: "button",
3030
- className: "dsh-cloudq-settings-card__btn dsh-cloudq-settings-card__btn--primary",
3031
- disabled,
3032
- onClick: handleGetAuthorizeUrl,
3033
- children: busy === "url" ? "获取中…" : "获取授权链接"
3034
- })]
3035
- }),
3036
- authorizeUrl && react_jsx_runtime.jsx("div", {
3037
- className: "dsh-cloudq-settings-card__hint",
3038
- children: "已在新标签页打开授权页。授权完成后,请将页面显示的授权码粘贴到下方:"
3039
- }),
3040
- react_jsx_runtime.jsx("div", {
3041
- className: "dsh-cloudq-settings-card__actions dsh-cloudq-settings-card__actions--stacked",
3042
- children: [react_jsx_runtime.jsx("input", {
3043
- className: "dsh-cloudq-settings-card__input",
3044
- placeholder: "粘贴授权码",
3045
- value: code,
3046
- onChange: (event) => setCode(event.target.value)
3047
- }), react_jsx_runtime.jsx("button", {
3048
- type: "button",
3049
- className: "dsh-cloudq-settings-card__btn",
3050
- disabled,
3051
- onClick: handleSaveCode,
3052
- children: busy === "save" ? "登录中…" : "完成登录"
3053
- })]
3054
- }),
3055
2980
  react_jsx_runtime.jsx("div", {
3056
2981
  className: "dsh-cloudq-settings-card__field",
3057
2982
  children: [
package/lib/index.js CHANGED
@@ -392,9 +392,9 @@ function runScript$1(scriptsDirectory, scriptName, args, { timeoutMs = 3e4, json
392
392
  * and referenced through a directory `resourceBase`, so every `{baseDir}`
393
393
  * placeholder in the body resolves against the installed package directory.
394
394
  *
395
- * It also exposes a small credential-management API (status / authorize-url /
396
- * save-code / logout) that the client half renders in the settings panel, so
397
- * the CloudQ login flow no longer requires touching a terminal.
395
+ * It also exposes a small credential-management API (status / AK-SK test &
396
+ * save / logout) that the client half renders in the settings panel, so the
397
+ * CloudQ credential flow no longer requires touching a terminal.
398
398
  *
399
399
  * @module dsh-cloudq
400
400
  */
@@ -420,8 +420,13 @@ function renderedSkillContent() {
420
420
  return rawSkillContent().replace(/\{baseDir\}/g, baseDir);
421
421
  }
422
422
  /** Run one helper from the bundled CloudQ skill. */
423
- function runScript(scriptName, args, options) {
424
- return runScript$1(resolve(skillDirectory(), "scripts"), scriptName, args, options);
423
+ async function runScript(scriptName, args, options) {
424
+ try {
425
+ return await runScript$1(resolve(skillDirectory(), "scripts"), scriptName, args, options);
426
+ } catch (error) {
427
+ if (error instanceof HttpError && error.code === "NeedAuth") throw new HttpError(401, "NeedAuth", "尚未配置 AK/SK,请前往「设置 → 插件 → CloudQ」完成配置。");
428
+ throw error;
429
+ }
425
430
  }
426
431
  /** Remove host filesystem details before returning credential state to the browser. */
427
432
  function publicCredentialStatus(status) {
@@ -434,15 +439,6 @@ function publicCredentialStatus(status) {
434
439
  async function credentialStatus() {
435
440
  return publicCredentialStatus(await runScript("login.py", ["--status"]));
436
441
  }
437
- function authorizeUrl() {
438
- return runScript("login.py", ["--authorize-url"]);
439
- }
440
- async function saveAuthCode(code) {
441
- return publicCredentialStatus(await runScript("login.py", ["--save", "--stdin"], {
442
- stdin: JSON.stringify({ code }),
443
- sensitiveValues: [code]
444
- }));
445
- }
446
442
  function logout() {
447
443
  return runScript("logout.py", [], { jsonOnly: false });
448
444
  }
@@ -616,23 +612,29 @@ const inject = [
616
612
  "sessionQuery"
617
613
  ];
618
614
  function apply(ctx) {
619
- ctx.effect(() => ctx.settings.register(SETTINGS_NAMESPACE, Config, { base: {} }), "dsh-cloudq: settings namespace");
620
- ctx.effect(() => ctx.settings.register(PLUGIN_MANAGER_NAMESPACE, Config, { base: {} }), "dsh-cloudq: plugin-manager settings namespace");
621
- ctx.effect(() => ctx.skills.register({
622
- name: "cloudq",
623
- description: "用户咨询腾讯云产品资源、AWS、阿里云等多云资源时,查看智能顾问架构图、架构目录、架构详情、架构评估结果、绘制架构图、开通智能顾问时、AI智能巡检、AI容量监测、AI混沌演练、AI云诊断、主动预警、架构健康度、云运维问答、云资源查询、云成本优化、安全合规、云资源盘点、闲置资源检查、云产品最佳实践等AIOps、ChatOps、CloudOps操作时使用。",
624
- whenToUse: "用户要求进入 CloudQ 模式、使用 CloudQ、咨询云上架构/资源/成本/巡检/诊断等多云或腾讯云运维问题,或点击输入栏 CloudQ 按钮时使用;也适用于架构图查看/评估、AI 巡检、容量监测、混沌演练、云诊断等场景。",
625
- source: "bundled",
626
- resourceBase: {
627
- kind: "directory",
628
- path: skillDirectory()
629
- },
630
- content: renderedSkillContent(),
631
- invocation: {
632
- modelInvocable: true,
633
- userInvocable: true
634
- }
635
- }), "dsh-cloudq: CloudQ skill");
615
+ ctx.effect(() => {
616
+ ctx.settings.register(SETTINGS_NAMESPACE, Config, { base: {} });
617
+ }, "dsh-cloudq: settings namespace");
618
+ ctx.effect(() => {
619
+ ctx.settings.register(PLUGIN_MANAGER_NAMESPACE, Config, { base: {} });
620
+ }, "dsh-cloudq: plugin-manager settings namespace");
621
+ ctx.effect(() => {
622
+ ctx.skills.register({
623
+ name: "cloudq",
624
+ description: "用户咨询腾讯云产品资源、AWS、阿里云等多云资源时,查看智能顾问架构图、架构目录、架构详情、架构评估结果、绘制架构图、开通智能顾问时、AI智能巡检、AI容量监测、AI混沌演练、AI云诊断、主动预警、架构健康度、云运维问答、云资源查询、云成本优化、安全合规、云资源盘点、闲置资源检查、云产品最佳实践等AIOps、ChatOps、CloudOps操作时使用。",
625
+ whenToUse: "用户要求进入 CloudQ 模式、使用 CloudQ、咨询云上架构/资源/成本/巡检/诊断等多云或腾讯云运维问题,或点击输入栏 CloudQ 按钮时使用;也适用于架构图查看/评估、AI 巡检、容量监测、混沌演练、云诊断等场景。",
626
+ source: "bundled",
627
+ resourceBase: {
628
+ kind: "directory",
629
+ path: skillDirectory()
630
+ },
631
+ content: renderedSkillContent(),
632
+ invocation: {
633
+ modelInvocable: true,
634
+ userInvocable: true
635
+ }
636
+ });
637
+ }, "dsh-cloudq: CloudQ skill");
636
638
  ctx.effect(() => {
637
639
  const disposers = [];
638
640
  disposers.push(ctx.webServer.register({
@@ -650,41 +652,6 @@ function apply(ctx) {
650
652
  }
651
653
  }
652
654
  }));
653
- disposers.push(ctx.webServer.register({
654
- kind: "exact",
655
- path: "/api/dsh-cloudq/login/url",
656
- handler: async (request, response) => {
657
- try {
658
- assertSafeRequest(request, "POST");
659
- const result = await authorizeUrl();
660
- sendJson(response, 200, {
661
- ok: true,
662
- authorize_url: result.authorize_url,
663
- state: result.state
664
- });
665
- } catch (error) {
666
- sendError(response, error);
667
- }
668
- }
669
- }));
670
- disposers.push(ctx.webServer.register({
671
- kind: "exact",
672
- path: "/api/dsh-cloudq/login/save",
673
- handler: async (request, response) => {
674
- try {
675
- assertSafeRequest(request, "POST");
676
- const body = await readJsonBody(request);
677
- const code = typeof body?.code === "string" ? body.code.trim() : "";
678
- if (!code) throw new HttpError(400, "missing-code", "缺少授权码。");
679
- sendJson(response, 200, {
680
- ok: true,
681
- status: await saveAuthCode(code)
682
- });
683
- } catch (error) {
684
- sendError(response, error);
685
- }
686
- }
687
- }));
688
655
  disposers.push(ctx.webServer.register({
689
656
  kind: "exact",
690
657
  path: "/api/dsh-cloudq/logout",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-cloudq",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CloudQ integration for DeepSeek Harness with secure credential, workspace, and plugin-management surfaces",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -139,7 +139,7 @@ def cmd_status() -> int:
139
139
  if cred_data is None:
140
140
  print(_json_ok({
141
141
  "logged_in": False,
142
- "message": "未找到 OAuth 凭证",
142
+ "message": "未找到凭证",
143
143
  }))
144
144
  return 0
145
145