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/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var CREDENTIAL_REF_NAME = "GROK_BUILD_ACCESS_TOKEN";
|
|
|
8
8
|
var PROXY_BASE_URL = "https://cli-chat-proxy.grok.com/v1";
|
|
9
9
|
var RESPONSES_URL = "https://cli-chat-proxy.grok.com/v1/responses";
|
|
10
10
|
var MODELS_V2_URL = "https://cli-chat-proxy.grok.com/v1/models-v2";
|
|
11
|
+
var BILLING_CREDITS_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
|
|
11
12
|
var TOKEN_AUTH_HEADER = "X-XAI-Token-Auth";
|
|
12
13
|
var TOKEN_AUTH_VALUE = "xai-grok-cli";
|
|
13
14
|
var CLIENT_IDENTIFIER_HEADER = "x-grok-client-identifier";
|
|
@@ -19,11 +20,16 @@ var SESSION_AUTH_MODES = Object.freeze(["oidc", "external", "web_login", "grok"]
|
|
|
19
20
|
var API_KEY_AUTH_MODES = Object.freeze(["api_key"]);
|
|
20
21
|
var AUTH_FILE_MAX_BYTES = 1048576;
|
|
21
22
|
var CATALOG_TIMEOUT_MS = 15e3;
|
|
23
|
+
var USAGE_TIMEOUT_MS = 8e3;
|
|
24
|
+
var STORE_TOKEN_TIMEOUT_MS = 5e3;
|
|
22
25
|
var STREAM_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
23
26
|
var MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
24
27
|
var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
|
|
25
28
|
var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
|
|
26
29
|
var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
|
|
30
|
+
var IMPORT_TIMEOUT_MS = 2500;
|
|
31
|
+
var CREDENTIALS_IO_TIMEOUT_MS = 1500;
|
|
32
|
+
var RPC_HANDLER_TIMEOUT_MS = 8e3;
|
|
27
33
|
|
|
28
34
|
// src/session.js
|
|
29
35
|
import { existsSync as existsSync2 } from "node:fs";
|
|
@@ -401,273 +407,312 @@ async function loadCatalog(accessToken, options = {}) {
|
|
|
401
407
|
}
|
|
402
408
|
}
|
|
403
409
|
|
|
404
|
-
// src/
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
+
// src/usage.js
|
|
411
|
+
var USAGE_SOURCE = "billing-credits-undocumented";
|
|
412
|
+
var SHANGHAI_TZ = "Asia/Shanghai";
|
|
413
|
+
function unavailable(reason) {
|
|
414
|
+
return Object.freeze({
|
|
415
|
+
status: "unavailable",
|
|
416
|
+
reason: typeof reason === "string" && reason.trim() ? reason.trim() : "Usage unavailable",
|
|
417
|
+
experimental: true,
|
|
418
|
+
source: USAGE_SOURCE
|
|
419
|
+
});
|
|
410
420
|
}
|
|
411
|
-
function
|
|
412
|
-
|
|
413
|
-
if (
|
|
414
|
-
|
|
415
|
-
|
|
421
|
+
function asFiniteNumber(value) {
|
|
422
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
423
|
+
if (typeof value === "string" && value.trim()) {
|
|
424
|
+
const number = Number(value);
|
|
425
|
+
if (Number.isFinite(number)) return number;
|
|
426
|
+
}
|
|
427
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "val" in value) {
|
|
428
|
+
return asFiniteNumber(value.val);
|
|
429
|
+
}
|
|
430
|
+
return void 0;
|
|
416
431
|
}
|
|
417
|
-
function
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
child.on("error", (error) => {
|
|
427
|
-
reject(new Error(`Could not start grok CLI (${bin})`, { cause: error }));
|
|
428
|
-
});
|
|
429
|
-
child.on("exit", (code, signal) => {
|
|
430
|
-
if (code === 0) resolve({ ok: true, code });
|
|
431
|
-
else reject(new Error(signal ? `grok login terminated by ${signal}` : `grok login exited with code ${code ?? "unknown"}`));
|
|
432
|
-
});
|
|
433
|
-
});
|
|
432
|
+
function asFinitePercent(value) {
|
|
433
|
+
return asFiniteNumber(value);
|
|
434
|
+
}
|
|
435
|
+
function asIsoString(value) {
|
|
436
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
437
|
+
const trimmed = value.trim();
|
|
438
|
+
const ms = Date.parse(trimmed);
|
|
439
|
+
if (!Number.isFinite(ms)) return void 0;
|
|
440
|
+
return new Date(ms).toISOString();
|
|
434
441
|
}
|
|
435
|
-
|
|
442
|
+
function formatShanghai(iso) {
|
|
443
|
+
if (!iso) return void 0;
|
|
436
444
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
445
|
+
return new Intl.DateTimeFormat("zh-CN", {
|
|
446
|
+
timeZone: SHANGHAI_TZ,
|
|
447
|
+
year: "numeric",
|
|
448
|
+
month: "2-digit",
|
|
449
|
+
day: "2-digit",
|
|
450
|
+
hour: "2-digit",
|
|
451
|
+
minute: "2-digit",
|
|
452
|
+
second: "2-digit",
|
|
453
|
+
hour12: false
|
|
454
|
+
}).format(new Date(iso));
|
|
439
455
|
} catch {
|
|
456
|
+
return void 0;
|
|
440
457
|
}
|
|
441
|
-
return CREDENTIAL_REF_NAME;
|
|
442
458
|
}
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
let lastPublic = publicSessionView(void 0);
|
|
446
|
-
const notifyCatalogChange = () => {
|
|
447
|
-
try {
|
|
448
|
-
onCatalogChange?.();
|
|
449
|
-
} catch (error) {
|
|
450
|
-
logger?.warn?.(
|
|
451
|
-
"Grok subscription catalog change notify failed: %s",
|
|
452
|
-
error instanceof Error ? error.message : "unknown"
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
const readStoredToken = async () => {
|
|
457
|
-
if (!credentials?.resolve) return void 0;
|
|
458
|
-
const hit = await credentials.resolve(await credentialRefOf());
|
|
459
|
-
const value = hit?.value;
|
|
460
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
461
|
-
};
|
|
462
|
-
const storeToken = async (token) => {
|
|
463
|
-
if (!credentials?.set) throw new Error("DSH credentials service is unavailable");
|
|
464
|
-
await credentials.set(await credentialRefOf(), token);
|
|
465
|
-
};
|
|
466
|
-
const clearToken = async () => {
|
|
467
|
-
if (!credentials?.unset) return;
|
|
468
|
-
await credentials.unset(await credentialRefOf());
|
|
469
|
-
};
|
|
470
|
-
const pull = async () => {
|
|
471
|
-
const parsed = readGrokAuthSession();
|
|
472
|
-
if (!parsed.session) {
|
|
473
|
-
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
474
|
-
lastPublic = publicSessionView(void 0);
|
|
475
|
-
await clearToken();
|
|
476
|
-
const message = parsed.reason === "api-key-only" ? "Found an API-key entry only. Sign in with SuperGrok / X Premium via grok login." : "No Grok Build subscription session in auth.json. Run grok login first.";
|
|
477
|
-
notifyCatalogChange();
|
|
478
|
-
return { ok: false, error: message, account: lastPublic, catalog };
|
|
479
|
-
}
|
|
480
|
-
await storeToken(parsed.session.accessToken);
|
|
481
|
-
lastPublic = publicSessionView(parsed.session);
|
|
482
|
-
catalog = await loadCatalog(parsed.session.accessToken);
|
|
483
|
-
notifyCatalogChange();
|
|
484
|
-
return { ok: true, account: lastPublic, catalog };
|
|
485
|
-
};
|
|
486
|
-
const status = async () => {
|
|
487
|
-
let token;
|
|
488
|
-
try {
|
|
489
|
-
token = await readStoredToken();
|
|
490
|
-
} catch (error) {
|
|
491
|
-
logger?.warn?.("could not resolve Grok subscription credential: %s", error instanceof Error ? error.message : "unknown");
|
|
492
|
-
token = void 0;
|
|
493
|
-
}
|
|
494
|
-
if (!token) {
|
|
495
|
-
lastPublic = publicSessionView(void 0);
|
|
496
|
-
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
497
|
-
return { account: lastPublic, catalog, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
498
|
-
}
|
|
499
|
-
if (lastPublic.signedIn !== true) {
|
|
500
|
-
try {
|
|
501
|
-
const parsed = readGrokAuthSession();
|
|
502
|
-
if (parsed.session) lastPublic = publicSessionView(parsed.session);
|
|
503
|
-
else lastPublic = Object.freeze({ signedIn: true, maskedAccount: "\u2022\u2022\u2022\u2022", authMode: "oidc", source: "dsh-credentials" });
|
|
504
|
-
} catch {
|
|
505
|
-
lastPublic = Object.freeze({ signedIn: true, maskedAccount: "\u2022\u2022\u2022\u2022", authMode: "oidc", source: "dsh-credentials" });
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return { account: lastPublic, catalog, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
509
|
-
};
|
|
510
|
-
const refreshCatalog = async () => {
|
|
511
|
-
const token = await readStoredToken();
|
|
512
|
-
if (!token) {
|
|
513
|
-
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
514
|
-
notifyCatalogChange();
|
|
515
|
-
return catalog;
|
|
516
|
-
}
|
|
517
|
-
catalog = await loadCatalog(token);
|
|
518
|
-
notifyCatalogChange();
|
|
519
|
-
return catalog;
|
|
520
|
-
};
|
|
521
|
-
const login = async (options) => {
|
|
522
|
-
await spawnGrokLogin(options);
|
|
523
|
-
return pull();
|
|
524
|
-
};
|
|
525
|
-
const logout = async () => {
|
|
526
|
-
await clearToken();
|
|
527
|
-
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
528
|
-
lastPublic = publicSessionView(void 0);
|
|
529
|
-
notifyCatalogChange();
|
|
530
|
-
return { ok: true, account: lastPublic, catalog };
|
|
531
|
-
};
|
|
532
|
-
return {
|
|
533
|
-
pull,
|
|
534
|
-
status,
|
|
535
|
-
refreshCatalog,
|
|
536
|
-
login,
|
|
537
|
-
logout,
|
|
538
|
-
currentToken: readStoredToken,
|
|
539
|
-
models: () => catalog.models,
|
|
540
|
-
catalog: () => catalog,
|
|
541
|
-
publicAccount: () => lastPublic
|
|
542
|
-
};
|
|
459
|
+
function isPlainObject(value) {
|
|
460
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
543
461
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
var RPC_ENDPOINTS = Object.freeze([
|
|
547
|
-
"status",
|
|
548
|
-
"login/cli",
|
|
549
|
-
"login/device",
|
|
550
|
-
"pull",
|
|
551
|
-
"logout",
|
|
552
|
-
"catalog/refresh"
|
|
553
|
-
]);
|
|
554
|
-
function publicResult(value) {
|
|
555
|
-
return { ok: true, value };
|
|
462
|
+
function topLevelKeys(body) {
|
|
463
|
+
return Object.keys(body).filter((key) => typeof key === "string").slice(0, 24);
|
|
556
464
|
}
|
|
557
|
-
function
|
|
558
|
-
const
|
|
559
|
-
|
|
465
|
+
function missingPercentReason(body) {
|
|
466
|
+
const keys = topLevelKeys(body);
|
|
467
|
+
if (keys.length === 0) {
|
|
468
|
+
return "Missing creditUsagePercent (empty object)";
|
|
469
|
+
}
|
|
470
|
+
return `Missing creditUsagePercent (keys: ${keys.join(",")})`;
|
|
560
471
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (!value || typeof value !== "object") return value;
|
|
565
|
-
const next = { ...value };
|
|
566
|
-
delete next.accessToken;
|
|
567
|
-
delete next.token;
|
|
568
|
-
delete next.refresh;
|
|
569
|
-
delete next.refresh_token;
|
|
570
|
-
delete next.key;
|
|
571
|
-
if (next.catalog) {
|
|
572
|
-
next.catalog = {
|
|
573
|
-
source: next.catalog.source,
|
|
574
|
-
error: next.catalog.error,
|
|
575
|
-
models: Array.isArray(next.catalog.models) ? next.catalog.models.map((model) => ({
|
|
576
|
-
id: model.id,
|
|
577
|
-
name: model.name,
|
|
578
|
-
contextWindow: model.contextWindow,
|
|
579
|
-
reasoning: model.reasoning,
|
|
580
|
-
source: model.source
|
|
581
|
-
})) : []
|
|
582
|
-
};
|
|
472
|
+
function firstDefined(...values) {
|
|
473
|
+
for (const value of values) {
|
|
474
|
+
if (value !== void 0 && value !== null) return value;
|
|
583
475
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
476
|
+
return void 0;
|
|
477
|
+
}
|
|
478
|
+
function readPercentCandidate(node) {
|
|
479
|
+
if (!isPlainObject(node)) return void 0;
|
|
480
|
+
return asFinitePercent(firstDefined(
|
|
481
|
+
node.creditUsagePercent,
|
|
482
|
+
node.usedPercent,
|
|
483
|
+
node.usagePercent,
|
|
484
|
+
node.percentUsed,
|
|
485
|
+
node.used_percent,
|
|
486
|
+
node.usage_percent
|
|
487
|
+
));
|
|
488
|
+
}
|
|
489
|
+
function resolveUsedPercent(body) {
|
|
490
|
+
const config = isPlainObject(body.config) ? body.config : void 0;
|
|
491
|
+
const usage = isPlainObject(body.usage) ? body.usage : void 0;
|
|
492
|
+
const credits = isPlainObject(body.credits) ? body.credits : void 0;
|
|
493
|
+
const direct = firstDefined(
|
|
494
|
+
readPercentCandidate(body),
|
|
495
|
+
readPercentCandidate(config),
|
|
496
|
+
readPercentCandidate(usage),
|
|
497
|
+
readPercentCandidate(credits),
|
|
498
|
+
asFinitePercent(credits?.usedPercent)
|
|
499
|
+
);
|
|
500
|
+
if (direct !== void 0) return direct;
|
|
501
|
+
const used = firstDefined(
|
|
502
|
+
asFiniteNumber(body.used),
|
|
503
|
+
asFiniteNumber(body.includedUsed),
|
|
504
|
+
asFiniteNumber(body.totalUsed),
|
|
505
|
+
asFiniteNumber(config?.includedUsed),
|
|
506
|
+
asFiniteNumber(config?.totalUsed),
|
|
507
|
+
asFiniteNumber(usage?.used),
|
|
508
|
+
asFiniteNumber(credits?.used)
|
|
509
|
+
);
|
|
510
|
+
const total = firstDefined(
|
|
511
|
+
asFiniteNumber(body.total),
|
|
512
|
+
asFiniteNumber(body.monthlyLimit),
|
|
513
|
+
asFiniteNumber(config?.monthlyLimit),
|
|
514
|
+
asFiniteNumber(usage?.total),
|
|
515
|
+
asFiniteNumber(credits?.total),
|
|
516
|
+
asFiniteNumber(credits?.limit)
|
|
517
|
+
);
|
|
518
|
+
if (used !== void 0 && total !== void 0 && total > 0) {
|
|
519
|
+
return used / total * 100;
|
|
592
520
|
}
|
|
593
|
-
return
|
|
521
|
+
return void 0;
|
|
594
522
|
}
|
|
595
|
-
function
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
523
|
+
function resolvePeriodBounds(body) {
|
|
524
|
+
const config = isPlainObject(body.config) ? body.config : void 0;
|
|
525
|
+
const period = firstDefined(
|
|
526
|
+
isPlainObject(config?.currentPeriod) ? config.currentPeriod : void 0,
|
|
527
|
+
isPlainObject(body.currentPeriod) ? body.currentPeriod : void 0,
|
|
528
|
+
isPlainObject(body.period) ? body.period : void 0
|
|
529
|
+
);
|
|
530
|
+
const start = firstDefined(
|
|
531
|
+
period ? asIsoString(period.start) : void 0,
|
|
532
|
+
asIsoString(config?.billingPeriodStart),
|
|
533
|
+
asIsoString(body.billingPeriodStart),
|
|
534
|
+
asIsoString(body.periodStart),
|
|
535
|
+
asIsoString(body.resetStart)
|
|
536
|
+
);
|
|
537
|
+
const end = firstDefined(
|
|
538
|
+
period ? asIsoString(period.end) : void 0,
|
|
539
|
+
asIsoString(config?.billingPeriodEnd),
|
|
540
|
+
asIsoString(body.billingPeriodEnd),
|
|
541
|
+
asIsoString(body.periodEnd),
|
|
542
|
+
asIsoString(body.resetAt),
|
|
543
|
+
asIsoString(body.reset_at)
|
|
544
|
+
);
|
|
545
|
+
return { start, end };
|
|
612
546
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
payload: body.payload
|
|
621
|
-
};
|
|
547
|
+
function resolveProductUsage(body) {
|
|
548
|
+
const config = isPlainObject(body.config) ? body.config : void 0;
|
|
549
|
+
return firstDefined(
|
|
550
|
+
Array.isArray(body.productUsage) ? body.productUsage : void 0,
|
|
551
|
+
Array.isArray(config?.productUsage) ? config.productUsage : void 0,
|
|
552
|
+
Array.isArray(body.products) ? body.products : void 0
|
|
553
|
+
);
|
|
622
554
|
}
|
|
623
|
-
function
|
|
624
|
-
|
|
555
|
+
function sanitizeProductUsage(raw) {
|
|
556
|
+
if (!Array.isArray(raw)) return void 0;
|
|
557
|
+
const rows = [];
|
|
558
|
+
for (const item of raw) {
|
|
559
|
+
if (!item || typeof item !== "object") continue;
|
|
560
|
+
const name2 = typeof item.name === "string" && item.name.trim() ? item.name.trim() : typeof item.product === "string" && item.product.trim() ? item.product.trim() : typeof item.id === "string" && item.id.trim() ? item.id.trim() : void 0;
|
|
561
|
+
const usedPercent = asFinitePercent(firstDefined(
|
|
562
|
+
item.creditUsagePercent,
|
|
563
|
+
item.usedPercent,
|
|
564
|
+
item.usagePercent,
|
|
565
|
+
item.percentUsed,
|
|
566
|
+
item.used_percent
|
|
567
|
+
));
|
|
568
|
+
if (!name2 && usedPercent === void 0) continue;
|
|
569
|
+
rows.push(Object.freeze({
|
|
570
|
+
...name2 ? { name: name2 } : {},
|
|
571
|
+
...usedPercent !== void 0 ? { usedPercent } : {}
|
|
572
|
+
}));
|
|
573
|
+
}
|
|
574
|
+
return rows.length ? Object.freeze(rows) : void 0;
|
|
575
|
+
}
|
|
576
|
+
function parseBillingCredits(body) {
|
|
577
|
+
if (body === null || body === void 0) {
|
|
578
|
+
return unavailable("Empty billing response");
|
|
579
|
+
}
|
|
580
|
+
if (typeof body !== "object" || Array.isArray(body)) {
|
|
581
|
+
return unavailable("Unexpected billing response shape");
|
|
582
|
+
}
|
|
583
|
+
const usedPercent = resolveUsedPercent(body);
|
|
584
|
+
if (usedPercent === void 0) {
|
|
585
|
+
return unavailable(missingPercentReason(body));
|
|
586
|
+
}
|
|
587
|
+
const { start: periodStart, end: periodEnd } = resolvePeriodBounds(body);
|
|
588
|
+
const productUsage = sanitizeProductUsage(resolveProductUsage(body));
|
|
589
|
+
const remainingPercent = Math.max(0, 100 - usedPercent);
|
|
590
|
+
return Object.freeze({
|
|
591
|
+
status: "ok",
|
|
592
|
+
experimental: true,
|
|
593
|
+
source: USAGE_SOURCE,
|
|
594
|
+
usedPercent,
|
|
595
|
+
remainingPercent,
|
|
596
|
+
...periodStart ? { periodStart, periodStartLocal: formatShanghai(periodStart) } : {},
|
|
597
|
+
...periodEnd ? { periodEnd, periodEndLocal: formatShanghai(periodEnd) } : {},
|
|
598
|
+
...productUsage ? { productUsage } : {}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function parseBillingCreditsJson(text) {
|
|
602
|
+
if (typeof text !== "string") return unavailable("Non-text billing response");
|
|
603
|
+
if (!text.trim()) return unavailable("Empty billing response");
|
|
604
|
+
let body;
|
|
625
605
|
try {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
606
|
+
body = JSON.parse(text);
|
|
607
|
+
} catch {
|
|
608
|
+
return unavailable("Invalid JSON from billing API");
|
|
609
|
+
}
|
|
610
|
+
return parseBillingCredits(body);
|
|
611
|
+
}
|
|
612
|
+
async function fetchBillingUsageOnce(accessToken, options = {}) {
|
|
613
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
614
|
+
if (typeof fetchImpl !== "function") {
|
|
615
|
+
return unavailable("Fetch unavailable");
|
|
616
|
+
}
|
|
617
|
+
let response;
|
|
618
|
+
try {
|
|
619
|
+
const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? USAGE_TIMEOUT_MS);
|
|
620
|
+
response = await fetchImpl(BILLING_CREDITS_URL, {
|
|
621
|
+
method: "GET",
|
|
622
|
+
headers: {
|
|
623
|
+
Accept: "application/json",
|
|
624
|
+
...buildProxyHeaders(accessToken, options)
|
|
625
|
+
},
|
|
626
|
+
redirect: "error",
|
|
627
|
+
signal
|
|
628
|
+
});
|
|
629
|
+
} catch (error) {
|
|
630
|
+
const message = error instanceof Error ? error.message : "network error";
|
|
631
|
+
if (/abort|timeout/i.test(message)) return unavailable("Billing request timed out");
|
|
632
|
+
return unavailable("Billing network error");
|
|
633
|
+
}
|
|
634
|
+
if (response.status === 401 || response.status === 403) {
|
|
635
|
+
return unavailable(`Billing unauthorized (HTTP ${response.status})`);
|
|
636
|
+
}
|
|
637
|
+
if (!response.ok) {
|
|
638
|
+
return unavailable(`Billing HTTP ${response.status}`);
|
|
639
|
+
}
|
|
640
|
+
const rawContentType = typeof response.headers?.get === "function" ? response.headers.get("content-type") ?? "" : "";
|
|
641
|
+
const contentType = String(rawContentType).split(";")[0].trim();
|
|
642
|
+
const contentTypeLower = contentType.toLowerCase();
|
|
643
|
+
const looksJson = !contentTypeLower || contentTypeLower.includes("json") || contentTypeLower === "text/plain";
|
|
644
|
+
let text;
|
|
645
|
+
try {
|
|
646
|
+
text = await response.text();
|
|
647
|
+
} catch {
|
|
648
|
+
return unavailable("Could not read billing body");
|
|
649
|
+
}
|
|
650
|
+
if (!looksJson) {
|
|
651
|
+
const parsedAnyway = parseBillingCreditsJson(text);
|
|
652
|
+
if (parsedAnyway.status === "ok") {
|
|
653
|
+
return Object.freeze({
|
|
654
|
+
...parsedAnyway,
|
|
655
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
656
|
+
});
|
|
657
657
|
}
|
|
658
|
+
return unavailable(`Non-JSON billing Content-Type: ${contentType || "unknown"}`);
|
|
659
|
+
}
|
|
660
|
+
const parsed = parseBillingCreditsJson(text);
|
|
661
|
+
if (parsed.status === "ok") {
|
|
662
|
+
return Object.freeze({
|
|
663
|
+
...parsed,
|
|
664
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
return parsed;
|
|
668
|
+
}
|
|
669
|
+
async function fetchBillingUsage(accessToken, options = {}) {
|
|
670
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
671
|
+
return unavailable("Not signed in");
|
|
672
|
+
}
|
|
673
|
+
const timeoutMs = options.timeoutMs ?? USAGE_TIMEOUT_MS;
|
|
674
|
+
let timer;
|
|
675
|
+
try {
|
|
676
|
+
const result = await Promise.race([
|
|
677
|
+
fetchBillingUsageOnce(accessToken, options),
|
|
678
|
+
new Promise((resolve) => {
|
|
679
|
+
timer = setTimeout(
|
|
680
|
+
() => resolve(unavailable("Billing request timed out")),
|
|
681
|
+
timeoutMs
|
|
682
|
+
);
|
|
683
|
+
})
|
|
684
|
+
]);
|
|
685
|
+
return result;
|
|
658
686
|
} catch (error) {
|
|
659
|
-
|
|
660
|
-
|
|
687
|
+
const message = error instanceof Error ? error.message : "network error";
|
|
688
|
+
if (/abort|timeout/i.test(message)) return unavailable("Billing request timed out");
|
|
689
|
+
return unavailable("Billing network error");
|
|
690
|
+
} finally {
|
|
691
|
+
if (timer) clearTimeout(timer);
|
|
661
692
|
}
|
|
662
|
-
return () => {
|
|
663
|
-
for (const dispose of disposers.reverse()) dispose?.();
|
|
664
|
-
};
|
|
665
693
|
}
|
|
666
694
|
|
|
667
695
|
// src/adapter.js
|
|
668
|
-
|
|
696
|
+
function withImportTimeout(promise, ms, message) {
|
|
697
|
+
let timer;
|
|
698
|
+
return Promise.race([
|
|
699
|
+
promise,
|
|
700
|
+
new Promise((_, reject) => {
|
|
701
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
702
|
+
})
|
|
703
|
+
]).finally(() => {
|
|
704
|
+
if (timer) clearTimeout(timer);
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async function optionalImport(specifier, options = {}) {
|
|
708
|
+
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : IMPORT_TIMEOUT_MS;
|
|
709
|
+
const importFn = typeof options.importFn === "function" ? options.importFn : (id) => import(id);
|
|
669
710
|
try {
|
|
670
|
-
return await
|
|
711
|
+
return await withImportTimeout(
|
|
712
|
+
Promise.resolve(importFn(specifier)),
|
|
713
|
+
timeoutMs,
|
|
714
|
+
`Import timed out after ${timeoutMs}ms: ${specifier}`
|
|
715
|
+
);
|
|
671
716
|
} catch {
|
|
672
717
|
return void 0;
|
|
673
718
|
}
|
|
@@ -987,37 +1032,48 @@ function withEncryptedReasoningInclude(api) {
|
|
|
987
1032
|
streamSimple: (model, context, options) => api.streamSimple(model, context, inject2(options))
|
|
988
1033
|
};
|
|
989
1034
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
return candidate.listModels(provider);
|
|
1008
|
-
}
|
|
1009
|
-
resolveModel(provider, model, signal) {
|
|
1010
|
-
return candidate.resolveModel(provider, model, signal);
|
|
1011
|
-
}
|
|
1012
|
-
prepareCall(provider, model, signal) {
|
|
1013
|
-
return candidate.prepareCall(provider, model, signal);
|
|
1014
|
-
}
|
|
1015
|
-
stream(options) {
|
|
1016
|
-
return candidate.stream(options);
|
|
1017
|
-
}
|
|
1035
|
+
function wrapAsHostAdapter(candidate, dshLlm) {
|
|
1036
|
+
if (!dshLlm?.LlmAdapter || candidate instanceof dshLlm.LlmAdapter) return candidate;
|
|
1037
|
+
class GrokBuildAdapter extends dshLlm.LlmAdapter {
|
|
1038
|
+
providerInfo(provider) {
|
|
1039
|
+
return candidate.providerInfo(provider);
|
|
1040
|
+
}
|
|
1041
|
+
providerRetryPolicy(provider) {
|
|
1042
|
+
return candidate.providerRetryPolicy(provider);
|
|
1043
|
+
}
|
|
1044
|
+
listModels(provider) {
|
|
1045
|
+
return candidate.listModels(provider);
|
|
1046
|
+
}
|
|
1047
|
+
resolveModel(provider, model, signal) {
|
|
1048
|
+
return candidate.resolveModel(provider, model, signal);
|
|
1049
|
+
}
|
|
1050
|
+
prepareCall(provider, model, signal) {
|
|
1051
|
+
return candidate.prepareCall(provider, model, signal);
|
|
1018
1052
|
}
|
|
1019
|
-
|
|
1053
|
+
stream(options) {
|
|
1054
|
+
return candidate.stream(options);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return new GrokBuildAdapter();
|
|
1058
|
+
}
|
|
1059
|
+
function createGrokBuildAdapterSync(session, options = {}) {
|
|
1060
|
+
const duck = createDuckAdapter(session);
|
|
1061
|
+
const dshLlm = options.dshLlm;
|
|
1062
|
+
return {
|
|
1063
|
+
adapter: wrapAsHostAdapter(duck, dshLlm),
|
|
1064
|
+
kind: "custom-mvp",
|
|
1065
|
+
note: "Registered a sync duck Responses adapter; pi-ai upgrade may follow in the background."
|
|
1020
1066
|
};
|
|
1067
|
+
}
|
|
1068
|
+
async function createGrokBuildAdapter(session, options = {}) {
|
|
1069
|
+
const importOpts = options.importOptions ?? {};
|
|
1070
|
+
const [piAi, dshPi, dshLlm] = await Promise.all([
|
|
1071
|
+
optionalImport("@earendil-works/pi-ai", importOpts),
|
|
1072
|
+
optionalImport("@deepseek-ai/dsh-llm-pi-ai", importOpts),
|
|
1073
|
+
optionalImport("@deepseek-ai/dsh-llm", importOpts)
|
|
1074
|
+
]);
|
|
1075
|
+
const duck = createDuckAdapter(session);
|
|
1076
|
+
const asHostAdapter = (candidate) => wrapAsHostAdapter(candidate, dshLlm);
|
|
1021
1077
|
if (!piAi?.createProvider || !dshPi?.PiAiAdapter) {
|
|
1022
1078
|
return {
|
|
1023
1079
|
adapter: asHostAdapter(duck),
|
|
@@ -1027,8 +1083,8 @@ async function createGrokBuildAdapter(session) {
|
|
|
1027
1083
|
}
|
|
1028
1084
|
let responsesApi;
|
|
1029
1085
|
try {
|
|
1030
|
-
const lazy = await
|
|
1031
|
-
responsesApi = typeof lazy
|
|
1086
|
+
const lazy = await optionalImport("@earendil-works/pi-ai/api/openai-responses.lazy", importOpts);
|
|
1087
|
+
responsesApi = typeof lazy?.openAIResponsesApi === "function" ? lazy.openAIResponsesApi() : void 0;
|
|
1032
1088
|
} catch {
|
|
1033
1089
|
responsesApi = void 0;
|
|
1034
1090
|
}
|
|
@@ -1120,10 +1176,513 @@ async function createGrokBuildAdapter(session) {
|
|
|
1120
1176
|
};
|
|
1121
1177
|
}
|
|
1122
1178
|
|
|
1179
|
+
// src/session.js
|
|
1180
|
+
function resolveGrokBin(env = process.env, exists = existsSync2) {
|
|
1181
|
+
if (typeof env.DSH_GROK_BIN === "string" && env.DSH_GROK_BIN.trim()) return env.DSH_GROK_BIN.trim();
|
|
1182
|
+
const local = join2(grokHome(env), "bin", "grok");
|
|
1183
|
+
if (exists(local)) return local;
|
|
1184
|
+
return "grok";
|
|
1185
|
+
}
|
|
1186
|
+
function grokCliAvailable(env = process.env, exists = existsSync2) {
|
|
1187
|
+
const bin = resolveGrokBin(env, exists);
|
|
1188
|
+
if (bin.includes("/") || bin.includes("\\")) return exists(bin);
|
|
1189
|
+
const delimiter = process.platform === "win32" ? ";" : ":";
|
|
1190
|
+
return String(env.PATH ?? "").split(delimiter).some((dir) => dir && exists(join2(dir, bin)));
|
|
1191
|
+
}
|
|
1192
|
+
function spawnGrokLogin(options = {}) {
|
|
1193
|
+
const spawnFn = options.spawn ?? spawn;
|
|
1194
|
+
const bin = resolveGrokBin(options.env, options.exists);
|
|
1195
|
+
const args = options.device ? ["login", "--device-auth"] : ["login"];
|
|
1196
|
+
return new Promise((resolve, reject) => {
|
|
1197
|
+
const child = spawnFn(bin, args, {
|
|
1198
|
+
stdio: options.stdio ?? "inherit",
|
|
1199
|
+
env: options.env ?? process.env
|
|
1200
|
+
});
|
|
1201
|
+
child.on("error", (error) => {
|
|
1202
|
+
reject(new Error(`Could not start grok CLI (${bin})`, { cause: error }));
|
|
1203
|
+
});
|
|
1204
|
+
child.on("exit", (code, signal) => {
|
|
1205
|
+
if (code === 0) resolve({ ok: true, code });
|
|
1206
|
+
else reject(new Error(signal ? `grok login terminated by ${signal}` : `grok login exited with code ${code ?? "unknown"}`));
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
async function defaultCredentialRefOf() {
|
|
1211
|
+
try {
|
|
1212
|
+
const mod = await optionalImport("@deepseek-ai/dsh-credentials");
|
|
1213
|
+
if (typeof mod?.credentialRef === "function") return mod.credentialRef(CREDENTIAL_REF_NAME);
|
|
1214
|
+
} catch {
|
|
1215
|
+
}
|
|
1216
|
+
return CREDENTIAL_REF_NAME;
|
|
1217
|
+
}
|
|
1218
|
+
function scheduleDeferred(run) {
|
|
1219
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
1220
|
+
else queueMicrotask(run);
|
|
1221
|
+
}
|
|
1222
|
+
function withTimeout(promise, ms, message, options = {}) {
|
|
1223
|
+
let timer;
|
|
1224
|
+
const shouldUnref = options.unref !== false;
|
|
1225
|
+
return Promise.race([
|
|
1226
|
+
promise,
|
|
1227
|
+
new Promise((_, reject) => {
|
|
1228
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
1229
|
+
if (shouldUnref && typeof timer?.unref === "function") timer.unref();
|
|
1230
|
+
})
|
|
1231
|
+
]).finally(() => {
|
|
1232
|
+
if (timer) clearTimeout(timer);
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
function createSessionService({
|
|
1236
|
+
credentials,
|
|
1237
|
+
logger,
|
|
1238
|
+
onCatalogChange,
|
|
1239
|
+
fetchBillingUsage: fetchBilling = fetchBillingUsage,
|
|
1240
|
+
loadCatalog: loadCatalogFn = loadCatalog,
|
|
1241
|
+
readAuth = readGrokAuthSession,
|
|
1242
|
+
storeTokenTimeoutMs = STORE_TOKEN_TIMEOUT_MS,
|
|
1243
|
+
credentialsIoTimeoutMs = CREDENTIALS_IO_TIMEOUT_MS,
|
|
1244
|
+
credentialRefOf = defaultCredentialRefOf
|
|
1245
|
+
} = {}) {
|
|
1246
|
+
let catalog = { models: [], source: "signed-out", error: void 0 };
|
|
1247
|
+
let lastPublic = publicSessionView(void 0);
|
|
1248
|
+
let lastUsage = unavailable("Not fetched yet");
|
|
1249
|
+
let memoryAccessToken;
|
|
1250
|
+
let memorySessionTouched = false;
|
|
1251
|
+
const resolveTimeoutMs = () => {
|
|
1252
|
+
return typeof storeTokenTimeoutMs === "number" && storeTokenTimeoutMs > 0 ? storeTokenTimeoutMs : STORE_TOKEN_TIMEOUT_MS;
|
|
1253
|
+
};
|
|
1254
|
+
const resolveIoTimeoutMs = () => {
|
|
1255
|
+
return typeof credentialsIoTimeoutMs === "number" && credentialsIoTimeoutMs > 0 ? credentialsIoTimeoutMs : CREDENTIALS_IO_TIMEOUT_MS;
|
|
1256
|
+
};
|
|
1257
|
+
const notifyCatalogChange = () => {
|
|
1258
|
+
scheduleDeferred(() => {
|
|
1259
|
+
try {
|
|
1260
|
+
onCatalogChange?.();
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
logger?.warn?.(
|
|
1263
|
+
"Grok subscription catalog change notify failed: %s",
|
|
1264
|
+
error instanceof Error ? error.message : "unknown"
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
};
|
|
1269
|
+
const readStoredToken = async () => {
|
|
1270
|
+
if (typeof memoryAccessToken === "string" && memoryAccessToken.length > 0) {
|
|
1271
|
+
return memoryAccessToken;
|
|
1272
|
+
}
|
|
1273
|
+
if (memorySessionTouched) return void 0;
|
|
1274
|
+
if (!credentials?.resolve) return void 0;
|
|
1275
|
+
const timeoutMs = resolveIoTimeoutMs();
|
|
1276
|
+
try {
|
|
1277
|
+
const hit = await withTimeout(
|
|
1278
|
+
(async () => {
|
|
1279
|
+
const ref = await credentialRefOf();
|
|
1280
|
+
return credentials.resolve(ref);
|
|
1281
|
+
})(),
|
|
1282
|
+
timeoutMs,
|
|
1283
|
+
`Resolving Grok credential timed out after ${timeoutMs}ms`
|
|
1284
|
+
);
|
|
1285
|
+
const value = hit?.value;
|
|
1286
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
logger?.warn?.(
|
|
1289
|
+
"Grok subscription credential resolve failed: %s",
|
|
1290
|
+
error instanceof Error ? error.message : "unknown"
|
|
1291
|
+
);
|
|
1292
|
+
return void 0;
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
const persistToken = async (token) => {
|
|
1296
|
+
if (!credentials?.set) throw new Error("DSH credentials service is unavailable");
|
|
1297
|
+
const timeoutMs = resolveTimeoutMs();
|
|
1298
|
+
await withTimeout(
|
|
1299
|
+
(async () => {
|
|
1300
|
+
const ref = await credentialRefOf();
|
|
1301
|
+
await credentials.set(ref, token);
|
|
1302
|
+
})(),
|
|
1303
|
+
timeoutMs,
|
|
1304
|
+
`Storing Grok credential timed out after ${timeoutMs}ms`
|
|
1305
|
+
);
|
|
1306
|
+
};
|
|
1307
|
+
const clearToken = async () => {
|
|
1308
|
+
if (!credentials?.unset) return;
|
|
1309
|
+
const timeoutMs = resolveTimeoutMs();
|
|
1310
|
+
await withTimeout(
|
|
1311
|
+
(async () => {
|
|
1312
|
+
const ref = await credentialRefOf();
|
|
1313
|
+
await credentials.unset(ref);
|
|
1314
|
+
})(),
|
|
1315
|
+
timeoutMs,
|
|
1316
|
+
`Clearing Grok credential timed out after ${timeoutMs}ms`
|
|
1317
|
+
);
|
|
1318
|
+
};
|
|
1319
|
+
const refreshUsage = async () => {
|
|
1320
|
+
const token = await readStoredToken();
|
|
1321
|
+
if (!token) {
|
|
1322
|
+
lastUsage = unavailable("Not signed in");
|
|
1323
|
+
return lastUsage;
|
|
1324
|
+
}
|
|
1325
|
+
try {
|
|
1326
|
+
lastUsage = await fetchBilling(token);
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
logger?.warn?.(
|
|
1329
|
+
"Grok subscription usage fetch failed: %s",
|
|
1330
|
+
error instanceof Error ? error.message : "unknown"
|
|
1331
|
+
);
|
|
1332
|
+
lastUsage = unavailable("Usage fetch failed");
|
|
1333
|
+
}
|
|
1334
|
+
return lastUsage;
|
|
1335
|
+
};
|
|
1336
|
+
const refreshCatalog = async () => {
|
|
1337
|
+
const token = await readStoredToken();
|
|
1338
|
+
if (!token) {
|
|
1339
|
+
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
1340
|
+
notifyCatalogChange();
|
|
1341
|
+
return catalog;
|
|
1342
|
+
}
|
|
1343
|
+
catalog = await loadCatalogFn(token);
|
|
1344
|
+
notifyCatalogChange();
|
|
1345
|
+
return catalog;
|
|
1346
|
+
};
|
|
1347
|
+
const kickBackgroundUsageRefresh = () => {
|
|
1348
|
+
void refreshUsage().catch((error) => {
|
|
1349
|
+
logger?.warn?.(
|
|
1350
|
+
"Grok subscription background usage refresh failed: %s",
|
|
1351
|
+
error instanceof Error ? error.message : "unknown"
|
|
1352
|
+
);
|
|
1353
|
+
});
|
|
1354
|
+
};
|
|
1355
|
+
const kickBackgroundCatalogRefresh = () => {
|
|
1356
|
+
void refreshCatalog().catch((error) => {
|
|
1357
|
+
logger?.warn?.(
|
|
1358
|
+
"Grok subscription background catalog refresh failed: %s",
|
|
1359
|
+
error instanceof Error ? error.message : "unknown"
|
|
1360
|
+
);
|
|
1361
|
+
});
|
|
1362
|
+
};
|
|
1363
|
+
const pull = async () => {
|
|
1364
|
+
const parsed = readAuth();
|
|
1365
|
+
if (!parsed.session) {
|
|
1366
|
+
memoryAccessToken = void 0;
|
|
1367
|
+
memorySessionTouched = true;
|
|
1368
|
+
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
1369
|
+
lastPublic = publicSessionView(void 0);
|
|
1370
|
+
lastUsage = unavailable("Not signed in");
|
|
1371
|
+
scheduleDeferred(() => {
|
|
1372
|
+
void clearToken().catch((error) => {
|
|
1373
|
+
logger?.warn?.(
|
|
1374
|
+
"Grok subscription deferred credential clear failed: %s",
|
|
1375
|
+
error instanceof Error ? error.message : "unknown"
|
|
1376
|
+
);
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
const message = parsed.reason === "api-key-only" ? "Found an API-key entry only. Sign in with SuperGrok / X Premium via grok login." : "No Grok Build subscription session in auth.json. Run grok login first.";
|
|
1380
|
+
notifyCatalogChange();
|
|
1381
|
+
return { ok: false, error: message, account: lastPublic, catalog, usage: lastUsage };
|
|
1382
|
+
}
|
|
1383
|
+
const accessToken = parsed.session.accessToken;
|
|
1384
|
+
memoryAccessToken = accessToken;
|
|
1385
|
+
memorySessionTouched = true;
|
|
1386
|
+
lastPublic = publicSessionView(parsed.session);
|
|
1387
|
+
scheduleDeferred(() => {
|
|
1388
|
+
void persistToken(accessToken).catch((error) => {
|
|
1389
|
+
logger?.warn?.(
|
|
1390
|
+
"Grok subscription deferred credential persist failed: %s",
|
|
1391
|
+
error instanceof Error ? error.message : "unknown"
|
|
1392
|
+
);
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
notifyCatalogChange();
|
|
1396
|
+
kickBackgroundCatalogRefresh();
|
|
1397
|
+
kickBackgroundUsageRefresh();
|
|
1398
|
+
return { ok: true, account: lastPublic, catalog, usage: lastUsage };
|
|
1399
|
+
};
|
|
1400
|
+
const status = async () => {
|
|
1401
|
+
if (typeof memoryAccessToken === "string" && memoryAccessToken.length > 0 || lastPublic.signedIn === true) {
|
|
1402
|
+
return { account: lastPublic, catalog, usage: lastUsage, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
1403
|
+
}
|
|
1404
|
+
try {
|
|
1405
|
+
const parsed = readAuth();
|
|
1406
|
+
if (parsed.session) {
|
|
1407
|
+
memoryAccessToken = parsed.session.accessToken;
|
|
1408
|
+
memorySessionTouched = true;
|
|
1409
|
+
lastPublic = publicSessionView(parsed.session);
|
|
1410
|
+
return { account: lastPublic, catalog, usage: lastUsage, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
1411
|
+
}
|
|
1412
|
+
} catch (error) {
|
|
1413
|
+
logger?.warn?.(
|
|
1414
|
+
"Grok subscription auth.json read failed during status: %s",
|
|
1415
|
+
error instanceof Error ? error.message : "unknown"
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
let token;
|
|
1419
|
+
try {
|
|
1420
|
+
token = await readStoredToken();
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
logger?.warn?.("could not resolve Grok subscription credential: %s", error instanceof Error ? error.message : "unknown");
|
|
1423
|
+
token = void 0;
|
|
1424
|
+
}
|
|
1425
|
+
if (!token) {
|
|
1426
|
+
lastPublic = publicSessionView(void 0);
|
|
1427
|
+
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
1428
|
+
lastUsage = unavailable("Not signed in");
|
|
1429
|
+
return { account: lastPublic, catalog, usage: lastUsage, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
1430
|
+
}
|
|
1431
|
+
memoryAccessToken = token;
|
|
1432
|
+
lastPublic = Object.freeze({ signedIn: true, maskedAccount: "\u2022\u2022\u2022\u2022", authMode: "oidc", source: "dsh-credentials" });
|
|
1433
|
+
return { account: lastPublic, catalog, usage: lastUsage, cliAvailable: grokCliAvailable(), authPath: authJsonPath() };
|
|
1434
|
+
};
|
|
1435
|
+
const login = async (options) => {
|
|
1436
|
+
await spawnGrokLogin(options);
|
|
1437
|
+
return pull();
|
|
1438
|
+
};
|
|
1439
|
+
const logout = async () => {
|
|
1440
|
+
memoryAccessToken = void 0;
|
|
1441
|
+
memorySessionTouched = true;
|
|
1442
|
+
await clearToken().catch((error) => {
|
|
1443
|
+
logger?.warn?.(
|
|
1444
|
+
"Grok subscription credential clear failed: %s",
|
|
1445
|
+
error instanceof Error ? error.message : "unknown"
|
|
1446
|
+
);
|
|
1447
|
+
});
|
|
1448
|
+
catalog = { models: [], source: "signed-out", error: void 0 };
|
|
1449
|
+
lastPublic = publicSessionView(void 0);
|
|
1450
|
+
lastUsage = unavailable("Not signed in");
|
|
1451
|
+
notifyCatalogChange();
|
|
1452
|
+
return { ok: true, account: lastPublic, catalog, usage: lastUsage };
|
|
1453
|
+
};
|
|
1454
|
+
return {
|
|
1455
|
+
pull,
|
|
1456
|
+
status,
|
|
1457
|
+
refreshCatalog,
|
|
1458
|
+
refreshUsage,
|
|
1459
|
+
login,
|
|
1460
|
+
logout,
|
|
1461
|
+
currentToken: readStoredToken,
|
|
1462
|
+
models: () => catalog.models,
|
|
1463
|
+
catalog: () => catalog,
|
|
1464
|
+
usage: () => lastUsage,
|
|
1465
|
+
publicAccount: () => lastPublic
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// src/rpc-contract.js
|
|
1470
|
+
var RPC_ENDPOINTS = Object.freeze([
|
|
1471
|
+
"status",
|
|
1472
|
+
"login/cli",
|
|
1473
|
+
"login/device",
|
|
1474
|
+
"pull",
|
|
1475
|
+
"logout",
|
|
1476
|
+
"catalog/refresh",
|
|
1477
|
+
"usage",
|
|
1478
|
+
"usage/refresh"
|
|
1479
|
+
]);
|
|
1480
|
+
function publicResult(value) {
|
|
1481
|
+
return { ok: true, value };
|
|
1482
|
+
}
|
|
1483
|
+
function publicError(error, fallback = "Grok subscription request failed") {
|
|
1484
|
+
const message = error instanceof Error ? error.message : fallback;
|
|
1485
|
+
return { ok: false, error: { code: "internal", message, details: { issues: [] } } };
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// src/rpc.js
|
|
1489
|
+
function stripSecrets(value) {
|
|
1490
|
+
if (!value || typeof value !== "object") return value;
|
|
1491
|
+
const next = { ...value };
|
|
1492
|
+
delete next.accessToken;
|
|
1493
|
+
delete next.token;
|
|
1494
|
+
delete next.refresh;
|
|
1495
|
+
delete next.refresh_token;
|
|
1496
|
+
delete next.key;
|
|
1497
|
+
if (next.catalog) {
|
|
1498
|
+
next.catalog = {
|
|
1499
|
+
source: next.catalog.source,
|
|
1500
|
+
error: next.catalog.error,
|
|
1501
|
+
models: Array.isArray(next.catalog.models) ? next.catalog.models.map((model) => ({
|
|
1502
|
+
id: model.id,
|
|
1503
|
+
name: model.name,
|
|
1504
|
+
contextWindow: model.contextWindow,
|
|
1505
|
+
reasoning: model.reasoning,
|
|
1506
|
+
source: model.source
|
|
1507
|
+
})) : []
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
if (next.account) {
|
|
1511
|
+
next.account = {
|
|
1512
|
+
signedIn: next.account.signedIn === true,
|
|
1513
|
+
maskedAccount: next.account.maskedAccount,
|
|
1514
|
+
authMode: next.account.authMode,
|
|
1515
|
+
expiresAt: next.account.expiresAt,
|
|
1516
|
+
source: next.account.source
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
if (next.usage) {
|
|
1520
|
+
next.usage = sanitizeUsage(next.usage);
|
|
1521
|
+
}
|
|
1522
|
+
return next;
|
|
1523
|
+
}
|
|
1524
|
+
function sanitizeUsage(usage) {
|
|
1525
|
+
if (!usage || typeof usage !== "object") {
|
|
1526
|
+
return { status: "unavailable", reason: "Usage unavailable", experimental: true, source: "billing-credits-undocumented" };
|
|
1527
|
+
}
|
|
1528
|
+
const out = {
|
|
1529
|
+
status: usage.status === "ok" ? "ok" : "unavailable",
|
|
1530
|
+
experimental: true,
|
|
1531
|
+
source: typeof usage.source === "string" ? usage.source : "billing-credits-undocumented"
|
|
1532
|
+
};
|
|
1533
|
+
if (out.status !== "ok") {
|
|
1534
|
+
out.reason = typeof usage.reason === "string" ? usage.reason : "Usage unavailable";
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
if (typeof usage.usedPercent === "number" && Number.isFinite(usage.usedPercent)) out.usedPercent = usage.usedPercent;
|
|
1538
|
+
if (typeof usage.remainingPercent === "number" && Number.isFinite(usage.remainingPercent)) out.remainingPercent = usage.remainingPercent;
|
|
1539
|
+
if (typeof usage.periodStart === "string") out.periodStart = usage.periodStart;
|
|
1540
|
+
if (typeof usage.periodStartLocal === "string") out.periodStartLocal = usage.periodStartLocal;
|
|
1541
|
+
if (typeof usage.periodEnd === "string") out.periodEnd = usage.periodEnd;
|
|
1542
|
+
if (typeof usage.periodEndLocal === "string") out.periodEndLocal = usage.periodEndLocal;
|
|
1543
|
+
if (typeof usage.fetchedAt === "string") out.fetchedAt = usage.fetchedAt;
|
|
1544
|
+
if (Array.isArray(usage.productUsage)) {
|
|
1545
|
+
out.productUsage = usage.productUsage.filter((row) => row && typeof row === "object").map((row) => ({
|
|
1546
|
+
...typeof row.name === "string" ? { name: row.name } : {},
|
|
1547
|
+
...typeof row.usedPercent === "number" && Number.isFinite(row.usedPercent) ? { usedPercent: row.usedPercent } : {}
|
|
1548
|
+
}));
|
|
1549
|
+
}
|
|
1550
|
+
return out;
|
|
1551
|
+
}
|
|
1552
|
+
async function dispatch(session, endpoint) {
|
|
1553
|
+
if (endpoint === "status") return publicResult(stripSecrets(await session.status()));
|
|
1554
|
+
if (endpoint === "pull") return publicResult(stripSecrets(await session.pull()));
|
|
1555
|
+
if (endpoint === "logout") return publicResult(stripSecrets(await session.logout()));
|
|
1556
|
+
if (endpoint === "catalog/refresh") {
|
|
1557
|
+
const catalog = await session.refreshCatalog();
|
|
1558
|
+
return publicResult(stripSecrets({ catalog, account: session.publicAccount(), usage: session.usage?.() }));
|
|
1559
|
+
}
|
|
1560
|
+
if (endpoint === "usage") {
|
|
1561
|
+
return publicResult(stripSecrets({ usage: session.usage?.() ?? { status: "unavailable", reason: "Usage unavailable", experimental: true } }));
|
|
1562
|
+
}
|
|
1563
|
+
if (endpoint === "usage/refresh") {
|
|
1564
|
+
const usage = await session.refreshUsage();
|
|
1565
|
+
return publicResult(stripSecrets({ usage, account: session.publicAccount() }));
|
|
1566
|
+
}
|
|
1567
|
+
if (endpoint === "login/cli") return publicResult(stripSecrets(await session.login({ device: false })));
|
|
1568
|
+
if (endpoint === "login/device") return publicResult(stripSecrets(await session.login({ device: true })));
|
|
1569
|
+
return publicError(new Error(`Unknown Grok subscription RPC: ${endpoint}`));
|
|
1570
|
+
}
|
|
1571
|
+
function createRpcHandler(session, options = {}) {
|
|
1572
|
+
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : RPC_HANDLER_TIMEOUT_MS;
|
|
1573
|
+
return async function handle(endpoint, _payload, _signal) {
|
|
1574
|
+
try {
|
|
1575
|
+
return await withTimeout(
|
|
1576
|
+
dispatch(session, endpoint),
|
|
1577
|
+
timeoutMs,
|
|
1578
|
+
`Grok subscription RPC "${endpoint}" timed out after ${timeoutMs}ms`,
|
|
1579
|
+
{ unref: false }
|
|
1580
|
+
);
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
return publicError(error);
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// src/transport.js
|
|
1588
|
+
function parseEnvelope(body, method) {
|
|
1589
|
+
if (!body || typeof body !== "object") return void 0;
|
|
1590
|
+
if (body.method !== method) return void 0;
|
|
1591
|
+
return {
|
|
1592
|
+
rpcId: body.rpcId,
|
|
1593
|
+
payload: body.payload
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
var cachedClientRequestSchema;
|
|
1597
|
+
var schemaLoadStarted = false;
|
|
1598
|
+
function kickSchemaLoad() {
|
|
1599
|
+
if (schemaLoadStarted) return;
|
|
1600
|
+
schemaLoadStarted = true;
|
|
1601
|
+
void optionalImport("@deepseek-ai/dsh-client-connection").then((mod) => {
|
|
1602
|
+
if (mod?.clientRequestSchema && typeof mod.clientRequestSchema.safeParse === "function") {
|
|
1603
|
+
cachedClientRequestSchema = mod.clientRequestSchema;
|
|
1604
|
+
}
|
|
1605
|
+
}).catch(() => {
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
function resolveEnvelope(body, method) {
|
|
1609
|
+
const schema = cachedClientRequestSchema;
|
|
1610
|
+
if (schema) {
|
|
1611
|
+
const envelope = schema.safeParse(body);
|
|
1612
|
+
if (!envelope.success || envelope.data.method !== method) return void 0;
|
|
1613
|
+
return { rpcId: envelope.data.rpcId, payload: envelope.data.payload };
|
|
1614
|
+
}
|
|
1615
|
+
return parseEnvelope(body, method);
|
|
1616
|
+
}
|
|
1617
|
+
function registerSubscriptionTransport(connection, handler) {
|
|
1618
|
+
kickSchemaLoad();
|
|
1619
|
+
const disposers = [];
|
|
1620
|
+
try {
|
|
1621
|
+
for (const endpoint of RPC_ENDPOINTS) {
|
|
1622
|
+
const method = `grok-subscription/${endpoint}`;
|
|
1623
|
+
if (typeof connection?.fetch?.register !== "function") {
|
|
1624
|
+
throw new Error("DSH connection.fetch.register is unavailable");
|
|
1625
|
+
}
|
|
1626
|
+
disposers.push(connection.fetch.register({
|
|
1627
|
+
path: `/api/${method}`,
|
|
1628
|
+
methods: ["POST"],
|
|
1629
|
+
requestBody: "buffered",
|
|
1630
|
+
async fetch(request) {
|
|
1631
|
+
if (request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
|
|
1632
|
+
return new Response("content type must be application/json", { status: 415 });
|
|
1633
|
+
}
|
|
1634
|
+
let body;
|
|
1635
|
+
try {
|
|
1636
|
+
body = await request.json();
|
|
1637
|
+
} catch {
|
|
1638
|
+
return new Response("invalid JSON", { status: 400 });
|
|
1639
|
+
}
|
|
1640
|
+
if (!cachedClientRequestSchema) {
|
|
1641
|
+
try {
|
|
1642
|
+
const mod = await optionalImport("@deepseek-ai/dsh-client-connection");
|
|
1643
|
+
if (mod?.clientRequestSchema && typeof mod.clientRequestSchema.safeParse === "function") {
|
|
1644
|
+
cachedClientRequestSchema = mod.clientRequestSchema;
|
|
1645
|
+
}
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
const envelope = resolveEnvelope(body, method);
|
|
1650
|
+
if (!envelope) return new Response("invalid RPC envelope", { status: 400 });
|
|
1651
|
+
let result;
|
|
1652
|
+
try {
|
|
1653
|
+
request.signal?.throwIfAborted?.();
|
|
1654
|
+
result = await handler(endpoint, envelope.payload, request.signal);
|
|
1655
|
+
} catch {
|
|
1656
|
+
result = { ok: false, error: { code: "internal", message: "Grok subscription request failed", details: { issues: [] } } };
|
|
1657
|
+
}
|
|
1658
|
+
return Response.json({ type: "server-response", rpcId: envelope.rpcId, result });
|
|
1659
|
+
}
|
|
1660
|
+
}));
|
|
1661
|
+
}
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
for (const dispose of disposers.reverse()) dispose?.();
|
|
1664
|
+
throw error;
|
|
1665
|
+
}
|
|
1666
|
+
return () => {
|
|
1667
|
+
for (const dispose of disposers.reverse()) dispose?.();
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1123
1671
|
// src/index.js
|
|
1124
1672
|
var name = CORDIS_ID;
|
|
1125
|
-
var inject = ["llm", "
|
|
1126
|
-
function
|
|
1673
|
+
var inject = ["llm", "web"];
|
|
1674
|
+
function scheduleDeferred2(run) {
|
|
1675
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
1676
|
+
else queueMicrotask(run);
|
|
1677
|
+
}
|
|
1678
|
+
function softService(ctx, key) {
|
|
1679
|
+
try {
|
|
1680
|
+
if (typeof ctx.get === "function") return ctx.get(key) ?? void 0;
|
|
1681
|
+
} catch {
|
|
1682
|
+
}
|
|
1683
|
+
return void 0;
|
|
1684
|
+
}
|
|
1685
|
+
function apply(ctx, options = {}) {
|
|
1127
1686
|
let active = true;
|
|
1128
1687
|
if (typeof ctx.effect === "function") {
|
|
1129
1688
|
ctx.effect(
|
|
@@ -1134,60 +1693,103 @@ function apply(ctx) {
|
|
|
1134
1693
|
);
|
|
1135
1694
|
}
|
|
1136
1695
|
const notifyCatalogChange = () => {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1696
|
+
scheduleDeferred2(() => {
|
|
1697
|
+
if (!active) return;
|
|
1698
|
+
try {
|
|
1699
|
+
ctx.emit("llm/adapters-updated");
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
ctx.logger?.warn?.("an llm/adapters-updated listener failed");
|
|
1702
|
+
ctx.logger?.warn?.(error);
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1144
1705
|
};
|
|
1706
|
+
const credentials = softService(ctx, "credentials");
|
|
1145
1707
|
const session = createSessionService({
|
|
1146
|
-
credentials
|
|
1708
|
+
credentials,
|
|
1147
1709
|
logger: ctx.logger,
|
|
1148
1710
|
onCatalogChange: notifyCatalogChange
|
|
1149
1711
|
});
|
|
1150
1712
|
const handler = createRpcHandler(session);
|
|
1151
|
-
|
|
1713
|
+
const createSync = options.createSync ?? createGrokBuildAdapterSync;
|
|
1714
|
+
try {
|
|
1715
|
+
const created = createSync(session);
|
|
1716
|
+
if (created.note) ctx.logger?.debug?.(created.note);
|
|
1717
|
+
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
1718
|
+
notifyCatalogChange();
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
ctx.logger?.warn?.(
|
|
1721
|
+
"Grok subscription duck adapter failed to register: %s",
|
|
1722
|
+
error instanceof Error ? error.message : "unknown"
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
const defer = options.defer ?? scheduleDeferred2;
|
|
1726
|
+
defer(() => {
|
|
1727
|
+
void deferredBoot(ctx, session, notifyCatalogChange, options);
|
|
1728
|
+
});
|
|
1152
1729
|
ctx.inject(["connection"], (connectionContext) => connectionContext.effect(
|
|
1153
1730
|
() => registerSubscriptionTransport(connectionContext.connection, handler),
|
|
1154
1731
|
"grok-subscription: host-only account RPC"
|
|
1155
1732
|
));
|
|
1156
1733
|
}
|
|
1157
|
-
async function
|
|
1158
|
-
|
|
1734
|
+
async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
1735
|
+
if (!options.skipSettings) {
|
|
1736
|
+
await tryRegisterSettings(ctx);
|
|
1737
|
+
}
|
|
1159
1738
|
try {
|
|
1160
|
-
|
|
1739
|
+
ctx.llm.registerConfigurableProviders?.([{
|
|
1740
|
+
provider: PROVIDER_ID,
|
|
1741
|
+
displayName: DISPLAY_NAME,
|
|
1742
|
+
settingsNs: SETTINGS_NAMESPACE,
|
|
1743
|
+
settingsPath: []
|
|
1744
|
+
}]);
|
|
1161
1745
|
} catch (error) {
|
|
1162
|
-
ctx.logger?.debug?.(
|
|
1746
|
+
ctx.logger?.debug?.(
|
|
1747
|
+
"Grok subscription provider directory skipped: %s",
|
|
1748
|
+
error instanceof Error ? error.message : "unknown"
|
|
1749
|
+
);
|
|
1163
1750
|
}
|
|
1751
|
+
if (!options.skipPull) {
|
|
1752
|
+
void session.pull().catch((error) => {
|
|
1753
|
+
ctx.logger?.debug?.(
|
|
1754
|
+
"Grok subscription startup pull skipped: %s",
|
|
1755
|
+
error instanceof Error ? error.message : "unknown"
|
|
1756
|
+
);
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
if (options.skipUpgrade) return;
|
|
1760
|
+
const createAsync = options.createAsync ?? createGrokBuildAdapter;
|
|
1164
1761
|
try {
|
|
1165
|
-
const created = await
|
|
1166
|
-
if (created.
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
settingsNs: SETTINGS_NAMESPACE,
|
|
1173
|
-
settingsPath: []
|
|
1174
|
-
}]);
|
|
1175
|
-
} catch (error) {
|
|
1176
|
-
ctx.logger?.debug?.("Grok subscription provider directory skipped: %s", error instanceof Error ? error.message : "unknown");
|
|
1762
|
+
const created = await createAsync(session);
|
|
1763
|
+
if (created.kind === "pi-ai") {
|
|
1764
|
+
if (created.note) ctx.logger?.warn?.(created.note);
|
|
1765
|
+
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
1766
|
+
notifyCatalogChange?.();
|
|
1767
|
+
} else if (created.note) {
|
|
1768
|
+
ctx.logger?.debug?.(created.note);
|
|
1177
1769
|
}
|
|
1178
|
-
notifyCatalogChange?.();
|
|
1179
1770
|
} catch (error) {
|
|
1180
|
-
ctx.logger?.warn?.(
|
|
1771
|
+
ctx.logger?.warn?.(
|
|
1772
|
+
"Grok subscription adapter upgrade failed: %s",
|
|
1773
|
+
error instanceof Error ? error.message : "unknown"
|
|
1774
|
+
);
|
|
1181
1775
|
}
|
|
1182
1776
|
}
|
|
1183
1777
|
async function tryRegisterSettings(ctx) {
|
|
1184
|
-
|
|
1778
|
+
const settings = softService(ctx, "settings");
|
|
1779
|
+
if (typeof settings?.register !== "function") return;
|
|
1185
1780
|
try {
|
|
1186
|
-
const mod = await
|
|
1781
|
+
const mod = await optionalImport("@deepseek-ai/schemastery");
|
|
1782
|
+
if (!mod) {
|
|
1783
|
+
ctx.logger?.debug?.("Grok subscription settings namespace skipped: schemastery unavailable or timed out");
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1187
1786
|
const z = mod.default ?? mod;
|
|
1188
|
-
|
|
1787
|
+
settings.register(SETTINGS_NAMESPACE, z.object({}));
|
|
1189
1788
|
} catch (error) {
|
|
1190
|
-
ctx.logger?.debug?.(
|
|
1789
|
+
ctx.logger?.debug?.(
|
|
1790
|
+
"Grok subscription settings namespace skipped: %s",
|
|
1791
|
+
error instanceof Error ? error.message : "unknown"
|
|
1792
|
+
);
|
|
1191
1793
|
}
|
|
1192
1794
|
}
|
|
1193
1795
|
export {
|
|
@@ -1195,6 +1797,9 @@ export {
|
|
|
1195
1797
|
DISPLAY_NAME_ZH,
|
|
1196
1798
|
PROVIDER_ID,
|
|
1197
1799
|
apply,
|
|
1800
|
+
deferredBoot,
|
|
1198
1801
|
inject,
|
|
1199
|
-
name
|
|
1802
|
+
name,
|
|
1803
|
+
scheduleDeferred2 as scheduleDeferred,
|
|
1804
|
+
softService
|
|
1200
1805
|
};
|