dsh-grok-subscription 0.1.3 → 0.1.13
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/README.md +79 -3
- package/lib/client.js +613 -49
- package/lib/index.js +914 -309
- package/package.json +21 -2
package/lib/client.js
CHANGED
|
@@ -21,18 +21,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var client_exports = {};
|
|
22
22
|
__export(client_exports, {
|
|
23
23
|
GrokSubscriptionSection: () => GrokSubscriptionSection,
|
|
24
|
+
RPC_CALL_TIMEOUT_MS: () => RPC_CALL_TIMEOUT_MS,
|
|
24
25
|
apply: () => apply,
|
|
26
|
+
callRpc: () => callRpc,
|
|
25
27
|
inject: () => inject,
|
|
26
28
|
pickCopy: () => pickCopy
|
|
27
29
|
});
|
|
28
30
|
module.exports = __toCommonJS(client_exports);
|
|
29
|
-
var
|
|
31
|
+
var import_react2 = require("react");
|
|
30
32
|
|
|
31
33
|
// src/locales.js
|
|
32
34
|
var zh = {
|
|
33
35
|
nav: "Grok \u8BA2\u9605",
|
|
34
36
|
title: "Grok \u8BA2\u9605",
|
|
35
|
-
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002",
|
|
37
|
+
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 0.1.13",
|
|
36
38
|
signedIn: "\u5DF2\u767B\u5F55",
|
|
37
39
|
signedOut: "\u672A\u767B\u5F55",
|
|
38
40
|
account: "\u8D26\u6237",
|
|
@@ -51,17 +53,35 @@ var zh = {
|
|
|
51
53
|
deviceHint: "\u8BBE\u5907\u7801\u63D0\u793A\u4F1A\u51FA\u73B0\u5728 DSH \u7EC8\u7AEF\u3002\u4E5F\u53EF\u624B\u52A8\u8FD0\u884C grok login --device-auth\uFF0C\u5B8C\u6210\u540E\u70B9\u201C\u4ECE Grok CLI \u62C9\u53D6\u201D\u3002",
|
|
52
54
|
pullHint: "\u5B89\u5168\u8BFB\u53D6 ~/.grok/auth.json\uFF08\u62D2\u7EDD\u7B26\u53F7\u94FE\u63A5\u4E0E\u7EC4/\u5176\u4ED6\u4EBA\u53EF\u8BFB\uFF09\uFF0C\u53EA\u628A\u77ED\u671F access token \u5199\u5165 DSH\u3002",
|
|
53
55
|
busy: "\u5904\u7406\u4E2D\u2026",
|
|
56
|
+
busyCatalog: "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u76EE\u5F55\u2026",
|
|
57
|
+
busyUsage: "\u6B63\u5728\u5237\u65B0\u7528\u91CF\u2026",
|
|
54
58
|
error: "\u64CD\u4F5C\u5931\u8D25",
|
|
55
59
|
pullOk: "\u5DF2\u4ECE Grok CLI \u62C9\u53D6\u6210\u529F",
|
|
56
60
|
loginOk: "\u767B\u5F55\u5B8C\u6210\uFF0C\u4F1A\u8BDD\u5DF2\u540C\u6B65",
|
|
57
61
|
logoutOk: "\u5DF2\u9000\u51FA\u767B\u5F55",
|
|
58
62
|
cliMissing: "\u672A\u53D1\u73B0 grok CLI\u3002\u8BF7\u5148\u5B89\u88C5\u5B98\u65B9 CLI \u5E76\u8FD0\u884C grok login\uFF0C\u7136\u540E\u62C9\u53D6\u3002",
|
|
59
|
-
caveats: "\u793E\u533A\u63D2\u4EF6\uFF0C\u4E0D\u662F\u5B98\u65B9\u4EA7\u54C1\u3002\u8BA2\u9605\u7528\u4E8E\u975E\u5B98\u65B9\u5BA2\u6237\u7AEF\u53EF\u80FD\u5904\u4E8E\u4F9B\u5E94\u5546\u6761\u6B3E\u7070\u8272\u5730\u5E26\uFF1B\u53EA\u4F7F\u7528\u4F60\u81EA\u5DF1\u7684\u8D26\u53F7\u3002\u534F\u8BAE\u53EF\u80FD\u53D8\u5316\uFF0C\u56E0\u4E3A\u5B98\u65B9\u6587\u6863\u672A\u516C\u5F00\u5B8C\u6574 HTTP wire protocol\u3002"
|
|
63
|
+
caveats: "\u793E\u533A\u63D2\u4EF6\uFF0C\u4E0D\u662F\u5B98\u65B9\u4EA7\u54C1\u3002\u8BA2\u9605\u7528\u4E8E\u975E\u5B98\u65B9\u5BA2\u6237\u7AEF\u53EF\u80FD\u5904\u4E8E\u4F9B\u5E94\u5546\u6761\u6B3E\u7070\u8272\u5730\u5E26\uFF1B\u53EA\u4F7F\u7528\u4F60\u81EA\u5DF1\u7684\u8D26\u53F7\u3002\u534F\u8BAE\u53EF\u80FD\u53D8\u5316\uFF0C\u56E0\u4E3A\u5B98\u65B9\u6587\u6863\u672A\u516C\u5F00\u5B8C\u6574 HTTP wire protocol\u3002",
|
|
64
|
+
usageTitle: "\u7528\u91CF\uFF08\u5B9E\u9A8C\u6027\uFF09",
|
|
65
|
+
usageSubtitle: "\u6765\u81EA\u672A\u6587\u6863\u5316\u7684\u8BA2\u9605 billing API\uFF08/v1/billing?format=credits\uFF09\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u53EF\u80FD\u968F\u65F6\u5931\u6548\u3002",
|
|
66
|
+
usageUsed: "\u5DF2\u7528",
|
|
67
|
+
usageRemaining: "\u5269\u4F59\u7EA6",
|
|
68
|
+
usageReset: "\u5468\u671F\u7ED3\u675F / \u91CD\u7F6E",
|
|
69
|
+
usageUnavailable: "\u4E0D\u53EF\u7528",
|
|
70
|
+
usageRefresh: "\u5237\u65B0\u7528\u91CF",
|
|
71
|
+
usageOpenGrok: "\u5728 grok.com \u67E5\u770B\u7528\u91CF",
|
|
72
|
+
usageProduct: "\u4EA7\u54C1\u660E\u7EC6",
|
|
73
|
+
usageFetchedAt: "\u6700\u8FD1\u5237\u65B0",
|
|
74
|
+
usageRefreshOk: "\u7528\u91CF\u5DF2\u5237\u65B0",
|
|
75
|
+
catalogRefreshOk: "\u6A21\u578B\u76EE\u5F55\u5DF2\u5237\u65B0",
|
|
76
|
+
composerQuotaDetails: "\u6BCF\u5468\u989D\u5EA6",
|
|
77
|
+
composerQuotaRemaining: "\u6BCF\u5468\u989D\u5EA6 \u5269\u4F59 {remaining}%",
|
|
78
|
+
composerQuotaResets: "\u91CD\u7F6E\u4E8E {reset}",
|
|
79
|
+
composerQuotaResetUnknown: "\u91CD\u7F6E\u65F6\u95F4\u672A\u77E5"
|
|
60
80
|
};
|
|
61
81
|
var en = {
|
|
62
82
|
nav: "Grok Subscription",
|
|
63
83
|
title: "Grok Subscription",
|
|
64
|
-
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY.",
|
|
84
|
+
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 0.1.13",
|
|
65
85
|
signedIn: "Signed in",
|
|
66
86
|
signedOut: "Signed out",
|
|
67
87
|
account: "Account",
|
|
@@ -80,12 +100,30 @@ var en = {
|
|
|
80
100
|
deviceHint: "The device-code prompt appears in the DSH terminal. You can also run grok login --device-auth yourself, then Pull from Grok CLI.",
|
|
81
101
|
pullHint: "Reads ~/.grok/auth.json securely (refuses symlinks and group/other-readable files) and stores only the short-lived access token in DSH.",
|
|
82
102
|
busy: "Working\u2026",
|
|
103
|
+
busyCatalog: "Refreshing model catalog\u2026",
|
|
104
|
+
busyUsage: "Refreshing usage\u2026",
|
|
83
105
|
error: "Request failed",
|
|
84
106
|
pullOk: "Pulled from Grok CLI successfully",
|
|
85
107
|
loginOk: "Signed in and session synced",
|
|
86
108
|
logoutOk: "Signed out",
|
|
87
109
|
cliMissing: "The grok CLI was not found. Install it, run grok login, then pull.",
|
|
88
|
-
caveats: "Community plugin, not an official product. Using a subscription from an unofficial client may be a vendor-ToS gray area; use your own account only. The protocol can change because official docs do not publish the full HTTP wire protocol."
|
|
110
|
+
caveats: "Community plugin, not an official product. Using a subscription from an unofficial client may be a vendor-ToS gray area; use your own account only. The protocol can change because official docs do not publish the full HTTP wire protocol.",
|
|
111
|
+
usageTitle: "Usage (experimental)",
|
|
112
|
+
usageSubtitle: "From the undocumented subscription billing API (/v1/billing?format=credits). Informational only; the endpoint may change or disappear.",
|
|
113
|
+
usageUsed: "Used",
|
|
114
|
+
usageRemaining: "Remaining \u2248",
|
|
115
|
+
usageReset: "Period end / reset",
|
|
116
|
+
usageUnavailable: "Unavailable",
|
|
117
|
+
usageRefresh: "Refresh usage",
|
|
118
|
+
usageOpenGrok: "View usage on grok.com",
|
|
119
|
+
usageProduct: "Product breakdown",
|
|
120
|
+
usageFetchedAt: "Last refreshed",
|
|
121
|
+
usageRefreshOk: "Usage refreshed",
|
|
122
|
+
catalogRefreshOk: "Model catalog refreshed",
|
|
123
|
+
composerQuotaDetails: "Weekly quota",
|
|
124
|
+
composerQuotaRemaining: "Weekly quota \xB7 {remaining}% left",
|
|
125
|
+
composerQuotaResets: "Resets {reset}",
|
|
126
|
+
composerQuotaResetUnknown: "Reset time unknown"
|
|
89
127
|
};
|
|
90
128
|
|
|
91
129
|
// src/rpc-contract.js
|
|
@@ -96,7 +134,9 @@ var RPC_ENDPOINTS = Object.freeze([
|
|
|
96
134
|
"login/device",
|
|
97
135
|
"pull",
|
|
98
136
|
"logout",
|
|
99
|
-
"catalog/refresh"
|
|
137
|
+
"catalog/refresh",
|
|
138
|
+
"usage",
|
|
139
|
+
"usage/refresh"
|
|
100
140
|
]);
|
|
101
141
|
function createRpcClient(transport) {
|
|
102
142
|
return Object.freeze({
|
|
@@ -114,7 +154,9 @@ function unwrap(response) {
|
|
|
114
154
|
}
|
|
115
155
|
|
|
116
156
|
// src/constants.js
|
|
157
|
+
var PROVIDER_ID = "grok-build";
|
|
117
158
|
var LOCALE_NS = "settings.grokSubscription";
|
|
159
|
+
var USAGE_PAGE_URL = "https://grok.com/?_s=usage";
|
|
118
160
|
var SESSION_AUTH_MODES = Object.freeze(["oidc", "external", "web_login", "grok"]);
|
|
119
161
|
var API_KEY_AUTH_MODES = Object.freeze(["api_key"]);
|
|
120
162
|
var STREAM_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -123,9 +165,340 @@ var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
|
|
|
123
165
|
var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
|
|
124
166
|
var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
|
|
125
167
|
|
|
126
|
-
// src/client.jsx
|
|
168
|
+
// src/client-composer-quota.jsx
|
|
169
|
+
var import_react = require("react");
|
|
170
|
+
var import_react_dom = require("react-dom");
|
|
171
|
+
|
|
172
|
+
// src/client-composer-quota-shared.js
|
|
173
|
+
var QUICK_QUOTA_REFRESH_EVENT = "dsh-grok-subscription:refresh-quick-quota";
|
|
174
|
+
var QUICK_QUOTA_REFRESH_MS = 6e4;
|
|
175
|
+
var COMPOSER_QUOTA_STYLE = `
|
|
176
|
+
.grokComposerQuota:focus-visible{outline:1px solid var(--dsw-alias-border-l3);outline-offset:2px}
|
|
177
|
+
.grokComposerQuota{display:inline-flex;align-items:center;gap:9px;flex:0 0 auto;height:28px;box-sizing:border-box;padding:0 5px;border:0;border-radius:6px;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
178
|
+
.grokComposerQuota:hover,.grokComposerQuota[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}
|
|
179
|
+
.grokQuotaPopover{position:fixed;z-index:1000;width:max-content;max-width:calc(100vw - 24px);max-height:calc(100vh - 24px);overflow:auto;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 4px 16px #0002;font-size:12px;line-height:20px;outline:none}
|
|
180
|
+
.grokQuotaDetail>div{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
181
|
+
.grokQuotaDetail strong{font-weight:500}
|
|
182
|
+
.grokQuotaReset{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}
|
|
183
|
+
`;
|
|
184
|
+
function fillTemplate(text, values) {
|
|
185
|
+
return Object.entries(values).reduce(
|
|
186
|
+
(next, [key, value]) => next.replaceAll(`{${key}}`, String(value)),
|
|
187
|
+
String(text ?? "")
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
function formatRemainingPercent(value) {
|
|
191
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
192
|
+
const rounded = Math.round(value * 10) / 10;
|
|
193
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
194
|
+
}
|
|
195
|
+
function formatShortReset(usage) {
|
|
196
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
197
|
+
const iso = typeof usage.periodEnd === "string" ? usage.periodEnd : void 0;
|
|
198
|
+
if (iso) {
|
|
199
|
+
const ms = Date.parse(iso);
|
|
200
|
+
if (Number.isFinite(ms)) {
|
|
201
|
+
return new Date(ms).toLocaleString(void 0, {
|
|
202
|
+
month: "numeric",
|
|
203
|
+
day: "numeric",
|
|
204
|
+
hour: "2-digit",
|
|
205
|
+
minute: "2-digit"
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const local = typeof usage.periodEndLocal === "string" ? usage.periodEndLocal.trim() : "";
|
|
210
|
+
if (!local) return void 0;
|
|
211
|
+
const match = local.match(/(\d{4})[/-](\d{1,2})[/-](\d{1,2})\s+(\d{1,2}):(\d{2})/);
|
|
212
|
+
if (match) {
|
|
213
|
+
return `${Number(match[2])}/${Number(match[3])} ${match[4]}:${match[5]}`;
|
|
214
|
+
}
|
|
215
|
+
return local;
|
|
216
|
+
}
|
|
217
|
+
function isComposerQuotaEnabled(provider, usage) {
|
|
218
|
+
if (provider !== PROVIDER_ID) return false;
|
|
219
|
+
if (!usage || usage.status !== "ok") return false;
|
|
220
|
+
return typeof usage.remainingPercent === "number" && Number.isFinite(usage.remainingPercent);
|
|
221
|
+
}
|
|
222
|
+
function notifyQuickQuota() {
|
|
223
|
+
try {
|
|
224
|
+
if (typeof window !== "undefined") {
|
|
225
|
+
window.dispatchEvent(new Event(QUICK_QUOTA_REFRESH_EVENT));
|
|
226
|
+
}
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/client-composer-quota.jsx
|
|
127
232
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
128
|
-
var
|
|
233
|
+
var QUICK_RPC_TIMEOUT_MS = 12e3;
|
|
234
|
+
async function quickCall(rpc, endpoint) {
|
|
235
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
236
|
+
let timer;
|
|
237
|
+
try {
|
|
238
|
+
const call = rpc.call(CHANNEL, endpoint, {}, controller?.signal);
|
|
239
|
+
const timeout = new Promise((_, reject) => {
|
|
240
|
+
timer = setTimeout(() => {
|
|
241
|
+
try {
|
|
242
|
+
controller?.abort();
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
reject(new Error(`Request timed out after ${QUICK_RPC_TIMEOUT_MS}ms`));
|
|
246
|
+
}, QUICK_RPC_TIMEOUT_MS);
|
|
247
|
+
});
|
|
248
|
+
return unwrap(await Promise.race([call, timeout]));
|
|
249
|
+
} finally {
|
|
250
|
+
if (timer) clearTimeout(timer);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function useAnchoredPositionFallback({ open, anchorRef }) {
|
|
254
|
+
const [position, setPosition] = (0, import_react.useState)(null);
|
|
255
|
+
(0, import_react.useEffect)(() => {
|
|
256
|
+
if (!open || !anchorRef?.current) {
|
|
257
|
+
setPosition(null);
|
|
258
|
+
return void 0;
|
|
259
|
+
}
|
|
260
|
+
const place = () => {
|
|
261
|
+
const rect = anchorRef.current.getBoundingClientRect();
|
|
262
|
+
setPosition({
|
|
263
|
+
position: "fixed",
|
|
264
|
+
left: Math.max(12, Math.min(rect.left, window.innerWidth - 12)),
|
|
265
|
+
top: Math.max(12, rect.top - 8),
|
|
266
|
+
transform: "translateY(-100%)"
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
place();
|
|
270
|
+
window.addEventListener("resize", place);
|
|
271
|
+
window.addEventListener("scroll", place, true);
|
|
272
|
+
return () => {
|
|
273
|
+
window.removeEventListener("resize", place);
|
|
274
|
+
window.removeEventListener("scroll", place, true);
|
|
275
|
+
};
|
|
276
|
+
}, [open, anchorRef]);
|
|
277
|
+
return position;
|
|
278
|
+
}
|
|
279
|
+
function useDismissOnOutsidePointerFallback(triggerRef, open, dismiss, panelRef) {
|
|
280
|
+
(0, import_react.useEffect)(() => {
|
|
281
|
+
if (!open) return void 0;
|
|
282
|
+
const onPointer = (event) => {
|
|
283
|
+
const target = event.target;
|
|
284
|
+
if (triggerRef.current?.contains?.(target) || panelRef.current?.contains?.(target)) return;
|
|
285
|
+
dismiss();
|
|
286
|
+
};
|
|
287
|
+
document.addEventListener("pointerdown", onPointer, true);
|
|
288
|
+
return () => document.removeEventListener("pointerdown", onPointer, true);
|
|
289
|
+
}, [open, triggerRef, panelRef, dismiss]);
|
|
290
|
+
}
|
|
291
|
+
function loadAnchoringHooks() {
|
|
292
|
+
try {
|
|
293
|
+
const req = typeof require === "function" ? require : void 0;
|
|
294
|
+
const mod = req?.("@deepseek-ai/dsh-client-ui-primitives");
|
|
295
|
+
if (mod?.useAnchoredPosition && mod?.useDismissOnOutsidePointer) {
|
|
296
|
+
return {
|
|
297
|
+
useAnchoredPosition: mod.useAnchoredPosition,
|
|
298
|
+
useDismissOnOutsidePointer: mod.useDismissOnOutsidePointer
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
useAnchoredPosition: useAnchoredPositionFallback,
|
|
305
|
+
useDismissOnOutsidePointer: useDismissOnOutsidePointerFallback
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
var anchoring = loadAnchoringHooks();
|
|
309
|
+
function useGrokQuickQuota(rpc, enabled) {
|
|
310
|
+
const [usage, setUsage] = (0, import_react.useState)(void 0);
|
|
311
|
+
(0, import_react.useEffect)(() => {
|
|
312
|
+
if (!enabled) {
|
|
313
|
+
setUsage(void 0);
|
|
314
|
+
return void 0;
|
|
315
|
+
}
|
|
316
|
+
let live = true;
|
|
317
|
+
let loading = false;
|
|
318
|
+
const accept = (next) => {
|
|
319
|
+
if (!live) return;
|
|
320
|
+
if (next && next.status === "ok" && typeof next.remainingPercent === "number" && Number.isFinite(next.remainingPercent)) {
|
|
321
|
+
setUsage(next);
|
|
322
|
+
} else {
|
|
323
|
+
setUsage(void 0);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const softRefresh = () => {
|
|
327
|
+
void quickCall(rpc, "usage/refresh").then((value) => {
|
|
328
|
+
if (value?.usage) accept(value.usage);
|
|
329
|
+
}).catch(() => {
|
|
330
|
+
});
|
|
331
|
+
};
|
|
332
|
+
const load = async () => {
|
|
333
|
+
if (loading) return;
|
|
334
|
+
loading = true;
|
|
335
|
+
try {
|
|
336
|
+
let status;
|
|
337
|
+
try {
|
|
338
|
+
status = await quickCall(rpc, "status");
|
|
339
|
+
} catch {
|
|
340
|
+
status = void 0;
|
|
341
|
+
}
|
|
342
|
+
if (!live) return;
|
|
343
|
+
if (status?.account?.signedIn !== true) {
|
|
344
|
+
try {
|
|
345
|
+
const value = await quickCall(rpc, "usage");
|
|
346
|
+
accept(value?.usage);
|
|
347
|
+
} catch {
|
|
348
|
+
accept(void 0);
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
let next = status?.usage;
|
|
353
|
+
if (!next || next.status !== "ok") {
|
|
354
|
+
try {
|
|
355
|
+
const value = await quickCall(rpc, "usage");
|
|
356
|
+
next = value?.usage ?? next;
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (!live) return;
|
|
361
|
+
accept(next);
|
|
362
|
+
softRefresh();
|
|
363
|
+
} catch {
|
|
364
|
+
if (live) setUsage(void 0);
|
|
365
|
+
} finally {
|
|
366
|
+
loading = false;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
const refresh = () => {
|
|
370
|
+
void load();
|
|
371
|
+
};
|
|
372
|
+
void load();
|
|
373
|
+
const timer = window.setInterval(refresh, QUICK_QUOTA_REFRESH_MS);
|
|
374
|
+
window.addEventListener(QUICK_QUOTA_REFRESH_EVENT, refresh);
|
|
375
|
+
return () => {
|
|
376
|
+
live = false;
|
|
377
|
+
window.clearInterval(timer);
|
|
378
|
+
window.removeEventListener(QUICK_QUOTA_REFRESH_EVENT, refresh);
|
|
379
|
+
};
|
|
380
|
+
}, [rpc, enabled]);
|
|
381
|
+
return usage;
|
|
382
|
+
}
|
|
383
|
+
function GrokComposerQuota({ rpc, t, directory }) {
|
|
384
|
+
const modelState = (0, import_react.useSyncExternalStore)(
|
|
385
|
+
(listener) => directory.subscribe(listener),
|
|
386
|
+
() => directory.getSnapshot()
|
|
387
|
+
);
|
|
388
|
+
const current = modelState?.current;
|
|
389
|
+
const provider = current?.provider;
|
|
390
|
+
const providerEnabled = provider === PROVIDER_ID;
|
|
391
|
+
const usage = useGrokQuickQuota(rpc, providerEnabled);
|
|
392
|
+
const enabled = isComposerQuotaEnabled(provider, usage);
|
|
393
|
+
const remaining = enabled ? formatRemainingPercent(usage.remainingPercent) : void 0;
|
|
394
|
+
const [open, setOpen] = (0, import_react.useState)(false);
|
|
395
|
+
const trigger = (0, import_react.useRef)(null);
|
|
396
|
+
const panel = (0, import_react.useRef)(null);
|
|
397
|
+
const pinned = (0, import_react.useRef)(false);
|
|
398
|
+
const dismissTimer = (0, import_react.useRef)(null);
|
|
399
|
+
const enter = () => {
|
|
400
|
+
clearTimeout(dismissTimer.current);
|
|
401
|
+
setOpen(true);
|
|
402
|
+
};
|
|
403
|
+
const leave = () => {
|
|
404
|
+
if (!pinned.current) dismissTimer.current = setTimeout(() => setOpen(false), 150);
|
|
405
|
+
};
|
|
406
|
+
const dismiss = () => {
|
|
407
|
+
pinned.current = false;
|
|
408
|
+
setOpen(false);
|
|
409
|
+
};
|
|
410
|
+
(0, import_react.useEffect)(() => () => clearTimeout(dismissTimer.current), []);
|
|
411
|
+
const id = (0, import_react.useId)();
|
|
412
|
+
const visible = open && enabled && remaining !== void 0;
|
|
413
|
+
const position = anchoring.useAnchoredPosition({
|
|
414
|
+
open: visible,
|
|
415
|
+
anchorRef: trigger,
|
|
416
|
+
panelRef: panel,
|
|
417
|
+
side: "top",
|
|
418
|
+
gap: 8,
|
|
419
|
+
margin: 12
|
|
420
|
+
});
|
|
421
|
+
anchoring.useDismissOnOutsidePointer(trigger, visible, dismiss, panel);
|
|
422
|
+
(0, import_react.useEffect)(() => {
|
|
423
|
+
setOpen(false);
|
|
424
|
+
}, [current?.model, enabled]);
|
|
425
|
+
(0, import_react.useEffect)(() => {
|
|
426
|
+
if (!visible) return void 0;
|
|
427
|
+
const escape = (event) => {
|
|
428
|
+
if (event.key !== "Escape") return;
|
|
429
|
+
event.preventDefault();
|
|
430
|
+
dismiss();
|
|
431
|
+
trigger.current?.focus?.();
|
|
432
|
+
};
|
|
433
|
+
document.addEventListener("keydown", escape);
|
|
434
|
+
return () => document.removeEventListener("keydown", escape);
|
|
435
|
+
}, [visible]);
|
|
436
|
+
if (!enabled || remaining === void 0) return null;
|
|
437
|
+
const reset = formatShortReset(usage);
|
|
438
|
+
const remainingLine = fillTemplate(t("composerQuotaRemaining"), { remaining });
|
|
439
|
+
const resetLine = reset ? fillTemplate(t("composerQuotaResets"), { reset }) : t("composerQuotaResetUnknown");
|
|
440
|
+
const detailsLabel = `${remainingLine} ${resetLine}`;
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
443
|
+
"button",
|
|
444
|
+
{
|
|
445
|
+
ref: trigger,
|
|
446
|
+
type: "button",
|
|
447
|
+
className: "grokComposerQuota",
|
|
448
|
+
title: detailsLabel,
|
|
449
|
+
"aria-label": `${t("composerQuotaDetails")}: ${detailsLabel}`,
|
|
450
|
+
"aria-haspopup": "dialog",
|
|
451
|
+
"aria-expanded": visible,
|
|
452
|
+
"aria-controls": visible ? id : void 0,
|
|
453
|
+
onMouseEnter: enter,
|
|
454
|
+
onMouseLeave: leave,
|
|
455
|
+
onClick: () => {
|
|
456
|
+
if (pinned.current) dismiss();
|
|
457
|
+
else {
|
|
458
|
+
pinned.current = true;
|
|
459
|
+
setOpen(true);
|
|
460
|
+
requestAnimationFrame(() => panel.current?.focus?.());
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: `${remaining}%` })
|
|
464
|
+
}
|
|
465
|
+
),
|
|
466
|
+
visible ? (0, import_react_dom.createPortal)(
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
468
|
+
"section",
|
|
469
|
+
{
|
|
470
|
+
ref: panel,
|
|
471
|
+
id,
|
|
472
|
+
role: "dialog",
|
|
473
|
+
"aria-label": t("composerQuotaDetails"),
|
|
474
|
+
className: "grokQuotaPopover",
|
|
475
|
+
tabIndex: -1,
|
|
476
|
+
onMouseEnter: enter,
|
|
477
|
+
onMouseLeave: leave,
|
|
478
|
+
style: { ...position || {}, visibility: position ? "visible" : "hidden" },
|
|
479
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grokQuotaDetail", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
480
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: remainingLine }),
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "grokQuotaReset", children: resetLine })
|
|
482
|
+
] }) })
|
|
483
|
+
}
|
|
484
|
+
),
|
|
485
|
+
document.body
|
|
486
|
+
) : null
|
|
487
|
+
] });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/client.jsx
|
|
491
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
492
|
+
var inject = [
|
|
493
|
+
"slots",
|
|
494
|
+
"locale",
|
|
495
|
+
"connection",
|
|
496
|
+
"settingsScope",
|
|
497
|
+
"modelDirectories",
|
|
498
|
+
"sessions",
|
|
499
|
+
"remote"
|
|
500
|
+
];
|
|
501
|
+
var RPC_CALL_TIMEOUT_MS = 12e3;
|
|
129
502
|
function pickCopy(locale) {
|
|
130
503
|
const language = typeof locale === "string" ? locale : locale?.language ?? locale?.lang;
|
|
131
504
|
const resolved = language ?? (typeof navigator !== "undefined" ? navigator.language : "zh-CN");
|
|
@@ -136,30 +509,148 @@ function sourceLabel(source, t) {
|
|
|
136
509
|
if (source === "fallback") return t("catalogSourceFallback");
|
|
137
510
|
return t("catalogSourceSignedOut");
|
|
138
511
|
}
|
|
512
|
+
function formatPercent(value) {
|
|
513
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
514
|
+
const rounded = Math.round(value * 10) / 10;
|
|
515
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
516
|
+
}
|
|
517
|
+
async function callRpc(rpc, endpoint, payload = {}) {
|
|
518
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
519
|
+
let timer;
|
|
520
|
+
try {
|
|
521
|
+
const call = rpc.call(CHANNEL, endpoint, payload, controller?.signal);
|
|
522
|
+
const timeout = new Promise((_, reject) => {
|
|
523
|
+
timer = setTimeout(() => {
|
|
524
|
+
try {
|
|
525
|
+
controller?.abort();
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
reject(new Error(`Request timed out after ${RPC_CALL_TIMEOUT_MS}ms`));
|
|
529
|
+
}, RPC_CALL_TIMEOUT_MS);
|
|
530
|
+
});
|
|
531
|
+
return unwrap(await Promise.race([call, timeout]));
|
|
532
|
+
} finally {
|
|
533
|
+
if (timer) clearTimeout(timer);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
|
|
537
|
+
const ok = usage?.status === "ok";
|
|
538
|
+
const used = ok ? formatPercent(usage.usedPercent) : void 0;
|
|
539
|
+
const remaining = ok ? formatPercent(usage.remainingPercent) : void 0;
|
|
540
|
+
const resetLabel = usage?.periodEndLocal || usage?.periodEnd;
|
|
541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "usageBlock", children: [
|
|
542
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("usageTitle") }),
|
|
543
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageSubtitle") }),
|
|
544
|
+
ok && used !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
545
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
|
|
546
|
+
t("usageUsed"),
|
|
547
|
+
": ",
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
|
|
549
|
+
used,
|
|
550
|
+
"%"
|
|
551
|
+
] }),
|
|
552
|
+
remaining !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
553
|
+
" \xB7 ",
|
|
554
|
+
t("usageRemaining"),
|
|
555
|
+
" ",
|
|
556
|
+
remaining,
|
|
557
|
+
"%"
|
|
558
|
+
] }) : null
|
|
559
|
+
] }),
|
|
560
|
+
resetLabel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
|
|
561
|
+
t("usageReset"),
|
|
562
|
+
": ",
|
|
563
|
+
resetLabel,
|
|
564
|
+
usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
565
|
+
" (",
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }),
|
|
567
|
+
")"
|
|
568
|
+
] }) : null
|
|
569
|
+
] }) : null,
|
|
570
|
+
Array.isArray(usage.productUsage) && usage.productUsage.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
571
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageProduct") }),
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: usage.productUsage.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
|
|
573
|
+
row.name ?? "\u2014",
|
|
574
|
+
typeof row.usedPercent === "number" ? ` \xB7 ${formatPercent(row.usedPercent)}%` : ""
|
|
575
|
+
] }, `${row.name ?? "row"}-${index}`)) })
|
|
576
|
+
] }) : null,
|
|
577
|
+
usage.fetchedAt ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
|
|
578
|
+
t("usageFetchedAt"),
|
|
579
|
+
": ",
|
|
580
|
+
usage.fetchedAt
|
|
581
|
+
] }) : null
|
|
582
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
|
|
583
|
+
t("usageUnavailable"),
|
|
584
|
+
usage?.reason ? `: ${usage.reason}` : ""
|
|
585
|
+
] }),
|
|
586
|
+
usageBusy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busyUsage") }) : null,
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
|
|
588
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(usageBusy) || !signedIn, onClick: onRefresh, children: t("usageRefresh") }),
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: USAGE_PAGE_URL, target: "_blank", rel: "noreferrer", children: t("usageOpenGrok") })
|
|
590
|
+
] })
|
|
591
|
+
] });
|
|
592
|
+
}
|
|
139
593
|
function GrokSubscriptionSection({ rpc, t }) {
|
|
140
|
-
const [busy, setBusy] = (0,
|
|
141
|
-
const [
|
|
142
|
-
const [
|
|
143
|
-
const [
|
|
594
|
+
const [busy, setBusy] = (0, import_react2.useState)("");
|
|
595
|
+
const [usageBusy, setUsageBusy] = (0, import_react2.useState)(false);
|
|
596
|
+
const [error, setError] = (0, import_react2.useState)("");
|
|
597
|
+
const [notice, setNotice] = (0, import_react2.useState)("");
|
|
598
|
+
const [status, setStatus] = (0, import_react2.useState)(void 0);
|
|
599
|
+
const initialUsageKick = (0, import_react2.useRef)(false);
|
|
600
|
+
const applyPartial = (value) => {
|
|
601
|
+
if (value?.account || value?.catalog || value?.usage) {
|
|
602
|
+
setStatus((current) => ({ ...current, ...value }));
|
|
603
|
+
}
|
|
604
|
+
};
|
|
144
605
|
const load = async () => {
|
|
145
|
-
const value =
|
|
606
|
+
const value = await callRpc(rpc, "status", {});
|
|
146
607
|
setStatus(value);
|
|
147
608
|
setError("");
|
|
609
|
+
return value;
|
|
148
610
|
};
|
|
149
|
-
(0,
|
|
150
|
-
void load().
|
|
611
|
+
(0, import_react2.useEffect)(() => {
|
|
612
|
+
void load().then((value) => {
|
|
613
|
+
if (initialUsageKick.current) return;
|
|
614
|
+
if (value?.account?.signedIn !== true) return;
|
|
615
|
+
initialUsageKick.current = true;
|
|
616
|
+
void callRpc(rpc, "usage/refresh", {}).then((partial) => {
|
|
617
|
+
applyPartial(partial);
|
|
618
|
+
notifyQuickQuota();
|
|
619
|
+
}).catch(() => {
|
|
620
|
+
});
|
|
621
|
+
}).catch((item) => {
|
|
151
622
|
setNotice("");
|
|
152
623
|
setError(item instanceof Error ? item.message : String(item));
|
|
153
624
|
});
|
|
154
625
|
}, [rpc]);
|
|
626
|
+
const kickFollowUpRefresh = () => {
|
|
627
|
+
void callRpc(rpc, "catalog/refresh", {}).then((partial) => {
|
|
628
|
+
applyPartial(partial);
|
|
629
|
+
}).catch(() => {
|
|
630
|
+
});
|
|
631
|
+
void callRpc(rpc, "usage/refresh", {}).then((partial) => {
|
|
632
|
+
applyPartial(partial);
|
|
633
|
+
notifyQuickQuota();
|
|
634
|
+
}).catch(() => {
|
|
635
|
+
});
|
|
636
|
+
};
|
|
155
637
|
const run = async (endpoint) => {
|
|
156
|
-
|
|
638
|
+
const isUsageRefresh = endpoint === "usage/refresh";
|
|
639
|
+
if (isUsageRefresh) {
|
|
640
|
+
setUsageBusy(true);
|
|
641
|
+
} else {
|
|
642
|
+
setBusy(endpoint);
|
|
643
|
+
}
|
|
157
644
|
setError("");
|
|
158
645
|
setNotice("");
|
|
646
|
+
let value;
|
|
159
647
|
try {
|
|
160
|
-
|
|
161
|
-
if (value?.account || value?.catalog
|
|
162
|
-
|
|
648
|
+
value = await callRpc(rpc, endpoint, {});
|
|
649
|
+
if (value?.account || value?.catalog || value?.usage) {
|
|
650
|
+
applyPartial(value);
|
|
651
|
+
} else {
|
|
652
|
+
await load();
|
|
653
|
+
}
|
|
163
654
|
if (value?.ok === false && value.error) {
|
|
164
655
|
setNotice("");
|
|
165
656
|
setError(value.error);
|
|
@@ -168,81 +659,113 @@ function GrokSubscriptionSection({ rpc, t }) {
|
|
|
168
659
|
pull: "pullOk",
|
|
169
660
|
"login/cli": "loginOk",
|
|
170
661
|
"login/device": "loginOk",
|
|
171
|
-
logout: "logoutOk"
|
|
662
|
+
logout: "logoutOk",
|
|
663
|
+
"usage/refresh": "usageRefreshOk"
|
|
172
664
|
}[endpoint];
|
|
173
665
|
if (successKey) setNotice(t(successKey));
|
|
666
|
+
if (endpoint === "usage/refresh" && value?.ok !== false && !value?.error) {
|
|
667
|
+
notifyQuickQuota();
|
|
668
|
+
}
|
|
174
669
|
}
|
|
175
670
|
} catch (item) {
|
|
176
671
|
setNotice("");
|
|
177
672
|
setError(item instanceof Error ? item.message : String(item));
|
|
178
673
|
} finally {
|
|
179
|
-
|
|
674
|
+
if (isUsageRefresh) setUsageBusy(false);
|
|
675
|
+
else setBusy("");
|
|
180
676
|
}
|
|
677
|
+
const shouldFollowUp = (endpoint === "pull" || endpoint === "login/cli" || endpoint === "login/device") && value?.ok !== false && !value?.error;
|
|
678
|
+
if (shouldFollowUp) kickFollowUpRefresh();
|
|
181
679
|
};
|
|
182
680
|
const account = status?.account;
|
|
183
681
|
const catalog = status?.catalog;
|
|
184
682
|
const models = catalog?.models ?? [];
|
|
185
683
|
const signedIn = account?.signedIn === true;
|
|
186
|
-
return /* @__PURE__ */ (0,
|
|
187
|
-
/* @__PURE__ */ (0,
|
|
684
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "grokSubscription", children: [
|
|
685
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
|
|
188
686
|
.grokSubscription { display: grid; gap: 12px; max-width: 42rem; }
|
|
189
687
|
.grokSubscription h2 { margin: 0 0 4px; font-size: 1.15rem; }
|
|
190
688
|
.grokSubscription p, .grokSubscription li { line-height: 1.5; }
|
|
191
689
|
.grokSubscription .muted { opacity: 0.78; font-size: 0.92rem; }
|
|
192
|
-
.grokSubscription .row { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
690
|
+
.grokSubscription .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
193
691
|
.grokSubscription button { cursor: pointer; }
|
|
194
692
|
.grokSubscription .error { color: #b42318; }
|
|
195
693
|
.grokSubscription .notice { color: #067647; }
|
|
196
694
|
.grokSubscription ul { margin: 0; padding-left: 1.2rem; }
|
|
695
|
+
.grokSubscription .usageBlock { display: grid; gap: 8px; padding: 10px 0; border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent); }
|
|
696
|
+
.grokSubscription a { color: inherit; }
|
|
197
697
|
` }),
|
|
198
|
-
/* @__PURE__ */ (0,
|
|
199
|
-
/* @__PURE__ */ (0,
|
|
200
|
-
/* @__PURE__ */ (0,
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
699
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t("title") }),
|
|
700
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("subtitle") })
|
|
201
701
|
] }),
|
|
202
|
-
/* @__PURE__ */ (0,
|
|
203
|
-
/* @__PURE__ */ (0,
|
|
702
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
|
|
204
704
|
t("account"),
|
|
205
705
|
": "
|
|
206
706
|
] }),
|
|
207
707
|
signedIn ? `${t("signedIn")} \xB7 ${account.maskedAccount ?? t("unknownAccount")}` : t("signedOut")
|
|
208
708
|
] }),
|
|
209
|
-
/* @__PURE__ */ (0,
|
|
210
|
-
/* @__PURE__ */ (0,
|
|
211
|
-
/* @__PURE__ */ (0,
|
|
212
|
-
/* @__PURE__ */ (0,
|
|
213
|
-
/* @__PURE__ */ (0,
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/cli"), children: t("loginCli") }),
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/device"), children: t("loginDevice") }),
|
|
712
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("pull"), children: t("pull") }),
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy) || !signedIn, onClick: () => void run("logout"), children: t("logout") })
|
|
214
714
|
] }),
|
|
215
|
-
busy ? /* @__PURE__ */ (0,
|
|
216
|
-
notice ? /* @__PURE__ */ (0,
|
|
217
|
-
error ? /* @__PURE__ */ (0,
|
|
715
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busy") }) : null,
|
|
716
|
+
notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: notice }) : null,
|
|
717
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
|
|
218
718
|
t("error"),
|
|
219
719
|
": ",
|
|
220
720
|
error
|
|
221
721
|
] }) : null,
|
|
222
|
-
/* @__PURE__ */ (0,
|
|
223
|
-
/* @__PURE__ */ (0,
|
|
224
|
-
/* @__PURE__ */ (0,
|
|
225
|
-
status?.cliAvailable === false ? /* @__PURE__ */ (0,
|
|
226
|
-
/* @__PURE__ */ (0,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
722
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("loginHint") }),
|
|
723
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("deviceHint") }),
|
|
724
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("pullHint") }),
|
|
725
|
+
status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("cliMissing") }) : null,
|
|
726
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
727
|
+
UsagePanel,
|
|
728
|
+
{
|
|
729
|
+
usage: status?.usage,
|
|
730
|
+
t,
|
|
731
|
+
usageBusy,
|
|
732
|
+
signedIn,
|
|
733
|
+
onRefresh: () => void run("usage/refresh")
|
|
734
|
+
}
|
|
735
|
+
),
|
|
736
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
737
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("models") }),
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: sourceLabel(catalog?.source, t) }),
|
|
739
|
+
models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("noModels") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
|
|
230
740
|
model.name,
|
|
231
741
|
" ",
|
|
232
|
-
/* @__PURE__ */ (0,
|
|
742
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: model.id })
|
|
233
743
|
] }, model.id)) }),
|
|
234
|
-
catalog?.error ? /* @__PURE__ */ (0,
|
|
744
|
+
catalog?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
|
|
235
745
|
t("catalogError"),
|
|
236
746
|
": ",
|
|
237
747
|
catalog.error
|
|
238
748
|
] }) : null
|
|
239
749
|
] }),
|
|
240
|
-
/* @__PURE__ */ (0,
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("caveats") })
|
|
241
751
|
] });
|
|
242
752
|
}
|
|
243
753
|
function apply(ctx) {
|
|
244
754
|
ctx.effect?.(() => ctx.locale?.register?.(LOCALE_NS, { zh, en }), "grok-subscription: copy");
|
|
245
|
-
|
|
755
|
+
let connection;
|
|
756
|
+
try {
|
|
757
|
+
connection = typeof ctx.get === "function" ? ctx.get("connection") : void 0;
|
|
758
|
+
} catch {
|
|
759
|
+
connection = void 0;
|
|
760
|
+
}
|
|
761
|
+
if (!connection) connection = ctx.connection;
|
|
762
|
+
if (!connection?.rpc) {
|
|
763
|
+
try {
|
|
764
|
+
ctx.logger?.warn?.("Grok subscription Settings section skipped: connection unavailable");
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
246
769
|
const rpc = createRpcClient(connection.rpc);
|
|
247
770
|
const bound = ctx.locale?.bind?.(LOCALE_NS);
|
|
248
771
|
const t = (key) => {
|
|
@@ -253,6 +776,14 @@ function apply(ctx) {
|
|
|
253
776
|
}
|
|
254
777
|
return pickCopy(ctx.locale)[key] ?? key;
|
|
255
778
|
};
|
|
779
|
+
ctx.effect?.(() => {
|
|
780
|
+
if (typeof document === "undefined") return void 0;
|
|
781
|
+
const tag = document.createElement("style");
|
|
782
|
+
tag.dataset.plugin = "dsh-grok-subscription";
|
|
783
|
+
tag.textContent = COMPOSER_QUOTA_STYLE;
|
|
784
|
+
document.head.append(tag);
|
|
785
|
+
return () => tag.remove();
|
|
786
|
+
}, "grok-subscription: composer-quota-style");
|
|
256
787
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
257
788
|
name: "settings.section",
|
|
258
789
|
id: "grok-subscription",
|
|
@@ -261,5 +792,38 @@ function apply(ctx) {
|
|
|
261
792
|
locale: LOCALE_NS,
|
|
262
793
|
inject: () => ({ rpc, t })
|
|
263
794
|
}, GrokSubscriptionSection));
|
|
795
|
+
const installDirectorySlots = (scope) => {
|
|
796
|
+
let modelDirectories;
|
|
797
|
+
try {
|
|
798
|
+
modelDirectories = typeof scope.get === "function" ? scope.get("modelDirectories") : void 0;
|
|
799
|
+
} catch {
|
|
800
|
+
modelDirectories = void 0;
|
|
801
|
+
}
|
|
802
|
+
if (!modelDirectories?.directoryFor) {
|
|
803
|
+
try {
|
|
804
|
+
ctx.logger?.warn?.("Grok composer quota skipped: modelDirectories unavailable");
|
|
805
|
+
} catch {
|
|
806
|
+
}
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
scope.slots.inject("conversation.input.right", () => scope.slots.register({
|
|
810
|
+
name: "conversation.input.right",
|
|
811
|
+
id: "grok-subscription-quota",
|
|
812
|
+
order: 16,
|
|
813
|
+
locale: LOCALE_NS,
|
|
814
|
+
inject: (sessionId) => ({
|
|
815
|
+
rpc,
|
|
816
|
+
t,
|
|
817
|
+
directory: modelDirectories.directoryFor(sessionId).store
|
|
818
|
+
})
|
|
819
|
+
}, GrokComposerQuota));
|
|
820
|
+
};
|
|
821
|
+
try {
|
|
822
|
+
const remoteSession = typeof ctx.get === "function" ? ctx.get("remote.session") : void 0;
|
|
823
|
+
if (remoteSession === void 0) installDirectorySlots(ctx);
|
|
824
|
+
else ctx.inject(["remote.session"], installDirectorySlots);
|
|
825
|
+
} catch {
|
|
826
|
+
installDirectorySlots(ctx);
|
|
827
|
+
}
|
|
264
828
|
}
|
|
265
829
|
return module.exports; } });
|