dsh-plugin-usage 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/lib/client/index.js +32 -10
- package/lib/host/index.js +65 -60
- package/package.json +1 -1
package/lib/client/index.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
// Auto-generated module entry wrapping the shared client body
|
|
2
2
|
// (source: src/client/index.js). Do not edit directly.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
3
|
+
// Static composition loads bundles as scripts: the file must self-register
|
|
4
|
+
// via window.__ModuleLoader__.load({ id, factory }). The shared body returns
|
|
5
|
+
// a plugin descriptor ({ inject, apply }) for the dynamic-plugin runner;
|
|
6
|
+
// this entry unwraps it, forwards the loader-provided React, and exposes
|
|
7
|
+
// apply/inject for the cordis loader.
|
|
8
|
+
window.__ModuleLoader__.load({
|
|
9
|
+
id: "dsh-plugin-usage",
|
|
10
|
+
factory: (require) => {
|
|
11
|
+
var module = { exports: {} };
|
|
12
|
+
var exports = module.exports;
|
|
13
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
const React = require("react");
|
|
15
|
+
let _usageCtx = null;
|
|
16
|
+
const _usagePlugin = (() => {
|
|
7
17
|
// ============================================================================
|
|
8
18
|
// Token 用量统计 — settings.section Client UI(纯 JS,动态插件沙箱)
|
|
9
19
|
// 可用全局:React / ctx / styles / host / console;浏览器全局 Date / Intl 等。
|
|
@@ -971,7 +981,15 @@ function hostCall(method, args) {
|
|
|
971
981
|
if (typeof host !== 'undefined' && host && typeof host.call === 'function') {
|
|
972
982
|
return Promise.resolve(host.call(method, args))
|
|
973
983
|
}
|
|
974
|
-
|
|
984
|
+
// Static bundle installs have no ctx/host globals: the entry apply() stores
|
|
985
|
+
// the real context in _usageCtx (declared by the build wrapper).
|
|
986
|
+
const scope = (typeof ctx !== 'undefined' && ctx && typeof ctx.get === 'function')
|
|
987
|
+
? ctx
|
|
988
|
+
: (typeof _usageCtx !== 'undefined' ? _usageCtx : null)
|
|
989
|
+
if (scope === null || typeof scope.get !== 'function') {
|
|
990
|
+
return Promise.reject(new Error('context unavailable'))
|
|
991
|
+
}
|
|
992
|
+
const connection = scope.get('connection')
|
|
975
993
|
if (connection === undefined || connection.rpc === undefined) {
|
|
976
994
|
return Promise.reject(new Error('connection service unavailable'))
|
|
977
995
|
}
|
|
@@ -2241,8 +2259,12 @@ function apply(ctx) {
|
|
|
2241
2259
|
|
|
2242
2260
|
return { inject: ['locale'], apply }
|
|
2243
2261
|
|
|
2244
|
-
})();
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
}
|
|
2262
|
+
})();
|
|
2263
|
+
exports.apply = function apply(ctx) {
|
|
2264
|
+
_usageCtx = ctx;
|
|
2265
|
+
return _usagePlugin.apply(ctx);
|
|
2266
|
+
};
|
|
2267
|
+
exports.inject = _usagePlugin.inject ?? [];
|
|
2268
|
+
return module.exports;
|
|
2269
|
+
}
|
|
2270
|
+
});
|
package/lib/host/index.js
CHANGED
|
@@ -355,69 +355,74 @@ export function apply(ctx) {
|
|
|
355
355
|
// browser half calls `connection.rpc.call('/usage', endpoint, args)`);
|
|
356
356
|
// dynamic-plugin runs expose `harness` directly. Support both from one file.
|
|
357
357
|
const usageHandlers = new Map();
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
},
|
|
375
|
-
};
|
|
376
|
-
})();
|
|
377
|
-
if (usageHarness !== null) {
|
|
378
|
-
usageHarness.handle(RPC_QUERY, (args) => query(args || {}));
|
|
379
|
-
usageHarness.handle(RPC_PRICES, (args) => {
|
|
380
|
-
const incoming = (args && args.prices) || null;
|
|
381
|
-
if (incoming && typeof incoming === 'object') {
|
|
382
|
-
for (const [model, p] of Object.entries(incoming)) {
|
|
383
|
-
if (p && typeof p === 'object') {
|
|
384
|
-
prices[model] = {
|
|
385
|
-
inputPerM: Number(p.inputPerM) || 0,
|
|
386
|
-
outputPerM: Number(p.outputPerM) || 0,
|
|
387
|
-
cacheReadPerM: Number(p.cacheReadPerM) || 0,
|
|
388
|
-
cacheWritePerM: Number(p.cacheWritePerM) || 0,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
358
|
+
function usageHandle(method, fn) {
|
|
359
|
+
const name = String(method);
|
|
360
|
+
usageHandlers.set(name.indexOf('/') === -1 ? name : name.slice(name.indexOf('/') + 1), fn);
|
|
361
|
+
}
|
|
362
|
+
usageHandle(RPC_QUERY, (args) => query(args || {}));
|
|
363
|
+
usageHandle(RPC_PRICES, (args) => {
|
|
364
|
+
const incoming = (args && args.prices) || null;
|
|
365
|
+
if (incoming && typeof incoming === 'object') {
|
|
366
|
+
for (const [model, p] of Object.entries(incoming)) {
|
|
367
|
+
if (p && typeof p === 'object') {
|
|
368
|
+
prices[model] = {
|
|
369
|
+
inputPerM: Number(p.inputPerM) || 0,
|
|
370
|
+
outputPerM: Number(p.outputPerM) || 0,
|
|
371
|
+
cacheReadPerM: Number(p.cacheReadPerM) || 0,
|
|
372
|
+
cacheWritePerM: Number(p.cacheWritePerM) || 0,
|
|
373
|
+
};
|
|
391
374
|
}
|
|
392
375
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
376
|
+
}
|
|
377
|
+
if (args && typeof args.currency === 'string')
|
|
378
|
+
currency = args.currency;
|
|
379
|
+
return { prices: Object.assign({}, prices), currency };
|
|
380
|
+
});
|
|
381
|
+
// ------------------------------------------------- client source loader
|
|
382
|
+
// The full dashboard UI lives in src/client/index.js (single source of
|
|
383
|
+
// truth). A dynamic plugin cannot import files, so the host reads the file
|
|
384
|
+
// and serves it to the browser half, which evaluates it with new Function.
|
|
385
|
+
const CLIENT_SOURCE_CANDIDATES = [
|
|
386
|
+
'/Users/ruanbw/projects/usage/src/client/index.js',
|
|
387
|
+
'/Users/ruanbw/projects/usage/lib/client/index.js',
|
|
388
|
+
];
|
|
389
|
+
usageHandle('usage/client-source', async () => {
|
|
390
|
+
const fs = ctx.get('fs');
|
|
391
|
+
if (fs === undefined)
|
|
392
|
+
return { ok: false, error: 'fs service unavailable' };
|
|
393
|
+
for (const p of CLIENT_SOURCE_CANDIDATES) {
|
|
394
|
+
try {
|
|
395
|
+
const target = await fs.resolve(p);
|
|
396
|
+
const body = await fs.readText(target);
|
|
397
|
+
if (typeof body === 'string' && body.length > 0)
|
|
398
|
+
return { ok: true, body };
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
// try the next candidate
|
|
419
402
|
}
|
|
420
|
-
|
|
403
|
+
}
|
|
404
|
+
return { ok: false, error: 'client source not found (tried: ' + CLIENT_SOURCE_CANDIDATES.join(', ') + ')' };
|
|
405
|
+
});
|
|
406
|
+
const dynamicHarness = (typeof harness !== 'undefined' && harness && typeof harness.handle === 'function') ? harness : null;
|
|
407
|
+
if (dynamicHarness !== null) {
|
|
408
|
+
for (const [endpoint, fn] of usageHandlers) {
|
|
409
|
+
dynamicHarness.handle('usage/' + endpoint, fn);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// Static (bundle) installs: the `connection` service is provided
|
|
414
|
+
// asynchronously by the client-connection plugin, so wait for it before
|
|
415
|
+
// registering the /usage channel (the official api-gateway pattern).
|
|
416
|
+
ctx.inject(['connection'], (connectionCtx) => {
|
|
417
|
+
const connection = connectionCtx.connection;
|
|
418
|
+
if (connection === undefined || connection.rpc === undefined)
|
|
419
|
+
return;
|
|
420
|
+
connection.rpc.handle('/usage', (endpoint, args) => {
|
|
421
|
+
const fn = usageHandlers.get(endpoint);
|
|
422
|
+
if (fn === undefined)
|
|
423
|
+
return { ok: false, error: `unknown endpoint: ${endpoint}` };
|
|
424
|
+
return fn(args);
|
|
425
|
+
}, { authority: 'loopback' });
|
|
421
426
|
});
|
|
422
427
|
}
|
|
423
428
|
// ---------------------------------------------------------------- startup
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-usage",
|
|
3
3
|
"description": "Token usage statistics for DeepSeek Harness — daily/custom-range views, model filters, call counts and cost, inspired by CC Switch statistics.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|