@ychris12138/dsh-usage-stats 0.2.8 → 0.2.9
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 +6 -6
- package/lib/client.js +1 -1
- package/package.json +1 -1
- package/scripts/install.mjs +38 -9
package/README.md
CHANGED
|
@@ -53,8 +53,8 @@ dsh plugin --profile web add "github:Ychris12138/dsh-usage-stats"
|
|
|
53
53
|
升级或卸载:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
dsh plugin --profile web update dsh-usage-stats
|
|
57
|
-
dsh plugin --profile web remove dsh-usage-stats
|
|
56
|
+
dsh plugin --profile web update "@ychris12138/dsh-usage-stats"
|
|
57
|
+
dsh plugin --profile web remove "@ychris12138/dsh-usage-stats"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
<details>
|
|
@@ -66,7 +66,7 @@ PowerShell、命令提示符和 macOS/Linux 终端使用同一条命令:
|
|
|
66
66
|
npx --yes github:Ychris12138/dsh-usage-stats
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
安装器会把运行文件复制到 `~/.dsh/profiles/node_modules/dsh-usage-stats`,并在 `profiles/web/cordis.patch.yml`
|
|
69
|
+
安装器会把运行文件复制到 `~/.dsh/profiles/node_modules/@ychris12138/dsh-usage-stats`,并在 `profiles/web/cordis.patch.yml` 中以带引号的 scoped identity 幂等启用插件。重复运行即可更新,不会重复追加配置;旧版 `name: dsh-usage-stats` 和未加引号的 `name: @ychris12138/dsh-usage-stats` 会自动迁移。设置了 `DSH_HOME` 时使用该目录。
|
|
70
70
|
|
|
71
71
|
`dsh plugin` 与 `npx` 是两条独立安装路径,请选择其中一种;不要同时保留手工 Cordis entry 和 bundle 注册,否则会重复挂载。
|
|
72
72
|
|
|
@@ -147,7 +147,7 @@ Z.ai 全球区使用 `api.z.ai`,中国区使用 `open.bigmodel.cn`。MiniMax
|
|
|
147
147
|
|
|
148
148
|
### New API、Sub2API 与自定义 monitor
|
|
149
149
|
|
|
150
|
-
在现有 `name: dsh-usage-stats` Cordis entry 下合并 `config`,不要追加第二个插件 entry。monitor 键必须是 Harness 中真实存在的 provider id;未知 provider、adapter 或非法映射会在路由和 timer 注册前阻止插件启动。例外:monitor 同时显式提供 `usageBaseURL` 与 `credentialRef` 时视为自包含,会在 provider 注册可见前临时物化为 provider(适用于 Harness 设置页里后加载的 provider),此时不要求该 provider 已出现在注册表中。
|
|
150
|
+
在现有 `name: "@ychris12138/dsh-usage-stats"` Cordis entry 下合并 `config`,不要追加第二个插件 entry。monitor 键必须是 Harness 中真实存在的 provider id;未知 provider、adapter 或非法映射会在路由和 timer 注册前阻止插件启动。例外:monitor 同时显式提供 `usageBaseURL` 与 `credentialRef` 时视为自包含,会在 provider 注册可见前临时物化为 provider(适用于 Harness 设置页里后加载的 provider),此时不要求该 provider 已出现在注册表中。
|
|
151
151
|
|
|
152
152
|
<details>
|
|
153
153
|
<summary><strong>展开 monitor 配置示例</strong></summary>
|
|
@@ -158,7 +158,7 @@ New API 默认用 provider 推理 Token 查询 `/api/usage/token/`,并从 `/ap
|
|
|
158
158
|
# ~/.dsh/profiles/web/cordis.patch.yml
|
|
159
159
|
- insert:
|
|
160
160
|
- id: usage-stats
|
|
161
|
-
name: dsh-usage-stats
|
|
161
|
+
name: "@ychris12138/dsh-usage-stats"
|
|
162
162
|
config:
|
|
163
163
|
monitors:
|
|
164
164
|
relay-a: # Harness provider id
|
|
@@ -264,7 +264,7 @@ Constraints:
|
|
|
264
264
|
|
|
265
265
|
Procedure:
|
|
266
266
|
1. Confirm node, npx, and dsh are available.
|
|
267
|
-
2. Prefer `dsh plugin --profile web update dsh-usage-stats` when already installed; otherwise use `dsh plugin --profile web add "github:Ychris12138/dsh-usage-stats"`.
|
|
267
|
+
2. Prefer `dsh plugin --profile web update "@ychris12138/dsh-usage-stats"` when already installed; otherwise use `dsh plugin --profile web add "github:Ychris12138/dsh-usage-stats"`.
|
|
268
268
|
3. If unavailable, use: npx --yes github:Ychris12138/dsh-usage-stats
|
|
269
269
|
4. Do not combine bundle installation with an existing manual dsh-usage-stats Cordis entry.
|
|
270
270
|
5. For npx, require a verified package and exactly one Cordis entry, then run again with --check.
|
package/lib/client.js
CHANGED
|
@@ -139,7 +139,7 @@ window.__ModuleLoader__.load({
|
|
|
139
139
|
const tagId = "dsh-usage-stats/UsageStats.module.css";
|
|
140
140
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
141
141
|
const tag = document.createElement("style");
|
|
142
|
-
tag.dataset.plugin = "dsh-usage-stats";
|
|
142
|
+
tag.dataset.plugin = "@ychris12138/dsh-usage-stats";
|
|
143
143
|
tag.dataset.pluginCss = tagId;
|
|
144
144
|
tag.textContent = css;
|
|
145
145
|
document.head.appendChild(tag);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ychris12138/dsh-usage-stats",
|
|
3
3
|
"description": "Token usage heatmap, provider balances, and subscription quotas for the dsh web GUI",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.9",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/Ychris12138/dsh-usage-stats.git"
|
package/scripts/install.mjs
CHANGED
|
@@ -33,13 +33,24 @@ Set DSH_HOME to override the default ~/.dsh location.`);
|
|
|
33
33
|
const sourceRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
34
34
|
const sourcePackage = JSON.parse(await readFile(join(sourceRoot, "package.json"), "utf8"));
|
|
35
35
|
const dshHome = process.env.DSH_HOME ?? join(homedir(), ".dsh");
|
|
36
|
-
const
|
|
36
|
+
const packagePath = sourcePackage.name.split("/");
|
|
37
|
+
if (packagePath.some((part) => part === "" || part === "." || part === "..")) {
|
|
38
|
+
throw new Error(`invalid package name: ${sourcePackage.name}`);
|
|
39
|
+
}
|
|
40
|
+
const target = join(dshHome, "profiles", "node_modules", ...packagePath);
|
|
37
41
|
const patchPath = join(dshHome, "profiles", "web", "cordis.patch.yml");
|
|
38
|
-
const
|
|
42
|
+
const legacyPackageName = sourcePackage.name.split("/").at(-1);
|
|
43
|
+
const quotedPackageName = JSON.stringify(sourcePackage.name);
|
|
44
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45
|
+
const scopedNamePattern = escapeRegExp(sourcePackage.name);
|
|
46
|
+
const legacyNamePattern = escapeRegExp(legacyPackageName);
|
|
47
|
+
const pluginLine = new RegExp(`^\\s+name:\\s*(?:"${scopedNamePattern}"|'${scopedNamePattern}')\\s*$`, "gm");
|
|
48
|
+
const unquotedScopedPluginLine = new RegExp(`^(\\s+name:\\s*)${scopedNamePattern}\\s*$`, "gm");
|
|
49
|
+
const legacyPluginLine = new RegExp(`^(\\s+name:\\s*)(?:"${legacyNamePattern}"|'${legacyNamePattern}'|${legacyNamePattern})\\s*$`, "gm");
|
|
39
50
|
const patchBlock = `# dsh-usage-stats: token usage heatmap + DeepSeek balance
|
|
40
51
|
- insert:
|
|
41
52
|
- id: usage-stats
|
|
42
|
-
name:
|
|
53
|
+
name: ${quotedPackageName}
|
|
43
54
|
`;
|
|
44
55
|
const emptySequenceRoot = /^\[\](?:[ \t]+#.*)?$/;
|
|
45
56
|
|
|
@@ -76,10 +87,26 @@ function assertNoEmptyRootConflict(text) {
|
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
|
|
90
|
+
function countMatches(text, pattern) {
|
|
91
|
+
return [...String(text).matchAll(pattern)].length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Migrate the pre-scope entry and repair an invalid unquoted scoped YAML value. */
|
|
95
|
+
function normalizePluginIdentity(text) {
|
|
96
|
+
const scopedCount = countMatches(text, pluginLine);
|
|
97
|
+
const legacyCount = countMatches(text, legacyPluginLine) + countMatches(text, unquotedScopedPluginLine);
|
|
98
|
+
if (scopedCount + legacyCount > 1) {
|
|
99
|
+
throw new Error(`multiple dsh-usage-stats entries in ${patchPath}; keep one ${quotedPackageName} entry`);
|
|
100
|
+
}
|
|
101
|
+
return String(text)
|
|
102
|
+
.replace(legacyPluginLine, (_, prefix) => `${prefix}${quotedPackageName}`)
|
|
103
|
+
.replace(unquotedScopedPluginLine, (_, prefix) => `${prefix}${quotedPackageName}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
/** Preserve existing YAML/comments while adding exactly one plugin patch entry. */
|
|
80
107
|
function enablePluginInPatch(text) {
|
|
81
|
-
const base = withoutEmptySequenceRoot(text);
|
|
82
|
-
if (
|
|
108
|
+
const base = normalizePluginIdentity(withoutEmptySequenceRoot(text));
|
|
109
|
+
if (countMatches(base, pluginLine) > 0) return base;
|
|
83
110
|
return base.trim() === "" ? patchBlock : `${base.trimEnd()}\n\n${patchBlock}`;
|
|
84
111
|
}
|
|
85
112
|
|
|
@@ -100,10 +127,12 @@ async function verify(expectEnabled) {
|
|
|
100
127
|
throw new Error(`installed package is ${installed.name ?? "unknown"}@${installed.version ?? "unknown"}; expected ${sourcePackage.name}@${sourcePackage.version}`);
|
|
101
128
|
}
|
|
102
129
|
if (expectEnabled) {
|
|
103
|
-
const patch = await readOptional(patchPath);
|
|
104
|
-
const count = patch === null ? 0 : [...patch.matchAll(pluginLine)].length;
|
|
105
|
-
if (count !== 1) throw new Error(`expected exactly one dsh-usage-stats entry in ${patchPath}; found ${count}`);
|
|
130
|
+
const patch = await readOptional(patchPath) ?? "";
|
|
106
131
|
assertNoEmptyRootConflict(patch);
|
|
132
|
+
const count = countMatches(patch, pluginLine);
|
|
133
|
+
if (count !== 1) throw new Error(`expected exactly one dsh-usage-stats entry in ${patchPath}; found ${count}`);
|
|
134
|
+
if (countMatches(patch, legacyPluginLine) > 0) throw new Error(`legacy package name ${legacyPackageName} remains in ${patchPath}`);
|
|
135
|
+
if (countMatches(patch, unquotedScopedPluginLine) > 0) throw new Error(`scoped package name must be quoted in ${patchPath}`);
|
|
107
136
|
}
|
|
108
137
|
console.log(`Verified ${sourcePackage.name}@${sourcePackage.version}`);
|
|
109
138
|
console.log(` package: ${target}`);
|
|
@@ -139,4 +168,4 @@ if (enable) {
|
|
|
139
168
|
|
|
140
169
|
await verify(enable);
|
|
141
170
|
console.log("Installation complete. Restart dsh web, then hard-refresh the browser.");
|
|
142
|
-
console.log("Balance is optional: configure DEEPSEEK_API_KEY in <DSH_HOME>/.credentials.yaml.");
|
|
171
|
+
console.log("Balance is optional: configure DEEPSEEK_API_KEY in <DSH_HOME>/.credentials.yaml.");
|