@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 +8 -1
- package/lib/index.js +42 -1
- package/package.json +1 -1
- package/src/client.ts +7 -1
- package/src/index.ts +36 -1
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] :
|
|
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.
|
|
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] :
|
|
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 = {};
|