@sunshaobei/dsh-balance-panel 3.0.6 → 3.0.8

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.js CHANGED
@@ -25,7 +25,7 @@
25
25
  * follows the product theme (light/dark) instead of carrying its own palette.
26
26
  */
27
27
  window.__ModuleLoader__.load({
28
- id: "dsh-balance-panel",
28
+ id: "@sunshaobei/dsh-balance-panel",
29
29
  factory: (require) => {
30
30
  var module = { exports: {} };
31
31
  var exports = module.exports;
@@ -37,6 +37,10 @@ window.__ModuleLoader__.load({
37
37
  // (from the host's modelDirectories service) into this tiny store;
38
38
  // the indicator component subscribes to it.
39
39
  var selStore = { sel: null, listeners: new Set() };
40
+ let clientLogQueue = Promise.resolve();
41
+ function clientLog(line) {
42
+ clientLogQueue = clientLogQueue.then(() => ctx.connection.rpc.call(CHANNEL, 'client-log', { line })).catch(() => { });
43
+ }
40
44
  function setSel(next) {
41
45
  var cur = selStore.sel;
42
46
  if (!cur && !next)
@@ -46,6 +50,7 @@ window.__ModuleLoader__.load({
46
50
  if (cur && next && cur.provider === next.provider && cur.model === next.model && cur.src === next.src)
47
51
  return;
48
52
  selStore.sel = next;
53
+ clientLog('setSel provider=' + JSON.stringify(next && next.provider) + ' model=' + JSON.stringify(next && next.model) + ' src=' + JSON.stringify(next && next.src));
49
54
  selStore.listeners.forEach(function (l) { l(next); });
50
55
  }
51
56
  /** Map a provider/model/spec string to a coarse provider family key. */
@@ -1148,6 +1153,8 @@ window.__ModuleLoader__.load({
1148
1153
  // expands the full card with EVERY provider.
1149
1154
  var RING_R = 7, RING_C = 2 * Math.PI * RING_R;
1150
1155
  var curSpec = matchSpec(specs, sel);
1156
+ if (specs && specs.rows)
1157
+ clientLog('capsule sel=' + JSON.stringify(sel) + ' rows=[' + specs.rows.map(function (r3x) { return r3x.id + ':' + r3x.kind; }).join(',') + '] matched=' + (curSpec ? curSpec.id : 'null'));
1151
1158
  var curView = curSpec ? views[curSpec.id] : null;
1152
1159
  // Compact window group: small ring first, tiny label beside it.
1153
1160
  var makeRing = function (key, pct, status, label) {
package/lib/index.js CHANGED
@@ -268,6 +268,31 @@ let chatgptTokenCache = null;
268
268
  // ChatGPT/Codex token resolver read the shared grant record that
269
269
  // dsh-codex-auth writes (llm-pi-ai/openai-codex).
270
270
  let codexCredentialsRef = null;
271
+ // Auto-detected local proxy for ChatGPT/Codex upstream calls. The usage and
272
+ // token endpoints are region-blocked on direct connections, and Node's fetch
273
+ // never reads the OS proxy — so when the panel row has no explicit proxy we
274
+ // probe the well-known local proxy ports once and remember the answer.
275
+ let localProxyCache = null; // null = not probed yet
276
+ async function detectLocalProxy() {
277
+ if (localProxyCache !== null)
278
+ return localProxyCache || undefined;
279
+ for (const port of [7897, 7890, 1087, 8118, 6152]) {
280
+ const ok = await new Promise((resolve) => {
281
+ const req = http.request({ host: '127.0.0.1', port, method: 'CONNECT', path: 'chatgpt.com:443', timeout: 2000 });
282
+ req.on('connect', (res, socket) => { socket.destroy(); resolve(res.statusCode === 200); });
283
+ req.on('error', () => resolve(false));
284
+ req.on('timeout', () => { req.destroy(); resolve(false); });
285
+ req.end();
286
+ });
287
+ if (ok) {
288
+ localProxyCache = 'http://127.0.0.1:' + port;
289
+ break;
290
+ }
291
+ }
292
+ if (localProxyCache === null)
293
+ localProxyCache = '';
294
+ return localProxyCache || undefined;
295
+ }
271
296
  async function readChatGPTAuthFile() {
272
297
  const authJson = chatgptAuthJsonPath();
273
298
  let raw;
@@ -1669,7 +1694,7 @@ async function fetchRow(ctx, provider, proxies, clientProxyUrl) {
1669
1694
  // query AND token refresh — same region constraints apply.
1670
1695
  const cgProxy = clientProxyUrl !== undefined
1671
1696
  ? clientProxyUrl
1672
- : (provider.proxy !== undefined ? proxies[provider.proxy] : undefined);
1697
+ : (provider.proxy !== undefined ? proxies[provider.proxy] : await detectLocalProxy());
1673
1698
  let { accessToken, accountId } = await resolveChatGPTToken(UPSTREAM_TIMEOUT_MS, cgProxy);
1674
1699
  let body;
1675
1700
  try {
@@ -1976,8 +2001,24 @@ export function apply(ctx, config = {}) {
1976
2001
  catalog
1977
2002
  });
1978
2003
  if (endpoint === 'specs') {
2004
+ try {
2005
+ const fsp2 = await import('node:fs/promises');
2006
+ const dbg = path.join(chatgptDshStoreDir(), 'debug.log');
2007
+ await fsp2.appendFile(dbg, new Date().toISOString() + ' specs rows=[' + rows.map(r2 => r2.id + ':' + r2.kind).join(',') + ']\n').catch(() => { });
2008
+ }
2009
+ catch { }
1979
2010
  return { ok: true, value: { rows: rows.map(rowSpec), refreshMs } };
1980
2011
  }
2012
+ if (endpoint === 'client-log') {
2013
+ try {
2014
+ const fsp2 = await import('node:fs/promises');
2015
+ const line = payload && typeof payload.line === 'string' ? payload.line : '';
2016
+ const dbg = path.join(chatgptDshStoreDir(), 'debug.log');
2017
+ await fsp2.appendFile(dbg, new Date().toISOString() + ' client: ' + line + '\n').catch(() => { });
2018
+ }
2019
+ catch { }
2020
+ return { ok: true, value: { logged: true } };
2021
+ }
1981
2022
  if (endpoint === 'fetch-all') {
1982
2023
  const overrides = {};
1983
2024
  const overrideErrors = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sunshaobei/dsh-balance-panel",
3
3
  "description": "Provider quota/balance widget for the dsh web surface: an inline sidebar card above the Settings row (collapsed glanceable row expanding into a Harness-native card with a settings panel), fed by a loopback Connection RPC channel whose host half proxies each provider with credentials that never reach the browser.",
4
- "version": "3.0.6",
4
+ "version": "3.0.8",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
package/src/client.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  * follows the product theme (light/dark) instead of carrying its own palette.
26
26
  */
27
27
  (window as any).__ModuleLoader__.load({
28
- id: "dsh-balance-panel",
28
+ id: "@sunshaobei/dsh-balance-panel",
29
29
  factory: (require) => {
30
30
  var module = { exports: {} as any };
31
31
  var exports = module.exports;
@@ -39,6 +39,10 @@
39
39
  // (from the host's modelDirectories service) into this tiny store;
40
40
  // the indicator component subscribes to it.
41
41
  var selStore = { sel: null, listeners: new Set() };
42
+ let clientLogQueue = Promise.resolve();
43
+ function clientLog(line) {
44
+ clientLogQueue = clientLogQueue.then(() => ctx.connection.rpc.call(CHANNEL, 'client-log', { line })).catch(() => {});
45
+ }
42
46
  function setSel(next) {
43
47
  var cur = selStore.sel;
44
48
  if (!cur && !next) return;
@@ -46,6 +50,7 @@
46
50
  // republishes the same value on every tick).
47
51
  if (cur && next && cur.provider === next.provider && cur.model === next.model && cur.src === next.src) return;
48
52
  selStore.sel = next;
53
+ clientLog('setSel provider=' + JSON.stringify(next && next.provider) + ' model=' + JSON.stringify(next && next.model) + ' src=' + JSON.stringify(next && next.src));
49
54
  selStore.listeners.forEach(function (l) { l(next); });
50
55
  }
51
56
  /** Map a provider/model/spec string to a coarse provider family key. */
@@ -1149,6 +1154,7 @@
1149
1154
  // expands the full card with EVERY provider.
1150
1155
  var RING_R = 7, RING_C = 2 * Math.PI * RING_R;
1151
1156
  var curSpec = matchSpec(specs, sel);
1157
+ if (specs && specs.rows) clientLog('capsule sel=' + JSON.stringify(sel) + ' rows=[' + specs.rows.map(function (r3x) { return r3x.id + ':' + r3x.kind; }).join(',') + '] matched=' + (curSpec ? curSpec.id : 'null'));
1152
1158
  var curView = curSpec ? views[curSpec.id] : null;
1153
1159
  // Compact window group: small ring first, tiny label beside it.
1154
1160
  var makeRing = function (key, pct, status, label) {
package/src/index.ts CHANGED
@@ -292,6 +292,27 @@ let chatgptTokenCache: { bundle: ChatGPTTokenBundle } | null = null;
292
292
  // dsh-codex-auth writes (llm-pi-ai/openai-codex).
293
293
  let codexCredentialsRef: any = null;
294
294
 
295
+ // Auto-detected local proxy for ChatGPT/Codex upstream calls. The usage and
296
+ // token endpoints are region-blocked on direct connections, and Node's fetch
297
+ // never reads the OS proxy — so when the panel row has no explicit proxy we
298
+ // probe the well-known local proxy ports once and remember the answer.
299
+ let localProxyCache: string | undefined | null = null; // null = not probed yet
300
+ async function detectLocalProxy(): Promise<string | undefined> {
301
+ if (localProxyCache !== null) return localProxyCache || undefined;
302
+ for (const port of [7897, 7890, 1087, 8118, 6152]) {
303
+ const ok = await new Promise<boolean>((resolve) => {
304
+ const req = http.request({ host: '127.0.0.1', port, method: 'CONNECT', path: 'chatgpt.com:443', timeout: 2000 });
305
+ req.on('connect', (res, socket) => { socket.destroy(); resolve(res.statusCode === 200); });
306
+ req.on('error', () => resolve(false));
307
+ req.on('timeout', () => { req.destroy(); resolve(false); });
308
+ req.end();
309
+ });
310
+ if (ok) { localProxyCache = 'http://127.0.0.1:' + port; break; }
311
+ }
312
+ if (localProxyCache === null) localProxyCache = '';
313
+ return localProxyCache || undefined;
314
+ }
315
+
295
316
  async function readChatGPTAuthFile(): Promise<ChatGPTTokenBundle> {
296
317
  const authJson = chatgptAuthJsonPath();
297
318
  let raw: string;
@@ -1628,7 +1649,7 @@ async function fetchRow(ctx, provider, proxies, clientProxyUrl) {
1628
1649
  // query AND token refresh — same region constraints apply.
1629
1650
  const cgProxy = clientProxyUrl !== undefined
1630
1651
  ? clientProxyUrl
1631
- : (provider.proxy !== undefined ? proxies[provider.proxy] : undefined);
1652
+ : (provider.proxy !== undefined ? proxies[provider.proxy] : await detectLocalProxy());
1632
1653
  let { accessToken, accountId } = await resolveChatGPTToken(UPSTREAM_TIMEOUT_MS, cgProxy);
1633
1654
  let body: any;
1634
1655
  try {
@@ -1921,8 +1942,22 @@ export function apply(ctx, config: Record<string, any> = {}) {
1921
1942
  catalog
1922
1943
  });
1923
1944
  if (endpoint === 'specs') {
1945
+ try {
1946
+ const fsp2 = await import('node:fs/promises');
1947
+ const dbg = path.join(chatgptDshStoreDir(), 'debug.log');
1948
+ await fsp2.appendFile(dbg, new Date().toISOString() + ' specs rows=[' + rows.map(r2 => r2.id + ':' + r2.kind).join(',') + ']\n').catch(() => {});
1949
+ } catch {}
1924
1950
  return { ok: true, value: { rows: rows.map(rowSpec), refreshMs } };
1925
1951
  }
1952
+ if (endpoint === 'client-log') {
1953
+ try {
1954
+ const fsp2 = await import('node:fs/promises');
1955
+ const line = payload && typeof payload.line === 'string' ? payload.line : '';
1956
+ const dbg = path.join(chatgptDshStoreDir(), 'debug.log');
1957
+ await fsp2.appendFile(dbg, new Date().toISOString() + ' client: ' + line + '\n').catch(() => {});
1958
+ } catch {}
1959
+ return { ok: true, value: { logged: true } };
1960
+ }
1926
1961
  if (endpoint === 'fetch-all') {
1927
1962
  const overrides = {};
1928
1963
  const overrideErrors = {};