dsh-dock 0.16.0 → 0.17.0
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 +3 -2
- package/client.js +91 -93
- package/features/balance/host.js +124 -2
- package/features/balance/view.js +15 -4
- package/package.json +2 -2
- package/src/client.jsx +23 -30
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
| **用量记录** | 记录全部 LLM API 调用:秒级时间筛选、Token/费用统计(**可配置各模型单价、持久保存并按自填单价计费**,支持多段分时价与周末/节假日谷价,内置峰谷计价 + 官网价目自动同步作兜底)、分组汇总、明细检索与 CSV 导出 |
|
|
20
20
|
| **模型设置** | 编辑各 Provider 模型目录:输入类型(文本/图片 + 标注)与思考强度档位;图片能力可由你手动声明,新增 Provider 后进入页面自动重读目录,写回官方配置热生效 |
|
|
21
21
|
| **模型提供商** | 管理自定义模型 Provider 本身:新增 / 编辑 / 删除(OpenAI 兼容、Anthropic Messages 等)、API Key 写入本机凭据库、拉取上游 `{baseURL}/models` 列表;排版同官方模型管理页。**独立功能,可单独停用**——停用后写配置与出站拉取的路由一并关闭 |
|
|
22
|
-
| **模型余额** | 展示所有模型 Provider 账户余额(5
|
|
22
|
+
| **模型余额** | 展示所有模型 Provider 账户余额(5 分钟自动刷新);内置「DeepSeek 账号」官网余额(走宿主登录态,充值/赠送分列,不是 API Key 余额接口) |
|
|
23
23
|
| **任务动画** | 19 种任务运行动画(含星际远征、星云潮汐、曲速航道、量子雷达、星座网络、深海脉动、神经电路等),速度随任务活动联动,配置全部持久化 |
|
|
24
24
|
| **任务通知** | 任务完成 / 异常 / 需确认通知:页内卡片、6 种提示音、浏览器系统通知、钉钉/飞书群机器人推送(宿主直发,浏览器关着也能推);与动画互相独立、各自启停 |
|
|
25
25
|
| **运行状态** | 任务运行状态一览:进行中任务的阶段 / 耗时 / 回合 / 步骤 / 工具 / Token、等待确认提醒(等待的工具与说明)与最近完成记录(只读,与动画/通知共用同一份宿主侧追踪) |
|
|
@@ -170,6 +170,7 @@ dsh plugin --profile web add dsh-dock # 换回 npm 版
|
|
|
170
170
|
|
|
171
171
|
1. 打开功能坞 → 模型余额
|
|
172
172
|
2. 列表展示全部已配置 Provider 的余额/配额状态(每 5 分钟自动刷新,可手动「刷新」)
|
|
173
|
+
3. 选的是 **「DeepSeek 账号」**(官网账号,未填 API Key)时,余额走宿主同一条登录态查官网站内余额:充值余额与赠送余额分列展示,美元按 7.2 估算为人民币;**未登录或登录失效**显示「需登录」并给出官网控制台链接,重新登录后自动恢复;金额与官网页一致按「分」截断(不四舍五入,避免显示比官网多一分钱)
|
|
173
174
|
|
|
174
175
|
**任务动画**
|
|
175
176
|
|
|
@@ -283,7 +284,7 @@ dsh plugin --profile web add dsh-dock # 换回 npm 版
|
|
|
283
284
|
- **任务动画/任务通知/运行状态不生效**:三者的路由都在宿主进程内(动画/通知的配置持久化也在宿主),旧宿主需重启 `dsh web` 加载(面板页会提示「宿主进程是旧版本」)。另注意通知与运行状态已各自独立成菜单项,需在功能坞里分别开启「任务通知」「运行状态」。
|
|
284
285
|
- **用量记录空白**:确认 dsh 是较新版本且插件宿主已加载(查看启动日志含 `[dsh-dock] host half loaded`);旧宿主进程需重启 dsh web 加载新路由。
|
|
285
286
|
- **费用不准 / 与实际账单对不上**:金额是按单价估算的,官网抓取来的刊例价不代表你的实际计费。在用量记录页点「单价设置」(独立子弹窗,可最大化/拖动/缩放),按模型填真实单价并保存(持久化到宿主配置),费用会立即按自填单价重算;每个模型还支持多段分时价(跨零点 / 半点边界 / 每天·仅工作日·仅周末节假日)+ 全局法定节假日列表。计价优先级为「自定义单价 > 官网同步价 > 内置价 > 兜底价」,明细里走兜底价的金额会标「兜底」。
|
|
286
|
-
- **余额显示「需登录 / 无密钥」**:对应 Provider 未在 dsh
|
|
287
|
+
- **余额显示「需登录 / 无密钥」**:对应 Provider 未在 dsh 凭据中配置可查询余额的密钥,属正常状态提示。「DeepSeek 账号」显示「需登录」则是设置里那条官方账号没登录(或登录已失效),去设置 → 模型里登录一次即可。
|
|
287
288
|
- **切模型 / 打开历史会话卡住,报 `preset "…" failed to mount`**:这是该 **agent 预设**与 dsh 版本
|
|
288
289
|
的 schema 漂移,不是本插件的问题。预设目录在 `~/.dsh/.agent-presets/<id>/`,dsh 升级后预设里的
|
|
289
290
|
行可能不再合法(例如 `@deepseek-ai/dsh-persona` 在 0.1.5-rc.1 把 `config.text` 改为必填的
|
package/client.js
CHANGED
|
@@ -40,18 +40,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
40
40
|
));
|
|
41
41
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
42
42
|
|
|
43
|
-
// node_modules/qrcode/lib/can-promise.js
|
|
43
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/can-promise.js
|
|
44
44
|
var require_can_promise = __commonJS({
|
|
45
|
-
"node_modules/qrcode/lib/can-promise.js"(exports, module2) {
|
|
45
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/can-promise.js"(exports, module2) {
|
|
46
46
|
module2.exports = function() {
|
|
47
47
|
return typeof Promise === "function" && Promise.prototype && Promise.prototype.then;
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
// node_modules/qrcode/lib/core/utils.js
|
|
52
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/utils.js
|
|
53
53
|
var require_utils = __commonJS({
|
|
54
|
-
"node_modules/qrcode/lib/core/utils.js"(exports) {
|
|
54
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/utils.js"(exports) {
|
|
55
55
|
var toSJISFunction;
|
|
56
56
|
var CODEWORDS_COUNT = [
|
|
57
57
|
0,
|
|
@@ -128,9 +128,9 @@ var require_utils = __commonJS({
|
|
|
128
128
|
}
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
// node_modules/qrcode/lib/core/error-correction-level.js
|
|
131
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/error-correction-level.js
|
|
132
132
|
var require_error_correction_level = __commonJS({
|
|
133
|
-
"node_modules/qrcode/lib/core/error-correction-level.js"(exports) {
|
|
133
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/error-correction-level.js"(exports) {
|
|
134
134
|
exports.L = { bit: 1 };
|
|
135
135
|
exports.M = { bit: 0 };
|
|
136
136
|
exports.Q = { bit: 3 };
|
|
@@ -173,9 +173,9 @@ var require_error_correction_level = __commonJS({
|
|
|
173
173
|
}
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
// node_modules/qrcode/lib/core/bit-buffer.js
|
|
176
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/bit-buffer.js
|
|
177
177
|
var require_bit_buffer = __commonJS({
|
|
178
|
-
"node_modules/qrcode/lib/core/bit-buffer.js"(exports, module2) {
|
|
178
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/bit-buffer.js"(exports, module2) {
|
|
179
179
|
function BitBuffer() {
|
|
180
180
|
this.buffer = [];
|
|
181
181
|
this.length = 0;
|
|
@@ -208,9 +208,9 @@ var require_bit_buffer = __commonJS({
|
|
|
208
208
|
}
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
// node_modules/qrcode/lib/core/bit-matrix.js
|
|
211
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/bit-matrix.js
|
|
212
212
|
var require_bit_matrix = __commonJS({
|
|
213
|
-
"node_modules/qrcode/lib/core/bit-matrix.js"(exports, module2) {
|
|
213
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/bit-matrix.js"(exports, module2) {
|
|
214
214
|
function BitMatrix(size) {
|
|
215
215
|
if (!size || size < 1) {
|
|
216
216
|
throw new Error("BitMatrix size must be defined and greater than 0");
|
|
@@ -237,9 +237,9 @@ var require_bit_matrix = __commonJS({
|
|
|
237
237
|
}
|
|
238
238
|
});
|
|
239
239
|
|
|
240
|
-
// node_modules/qrcode/lib/core/alignment-pattern.js
|
|
240
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/alignment-pattern.js
|
|
241
241
|
var require_alignment_pattern = __commonJS({
|
|
242
|
-
"node_modules/qrcode/lib/core/alignment-pattern.js"(exports) {
|
|
242
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/alignment-pattern.js"(exports) {
|
|
243
243
|
var getSymbolSize = require_utils().getSymbolSize;
|
|
244
244
|
exports.getRowColCoords = function getRowColCoords(version) {
|
|
245
245
|
if (version === 1) return [];
|
|
@@ -272,9 +272,9 @@ var require_alignment_pattern = __commonJS({
|
|
|
272
272
|
}
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
-
// node_modules/qrcode/lib/core/finder-pattern.js
|
|
275
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/finder-pattern.js
|
|
276
276
|
var require_finder_pattern = __commonJS({
|
|
277
|
-
"node_modules/qrcode/lib/core/finder-pattern.js"(exports) {
|
|
277
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/finder-pattern.js"(exports) {
|
|
278
278
|
var getSymbolSize = require_utils().getSymbolSize;
|
|
279
279
|
var FINDER_PATTERN_SIZE = 7;
|
|
280
280
|
exports.getPositions = function getPositions(version) {
|
|
@@ -291,9 +291,9 @@ var require_finder_pattern = __commonJS({
|
|
|
291
291
|
}
|
|
292
292
|
});
|
|
293
293
|
|
|
294
|
-
// node_modules/qrcode/lib/core/mask-pattern.js
|
|
294
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/mask-pattern.js
|
|
295
295
|
var require_mask_pattern = __commonJS({
|
|
296
|
-
"node_modules/qrcode/lib/core/mask-pattern.js"(exports) {
|
|
296
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/mask-pattern.js"(exports) {
|
|
297
297
|
exports.Patterns = {
|
|
298
298
|
PATTERN000: 0,
|
|
299
299
|
PATTERN001: 1,
|
|
@@ -433,9 +433,9 @@ var require_mask_pattern = __commonJS({
|
|
|
433
433
|
}
|
|
434
434
|
});
|
|
435
435
|
|
|
436
|
-
// node_modules/qrcode/lib/core/error-correction-code.js
|
|
436
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/error-correction-code.js
|
|
437
437
|
var require_error_correction_code = __commonJS({
|
|
438
|
-
"node_modules/qrcode/lib/core/error-correction-code.js"(exports) {
|
|
438
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/error-correction-code.js"(exports) {
|
|
439
439
|
var ECLevel = require_error_correction_level();
|
|
440
440
|
var EC_BLOCKS_TABLE = [
|
|
441
441
|
// L M Q H
|
|
@@ -794,9 +794,9 @@ var require_error_correction_code = __commonJS({
|
|
|
794
794
|
}
|
|
795
795
|
});
|
|
796
796
|
|
|
797
|
-
// node_modules/qrcode/lib/core/galois-field.js
|
|
797
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/galois-field.js
|
|
798
798
|
var require_galois_field = __commonJS({
|
|
799
|
-
"node_modules/qrcode/lib/core/galois-field.js"(exports) {
|
|
799
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/galois-field.js"(exports) {
|
|
800
800
|
var EXP_TABLE = new Uint8Array(512);
|
|
801
801
|
var LOG_TABLE = new Uint8Array(256);
|
|
802
802
|
(function initTables() {
|
|
@@ -827,9 +827,9 @@ var require_galois_field = __commonJS({
|
|
|
827
827
|
}
|
|
828
828
|
});
|
|
829
829
|
|
|
830
|
-
// node_modules/qrcode/lib/core/polynomial.js
|
|
830
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/polynomial.js
|
|
831
831
|
var require_polynomial = __commonJS({
|
|
832
|
-
"node_modules/qrcode/lib/core/polynomial.js"(exports) {
|
|
832
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/polynomial.js"(exports) {
|
|
833
833
|
var GF = require_galois_field();
|
|
834
834
|
exports.mul = function mul(p1, p2) {
|
|
835
835
|
const coeff = new Uint8Array(p1.length + p2.length - 1);
|
|
@@ -863,9 +863,9 @@ var require_polynomial = __commonJS({
|
|
|
863
863
|
}
|
|
864
864
|
});
|
|
865
865
|
|
|
866
|
-
// node_modules/qrcode/lib/core/reed-solomon-encoder.js
|
|
866
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/reed-solomon-encoder.js
|
|
867
867
|
var require_reed_solomon_encoder = __commonJS({
|
|
868
|
-
"node_modules/qrcode/lib/core/reed-solomon-encoder.js"(exports, module2) {
|
|
868
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/reed-solomon-encoder.js"(exports, module2) {
|
|
869
869
|
var Polynomial = require_polynomial();
|
|
870
870
|
function ReedSolomonEncoder(degree) {
|
|
871
871
|
this.genPoly = void 0;
|
|
@@ -895,18 +895,18 @@ var require_reed_solomon_encoder = __commonJS({
|
|
|
895
895
|
}
|
|
896
896
|
});
|
|
897
897
|
|
|
898
|
-
// node_modules/qrcode/lib/core/version-check.js
|
|
898
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/version-check.js
|
|
899
899
|
var require_version_check = __commonJS({
|
|
900
|
-
"node_modules/qrcode/lib/core/version-check.js"(exports) {
|
|
900
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/version-check.js"(exports) {
|
|
901
901
|
exports.isValid = function isValid(version) {
|
|
902
902
|
return !isNaN(version) && version >= 1 && version <= 40;
|
|
903
903
|
};
|
|
904
904
|
}
|
|
905
905
|
});
|
|
906
906
|
|
|
907
|
-
// node_modules/qrcode/lib/core/regex.js
|
|
907
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/regex.js
|
|
908
908
|
var require_regex = __commonJS({
|
|
909
|
-
"node_modules/qrcode/lib/core/regex.js"(exports) {
|
|
909
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/regex.js"(exports) {
|
|
910
910
|
var numeric = "[0-9]+";
|
|
911
911
|
var alphanumeric = "[A-Z $%*+\\-./:]+";
|
|
912
912
|
var kanji = "(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+";
|
|
@@ -932,9 +932,9 @@ var require_regex = __commonJS({
|
|
|
932
932
|
}
|
|
933
933
|
});
|
|
934
934
|
|
|
935
|
-
// node_modules/qrcode/lib/core/mode.js
|
|
935
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/mode.js
|
|
936
936
|
var require_mode = __commonJS({
|
|
937
|
-
"node_modules/qrcode/lib/core/mode.js"(exports) {
|
|
937
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/mode.js"(exports) {
|
|
938
938
|
var VersionCheck = require_version_check();
|
|
939
939
|
var Regex = require_regex();
|
|
940
940
|
exports.NUMERIC = {
|
|
@@ -1013,9 +1013,9 @@ var require_mode = __commonJS({
|
|
|
1013
1013
|
}
|
|
1014
1014
|
});
|
|
1015
1015
|
|
|
1016
|
-
// node_modules/qrcode/lib/core/version.js
|
|
1016
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/version.js
|
|
1017
1017
|
var require_version = __commonJS({
|
|
1018
|
-
"node_modules/qrcode/lib/core/version.js"(exports) {
|
|
1018
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/version.js"(exports) {
|
|
1019
1019
|
var Utils = require_utils();
|
|
1020
1020
|
var ECCode = require_error_correction_code();
|
|
1021
1021
|
var ECLevel = require_error_correction_level();
|
|
@@ -1108,9 +1108,9 @@ var require_version = __commonJS({
|
|
|
1108
1108
|
}
|
|
1109
1109
|
});
|
|
1110
1110
|
|
|
1111
|
-
// node_modules/qrcode/lib/core/format-info.js
|
|
1111
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/format-info.js
|
|
1112
1112
|
var require_format_info = __commonJS({
|
|
1113
|
-
"node_modules/qrcode/lib/core/format-info.js"(exports) {
|
|
1113
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/format-info.js"(exports) {
|
|
1114
1114
|
var Utils = require_utils();
|
|
1115
1115
|
var G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0;
|
|
1116
1116
|
var G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1;
|
|
@@ -1126,9 +1126,9 @@ var require_format_info = __commonJS({
|
|
|
1126
1126
|
}
|
|
1127
1127
|
});
|
|
1128
1128
|
|
|
1129
|
-
// node_modules/qrcode/lib/core/numeric-data.js
|
|
1129
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/numeric-data.js
|
|
1130
1130
|
var require_numeric_data = __commonJS({
|
|
1131
|
-
"node_modules/qrcode/lib/core/numeric-data.js"(exports, module2) {
|
|
1131
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/numeric-data.js"(exports, module2) {
|
|
1132
1132
|
var Mode = require_mode();
|
|
1133
1133
|
function NumericData(data) {
|
|
1134
1134
|
this.mode = Mode.NUMERIC;
|
|
@@ -1161,9 +1161,9 @@ var require_numeric_data = __commonJS({
|
|
|
1161
1161
|
}
|
|
1162
1162
|
});
|
|
1163
1163
|
|
|
1164
|
-
// node_modules/qrcode/lib/core/alphanumeric-data.js
|
|
1164
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/alphanumeric-data.js
|
|
1165
1165
|
var require_alphanumeric_data = __commonJS({
|
|
1166
|
-
"node_modules/qrcode/lib/core/alphanumeric-data.js"(exports, module2) {
|
|
1166
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/alphanumeric-data.js"(exports, module2) {
|
|
1167
1167
|
var Mode = require_mode();
|
|
1168
1168
|
var ALPHA_NUM_CHARS = [
|
|
1169
1169
|
"0",
|
|
@@ -1240,9 +1240,9 @@ var require_alphanumeric_data = __commonJS({
|
|
|
1240
1240
|
}
|
|
1241
1241
|
});
|
|
1242
1242
|
|
|
1243
|
-
// node_modules/qrcode/lib/core/byte-data.js
|
|
1243
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/byte-data.js
|
|
1244
1244
|
var require_byte_data = __commonJS({
|
|
1245
|
-
"node_modules/qrcode/lib/core/byte-data.js"(exports, module2) {
|
|
1245
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/byte-data.js"(exports, module2) {
|
|
1246
1246
|
var Mode = require_mode();
|
|
1247
1247
|
function ByteData(data) {
|
|
1248
1248
|
this.mode = Mode.BYTE;
|
|
@@ -1270,9 +1270,9 @@ var require_byte_data = __commonJS({
|
|
|
1270
1270
|
}
|
|
1271
1271
|
});
|
|
1272
1272
|
|
|
1273
|
-
// node_modules/qrcode/lib/core/kanji-data.js
|
|
1273
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/kanji-data.js
|
|
1274
1274
|
var require_kanji_data = __commonJS({
|
|
1275
|
-
"node_modules/qrcode/lib/core/kanji-data.js"(exports, module2) {
|
|
1275
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/kanji-data.js"(exports, module2) {
|
|
1276
1276
|
var Mode = require_mode();
|
|
1277
1277
|
var Utils = require_utils();
|
|
1278
1278
|
function KanjiData(data) {
|
|
@@ -1309,9 +1309,9 @@ var require_kanji_data = __commonJS({
|
|
|
1309
1309
|
}
|
|
1310
1310
|
});
|
|
1311
1311
|
|
|
1312
|
-
// node_modules/dijkstrajs/dijkstra.js
|
|
1312
|
+
// .devdeps/node_modules/.pnpm/dijkstrajs@1.0.3/node_modules/dijkstrajs/dijkstra.js
|
|
1313
1313
|
var require_dijkstra = __commonJS({
|
|
1314
|
-
"node_modules/dijkstrajs/dijkstra.js"(exports, module2) {
|
|
1314
|
+
".devdeps/node_modules/.pnpm/dijkstrajs@1.0.3/node_modules/dijkstrajs/dijkstra.js"(exports, module2) {
|
|
1315
1315
|
"use strict";
|
|
1316
1316
|
var dijkstra = {
|
|
1317
1317
|
single_source_shortest_paths: function(graph, s, d) {
|
|
@@ -1410,9 +1410,9 @@ var require_dijkstra = __commonJS({
|
|
|
1410
1410
|
}
|
|
1411
1411
|
});
|
|
1412
1412
|
|
|
1413
|
-
// node_modules/qrcode/lib/core/segments.js
|
|
1413
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/segments.js
|
|
1414
1414
|
var require_segments = __commonJS({
|
|
1415
|
-
"node_modules/qrcode/lib/core/segments.js"(exports) {
|
|
1415
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/segments.js"(exports) {
|
|
1416
1416
|
var Mode = require_mode();
|
|
1417
1417
|
var NumericData = require_numeric_data();
|
|
1418
1418
|
var AlphanumericData = require_alphanumeric_data();
|
|
@@ -1596,9 +1596,9 @@ var require_segments = __commonJS({
|
|
|
1596
1596
|
}
|
|
1597
1597
|
});
|
|
1598
1598
|
|
|
1599
|
-
// node_modules/qrcode/lib/core/qrcode.js
|
|
1599
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/qrcode.js
|
|
1600
1600
|
var require_qrcode = __commonJS({
|
|
1601
|
-
"node_modules/qrcode/lib/core/qrcode.js"(exports) {
|
|
1601
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/qrcode.js"(exports) {
|
|
1602
1602
|
var Utils = require_utils();
|
|
1603
1603
|
var ECLevel = require_error_correction_level();
|
|
1604
1604
|
var BitBuffer = require_bit_buffer();
|
|
@@ -1857,9 +1857,9 @@ var require_qrcode = __commonJS({
|
|
|
1857
1857
|
}
|
|
1858
1858
|
});
|
|
1859
1859
|
|
|
1860
|
-
// node_modules/qrcode/lib/renderer/utils.js
|
|
1860
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/utils.js
|
|
1861
1861
|
var require_utils2 = __commonJS({
|
|
1862
|
-
"node_modules/qrcode/lib/renderer/utils.js"(exports) {
|
|
1862
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/utils.js"(exports) {
|
|
1863
1863
|
function hex2rgba(hex) {
|
|
1864
1864
|
if (typeof hex === "number") {
|
|
1865
1865
|
hex = hex.toString();
|
|
@@ -1937,9 +1937,9 @@ var require_utils2 = __commonJS({
|
|
|
1937
1937
|
}
|
|
1938
1938
|
});
|
|
1939
1939
|
|
|
1940
|
-
// node_modules/qrcode/lib/renderer/canvas.js
|
|
1940
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/canvas.js
|
|
1941
1941
|
var require_canvas = __commonJS({
|
|
1942
|
-
"node_modules/qrcode/lib/renderer/canvas.js"(exports) {
|
|
1942
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/canvas.js"(exports) {
|
|
1943
1943
|
var Utils = require_utils2();
|
|
1944
1944
|
function clearCanvas(ctx, canvas, size) {
|
|
1945
1945
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
@@ -1990,9 +1990,9 @@ var require_canvas = __commonJS({
|
|
|
1990
1990
|
}
|
|
1991
1991
|
});
|
|
1992
1992
|
|
|
1993
|
-
// node_modules/qrcode/lib/renderer/svg-tag.js
|
|
1993
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/svg-tag.js
|
|
1994
1994
|
var require_svg_tag = __commonJS({
|
|
1995
|
-
"node_modules/qrcode/lib/renderer/svg-tag.js"(exports) {
|
|
1995
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/svg-tag.js"(exports) {
|
|
1996
1996
|
var Utils = require_utils2();
|
|
1997
1997
|
function getColorAttrib(color, attrib) {
|
|
1998
1998
|
const alpha = color.a / 255;
|
|
@@ -2048,9 +2048,9 @@ var require_svg_tag = __commonJS({
|
|
|
2048
2048
|
}
|
|
2049
2049
|
});
|
|
2050
2050
|
|
|
2051
|
-
// node_modules/qrcode/lib/browser.js
|
|
2051
|
+
// .devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/browser.js
|
|
2052
2052
|
var require_browser = __commonJS({
|
|
2053
|
-
"node_modules/qrcode/lib/browser.js"(exports) {
|
|
2053
|
+
".devdeps/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/browser.js"(exports) {
|
|
2054
2054
|
var canPromise = require_can_promise();
|
|
2055
2055
|
var QRCode2 = require_qrcode();
|
|
2056
2056
|
var CanvasRenderer = require_canvas();
|
|
@@ -5114,6 +5114,7 @@ var ACCENT_PALETTE = ["#2563eb", "#0d9488", "#b45309", "#7c3aed", "#be185d", "#0
|
|
|
5114
5114
|
var CURATED_ACCENTS = {
|
|
5115
5115
|
"deepseek-official": "#2563eb",
|
|
5116
5116
|
deepseek: "#2563eb",
|
|
5117
|
+
"deepseek-account": "#2563eb",
|
|
5117
5118
|
"qwen-token-plan-cn": "#b45309",
|
|
5118
5119
|
fangzhou: "#7c3aed",
|
|
5119
5120
|
openai: "#15803d",
|
|
@@ -5132,6 +5133,13 @@ function accentOf(id) {
|
|
|
5132
5133
|
return ACCENT_PALETTE[h % ACCENT_PALETTE.length];
|
|
5133
5134
|
}
|
|
5134
5135
|
var fmt = (v) => v == null || v === "" ? "\u2014" : String(v);
|
|
5136
|
+
function moneyText(v) {
|
|
5137
|
+
if (v == null || v === "") return fmt(v);
|
|
5138
|
+
const n = Number(v);
|
|
5139
|
+
if (!Number.isFinite(n)) return fmt(v);
|
|
5140
|
+
const cents = Math.trunc(Number((n * 100).toFixed(6)));
|
|
5141
|
+
return (cents / 100).toFixed(2);
|
|
5142
|
+
}
|
|
5135
5143
|
function cnyBalanceInfo(info) {
|
|
5136
5144
|
if (!info) return info;
|
|
5137
5145
|
if (info.currency === "CNY") return Object.assign({}, info, { currency: "\u4EBA\u6C11\u5E01" });
|
|
@@ -5269,12 +5277,12 @@ function BalanceView(props) {
|
|
|
5269
5277
|
"div",
|
|
5270
5278
|
{ key: idx, className: "dkb-main" },
|
|
5271
5279
|
import_react8.default.createElement("span", { className: "dkb-main-label", style: { color: accent } }, i.currency),
|
|
5272
|
-
import_react8.default.createElement("span", { className: "dkb-main-value" },
|
|
5280
|
+
import_react8.default.createElement("span", { className: "dkb-main-value" }, moneyText(i.totalBalance)),
|
|
5273
5281
|
import_react8.default.createElement(
|
|
5274
5282
|
"span",
|
|
5275
5283
|
{ className: "dkb-main-parts" },
|
|
5276
|
-
i.grantedBalance != null ? import_react8.default.createElement("span", { className: "dkb-main-part", style: { color: C_GRANTED } }, "\u8D60\u9001 " +
|
|
5277
|
-
i.toppedUpBalance != null ? import_react8.default.createElement("span", { className: "dkb-main-part", style: { color: C_TOPUP } }, "\u5145\u503C " +
|
|
5284
|
+
i.grantedBalance != null ? import_react8.default.createElement("span", { className: "dkb-main-part", style: { color: C_GRANTED } }, "\u8D60\u9001 " + moneyText(i.grantedBalance)) : null,
|
|
5285
|
+
i.toppedUpBalance != null ? import_react8.default.createElement("span", { className: "dkb-main-part", style: { color: C_TOPUP } }, "\u5145\u503C " + moneyText(i.toppedUpBalance)) : null
|
|
5278
5286
|
)
|
|
5279
5287
|
);
|
|
5280
5288
|
})
|
|
@@ -5399,7 +5407,7 @@ function chipBalanceText(p) {
|
|
|
5399
5407
|
if (!info) return "\u4F59\u989D \u2026";
|
|
5400
5408
|
const cur = info.currency === "CNY" || info.currency === "\u4EBA\u6C11\u5E01\uFF08\u4F30\u7B97\uFF09" ? "\xA5" : "";
|
|
5401
5409
|
const v = Number(info.totalBalance);
|
|
5402
|
-
const s = Number.isFinite(v) ? v >= 1e4 ? (v / 1e4)
|
|
5410
|
+
const s = Number.isFinite(v) ? v >= 1e4 ? moneyText(v / 1e4) + "\u4E07" : moneyText(v) : fmt(info.totalBalance);
|
|
5403
5411
|
return "\u4F59\u989D " + cur + s;
|
|
5404
5412
|
}
|
|
5405
5413
|
function BalanceChip(props) {
|
|
@@ -13886,7 +13894,7 @@ var feature13 = {
|
|
|
13886
13894
|
};
|
|
13887
13895
|
|
|
13888
13896
|
// src/client.jsx
|
|
13889
|
-
var DOCK_VERSION = "0.
|
|
13897
|
+
var DOCK_VERSION = "0.17.0";
|
|
13890
13898
|
var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8, feature9, feature10, feature11, feature12, feature13];
|
|
13891
13899
|
var PLANNED_FEATURES = [];
|
|
13892
13900
|
var PLANNED_NOTES = {};
|
|
@@ -13962,16 +13970,15 @@ var SHELL_CSS = [
|
|
|
13962
13970
|
".dock-switch.on{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary);}",
|
|
13963
13971
|
".dock-badge{flex:none;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:3px 10px;font-size:12px;}",
|
|
13964
13972
|
".dock-body{border-top:1px solid var(--dsw-alias-border-l1);padding-top:8px;color:var(--dsw-alias-label-secondary);display:flex;flex-direction:column;gap:4px;}",
|
|
13965
|
-
//
|
|
13966
|
-
//
|
|
13967
|
-
//
|
|
13968
|
-
//
|
|
13969
|
-
".docke2-
|
|
13970
|
-
".docke2-
|
|
13971
|
-
".docke2-
|
|
13972
|
-
".docke2-
|
|
13973
|
-
".docke2-
|
|
13974
|
-
".docke2-mi-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
13973
|
+
// 侧栏入口按钮(docke2- 前缀)+ 功能坞弹出面板(dockm- 前缀,仿 dsh 设置的居中模态:遮罩 + 对话框,左导航 + 右内容)
|
|
13974
|
+
// 侧栏入口按钮(docke2- 前缀):与 dsh 设置按钮(.VOzbGW_trigger)几何逐条对齐——
|
|
13975
|
+
// 宽栏同为整宽左对齐行按钮(高 42、margin 4/-2、padding 0 10px 0 8px、圆角 12),窄栏同为 36 圆钮居中,
|
|
13976
|
+
// 使两按钮上下成列、左右边缘完全对齐(此前右对齐/叠放都会错位)。
|
|
13977
|
+
".docke2-btn{box-sizing:border-box;cursor:pointer;display:flex;align-items:center;gap:8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);border-radius:12px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
|
|
13978
|
+
".docke2-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
|
|
13979
|
+
".docke2-btn.docke2-on{color:var(--dk-accent);background:var(--dk-accent-soft);}",
|
|
13980
|
+
".docke2-btn.docke2-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;border-radius:50%;justify-content:center;gap:0;}",
|
|
13981
|
+
".docke2-label{white-space:nowrap;overflow:hidden;}",
|
|
13975
13982
|
// 遮罩固定用深色半透明:亮色主题下宿主 bg-layer-1 是纯白,白底遮罩会让弹窗与页面完全无层次
|
|
13976
13983
|
".dockm-backdrop{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;background:rgba(15,17,21,.45);backdrop-filter:blur(4px);pointer-events:auto;animation:dockm-fade .15s var(--ds-ease-in-out);}",
|
|
13977
13984
|
"@keyframes dockm-fade{from{opacity:0}to{opacity:1}}",
|
|
@@ -14115,27 +14122,18 @@ function DockIcon(props) {
|
|
|
14115
14122
|
import_react26.default.createElement("rect", { x: 9, y: 9, width: 4.4, height: 4.4, rx: 1.2 })
|
|
14116
14123
|
);
|
|
14117
14124
|
}
|
|
14118
|
-
function
|
|
14125
|
+
function DockEntry(props) {
|
|
14126
|
+
const wide = !!props.wide;
|
|
14119
14127
|
const [open, setOpen] = import_react26.default.useState(panelNav.open);
|
|
14120
14128
|
import_react26.default.useEffect(() => subscribePanel(() => setOpen(panelNav.open)), []);
|
|
14121
|
-
return import_react26.default.createElement(
|
|
14122
|
-
"button",
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
setPanelOpen(!open);
|
|
14130
|
-
}
|
|
14131
|
-
},
|
|
14132
|
-
import_react26.default.createElement(
|
|
14133
|
-
"span",
|
|
14134
|
-
{ className: "docke2-mi-icon" },
|
|
14135
|
-
import_react26.default.createElement(DockIcon, { size: 14 })
|
|
14136
|
-
),
|
|
14137
|
-
import_react26.default.createElement("span", { className: "docke2-mi-label" }, "\u529F\u80FD\u575E")
|
|
14138
|
-
);
|
|
14129
|
+
return import_react26.default.createElement("button", {
|
|
14130
|
+
type: "button",
|
|
14131
|
+
className: "docke2-btn" + (open ? " docke2-on" : "") + (wide ? "" : " docke2-rail"),
|
|
14132
|
+
title: "\u529F\u80FD\u575E",
|
|
14133
|
+
"aria-label": "\u529F\u80FD\u575E",
|
|
14134
|
+
"aria-expanded": open,
|
|
14135
|
+
onClick: () => setPanelOpen(!open)
|
|
14136
|
+
}, import_react26.default.createElement(DockIcon, { size: wide ? 16 : 18 }), wide ? import_react26.default.createElement("span", { className: "docke2-label" }, "\u529F\u80FD\u575E") : null);
|
|
14139
14137
|
}
|
|
14140
14138
|
function DockModal() {
|
|
14141
14139
|
const [nav, setNav] = import_react26.default.useState({ open: panelNav.open, active: panelNav.active, params: panelNav.params });
|
|
@@ -14550,9 +14548,9 @@ function apply(ctx) {
|
|
|
14550
14548
|
initFeatureState(BUILTIN_FEATURES.concat(PLANNED_FEATURES));
|
|
14551
14549
|
const slots = ctx.get("slots");
|
|
14552
14550
|
if (slots === void 0) return;
|
|
14553
|
-
slots.inject("
|
|
14554
|
-
{ name: "
|
|
14555
|
-
(props) => import_react26.default.createElement(
|
|
14551
|
+
slots.inject("sidebar.footer.action", () => slots.register(
|
|
14552
|
+
{ name: "sidebar.footer.action", id: "dsh-dock", order: 1, label: "\u529F\u80FD\u575E" },
|
|
14553
|
+
(props) => import_react26.default.createElement(DockEntry, props)
|
|
14556
14554
|
));
|
|
14557
14555
|
slots.inject("shell.overlay", () => slots.register(
|
|
14558
14556
|
{ name: "shell.overlay", id: "dsh-dock-panel", order: 21, label: "\u529F\u80FD\u575E\u9762\u677F" },
|
package/features/balance/host.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
// 在回环 webServer 上注册 /dsh-dock/balance 路由:枚举所有已配置的模型 Provider,
|
|
4
4
|
// 按余额策略查询各账号余额/配额,返回归一化 JSON 给浏览器半身(同源 fetch,无需 typert)。
|
|
5
5
|
// 余额策略表沿用 @wycto/dsh-balance-panel@0.1.1 的实现(MIT,同作者)。
|
|
6
|
+
// 「DeepSeek 账号」这条官方 Provider 没有 API Key 余额接口,余额在官网站内:
|
|
7
|
+
// 改走宿主 deepseekAccount 服务(设置里「DeepSeek 账号 · 已登录」的同一份登录态)。
|
|
6
8
|
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
9
|
+
import { APP_IDENTITY } from '@deepseek-ai/dsh-llm'
|
|
7
10
|
import { sendJson, walkPath, rejectUntrustedRequest } from '../../src/host-core.js'
|
|
8
11
|
|
|
9
12
|
/** deepseek-official 内置默认值(适配器默认配置的镜像,配置缺失时兜底)。 */
|
|
@@ -12,6 +15,13 @@ const DEEPSEEK_DEFAULTS = {
|
|
|
12
15
|
baseURL: 'https://api.deepseek.com',
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
/** 官方 DeepSeek 账号 Provider(llm-deepseek-account 注册的 id;余额来自官网账号,不是 API Key)。 */
|
|
19
|
+
const ACCOUNT_PROVIDER_ID = 'deepseek-account'
|
|
20
|
+
/** 账号 Provider 的中文显示名(宿主 entry 的 displayName 是英文 'DeepSeek Account')。 */
|
|
21
|
+
const ACCOUNT_PROVIDER_NAME = 'DeepSeek 账号'
|
|
22
|
+
/** 账号未登录 / 查询失败时的兜底控制台地址(正常从 getState().links 取)。 */
|
|
23
|
+
const ACCOUNT_FALLBACK_URL = 'https://platform.deepseek.com/usage'
|
|
24
|
+
|
|
15
25
|
function parseDeepSeek(body) {
|
|
16
26
|
const infos = body && Array.isArray(body.balance_infos) ? body.balance_infos : []
|
|
17
27
|
if (infos.length === 0) return { status: 'error', message: '无法识别的余额响应' }
|
|
@@ -129,6 +139,109 @@ function parseXai(body) {
|
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
141
|
|
|
142
|
+
/** 官网账号余额的 client metadata:Platform 用版本/语言/时区生成请求头
|
|
143
|
+
* (见 dsh 的 accountClientMetadata;时区取本机,正值=东经秒)。 */
|
|
144
|
+
function accountClientMetadata() {
|
|
145
|
+
return {
|
|
146
|
+
version: APP_IDENTITY.version,
|
|
147
|
+
locale: 'zh-CN',
|
|
148
|
+
timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** 金额字符串化:去浮点尾巴(0.1+0.2 这类),最多保留四位小数。 */
|
|
153
|
+
function moneyText(value) {
|
|
154
|
+
const n = Number(value)
|
|
155
|
+
return Number.isFinite(n) ? String(Math.round(n * 1e4) / 1e4) : '0'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 把官网账号的钱包投影成前端统一的 currency infos:
|
|
160
|
+
* normal_wallets(充值钱包,AccountDetails.balance.value)→ toppedUpBalance,
|
|
161
|
+
* bonus_wallets(赠送钱包)→ grantedBalance,total = 两者之和。
|
|
162
|
+
* 有非零币种时隐藏全零币种(否则国内账号会多出一张「人民币(估算)0」的美元卡);
|
|
163
|
+
* 钱包全空时兜底一张 CNY 0,避免前端出现空白卡片。
|
|
164
|
+
*/
|
|
165
|
+
function parseAccountWallets(detail) {
|
|
166
|
+
const byCurrency = new Map()
|
|
167
|
+
const add = (wallets, field) => {
|
|
168
|
+
for (const wallet of Array.isArray(wallets) ? wallets : []) {
|
|
169
|
+
if (!wallet || typeof wallet.currency !== 'string') continue
|
|
170
|
+
const amount = Number(wallet.balance)
|
|
171
|
+
if (!Number.isFinite(amount)) continue
|
|
172
|
+
const currency = wallet.currency.toUpperCase()
|
|
173
|
+
const slot = byCurrency.get(currency) || { currency, granted: 0, toppedUp: 0 }
|
|
174
|
+
slot[field] += amount
|
|
175
|
+
byCurrency.set(currency, slot)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
add(detail.value, 'toppedUp')
|
|
179
|
+
add(detail.bonusWallets, 'granted')
|
|
180
|
+
const order = ['CNY', 'USD']
|
|
181
|
+
let infos = Array.from(byCurrency.values())
|
|
182
|
+
.sort((a, b) => order.indexOf(a.currency) - order.indexOf(b.currency))
|
|
183
|
+
.map((s) => ({
|
|
184
|
+
currency: s.currency,
|
|
185
|
+
totalBalance: moneyText(s.toppedUp + s.granted),
|
|
186
|
+
grantedBalance: moneyText(s.granted),
|
|
187
|
+
toppedUpBalance: moneyText(s.toppedUp),
|
|
188
|
+
}))
|
|
189
|
+
const nonzero = infos.filter((info) => Number(info.totalBalance) !== 0)
|
|
190
|
+
if (nonzero.length > 0) infos = nonzero
|
|
191
|
+
else if (infos.length === 0) {
|
|
192
|
+
infos = [{ currency: 'CNY', totalBalance: '0', grantedBalance: '0', toppedUpBalance: '0' }]
|
|
193
|
+
} else {
|
|
194
|
+
// 全零:只留一张兜底卡(优先 CNY),别把 0 美元的「人民币(估算)」卡也摆出来
|
|
195
|
+
infos = [infos.find((info) => info.currency === 'CNY') || infos[0]]
|
|
196
|
+
}
|
|
197
|
+
return { status: 'ok', kind: 'currency', available: true, infos }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 查询「DeepSeek 账号」的官网余额(走宿主 deepseekAccount 服务,不是 HTTP API Key)。
|
|
202
|
+
* 未登录 / 登录失效都归为 login-required(前端显示「需登录」并给控制台链接),
|
|
203
|
+
* 平台失败归为 error(绝不把失败伪装成 0 余额)。
|
|
204
|
+
*/
|
|
205
|
+
async function queryOfficialAccountBalance(ctx) {
|
|
206
|
+
const fallbackUrl = ACCOUNT_FALLBACK_URL
|
|
207
|
+
let account
|
|
208
|
+
try {
|
|
209
|
+
account = ctx.get('deepseekAccount')
|
|
210
|
+
} catch {
|
|
211
|
+
account = undefined
|
|
212
|
+
}
|
|
213
|
+
if (!account || typeof account.getState !== 'function' || typeof account.getBalance !== 'function') {
|
|
214
|
+
return { status: 'unsupported', message: '当前宿主未挂载 DeepSeek 账号服务(需在设置里登录 DeepSeek 账号)' }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let state
|
|
218
|
+
try {
|
|
219
|
+
state = await account.getState()
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return { status: 'error', message: '读取 DeepSeek 账号状态失败:' + (e && e.message ? e.message : String(e)) }
|
|
222
|
+
}
|
|
223
|
+
const links = state && state.links ? state.links : {}
|
|
224
|
+
const consoleUrl = typeof links.usageUrl === 'string' && links.usageUrl ? links.usageUrl : fallbackUrl
|
|
225
|
+
if (!state || state.status !== 'credential-stored') {
|
|
226
|
+
return { status: 'login-required', consoleUrl, message: 'DeepSeek 账号未登录,登录后可查看官网余额' }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let detail
|
|
230
|
+
try {
|
|
231
|
+
detail = await account.getBalance(accountClientMetadata())
|
|
232
|
+
} catch (e) {
|
|
233
|
+
return { status: 'error', message: '查询官网余额失败:' + (e && e.message ? e.message : String(e)) }
|
|
234
|
+
}
|
|
235
|
+
// null = 凭证已失效/被移除(平台返回 401 时账号服务会清掉 grant,这里按需重新登录处理)
|
|
236
|
+
if (detail === null) {
|
|
237
|
+
return { status: 'login-required', consoleUrl, message: 'DeepSeek 账号登录已失效,请重新登录' }
|
|
238
|
+
}
|
|
239
|
+
if (!detail || detail.status !== 'ready') {
|
|
240
|
+
return { status: 'error', message: '官网余额查询失败(平台接口返回失败)' }
|
|
241
|
+
}
|
|
242
|
+
return parseAccountWallets(detail)
|
|
243
|
+
}
|
|
244
|
+
|
|
132
245
|
/** 策略表:key 为规范 provider id,aliases 为别名。 */
|
|
133
246
|
const STRATEGIES = {
|
|
134
247
|
deepseek: {
|
|
@@ -314,7 +427,10 @@ function collectProviders(ctx) {
|
|
|
314
427
|
|
|
315
428
|
out.set(e.provider, {
|
|
316
429
|
id: e.provider,
|
|
317
|
-
displayName: (profile && typeof profile.displayName === 'string' && profile.displayName)
|
|
430
|
+
displayName: (profile && typeof profile.displayName === 'string' && profile.displayName)
|
|
431
|
+
|| (e.provider === ACCOUNT_PROVIDER_ID ? ACCOUNT_PROVIDER_NAME : undefined)
|
|
432
|
+
|| e.displayName
|
|
433
|
+
|| e.provider,
|
|
318
434
|
apiKeyEnv,
|
|
319
435
|
baseURL,
|
|
320
436
|
api,
|
|
@@ -328,7 +444,7 @@ function collectProviders(ctx) {
|
|
|
328
444
|
const isDeepseek = sel.provider === 'deepseek-official' || sel.provider === 'deepseek'
|
|
329
445
|
out.set(sel.provider, {
|
|
330
446
|
id: sel.provider,
|
|
331
|
-
displayName: sel.provider,
|
|
447
|
+
displayName: sel.provider === ACCOUNT_PROVIDER_ID ? ACCOUNT_PROVIDER_NAME : sel.provider,
|
|
332
448
|
apiKeyEnv: isDeepseek ? DEEPSEEK_DEFAULTS.apiKeyEnv : undefined,
|
|
333
449
|
baseURL: isDeepseek ? DEEPSEEK_DEFAULTS.baseURL : undefined,
|
|
334
450
|
api: isDeepseek ? 'deepseek' : undefined,
|
|
@@ -405,6 +521,12 @@ async function queryProviderBalance(ctx, p) {
|
|
|
405
521
|
}
|
|
406
522
|
}
|
|
407
523
|
|
|
524
|
+
// 0) 官方 DeepSeek 账号:余额在官网站内,走宿主 deepseekAccount 服务(没有 API Key 余额接口)
|
|
525
|
+
if (String(p.id).toLowerCase() === ACCOUNT_PROVIDER_ID) {
|
|
526
|
+
entry.balance = await queryOfficialAccountBalance(ctx)
|
|
527
|
+
return entry
|
|
528
|
+
}
|
|
529
|
+
|
|
408
530
|
// 1) 自定义 balance 配置优先
|
|
409
531
|
const custom = p.profile && typeof p.profile.balance === 'object' && p.profile.balance !== null
|
|
410
532
|
? p.profile.balance
|
package/features/balance/view.js
CHANGED
|
@@ -10,6 +10,7 @@ const ACCENT_PALETTE = ['#2563eb', '#0d9488', '#b45309', '#7c3aed', '#be185d', '
|
|
|
10
10
|
const CURATED_ACCENTS = {
|
|
11
11
|
'deepseek-official': '#2563eb',
|
|
12
12
|
deepseek: '#2563eb',
|
|
13
|
+
'deepseek-account': '#2563eb',
|
|
13
14
|
'qwen-token-plan-cn': '#b45309',
|
|
14
15
|
fangzhou: '#7c3aed',
|
|
15
16
|
openai: '#15803d',
|
|
@@ -32,6 +33,16 @@ function accentOf(id) {
|
|
|
32
33
|
return ACCENT_PALETTE[h % ACCENT_PALETTE.length];
|
|
33
34
|
}
|
|
34
35
|
const fmt = (v) => (v == null || v === '' ? '—' : String(v));
|
|
36
|
+
// 金额展示统一按「分」截断(不四舍五入)并固定两位小数,与官网 platform.deepseek.com 的口径一致:
|
|
37
|
+
// 账号真实余额是 77.6772,官网显示 ¥77.67,四舍五入会显示成 77.68——比官网多一分钱(实际踩到过)。
|
|
38
|
+
// 先 toFixed(6) 抹掉二进制浮点误差(如 0.29*100 = 28.999…)再向零截断。
|
|
39
|
+
function moneyText(v) {
|
|
40
|
+
if (v == null || v === '') return fmt(v);
|
|
41
|
+
const n = Number(v);
|
|
42
|
+
if (!Number.isFinite(n)) return fmt(v);
|
|
43
|
+
const cents = Math.trunc(Number((n * 100).toFixed(6)));
|
|
44
|
+
return (cents / 100).toFixed(2);
|
|
45
|
+
}
|
|
35
46
|
function cnyBalanceInfo(info) {
|
|
36
47
|
if (!info) return info;
|
|
37
48
|
if (info.currency === 'CNY') return Object.assign({}, info, { currency: '人民币' });
|
|
@@ -147,10 +158,10 @@ function BalanceView(props) {
|
|
|
147
158
|
const i = cnyBalanceInfo(rawInfo);
|
|
148
159
|
return react.createElement("div", { key: idx, className: "dkb-main" },
|
|
149
160
|
react.createElement("span", { className: "dkb-main-label", style: { color: accent } }, i.currency),
|
|
150
|
-
react.createElement("span", { className: "dkb-main-value" },
|
|
161
|
+
react.createElement("span", { className: "dkb-main-value" }, moneyText(i.totalBalance)),
|
|
151
162
|
react.createElement("span", { className: "dkb-main-parts" },
|
|
152
|
-
i.grantedBalance != null ? react.createElement("span", { className: "dkb-main-part", style: { color: C_GRANTED } }, "赠送 " +
|
|
153
|
-
i.toppedUpBalance != null ? react.createElement("span", { className: "dkb-main-part", style: { color: C_TOPUP } }, "充值 " +
|
|
163
|
+
i.grantedBalance != null ? react.createElement("span", { className: "dkb-main-part", style: { color: C_GRANTED } }, "赠送 " + moneyText(i.grantedBalance)) : null,
|
|
164
|
+
i.toppedUpBalance != null ? react.createElement("span", { className: "dkb-main-part", style: { color: C_TOPUP } }, "充值 " + moneyText(i.toppedUpBalance)) : null));
|
|
154
165
|
}));
|
|
155
166
|
} else if (b && b.status === "login-required" && b.consoleUrl) {
|
|
156
167
|
balBody = react.createElement("div", { className: "dkb-main" },
|
|
@@ -265,7 +276,7 @@ function chipBalanceText(p) {
|
|
|
265
276
|
if (!info) return "余额 …";
|
|
266
277
|
const cur = info.currency === "CNY" || info.currency === "人民币(估算)" ? "¥" : "";
|
|
267
278
|
const v = Number(info.totalBalance);
|
|
268
|
-
const s = Number.isFinite(v) ? (v >= 10000 ? (v / 10000)
|
|
279
|
+
const s = Number.isFinite(v) ? (v >= 10000 ? moneyText(v / 10000) + "万" : moneyText(v)) : fmt(info.totalBalance);
|
|
269
280
|
return "余额 " + cur + s;
|
|
270
281
|
}
|
|
271
282
|
function BalanceChip(props) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-dock",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "DeepSeek Harness 功能坞插件(dsh-dock):模块化功能中枢——每个功能一个 features/ 模块(可单独提取成包发布,又可经 dockBridge 装回面板)。含用量记录(LLM 调用记账/费用统计,支持各模型自定义单价、多段分时价与周末/节假日谷价)、模型设置、图片理解代理、模型余额、任务动画(19 种运行动画 + 桌面伙伴 3D 场景)、任务通知(完成/异常/需确认通知、提示音、系统通知、钉钉/飞书推送)、运行状态(进行中任务与最近完成一览)、语音朗读(浏览器 TTS 读回复:回合下方喇叭按钮 + 自动朗读);侧栏入口 + 可拖拽弹窗(最大化/最小化/缩放)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build:client": "node scripts/build-client.mjs",
|
|
45
45
|
"test:client": "node scripts/test-client-views.mjs",
|
|
46
|
-
"test:host": "node scripts/test-task-host.mjs && node scripts/test-tokenlog-host.mjs && node scripts/test-route-trust-host.mjs && node scripts/test-tts-host.mjs && node scripts/test-legacy-import.mjs && node scripts/test-mobile-host.mjs && node scripts/test-mobile-relay-host.mjs && node scripts/test-mobile-relay.mjs && node scripts/test-welcome-ack.mjs",
|
|
46
|
+
"test:host": "node scripts/test-task-host.mjs && node scripts/test-tokenlog-host.mjs && node scripts/test-balance-host.mjs && node scripts/test-route-trust-host.mjs && node scripts/test-tts-host.mjs && node scripts/test-legacy-import.mjs && node scripts/test-mobile-host.mjs && node scripts/test-mobile-relay-host.mjs && node scripts/test-mobile-relay.mjs && node scripts/test-welcome-ack.mjs",
|
|
47
47
|
"dev:client": "node scripts/watch-client.mjs",
|
|
48
48
|
"dev:link": "node scripts/dev-link-deps.mjs"
|
|
49
49
|
},
|
package/src/client.jsx
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// 在浏览器端 require("dsh-dock").dockBridge.register(def) 把自己的视图注册进本面板;
|
|
9
9
|
// dock 未安装时该包走自己的独立面板——「单独成包发布」与「完美集成功能坞」两全。
|
|
10
10
|
//
|
|
11
|
-
// 面板挂载在 settings.section:设置 →
|
|
12
|
-
//
|
|
11
|
+
// 面板挂载在 settings.section:设置 → 功能坞;侧栏底部另有「功能坞」入口按钮
|
|
12
|
+
// (sidebar.footer.action),点击弹出居中功能面板(shell.overlay)。
|
|
13
13
|
// 弹层窗口:默认大窗,支持最大化/最小化、标题栏拖动、右下角缩放(几何页内记忆)。
|
|
14
14
|
import react from "react";
|
|
15
15
|
import {
|
|
@@ -33,7 +33,7 @@ import { feature as fMobileRelay } from "../features/mobile-relay/view.jsx";
|
|
|
33
33
|
import { feature as fMobile } from "../features/mobile/view.js";
|
|
34
34
|
|
|
35
35
|
const name = "dsh-dock";
|
|
36
|
-
const DOCK_VERSION = "0.
|
|
36
|
+
const DOCK_VERSION = "0.17.0";
|
|
37
37
|
|
|
38
38
|
// ---- 内置功能注册表:新功能 = features/<id>/ 加模块 + 这里 import 一行 ----
|
|
39
39
|
const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fProviderConfig, fHeartbeat, fTheme, fBalance, fAnimation, fNotify, fRunState, fTts, fGames, fMobileRelay, fMobile];
|
|
@@ -125,16 +125,15 @@ const SHELL_CSS = [
|
|
|
125
125
|
".dock-switch.on{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary);}",
|
|
126
126
|
".dock-badge{flex:none;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:3px 10px;font-size:12px;}",
|
|
127
127
|
".dock-body{border-top:1px solid var(--dsw-alias-border-l1);padding-top:8px;color:var(--dsw-alias-label-secondary);display:flex;flex-direction:column;gap:4px;}",
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
".docke2-
|
|
133
|
-
".docke2-
|
|
134
|
-
".docke2-
|
|
135
|
-
".docke2-
|
|
136
|
-
".docke2-
|
|
137
|
-
".docke2-mi-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
128
|
+
// 侧栏入口按钮(docke2- 前缀)+ 功能坞弹出面板(dockm- 前缀,仿 dsh 设置的居中模态:遮罩 + 对话框,左导航 + 右内容)
|
|
129
|
+
// 侧栏入口按钮(docke2- 前缀):与 dsh 设置按钮(.VOzbGW_trigger)几何逐条对齐——
|
|
130
|
+
// 宽栏同为整宽左对齐行按钮(高 42、margin 4/-2、padding 0 10px 0 8px、圆角 12),窄栏同为 36 圆钮居中,
|
|
131
|
+
// 使两按钮上下成列、左右边缘完全对齐(此前右对齐/叠放都会错位)。
|
|
132
|
+
".docke2-btn{box-sizing:border-box;cursor:pointer;display:flex;align-items:center;gap:8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);border-radius:12px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
|
|
133
|
+
".docke2-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
|
|
134
|
+
".docke2-btn.docke2-on{color:var(--dk-accent);background:var(--dk-accent-soft);}",
|
|
135
|
+
".docke2-btn.docke2-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;border-radius:50%;justify-content:center;gap:0;}",
|
|
136
|
+
".docke2-label{white-space:nowrap;overflow:hidden;}",
|
|
138
137
|
// 遮罩固定用深色半透明:亮色主题下宿主 bg-layer-1 是纯白,白底遮罩会让弹窗与页面完全无层次
|
|
139
138
|
".dockm-backdrop{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;background:rgba(15,17,21,.45);backdrop-filter:blur(4px);pointer-events:auto;animation:dockm-fade .15s var(--ds-ease-in-out);}",
|
|
140
139
|
"@keyframes dockm-fade{from{opacity:0}to{opacity:1}}",
|
|
@@ -303,24 +302,18 @@ function DockIcon(props) {
|
|
|
303
302
|
react.createElement("rect", { x: 9, y: 9, width: 4.4, height: 4.4, rx: 1.2 }));
|
|
304
303
|
}
|
|
305
304
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
// dismiss()(关闭菜单,宿主负责焦点回还),再切换功能坞弹层;它不是 disclosure 触发器,
|
|
309
|
-
// 不带 aria-expanded/aria-haspopup。开合状态与旧侧栏入口同源:panelNav.open + subscribePanel。
|
|
310
|
-
function DockMenuItem(props) {
|
|
305
|
+
function DockEntry(props) {
|
|
306
|
+
const wide = !!props.wide;
|
|
311
307
|
const [open, setOpen] = react.useState(panelNav.open);
|
|
312
308
|
react.useEffect(() => subscribePanel(() => setOpen(panelNav.open)), []);
|
|
313
309
|
return react.createElement("button", {
|
|
314
310
|
type: "button",
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}, react.createElement("span", { className: "docke2-mi-icon" },
|
|
322
|
-
react.createElement(DockIcon, { size: 14 })),
|
|
323
|
-
react.createElement("span", { className: "docke2-mi-label" }, "功能坞"));
|
|
311
|
+
className: "docke2-btn" + (open ? " docke2-on" : "") + (wide ? "" : " docke2-rail"),
|
|
312
|
+
title: "功能坞",
|
|
313
|
+
"aria-label": "功能坞",
|
|
314
|
+
"aria-expanded": open,
|
|
315
|
+
onClick: () => setPanelOpen(!open)
|
|
316
|
+
}, react.createElement(DockIcon, { size: wide ? 16 : 18 }), wide ? react.createElement("span", { className: "docke2-label" }, "功能坞") : null);
|
|
324
317
|
}
|
|
325
318
|
|
|
326
319
|
// ---- 功能坞弹出面板:shell.overlay(仿 dsh 设置:居中模态 = 遮罩 + 对话框) ----
|
|
@@ -691,9 +684,9 @@ export function apply(ctx) {
|
|
|
691
684
|
const slots = ctx.get("slots");
|
|
692
685
|
if (slots === undefined) return;
|
|
693
686
|
|
|
694
|
-
slots.inject("
|
|
695
|
-
{ name: "
|
|
696
|
-
(props) => react.createElement(
|
|
687
|
+
slots.inject("sidebar.footer.action", () => slots.register(
|
|
688
|
+
{ name: "sidebar.footer.action", id: "dsh-dock", order: 1, label: "功能坞" },
|
|
689
|
+
(props) => react.createElement(DockEntry, props)));
|
|
697
690
|
slots.inject("shell.overlay", () => slots.register(
|
|
698
691
|
{ name: "shell.overlay", id: "dsh-dock-panel", order: 21, label: "功能坞面板" },
|
|
699
692
|
() => react.createElement(DockModal, null)));
|