@jxgame2020/dsh-token-quota 0.1.11 → 0.1.12
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 +332 -90
- package/lib/index.js +52 -1
- package/lib/types/client/TokenQuotaPanel.d.ts +3 -1
- package/lib/types/client/TokenQuotaPanel.js +35 -2
- package/lib/types/client/index.js +32 -1
- package/lib/types/client/locales.d.ts +24 -0
- package/lib/types/client/locales.js +24 -0
- package/lib/types/client/store.d.ts +21 -0
- package/lib/types/client/store.js +14 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +54 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -39,11 +39,18 @@ window.__ModuleLoader__.load({
|
|
|
39
39
|
upgrade: null,
|
|
40
40
|
upgradeDismissed: false,
|
|
41
41
|
checkingUpdates: false,
|
|
42
|
+
lastCheckResult: "idle",
|
|
42
43
|
dialogOpen: false,
|
|
43
44
|
fullNotice: null,
|
|
44
45
|
reset: null,
|
|
45
46
|
logOpen: false,
|
|
46
|
-
log: null
|
|
47
|
+
log: null,
|
|
48
|
+
logSearch: "",
|
|
49
|
+
logPage: 0,
|
|
50
|
+
logPageSize: 15,
|
|
51
|
+
logClearOpen: false,
|
|
52
|
+
logClearBefore: "",
|
|
53
|
+
logClearing: false
|
|
47
54
|
}),
|
|
48
55
|
actions: {
|
|
49
56
|
setSnapshot: (d, snapshot) => {
|
|
@@ -76,6 +83,9 @@ window.__ModuleLoader__.load({
|
|
|
76
83
|
setCheckingUpdates: (d, checking) => {
|
|
77
84
|
d.checkingUpdates = checking;
|
|
78
85
|
},
|
|
86
|
+
setLastCheckResult: (d, result) => {
|
|
87
|
+
d.lastCheckResult = result;
|
|
88
|
+
},
|
|
79
89
|
setDialogOpen: (d, open) => {
|
|
80
90
|
d.dialogOpen = open;
|
|
81
91
|
},
|
|
@@ -90,6 +100,26 @@ window.__ModuleLoader__.load({
|
|
|
90
100
|
},
|
|
91
101
|
setLog: (d, log) => {
|
|
92
102
|
d.log = log;
|
|
103
|
+
},
|
|
104
|
+
setLogSearch: (d, search) => {
|
|
105
|
+
d.logSearch = search;
|
|
106
|
+
d.logPage = 0;
|
|
107
|
+
},
|
|
108
|
+
setLogPage: (d, page) => {
|
|
109
|
+
d.logPage = page;
|
|
110
|
+
},
|
|
111
|
+
setLogPageSize: (d, size) => {
|
|
112
|
+
d.logPageSize = size;
|
|
113
|
+
d.logPage = 0;
|
|
114
|
+
},
|
|
115
|
+
setLogClearOpen: (d, open) => {
|
|
116
|
+
d.logClearOpen = open;
|
|
117
|
+
},
|
|
118
|
+
setLogClearBefore: (d, before) => {
|
|
119
|
+
d.logClearBefore = before;
|
|
120
|
+
},
|
|
121
|
+
setLogClearing: (d, clearing) => {
|
|
122
|
+
d.logClearing = clearing;
|
|
93
123
|
}
|
|
94
124
|
}
|
|
95
125
|
});
|
|
@@ -136,7 +166,7 @@ window.__ModuleLoader__.load({
|
|
|
136
166
|
}
|
|
137
167
|
//#endregion
|
|
138
168
|
//#region \0dsh-css:/Users/jxgame/Desktop/works/deepseek-harness-package/dsh-token-quota/packages/token-quota/src/client/TokenQuotaPanel.module.css.mjs
|
|
139
|
-
const css = ".NPLLIq_panel{box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);pointer-events:auto;z-index:40;border-radius:12px;flex-direction:column;width:320px;max-height:min(72vh,560px);display:flex;position:fixed;top:56px;right:12px;overflow:hidden;box-shadow:0 8px 28px #0000002e}.NPLLIq_header{border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:flex-start;gap:8px;padding:10px 12px;display:flex}.NPLLIq_header:active{cursor:grabbing}.NPLLIq_headerText{flex-direction:column;gap:2px;min-width:0;display:flex}.NPLLIq_title{color:var(--dsw-alias-label-primary);align-items:baseline;gap:6px;font-size:13px;font-weight:600;display:flex}.NPLLIq_version{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:10px;font-weight:500}.NPLLIq_versionNew{color:var(--dsw-alias-status-warning-foreground,#b56900);background:var(--dsw-alias-status-warning-background,#fff6d7);cursor:default;border-radius:4px;padding:1px 6px}.NPLLIq_subtitle{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_collapse{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_collapse:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_body{padding:4px 0;overflow-y:auto}.NPLLIq_row{border-bottom:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:6px;padding:8px 12px;display:flex}.NPLLIq_rowHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.NPLLIq_rowName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:6px;font-size:12px;font-weight:500;display:inline-flex;overflow:hidden}.NPLLIq_currentBadge{color:var(--dsw-alias-state-success-primary);border:1px solid var(--dsw-alias-state-success-primary);border-radius:8px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_rowMeta{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.NPLLIq_bar{background:var(--dsw-alias-bg-layer-1);border-radius:3px;height:6px;overflow:hidden}.NPLLIq_fillIdle{background:var(--dsw-alias-brand-primary);height:100%;transition:width .2s}.NPLLIq_fillWarn{background:var(--dsw-alias-state-warn-primary);height:100%;transition:width .2s}.NPLLIq_fillOver{background:var(--dsw-alias-state-error-primary);height:100%;transition:width .2s}.NPLLIq_controls{align-items:center;gap:6px;display:flex}.NPLLIq_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-width:0;color:var(--dsw-alias-label-primary);border-radius:6px;flex:1;padding:4px 8px;font-size:11px}.NPLLIq_input::placeholder{color:var(--dsw-alias-label-secondary)}.NPLLIq_save,.NPLLIq_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px;font-size:11px}.NPLLIq_save:hover,.NPLLIq_switch:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.NPLLIq_switch:disabled{opacity:.5;cursor:default}.NPLLIq_notice,.NPLLIq_noticeError{color:var(--dsw-alias-label-secondary);padding:10px 12px;font-size:12px}.NPLLIq_noticeError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_tab{background:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay);pointer-events:auto;cursor:pointer;z-index:90;border-radius:10px;align-items:center;gap:6px;padding:9px 14px;font-size:12px;font-weight:600;display:inline-flex;position:fixed;top:56px;right:12px;box-shadow:0 6px 20px #00000040}.NPLLIq_tab:hover{filter:brightness(1.08)}.NPLLIq_tabIcon{font-size:12px;line-height:1}.NPLLIq_tabLabel{white-space:nowrap}.NPLLIq_headerActions{flex:none;align-items:center;gap:4px;display:flex}.NPLLIq_settingsBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_settingsBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_selectBtn{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_selectBtn:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_gearBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;flex:none;margin-left:6px;padding:0 2px;font-size:12px;line-height:1}.NPLLIq_gearBtn:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_fullNotice{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;margin:6px 12px;padding:8px 10px;font-size:12px}.NPLLIq_upgradeBanner{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-divider-1,#0000001a);border-radius:8px;margin:6px 12px;padding:10px 30px 10px 10px;font-size:12px;position:relative}.NPLLIq_upgradeBannerText{margin-bottom:6px;font-weight:600}.NPLLIq_upgradeCommands{flex-direction:column;gap:4px;display:flex}.NPLLIq_upgradeCommandRow{align-items:center;gap:6px;display:flex}.NPLLIq_upgradeCommand{background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:nowrap;border-radius:4px;flex:1;min-width:0;padding:4px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow-x:auto}.NPLLIq_copyBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;flex:none;padding:3px 8px;font-size:11px}.NPLLIq_copyBtn:hover{background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_upgradeHint{color:var(--dsw-alias-label-secondary);margin-top:6px;font-size:11px}.NPLLIq_upgradeClose{width:20px;height:20px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;font-size:16px;line-height:1;position:absolute;top:4px;right:6px}.NPLLIq_upgradeClose:hover{background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary)}.NPLLIq_checkUpdatesRow{align-items:center;gap:8px;margin-top:8px;display:flex}.NPLLIq_checkBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;padding:4px 10px;font-size:12px}.NPLLIq_checkBtn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_checkBtn:disabled{opacity:.6;cursor:default}.NPLLIq_dialog{z-index:100;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;gap:12px;width:280px;max-height:80vh;padding:14px;display:flex;position:fixed;top:50%;left:50%;overflow-y:auto;transform:translate(-50%,-50%);box-shadow:0 8px 28px #00000040}.NPLLIq_dialogTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.NPLLIq_dialogSection{flex-direction:column;gap:6px;display:flex}.NPLLIq_dialogLabel{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500}.NPLLIq_monitorHint{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_monitorList{flex-direction:column;gap:2px;max-height:180px;display:flex;overflow-y:auto}.NPLLIq_monitorRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:2px 4px;font-size:12px;display:flex}.NPLLIq_monitorRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_monitorName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.NPLLIq_radioRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:3px 4px;font-size:12px;display:flex}.NPLLIq_radioRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_dialogActions{justify-content:flex-end;gap:6px;display:flex}.NPLLIq_dialogBtn,.NPLLIq_dialogBtnPrimary{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:6px;padding:4px 12px;font-size:11px}.NPLLIq_dialogBtn{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.NPLLIq_dialogBtnPrimary{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay)}.NPLLIq_dialogHeader{z-index:2;background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:center;gap:8px;margin:-14px -14px 0;padding:14px 14px 8px;display:flex;position:sticky;top:-14px}.NPLLIq_dialogHeader:active{cursor:grabbing}.NPLLIq_dialogClose{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;font-size:18px;line-height:1;display:flex}.NPLLIq_dialogClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_resetRow{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.NPLLIq_resetSelect{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:6px;padding:3px 6px;font-size:12px}.NPLLIq_logTable{border-collapse:collapse;width:100%;font-size:11px}.NPLLIq_logTable th{text-align:left;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);padding:4px 6px;font-weight:500}.NPLLIq_logTable td{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);font-variant-numeric:tabular-nums;padding:4px 6px}.NPLLIq_logDayCol{white-space:nowrap;color:var(--dsw-alias-label-secondary)}.NPLLIq_logModelCol{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.NPLLIq_logUsedCol{text-align:right}.NPLLIq_logDialog{gap:0;width:380px;max-height:min(72vh,480px);padding:0}.NPLLIq_logHeader{background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;flex:none;justify-content:space-between;align-items:center;gap:8px;padding:14px 14px 8px;display:flex}.NPLLIq_logHeader:active{cursor:grabbing}.NPLLIq_logScroll{flex:1;min-height:0;padding:0 14px 14px;overflow:auto}";
|
|
169
|
+
const css = ".NPLLIq_panel{box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);pointer-events:auto;z-index:40;border-radius:12px;flex-direction:column;width:320px;max-height:min(72vh,560px);display:flex;position:fixed;top:56px;right:12px;overflow:hidden;box-shadow:0 8px 28px #0000002e}.NPLLIq_header{border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:flex-start;gap:8px;padding:10px 12px;display:flex}.NPLLIq_header:active{cursor:grabbing}.NPLLIq_headerText{flex-direction:column;gap:2px;min-width:0;display:flex}.NPLLIq_title{color:var(--dsw-alias-label-primary);align-items:baseline;gap:6px;font-size:13px;font-weight:600;display:flex}.NPLLIq_version{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:10px;font-weight:500}.NPLLIq_versionNew{color:var(--dsw-alias-status-warning-foreground,#b56900);background:var(--dsw-alias-status-warning-background,#fff6d7);cursor:default;border-radius:4px;padding:1px 6px}.NPLLIq_subtitle{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_collapse{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_collapse:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_body{padding:4px 0;overflow-y:auto}.NPLLIq_row{border-bottom:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:6px;padding:8px 12px;display:flex}.NPLLIq_rowHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.NPLLIq_rowName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:6px;font-size:12px;font-weight:500;display:inline-flex;overflow:hidden}.NPLLIq_currentBadge{color:var(--dsw-alias-state-success-primary);border:1px solid var(--dsw-alias-state-success-primary);border-radius:8px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_rowMeta{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.NPLLIq_bar{background:var(--dsw-alias-bg-layer-1);border-radius:3px;height:6px;overflow:hidden}.NPLLIq_fillIdle{background:var(--dsw-alias-brand-primary);height:100%;transition:width .2s}.NPLLIq_fillWarn{background:var(--dsw-alias-state-warn-primary);height:100%;transition:width .2s}.NPLLIq_fillOver{background:var(--dsw-alias-state-error-primary);height:100%;transition:width .2s}.NPLLIq_controls{align-items:center;gap:6px;display:flex}.NPLLIq_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-width:0;color:var(--dsw-alias-label-primary);border-radius:6px;flex:1;padding:4px 8px;font-size:11px}.NPLLIq_input::placeholder{color:var(--dsw-alias-label-secondary)}.NPLLIq_save,.NPLLIq_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px;font-size:11px}.NPLLIq_save:hover,.NPLLIq_switch:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.NPLLIq_switch:disabled{opacity:.5;cursor:default}.NPLLIq_notice,.NPLLIq_noticeError{color:var(--dsw-alias-label-secondary);padding:10px 12px;font-size:12px}.NPLLIq_noticeError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_tab{background:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay);pointer-events:auto;cursor:pointer;z-index:90;border-radius:10px;align-items:center;gap:6px;padding:9px 14px;font-size:12px;font-weight:600;display:inline-flex;position:fixed;top:56px;right:12px;box-shadow:0 6px 20px #00000040}.NPLLIq_tab:hover{filter:brightness(1.08)}.NPLLIq_tabIcon{font-size:12px;line-height:1}.NPLLIq_tabLabel{white-space:nowrap}.NPLLIq_headerActions{flex:none;align-items:center;gap:4px;display:flex}.NPLLIq_settingsBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_settingsBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_selectBtn{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_selectBtn:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_gearBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;flex:none;margin-left:6px;padding:0 2px;font-size:12px;line-height:1}.NPLLIq_gearBtn:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_fullNotice{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;margin:6px 12px;padding:8px 10px;font-size:12px}.NPLLIq_upgradeBanner{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-divider-1,#0000001a);border-radius:8px;margin:6px 12px;padding:10px 30px 10px 10px;font-size:12px;position:relative}.NPLLIq_upgradeBannerText{margin-bottom:6px;font-weight:600}.NPLLIq_upgradeCommands{flex-direction:column;gap:4px;display:flex}.NPLLIq_upgradeCommandRow{align-items:center;gap:6px;display:flex}.NPLLIq_upgradeCommand{background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:nowrap;border-radius:4px;flex:1;min-width:0;padding:4px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow-x:auto}.NPLLIq_copyBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;flex:none;padding:3px 8px;font-size:11px}.NPLLIq_copyBtn:hover{background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_upgradeHint{color:var(--dsw-alias-label-secondary);margin-top:6px;font-size:11px}.NPLLIq_upgradeClose{width:20px;height:20px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;font-size:16px;line-height:1;position:absolute;top:4px;right:6px}.NPLLIq_upgradeClose:hover{background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary)}.NPLLIq_checkUpdatesRow{justify-content:space-between;align-items:center;gap:12px;display:flex}.NPLLIq_checkUpdatesLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.NPLLIq_checkUpdatesLabel input{margin:0}.NPLLIq_checkBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-align:center;border-radius:4px;flex:none;min-width:72px;padding:4px 10px;font-size:12px}.NPLLIq_checkBtn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_checkBtn:disabled{opacity:.6;cursor:default}.NPLLIq_dialog{z-index:100;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;gap:12px;width:280px;max-height:80vh;padding:14px;display:flex;position:fixed;top:50%;left:50%;overflow-y:auto;transform:translate(-50%,-50%);box-shadow:0 8px 28px #00000040}.NPLLIq_dialogTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.NPLLIq_dialogSection{flex-direction:column;gap:6px;display:flex}.NPLLIq_dialogLabel{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500}.NPLLIq_monitorHint{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_monitorList{flex-direction:column;gap:2px;max-height:180px;display:flex;overflow-y:auto}.NPLLIq_monitorRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:2px 4px;font-size:12px;display:flex}.NPLLIq_monitorRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_monitorName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.NPLLIq_radioRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:3px 4px;font-size:12px;display:flex}.NPLLIq_radioRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_dialogActions{justify-content:flex-end;gap:6px;display:flex}.NPLLIq_dialogBtn,.NPLLIq_dialogBtnPrimary{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:6px;padding:4px 12px;font-size:11px}.NPLLIq_dialogBtn{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.NPLLIq_dialogBtnPrimary{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay)}.NPLLIq_dialogHeader{z-index:2;background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:center;gap:8px;margin:-14px -14px 0;padding:14px 14px 8px;display:flex;position:sticky;top:-14px}.NPLLIq_dialogHeader:active{cursor:grabbing}.NPLLIq_dialogClose{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;font-size:18px;line-height:1;display:flex}.NPLLIq_dialogClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_resetRow{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.NPLLIq_resetSelect{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:6px;padding:3px 6px;font-size:12px}.NPLLIq_logTable{border-collapse:collapse;width:100%;font-size:11px}.NPLLIq_logTable th{text-align:left;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);padding:4px 6px;font-weight:500}.NPLLIq_logTable td{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);font-variant-numeric:tabular-nums;padding:4px 6px}.NPLLIq_logDayCol{white-space:nowrap;color:var(--dsw-alias-label-secondary)}.NPLLIq_logModelCol{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.NPLLIq_logUsedCol{text-align:right}.NPLLIq_logDialog{gap:0;width:380px;max-height:min(72vh,480px);padding:0}.NPLLIq_logHeader{background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;flex:none;justify-content:space-between;align-items:center;gap:8px;padding:14px 14px 8px;display:flex}.NPLLIq_logHeader:active{cursor:grabbing}.NPLLIq_logScroll{flex:1;min-height:0;padding:0 14px 14px;overflow:auto}.NPLLIq_logToolbar{flex:1;align-items:center;gap:6px;display:flex}.NPLLIq_logSearch{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);min-width:0;color:var(--dsw-alias-label-primary);border-radius:4px;flex:1;padding:3px 6px;font-size:11px}.NPLLIq_logSearch:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}.NPLLIq_logClearBtn{border:1px solid var(--dsw-alias-state-error-border,#b400004d);color:var(--dsw-alias-state-error-primary,#c00);cursor:pointer;white-space:nowrap;background:0 0;border-radius:4px;flex:none;padding:3px 8px;font-size:11px}.NPLLIq_logClearBtn:hover{background:var(--dsw-alias-state-error-background,#ff00000f)}.NPLLIq_logPager{border-top:1px solid var(--dsw-alias-divider-1,#00000014);background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-secondary);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 14px;font-size:11px;display:flex}.NPLLIq_logPagerInfo{white-space:nowrap}.NPLLIq_logPagerControls{align-items:center;gap:4px;display:flex}.NPLLIq_logPagerBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:24px;height:24px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;font-size:13px;line-height:1}.NPLLIq_logPagerBtn:disabled{opacity:.4;cursor:default}.NPLLIq_logPagerPages{text-align:center;min-width:50px;color:var(--dsw-alias-label-secondary)}.NPLLIq_logPageSizeSelect{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;padding:2px 4px;font-size:11px}.NPLLIq_dialogOverlay{z-index:100001;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.NPLLIq_logClearDialog{gap:0;width:320px;padding:16px}.NPLLIq_dialogBody{color:var(--dsw-alias-label-secondary);margin:6px 0 10px;font-size:12px;line-height:1.5}.NPLLIq_logClearDate{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:100%;color:var(--dsw-alias-label-primary);border-radius:6px;margin-bottom:12px;padding:6px 8px;font-size:13px}.NPLLIq_logClearDate:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}";
|
|
140
170
|
const tagId = "@jxgame2020/dsh-token-quota/TokenQuotaPanel.module.css";
|
|
141
171
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
142
172
|
const tag = document.createElement("style");
|
|
@@ -146,71 +176,85 @@ window.__ModuleLoader__.load({
|
|
|
146
176
|
document.head.appendChild(tag);
|
|
147
177
|
}
|
|
148
178
|
var TokenQuotaPanel_module_css_default = {
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"controls": "NPLLIq_controls",
|
|
152
|
-
"tabIcon": "NPLLIq_tabIcon",
|
|
153
|
-
"logModelCol": "NPLLIq_logModelCol",
|
|
154
|
-
"rowMeta": "NPLLIq_rowMeta",
|
|
155
|
-
"fillOver": "NPLLIq_fillOver",
|
|
156
|
-
"copyBtn": "NPLLIq_copyBtn",
|
|
157
|
-
"checkBtn": "NPLLIq_checkBtn",
|
|
179
|
+
"logHeader": "NPLLIq_logHeader",
|
|
180
|
+
"title": "NPLLIq_title",
|
|
158
181
|
"radioRow": "NPLLIq_radioRow",
|
|
159
|
-
"
|
|
182
|
+
"logModelCol": "NPLLIq_logModelCol",
|
|
183
|
+
"logPagerInfo": "NPLLIq_logPagerInfo",
|
|
184
|
+
"upgradeClose": "NPLLIq_upgradeClose",
|
|
185
|
+
"dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
|
|
186
|
+
"subtitle": "NPLLIq_subtitle",
|
|
187
|
+
"logPagerControls": "NPLLIq_logPagerControls",
|
|
160
188
|
"dialogBtn": "NPLLIq_dialogBtn",
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"header": "NPLLIq_header",
|
|
189
|
+
"logPagerPages": "NPLLIq_logPagerPages",
|
|
190
|
+
"headerActions": "NPLLIq_headerActions",
|
|
164
191
|
"monitorHint": "NPLLIq_monitorHint",
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"selectBtn": "NPLLIq_selectBtn",
|
|
169
|
-
"logHeader": "NPLLIq_logHeader",
|
|
192
|
+
"dialogTitle": "NPLLIq_dialogTitle",
|
|
193
|
+
"logDialog": "NPLLIq_logDialog",
|
|
194
|
+
"header": "NPLLIq_header",
|
|
170
195
|
"dialog": "NPLLIq_dialog",
|
|
171
|
-
"monitorRow": "NPLLIq_monitorRow",
|
|
172
196
|
"tab": "NPLLIq_tab",
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"rowName": "NPLLIq_rowName",
|
|
197
|
+
"dialogHeader": "NPLLIq_dialogHeader",
|
|
198
|
+
"logToolbar": "NPLLIq_logToolbar",
|
|
176
199
|
"dialogLabel": "NPLLIq_dialogLabel",
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"collapse": "NPLLIq_collapse",
|
|
180
|
-
"panel": "NPLLIq_panel",
|
|
181
|
-
"resetSelect": "NPLLIq_resetSelect",
|
|
182
|
-
"fillWarn": "NPLLIq_fillWarn",
|
|
183
|
-
"fullNotice": "NPLLIq_fullNotice",
|
|
184
|
-
"monitorName": "NPLLIq_monitorName",
|
|
185
|
-
"switch": "NPLLIq_switch",
|
|
186
|
-
"dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
|
|
187
|
-
"versionNew": "NPLLIq_versionNew",
|
|
188
|
-
"row": "NPLLIq_row",
|
|
189
|
-
"input": "NPLLIq_input",
|
|
200
|
+
"logClearDate": "NPLLIq_logClearDate",
|
|
201
|
+
"upgradeBanner": "NPLLIq_upgradeBanner",
|
|
190
202
|
"bar": "NPLLIq_bar",
|
|
191
|
-
"
|
|
203
|
+
"row": "NPLLIq_row",
|
|
204
|
+
"copyBtn": "NPLLIq_copyBtn",
|
|
205
|
+
"dialogActions": "NPLLIq_dialogActions",
|
|
206
|
+
"logUsedCol": "NPLLIq_logUsedCol",
|
|
207
|
+
"monitorRow": "NPLLIq_monitorRow",
|
|
192
208
|
"tabLabel": "NPLLIq_tabLabel",
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"upgradeBannerText": "NPLLIq_upgradeBannerText",
|
|
197
|
-
"upgradeHint": "NPLLIq_upgradeHint",
|
|
209
|
+
"monitorName": "NPLLIq_monitorName",
|
|
210
|
+
"logPageSizeSelect": "NPLLIq_logPageSizeSelect",
|
|
211
|
+
"dialogSection": "NPLLIq_dialogSection",
|
|
198
212
|
"save": "NPLLIq_save",
|
|
213
|
+
"switch": "NPLLIq_switch",
|
|
214
|
+
"upgradeCommandRow": "NPLLIq_upgradeCommandRow",
|
|
215
|
+
"tabIcon": "NPLLIq_tabIcon",
|
|
199
216
|
"version": "NPLLIq_version",
|
|
217
|
+
"upgradeBannerText": "NPLLIq_upgradeBannerText",
|
|
218
|
+
"panel": "NPLLIq_panel",
|
|
219
|
+
"notice": "NPLLIq_notice",
|
|
220
|
+
"fillOver": "NPLLIq_fillOver",
|
|
221
|
+
"dialogOverlay": "NPLLIq_dialogOverlay",
|
|
222
|
+
"fullNotice": "NPLLIq_fullNotice",
|
|
223
|
+
"logDayCol": "NPLLIq_logDayCol",
|
|
200
224
|
"rowHeader": "NPLLIq_rowHeader",
|
|
201
|
-
"
|
|
202
|
-
"upgradeCommand": "NPLLIq_upgradeCommand",
|
|
203
|
-
"logDialog": "NPLLIq_logDialog",
|
|
204
|
-
"title": "NPLLIq_title",
|
|
205
|
-
"dialogHeader": "NPLLIq_dialogHeader",
|
|
206
|
-
"upgradeCommandRow": "NPLLIq_upgradeCommandRow",
|
|
225
|
+
"settingsBtn": "NPLLIq_settingsBtn",
|
|
207
226
|
"gearBtn": "NPLLIq_gearBtn",
|
|
208
|
-
"
|
|
227
|
+
"headerText": "NPLLIq_headerText",
|
|
228
|
+
"collapse": "NPLLIq_collapse",
|
|
229
|
+
"resetSelect": "NPLLIq_resetSelect",
|
|
230
|
+
"upgradeCommands": "NPLLIq_upgradeCommands",
|
|
231
|
+
"body": "NPLLIq_body",
|
|
232
|
+
"rowName": "NPLLIq_rowName",
|
|
233
|
+
"monitorList": "NPLLIq_monitorList",
|
|
209
234
|
"logScroll": "NPLLIq_logScroll",
|
|
210
|
-
"
|
|
211
|
-
"
|
|
212
|
-
"
|
|
213
|
-
"
|
|
235
|
+
"input": "NPLLIq_input",
|
|
236
|
+
"resetRow": "NPLLIq_resetRow",
|
|
237
|
+
"logPagerBtn": "NPLLIq_logPagerBtn",
|
|
238
|
+
"logClearDialog": "NPLLIq_logClearDialog",
|
|
239
|
+
"logTable": "NPLLIq_logTable",
|
|
240
|
+
"upgradeHint": "NPLLIq_upgradeHint",
|
|
241
|
+
"rowMeta": "NPLLIq_rowMeta",
|
|
242
|
+
"fillIdle": "NPLLIq_fillIdle",
|
|
243
|
+
"logClearBtn": "NPLLIq_logClearBtn",
|
|
244
|
+
"logPager": "NPLLIq_logPager",
|
|
245
|
+
"versionNew": "NPLLIq_versionNew",
|
|
246
|
+
"dialogBody": "NPLLIq_dialogBody",
|
|
247
|
+
"noticeError": "NPLLIq_noticeError",
|
|
248
|
+
"checkUpdatesRow": "NPLLIq_checkUpdatesRow",
|
|
249
|
+
"logSearch": "NPLLIq_logSearch",
|
|
250
|
+
"checkBtn": "NPLLIq_checkBtn",
|
|
251
|
+
"selectBtn": "NPLLIq_selectBtn",
|
|
252
|
+
"upgradeCommand": "NPLLIq_upgradeCommand",
|
|
253
|
+
"currentBadge": "NPLLIq_currentBadge",
|
|
254
|
+
"controls": "NPLLIq_controls",
|
|
255
|
+
"fillWarn": "NPLLIq_fillWarn",
|
|
256
|
+
"dialogClose": "NPLLIq_dialogClose",
|
|
257
|
+
"checkUpdatesLabel": "NPLLIq_checkUpdatesLabel"
|
|
214
258
|
};
|
|
215
259
|
//#endregion
|
|
216
260
|
//#region lib/types/client/TokenQuotaPanel.js
|
|
@@ -297,7 +341,7 @@ window.__ModuleLoader__.load({
|
|
|
297
341
|
* @param props - composed slot props.
|
|
298
342
|
* @returns the panel element tree.
|
|
299
343
|
*/
|
|
300
|
-
function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, useStore, actions, useSessions }) {
|
|
344
|
+
function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions }) {
|
|
301
345
|
const [collapsed, setCollapsed] = (0, react.useState)(false);
|
|
302
346
|
const [drafts, setDrafts] = (0, react.useState)({});
|
|
303
347
|
const [editingKey, setEditingKey] = (0, react.useState)(null);
|
|
@@ -323,10 +367,17 @@ window.__ModuleLoader__.load({
|
|
|
323
367
|
const upgrade = useStore((s) => s.upgrade);
|
|
324
368
|
const upgradeDismissed = useStore((s) => s.upgradeDismissed);
|
|
325
369
|
const checkingUpdates = useStore((s) => s.checkingUpdates);
|
|
370
|
+
const lastCheckResult = useStore((s) => s.lastCheckResult);
|
|
326
371
|
const reset = useStore((s) => s.reset);
|
|
327
372
|
const dialogOpen = useStore((s) => s.dialogOpen);
|
|
328
373
|
const logOpen = useStore((s) => s.logOpen);
|
|
329
374
|
const log = useStore((s) => s.log);
|
|
375
|
+
const logSearch = useStore((s) => s.logSearch);
|
|
376
|
+
const logPage = useStore((s) => s.logPage);
|
|
377
|
+
const logPageSize = useStore((s) => s.logPageSize);
|
|
378
|
+
const logClearOpen = useStore((s) => s.logClearOpen);
|
|
379
|
+
const logClearBefore = useStore((s) => s.logClearBefore);
|
|
380
|
+
const logClearing = useStore((s) => s.logClearing);
|
|
330
381
|
const fullNotice = useStore((s) => s.fullNotice);
|
|
331
382
|
const loading = useStore((s) => s.loading);
|
|
332
383
|
const error = useStore((s) => s.error);
|
|
@@ -343,6 +394,22 @@ window.__ModuleLoader__.load({
|
|
|
343
394
|
name: model.name
|
|
344
395
|
}))).sort((a, b) => a.key.localeCompare(b.key)), [groups]);
|
|
345
396
|
const isMonitoredKey = (key) => monitored === null || monitored.includes(key);
|
|
397
|
+
const filteredLogEntries = (0, react.useMemo)(() => {
|
|
398
|
+
if (log === null) return [];
|
|
399
|
+
const q = logSearch.trim().toLowerCase();
|
|
400
|
+
if (q === "") return log.entries;
|
|
401
|
+
return log.entries.filter((e) => e.key.toLowerCase().includes(q) || e.provider.toLowerCase().includes(q) || e.model.toLowerCase().includes(q));
|
|
402
|
+
}, [log, logSearch]);
|
|
403
|
+
const logTotalPages = Math.max(1, Math.ceil(filteredLogEntries.length / logPageSize));
|
|
404
|
+
const logCurrentPage = Math.min(logPage, logTotalPages - 1);
|
|
405
|
+
const logPageEntries = (0, react.useMemo)(() => {
|
|
406
|
+
const start = logCurrentPage * logPageSize;
|
|
407
|
+
return filteredLogEntries.slice(start, start + logPageSize);
|
|
408
|
+
}, [
|
|
409
|
+
filteredLogEntries,
|
|
410
|
+
logCurrentPage,
|
|
411
|
+
logPageSize
|
|
412
|
+
]);
|
|
346
413
|
/** Copy text to the clipboard (clipboard API with execCommand fallback). */
|
|
347
414
|
const copyToClipboard = async (text) => {
|
|
348
415
|
try {
|
|
@@ -433,13 +500,13 @@ window.__ModuleLoader__.load({
|
|
|
433
500
|
title: t("upgradeAvailableHint"),
|
|
434
501
|
children: [
|
|
435
502
|
"v",
|
|
436
|
-
"0.1.
|
|
503
|
+
"0.1.12",
|
|
437
504
|
" → v",
|
|
438
505
|
upgrade.latestVersion
|
|
439
506
|
]
|
|
440
507
|
}) : (0, react_jsx_runtime.jsxs)("span", {
|
|
441
508
|
className: TokenQuotaPanel_module_css_default.version,
|
|
442
|
-
children: ["v", "0.1.
|
|
509
|
+
children: ["v", "0.1.12"]
|
|
443
510
|
})]
|
|
444
511
|
}), (0, react_jsx_runtime.jsx)("div", {
|
|
445
512
|
className: TokenQuotaPanel_module_css_default.subtitle,
|
|
@@ -760,27 +827,27 @@ window.__ModuleLoader__.load({
|
|
|
760
827
|
})
|
|
761
828
|
]
|
|
762
829
|
}),
|
|
763
|
-
(0, react_jsx_runtime.
|
|
830
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
764
831
|
className: TokenQuotaPanel_module_css_default.dialogSection,
|
|
765
|
-
children:
|
|
766
|
-
className: TokenQuotaPanel_module_css_default.radioRow,
|
|
767
|
-
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
768
|
-
type: "checkbox",
|
|
769
|
-
checked: checkUpdates,
|
|
770
|
-
onChange: (event) => {
|
|
771
|
-
setCheckUpdates(event.target.checked);
|
|
772
|
-
}
|
|
773
|
-
}), (0, react_jsx_runtime.jsx)("span", { children: t("checkUpdatesLabel") })]
|
|
774
|
-
}), (0, react_jsx_runtime.jsx)("div", {
|
|
832
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
775
833
|
className: TokenQuotaPanel_module_css_default.checkUpdatesRow,
|
|
776
|
-
children: (0, react_jsx_runtime.
|
|
834
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
835
|
+
className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
|
|
836
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
837
|
+
type: "checkbox",
|
|
838
|
+
checked: checkUpdates,
|
|
839
|
+
onChange: (event) => {
|
|
840
|
+
setCheckUpdates(event.target.checked);
|
|
841
|
+
}
|
|
842
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("checkUpdatesLabel") })]
|
|
843
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
777
844
|
type: "button",
|
|
778
845
|
className: TokenQuotaPanel_module_css_default.checkBtn,
|
|
779
846
|
disabled: checkingUpdates,
|
|
780
847
|
onClick: checkUpdatesNow,
|
|
781
|
-
children: checkingUpdates ? t("checkingUpdates") : t("checkUpdatesNow")
|
|
782
|
-
})
|
|
783
|
-
})
|
|
848
|
+
children: checkingUpdates ? t("checkingUpdates") : lastCheckResult === "up-to-date" ? t("checkUpToDate") : lastCheckResult === "error" ? t("checkFailed") : t("checkUpdatesNow")
|
|
849
|
+
})]
|
|
850
|
+
})
|
|
784
851
|
})
|
|
785
852
|
]
|
|
786
853
|
}),
|
|
@@ -798,22 +865,47 @@ window.__ModuleLoader__.load({
|
|
|
798
865
|
onPointerDown: (event) => {
|
|
799
866
|
beginDrag(event, logRef.current, setLogPos);
|
|
800
867
|
},
|
|
801
|
-
children: [
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
868
|
+
children: [
|
|
869
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
870
|
+
className: TokenQuotaPanel_module_css_default.dialogTitle,
|
|
871
|
+
children: t("logsTitle")
|
|
872
|
+
}),
|
|
873
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
874
|
+
className: TokenQuotaPanel_module_css_default.logToolbar,
|
|
875
|
+
onPointerDown: (event) => {
|
|
876
|
+
event.stopPropagation();
|
|
877
|
+
},
|
|
878
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
879
|
+
type: "search",
|
|
880
|
+
className: TokenQuotaPanel_module_css_default.logSearch,
|
|
881
|
+
placeholder: t("logSearchPlaceholder"),
|
|
882
|
+
value: logSearch,
|
|
883
|
+
onChange: (event) => {
|
|
884
|
+
actions.setLogSearch(event.target.value);
|
|
885
|
+
}
|
|
886
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
887
|
+
type: "button",
|
|
888
|
+
className: TokenQuotaPanel_module_css_default.logClearBtn,
|
|
889
|
+
onClick: () => {
|
|
890
|
+
actions.setLogClearOpen(true);
|
|
891
|
+
},
|
|
892
|
+
children: t("logClear")
|
|
893
|
+
})]
|
|
894
|
+
}),
|
|
895
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
896
|
+
type: "button",
|
|
897
|
+
className: TokenQuotaPanel_module_css_default.dialogClose,
|
|
898
|
+
title: t("close"),
|
|
899
|
+
onClick: () => {
|
|
900
|
+
actions.setLogOpen(false);
|
|
901
|
+
},
|
|
902
|
+
children: "×"
|
|
903
|
+
})
|
|
904
|
+
]
|
|
813
905
|
}),
|
|
814
|
-
log !== null &&
|
|
906
|
+
log !== null && filteredLogEntries.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
815
907
|
className: TokenQuotaPanel_module_css_default.notice,
|
|
816
|
-
children: t("logEmpty")
|
|
908
|
+
children: logSearch.trim() !== "" ? t("logNoMatch") : t("logEmpty")
|
|
817
909
|
}),
|
|
818
910
|
(0, react_jsx_runtime.jsx)("div", {
|
|
819
911
|
className: TokenQuotaPanel_module_css_default.logScroll,
|
|
@@ -826,7 +918,7 @@ window.__ModuleLoader__.load({
|
|
|
826
918
|
className: TokenQuotaPanel_module_css_default.logUsedCol,
|
|
827
919
|
children: t("logUsed")
|
|
828
920
|
})
|
|
829
|
-
] }) }), (0, react_jsx_runtime.jsx)("tbody", { children:
|
|
921
|
+
] }) }), (0, react_jsx_runtime.jsx)("tbody", { children: logPageEntries.map((entry) => (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
830
922
|
(0, react_jsx_runtime.jsx)("td", {
|
|
831
923
|
className: TokenQuotaPanel_module_css_default.logDayCol,
|
|
832
924
|
children: entry.day
|
|
@@ -842,8 +934,105 @@ window.__ModuleLoader__.load({
|
|
|
842
934
|
})
|
|
843
935
|
] }, `${entry.day}/${entry.key}`)) })]
|
|
844
936
|
})
|
|
937
|
+
}),
|
|
938
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
939
|
+
className: TokenQuotaPanel_module_css_default.logPager,
|
|
940
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
941
|
+
className: TokenQuotaPanel_module_css_default.logPagerInfo,
|
|
942
|
+
children: t("logPagerInfo").replace("{from}", String(logCurrentPage * logPageSize + 1)).replace("{to}", String(Math.min((logCurrentPage + 1) * logPageSize, filteredLogEntries.length))).replace("{total}", String(filteredLogEntries.length))
|
|
943
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
944
|
+
className: TokenQuotaPanel_module_css_default.logPagerControls,
|
|
945
|
+
children: [
|
|
946
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
947
|
+
type: "button",
|
|
948
|
+
className: TokenQuotaPanel_module_css_default.logPagerBtn,
|
|
949
|
+
disabled: logCurrentPage <= 0,
|
|
950
|
+
onClick: () => {
|
|
951
|
+
actions.setLogPage(logCurrentPage - 1);
|
|
952
|
+
},
|
|
953
|
+
children: "‹"
|
|
954
|
+
}),
|
|
955
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
956
|
+
className: TokenQuotaPanel_module_css_default.logPagerPages,
|
|
957
|
+
children: [
|
|
958
|
+
logCurrentPage + 1,
|
|
959
|
+
" / ",
|
|
960
|
+
logTotalPages
|
|
961
|
+
]
|
|
962
|
+
}),
|
|
963
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
964
|
+
type: "button",
|
|
965
|
+
className: TokenQuotaPanel_module_css_default.logPagerBtn,
|
|
966
|
+
disabled: logCurrentPage >= logTotalPages - 1,
|
|
967
|
+
onClick: () => {
|
|
968
|
+
actions.setLogPage(logCurrentPage + 1);
|
|
969
|
+
},
|
|
970
|
+
children: "›"
|
|
971
|
+
}),
|
|
972
|
+
(0, react_jsx_runtime.jsx)("select", {
|
|
973
|
+
className: TokenQuotaPanel_module_css_default.logPageSizeSelect,
|
|
974
|
+
value: logPageSize,
|
|
975
|
+
onChange: (event) => {
|
|
976
|
+
actions.setLogPageSize(Number(event.target.value));
|
|
977
|
+
},
|
|
978
|
+
children: [
|
|
979
|
+
10,
|
|
980
|
+
15,
|
|
981
|
+
20,
|
|
982
|
+
50,
|
|
983
|
+
100
|
|
984
|
+
].map((size) => (0, react_jsx_runtime.jsxs)("option", {
|
|
985
|
+
value: size,
|
|
986
|
+
children: [size, t("logPerPage")]
|
|
987
|
+
}, size))
|
|
988
|
+
})
|
|
989
|
+
]
|
|
990
|
+
})]
|
|
845
991
|
})
|
|
846
992
|
]
|
|
993
|
+
}),
|
|
994
|
+
logClearOpen && (0, react_jsx_runtime.jsx)("div", {
|
|
995
|
+
className: TokenQuotaPanel_module_css_default.dialogOverlay,
|
|
996
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
997
|
+
className: `${TokenQuotaPanel_module_css_default.dialog} ${TokenQuotaPanel_module_css_default.logClearDialog}`,
|
|
998
|
+
children: [
|
|
999
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1000
|
+
className: TokenQuotaPanel_module_css_default.dialogTitle,
|
|
1001
|
+
children: t("logClearTitle")
|
|
1002
|
+
}),
|
|
1003
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1004
|
+
className: TokenQuotaPanel_module_css_default.dialogBody,
|
|
1005
|
+
children: t("logClearHint")
|
|
1006
|
+
}),
|
|
1007
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
1008
|
+
type: "date",
|
|
1009
|
+
className: TokenQuotaPanel_module_css_default.logClearDate,
|
|
1010
|
+
value: logClearBefore,
|
|
1011
|
+
onChange: (event) => {
|
|
1012
|
+
actions.setLogClearBefore(event.target.value);
|
|
1013
|
+
}
|
|
1014
|
+
}),
|
|
1015
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1016
|
+
className: TokenQuotaPanel_module_css_default.dialogActions,
|
|
1017
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
1018
|
+
type: "button",
|
|
1019
|
+
className: TokenQuotaPanel_module_css_default.checkBtn,
|
|
1020
|
+
disabled: logClearBefore === "" || logClearing,
|
|
1021
|
+
onClick: () => {
|
|
1022
|
+
clearLogBefore(logClearBefore);
|
|
1023
|
+
},
|
|
1024
|
+
children: logClearing ? t("logClearing") : t("logClearConfirm")
|
|
1025
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1026
|
+
type: "button",
|
|
1027
|
+
className: TokenQuotaPanel_module_css_default.checkBtn,
|
|
1028
|
+
onClick: () => {
|
|
1029
|
+
actions.setLogClearOpen(false);
|
|
1030
|
+
},
|
|
1031
|
+
children: t("logClearCancel")
|
|
1032
|
+
})]
|
|
1033
|
+
})
|
|
1034
|
+
]
|
|
1035
|
+
})
|
|
847
1036
|
})
|
|
848
1037
|
]
|
|
849
1038
|
});
|
|
@@ -893,6 +1082,16 @@ window.__ModuleLoader__.load({
|
|
|
893
1082
|
logs: "日志",
|
|
894
1083
|
logsTitle: "用量日志",
|
|
895
1084
|
logEmpty: "暂无记录",
|
|
1085
|
+
logNoMatch: "无匹配记录",
|
|
1086
|
+
logSearchPlaceholder: "搜索模型…",
|
|
1087
|
+
logClear: "清除",
|
|
1088
|
+
logClearTitle: "清除历史日志",
|
|
1089
|
+
logClearHint: "将删除所选日期之前的所有记录(不含当日)",
|
|
1090
|
+
logClearConfirm: "确认清除",
|
|
1091
|
+
logClearCancel: "取消",
|
|
1092
|
+
logClearing: "清除中…",
|
|
1093
|
+
logPagerInfo: "第 {from}-{to} 条 / 共 {total} 条",
|
|
1094
|
+
logPerPage: "条/页",
|
|
896
1095
|
logDay: "日期",
|
|
897
1096
|
logModel: "模型",
|
|
898
1097
|
logUsed: "用量",
|
|
@@ -906,7 +1105,9 @@ window.__ModuleLoader__.load({
|
|
|
906
1105
|
upgradeHint: "升级后请重启 dsh web 并刷新浏览器页面",
|
|
907
1106
|
checkUpdatesLabel: "自动检查更新",
|
|
908
1107
|
checkUpdatesNow: "立即检查",
|
|
909
|
-
checkingUpdates: "检查中…"
|
|
1108
|
+
checkingUpdates: "检查中…",
|
|
1109
|
+
checkUpToDate: "已是最新",
|
|
1110
|
+
checkFailed: "检查失败"
|
|
910
1111
|
};
|
|
911
1112
|
/** English copy. */
|
|
912
1113
|
const en = {
|
|
@@ -945,6 +1146,16 @@ window.__ModuleLoader__.load({
|
|
|
945
1146
|
logs: "Logs",
|
|
946
1147
|
logsTitle: "Usage Log",
|
|
947
1148
|
logEmpty: "No records",
|
|
1149
|
+
logNoMatch: "No matching records",
|
|
1150
|
+
logSearchPlaceholder: "Search model…",
|
|
1151
|
+
logClear: "Clear",
|
|
1152
|
+
logClearTitle: "Clear historical log",
|
|
1153
|
+
logClearHint: "This deletes all records before the selected date (the date itself is kept).",
|
|
1154
|
+
logClearConfirm: "Confirm",
|
|
1155
|
+
logClearCancel: "Cancel",
|
|
1156
|
+
logClearing: "Clearing…",
|
|
1157
|
+
logPagerInfo: "{from}-{to} of {total}",
|
|
1158
|
+
logPerPage: "/page",
|
|
948
1159
|
logDay: "Day",
|
|
949
1160
|
logModel: "Model",
|
|
950
1161
|
logUsed: "Used",
|
|
@@ -958,7 +1169,9 @@ window.__ModuleLoader__.load({
|
|
|
958
1169
|
upgradeHint: "After upgrading, restart dsh web and refresh the browser",
|
|
959
1170
|
checkUpdatesLabel: "Check for updates automatically",
|
|
960
1171
|
checkUpdatesNow: "Check now",
|
|
961
|
-
checkingUpdates: "Checking…"
|
|
1172
|
+
checkingUpdates: "Checking…",
|
|
1173
|
+
checkUpToDate: "Up to date",
|
|
1174
|
+
checkFailed: "Check failed"
|
|
962
1175
|
};
|
|
963
1176
|
//#endregion
|
|
964
1177
|
//#region lib/types/client/index.js
|
|
@@ -1163,21 +1376,28 @@ window.__ModuleLoader__.load({
|
|
|
1163
1376
|
/** Ask the Host to check the registry right now, then refresh the snapshot. */
|
|
1164
1377
|
const checkUpdatesNow = () => {
|
|
1165
1378
|
bound?.setCheckingUpdates(true);
|
|
1379
|
+
bound?.setLastCheckResult("idle");
|
|
1166
1380
|
fetch("/token-quota/check-updates", { method: "POST" }).then((response) => {
|
|
1167
1381
|
if (!response.ok) {
|
|
1168
1382
|
bound?.setError(`check-updates route: ${String(response.status)}`);
|
|
1383
|
+
bound?.setLastCheckResult("error");
|
|
1169
1384
|
return;
|
|
1170
1385
|
}
|
|
1171
1386
|
return response.json();
|
|
1172
1387
|
}, () => {
|
|
1173
1388
|
bound?.setError("update check failed");
|
|
1389
|
+
bound?.setLastCheckResult("error");
|
|
1174
1390
|
}).then((snapshot) => {
|
|
1175
1391
|
if (snapshot !== void 0) {
|
|
1176
1392
|
bound?.setSnapshot(snapshot);
|
|
1177
1393
|
bound?.setUpgrade(snapshot.upgrade ?? null);
|
|
1178
1394
|
actOnFull(snapshot);
|
|
1395
|
+
if (snapshot.upgrade === null) bound?.setLastCheckResult("up-to-date");
|
|
1179
1396
|
}
|
|
1180
1397
|
bound?.setCheckingUpdates(false);
|
|
1398
|
+
setTimeout(() => {
|
|
1399
|
+
bound?.setLastCheckResult("idle");
|
|
1400
|
+
}, 2500);
|
|
1181
1401
|
});
|
|
1182
1402
|
};
|
|
1183
1403
|
const setReset = (reset) => {
|
|
@@ -1201,6 +1421,27 @@ window.__ModuleLoader__.load({
|
|
|
1201
1421
|
bound?.setError("model switch failed");
|
|
1202
1422
|
});
|
|
1203
1423
|
};
|
|
1424
|
+
/** Ask the Host to clear log entries before a given date, then refresh. */
|
|
1425
|
+
const clearLogBefore = (before) => {
|
|
1426
|
+
bound?.setLogClearing(true);
|
|
1427
|
+
fetch("/token-quota/clear-log", {
|
|
1428
|
+
method: "POST",
|
|
1429
|
+
headers: { "content-type": "application/json" },
|
|
1430
|
+
body: JSON.stringify({ before })
|
|
1431
|
+
}).then((response) => {
|
|
1432
|
+
if (!response.ok) {
|
|
1433
|
+
bound?.setError(`clear-log route: ${String(response.status)}`);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
return response.json();
|
|
1437
|
+
}, () => {
|
|
1438
|
+
bound?.setError("clear log failed");
|
|
1439
|
+
}).then((log) => {
|
|
1440
|
+
if (log !== void 0) bound?.setLog(log);
|
|
1441
|
+
bound?.setLogClearing(false);
|
|
1442
|
+
bound?.setLogClearOpen(false);
|
|
1443
|
+
});
|
|
1444
|
+
};
|
|
1204
1445
|
const injected = (actions) => {
|
|
1205
1446
|
bound = actions;
|
|
1206
1447
|
return {
|
|
@@ -1211,7 +1452,8 @@ window.__ModuleLoader__.load({
|
|
|
1211
1452
|
setOnFull,
|
|
1212
1453
|
setReset,
|
|
1213
1454
|
setCheckUpdates,
|
|
1214
|
-
checkUpdatesNow
|
|
1455
|
+
checkUpdatesNow,
|
|
1456
|
+
clearLogBefore
|
|
1215
1457
|
};
|
|
1216
1458
|
};
|
|
1217
1459
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
package/lib/index.js
CHANGED
|
@@ -243,6 +243,8 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
243
243
|
disposeRouteLog;
|
|
244
244
|
/** Disposer for the optional manual-check route (`POST /token-quota/check-updates`). */
|
|
245
245
|
disposeRouteCheck;
|
|
246
|
+
/** Disposer for the optional clear-log route (`POST /token-quota/clear-log`). */
|
|
247
|
+
disposeRouteClear;
|
|
246
248
|
/** Disposer for the webServer-arrival watcher when the service mounts later. */
|
|
247
249
|
disposeRouteWatcher;
|
|
248
250
|
/**
|
|
@@ -353,6 +355,41 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
353
355
|
});
|
|
354
356
|
}
|
|
355
357
|
});
|
|
358
|
+
this.disposeRouteClear = server.register({
|
|
359
|
+
kind: "exact",
|
|
360
|
+
path: "/token-quota/clear-log",
|
|
361
|
+
handler: (req, res) => {
|
|
362
|
+
if (req.method !== "POST") {
|
|
363
|
+
res.writeHead(405);
|
|
364
|
+
res.end();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const chunks = [];
|
|
368
|
+
req.on("data", (chunk) => {
|
|
369
|
+
chunks.push(Buffer.from(chunk));
|
|
370
|
+
});
|
|
371
|
+
req.on("end", () => {
|
|
372
|
+
let before = "";
|
|
373
|
+
try {
|
|
374
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
375
|
+
if (raw.length > 0) {
|
|
376
|
+
const payload = JSON.parse(raw);
|
|
377
|
+
before = typeof payload.before === "string" ? payload.before : "";
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
381
|
+
res.end(JSON.stringify({ error: "bad payload" }));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const updated = this.clearLogBefore(before);
|
|
385
|
+
res.writeHead(200, {
|
|
386
|
+
"content-type": "application/json",
|
|
387
|
+
"cache-control": "no-store"
|
|
388
|
+
});
|
|
389
|
+
res.end(JSON.stringify(updated));
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
});
|
|
356
393
|
}
|
|
357
394
|
/** Query the npm registry and rebuild the cached upgrade info (coalesced). */
|
|
358
395
|
refreshUpgrade() {
|
|
@@ -367,7 +404,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
367
404
|
return this.upgradeCheck;
|
|
368
405
|
}
|
|
369
406
|
async doRefreshUpgrade() {
|
|
370
|
-
const current = "0.1.
|
|
407
|
+
const current = "0.1.12";
|
|
371
408
|
let latest;
|
|
372
409
|
try {
|
|
373
410
|
const response = await fetch(REGISTRY_LATEST_URL, {
|
|
@@ -442,6 +479,19 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
442
479
|
entries.sort((left, right) => right.day.localeCompare(left.day) || left.key.localeCompare(right.key));
|
|
443
480
|
return { entries };
|
|
444
481
|
}
|
|
482
|
+
/**
|
|
483
|
+
* Remove all historical cycles whose day portion strictly precedes `before`
|
|
484
|
+
* (format `YYYY-MM-DD`). The current cycle is never cleared. Returns the
|
|
485
|
+
* trimmed log so the client can refresh immediately.
|
|
486
|
+
*/
|
|
487
|
+
clearLogBefore(before) {
|
|
488
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(before)) return this.readLog();
|
|
489
|
+
const kept = {};
|
|
490
|
+
for (const [cycle, usageByKey] of Object.entries(this.history)) if (cycle.slice(0, 10) >= before) kept[cycle] = usageByKey;
|
|
491
|
+
this.history = kept;
|
|
492
|
+
this.scheduleWrite();
|
|
493
|
+
return this.readLog();
|
|
494
|
+
}
|
|
445
495
|
/** Read the current snapshot (also used by tests and inspection). */
|
|
446
496
|
readSnapshot() {
|
|
447
497
|
this.rollCycleIfNeeded();
|
|
@@ -644,6 +694,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
644
694
|
if (this.disposeRoute !== void 0) this.disposeRoute();
|
|
645
695
|
if (this.disposeRouteLog !== void 0) this.disposeRouteLog();
|
|
646
696
|
if (this.disposeRouteCheck !== void 0) this.disposeRouteCheck();
|
|
697
|
+
if (this.disposeRouteClear !== void 0) this.disposeRouteClear();
|
|
647
698
|
this.flush();
|
|
648
699
|
}
|
|
649
700
|
safeSplit(key) {
|
|
@@ -20,6 +20,8 @@ export interface TokenQuotaPanelInjected {
|
|
|
20
20
|
setCheckUpdates: (enabled: boolean) => void;
|
|
21
21
|
/** Ask the Host to check the registry right now, then refresh the snapshot. */
|
|
22
22
|
checkUpdatesNow: () => void;
|
|
23
|
+
/** Ask the Host to clear log entries before a date, then refresh the log. */
|
|
24
|
+
clearLogBefore: (before: string) => void;
|
|
23
25
|
}
|
|
24
26
|
/** Full component props: runtime + store + locale + injected face. */
|
|
25
27
|
export type TokenQuotaPanelComponentProps = PropsRuntime<'shell.overlay'> & PropsStore<ReturnType<typeof createTokenQuotaPanelStore>> & PropsLocale<'token-quota'> & TokenQuotaPanelInjected;
|
|
@@ -28,5 +30,5 @@ export type TokenQuotaPanelComponentProps = PropsRuntime<'shell.overlay'> & Prop
|
|
|
28
30
|
* @param props - composed slot props.
|
|
29
31
|
* @returns the panel element tree.
|
|
30
32
|
*/
|
|
31
|
-
export declare function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, useStore, actions, useSessions, }: TokenQuotaPanelComponentProps): import("react").JSX.Element;
|
|
33
|
+
export declare function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions, }: TokenQuotaPanelComponentProps): import("react").JSX.Element;
|
|
32
34
|
//# sourceMappingURL=TokenQuotaPanel.d.ts.map
|
|
@@ -86,7 +86,7 @@ function loadPanelPos() {
|
|
|
86
86
|
* @param props - composed slot props.
|
|
87
87
|
* @returns the panel element tree.
|
|
88
88
|
*/
|
|
89
|
-
export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, useStore, actions, useSessions, }) {
|
|
89
|
+
export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions, }) {
|
|
90
90
|
const [collapsed, setCollapsed] = useState(false);
|
|
91
91
|
const [drafts, setDrafts] = useState({});
|
|
92
92
|
const [editingKey, setEditingKey] = useState(null);
|
|
@@ -117,10 +117,17 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
|
|
|
117
117
|
const upgrade = useStore(s => s.upgrade);
|
|
118
118
|
const upgradeDismissed = useStore(s => s.upgradeDismissed);
|
|
119
119
|
const checkingUpdates = useStore(s => s.checkingUpdates);
|
|
120
|
+
const lastCheckResult = useStore(s => s.lastCheckResult);
|
|
120
121
|
const reset = useStore(s => s.reset);
|
|
121
122
|
const dialogOpen = useStore(s => s.dialogOpen);
|
|
122
123
|
const logOpen = useStore(s => s.logOpen);
|
|
123
124
|
const log = useStore(s => s.log);
|
|
125
|
+
const logSearch = useStore(s => s.logSearch);
|
|
126
|
+
const logPage = useStore(s => s.logPage);
|
|
127
|
+
const logPageSize = useStore(s => s.logPageSize);
|
|
128
|
+
const logClearOpen = useStore(s => s.logClearOpen);
|
|
129
|
+
const logClearBefore = useStore(s => s.logClearBefore);
|
|
130
|
+
const logClearing = useStore(s => s.logClearing);
|
|
124
131
|
const fullNotice = useStore(s => s.fullNotice);
|
|
125
132
|
const loading = useStore(s => s.loading);
|
|
126
133
|
const error = useStore(s => s.error);
|
|
@@ -136,6 +143,23 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
|
|
|
136
143
|
name: model.name,
|
|
137
144
|
}))).sort((a, b) => a.key.localeCompare(b.key)), [groups]);
|
|
138
145
|
const isMonitoredKey = (key) => monitored === null || monitored.includes(key);
|
|
146
|
+
// Log dialog: filter by search, then paginate.
|
|
147
|
+
const filteredLogEntries = useMemo(() => {
|
|
148
|
+
if (log === null)
|
|
149
|
+
return [];
|
|
150
|
+
const q = logSearch.trim().toLowerCase();
|
|
151
|
+
if (q === '')
|
|
152
|
+
return log.entries;
|
|
153
|
+
return log.entries.filter(e => e.key.toLowerCase().includes(q)
|
|
154
|
+
|| e.provider.toLowerCase().includes(q)
|
|
155
|
+
|| e.model.toLowerCase().includes(q));
|
|
156
|
+
}, [log, logSearch]);
|
|
157
|
+
const logTotalPages = Math.max(1, Math.ceil(filteredLogEntries.length / logPageSize));
|
|
158
|
+
const logCurrentPage = Math.min(logPage, logTotalPages - 1);
|
|
159
|
+
const logPageEntries = useMemo(() => {
|
|
160
|
+
const start = logCurrentPage * logPageSize;
|
|
161
|
+
return filteredLogEntries.slice(start, start + logPageSize);
|
|
162
|
+
}, [filteredLogEntries, logCurrentPage, logPageSize]);
|
|
139
163
|
/** Copy text to the clipboard (clipboard API with execCommand fallback). */
|
|
140
164
|
const copyToClipboard = async (text) => {
|
|
141
165
|
try {
|
|
@@ -237,6 +261,15 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
|
|
|
237
261
|
hour: reset?.hour ?? 0,
|
|
238
262
|
minute: Number(event.target.value),
|
|
239
263
|
});
|
|
240
|
-
}, children: Array.from({ length: 12 }, (_, i) => i * 5).map(minute => (_jsxs("option", { value: minute, children: [String(minute).padStart(2, '0'), " \u5206"] }, minute))) })] })] }),
|
|
264
|
+
}, children: Array.from({ length: 12 }, (_, i) => i * 5).map(minute => (_jsxs("option", { value: minute, children: [String(minute).padStart(2, '0'), " \u5206"] }, minute))) })] })] }), _jsx("div", { className: css.dialogSection, children: _jsxs("div", { className: css.checkUpdatesRow, children: [_jsxs("label", { className: css.checkUpdatesLabel, children: [_jsx("input", { type: "checkbox", checked: checkUpdates, onChange: (event) => { setCheckUpdates(event.target.checked); } }), _jsx("span", { children: t('checkUpdatesLabel') })] }), _jsx("button", { type: "button", className: css.checkBtn, disabled: checkingUpdates, onClick: checkUpdatesNow, children: checkingUpdates
|
|
265
|
+
? t('checkingUpdates')
|
|
266
|
+
: lastCheckResult === 'up-to-date'
|
|
267
|
+
? t('checkUpToDate')
|
|
268
|
+
: lastCheckResult === 'error'
|
|
269
|
+
? t('checkFailed')
|
|
270
|
+
: t('checkUpdatesNow') })] }) })] })), logOpen && (_jsxs("div", { ref: logRef, className: `${css.dialog} ${css.logDialog}`, style: logPos !== null ? { left: logPos.x, top: logPos.y, transform: 'none' } : undefined, children: [_jsxs("div", { className: css.logHeader, onPointerDown: (event) => { beginDrag(event, logRef.current, setLogPos); }, children: [_jsx("div", { className: css.dialogTitle, children: t('logsTitle') }), _jsxs("div", { className: css.logToolbar, onPointerDown: (event) => { event.stopPropagation(); }, children: [_jsx("input", { type: "search", className: css.logSearch, placeholder: t('logSearchPlaceholder'), value: logSearch, onChange: (event) => { actions.setLogSearch(event.target.value); } }), _jsx("button", { type: "button", className: css.logClearBtn, onClick: () => { actions.setLogClearOpen(true); }, children: t('logClear') })] }), _jsx("button", { type: "button", className: css.dialogClose, title: t('close'), onClick: () => { actions.setLogOpen(false); }, children: "\u00D7" })] }), log !== null && filteredLogEntries.length === 0 && (_jsx("div", { className: css.notice, children: logSearch.trim() !== '' ? t('logNoMatch') : t('logEmpty') })), _jsx("div", { className: css.logScroll, children: _jsxs("table", { className: css.logTable, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t('logDay') }), _jsx("th", { children: t('logModel') }), _jsx("th", { className: css.logUsedCol, children: t('logUsed') })] }) }), _jsx("tbody", { children: logPageEntries.map(entry => (_jsxs("tr", { children: [_jsx("td", { className: css.logDayCol, children: entry.day }), _jsx("td", { className: css.logModelCol, title: entry.key, children: entry.key }), _jsx("td", { className: css.logUsedCol, children: formatTokens(entry.used) })] }, `${entry.day}/${entry.key}`))) })] }) }), _jsxs("div", { className: css.logPager, children: [_jsx("span", { className: css.logPagerInfo, children: t('logPagerInfo')
|
|
271
|
+
.replace('{from}', String(logCurrentPage * logPageSize + 1))
|
|
272
|
+
.replace('{to}', String(Math.min((logCurrentPage + 1) * logPageSize, filteredLogEntries.length)))
|
|
273
|
+
.replace('{total}', String(filteredLogEntries.length)) }), _jsxs("div", { className: css.logPagerControls, children: [_jsx("button", { type: "button", className: css.logPagerBtn, disabled: logCurrentPage <= 0, onClick: () => { actions.setLogPage(logCurrentPage - 1); }, children: "\u2039" }), _jsxs("span", { className: css.logPagerPages, children: [logCurrentPage + 1, " / ", logTotalPages] }), _jsx("button", { type: "button", className: css.logPagerBtn, disabled: logCurrentPage >= logTotalPages - 1, onClick: () => { actions.setLogPage(logCurrentPage + 1); }, children: "\u203A" }), _jsx("select", { className: css.logPageSizeSelect, value: logPageSize, onChange: (event) => { actions.setLogPageSize(Number(event.target.value)); }, children: [10, 15, 20, 50, 100].map(size => (_jsxs("option", { value: size, children: [size, t('logPerPage')] }, size))) })] })] })] })), logClearOpen && (_jsx("div", { className: css.dialogOverlay, children: _jsxs("div", { className: `${css.dialog} ${css.logClearDialog}`, children: [_jsx("div", { className: css.dialogTitle, children: t('logClearTitle') }), _jsx("div", { className: css.dialogBody, children: t('logClearHint') }), _jsx("input", { type: "date", className: css.logClearDate, value: logClearBefore, onChange: (event) => { actions.setLogClearBefore(event.target.value); } }), _jsxs("div", { className: css.dialogActions, children: [_jsx("button", { type: "button", className: css.checkBtn, disabled: logClearBefore === '' || logClearing, onClick: () => { clearLogBefore(logClearBefore); }, children: logClearing ? t('logClearing') : t('logClearConfirm') }), _jsx("button", { type: "button", className: css.checkBtn, onClick: () => { actions.setLogClearOpen(false); }, children: t('logClearCancel') })] })] }) }))] }));
|
|
241
274
|
}
|
|
242
275
|
//# sourceMappingURL=TokenQuotaPanel.js.map
|
|
@@ -236,21 +236,30 @@ export function apply(ctx) {
|
|
|
236
236
|
/** Ask the Host to check the registry right now, then refresh the snapshot. */
|
|
237
237
|
const checkUpdatesNow = () => {
|
|
238
238
|
bound?.setCheckingUpdates(true);
|
|
239
|
+
bound?.setLastCheckResult('idle');
|
|
239
240
|
void fetch('/token-quota/check-updates', { method: 'POST' }).then((response) => {
|
|
240
241
|
if (!response.ok) {
|
|
241
242
|
bound?.setError(`check-updates route: ${String(response.status)}`);
|
|
243
|
+
bound?.setLastCheckResult('error');
|
|
242
244
|
return undefined;
|
|
243
245
|
}
|
|
244
246
|
return response.json();
|
|
245
247
|
}, () => {
|
|
246
248
|
bound?.setError('update check failed');
|
|
249
|
+
bound?.setLastCheckResult('error');
|
|
247
250
|
}).then((snapshot) => {
|
|
248
251
|
if (snapshot !== undefined) {
|
|
249
252
|
bound?.setSnapshot(snapshot);
|
|
250
253
|
bound?.setUpgrade(snapshot.upgrade ?? null);
|
|
251
254
|
actOnFull(snapshot);
|
|
255
|
+
if (snapshot.upgrade === null) {
|
|
256
|
+
bound?.setLastCheckResult('up-to-date');
|
|
257
|
+
}
|
|
252
258
|
}
|
|
253
259
|
bound?.setCheckingUpdates(false);
|
|
260
|
+
// Clear the transient result hint after a short delay so the button
|
|
261
|
+
// returns to its idle label.
|
|
262
|
+
setTimeout(() => { bound?.setLastCheckResult('idle'); }, 2500);
|
|
254
263
|
});
|
|
255
264
|
};
|
|
256
265
|
const setReset = (reset) => {
|
|
@@ -276,11 +285,33 @@ export function apply(ctx) {
|
|
|
276
285
|
bound?.setError('model switch failed');
|
|
277
286
|
});
|
|
278
287
|
};
|
|
288
|
+
/** Ask the Host to clear log entries before a given date, then refresh. */
|
|
289
|
+
const clearLogBefore = (before) => {
|
|
290
|
+
bound?.setLogClearing(true);
|
|
291
|
+
void fetch('/token-quota/clear-log', {
|
|
292
|
+
method: 'POST',
|
|
293
|
+
headers: { 'content-type': 'application/json' },
|
|
294
|
+
body: JSON.stringify({ before }),
|
|
295
|
+
}).then((response) => {
|
|
296
|
+
if (!response.ok) {
|
|
297
|
+
bound?.setError(`clear-log route: ${String(response.status)}`);
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
return response.json();
|
|
301
|
+
}, () => {
|
|
302
|
+
bound?.setError('clear log failed');
|
|
303
|
+
}).then((log) => {
|
|
304
|
+
if (log !== undefined)
|
|
305
|
+
bound?.setLog(log);
|
|
306
|
+
bound?.setLogClearing(false);
|
|
307
|
+
bound?.setLogClearOpen(false);
|
|
308
|
+
});
|
|
309
|
+
};
|
|
279
310
|
const injected = (actions) => {
|
|
280
311
|
bound = actions;
|
|
281
312
|
return {
|
|
282
313
|
load, setLimit, selectModel, setMonitored, setOnFull, setReset,
|
|
283
|
-
setCheckUpdates, checkUpdatesNow,
|
|
314
|
+
setCheckUpdates, checkUpdatesNow, clearLogBefore,
|
|
284
315
|
};
|
|
285
316
|
};
|
|
286
317
|
ctx.slots.inject('shell.overlay', () => ctx.slots.register({
|
|
@@ -41,6 +41,16 @@ export declare const zh: {
|
|
|
41
41
|
logs: string;
|
|
42
42
|
logsTitle: string;
|
|
43
43
|
logEmpty: string;
|
|
44
|
+
logNoMatch: string;
|
|
45
|
+
logSearchPlaceholder: string;
|
|
46
|
+
logClear: string;
|
|
47
|
+
logClearTitle: string;
|
|
48
|
+
logClearHint: string;
|
|
49
|
+
logClearConfirm: string;
|
|
50
|
+
logClearCancel: string;
|
|
51
|
+
logClearing: string;
|
|
52
|
+
logPagerInfo: string;
|
|
53
|
+
logPerPage: string;
|
|
44
54
|
logDay: string;
|
|
45
55
|
logModel: string;
|
|
46
56
|
logUsed: string;
|
|
@@ -55,6 +65,8 @@ export declare const zh: {
|
|
|
55
65
|
checkUpdatesLabel: string;
|
|
56
66
|
checkUpdatesNow: string;
|
|
57
67
|
checkingUpdates: string;
|
|
68
|
+
checkUpToDate: string;
|
|
69
|
+
checkFailed: string;
|
|
58
70
|
};
|
|
59
71
|
/** Dictionary keys owned by the token-quota panel (a string-literal union). */
|
|
60
72
|
export type TokenQuotaKey = keyof typeof zh;
|
|
@@ -95,6 +107,16 @@ export declare const en: {
|
|
|
95
107
|
logs: string;
|
|
96
108
|
logsTitle: string;
|
|
97
109
|
logEmpty: string;
|
|
110
|
+
logNoMatch: string;
|
|
111
|
+
logSearchPlaceholder: string;
|
|
112
|
+
logClear: string;
|
|
113
|
+
logClearTitle: string;
|
|
114
|
+
logClearHint: string;
|
|
115
|
+
logClearConfirm: string;
|
|
116
|
+
logClearCancel: string;
|
|
117
|
+
logClearing: string;
|
|
118
|
+
logPagerInfo: string;
|
|
119
|
+
logPerPage: string;
|
|
98
120
|
logDay: string;
|
|
99
121
|
logModel: string;
|
|
100
122
|
logUsed: string;
|
|
@@ -109,5 +131,7 @@ export declare const en: {
|
|
|
109
131
|
checkUpdatesLabel: string;
|
|
110
132
|
checkUpdatesNow: string;
|
|
111
133
|
checkingUpdates: string;
|
|
134
|
+
checkUpToDate: string;
|
|
135
|
+
checkFailed: string;
|
|
112
136
|
};
|
|
113
137
|
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -41,6 +41,16 @@ export const zh = {
|
|
|
41
41
|
logs: '日志',
|
|
42
42
|
logsTitle: '用量日志',
|
|
43
43
|
logEmpty: '暂无记录',
|
|
44
|
+
logNoMatch: '无匹配记录',
|
|
45
|
+
logSearchPlaceholder: '搜索模型…',
|
|
46
|
+
logClear: '清除',
|
|
47
|
+
logClearTitle: '清除历史日志',
|
|
48
|
+
logClearHint: '将删除所选日期之前的所有记录(不含当日)',
|
|
49
|
+
logClearConfirm: '确认清除',
|
|
50
|
+
logClearCancel: '取消',
|
|
51
|
+
logClearing: '清除中…',
|
|
52
|
+
logPagerInfo: '第 {from}-{to} 条 / 共 {total} 条',
|
|
53
|
+
logPerPage: '条/页',
|
|
44
54
|
logDay: '日期',
|
|
45
55
|
logModel: '模型',
|
|
46
56
|
logUsed: '用量',
|
|
@@ -55,6 +65,8 @@ export const zh = {
|
|
|
55
65
|
checkUpdatesLabel: '自动检查更新',
|
|
56
66
|
checkUpdatesNow: '立即检查',
|
|
57
67
|
checkingUpdates: '检查中…',
|
|
68
|
+
checkUpToDate: '已是最新',
|
|
69
|
+
checkFailed: '检查失败',
|
|
58
70
|
};
|
|
59
71
|
/** English copy. */
|
|
60
72
|
export const en = {
|
|
@@ -93,6 +105,16 @@ export const en = {
|
|
|
93
105
|
logs: 'Logs',
|
|
94
106
|
logsTitle: 'Usage Log',
|
|
95
107
|
logEmpty: 'No records',
|
|
108
|
+
logNoMatch: 'No matching records',
|
|
109
|
+
logSearchPlaceholder: 'Search model…',
|
|
110
|
+
logClear: 'Clear',
|
|
111
|
+
logClearTitle: 'Clear historical log',
|
|
112
|
+
logClearHint: 'This deletes all records before the selected date (the date itself is kept).',
|
|
113
|
+
logClearConfirm: 'Confirm',
|
|
114
|
+
logClearCancel: 'Cancel',
|
|
115
|
+
logClearing: 'Clearing…',
|
|
116
|
+
logPagerInfo: '{from}-{to} of {total}',
|
|
117
|
+
logPerPage: '/page',
|
|
96
118
|
logDay: 'Day',
|
|
97
119
|
logModel: 'Model',
|
|
98
120
|
logUsed: 'Used',
|
|
@@ -107,5 +129,7 @@ export const en = {
|
|
|
107
129
|
checkUpdatesLabel: 'Check for updates automatically',
|
|
108
130
|
checkUpdatesNow: 'Check now',
|
|
109
131
|
checkingUpdates: 'Checking…',
|
|
132
|
+
checkUpToDate: 'Up to date',
|
|
133
|
+
checkFailed: 'Check failed',
|
|
110
134
|
};
|
|
111
135
|
//# sourceMappingURL=locales.js.map
|
|
@@ -54,6 +54,8 @@ export interface TokenQuotaPanelState {
|
|
|
54
54
|
upgradeDismissed: boolean;
|
|
55
55
|
/** Whether a manual update check is in flight. */
|
|
56
56
|
checkingUpdates: boolean;
|
|
57
|
+
/** Result of the most recent manual check; 'idle' = nothing to show. */
|
|
58
|
+
lastCheckResult: 'idle' | 'up-to-date' | 'error';
|
|
57
59
|
/** Whether the settings dialog is open. */
|
|
58
60
|
dialogOpen: boolean;
|
|
59
61
|
/** Full-quota notice shown for the `'stop'` strategy; null when none. */
|
|
@@ -64,6 +66,18 @@ export interface TokenQuotaPanelState {
|
|
|
64
66
|
logOpen: boolean;
|
|
65
67
|
/** Latest usage history; null before the first fetch. */
|
|
66
68
|
log: TokenQuotaLog | null;
|
|
69
|
+
/** Filter string for the log table (matches provider/model key). */
|
|
70
|
+
logSearch: string;
|
|
71
|
+
/** Current page index (0-based) in the filtered log table. */
|
|
72
|
+
logPage: number;
|
|
73
|
+
/** Rows per page in the log table. */
|
|
74
|
+
logPageSize: number;
|
|
75
|
+
/** Whether the clear-log confirmation dialog is open. */
|
|
76
|
+
logClearOpen: boolean;
|
|
77
|
+
/** The `YYYY-MM-DD` date before which log entries will be cleared. */
|
|
78
|
+
logClearBefore: string;
|
|
79
|
+
/** Whether a clear-log request is in flight. */
|
|
80
|
+
logClearing: boolean;
|
|
67
81
|
}
|
|
68
82
|
/** Declared write surface. */
|
|
69
83
|
export type TokenQuotaPanelActions = {
|
|
@@ -76,11 +90,18 @@ export type TokenQuotaPanelActions = {
|
|
|
76
90
|
setUpgrade: (d: TokenQuotaPanelState, upgrade: TokenQuotaUpgrade | null) => void;
|
|
77
91
|
setUpgradeDismissed: (d: TokenQuotaPanelState, dismissed: boolean) => void;
|
|
78
92
|
setCheckingUpdates: (d: TokenQuotaPanelState, checking: boolean) => void;
|
|
93
|
+
setLastCheckResult: (d: TokenQuotaPanelState, result: 'idle' | 'up-to-date' | 'error') => void;
|
|
79
94
|
setDialogOpen: (d: TokenQuotaPanelState, open: boolean) => void;
|
|
80
95
|
setFullNotice: (d: TokenQuotaPanelState, notice: string | null) => void;
|
|
81
96
|
setReset: (d: TokenQuotaPanelState, reset: TokenQuotaReset | null) => void;
|
|
82
97
|
setLogOpen: (d: TokenQuotaPanelState, open: boolean) => void;
|
|
83
98
|
setLog: (d: TokenQuotaPanelState, log: TokenQuotaLog | null) => void;
|
|
99
|
+
setLogSearch: (d: TokenQuotaPanelState, search: string) => void;
|
|
100
|
+
setLogPage: (d: TokenQuotaPanelState, page: number) => void;
|
|
101
|
+
setLogPageSize: (d: TokenQuotaPanelState, size: number) => void;
|
|
102
|
+
setLogClearOpen: (d: TokenQuotaPanelState, open: boolean) => void;
|
|
103
|
+
setLogClearBefore: (d: TokenQuotaPanelState, before: string) => void;
|
|
104
|
+
setLogClearing: (d: TokenQuotaPanelState, clearing: boolean) => void;
|
|
84
105
|
};
|
|
85
106
|
/**
|
|
86
107
|
* Declares the panel state and write surface.
|
|
@@ -30,11 +30,18 @@ export function createTokenQuotaPanelStore() {
|
|
|
30
30
|
upgrade: null,
|
|
31
31
|
upgradeDismissed: false,
|
|
32
32
|
checkingUpdates: false,
|
|
33
|
+
lastCheckResult: 'idle',
|
|
33
34
|
dialogOpen: false,
|
|
34
35
|
fullNotice: null,
|
|
35
36
|
reset: null,
|
|
36
37
|
logOpen: false,
|
|
37
38
|
log: null,
|
|
39
|
+
logSearch: '',
|
|
40
|
+
logPage: 0,
|
|
41
|
+
logPageSize: 15,
|
|
42
|
+
logClearOpen: false,
|
|
43
|
+
logClearBefore: '',
|
|
44
|
+
logClearing: false,
|
|
38
45
|
}),
|
|
39
46
|
actions: {
|
|
40
47
|
setSnapshot: (d, snapshot) => { d.snapshot = snapshot; },
|
|
@@ -53,11 +60,18 @@ export function createTokenQuotaPanelStore() {
|
|
|
53
60
|
},
|
|
54
61
|
setUpgradeDismissed: (d, dismissed) => { d.upgradeDismissed = dismissed; },
|
|
55
62
|
setCheckingUpdates: (d, checking) => { d.checkingUpdates = checking; },
|
|
63
|
+
setLastCheckResult: (d, result) => { d.lastCheckResult = result; },
|
|
56
64
|
setDialogOpen: (d, open) => { d.dialogOpen = open; },
|
|
57
65
|
setFullNotice: (d, notice) => { d.fullNotice = notice; },
|
|
58
66
|
setReset: (d, reset) => { d.reset = reset; },
|
|
59
67
|
setLogOpen: (d, open) => { d.logOpen = open; },
|
|
60
68
|
setLog: (d, log) => { d.log = log; },
|
|
69
|
+
setLogSearch: (d, search) => { d.logSearch = search; d.logPage = 0; },
|
|
70
|
+
setLogPage: (d, page) => { d.logPage = page; },
|
|
71
|
+
setLogPageSize: (d, size) => { d.logPageSize = size; d.logPage = 0; },
|
|
72
|
+
setLogClearOpen: (d, open) => { d.logClearOpen = open; },
|
|
73
|
+
setLogClearBefore: (d, before) => { d.logClearBefore = before; },
|
|
74
|
+
setLogClearing: (d, clearing) => { d.logClearing = clearing; },
|
|
61
75
|
},
|
|
62
76
|
});
|
|
63
77
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -77,6 +77,8 @@ export declare class TokenQuotaService extends Service {
|
|
|
77
77
|
private disposeRouteLog;
|
|
78
78
|
/** Disposer for the optional manual-check route (`POST /token-quota/check-updates`). */
|
|
79
79
|
private disposeRouteCheck;
|
|
80
|
+
/** Disposer for the optional clear-log route (`POST /token-quota/clear-log`). */
|
|
81
|
+
private disposeRouteClear;
|
|
80
82
|
/** Disposer for the webServer-arrival watcher when the service mounts later. */
|
|
81
83
|
private disposeRouteWatcher;
|
|
82
84
|
/**
|
|
@@ -93,6 +95,12 @@ export declare class TokenQuotaService extends Service {
|
|
|
93
95
|
private syncUpdateChecking;
|
|
94
96
|
/** Read the full per-cycle usage history (log dialog data). */
|
|
95
97
|
readLog(): TokenQuotaLog;
|
|
98
|
+
/**
|
|
99
|
+
* Remove all historical cycles whose day portion strictly precedes `before`
|
|
100
|
+
* (format `YYYY-MM-DD`). The current cycle is never cleared. Returns the
|
|
101
|
+
* trimmed log so the client can refresh immediately.
|
|
102
|
+
*/
|
|
103
|
+
clearLogBefore(before: string): TokenQuotaLog;
|
|
96
104
|
/** Read the current snapshot (also used by tests and inspection). */
|
|
97
105
|
readSnapshot(): TokenQuotaSnapshot;
|
|
98
106
|
/** Today's used tokens for one model key, or `0`. */
|
package/lib/types/index.js
CHANGED
|
@@ -243,6 +243,8 @@ export class TokenQuotaService extends Service {
|
|
|
243
243
|
disposeRouteLog;
|
|
244
244
|
/** Disposer for the optional manual-check route (`POST /token-quota/check-updates`). */
|
|
245
245
|
disposeRouteCheck;
|
|
246
|
+
/** Disposer for the optional clear-log route (`POST /token-quota/clear-log`). */
|
|
247
|
+
disposeRouteClear;
|
|
246
248
|
/** Disposer for the webServer-arrival watcher when the service mounts later. */
|
|
247
249
|
disposeRouteWatcher;
|
|
248
250
|
/**
|
|
@@ -376,6 +378,37 @@ export class TokenQuotaService extends Service {
|
|
|
376
378
|
});
|
|
377
379
|
},
|
|
378
380
|
});
|
|
381
|
+
this.disposeRouteClear = server.register({
|
|
382
|
+
kind: 'exact',
|
|
383
|
+
path: '/token-quota/clear-log',
|
|
384
|
+
handler: (req, res) => {
|
|
385
|
+
if (req.method !== 'POST') {
|
|
386
|
+
res.writeHead(405);
|
|
387
|
+
res.end();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const chunks = [];
|
|
391
|
+
req.on('data', (chunk) => { chunks.push(Buffer.from(chunk)); });
|
|
392
|
+
req.on('end', () => {
|
|
393
|
+
let before = '';
|
|
394
|
+
try {
|
|
395
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
396
|
+
if (raw.length > 0) {
|
|
397
|
+
const payload = JSON.parse(raw);
|
|
398
|
+
before = typeof payload.before === 'string' ? payload.before : '';
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
res.writeHead(400, { 'content-type': 'application/json' });
|
|
403
|
+
res.end(JSON.stringify({ error: 'bad payload' }));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const updated = this.clearLogBefore(before);
|
|
407
|
+
res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
|
408
|
+
res.end(JSON.stringify(updated));
|
|
409
|
+
});
|
|
410
|
+
},
|
|
411
|
+
});
|
|
379
412
|
}
|
|
380
413
|
/** Query the npm registry and rebuild the cached upgrade info (coalesced). */
|
|
381
414
|
refreshUpgrade() {
|
|
@@ -470,6 +503,25 @@ export class TokenQuotaService extends Service {
|
|
|
470
503
|
entries.sort((left, right) => right.day.localeCompare(left.day) || left.key.localeCompare(right.key));
|
|
471
504
|
return { entries };
|
|
472
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Remove all historical cycles whose day portion strictly precedes `before`
|
|
508
|
+
* (format `YYYY-MM-DD`). The current cycle is never cleared. Returns the
|
|
509
|
+
* trimmed log so the client can refresh immediately.
|
|
510
|
+
*/
|
|
511
|
+
clearLogBefore(before) {
|
|
512
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(before))
|
|
513
|
+
return this.readLog();
|
|
514
|
+
const kept = {};
|
|
515
|
+
for (const [cycle, usageByKey] of Object.entries(this.history)) {
|
|
516
|
+
const dayPart = cycle.slice(0, 10);
|
|
517
|
+
if (dayPart >= before) {
|
|
518
|
+
kept[cycle] = usageByKey;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
this.history = kept;
|
|
522
|
+
this.scheduleWrite();
|
|
523
|
+
return this.readLog();
|
|
524
|
+
}
|
|
473
525
|
/** Read the current snapshot (also used by tests and inspection). */
|
|
474
526
|
readSnapshot() {
|
|
475
527
|
this.rollCycleIfNeeded();
|
|
@@ -728,6 +780,8 @@ export class TokenQuotaService extends Service {
|
|
|
728
780
|
this.disposeRouteLog();
|
|
729
781
|
if (this.disposeRouteCheck !== undefined)
|
|
730
782
|
this.disposeRouteCheck();
|
|
783
|
+
if (this.disposeRouteClear !== undefined)
|
|
784
|
+
this.disposeRouteClear();
|
|
731
785
|
this.flush();
|
|
732
786
|
}
|
|
733
787
|
safeSplit(key) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxgame2020/dsh-token-quota",
|
|
3
3
|
"description": "Daily per-model token quota accounting, enforcement, and a floating quota panel for the DeepSeek Harness — a fully self-contained plugin (no core-Harness wiring, no generated RPC contract)",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.12",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|