dsh-grok-subscription 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,1200 @@
1
+ // src/constants.js
2
+ var CORDIS_ID = "grok-subscription";
3
+ var PROVIDER_ID = "grok-build";
4
+ var DISPLAY_NAME = "Grok Build (subscription)";
5
+ var DISPLAY_NAME_ZH = "Grok \u8BA2\u9605";
6
+ var SETTINGS_NAMESPACE = "grokSubscription";
7
+ var CREDENTIAL_REF_NAME = "GROK_BUILD_ACCESS_TOKEN";
8
+ var PROXY_BASE_URL = "https://cli-chat-proxy.grok.com/v1";
9
+ var RESPONSES_URL = "https://cli-chat-proxy.grok.com/v1/responses";
10
+ var MODELS_V2_URL = "https://cli-chat-proxy.grok.com/v1/models-v2";
11
+ var TOKEN_AUTH_HEADER = "X-XAI-Token-Auth";
12
+ var TOKEN_AUTH_VALUE = "xai-grok-cli";
13
+ var CLIENT_IDENTIFIER_HEADER = "x-grok-client-identifier";
14
+ var CLIENT_VERSION_HEADER = "x-grok-client-version";
15
+ var CLIENT_IDENTIFIER = "grok-shell";
16
+ var CLIENT_VERSION_FALLBACK = "1.0.5";
17
+ var API_KEY_SCOPE = "xai::api_key";
18
+ var SESSION_AUTH_MODES = Object.freeze(["oidc", "external", "web_login", "grok"]);
19
+ var API_KEY_AUTH_MODES = Object.freeze(["api_key"]);
20
+ var AUTH_FILE_MAX_BYTES = 1048576;
21
+ var CATALOG_TIMEOUT_MS = 15e3;
22
+ var STREAM_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
23
+ var MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;
24
+ var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
25
+ var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
26
+ var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
27
+
28
+ // src/session.js
29
+ import { existsSync as existsSync2 } from "node:fs";
30
+ import { spawn } from "node:child_process";
31
+ import { join as join2 } from "node:path";
32
+
33
+ // src/auth-file.js
34
+ import { lstatSync, openSync, readFileSync, closeSync, constants as fsConstants } from "node:fs";
35
+ import { homedir } from "node:os";
36
+ import { join } from "node:path";
37
+ var XAI_OAUTH_ISSUER = "https://auth.x.ai";
38
+ function grokHome(env = process.env) {
39
+ const override = typeof env.GROK_HOME === "string" && env.GROK_HOME.trim() ? env.GROK_HOME.trim() : void 0;
40
+ return override ?? join(homedir(), ".grok");
41
+ }
42
+ function authJsonPath(env = process.env) {
43
+ if (typeof env.DSH_GROK_AUTH_PATH === "string" && env.DSH_GROK_AUTH_PATH.trim()) {
44
+ return env.DSH_GROK_AUTH_PATH.trim();
45
+ }
46
+ return join(grokHome(env), "auth.json");
47
+ }
48
+ function versionJsonPath(env = process.env) {
49
+ if (typeof env.DSH_GROK_VERSION_PATH === "string" && env.DSH_GROK_VERSION_PATH.trim()) {
50
+ return env.DSH_GROK_VERSION_PATH.trim();
51
+ }
52
+ return join(grokHome(env), "version.json");
53
+ }
54
+ function inspectAuthFileSafety(meta, options = {}) {
55
+ const platform = options.platform ?? process.platform;
56
+ const currentUid = options.uid ?? process.getuid?.();
57
+ if (meta.isSymbolicLink) {
58
+ throw new Error("Refusing to read a symbolic-link Grok auth.json");
59
+ }
60
+ if (!meta.isFile) {
61
+ throw new Error("Grok auth.json is not a regular file");
62
+ }
63
+ if (typeof meta.size === "number" && meta.size > AUTH_FILE_MAX_BYTES) {
64
+ throw new Error("Grok auth.json is larger than the allowed size");
65
+ }
66
+ if (platform === "win32") return;
67
+ if (typeof meta.mode === "number" && (meta.mode & 63) !== 0) {
68
+ throw new Error("Grok auth.json must be owner-only (chmod 600); group/other access is refused");
69
+ }
70
+ if (typeof currentUid === "number" && typeof meta.uid === "number" && meta.uid !== currentUid) {
71
+ throw new Error("Grok auth.json is not owned by the current user");
72
+ }
73
+ }
74
+ function readSecureJsonFile(path, options = {}) {
75
+ const lstat = options.lstat ?? ((target) => lstatSync(target, { throwIfNoEntry: false }));
76
+ const read = options.read ?? ((target) => {
77
+ const fd = openSync(target, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
78
+ try {
79
+ return readFileSync(fd, "utf8");
80
+ } finally {
81
+ closeSync(fd);
82
+ }
83
+ });
84
+ const stat = lstat(path);
85
+ if (!stat) throw new Error(`Grok file not found: ${path}`);
86
+ inspectAuthFileSafety({
87
+ isSymbolicLink: typeof stat.isSymbolicLink === "function" ? stat.isSymbolicLink() : Boolean(stat.isSymbolicLink),
88
+ isFile: typeof stat.isFile === "function" ? stat.isFile() : Boolean(stat.isFile),
89
+ mode: stat.mode,
90
+ uid: stat.uid,
91
+ size: stat.size
92
+ }, options);
93
+ const text = read(path);
94
+ if (typeof text !== "string" || text.trim() === "") {
95
+ throw new Error("Grok auth document is empty");
96
+ }
97
+ try {
98
+ return JSON.parse(text);
99
+ } catch (error) {
100
+ throw new Error("Grok auth document is not valid JSON", { cause: error });
101
+ }
102
+ }
103
+ function asNonEmptyString(value) {
104
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
105
+ }
106
+ function normalizeAuthMode(value) {
107
+ if (typeof value !== "string") return void 0;
108
+ return value.trim().toLowerCase();
109
+ }
110
+ function accessTokenFromEntry(entry) {
111
+ if (typeof entry === "string") return asNonEmptyString(entry);
112
+ if (!entry || typeof entry !== "object") return void 0;
113
+ return asNonEmptyString(entry.key) ?? asNonEmptyString(entry.access_token) ?? asNonEmptyString(entry.accessToken);
114
+ }
115
+ function isApiKeyOnlyEntry(entry, scope) {
116
+ if (scope === API_KEY_SCOPE) return true;
117
+ if (typeof entry === "string") return false;
118
+ if (!entry || typeof entry !== "object") return true;
119
+ const mode = normalizeAuthMode(entry.auth_mode ?? entry.authMode);
120
+ if (mode && API_KEY_AUTH_MODES.includes(mode)) return true;
121
+ if (mode && SESSION_AUTH_MODES.includes(mode)) return false;
122
+ const token = accessTokenFromEntry(entry);
123
+ const refresh = asNonEmptyString(entry.refresh_token ?? entry.refreshToken);
124
+ const issuer = asNonEmptyString(entry.oidc_issuer ?? entry.oidcIssuer);
125
+ if (token && (refresh || issuer || mode)) return false;
126
+ if (token && !refresh && !issuer && !mode) return true;
127
+ return !token;
128
+ }
129
+ function maskAccount(value) {
130
+ if (typeof value !== "string" || value.trim() === "") return void 0;
131
+ const text = value.trim();
132
+ if (!text.includes("@")) {
133
+ if (text.length <= 2) return `${text[0] ?? ""}\u2022`;
134
+ return `${text[0]}\u2022\u2022\u2022${text.at(-1)}`;
135
+ }
136
+ const [local, domain] = text.split("@", 2);
137
+ if (!domain) return "\u2022\u2022\u2022\u2022";
138
+ if (local.length <= 2) return `${local.slice(0, 1)}\u2022\u2022@${domain}`;
139
+ return `${local[0]}\u2022\u2022\u2022${local.at(-1)}@${domain}`;
140
+ }
141
+ function sessionFromEntry(scope, entry) {
142
+ if (isApiKeyOnlyEntry(entry, scope)) return void 0;
143
+ const token = accessTokenFromEntry(entry);
144
+ if (!token) return void 0;
145
+ const object = entry && typeof entry === "object" ? entry : {};
146
+ const email = asNonEmptyString(object.email);
147
+ const userId = asNonEmptyString(object.user_id ?? object.userId);
148
+ const expiresAt = asNonEmptyString(object.expires_at ?? object.expiresAt);
149
+ const authMode = normalizeAuthMode(object.auth_mode ?? object.authMode) ?? "oidc";
150
+ return Object.freeze({
151
+ scope,
152
+ accessToken: token,
153
+ authMode,
154
+ email,
155
+ userId,
156
+ expiresAt,
157
+ maskedAccount: maskAccount(email) ?? maskAccount(userId)
158
+ });
159
+ }
160
+ function preferredScopeScore(scope, entry) {
161
+ const issuer = entry && typeof entry === "object" ? asNonEmptyString(entry.oidc_issuer ?? entry.oidcIssuer) : void 0;
162
+ if (typeof scope === "string" && scope.startsWith(`${XAI_OAUTH_ISSUER}::`)) return 3;
163
+ if (issuer === XAI_OAUTH_ISSUER) return 2;
164
+ if (SESSION_AUTH_MODES.includes(normalizeAuthMode(entry?.auth_mode ?? entry?.authMode) ?? "")) return 1;
165
+ return 0;
166
+ }
167
+ function parseAuthDocument(document) {
168
+ if (Array.isArray(document)) {
169
+ return { session: void 0, reason: "unsupported-shape" };
170
+ }
171
+ if (!document || typeof document !== "object") {
172
+ return { session: void 0, reason: "invalid-document" };
173
+ }
174
+ if (asNonEmptyString(document.access_token) || asNonEmptyString(document.key)) {
175
+ const session = sessionFromEntry("document", document);
176
+ if (session) return { session, reason: void 0 };
177
+ if (isApiKeyOnlyEntry(document, document.scope)) {
178
+ return { session: void 0, reason: "api-key-only" };
179
+ }
180
+ }
181
+ const candidates = [];
182
+ let sawApiKey = false;
183
+ for (const [scope, entry] of Object.entries(document)) {
184
+ if (isApiKeyOnlyEntry(entry, scope)) {
185
+ sawApiKey = true;
186
+ continue;
187
+ }
188
+ const session = sessionFromEntry(scope, entry);
189
+ if (session) candidates.push(session);
190
+ }
191
+ if (candidates.length === 0) {
192
+ return { session: void 0, reason: sawApiKey ? "api-key-only" : "no-session" };
193
+ }
194
+ candidates.sort((a, b) => preferredScopeScore(b.scope, document[b.scope]) - preferredScopeScore(a.scope, document[a.scope]));
195
+ return { session: candidates[0], reason: void 0 };
196
+ }
197
+ function readGrokAuthSession(path = authJsonPath(), options = {}) {
198
+ const document = readSecureJsonFile(path, options);
199
+ return parseAuthDocument(document);
200
+ }
201
+ function publicSessionView(session) {
202
+ if (!session) {
203
+ return Object.freeze({
204
+ signedIn: false,
205
+ maskedAccount: void 0,
206
+ authMode: void 0,
207
+ source: void 0
208
+ });
209
+ }
210
+ return Object.freeze({
211
+ signedIn: true,
212
+ maskedAccount: session.maskedAccount,
213
+ authMode: session.authMode,
214
+ expiresAt: session.expiresAt,
215
+ source: "grok-cli"
216
+ });
217
+ }
218
+
219
+ // src/headers.js
220
+ import { readFileSync as readFileSync2, existsSync } from "node:fs";
221
+ function parseClientVersion(document) {
222
+ if (typeof document === "string" && document.trim()) return document.trim();
223
+ if (!document || typeof document !== "object") return void 0;
224
+ for (const key of ["version", "cliVersion", "cli_version", "grokVersion"]) {
225
+ if (typeof document[key] === "string" && document[key].trim()) return document[key].trim();
226
+ }
227
+ if (document.grok && typeof document.grok === "object" && typeof document.grok.version === "string") {
228
+ return document.grok.version.trim();
229
+ }
230
+ return void 0;
231
+ }
232
+ function readClientVersion(env = process.env, options = {}) {
233
+ if (typeof env.DSH_GROK_CLIENT_VERSION === "string" && env.DSH_GROK_CLIENT_VERSION.trim()) {
234
+ return env.DSH_GROK_CLIENT_VERSION.trim();
235
+ }
236
+ const path = options.path ?? versionJsonPath(env);
237
+ const exists = options.exists ?? existsSync;
238
+ const read = options.read ?? ((target) => readFileSync2(target, "utf8"));
239
+ if (!exists(path)) return CLIENT_VERSION_FALLBACK;
240
+ try {
241
+ const parsed = parseClientVersion(JSON.parse(read(path)));
242
+ return parsed ?? CLIENT_VERSION_FALLBACK;
243
+ } catch {
244
+ return CLIENT_VERSION_FALLBACK;
245
+ }
246
+ }
247
+ function fingerprintHeaders(options = {}) {
248
+ const identifier = options.identifier ?? CLIENT_IDENTIFIER;
249
+ const version = options.version ?? readClientVersion(options.env, options);
250
+ return Object.freeze({
251
+ [TOKEN_AUTH_HEADER]: TOKEN_AUTH_VALUE,
252
+ [CLIENT_IDENTIFIER_HEADER]: identifier,
253
+ [CLIENT_VERSION_HEADER]: version,
254
+ "User-Agent": `${identifier}/${version}`
255
+ });
256
+ }
257
+ function buildProxyHeaders(accessToken, options = {}) {
258
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
259
+ throw new Error("A Grok Build access token is required to build proxy headers");
260
+ }
261
+ return Object.freeze({
262
+ Authorization: `Bearer ${accessToken}`,
263
+ ...fingerprintHeaders(options)
264
+ });
265
+ }
266
+
267
+ // src/catalog.js
268
+ var REASONING_LEVELS = Object.freeze(["low", "medium", "high", "xhigh"]);
269
+ function asId(value) {
270
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
271
+ }
272
+ function rowsFromBody(body) {
273
+ if (Array.isArray(body)) return body;
274
+ if (!body || typeof body !== "object") return [];
275
+ if (Array.isArray(body.data)) return body.data;
276
+ if (Array.isArray(body.models)) return body.models;
277
+ if (Array.isArray(body.items)) return body.items;
278
+ return [];
279
+ }
280
+ function contextWindowOf(row) {
281
+ const value = row.contextWindow ?? row.context_window ?? row.context ?? row.max_context;
282
+ const number = typeof value === "number" ? value : Number(value);
283
+ return Number.isFinite(number) && number > 0 ? Math.trunc(number) : void 0;
284
+ }
285
+ function reasoningEffortsOf(row) {
286
+ const raw = row.reasoning_efforts ?? row.reasoningEfforts ?? row.supported_reasoning_efforts;
287
+ if (!Array.isArray(raw)) return void 0;
288
+ const levels = raw.map((item) => typeof item === "string" ? item.trim().toLowerCase() : void 0).filter((item) => REASONING_LEVELS.includes(item));
289
+ return levels.length ? [...new Set(levels)] : void 0;
290
+ }
291
+ function displayNameOf(id, row) {
292
+ if (typeof row.name === "string" && row.name.trim()) return row.name.trim();
293
+ return id.replace(/^grok-/, "Grok ").replace(/\b\w/g, (char) => char.toUpperCase()).replace("Grok ", "Grok ");
294
+ }
295
+ function extractLiveModels(body) {
296
+ const seen = /* @__PURE__ */ new Set();
297
+ const models = [];
298
+ for (const row of rowsFromBody(body)) {
299
+ const id = typeof row === "string" ? asId(row) : asId(row?.id ?? row?.model ?? row?.name);
300
+ if (!id || seen.has(id)) continue;
301
+ seen.add(id);
302
+ const object = row && typeof row === "object" ? row : { id };
303
+ models.push(Object.freeze({
304
+ id,
305
+ name: displayNameOf(id, object),
306
+ contextWindow: contextWindowOf(object) ?? 5e5,
307
+ maxTokens: contextWindowOf(object) ?? 5e5,
308
+ reasoning: object.reasoning !== false,
309
+ reasoningEfforts: reasoningEffortsOf(object)
310
+ }));
311
+ }
312
+ return models;
313
+ }
314
+ function fallbackModels() {
315
+ return FALLBACK_MODEL_IDS.map((id) => Object.freeze({
316
+ id,
317
+ name: id === "grok-4.7" ? "Grok 4.7" : id === "grok-4.6" ? "Grok 4.6" : "Grok 4.5",
318
+ contextWindow: 5e5,
319
+ maxTokens: 5e5,
320
+ reasoning: true,
321
+ reasoningEfforts: id === "grok-4.5" ? ["low", "medium", "high"] : ["low", "medium", "high", "xhigh"],
322
+ source: "fallback"
323
+ }));
324
+ }
325
+ function mergeCatalog(live) {
326
+ if (!Array.isArray(live) || live.length === 0) {
327
+ return fallbackModels().map((model) => ({ ...model }));
328
+ }
329
+ return live.map((model) => ({ ...model, source: "live" }));
330
+ }
331
+ function toPiModels(models) {
332
+ return models.map((model) => {
333
+ const efforts = model.reasoningEfforts ?? ["low", "medium", "high", "xhigh"];
334
+ const thinkingLevelMap = {
335
+ off: null,
336
+ minimal: null,
337
+ low: efforts.includes("low") ? "low" : null,
338
+ medium: efforts.includes("medium") ? "medium" : null,
339
+ high: efforts.includes("high") ? "high" : null,
340
+ xhigh: efforts.includes("xhigh") ? "xhigh" : null,
341
+ max: null
342
+ };
343
+ return {
344
+ id: model.id,
345
+ name: model.name,
346
+ api: "openai-responses",
347
+ provider: PROVIDER_ID,
348
+ baseUrl: PROXY_BASE_URL,
349
+ reasoning: model.reasoning !== false,
350
+ thinkingLevelMap,
351
+ input: ["text"],
352
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
353
+ contextWindow: model.contextWindow ?? 5e5,
354
+ maxTokens: model.maxTokens ?? 5e5,
355
+ compat: { supportsLongCacheRetention: false, supportsDeveloperRole: false }
356
+ };
357
+ });
358
+ }
359
+ function toLlmModels(models) {
360
+ return models.map((model) => ({
361
+ provider: PROVIDER_ID,
362
+ id: model.id,
363
+ name: model.name,
364
+ inputModalities: ["text"]
365
+ }));
366
+ }
367
+ async function fetchLiveCatalog(accessToken, options = {}) {
368
+ const fetchImpl = options.fetch ?? globalThis.fetch;
369
+ const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? CATALOG_TIMEOUT_MS);
370
+ const response = await fetchImpl(MODELS_V2_URL, {
371
+ method: "GET",
372
+ headers: {
373
+ Accept: "application/json",
374
+ ...buildProxyHeaders(accessToken, options)
375
+ },
376
+ redirect: "error",
377
+ signal
378
+ });
379
+ if (!response.ok) {
380
+ throw new Error(`Grok models-v2 returned HTTP ${response.status}`);
381
+ }
382
+ const body = await response.json();
383
+ return extractLiveModels(body);
384
+ }
385
+ async function loadCatalog(accessToken, options = {}) {
386
+ if (!accessToken) {
387
+ return { models: [], source: "signed-out", error: void 0 };
388
+ }
389
+ try {
390
+ const live = await fetchLiveCatalog(accessToken, options);
391
+ if (live.length === 0) {
392
+ return { models: mergeCatalog([]), source: "fallback", error: "Live models-v2 listing was empty" };
393
+ }
394
+ return { models: mergeCatalog(live), source: "live", error: void 0 };
395
+ } catch (error) {
396
+ return {
397
+ models: mergeCatalog([]),
398
+ source: "fallback",
399
+ error: error instanceof Error ? error.message : "Could not refresh Grok model catalog"
400
+ };
401
+ }
402
+ }
403
+
404
+ // src/session.js
405
+ function resolveGrokBin(env = process.env, exists = existsSync2) {
406
+ if (typeof env.DSH_GROK_BIN === "string" && env.DSH_GROK_BIN.trim()) return env.DSH_GROK_BIN.trim();
407
+ const local = join2(grokHome(env), "bin", "grok");
408
+ if (exists(local)) return local;
409
+ return "grok";
410
+ }
411
+ function grokCliAvailable(env = process.env, exists = existsSync2) {
412
+ const bin = resolveGrokBin(env, exists);
413
+ if (bin.includes("/") || bin.includes("\\")) return exists(bin);
414
+ const delimiter = process.platform === "win32" ? ";" : ":";
415
+ return String(env.PATH ?? "").split(delimiter).some((dir) => dir && exists(join2(dir, bin)));
416
+ }
417
+ function spawnGrokLogin(options = {}) {
418
+ const spawnFn = options.spawn ?? spawn;
419
+ const bin = resolveGrokBin(options.env, options.exists);
420
+ const args = options.device ? ["login", "--device-auth"] : ["login"];
421
+ return new Promise((resolve, reject) => {
422
+ const child = spawnFn(bin, args, {
423
+ stdio: options.stdio ?? "inherit",
424
+ env: options.env ?? process.env
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
+ });
434
+ }
435
+ async function credentialRefOf() {
436
+ try {
437
+ const mod = await import("@deepseek-ai/dsh-credentials");
438
+ if (typeof mod.credentialRef === "function") return mod.credentialRef(CREDENTIAL_REF_NAME);
439
+ } catch {
440
+ }
441
+ return CREDENTIAL_REF_NAME;
442
+ }
443
+ function createSessionService({ credentials, logger, onCatalogChange } = {}) {
444
+ let catalog = { models: [], source: "signed-out", error: void 0 };
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
+ };
543
+ }
544
+
545
+ // src/rpc-contract.js
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 };
556
+ }
557
+ function publicError(error, fallback = "Grok subscription request failed") {
558
+ const message = error instanceof Error ? error.message : fallback;
559
+ return { ok: false, error: { code: "internal", message, details: { issues: [] } } };
560
+ }
561
+
562
+ // src/rpc.js
563
+ function stripSecrets(value) {
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
+ };
583
+ }
584
+ if (next.account) {
585
+ next.account = {
586
+ signedIn: next.account.signedIn === true,
587
+ maskedAccount: next.account.maskedAccount,
588
+ authMode: next.account.authMode,
589
+ expiresAt: next.account.expiresAt,
590
+ source: next.account.source
591
+ };
592
+ }
593
+ return next;
594
+ }
595
+ function createRpcHandler(session) {
596
+ return async function handle(endpoint, _payload, _signal) {
597
+ try {
598
+ if (endpoint === "status") return publicResult(stripSecrets(await session.status()));
599
+ if (endpoint === "pull") return publicResult(stripSecrets(await session.pull()));
600
+ if (endpoint === "logout") return publicResult(stripSecrets(await session.logout()));
601
+ if (endpoint === "catalog/refresh") {
602
+ const catalog = await session.refreshCatalog();
603
+ return publicResult(stripSecrets({ catalog, account: session.publicAccount() }));
604
+ }
605
+ if (endpoint === "login/cli") return publicResult(stripSecrets(await session.login({ device: false })));
606
+ if (endpoint === "login/device") return publicResult(stripSecrets(await session.login({ device: true })));
607
+ return publicError(new Error(`Unknown Grok subscription RPC: ${endpoint}`));
608
+ } catch (error) {
609
+ return publicError(error);
610
+ }
611
+ };
612
+ }
613
+
614
+ // src/transport.js
615
+ function parseEnvelope(body, method) {
616
+ if (!body || typeof body !== "object") return void 0;
617
+ if (body.method !== method) return void 0;
618
+ return {
619
+ rpcId: body.rpcId,
620
+ payload: body.payload
621
+ };
622
+ }
623
+ function registerSubscriptionTransport(connection, handler) {
624
+ const disposers = [];
625
+ try {
626
+ for (const endpoint of RPC_ENDPOINTS) {
627
+ const method = `grok-subscription/${endpoint}`;
628
+ if (typeof connection?.fetch?.register !== "function") {
629
+ throw new Error("DSH connection.fetch.register is unavailable");
630
+ }
631
+ disposers.push(connection.fetch.register({
632
+ path: `/api/${method}`,
633
+ methods: ["POST"],
634
+ requestBody: "buffered",
635
+ async fetch(request) {
636
+ if (request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
637
+ return new Response("content type must be application/json", { status: 415 });
638
+ }
639
+ let body;
640
+ try {
641
+ body = await request.json();
642
+ } catch {
643
+ return new Response("invalid JSON", { status: 400 });
644
+ }
645
+ const envelope = parseEnvelope(body, method);
646
+ if (!envelope) return new Response("invalid RPC envelope", { status: 400 });
647
+ let result;
648
+ try {
649
+ request.signal?.throwIfAborted?.();
650
+ result = await handler(endpoint, envelope.payload, request.signal);
651
+ } catch {
652
+ result = { ok: false, error: { code: "internal", message: "Grok subscription request failed", details: { issues: [] } } };
653
+ }
654
+ return Response.json({ type: "server-response", rpcId: envelope.rpcId, result });
655
+ }
656
+ }));
657
+ }
658
+ } catch (error) {
659
+ for (const dispose of disposers.reverse()) dispose?.();
660
+ throw error;
661
+ }
662
+ return () => {
663
+ for (const dispose of disposers.reverse()) dispose?.();
664
+ };
665
+ }
666
+
667
+ // src/adapter.js
668
+ async function optionalImport(specifier) {
669
+ try {
670
+ return await import(specifier);
671
+ } catch {
672
+ return void 0;
673
+ }
674
+ }
675
+ function textOf(content) {
676
+ if (typeof content === "string") return content;
677
+ if (!Array.isArray(content)) return "";
678
+ return content.map((block) => {
679
+ if (typeof block === "string") return block;
680
+ if (block?.type === "text") return block.text ?? "";
681
+ if (block?.type === "reasoning") return "";
682
+ if (block?.type === "tool-result") {
683
+ return typeof block.content === "string" ? block.content : textOf(block.content);
684
+ }
685
+ return "";
686
+ }).join("");
687
+ }
688
+ function responsesInput(options) {
689
+ const input = [];
690
+ const system = typeof options.system === "string" && options.system ? options.system : void 0;
691
+ if (system) input.push({ role: "system", content: system });
692
+ for (const message of options.messages ?? []) {
693
+ const role = message.role === "assistant" ? "assistant" : message.role === "system" ? "system" : "user";
694
+ const toolCalls = (message.content ?? []).filter((block) => block?.type === "tool-call");
695
+ const toolResults = (message.content ?? []).filter((block) => block?.type === "tool-result");
696
+ if (toolResults.length) {
697
+ for (const block of toolResults) {
698
+ input.push({
699
+ type: "function_call_output",
700
+ call_id: block.toolCallId ?? block.id,
701
+ output: textOf(block.content)
702
+ });
703
+ }
704
+ continue;
705
+ }
706
+ if (role === "assistant" && toolCalls.length) {
707
+ for (const block of toolCalls) {
708
+ input.push({
709
+ type: "function_call",
710
+ call_id: block.id,
711
+ name: block.name,
712
+ arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
713
+ });
714
+ }
715
+ const text2 = textOf(message.content);
716
+ if (text2) input.push({ role: "assistant", content: text2 });
717
+ continue;
718
+ }
719
+ const text = textOf(message.content);
720
+ if (!text) continue;
721
+ input.push({ role, content: text });
722
+ }
723
+ return input;
724
+ }
725
+ function mapFinish(reason) {
726
+ if (reason === "toolUse" || reason === "tool_calls") return { kind: "tool-calls" };
727
+ if (reason === "length" || reason === "max_tokens") return { kind: "max-tokens" };
728
+ return { kind: "stop" };
729
+ }
730
+ async function* streamResponses(options, token) {
731
+ const headers = {
732
+ Accept: "text/event-stream",
733
+ "Content-Type": "application/json",
734
+ ...buildProxyHeaders(token)
735
+ };
736
+ try {
737
+ const llm = await optionalImport("@deepseek-ai/dsh-llm");
738
+ if (typeof llm?.attributionHeaders === "function") Object.assign(headers, llm.attributionHeaders());
739
+ } catch {
740
+ }
741
+ const body = {
742
+ model: options.model,
743
+ input: responsesInput(options),
744
+ stream: true
745
+ };
746
+ if (typeof options.maxTokens === "number") body.max_output_tokens = options.maxTokens;
747
+ if (typeof options.temperature === "number") body.temperature = options.temperature;
748
+ if (Array.isArray(options.tools) && options.tools.length) {
749
+ body.tools = options.tools.map((tool) => ({
750
+ type: "function",
751
+ name: tool.name,
752
+ description: tool.description,
753
+ parameters: tool.parameters
754
+ }));
755
+ }
756
+ if (options.reasoningEffort) {
757
+ body.reasoning = { effort: options.reasoningEffort };
758
+ body.include = ["reasoning.encrypted_content"];
759
+ }
760
+ const response = await fetch(RESPONSES_URL, {
761
+ method: "POST",
762
+ headers,
763
+ body: JSON.stringify(body),
764
+ redirect: "error",
765
+ signal: options.signal
766
+ });
767
+ if (!response.ok) {
768
+ const error = new Error(`Grok Build proxy returned HTTP ${response.status}`);
769
+ error.status = response.status;
770
+ throw error;
771
+ }
772
+ if (!response.body) throw new Error("Grok Build proxy returned an empty body");
773
+ const decoder = new TextDecoder();
774
+ let buffer = "";
775
+ let textIndex;
776
+ let reasoningIndex;
777
+ let textContent = "";
778
+ let reasoningContent = "";
779
+ let nextIndex = 0;
780
+ const toolBlocks = /* @__PURE__ */ new Map();
781
+ let usage;
782
+ let finish = { kind: "stop" };
783
+ const flushSse = function* (raw) {
784
+ const lines = raw.split("\n");
785
+ let event = "message";
786
+ const data = [];
787
+ for (const line of lines) {
788
+ if (line.startsWith("event:")) event = line.slice(6).trim();
789
+ else if (line.startsWith("data:")) data.push(line.slice(5).trim());
790
+ }
791
+ const payloadText = data.join("\n");
792
+ if (!payloadText || payloadText === "[DONE]") return;
793
+ let payload;
794
+ try {
795
+ payload = JSON.parse(payloadText);
796
+ } catch {
797
+ return;
798
+ }
799
+ const type = payload.type ?? event;
800
+ if (type === "response.output_text.delta" || type === "response.text.delta") {
801
+ const delta = payload.delta ?? payload.text ?? "";
802
+ if (!delta) return;
803
+ if (textIndex === void 0) {
804
+ textIndex = nextIndex++;
805
+ yield { type: "block-start", index: textIndex, blockType: "text" };
806
+ }
807
+ textContent += delta;
808
+ yield { type: "text-delta", index: textIndex, text: String(delta) };
809
+ return;
810
+ }
811
+ if (type === "response.reasoning_text.delta" || type === "response.reasoning.delta" || type === "response.reasoning_summary_text.delta") {
812
+ const delta = payload.delta ?? payload.text ?? "";
813
+ if (!delta) return;
814
+ if (reasoningIndex === void 0) {
815
+ reasoningIndex = nextIndex++;
816
+ yield { type: "block-start", index: reasoningIndex, blockType: "reasoning" };
817
+ }
818
+ reasoningContent += delta;
819
+ yield { type: "reasoning-delta", index: reasoningIndex, text: String(delta) };
820
+ return;
821
+ }
822
+ if (type === "response.function_call_arguments.delta") {
823
+ const id = payload.item_id ?? payload.call_id ?? payload.id;
824
+ if (!id) return;
825
+ let tool = toolBlocks.get(id);
826
+ if (!tool) {
827
+ tool = { index: nextIndex++, id, name: payload.name ?? payload.item?.name, arguments: "" };
828
+ toolBlocks.set(id, tool);
829
+ yield { type: "block-start", index: tool.index, blockType: "tool-call" };
830
+ }
831
+ const delta = payload.delta ?? payload.arguments ?? "";
832
+ tool.arguments += delta;
833
+ yield { type: "tool-call-delta", index: tool.index, id, name: tool.name, argumentsDelta: String(delta) };
834
+ return;
835
+ }
836
+ if (type === "response.completed") {
837
+ const responseUsage = payload.response?.usage ?? payload.usage;
838
+ if (responseUsage) {
839
+ usage = {
840
+ inputTokens: responseUsage.input_tokens ?? responseUsage.prompt_tokens ?? 0,
841
+ outputTokens: responseUsage.output_tokens ?? responseUsage.completion_tokens ?? 0,
842
+ totalTokens: responseUsage.total_tokens
843
+ };
844
+ }
845
+ finish = mapFinish(payload.response?.status === "incomplete" ? "length" : "stop");
846
+ if (toolBlocks.size) finish = { kind: "tool-calls" };
847
+ }
848
+ if (type === "response.failed" || type === "error") {
849
+ const message = payload.error?.message ?? payload.message ?? "Grok Build stream failed";
850
+ finish = { kind: "error", failure: { message, code: "PROVIDER", status: payload.error?.status } };
851
+ }
852
+ };
853
+ for await (const chunk of response.body) {
854
+ buffer += decoder.decode(chunk, { stream: true });
855
+ let separator;
856
+ while ((separator = buffer.indexOf("\n\n")) !== -1) {
857
+ const raw = buffer.slice(0, separator);
858
+ buffer = buffer.slice(separator + 2);
859
+ yield* flushSse(raw);
860
+ }
861
+ }
862
+ if (buffer.trim()) yield* flushSse(buffer);
863
+ if (reasoningIndex !== void 0) {
864
+ yield { type: "block-end", index: reasoningIndex, block: { type: "reasoning", text: reasoningContent } };
865
+ }
866
+ if (textIndex !== void 0) {
867
+ yield { type: "block-end", index: textIndex, block: { type: "text", text: textContent } };
868
+ }
869
+ for (const tool of toolBlocks.values()) {
870
+ yield {
871
+ type: "block-end",
872
+ index: tool.index,
873
+ block: { type: "tool-call", id: tool.id, name: tool.name ?? "tool", arguments: tool.arguments }
874
+ };
875
+ }
876
+ if (usage) yield { type: "usage", usage };
877
+ yield { type: "finish", reason: finish };
878
+ }
879
+ function visiblePiModels(session) {
880
+ if (session.publicAccount()?.signedIn !== true) return [];
881
+ return toPiModels(session.models()).map((model) => model.provider === PROVIDER_ID ? model : { ...model, provider: PROVIDER_ID });
882
+ }
883
+ function createDuckAdapter(session) {
884
+ const providerInfo = () => ({ id: PROVIDER_ID, name: DISPLAY_NAME });
885
+ const list = () => {
886
+ const signedIn = session.publicAccount()?.signedIn === true;
887
+ return signedIn ? toLlmModels(session.models()) : [];
888
+ };
889
+ return {
890
+ providerInfo,
891
+ providerRetryPolicy() {
892
+ return void 0;
893
+ },
894
+ async listModels(provider) {
895
+ if (provider !== PROVIDER_ID) return [];
896
+ return list();
897
+ },
898
+ async resolveModel(provider, model) {
899
+ const found = list().find((item) => item.id === model);
900
+ return {
901
+ provider,
902
+ id: model,
903
+ name: found?.name ?? model,
904
+ inputModalities: ["text"],
905
+ context: { contextWindow: 5e5 }
906
+ };
907
+ },
908
+ async prepareCall(provider, model, signal) {
909
+ const resolved = await this.resolveModel(provider, model, signal);
910
+ return {
911
+ model: resolved,
912
+ stream: (options) => this.stream(options)
913
+ };
914
+ },
915
+ async *stream(options) {
916
+ if (options.provider !== PROVIDER_ID) {
917
+ yield { type: "finish", reason: { kind: "error", failure: { message: "Unknown provider", code: "NO_ADAPTER" } } };
918
+ return;
919
+ }
920
+ const token = await session.currentToken();
921
+ if (!token) {
922
+ yield { type: "finish", reason: { kind: "error", failure: { message: "Grok subscription is not signed in", code: "MISSING_CREDENTIAL" } } };
923
+ return;
924
+ }
925
+ try {
926
+ yield* streamResponses(options, token);
927
+ } catch (error) {
928
+ const aborted = options.signal?.aborted === true;
929
+ yield {
930
+ type: "finish",
931
+ reason: {
932
+ kind: aborted ? "aborted" : "error",
933
+ failure: {
934
+ message: error instanceof Error ? error.message : "Grok Build request failed",
935
+ code: aborted ? "ABORTED" : "PROVIDER",
936
+ status: error?.status
937
+ }
938
+ }
939
+ };
940
+ }
941
+ }
942
+ };
943
+ }
944
+ function createStore(session) {
945
+ return {
946
+ async read(providerId) {
947
+ if (providerId !== PROVIDER_ID) return void 0;
948
+ const token = await session.currentToken();
949
+ return token ? { type: "api_key", key: token } : void 0;
950
+ },
951
+ async list() {
952
+ const token = await session.currentToken();
953
+ return token ? [{ providerId: PROVIDER_ID, type: "api_key" }] : [];
954
+ },
955
+ async modify(providerId, update) {
956
+ if (providerId !== PROVIDER_ID) return void 0;
957
+ const current = await this.read(providerId);
958
+ return update(current);
959
+ },
960
+ async delete(providerId) {
961
+ if (providerId === PROVIDER_ID) await session.logout();
962
+ }
963
+ };
964
+ }
965
+ function buildAuthConfig() {
966
+ return {
967
+ apiKey: {
968
+ name: "Grok Build subscription token",
969
+ async resolve({ credential }) {
970
+ const key = credential?.type === "api_key" ? credential.key : void 0;
971
+ if (typeof key !== "string" || key.length === 0) return void 0;
972
+ return { auth: { apiKey: key, headers: fingerprintHeaders() }, source: "Grok Build subscription" };
973
+ }
974
+ }
975
+ };
976
+ }
977
+ function withEncryptedReasoningInclude(api) {
978
+ const inject2 = (options) => ({
979
+ ...options,
980
+ samplingParams: {
981
+ ...options?.samplingParams,
982
+ include: ["reasoning.encrypted_content"]
983
+ }
984
+ });
985
+ return {
986
+ stream: (model, context, options) => api.stream(model, context, inject2(options)),
987
+ streamSimple: (model, context, options) => api.streamSimple(model, context, inject2(options))
988
+ };
989
+ }
990
+ async function createGrokBuildAdapter(session) {
991
+ const [piAi, dshPi, dshLlm] = await Promise.all([
992
+ optionalImport("@earendil-works/pi-ai"),
993
+ optionalImport("@deepseek-ai/dsh-llm-pi-ai"),
994
+ optionalImport("@deepseek-ai/dsh-llm")
995
+ ]);
996
+ const duck = createDuckAdapter(session);
997
+ const asHostAdapter = (candidate) => {
998
+ if (!dshLlm?.LlmAdapter || candidate instanceof dshLlm.LlmAdapter) return candidate;
999
+ class GrokBuildAdapter extends dshLlm.LlmAdapter {
1000
+ providerInfo(provider) {
1001
+ return candidate.providerInfo(provider);
1002
+ }
1003
+ providerRetryPolicy(provider) {
1004
+ return candidate.providerRetryPolicy(provider);
1005
+ }
1006
+ listModels(provider) {
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
+ }
1018
+ }
1019
+ return new GrokBuildAdapter();
1020
+ };
1021
+ if (!piAi?.createProvider || !dshPi?.PiAiAdapter) {
1022
+ return {
1023
+ adapter: asHostAdapter(duck),
1024
+ kind: "custom-mvp",
1025
+ note: "PiAiAdapter or @earendil-works/pi-ai is not available in this host; using a documented custom Responses adapter."
1026
+ };
1027
+ }
1028
+ let responsesApi;
1029
+ try {
1030
+ const lazy = await import("@earendil-works/pi-ai/api/openai-responses.lazy");
1031
+ responsesApi = typeof lazy.openAIResponsesApi === "function" ? lazy.openAIResponsesApi() : void 0;
1032
+ } catch {
1033
+ responsesApi = void 0;
1034
+ }
1035
+ if (!responsesApi) {
1036
+ return { adapter: asHostAdapter(duck), kind: "custom-mvp", note: "openai-responses API module is unavailable; using the custom adapter." };
1037
+ }
1038
+ responsesApi = withEncryptedReasoningInclude(responsesApi);
1039
+ const store = createStore(session);
1040
+ const authModels = piAi.createModels({
1041
+ credentials: store,
1042
+ authContext: {
1043
+ env: async () => void 0,
1044
+ fileExists: async () => false
1045
+ }
1046
+ });
1047
+ const authProvider = piAi.createProvider({
1048
+ id: PROVIDER_ID,
1049
+ name: DISPLAY_NAME,
1050
+ baseUrl: PROXY_BASE_URL,
1051
+ headers: fingerprintHeaders(),
1052
+ auth: buildAuthConfig(),
1053
+ models: [],
1054
+ api: { "openai-responses": responsesApi }
1055
+ });
1056
+ authModels.setProvider(authProvider);
1057
+ const buildLiveProvider = () => {
1058
+ const base = piAi.createProvider({
1059
+ id: PROVIDER_ID,
1060
+ name: DISPLAY_NAME,
1061
+ baseUrl: PROXY_BASE_URL,
1062
+ headers: fingerprintHeaders(),
1063
+ auth: buildAuthConfig(),
1064
+ // Snapshot for createProvider internals; PiAiAdapter.listModels uses getModels().
1065
+ models: visiblePiModels(session),
1066
+ fetchModels: async (context) => {
1067
+ if (!context.allowNetwork) return visiblePiModels(session);
1068
+ const token = context.credential?.type === "api_key" ? context.credential.key : await session.currentToken();
1069
+ if (!token || session.publicAccount()?.signedIn !== true) return [];
1070
+ const catalog = await session.refreshCatalog();
1071
+ return toPiModels(catalog.models).map((model) => model.provider === PROVIDER_ID ? model : { ...model, provider: PROVIDER_ID });
1072
+ },
1073
+ api: { "openai-responses": responsesApi }
1074
+ });
1075
+ return {
1076
+ ...base,
1077
+ // Critical: listModels reads getModels(), not fetchModels / frozen models[].
1078
+ getModels: () => visiblePiModels(session)
1079
+ };
1080
+ };
1081
+ const buildProfile = () => Object.freeze({
1082
+ provider: PROVIDER_ID,
1083
+ displayName: DISPLAY_NAME,
1084
+ piProvider: buildLiveProvider(),
1085
+ configuredMaxTokens: /* @__PURE__ */ new Map(),
1086
+ modelErrors: /* @__PURE__ */ new Map(),
1087
+ streamIdleTimeoutMs: STREAM_IDLE_TIMEOUT_MS,
1088
+ maxRequestImageBytes: MAX_REQUEST_IMAGE_BYTES,
1089
+ requestImagePixelBudget: REQUEST_IMAGE_PIXEL_BUDGET,
1090
+ requestImageMaxBytes: REQUEST_IMAGE_MAX_BYTES,
1091
+ cacheRetention: "short",
1092
+ transport: "sse"
1093
+ });
1094
+ const LlmError = dshLlm?.LlmError;
1095
+ const adapter = new dshPi.PiAiAdapter({
1096
+ // Rebuild provider each call so getModels() sees post-pull session.models().
1097
+ profiles: () => /* @__PURE__ */ new Map([[PROVIDER_ID, buildProfile()]]),
1098
+ resolveApiKey: async () => {
1099
+ const token = await session.currentToken();
1100
+ if (!token) {
1101
+ if (LlmError) throw new LlmError("Grok subscription is not signed in", "MISSING_CREDENTIAL");
1102
+ throw new Error("Grok subscription is not signed in");
1103
+ }
1104
+ return token;
1105
+ },
1106
+ auth: Object.freeze({
1107
+ credentials: store,
1108
+ authContext: Object.freeze({
1109
+ env: async () => void 0,
1110
+ fileExists: async () => false
1111
+ })
1112
+ })
1113
+ });
1114
+ return {
1115
+ adapter,
1116
+ kind: "pi-ai",
1117
+ note: void 0,
1118
+ provider: buildLiveProvider(),
1119
+ refresh: () => authModels.refresh({ allowNetwork: true, force: true })
1120
+ };
1121
+ }
1122
+
1123
+ // src/index.js
1124
+ var name = CORDIS_ID;
1125
+ var inject = ["llm", "credentials", "settings", "web"];
1126
+ function apply(ctx) {
1127
+ let active = true;
1128
+ if (typeof ctx.effect === "function") {
1129
+ ctx.effect(
1130
+ () => () => {
1131
+ active = false;
1132
+ },
1133
+ "grok-subscription: startup lifetime"
1134
+ );
1135
+ }
1136
+ const notifyCatalogChange = () => {
1137
+ if (!active) return;
1138
+ try {
1139
+ ctx.emit("llm/adapters-updated");
1140
+ } catch (error) {
1141
+ ctx.logger?.warn?.("an llm/adapters-updated listener failed");
1142
+ ctx.logger?.warn?.(error);
1143
+ }
1144
+ };
1145
+ const session = createSessionService({
1146
+ credentials: ctx.credentials,
1147
+ logger: ctx.logger,
1148
+ onCatalogChange: notifyCatalogChange
1149
+ });
1150
+ const handler = createRpcHandler(session);
1151
+ void boot(ctx, session, notifyCatalogChange);
1152
+ ctx.inject(["connection"], (connectionContext) => connectionContext.effect(
1153
+ () => registerSubscriptionTransport(connectionContext.connection, handler),
1154
+ "grok-subscription: host-only account RPC"
1155
+ ));
1156
+ }
1157
+ async function boot(ctx, session, notifyCatalogChange) {
1158
+ await tryRegisterSettings(ctx);
1159
+ try {
1160
+ await session.pull();
1161
+ } catch (error) {
1162
+ ctx.logger?.debug?.("Grok subscription startup pull skipped: %s", error instanceof Error ? error.message : "unknown");
1163
+ }
1164
+ try {
1165
+ const created = await createGrokBuildAdapter(session);
1166
+ if (created.note) ctx.logger?.warn?.(created.note);
1167
+ ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
1168
+ try {
1169
+ ctx.llm.registerConfigurableProviders?.([{
1170
+ provider: PROVIDER_ID,
1171
+ displayName: DISPLAY_NAME,
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");
1177
+ }
1178
+ notifyCatalogChange?.();
1179
+ } catch (error) {
1180
+ ctx.logger?.warn?.("Grok subscription adapter failed to start: %s", error instanceof Error ? error.message : "unknown");
1181
+ }
1182
+ }
1183
+ async function tryRegisterSettings(ctx) {
1184
+ if (typeof ctx.settings?.register !== "function") return;
1185
+ try {
1186
+ const mod = await import("@deepseek-ai/schemastery");
1187
+ const z = mod.default ?? mod;
1188
+ ctx.settings.register(SETTINGS_NAMESPACE, z.object({}));
1189
+ } catch (error) {
1190
+ ctx.logger?.debug?.("Grok subscription settings namespace skipped: %s", error instanceof Error ? error.message : "unknown");
1191
+ }
1192
+ }
1193
+ export {
1194
+ DISPLAY_NAME,
1195
+ DISPLAY_NAME_ZH,
1196
+ PROVIDER_ID,
1197
+ apply,
1198
+ inject,
1199
+ name
1200
+ };