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.
@@ -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
- // The shared body returns a plugin descriptor ({ inject, apply }) for the
4
- // dynamic-plugin runner; the static composition loader calls module apply()
5
- // directly, so this entry unwraps the descriptor and runs its apply.
6
- const _usagePlugin = (() => {
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
- const connection = ctx.get('connection')
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
- export const inject = _usagePlugin.inject ?? [];
2246
- export function apply(ctx) {
2247
- return _usagePlugin.apply(ctx);
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
- const usageHarness = (() => {
359
- if (typeof harness !== 'undefined' && harness && typeof harness.handle === 'function')
360
- return harness;
361
- const connection = ctx.get('connection');
362
- if (connection === undefined || connection.rpc === undefined)
363
- return null;
364
- connection.rpc.handle('/usage', (endpoint, args) => {
365
- const fn = usageHandlers.get(endpoint);
366
- if (fn === undefined)
367
- return { ok: false, error: `unknown endpoint: ${endpoint}` };
368
- return fn(args);
369
- }, { authority: 'loopback' });
370
- return {
371
- handle(method, fn) {
372
- const name = String(method);
373
- usageHandlers.set(name.indexOf('/') === -1 ? name : name.slice(name.indexOf('/') + 1), fn);
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
- if (args && typeof args.currency === 'string')
394
- currency = args.currency;
395
- return { prices: Object.assign({}, prices), currency };
396
- });
397
- // ------------------------------------------------- client source loader
398
- // The full dashboard UI lives in src/client/index.js (single source of
399
- // truth). A dynamic plugin cannot import files, so the host reads the file
400
- // and serves it to the browser half, which evaluates it with new Function.
401
- const CLIENT_SOURCE_CANDIDATES = [
402
- '/Users/ruanbw/projects/usage/src/client/index.js',
403
- '/Users/ruanbw/projects/usage/lib/client/index.js',
404
- ];
405
- usageHarness.handle('usage/client-source', async () => {
406
- const fs = ctx.get('fs');
407
- if (fs === undefined)
408
- return { ok: false, error: 'fs service unavailable' };
409
- for (const p of CLIENT_SOURCE_CANDIDATES) {
410
- try {
411
- const target = await fs.resolve(p);
412
- const body = await fs.readText(target);
413
- if (typeof body === 'string' && body.length > 0)
414
- return { ok: true, body };
415
- }
416
- catch (error) {
417
- // try the next candidate
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
- return { ok: false, error: 'client source not found (tried: ' + CLIENT_SOURCE_CANDIDATES.join(', ') + ')' };
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.1",
4
+ "version": "0.1.3",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",