dsh-coding-subscription-oauth 0.6.5 → 0.7.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/CONTRIBUTING.md +1 -1
  3. package/INSTALL.md +12 -8
  4. package/README.de.md +5 -5
  5. package/README.es.md +5 -5
  6. package/README.fr.md +5 -5
  7. package/README.ja.md +5 -5
  8. package/README.ko.md +5 -5
  9. package/README.md +7 -6
  10. package/README.pt-BR.md +5 -5
  11. package/README.ru.md +5 -5
  12. package/README.zh-CN.md +7 -6
  13. package/compatibility/dsh-bom.json +8 -2
  14. package/docs/00-project-rules.md +3 -2
  15. package/docs/02-architecture.md +5 -5
  16. package/docs/02-architecture.zh-CN.md +5 -5
  17. package/lib/auth-routes.d.ts +10 -1
  18. package/lib/auth-routes.d.ts.map +1 -1
  19. package/lib/bin.js +483 -50
  20. package/lib/bin.js.map +4 -4
  21. package/lib/client.js +5 -5
  22. package/lib/client.js.map +4 -4
  23. package/lib/compatibility.d.ts +0 -1
  24. package/lib/compatibility.d.ts.map +1 -1
  25. package/lib/gateway-protocol.d.ts +3 -3
  26. package/lib/gateway-protocol.d.ts.map +1 -1
  27. package/lib/grok-errors.d.ts +2 -2
  28. package/lib/grok-errors.d.ts.map +1 -1
  29. package/lib/http-json.d.ts +2 -2
  30. package/lib/http-json.d.ts.map +1 -1
  31. package/lib/ids.d.ts +4 -0
  32. package/lib/ids.d.ts.map +1 -1
  33. package/lib/index.d.ts +2 -1
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +677 -163
  36. package/lib/index.js.map +4 -4
  37. package/lib/kimi-errors.d.ts +2 -2
  38. package/lib/kimi-errors.d.ts.map +1 -1
  39. package/lib/oauth-session.d.ts +2 -0
  40. package/lib/oauth-session.d.ts.map +1 -1
  41. package/lib/session.d.ts +2 -0
  42. package/lib/session.d.ts.map +1 -1
  43. package/lib/store.d.ts +73 -1
  44. package/lib/store.d.ts.map +1 -1
  45. package/package.json +6 -8
  46. package/lib/runtime/gateway-protocol.d.ts +0 -47
  47. package/lib/runtime/gateway-protocol.d.ts.map +0 -1
  48. package/lib/runtime/grok-errors.d.ts +0 -13
  49. package/lib/runtime/grok-errors.d.ts.map +0 -1
  50. package/lib/runtime/http-json.d.ts +0 -10
  51. package/lib/runtime/http-json.d.ts.map +0 -1
  52. package/lib/runtime/kimi-errors.d.ts +0 -13
  53. package/lib/runtime/kimi-errors.d.ts.map +0 -1
package/lib/index.js CHANGED
@@ -25609,7 +25609,7 @@ import { dirname as dirname6, join as join8 } from "node:path";
25609
25609
  import { assertUsableApiKey, RetryPolicySchema } from "@deepseek-ai/dsh-llm";
25610
25610
  import z2 from "@deepseek-ai/schemastery";
25611
25611
 
25612
- // node_modules/.pnpm/dsh-coding-oauth-core@0.1.1/node_modules/dsh-coding-oauth-core/lib/proxy.js
25612
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/proxy.js
25613
25613
  var import_undici = __toESM(require_undici(), 1);
25614
25614
  var DEFAULT_PROXIED_HOSTS = [
25615
25615
  "auth.x.ai",
@@ -25744,7 +25744,7 @@ function grokBuildProxyInEffect() {
25744
25744
  return codingOAuthProxyInEffect();
25745
25745
  }
25746
25746
 
25747
- // node_modules/.pnpm/dsh-coding-oauth-core@0.1.1/node_modules/dsh-coding-oauth-core/lib/route-registration.js
25747
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/route-registration.js
25748
25748
  function releaseRegistrationsSynchronously(disposers) {
25749
25749
  for (const dispose of [...disposers].reverse()) {
25750
25750
  try {
@@ -25780,7 +25780,7 @@ function setupAtomicallySynchronously(registry, setup) {
25780
25780
  return onceSynchronously(disposers);
25781
25781
  }
25782
25782
 
25783
- // node_modules/.pnpm/dsh-coding-oauth-core@0.1.1/node_modules/dsh-coding-oauth-core/lib/ids.js
25783
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/ids.js
25784
25784
  var XAI_PI_PROVIDER = "xai";
25785
25785
  var CODEX_PI_PROVIDER = "openai-codex";
25786
25786
  var KIMI_PI_PROVIDER = "kimi-coding";
@@ -25808,7 +25808,7 @@ var CLAUDE_CODE_OAUTH_MODELS_CACHE_FILENAME = ".claude-code-oauth-models.json";
25808
25808
  var DEFAULT_GROK_BUILD_MODEL = "grok-4.6";
25809
25809
  var GROK_BUILD_STREAM_IDLE_TIMEOUT_MS = 3e5;
25810
25810
 
25811
- // node_modules/.pnpm/dsh-coding-oauth-core@0.1.1/node_modules/dsh-coding-oauth-core/lib/state-contract.js
25811
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/state-contract.js
25812
25812
  var CAPABILITY_SETTINGS_NAMESPACE = "coding-subscription-oauth";
25813
25813
  var GATEWAY_KEY_FILENAME = ".coding-oauth-gateway.json";
25814
25814
  var IMAGINE_MEDIA_STORE_DIRNAME = ".dsh-coding-subscription-oauth-media";
@@ -25861,7 +25861,115 @@ var CODING_OAUTH_MANAGEMENT_PATHS = Object.freeze([
25861
25861
  GATEWAY_ROTATE_PATH
25862
25862
  ]);
25863
25863
 
25864
- // node_modules/.pnpm/dsh-coding-oauth-core@0.1.1/node_modules/dsh-coding-oauth-core/lib/index.js
25864
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/http-json.js
25865
+ var JSON_BODY_LIMIT_BYTES = 64 * 1024;
25866
+ var JsonRequestError = class extends Error {
25867
+ statusCode;
25868
+ constructor(statusCode, message) {
25869
+ super(message);
25870
+ this.name = "JsonRequestError";
25871
+ this.statusCode = statusCode;
25872
+ }
25873
+ };
25874
+ function declaredLength(request) {
25875
+ const value = request.headers["content-length"];
25876
+ if (typeof value !== "string" || !/^\d+$/u.test(value))
25877
+ return void 0;
25878
+ const length = Number(value);
25879
+ return Number.isSafeInteger(length) ? length : void 0;
25880
+ }
25881
+ function readJsonRequest(request, limit = JSON_BODY_LIMIT_BYTES) {
25882
+ const length = declaredLength(request);
25883
+ if (length !== void 0 && length > limit) {
25884
+ request.resume();
25885
+ return Promise.reject(new JsonRequestError(413, "request body is too large"));
25886
+ }
25887
+ return new Promise((resolve8, reject) => {
25888
+ const chunks = [];
25889
+ let size = 0;
25890
+ let settled = false;
25891
+ const cleanup = () => {
25892
+ request.removeListener("data", onData);
25893
+ request.removeListener("end", onEnd);
25894
+ request.removeListener("error", onError);
25895
+ request.removeListener("aborted", onAborted);
25896
+ };
25897
+ const fail = (error) => {
25898
+ if (settled)
25899
+ return;
25900
+ settled = true;
25901
+ cleanup();
25902
+ request.resume();
25903
+ reject(error);
25904
+ };
25905
+ const onData = (value) => {
25906
+ if (settled)
25907
+ return;
25908
+ const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
25909
+ size += chunk.byteLength;
25910
+ if (size > limit) {
25911
+ fail(new JsonRequestError(413, "request body is too large"));
25912
+ return;
25913
+ }
25914
+ chunks.push(chunk);
25915
+ };
25916
+ const onEnd = () => {
25917
+ if (settled)
25918
+ return;
25919
+ settled = true;
25920
+ cleanup();
25921
+ const text = Buffer.concat(chunks, size).toString("utf8").trim();
25922
+ if (text.length === 0) {
25923
+ resolve8({});
25924
+ return;
25925
+ }
25926
+ try {
25927
+ resolve8(JSON.parse(text));
25928
+ } catch {
25929
+ reject(new JsonRequestError(400, "request body must contain valid JSON"));
25930
+ }
25931
+ };
25932
+ const onError = () => fail(new JsonRequestError(400, "request body could not be read"));
25933
+ const onAborted = () => fail(new JsonRequestError(400, "request body was aborted"));
25934
+ request.on("data", onData);
25935
+ request.once("end", onEnd);
25936
+ request.once("error", onError);
25937
+ request.once("aborted", onAborted);
25938
+ });
25939
+ }
25940
+ function requestErrorStatus(error, fallback) {
25941
+ return error instanceof JsonRequestError ? error.statusCode : fallback;
25942
+ }
25943
+
25944
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/grok-errors.js
25945
+ var XAI_CAPACITY = /\b(?:at\s+capacity|high\s+demand|priority\s+processing|overloaded)\b/i;
25946
+ function isXaiCapacityError(detail) {
25947
+ return XAI_CAPACITY.test(detail);
25948
+ }
25949
+ function remapXaiCapacityFailure(failure) {
25950
+ if (!isXaiCapacityError(failure.message))
25951
+ return failure;
25952
+ return { ...failure, code: "RATE_LIMIT" };
25953
+ }
25954
+
25955
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/kimi-errors.js
25956
+ import { CONTEXT_WINDOW_EXCEEDED_CODE, isContextWindowExceededError } from "@deepseek-ai/dsh-llm";
25957
+ var KIMI_CONTEXT_CAPACITY = /\bsupports only\s+\d+\s*k\s+context\b/i;
25958
+ function isMisclassifiedContextWindowError(detail) {
25959
+ return isContextWindowExceededError(detail) || KIMI_CONTEXT_CAPACITY.test(detail);
25960
+ }
25961
+ function remapAuthFailureIfContextOverflow(failure) {
25962
+ if (failure.code !== "AUTH" || !isMisclassifiedContextWindowError(failure.message))
25963
+ return failure;
25964
+ return { ...failure, code: CONTEXT_WINDOW_EXCEEDED_CODE };
25965
+ }
25966
+
25967
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/gateway-protocol.js
25968
+ function isThinkingLevel(value) {
25969
+ return value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max";
25970
+ }
25971
+
25972
+ // node_modules/.pnpm/dsh-coding-oauth-core@0.1.2_@deepseek-ai+dsh-llm@0.1.1-rc.2_@deepseek-ai+cordis@4.0.1_@_82bbdc1424db474a5d3f648945c0fadb/node_modules/dsh-coding-oauth-core/lib/index.js
25865
25973
  var CODING_OAUTH_CORE_ABI = "dsh-coding-oauth-core/v1";
25866
25974
  var STORE_SYMBOL = /* @__PURE__ */ Symbol.for("dsh.coding-oauth-core.runtime-store");
25867
25975
  function resolveCodingOAuthScope(context) {
@@ -26068,29 +26176,6 @@ import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
26068
26176
 
26069
26177
  // src/alias-adapter.ts
26070
26178
  import { LlmAdapter, LlmError } from "@deepseek-ai/dsh-llm";
26071
-
26072
- // src/runtime/grok-errors.ts
26073
- var XAI_CAPACITY = /\b(?:at\s+capacity|high\s+demand|priority\s+processing|overloaded)\b/i;
26074
- function isXaiCapacityError(detail) {
26075
- return XAI_CAPACITY.test(detail);
26076
- }
26077
- function remapXaiCapacityFailure(failure) {
26078
- if (!isXaiCapacityError(failure.message)) return failure;
26079
- return { ...failure, code: "RATE_LIMIT" };
26080
- }
26081
-
26082
- // src/runtime/kimi-errors.ts
26083
- import { CONTEXT_WINDOW_EXCEEDED_CODE, isContextWindowExceededError } from "@deepseek-ai/dsh-llm";
26084
- var KIMI_CONTEXT_CAPACITY = /\bsupports only\s+\d+\s*k\s+context\b/i;
26085
- function isMisclassifiedContextWindowError(detail) {
26086
- return isContextWindowExceededError(detail) || KIMI_CONTEXT_CAPACITY.test(detail);
26087
- }
26088
- function remapAuthFailureIfContextOverflow(failure) {
26089
- if (failure.code !== "AUTH" || !isMisclassifiedContextWindowError(failure.message)) return failure;
26090
- return { ...failure, code: CONTEXT_WINDOW_EXCEEDED_CODE };
26091
- }
26092
-
26093
- // src/alias-adapter.ts
26094
26179
  function normalizeReplayForRoute(message, route2, nativeReplayProvider) {
26095
26180
  if (message.role !== "assistant" || message.source.kind !== "model") return message;
26096
26181
  if (message.source.provider !== route2) {
@@ -26174,6 +26259,11 @@ var AliasLlmAdapter = class extends LlmAdapter {
26174
26259
  }
26175
26260
  };
26176
26261
 
26262
+ // src/ids.ts
26263
+ var CODING_OAUTH_API_BASE = "/plugins/dsh-grok-build";
26264
+ var CODING_OAUTH_ACCOUNTS_SET_ACTIVE_PATH = `${CODING_OAUTH_API_BASE}/oauth/accounts/set-active`;
26265
+ var CODING_OAUTH_ACCOUNTS_REMOVE_PATH = `${CODING_OAUTH_API_BASE}/oauth/accounts/remove`;
26266
+
26177
26267
  // src/provider.ts
26178
26268
  import { createProvider } from "@earendil-works/pi-ai";
26179
26269
  import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
@@ -27304,6 +27394,7 @@ async function importGrokAuth(store, filename = grokAuthPath()) {
27304
27394
  }
27305
27395
 
27306
27396
  // src/store.ts
27397
+ import { randomBytes as randomBytes2 } from "node:crypto";
27307
27398
  import { mkdir, rm } from "node:fs/promises";
27308
27399
  import { dirname, join as join3, resolve as resolve3 } from "node:path";
27309
27400
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
@@ -28061,25 +28152,17 @@ function nonEmptyString2(value) {
28061
28152
  }
28062
28153
 
28063
28154
  // src/store.ts
28064
- var AUTH_FORMAT_VERSION = 1;
28065
- function parseDocument(text, filename, label) {
28066
- let value;
28067
- try {
28068
- value = JSON.parse(text);
28069
- } catch {
28070
- throw new Error(`${label}: ${filename} is not valid JSON`);
28071
- }
28072
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
28073
- throw new Error(`${label}: ${filename} must contain an object`);
28074
- }
28075
- const document = value;
28076
- if (document["version"] !== AUTH_FORMAT_VERSION) {
28077
- throw new Error(`${label}: ${filename} has unsupported auth format version ${String(document["version"])}`);
28078
- }
28079
- if (Object.keys(document).some((key) => key !== "version" && key !== "credential")) {
28080
- throw new Error(`${label}: ${filename} contains an unknown top-level field`);
28081
- }
28082
- const raw = document["credential"];
28155
+ var AUTH_FORMAT_VERSION = 2;
28156
+ var AUTH_FORMAT_VERSION_V1 = 1;
28157
+ var OAUTH_MAX_ACCOUNTS = 8;
28158
+ var MAX_ACCOUNTS = OAUTH_MAX_ACCOUNTS;
28159
+ var LEGACY_ACCOUNT_ID = "legacy";
28160
+ var ACCOUNT_ID_PATTERN = /^[A-Za-z0-9._:@+-]+$/u;
28161
+ var UNCHANGED = /* @__PURE__ */ Symbol("unchanged");
28162
+ function isValidAccountId(value) {
28163
+ return typeof value === "string" && value.length > 0 && value.length <= 128 && ACCOUNT_ID_PATTERN.test(value);
28164
+ }
28165
+ function parseOAuthCredential(raw, filename, label) {
28083
28166
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
28084
28167
  throw new Error(`${label}: ${filename} credential must be an object`);
28085
28168
  }
@@ -28088,23 +28171,178 @@ function parseDocument(text, filename, label) {
28088
28171
  if (Object.keys(credential).some((key) => !allowed.has(key))) {
28089
28172
  throw new Error(`${label}: ${filename} credential contains an unknown field`);
28090
28173
  }
28091
- if (credential["type"] !== "oauth") throw new Error(`${label}: ${filename} credential type must be oauth`);
28174
+ if (credential.type !== "oauth") throw new Error(`${label}: ${filename} credential type must be oauth`);
28092
28175
  for (const key of ["access", "refresh"]) {
28093
28176
  if (typeof credential[key] !== "string" || credential[key].length === 0) {
28094
28177
  throw new Error(`${label}: ${filename} credential ${key} must be a non-empty string`);
28095
28178
  }
28096
28179
  }
28097
- if (credential["accountId"] !== void 0 && (typeof credential["accountId"] !== "string" || credential["accountId"].length === 0)) {
28180
+ if (credential.accountId !== void 0 && (typeof credential.accountId !== "string" || credential.accountId.length === 0)) {
28098
28181
  throw new Error(`${label}: ${filename} credential accountId must be a non-empty string when present`);
28099
28182
  }
28100
- if (typeof credential["expires"] !== "number" || !Number.isFinite(credential["expires"]) || credential["expires"] <= 0) {
28183
+ if (typeof credential.expires !== "number" || !Number.isFinite(credential.expires) || credential.expires <= 0) {
28101
28184
  throw new Error(`${label}: ${filename} credential expires must be a positive finite number`);
28102
28185
  }
28103
- return { version: AUTH_FORMAT_VERSION, credential };
28186
+ return credential;
28187
+ }
28188
+ function parseAccountRecord(raw, filename, label, index) {
28189
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
28190
+ throw new Error(`${label}: ${filename} accounts[${String(index)}] must be an object`);
28191
+ }
28192
+ const record2 = raw;
28193
+ const allowed = /* @__PURE__ */ new Set(["id", "label", "credential", "createdAt"]);
28194
+ if (Object.keys(record2).some((key) => !allowed.has(key))) {
28195
+ throw new Error(`${label}: ${filename} accounts[${String(index)}] contains an unknown field`);
28196
+ }
28197
+ if (!isValidAccountId(record2.id)) {
28198
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].id is not a valid account id`);
28199
+ }
28200
+ if (record2.label !== void 0 && (typeof record2.label !== "string" || record2.label.length === 0)) {
28201
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].label must be a non-empty string when present`);
28202
+ }
28203
+ if (typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt) || record2.createdAt <= 0) {
28204
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].createdAt must be a positive finite number`);
28205
+ }
28206
+ const credential = parseOAuthCredential(record2.credential, filename, label);
28207
+ return {
28208
+ id: record2.id,
28209
+ ...record2.label === void 0 ? {} : { label: record2.label },
28210
+ credential,
28211
+ createdAt: record2.createdAt
28212
+ };
28213
+ }
28214
+ function parseDocumentV2(document, filename, label) {
28215
+ if (Object.keys(document).some((key) => key !== "version" && key !== "activeAccountId" && key !== "accounts")) {
28216
+ throw new Error(`${label}: ${filename} contains an unknown top-level field`);
28217
+ }
28218
+ if (!isValidAccountId(document.activeAccountId)) {
28219
+ throw new Error(`${label}: ${filename} activeAccountId is not a valid account id`);
28220
+ }
28221
+ if (!Array.isArray(document.accounts)) {
28222
+ throw new Error(`${label}: ${filename} accounts must be an array`);
28223
+ }
28224
+ if (document.accounts.length < 1 || document.accounts.length > MAX_ACCOUNTS) {
28225
+ throw new Error(`${label}: ${filename} accounts must contain between 1 and ${String(MAX_ACCOUNTS)} entries`);
28226
+ }
28227
+ const accounts = document.accounts.map((entry, index) => parseAccountRecord(entry, filename, label, index));
28228
+ const ids = /* @__PURE__ */ new Set();
28229
+ for (const account of accounts) {
28230
+ if (ids.has(account.id)) {
28231
+ throw new Error(`${label}: ${filename} accounts contains a duplicate id`);
28232
+ }
28233
+ ids.add(account.id);
28234
+ }
28235
+ if (!ids.has(document.activeAccountId)) {
28236
+ throw new Error(`${label}: ${filename} activeAccountId does not match any account`);
28237
+ }
28238
+ return {
28239
+ version: AUTH_FORMAT_VERSION,
28240
+ activeAccountId: document.activeAccountId,
28241
+ accounts
28242
+ };
28243
+ }
28244
+ function parseDocumentV1(document, filename, label) {
28245
+ if (Object.keys(document).some((key) => key !== "version" && key !== "credential")) {
28246
+ throw new Error(`${label}: ${filename} contains an unknown top-level field`);
28247
+ }
28248
+ return parseOAuthCredential(document.credential, filename, label);
28249
+ }
28250
+ function parseDocument(text, filename, label) {
28251
+ let value;
28252
+ try {
28253
+ value = JSON.parse(text);
28254
+ } catch {
28255
+ throw new Error(`${label}: ${filename} is not valid JSON`);
28256
+ }
28257
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
28258
+ throw new Error(`${label}: ${filename} must contain an object`);
28259
+ }
28260
+ const document = value;
28261
+ if (document.version === AUTH_FORMAT_VERSION_V1) {
28262
+ return { document: migrateV1Credential(parseDocumentV1(document, filename, label)), migratedFromV1: true };
28263
+ }
28264
+ if (document.version === AUTH_FORMAT_VERSION) {
28265
+ return { document: parseDocumentV2(document, filename, label), migratedFromV1: false };
28266
+ }
28267
+ throw new Error(`${label}: ${filename} has unsupported auth format version ${String(document.version)}`);
28268
+ }
28269
+ function migrateV1Credential(credential) {
28270
+ const accountIdField = credential.accountId;
28271
+ const id = isValidAccountId(accountIdField) ? accountIdField : LEGACY_ACCOUNT_ID;
28272
+ return {
28273
+ version: AUTH_FORMAT_VERSION,
28274
+ activeAccountId: id,
28275
+ accounts: [
28276
+ {
28277
+ id,
28278
+ credential: cloneCredential2(credential),
28279
+ createdAt: Date.now()
28280
+ }
28281
+ ]
28282
+ };
28104
28283
  }
28105
28284
  function cloneCredential2(credential) {
28106
28285
  return structuredClone(credential);
28107
28286
  }
28287
+ function cloneDocument(document) {
28288
+ return structuredClone(document);
28289
+ }
28290
+ function activeCredential(document) {
28291
+ const active = document.accounts.find((account) => account.id === document.activeAccountId);
28292
+ if (active === void 0) {
28293
+ throw new Error("active account missing from document");
28294
+ }
28295
+ return cloneCredential2(active.credential);
28296
+ }
28297
+ function resolveAccountIdForCredential(credential) {
28298
+ const accountIdField = credential.accountId;
28299
+ if (isValidAccountId(accountIdField)) return accountIdField;
28300
+ return `acct-${randomBytes2(8).toString("hex")}`;
28301
+ }
28302
+ function documentFromCredential(credential) {
28303
+ const id = resolveAccountIdForCredential(credential);
28304
+ return {
28305
+ version: AUTH_FORMAT_VERSION,
28306
+ activeAccountId: id,
28307
+ accounts: [
28308
+ {
28309
+ id,
28310
+ credential: cloneCredential2(credential),
28311
+ createdAt: Date.now()
28312
+ }
28313
+ ]
28314
+ };
28315
+ }
28316
+ function withUpdatedActiveCredential(document, credential) {
28317
+ const accounts = document.accounts.map(
28318
+ (account) => account.id === document.activeAccountId ? { ...account, credential: cloneCredential2(credential) } : cloneAccount(account)
28319
+ );
28320
+ return {
28321
+ version: AUTH_FORMAT_VERSION,
28322
+ activeAccountId: document.activeAccountId,
28323
+ accounts
28324
+ };
28325
+ }
28326
+ function cloneAccount(account) {
28327
+ return {
28328
+ id: account.id,
28329
+ ...account.label === void 0 ? {} : { label: account.label },
28330
+ credential: cloneCredential2(account.credential),
28331
+ createdAt: account.createdAt
28332
+ };
28333
+ }
28334
+ function summarizeAccount(account) {
28335
+ const accountIdField = account.credential.accountId;
28336
+ return {
28337
+ id: account.id,
28338
+ ...account.label === void 0 ? {} : { label: account.label },
28339
+ expires: account.credential.expires,
28340
+ ...typeof accountIdField === "string" && accountIdField.length > 0 ? { accountId: accountIdField } : {}
28341
+ };
28342
+ }
28343
+ function validatedDocument(document, filename, label) {
28344
+ return parseDocument(JSON.stringify(document), filename, label).document;
28345
+ }
28108
28346
  function oauthCredentialPath(basename, dshHome) {
28109
28347
  if (basename.length === 0 || basename.length > 128 || basename === "." || basename === ".." || !/^\.?[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(basename)) {
28110
28348
  throw new TypeError("OAuth credential basename must be a safe local filename");
@@ -28123,6 +28361,7 @@ var OAuthCredentialFileStore = class {
28123
28361
  providerId;
28124
28362
  label;
28125
28363
  filename;
28364
+ loginPersist;
28126
28365
  async readCurrent() {
28127
28366
  return this.loadCurrent({ allowUnreadable: false });
28128
28367
  }
@@ -28132,6 +28371,10 @@ var OAuthCredentialFileStore = class {
28132
28371
  * Unsafe/symlink/wrong-owner/too-large files still throw.
28133
28372
  */
28134
28373
  async loadCurrent(options) {
28374
+ const loaded = await this.loadDocument({ allowUnreadable: options.allowUnreadable, persistMigration: false });
28375
+ return loaded === void 0 ? void 0 : activeCredential(loaded.document);
28376
+ }
28377
+ async loadDocument(options) {
28135
28378
  let text;
28136
28379
  try {
28137
28380
  text = (await readHardenedOAuthSourceFile(this.filename)).text;
@@ -28143,7 +28386,11 @@ var OAuthCredentialFileStore = class {
28143
28386
  throw error;
28144
28387
  }
28145
28388
  try {
28146
- return cloneCredential2(parseDocument(text, this.filename, this.label).credential);
28389
+ const parsed = parseDocument(text, this.filename, this.label);
28390
+ if (options.persistMigration && parsed.migratedFromV1) {
28391
+ await this.writeDocument(parsed.document);
28392
+ }
28393
+ return parsed;
28147
28394
  } catch (error) {
28148
28395
  if (options.allowUnreadable) return void 0;
28149
28396
  throw error;
@@ -28161,6 +28408,91 @@ var OAuthCredentialFileStore = class {
28161
28408
  throw error;
28162
28409
  }
28163
28410
  }
28411
+ async writeDocument(document) {
28412
+ const validated = validatedDocument(document, this.filename, this.label);
28413
+ await this.assertDestinationReplaceable();
28414
+ await writeFileAtomic(this.filename, `${JSON.stringify(validated, null, 2)}
28415
+ `, {
28416
+ mode: 384,
28417
+ dirMode: 448
28418
+ });
28419
+ return cloneDocument(validated);
28420
+ }
28421
+ nextDocumentAfterLogin(current, credential, options) {
28422
+ const parsed = parseOAuthCredential(credential, this.filename, this.label);
28423
+ if (options.mode === "overwrite-active") {
28424
+ if (current === void 0) return documentFromCredential(parsed);
28425
+ if (options.confirmOverwrite !== true) {
28426
+ throw new Error(`${this.label}: confirmOverwrite is required to replace the active account`);
28427
+ }
28428
+ return withUpdatedActiveCredential(current, parsed);
28429
+ }
28430
+ const id = resolveAccountIdForCredential(parsed);
28431
+ if (current === void 0) {
28432
+ return {
28433
+ version: AUTH_FORMAT_VERSION,
28434
+ activeAccountId: id,
28435
+ accounts: [
28436
+ {
28437
+ id,
28438
+ credential: cloneCredential2(parsed),
28439
+ createdAt: Date.now()
28440
+ }
28441
+ ]
28442
+ };
28443
+ }
28444
+ const existingIndex = current.accounts.findIndex((account) => account.id === id);
28445
+ if (existingIndex < 0 && current.accounts.length >= MAX_ACCOUNTS) {
28446
+ throw new Error(`${this.label}: at most ${String(MAX_ACCOUNTS)} accounts may be stored`);
28447
+ }
28448
+ const accounts = current.accounts.map(cloneAccount);
28449
+ if (existingIndex >= 0) {
28450
+ const existing = accounts[existingIndex];
28451
+ if (existing === void 0) {
28452
+ throw new Error(`${this.label}: account "${id}" is not stored`);
28453
+ }
28454
+ accounts[existingIndex] = {
28455
+ id,
28456
+ ...existing.label === void 0 ? {} : { label: existing.label },
28457
+ credential: cloneCredential2(parsed),
28458
+ createdAt: existing.createdAt
28459
+ };
28460
+ } else {
28461
+ accounts.push({
28462
+ id,
28463
+ credential: cloneCredential2(parsed),
28464
+ createdAt: Date.now()
28465
+ });
28466
+ }
28467
+ return {
28468
+ version: AUTH_FORMAT_VERSION,
28469
+ activeAccountId: id,
28470
+ accounts
28471
+ };
28472
+ }
28473
+ async mutateDocument(options, fn) {
28474
+ await mkdir(dirname(this.filename), { recursive: true, mode: 448 });
28475
+ return withFileLock(this.filename, async () => {
28476
+ const loaded = await this.loadDocument({
28477
+ allowUnreadable: options.allowUnreadable,
28478
+ persistMigration: false
28479
+ });
28480
+ const current = loaded === void 0 ? void 0 : loaded.document;
28481
+ const migratedFromV1 = loaded?.migratedFromV1 === true;
28482
+ const next = await fn(current === void 0 ? void 0 : cloneDocument(current));
28483
+ if (next === UNCHANGED) {
28484
+ if (migratedFromV1 && current !== void 0) {
28485
+ return this.writeDocument(current);
28486
+ }
28487
+ return current === void 0 ? void 0 : cloneDocument(current);
28488
+ }
28489
+ if (next === void 0) {
28490
+ await rm(this.filename, { force: true });
28491
+ return void 0;
28492
+ }
28493
+ return this.writeDocument(next);
28494
+ });
28495
+ }
28164
28496
  async read(providerId) {
28165
28497
  return providerId === this.providerId ? this.readCurrent() : void 0;
28166
28498
  }
@@ -28171,27 +28503,42 @@ var OAuthCredentialFileStore = class {
28171
28503
  if (providerId !== this.providerId) {
28172
28504
  throw new Error(`${this.label}: credential store does not own provider "${providerId}"`);
28173
28505
  }
28174
- await mkdir(dirname(this.filename), { recursive: true, mode: 448 });
28175
- return withFileLock(this.filename, async () => {
28176
- const current = await this.loadCurrent({ allowUnreadable: true });
28177
- const candidate = await fn(current);
28178
- if (candidate === void 0) return current;
28179
- const document = parseDocument(
28180
- JSON.stringify({
28181
- version: AUTH_FORMAT_VERSION,
28182
- credential: candidate
28183
- }),
28184
- this.filename,
28185
- this.label
28186
- );
28187
- await this.assertDestinationReplaceable();
28188
- await writeFileAtomic(this.filename, `${JSON.stringify(document, null, 2)}
28189
- `, {
28190
- mode: 384,
28191
- dirMode: 448
28192
- });
28193
- return cloneCredential2(document.credential);
28506
+ const document = await this.mutateDocument({ allowUnreadable: true }, async (current) => {
28507
+ const active = current === void 0 ? void 0 : activeCredential(current);
28508
+ const candidate = await fn(active);
28509
+ if (candidate === void 0) return UNCHANGED;
28510
+ if (candidate.type !== "oauth") {
28511
+ throw new Error(`${this.label}: ${this.filename} credential type must be oauth`);
28512
+ }
28513
+ if (this.loginPersist !== void 0) {
28514
+ return this.nextDocumentAfterLogin(current, candidate, this.loginPersist);
28515
+ }
28516
+ if (current === void 0) return documentFromCredential(candidate);
28517
+ return withUpdatedActiveCredential(current, candidate);
28194
28518
  });
28519
+ return document === void 0 ? void 0 : activeCredential(document);
28520
+ }
28521
+ /**
28522
+ * Redirect `modify` writes (pi-ai `models.login`) through multi-account upsert
28523
+ * semantics for the duration of `fn`.
28524
+ */
28525
+ async runLoginPersist(options, fn) {
28526
+ if (this.loginPersist !== void 0) {
28527
+ throw new Error(`${this.label}: login persist already in progress`);
28528
+ }
28529
+ this.loginPersist = options;
28530
+ try {
28531
+ return await fn();
28532
+ } finally {
28533
+ this.loginPersist = void 0;
28534
+ }
28535
+ }
28536
+ /** Persist a login/import credential without going through pi-ai `modify`. */
28537
+ async persistLoginCredential(credential, options) {
28538
+ await this.mutateDocument(
28539
+ { allowUnreadable: true },
28540
+ async (current) => this.nextDocumentAfterLogin(current, credential, options)
28541
+ );
28195
28542
  }
28196
28543
  /**
28197
28544
  * Force the next `getAuth()` to refresh by backdating `expires` into the past.
@@ -28216,6 +28563,163 @@ var OAuthCredentialFileStore = class {
28216
28563
  await mkdir(dirname(this.filename), { recursive: true, mode: 448 });
28217
28564
  await withFileLock(this.filename, () => rm(this.filename, { force: true }));
28218
28565
  }
28566
+ /** Read-only account list for Settings / API. Never includes tokens. */
28567
+ async listAccounts() {
28568
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28569
+ if (current === void 0) return UNCHANGED;
28570
+ return UNCHANGED;
28571
+ });
28572
+ if (document === void 0) return [];
28573
+ return document.accounts.map(summarizeAccount);
28574
+ }
28575
+ async getActiveAccountId() {
28576
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28577
+ if (current === void 0) return UNCHANGED;
28578
+ return UNCHANGED;
28579
+ });
28580
+ return document?.activeAccountId;
28581
+ }
28582
+ /**
28583
+ * Read one account's credential without changing activeAccountId.
28584
+ * Used by the optional quota-aware pool proxy for sticky per-request routing.
28585
+ */
28586
+ async readAccount(id) {
28587
+ if (!isValidAccountId(id)) return void 0;
28588
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28589
+ if (current === void 0) return UNCHANGED;
28590
+ return UNCHANGED;
28591
+ });
28592
+ const account = document?.accounts.find((entry) => entry.id === id);
28593
+ return account === void 0 ? void 0 : cloneCredential2(account.credential);
28594
+ }
28595
+ /**
28596
+ * Serialized refresh/write for one account id. Does not move activeAccountId.
28597
+ * Returns undefined when the account is absent (caller treats as missing credential).
28598
+ */
28599
+ async modifyAccount(id, fn) {
28600
+ if (!isValidAccountId(id)) {
28601
+ throw new TypeError(`${this.label}: account id is invalid`);
28602
+ }
28603
+ const document = await this.mutateDocument({ allowUnreadable: true }, async (current) => {
28604
+ if (current === void 0) return UNCHANGED;
28605
+ const index = current.accounts.findIndex((account2) => account2.id === id);
28606
+ if (index < 0) return UNCHANGED;
28607
+ const existing = current.accounts[index];
28608
+ if (existing === void 0) return UNCHANGED;
28609
+ const candidate = await fn(cloneCredential2(existing.credential));
28610
+ if (candidate === void 0) return UNCHANGED;
28611
+ if (candidate.type !== "oauth") {
28612
+ throw new Error(`${this.label}: ${this.filename} credential type must be oauth`);
28613
+ }
28614
+ const accounts = current.accounts.map(cloneAccount);
28615
+ const target = accounts[index];
28616
+ if (target === void 0) return UNCHANGED;
28617
+ accounts[index] = {
28618
+ ...target,
28619
+ credential: cloneCredential2(candidate)
28620
+ };
28621
+ return {
28622
+ version: AUTH_FORMAT_VERSION,
28623
+ activeAccountId: current.activeAccountId,
28624
+ accounts
28625
+ };
28626
+ });
28627
+ if (document === void 0) return void 0;
28628
+ const account = document.accounts.find((entry) => entry.id === id);
28629
+ return account === void 0 ? void 0 : cloneCredential2(account.credential);
28630
+ }
28631
+ async setActiveAccount(id) {
28632
+ if (!isValidAccountId(id)) {
28633
+ throw new TypeError(`${this.label}: account id is invalid`);
28634
+ }
28635
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28636
+ if (current === void 0) {
28637
+ throw new Error(`${this.label}: no credential accounts are stored`);
28638
+ }
28639
+ if (!current.accounts.some((account) => account.id === id)) {
28640
+ throw new Error(`${this.label}: account "${id}" is not stored`);
28641
+ }
28642
+ if (current.activeAccountId === id) return UNCHANGED;
28643
+ return {
28644
+ version: AUTH_FORMAT_VERSION,
28645
+ activeAccountId: id,
28646
+ accounts: current.accounts.map(cloneAccount)
28647
+ };
28648
+ });
28649
+ }
28650
+ async upsertAccount(input) {
28651
+ if (!isValidAccountId(input.id)) {
28652
+ throw new TypeError(`${this.label}: account id is invalid`);
28653
+ }
28654
+ if (input.label !== void 0 && (typeof input.label !== "string" || input.label.length === 0)) {
28655
+ throw new TypeError(`${this.label}: account label must be a non-empty string when present`);
28656
+ }
28657
+ const credential = parseOAuthCredential(input.credential, this.filename, this.label);
28658
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28659
+ if (current === void 0) {
28660
+ return {
28661
+ version: AUTH_FORMAT_VERSION,
28662
+ activeAccountId: input.id,
28663
+ accounts: [
28664
+ {
28665
+ id: input.id,
28666
+ ...input.label === void 0 ? {} : { label: input.label },
28667
+ credential: cloneCredential2(credential),
28668
+ createdAt: Date.now()
28669
+ }
28670
+ ]
28671
+ };
28672
+ }
28673
+ const existingIndex = current.accounts.findIndex((account) => account.id === input.id);
28674
+ if (existingIndex < 0 && current.accounts.length >= MAX_ACCOUNTS) {
28675
+ throw new Error(`${this.label}: at most ${String(MAX_ACCOUNTS)} accounts may be stored`);
28676
+ }
28677
+ const accounts = current.accounts.map(cloneAccount);
28678
+ if (existingIndex >= 0) {
28679
+ const existing = accounts[existingIndex];
28680
+ if (existing === void 0) {
28681
+ throw new Error(`${this.label}: account "${input.id}" is not stored`);
28682
+ }
28683
+ accounts[existingIndex] = {
28684
+ id: input.id,
28685
+ ...input.label !== void 0 ? { label: input.label } : existing.label === void 0 ? {} : { label: existing.label },
28686
+ credential: cloneCredential2(credential),
28687
+ createdAt: existing.createdAt
28688
+ };
28689
+ } else {
28690
+ accounts.push({
28691
+ id: input.id,
28692
+ ...input.label === void 0 ? {} : { label: input.label },
28693
+ credential: cloneCredential2(credential),
28694
+ createdAt: Date.now()
28695
+ });
28696
+ }
28697
+ return {
28698
+ version: AUTH_FORMAT_VERSION,
28699
+ activeAccountId: input.makeActive === true ? input.id : current.activeAccountId,
28700
+ accounts
28701
+ };
28702
+ });
28703
+ }
28704
+ async removeAccount(id) {
28705
+ if (!isValidAccountId(id)) {
28706
+ throw new TypeError(`${this.label}: account id is invalid`);
28707
+ }
28708
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28709
+ if (current === void 0) return UNCHANGED;
28710
+ const accounts = current.accounts.filter((account) => account.id !== id).map(cloneAccount);
28711
+ if (accounts.length === current.accounts.length) return UNCHANGED;
28712
+ if (accounts.length === 0) return void 0;
28713
+ const fallback = accounts[0];
28714
+ if (fallback === void 0) return void 0;
28715
+ const activeAccountId = current.activeAccountId === id ? fallback.id : current.activeAccountId;
28716
+ return {
28717
+ version: AUTH_FORMAT_VERSION,
28718
+ activeAccountId,
28719
+ accounts
28720
+ };
28721
+ });
28722
+ }
28219
28723
  };
28220
28724
  var GrokBuildCredentialStore = class extends OAuthCredentialFileStore {
28221
28725
  constructor(filename = grokBuildAuthPath()) {
@@ -28248,85 +28752,9 @@ async function importGrokBuildSession(session, filename) {
28248
28752
  await session.refreshLiveCatalog();
28249
28753
  }
28250
28754
 
28251
- // src/runtime/http-json.ts
28252
- var JSON_BODY_LIMIT_BYTES = 64 * 1024;
28253
- var JsonRequestError = class extends Error {
28254
- statusCode;
28255
- constructor(statusCode, message) {
28256
- super(message);
28257
- this.name = "JsonRequestError";
28258
- this.statusCode = statusCode;
28259
- }
28260
- };
28261
- function declaredLength(request) {
28262
- const value = request.headers["content-length"];
28263
- if (typeof value !== "string" || !/^\d+$/u.test(value)) return void 0;
28264
- const length = Number(value);
28265
- return Number.isSafeInteger(length) ? length : void 0;
28266
- }
28267
- function readJsonRequest(request, limit = JSON_BODY_LIMIT_BYTES) {
28268
- const length = declaredLength(request);
28269
- if (length !== void 0 && length > limit) {
28270
- request.resume();
28271
- return Promise.reject(new JsonRequestError(413, "request body is too large"));
28272
- }
28273
- return new Promise((resolve8, reject) => {
28274
- const chunks = [];
28275
- let size = 0;
28276
- let settled = false;
28277
- const cleanup = () => {
28278
- request.removeListener("data", onData);
28279
- request.removeListener("end", onEnd);
28280
- request.removeListener("error", onError);
28281
- request.removeListener("aborted", onAborted);
28282
- };
28283
- const fail = (error) => {
28284
- if (settled) return;
28285
- settled = true;
28286
- cleanup();
28287
- request.resume();
28288
- reject(error);
28289
- };
28290
- const onData = (value) => {
28291
- if (settled) return;
28292
- const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
28293
- size += chunk.byteLength;
28294
- if (size > limit) {
28295
- fail(new JsonRequestError(413, "request body is too large"));
28296
- return;
28297
- }
28298
- chunks.push(chunk);
28299
- };
28300
- const onEnd = () => {
28301
- if (settled) return;
28302
- settled = true;
28303
- cleanup();
28304
- const text = Buffer.concat(chunks, size).toString("utf8").trim();
28305
- if (text.length === 0) {
28306
- resolve8({});
28307
- return;
28308
- }
28309
- try {
28310
- resolve8(JSON.parse(text));
28311
- } catch {
28312
- reject(new JsonRequestError(400, "request body must contain valid JSON"));
28313
- }
28314
- };
28315
- const onError = () => fail(new JsonRequestError(400, "request body could not be read"));
28316
- const onAborted = () => fail(new JsonRequestError(400, "request body was aborted"));
28317
- request.on("data", onData);
28318
- request.once("end", onEnd);
28319
- request.once("error", onError);
28320
- request.once("aborted", onAborted);
28321
- });
28322
- }
28323
- function requestErrorStatus(error, fallback) {
28324
- return error instanceof JsonRequestError ? error.statusCode : fallback;
28325
- }
28326
-
28327
28755
  // src/oauth.ts
28328
28756
  import { Buffer as Buffer2 } from "node:buffer";
28329
- import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
28757
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "node:crypto";
28330
28758
  import { createServer } from "node:http";
28331
28759
  var GROK_BUILD_OAUTH_ISSUER = "https://auth.x.ai";
28332
28760
  var APPROVED_GROK_ISSUER_ORIGIN2 = "https://auth.x.ai";
@@ -28493,12 +28921,12 @@ async function discoverOAuthEndpoints(issuer, signal, options = {}) {
28493
28921
  return parsed;
28494
28922
  }
28495
28923
  function generatePkce() {
28496
- const verifier = randomBytes2(32).toString("base64url");
28924
+ const verifier = randomBytes3(32).toString("base64url");
28497
28925
  const challenge = createHash2("sha256").update(verifier).digest("base64url");
28498
28926
  return { verifier, challenge };
28499
28927
  }
28500
28928
  function randomToken() {
28501
- return randomBytes2(16).toString("base64url");
28929
+ return randomBytes3(16).toString("base64url");
28502
28930
  }
28503
28931
  function buildAuthorizeUrl(endpoints, params, redirectUri, challenge, state, nonce) {
28504
28932
  const url = new URL(endpoints.authorization_endpoint);
@@ -28983,6 +29411,14 @@ function waitForPromptAbort(prompt) {
28983
29411
  async function grokImportAvailable() {
28984
29412
  return (await probeGrokAuth()).available;
28985
29413
  }
29414
+ async function signedInAccountFields(store) {
29415
+ const accounts = [...await store.listAccounts()];
29416
+ const activeAccountId = await store.getActiveAccountId();
29417
+ if (activeAccountId === void 0 || accounts.length === 0) {
29418
+ throw new Error("signed-in status requires at least one stored account");
29419
+ }
29420
+ return { accounts, activeAccountId };
29421
+ }
28986
29422
  var GrokBuildWebAuth = class {
28987
29423
  constructor(session) {
28988
29424
  this.session = session;
@@ -29042,6 +29478,16 @@ var GrokBuildWebAuth = class {
29042
29478
  await this.session.setSelectedModels(ids);
29043
29479
  this.state = await this.readStoredStatus();
29044
29480
  }
29481
+ async setActiveAccount(id) {
29482
+ await this.session.store.setActiveAccount(id);
29483
+ this.session.notifyCredentialChange();
29484
+ this.state = await this.readStoredStatus();
29485
+ }
29486
+ async removeAccount(id) {
29487
+ await this.session.store.removeAccount(id);
29488
+ this.session.notifyCredentialChange();
29489
+ this.state = await this.readStoredStatus();
29490
+ }
29045
29491
  async signOut() {
29046
29492
  this.cancellation?.abort(new Error("grok-build: sign-in cancelled"));
29047
29493
  await this.operation?.catch(() => void 0);
@@ -29159,6 +29605,7 @@ var GrokBuildWebAuth = class {
29159
29605
  if (!stored.authenticated) return { status: "signed-out", grokImportAvailable: grok };
29160
29606
  const available = this.session.availableModels().map((model) => model.id);
29161
29607
  const selected = this.session.selectedModelIds();
29608
+ const accounts = await signedInAccountFields(this.session.store);
29162
29609
  return {
29163
29610
  status: "signed-in",
29164
29611
  models: this.session.visibleModels().map((model) => model.id),
@@ -29166,6 +29613,7 @@ var GrokBuildWebAuth = class {
29166
29613
  selected: selected ?? available,
29167
29614
  catalogSource: this.session.catalogSource,
29168
29615
  grokImportAvailable: grok,
29616
+ ...accounts,
29169
29617
  ...this.session.catalogError === void 0 ? {} : { catalogError: this.session.catalogError }
29170
29618
  };
29171
29619
  }
@@ -29248,6 +29696,16 @@ var SubscriptionWebAuth = class {
29248
29696
  await this.session.setSelectedModels(ids);
29249
29697
  this.state = await this.readStoredStatus();
29250
29698
  }
29699
+ async setActiveAccount(id) {
29700
+ await this.session.store.setActiveAccount(id);
29701
+ this.session.notifyCredentialChange();
29702
+ this.state = await this.readStoredStatus();
29703
+ }
29704
+ async removeAccount(id) {
29705
+ await this.session.store.removeAccount(id);
29706
+ this.session.notifyCredentialChange();
29707
+ this.state = await this.readStoredStatus();
29708
+ }
29251
29709
  async signOut() {
29252
29710
  this.cancellation?.abort(new Error(`${this.session.definition.route}: sign-in cancelled`));
29253
29711
  await this.operation?.catch(() => void 0);
@@ -29280,7 +29738,14 @@ var SubscriptionWebAuth = class {
29280
29738
  const base = this.baseStatus();
29281
29739
  try {
29282
29740
  const stored = await this.session.status();
29283
- return stored.authenticated ? { ...base, status: "signed-in", ...stored.expiresAt === void 0 ? {} : { expiresAt: stored.expiresAt } } : { ...base, status: "signed-out" };
29741
+ if (!stored.authenticated) return { ...base, status: "signed-out" };
29742
+ const accounts = await signedInAccountFields(this.session.store);
29743
+ return {
29744
+ ...base,
29745
+ status: "signed-in",
29746
+ ...accounts,
29747
+ ...stored.expiresAt === void 0 ? {} : { expiresAt: stored.expiresAt }
29748
+ };
29284
29749
  } catch (error) {
29285
29750
  return { ...base, status: "error", message: safeMessage(error) };
29286
29751
  }
@@ -29660,6 +30125,50 @@ function registerCodingOAuthRoutes(ctx, grokSession, subscriptionSessions, owner
29660
30125
  json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
29661
30126
  }
29662
30127
  }
30128
+ }),
30129
+ webServer.register({
30130
+ kind: "exact",
30131
+ path: CODING_OAUTH_ACCOUNTS_SET_ACTIVE_PATH,
30132
+ handler: async (req, res) => {
30133
+ if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
30134
+ const decision = ownerRequestPolicy.authorize(req);
30135
+ if (!decision.authorized || decision.accessMode === void 0) return json(res, 403, { error: "forbidden" });
30136
+ try {
30137
+ const body = recordBody(await readJsonRequest(req));
30138
+ const slug = providerSlug(body);
30139
+ const accountId = body["accountId"];
30140
+ if (typeof accountId !== "string" || accountId.trim().length === 0) {
30141
+ return json(res, 400, { error: "accountId must be a non-empty string" });
30142
+ }
30143
+ if (slug === "grok") await grok.setActiveAccount(accountId);
30144
+ else await subscription(slug).setActiveAccount(accountId);
30145
+ json(res, 200, await allStatus(decision.accessMode));
30146
+ } catch (error) {
30147
+ json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
30148
+ }
30149
+ }
30150
+ }),
30151
+ webServer.register({
30152
+ kind: "exact",
30153
+ path: CODING_OAUTH_ACCOUNTS_REMOVE_PATH,
30154
+ handler: async (req, res) => {
30155
+ if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
30156
+ const decision = ownerRequestPolicy.authorize(req);
30157
+ if (!decision.authorized || decision.accessMode === void 0) return json(res, 403, { error: "forbidden" });
30158
+ try {
30159
+ const body = recordBody(await readJsonRequest(req));
30160
+ const slug = providerSlug(body);
30161
+ const accountId = body["accountId"];
30162
+ if (typeof accountId !== "string" || accountId.trim().length === 0) {
30163
+ return json(res, 400, { error: "accountId must be a non-empty string" });
30164
+ }
30165
+ if (slug === "grok") await grok.removeAccount(accountId);
30166
+ else await subscription(slug).removeAccount(accountId);
30167
+ json(res, 200, await allStatus(decision.accessMode));
30168
+ } catch (error) {
30169
+ json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
30170
+ }
30171
+ }
29663
30172
  })
29664
30173
  ]);
29665
30174
  return releaseRoutes;
@@ -30596,7 +31105,7 @@ import { lookup as dnsLookup } from "node:dns/promises";
30596
31105
  import { isIP as isIP2 } from "node:net";
30597
31106
 
30598
31107
  // src/media-store.ts
30599
- import { createHash as createHash3, randomBytes as randomBytes3 } from "node:crypto";
31108
+ import { createHash as createHash3, randomBytes as randomBytes4 } from "node:crypto";
30600
31109
  import { constants as fsConstants } from "node:fs";
30601
31110
  import { chmod, link, lstat as lstat2, mkdir as mkdir2, open as open2, readdir, unlink } from "node:fs/promises";
30602
31111
  import { dirname as dirname2, isAbsolute, join as join4, relative, resolve as resolve4, sep } from "node:path";
@@ -30629,7 +31138,7 @@ function sha256Hex(data) {
30629
31138
  return createHash3("sha256").update(data).digest("hex");
30630
31139
  }
30631
31140
  function generateArtifactId() {
30632
- return randomBytes3(16).toString("hex");
31141
+ return randomBytes4(16).toString("hex");
30633
31142
  }
30634
31143
  function clampPositive(value, ceiling, label) {
30635
31144
  const raw = value ?? ceiling;
@@ -30816,7 +31325,7 @@ async function writeExclusiveFile(root, path, data) {
30816
31325
  throw collision;
30817
31326
  }
30818
31327
  const payload = typeof data === "string" ? Buffer.from(data) : data;
30819
- const temporary = join4(dirname2(target), `.tmp-${randomBytes3(8).toString("hex")}`);
31328
+ const temporary = join4(dirname2(target), `.tmp-${randomBytes4(8).toString("hex")}`);
30820
31329
  const handle = await openNoFollow(
30821
31330
  temporary,
30822
31331
  fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,
@@ -33429,7 +33938,6 @@ var DSH_EXACT_BOM = Object.freeze({
33429
33938
  "@deepseek-ai/dsh-atomic-write": "0.1.1-rc.2",
33430
33939
  "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
33431
33940
  "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
33432
- "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
33433
33941
  "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
33434
33942
  "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
33435
33943
  "@deepseek-ai/dsh-client-web": "0.1.1-rc.2",
@@ -33571,7 +34079,7 @@ function createDshHostAdapter(context) {
33571
34079
  }
33572
34080
 
33573
34081
  // src/gateway-auth.ts
33574
- import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
34082
+ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
33575
34083
  import { mkdir as mkdir3 } from "node:fs/promises";
33576
34084
  import { dirname as dirname3, join as join5, resolve as resolve5 } from "node:path";
33577
34085
  import { writeFileAtomic as writeFileAtomic3 } from "@deepseek-ai/dsh-atomic-write";
@@ -33581,7 +34089,7 @@ function gatewayKeyPath(dshHome) {
33581
34089
  return resolve5(join5(resolveDshHome2(dshHome), GATEWAY_KEY_FILENAME));
33582
34090
  }
33583
34091
  function generateGatewayApiKey() {
33584
- return randomBytes4(32).toString("base64url");
34092
+ return randomBytes5(32).toString("base64url");
33585
34093
  }
33586
34094
  function gatewayKeysEqual(left, right) {
33587
34095
  const a = Buffer.from(left);
@@ -33637,11 +34145,6 @@ async function persistGatewayKeyDocument(path, document) {
33637
34145
  `, { mode: 384 });
33638
34146
  }
33639
34147
 
33640
- // src/runtime/gateway-protocol.ts
33641
- function isThinkingLevel(value) {
33642
- return value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max";
33643
- }
33644
-
33645
34148
  // src/gateway-backend.ts
33646
34149
  var GatewayRequestError = class extends Error {
33647
34150
  constructor(status, code, message) {
@@ -35479,6 +35982,10 @@ var OAuthProviderSession = class {
35479
35982
  async invalidateAccessToken() {
35480
35983
  await this.store.invalidate(this.definition.nativeProviderId);
35481
35984
  }
35985
+ /** Refresh host discovery after an account switch/remove without a full logout. */
35986
+ notifyCredentialChange() {
35987
+ this.onCatalogChange?.();
35988
+ }
35482
35989
  async storedCredential() {
35483
35990
  const credential = await this.store.read(this.definition.nativeProviderId);
35484
35991
  return credential?.type === "oauth" ? credential : void 0;
@@ -35643,6 +36150,10 @@ var GrokBuildSession = class {
35643
36150
  async invalidateAccessToken() {
35644
36151
  await this.store.invalidate(XAI_PI_PROVIDER);
35645
36152
  }
36153
+ /** Refresh host discovery after an account switch/remove without a full logout. */
36154
+ notifyCredentialChange() {
36155
+ this.onCatalogChange?.();
36156
+ }
35646
36157
  async logout() {
35647
36158
  try {
35648
36159
  await this.store.delete(XAI_PI_PROVIDER);
@@ -36125,6 +36636,7 @@ export {
36125
36636
  KIMI_CODE_OAUTH_ROUTE,
36126
36637
  KIMI_PI_PROVIDER,
36127
36638
  LOOPBACK_OWNER_REQUEST_POLICY,
36639
+ OAUTH_MAX_ACCOUNTS,
36128
36640
  OAUTH_PROVIDER_DEFINITIONS,
36129
36641
  OAuthCredentialFileStore,
36130
36642
  OAuthProviderSession,
@@ -36163,6 +36675,7 @@ export {
36163
36675
  importGrokBuildFromGrok,
36164
36676
  importGrokBuildSession,
36165
36677
  inject,
36678
+ isValidAccountId,
36166
36679
  loginGrokBuild,
36167
36680
  loginGrokBuildPkce,
36168
36681
  loginGrokBuildSession,
@@ -36181,6 +36694,7 @@ export {
36181
36694
  refreshGrokBuildToken,
36182
36695
  registerCodingOAuthRoutes,
36183
36696
  registerGrokBuildAuthRoutes,
36697
+ resolveAccountIdForCredential,
36184
36698
  resolveOAuthParams,
36185
36699
  safeMessage,
36186
36700
  thinkingLevelMapFromLiveEfforts