dsh-cloudq 0.1.1 → 0.1.3
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 +52 -39
- package/lib/client.js +396 -110
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,61 +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
|
+
|
|
7
|
+
## Screenshots
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
6
14
|
|
|
7
15
|
- Node.js `>=22.19.0`
|
|
8
|
-
- DeepSeek Harness `0.1.1-rc.2`
|
|
9
|
-
- `dsh plugin`
|
|
10
|
-
-
|
|
11
|
-
- 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
|
|
12
20
|
|
|
13
|
-
##
|
|
21
|
+
## Install
|
|
14
22
|
|
|
15
23
|
```sh
|
|
16
24
|
dsh plugin --profile web add dsh-cloudq
|
|
17
25
|
```
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
Restart the Web profile after installation:
|
|
20
28
|
|
|
21
29
|
```sh
|
|
22
30
|
dsh --profile web
|
|
23
31
|
```
|
|
24
32
|
|
|
25
|
-
|
|
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`.
|
|
26
34
|
|
|
27
|
-
##
|
|
35
|
+
## Upgrade and remove
|
|
28
36
|
|
|
29
37
|
```sh
|
|
30
38
|
dsh plugin --profile web update dsh-cloudq
|
|
31
39
|
dsh plugin --profile web remove dsh-cloudq
|
|
32
40
|
```
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
Restart the Web profile after changing the installed package set.
|
|
35
43
|
|
|
36
|
-
##
|
|
44
|
+
## Credentials
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
The settings card accepts a Tencent Cloud `SecretId`/`SecretKey` pair.
|
|
39
47
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
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.
|
|
44
52
|
|
|
45
|
-
|
|
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.
|
|
46
54
|
|
|
47
|
-
##
|
|
55
|
+
## Security model
|
|
48
56
|
|
|
49
|
-
- Host
|
|
50
|
-
- JSON
|
|
51
|
-
- Python
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
- 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.
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
Report security issues through [GitHub Issues](https://github.com/TencentCloud/cloudq-for-dsh/issues) without including live credentials.
|
|
57
65
|
|
|
58
|
-
##
|
|
66
|
+
## Development
|
|
59
67
|
|
|
60
68
|
```sh
|
|
61
69
|
pnpm install
|
|
@@ -67,23 +75,28 @@ pnpm run check:client
|
|
|
67
75
|
npm pack --dry-run --registry=https://registry.npmjs.org/
|
|
68
76
|
```
|
|
69
77
|
|
|
70
|
-
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.
|
|
71
79
|
|
|
72
|
-
##
|
|
80
|
+
## Repository layout
|
|
73
81
|
|
|
74
82
|
```text
|
|
75
|
-
src/ Host
|
|
76
|
-
skills/cloudq/
|
|
77
|
-
assets/cloudq.png Host
|
|
78
|
-
scripts/
|
|
79
|
-
tests/
|
|
80
|
-
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
|
|
81
89
|
```
|
|
82
90
|
|
|
83
|
-
##
|
|
91
|
+
## Release
|
|
92
|
+
|
|
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.
|
|
94
|
+
|
|
95
|
+
## More documentation
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
- Changelog: [`CHANGELOG.md`](CHANGELOG.md)
|
|
98
|
+
- Development & maintenance guide: [`DEVELOPMENT.md`](DEVELOPMENT.md)
|
|
86
99
|
|
|
87
|
-
##
|
|
100
|
+
## License
|
|
88
101
|
|
|
89
|
-
|
|
102
|
+
MIT. See [`LICENSE`](LICENSE).
|
package/lib/client.js
CHANGED
|
@@ -432,10 +432,6 @@ window.__ModuleLoader__.load({
|
|
|
432
432
|
font-size: 11px;
|
|
433
433
|
font-weight: 500;
|
|
434
434
|
}
|
|
435
|
-
.dsh-cloudq-pm__row-tag--self {
|
|
436
|
-
background: var(--dsw-alias-state-business-primary, #315efb);
|
|
437
|
-
color: #fff;
|
|
438
|
-
}
|
|
439
435
|
.dsh-cloudq-pm__row-tag--off {
|
|
440
436
|
background: var(--dsw-alias-state-danger, #d54941);
|
|
441
437
|
color: #fff;
|
|
@@ -1143,12 +1139,145 @@ body.dsh-cloudq-mode-active [class*=composerHero] [class*=root][class*=hero] {
|
|
|
1143
1139
|
margin-bottom: 16px;
|
|
1144
1140
|
}
|
|
1145
1141
|
.dsh-cloudq-usage__card {
|
|
1146
|
-
flex: 1;
|
|
1142
|
+
flex: 0 1 auto;
|
|
1143
|
+
min-width: 240px;
|
|
1147
1144
|
padding: 14px 16px;
|
|
1148
1145
|
border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
|
|
1149
1146
|
border-radius: 10px;
|
|
1150
1147
|
background: var(--dsw-alias-bg-sub, #f7f8fa);
|
|
1151
1148
|
}
|
|
1149
|
+
.dsh-cloudq-usage__card-value-row {
|
|
1150
|
+
display: flex;
|
|
1151
|
+
align-items: baseline;
|
|
1152
|
+
gap: 6px;
|
|
1153
|
+
flex-wrap: wrap;
|
|
1154
|
+
}
|
|
1155
|
+
.dsh-cloudq-usage__card-unit {
|
|
1156
|
+
font-size: 13px;
|
|
1157
|
+
color: var(--dsw-alias-label-secondary, #626a76);
|
|
1158
|
+
}
|
|
1159
|
+
.dsh-cloudq-usage__free-badge {
|
|
1160
|
+
position: relative;
|
|
1161
|
+
display: inline-flex;
|
|
1162
|
+
align-items: center;
|
|
1163
|
+
gap: 4px;
|
|
1164
|
+
padding: 1px 8px;
|
|
1165
|
+
border-radius: 999px;
|
|
1166
|
+
background: var(--dsw-alias-state-business-primary-bg, #e8efff);
|
|
1167
|
+
color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1168
|
+
font-size: 12px;
|
|
1169
|
+
cursor: help;
|
|
1170
|
+
}
|
|
1171
|
+
.dsh-cloudq-usage__free-badge .dsh-cloudq-tooltip {
|
|
1172
|
+
opacity: 0;
|
|
1173
|
+
visibility: hidden;
|
|
1174
|
+
transition: opacity .12s ease;
|
|
1175
|
+
}
|
|
1176
|
+
.dsh-cloudq-usage__free-badge:hover .dsh-cloudq-tooltip,
|
|
1177
|
+
.dsh-cloudq-usage__free-badge:focus-visible .dsh-cloudq-tooltip {
|
|
1178
|
+
opacity: 1;
|
|
1179
|
+
visibility: visible;
|
|
1180
|
+
}
|
|
1181
|
+
.dsh-cloudq-usage__range {
|
|
1182
|
+
display: flex;
|
|
1183
|
+
align-items: center;
|
|
1184
|
+
gap: 8px;
|
|
1185
|
+
flex-wrap: wrap;
|
|
1186
|
+
margin-bottom: 12px;
|
|
1187
|
+
}
|
|
1188
|
+
.dsh-cloudq-usage__range-picker {
|
|
1189
|
+
position: relative;
|
|
1190
|
+
}
|
|
1191
|
+
.dsh-cloudq-usage__range-btn {
|
|
1192
|
+
display: inline-flex;
|
|
1193
|
+
align-items: center;
|
|
1194
|
+
gap: 8px;
|
|
1195
|
+
height: 28px;
|
|
1196
|
+
padding: 0 10px;
|
|
1197
|
+
border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
|
|
1198
|
+
border-radius: 6px;
|
|
1199
|
+
background: var(--dsw-alias-bg-card, #fff);
|
|
1200
|
+
color: var(--dsw-alias-label-primary, #1d2129);
|
|
1201
|
+
font-size: 12px;
|
|
1202
|
+
cursor: pointer;
|
|
1203
|
+
transition: border-color .15s ease, box-shadow .15s ease;
|
|
1204
|
+
}
|
|
1205
|
+
.dsh-cloudq-usage__range-btn:hover {
|
|
1206
|
+
border-color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1207
|
+
}
|
|
1208
|
+
.dsh-cloudq-usage__range-picker.is-open .dsh-cloudq-usage__range-btn {
|
|
1209
|
+
border-color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1210
|
+
box-shadow: 0 0 0 2px rgba(49, 94, 251, .12);
|
|
1211
|
+
}
|
|
1212
|
+
.dsh-cloudq-usage__range-btn-chevron {
|
|
1213
|
+
font-size: 10px;
|
|
1214
|
+
color: var(--dsw-alias-label-tertiary, #9aa1ab);
|
|
1215
|
+
transition: transform .15s ease;
|
|
1216
|
+
}
|
|
1217
|
+
.dsh-cloudq-usage__range-picker.is-open .dsh-cloudq-usage__range-btn-chevron {
|
|
1218
|
+
transform: rotate(180deg);
|
|
1219
|
+
}
|
|
1220
|
+
.dsh-cloudq-usage__range-menu {
|
|
1221
|
+
position: absolute;
|
|
1222
|
+
z-index: 1400;
|
|
1223
|
+
top: calc(100% + 6px);
|
|
1224
|
+
left: 0;
|
|
1225
|
+
min-width: 136px;
|
|
1226
|
+
padding: 4px;
|
|
1227
|
+
border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
|
|
1228
|
+
border-radius: 8px;
|
|
1229
|
+
background: var(--dsw-alias-bg-card, #fff);
|
|
1230
|
+
box-shadow: 0 8px 24px rgba(29, 33, 41, .12);
|
|
1231
|
+
}
|
|
1232
|
+
.dsh-cloudq-usage__range-option {
|
|
1233
|
+
display: flex;
|
|
1234
|
+
align-items: center;
|
|
1235
|
+
gap: 4px;
|
|
1236
|
+
width: 100%;
|
|
1237
|
+
padding: 6px 8px;
|
|
1238
|
+
border: 0;
|
|
1239
|
+
border-radius: 5px;
|
|
1240
|
+
background: transparent;
|
|
1241
|
+
color: var(--dsw-alias-label-primary, #1d2129);
|
|
1242
|
+
font-size: 12px;
|
|
1243
|
+
text-align: left;
|
|
1244
|
+
cursor: pointer;
|
|
1245
|
+
}
|
|
1246
|
+
.dsh-cloudq-usage__range-option:hover {
|
|
1247
|
+
background: var(--dsw-alias-bg-sub, #f2f4f8);
|
|
1248
|
+
}
|
|
1249
|
+
.dsh-cloudq-usage__range-option.is-active {
|
|
1250
|
+
color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1251
|
+
font-weight: 500;
|
|
1252
|
+
}
|
|
1253
|
+
.dsh-cloudq-usage__range-option-check {
|
|
1254
|
+
width: 14px;
|
|
1255
|
+
flex: none;
|
|
1256
|
+
color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1257
|
+
visibility: hidden;
|
|
1258
|
+
}
|
|
1259
|
+
.dsh-cloudq-usage__range-option.is-active .dsh-cloudq-usage__range-option-check {
|
|
1260
|
+
visibility: visible;
|
|
1261
|
+
}
|
|
1262
|
+
.dsh-cloudq-usage__date {
|
|
1263
|
+
height: 28px;
|
|
1264
|
+
padding: 0 8px;
|
|
1265
|
+
border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
|
|
1266
|
+
border-radius: 6px;
|
|
1267
|
+
background: var(--dsw-alias-bg-card, #fff);
|
|
1268
|
+
color: var(--dsw-alias-label-primary, #1d2129);
|
|
1269
|
+
font-size: 12px;
|
|
1270
|
+
outline: none;
|
|
1271
|
+
transition: border-color .15s ease, box-shadow .15s ease;
|
|
1272
|
+
}
|
|
1273
|
+
.dsh-cloudq-usage__date:focus {
|
|
1274
|
+
border-color: var(--dsw-alias-state-business-primary, #315efb);
|
|
1275
|
+
box-shadow: 0 0 0 2px rgba(49, 94, 251, .12);
|
|
1276
|
+
}
|
|
1277
|
+
.dsh-cloudq-usage__range-error {
|
|
1278
|
+
font-size: 12px;
|
|
1279
|
+
color: var(--dsw-alias-state-danger, #d54941);
|
|
1280
|
+
}
|
|
1152
1281
|
.dsh-cloudq-usage__card-label {
|
|
1153
1282
|
font-size: 12px;
|
|
1154
1283
|
color: var(--dsw-alias-label-tertiary, #9aa1ab);
|
|
@@ -1751,22 +1880,58 @@ display: none;
|
|
|
1751
1880
|
const API_ARCH_DIRECTORIES = "/api/dsh-cloudq/architecture/directories";
|
|
1752
1881
|
const API_ARCH_LIST = "/api/dsh-cloudq/architecture/list";
|
|
1753
1882
|
const CLOUDQ_ARCH_FOLDER_KEY = "dsh-cloudq.architecture-folder-id";
|
|
1754
|
-
|
|
1883
|
+
const USAGE_RANGE_PRESETS = [
|
|
1884
|
+
{
|
|
1885
|
+
id: "3d",
|
|
1886
|
+
label: "最近 3 天",
|
|
1887
|
+
days: 3
|
|
1888
|
+
},
|
|
1889
|
+
{
|
|
1890
|
+
id: "7d",
|
|
1891
|
+
label: "最近 1 周",
|
|
1892
|
+
days: 7
|
|
1893
|
+
},
|
|
1894
|
+
{
|
|
1895
|
+
id: "30d",
|
|
1896
|
+
label: "最近 1 月",
|
|
1897
|
+
days: 30
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
id: "custom",
|
|
1901
|
+
label: "自定义",
|
|
1902
|
+
days: 0
|
|
1903
|
+
}
|
|
1904
|
+
];
|
|
1905
|
+
let usagePreset = "7d";
|
|
1906
|
+
let usageCustomStart = "";
|
|
1907
|
+
let usageCustomEnd = "";
|
|
1908
|
+
function formatUsageTimestamp(d) {
|
|
1909
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1910
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1911
|
+
}
|
|
1912
|
+
/** Resolve the current range to API timestamps; null when custom dates are incomplete. */
|
|
1913
|
+
function usageRangeTimestamps() {
|
|
1914
|
+
if (usagePreset === "custom") {
|
|
1915
|
+
if (!usageCustomStart || !usageCustomEnd) return null;
|
|
1916
|
+
return {
|
|
1917
|
+
start: `${usageCustomStart} 00:00:00`,
|
|
1918
|
+
end: `${usageCustomEnd} 23:59:59`
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
const preset = USAGE_RANGE_PRESETS.find((item) => item.id === usagePreset) ?? USAGE_RANGE_PRESETS[1];
|
|
1755
1922
|
const end = /* @__PURE__ */ new Date();
|
|
1756
|
-
const start = /* @__PURE__ */ new Date(end.getTime() - 6048e5);
|
|
1757
|
-
const fmt = (d) => {
|
|
1758
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
1759
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1760
|
-
};
|
|
1761
1923
|
return {
|
|
1762
|
-
start:
|
|
1763
|
-
end:
|
|
1924
|
+
start: formatUsageTimestamp(/* @__PURE__ */ new Date(end.getTime() - preset.days * 24 * 60 * 60 * 1e3)),
|
|
1925
|
+
end: formatUsageTimestamp(end)
|
|
1764
1926
|
};
|
|
1765
1927
|
}
|
|
1766
1928
|
function formatCredits(value) {
|
|
1767
1929
|
const num = typeof value === "number" ? value : Number(value);
|
|
1768
1930
|
if (!Number.isFinite(num)) return "—";
|
|
1769
|
-
return num.
|
|
1931
|
+
return num.toLocaleString("en-US", {
|
|
1932
|
+
minimumFractionDigits: 2,
|
|
1933
|
+
maximumFractionDigits: 2
|
|
1934
|
+
});
|
|
1770
1935
|
}
|
|
1771
1936
|
function formatDateTime(raw) {
|
|
1772
1937
|
if (!raw) return "—";
|
|
@@ -1828,93 +1993,221 @@ display: none;
|
|
|
1828
1993
|
let architecturePickerCleanup = null;
|
|
1829
1994
|
function renderUsageView() {
|
|
1830
1995
|
panelBody.textContent = "";
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1996
|
+
const overviewWrap = document.createElement("div");
|
|
1997
|
+
overviewWrap.className = "dsh-cloudq-usage__overview";
|
|
1998
|
+
const card = document.createElement("div");
|
|
1999
|
+
card.className = "dsh-cloudq-usage__card";
|
|
2000
|
+
const label = document.createElement("div");
|
|
2001
|
+
label.className = "dsh-cloudq-usage__card-label";
|
|
2002
|
+
label.textContent = "累计消耗 Credit";
|
|
2003
|
+
const valueRow = document.createElement("div");
|
|
2004
|
+
valueRow.className = "dsh-cloudq-usage__card-value-row";
|
|
2005
|
+
const value = document.createElement("span");
|
|
2006
|
+
value.className = "dsh-cloudq-usage__card-value";
|
|
2007
|
+
value.textContent = "…";
|
|
2008
|
+
const unit = document.createElement("span");
|
|
2009
|
+
unit.className = "dsh-cloudq-usage__card-unit";
|
|
2010
|
+
unit.textContent = "个";
|
|
2011
|
+
const freeBadge = document.createElement("span");
|
|
2012
|
+
freeBadge.className = "dsh-cloudq-usage__free-badge";
|
|
2013
|
+
freeBadge.textContent = "公测期免费 ⓘ";
|
|
2014
|
+
freeBadge.tabIndex = 0;
|
|
2015
|
+
const freeBadgeTip = document.createElement("span");
|
|
2016
|
+
freeBadgeTip.className = "dsh-cloudq-tooltip";
|
|
2017
|
+
freeBadgeTip.setAttribute("role", "tooltip");
|
|
2018
|
+
freeBadgeTip.textContent = "正式计费将提前 1 个月通知";
|
|
2019
|
+
freeBadge.appendChild(freeBadgeTip);
|
|
2020
|
+
valueRow.append(value, unit, freeBadge);
|
|
2021
|
+
card.append(label, valueRow);
|
|
2022
|
+
overviewWrap.appendChild(card);
|
|
2023
|
+
panelBody.appendChild(overviewWrap);
|
|
2024
|
+
const rangeBar = document.createElement("div");
|
|
2025
|
+
rangeBar.className = "dsh-cloudq-usage__range";
|
|
2026
|
+
const picker = document.createElement("div");
|
|
2027
|
+
picker.className = "dsh-cloudq-usage__range-picker";
|
|
2028
|
+
const pickerBtn = document.createElement("button");
|
|
2029
|
+
pickerBtn.type = "button";
|
|
2030
|
+
pickerBtn.className = "dsh-cloudq-usage__range-btn";
|
|
2031
|
+
pickerBtn.setAttribute("aria-haspopup", "listbox");
|
|
2032
|
+
pickerBtn.setAttribute("aria-expanded", "false");
|
|
2033
|
+
pickerBtn.setAttribute("aria-label", "用量时间范围");
|
|
2034
|
+
const pickerLabel = document.createElement("span");
|
|
2035
|
+
pickerLabel.className = "dsh-cloudq-usage__range-btn-label";
|
|
2036
|
+
const pickerChevron = document.createElement("span");
|
|
2037
|
+
pickerChevron.className = "dsh-cloudq-usage__range-btn-chevron";
|
|
2038
|
+
pickerChevron.textContent = "▾";
|
|
2039
|
+
pickerBtn.append(pickerLabel, pickerChevron);
|
|
2040
|
+
const menu = document.createElement("div");
|
|
2041
|
+
menu.className = "dsh-cloudq-usage__range-menu";
|
|
2042
|
+
menu.setAttribute("role", "listbox");
|
|
2043
|
+
menu.hidden = true;
|
|
2044
|
+
const optionButtons = /* @__PURE__ */ new Map();
|
|
2045
|
+
for (const preset of USAGE_RANGE_PRESETS) {
|
|
2046
|
+
const option = document.createElement("button");
|
|
2047
|
+
option.type = "button";
|
|
2048
|
+
option.className = "dsh-cloudq-usage__range-option";
|
|
2049
|
+
option.setAttribute("role", "option");
|
|
2050
|
+
const check = document.createElement("span");
|
|
2051
|
+
check.className = "dsh-cloudq-usage__range-option-check";
|
|
2052
|
+
check.textContent = "✓";
|
|
2053
|
+
const text = document.createElement("span");
|
|
2054
|
+
text.textContent = preset.label;
|
|
2055
|
+
option.append(check, text);
|
|
2056
|
+
option.addEventListener("click", () => {
|
|
2057
|
+
usagePreset = preset.id;
|
|
2058
|
+
syncPicker();
|
|
2059
|
+
closeMenu();
|
|
2060
|
+
syncCustomVisibility();
|
|
2061
|
+
loadUsage();
|
|
2062
|
+
});
|
|
2063
|
+
optionButtons.set(preset.id, option);
|
|
2064
|
+
menu.appendChild(option);
|
|
2065
|
+
}
|
|
2066
|
+
const syncPicker = () => {
|
|
2067
|
+
const active = USAGE_RANGE_PRESETS.find((item) => item.id === usagePreset) ?? USAGE_RANGE_PRESETS[1];
|
|
2068
|
+
pickerLabel.textContent = active.label;
|
|
2069
|
+
for (const [id, option] of optionButtons) {
|
|
2070
|
+
const isActive = id === active.id;
|
|
2071
|
+
option.classList.toggle("is-active", isActive);
|
|
2072
|
+
option.setAttribute("aria-selected", String(isActive));
|
|
1906
2073
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
2074
|
+
};
|
|
2075
|
+
const pickerAbort = new AbortController();
|
|
2076
|
+
const closeMenu = () => {
|
|
2077
|
+
menu.hidden = true;
|
|
2078
|
+
picker.classList.remove("is-open");
|
|
2079
|
+
pickerBtn.setAttribute("aria-expanded", "false");
|
|
2080
|
+
};
|
|
2081
|
+
pickerBtn.addEventListener("click", () => {
|
|
2082
|
+
const willOpen = menu.hidden;
|
|
2083
|
+
menu.hidden = !willOpen;
|
|
2084
|
+
picker.classList.toggle("is-open", willOpen);
|
|
2085
|
+
pickerBtn.setAttribute("aria-expanded", String(willOpen));
|
|
1917
2086
|
});
|
|
2087
|
+
document.addEventListener("mousedown", (event) => {
|
|
2088
|
+
if (!menu.isConnected) {
|
|
2089
|
+
pickerAbort.abort();
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
if (!picker.contains(event.target)) closeMenu();
|
|
2093
|
+
}, { signal: pickerAbort.signal });
|
|
2094
|
+
document.addEventListener("keydown", (event) => {
|
|
2095
|
+
if (!menu.isConnected) return;
|
|
2096
|
+
if (event.key === "Escape") closeMenu();
|
|
2097
|
+
}, { signal: pickerAbort.signal });
|
|
2098
|
+
syncPicker();
|
|
2099
|
+
const startDate = document.createElement("input");
|
|
2100
|
+
startDate.type = "date";
|
|
2101
|
+
startDate.className = "dsh-cloudq-usage__date";
|
|
2102
|
+
startDate.setAttribute("aria-label", "开始日期");
|
|
2103
|
+
startDate.value = usageCustomStart;
|
|
2104
|
+
const endDate = document.createElement("input");
|
|
2105
|
+
endDate.type = "date";
|
|
2106
|
+
endDate.className = "dsh-cloudq-usage__date";
|
|
2107
|
+
endDate.setAttribute("aria-label", "结束日期");
|
|
2108
|
+
endDate.value = usageCustomEnd;
|
|
2109
|
+
const rangeError = document.createElement("span");
|
|
2110
|
+
rangeError.className = "dsh-cloudq-usage__range-error";
|
|
2111
|
+
const syncCustomVisibility = () => {
|
|
2112
|
+
const custom = usagePreset === "custom";
|
|
2113
|
+
startDate.hidden = !custom;
|
|
2114
|
+
endDate.hidden = !custom;
|
|
2115
|
+
};
|
|
2116
|
+
const tableWrap = document.createElement("div");
|
|
2117
|
+
const loadUsage = () => {
|
|
2118
|
+
const range = usageRangeTimestamps();
|
|
2119
|
+
rangeError.textContent = "";
|
|
2120
|
+
if (!range) {
|
|
2121
|
+
tableWrap.textContent = "";
|
|
2122
|
+
const hint = document.createElement("div");
|
|
2123
|
+
hint.className = "dsh-cloudq-panel__hint";
|
|
2124
|
+
hint.textContent = "请选择开始日期与结束日期。";
|
|
2125
|
+
tableWrap.appendChild(hint);
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (usagePreset === "custom" && usageCustomStart > usageCustomEnd) {
|
|
2129
|
+
rangeError.textContent = "开始日期不能晚于结束日期。";
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
tableWrap.textContent = "";
|
|
2133
|
+
const hint = document.createElement("div");
|
|
2134
|
+
hint.className = "dsh-cloudq-panel__hint";
|
|
2135
|
+
hint.textContent = "正在加载用量…";
|
|
2136
|
+
tableWrap.appendChild(hint);
|
|
2137
|
+
cloudqRequest(`${API_USAGE}?start=${encodeURIComponent(range.start)}&end=${encodeURIComponent(range.end)}`).then((data) => {
|
|
2138
|
+
if (panelView !== "usage" || !panelExpanded) return;
|
|
2139
|
+
value.textContent = formatCredits(data.overview?.TotalCredits);
|
|
2140
|
+
const rows = Array.isArray(data.detail) ? data.detail : [];
|
|
2141
|
+
tableWrap.textContent = "";
|
|
2142
|
+
const table = document.createElement("table");
|
|
2143
|
+
table.className = "dsh-cloudq-usage__table";
|
|
2144
|
+
const thead = document.createElement("thead");
|
|
2145
|
+
const headerRow = document.createElement("tr");
|
|
2146
|
+
for (const heading of [
|
|
2147
|
+
"时间",
|
|
2148
|
+
"渠道",
|
|
2149
|
+
"内容",
|
|
2150
|
+
"积分"
|
|
2151
|
+
]) {
|
|
2152
|
+
const cell = document.createElement("th");
|
|
2153
|
+
cell.textContent = heading;
|
|
2154
|
+
headerRow.appendChild(cell);
|
|
2155
|
+
}
|
|
2156
|
+
thead.appendChild(headerRow);
|
|
2157
|
+
table.appendChild(thead);
|
|
2158
|
+
const tbody = document.createElement("tbody");
|
|
2159
|
+
if (rows.length === 0) {
|
|
2160
|
+
const tr = document.createElement("tr");
|
|
2161
|
+
const empty = document.createElement("td");
|
|
2162
|
+
empty.colSpan = 4;
|
|
2163
|
+
empty.className = "dsh-cloudq-usage__table__empty";
|
|
2164
|
+
empty.textContent = "当前时间范围内暂无用量记录";
|
|
2165
|
+
tr.appendChild(empty);
|
|
2166
|
+
tbody.appendChild(tr);
|
|
2167
|
+
} else for (const row of rows) {
|
|
2168
|
+
const tr = document.createElement("tr");
|
|
2169
|
+
const time = document.createElement("td");
|
|
2170
|
+
time.textContent = formatDateTime(row.StartTime);
|
|
2171
|
+
const channel = document.createElement("td");
|
|
2172
|
+
channel.textContent = row.Channel ?? "—";
|
|
2173
|
+
const message = document.createElement("td");
|
|
2174
|
+
const msgSpan = document.createElement("span");
|
|
2175
|
+
msgSpan.className = "dsh-cloudq-usage__table__message";
|
|
2176
|
+
msgSpan.textContent = row.Message ?? "";
|
|
2177
|
+
message.appendChild(msgSpan);
|
|
2178
|
+
const credits = document.createElement("td");
|
|
2179
|
+
credits.className = "dsh-cloudq-usage__table__credits";
|
|
2180
|
+
credits.textContent = formatCredits(row.Credits);
|
|
2181
|
+
tr.appendChild(time);
|
|
2182
|
+
tr.appendChild(channel);
|
|
2183
|
+
tr.appendChild(message);
|
|
2184
|
+
tr.appendChild(credits);
|
|
2185
|
+
tbody.appendChild(tr);
|
|
2186
|
+
}
|
|
2187
|
+
table.appendChild(tbody);
|
|
2188
|
+
tableWrap.appendChild(table);
|
|
2189
|
+
}).catch((error) => {
|
|
2190
|
+
if (panelView !== "usage" || !panelExpanded) return;
|
|
2191
|
+
tableWrap.textContent = "";
|
|
2192
|
+
const err = document.createElement("div");
|
|
2193
|
+
err.className = "dsh-cloudq-panel__error";
|
|
2194
|
+
err.textContent = cloudqPanelErrorText(error, "用量加载失败");
|
|
2195
|
+
tableWrap.appendChild(err);
|
|
2196
|
+
});
|
|
2197
|
+
};
|
|
2198
|
+
const onCustomDateChange = () => {
|
|
2199
|
+
usageCustomStart = startDate.value;
|
|
2200
|
+
usageCustomEnd = endDate.value;
|
|
2201
|
+
loadUsage();
|
|
2202
|
+
};
|
|
2203
|
+
startDate.addEventListener("change", onCustomDateChange);
|
|
2204
|
+
endDate.addEventListener("change", onCustomDateChange);
|
|
2205
|
+
picker.append(pickerBtn, menu);
|
|
2206
|
+
rangeBar.append(picker, startDate, endDate, rangeError);
|
|
2207
|
+
syncCustomVisibility();
|
|
2208
|
+
panelBody.appendChild(rangeBar);
|
|
2209
|
+
panelBody.appendChild(tableWrap);
|
|
2210
|
+
loadUsage();
|
|
1918
2211
|
}
|
|
1919
2212
|
const INSPIRATION_TABS = [
|
|
1920
2213
|
{
|
|
@@ -3206,17 +3499,10 @@ display: none;
|
|
|
3206
3499
|
className: "dsh-cloudq-pm__row-info",
|
|
3207
3500
|
children: [react_jsx_runtime.jsx("div", {
|
|
3208
3501
|
className: "dsh-cloudq-pm__row-name",
|
|
3209
|
-
children: [
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
children: "本插件"
|
|
3214
|
-
}) : null,
|
|
3215
|
-
!on ? react_jsx_runtime.jsx("span", {
|
|
3216
|
-
className: "dsh-cloudq-pm__row-tag dsh-cloudq-pm__row-tag--off",
|
|
3217
|
-
children: "已停用"
|
|
3218
|
-
}) : null
|
|
3219
|
-
]
|
|
3502
|
+
children: [plugin.name, !on ? react_jsx_runtime.jsx("span", {
|
|
3503
|
+
className: "dsh-cloudq-pm__row-tag dsh-cloudq-pm__row-tag--off",
|
|
3504
|
+
children: "已停用"
|
|
3505
|
+
}) : null]
|
|
3220
3506
|
}), react_jsx_runtime.jsx("div", {
|
|
3221
3507
|
className: "dsh-cloudq-pm__row-bundle",
|
|
3222
3508
|
children: `${plugin.id} · ${plugin.bundle}`
|