dsh-coding-subscription-oauth 0.6.5 → 0.7.1

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 (55) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/CONTRIBUTING.md +1 -1
  3. package/INSTALL.md +13 -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/adapter.d.ts.map +1 -1
  18. package/lib/auth-routes.d.ts +10 -1
  19. package/lib/auth-routes.d.ts.map +1 -1
  20. package/lib/bin.js +483 -50
  21. package/lib/bin.js.map +4 -4
  22. package/lib/client.js +5 -5
  23. package/lib/client.js.map +4 -4
  24. package/lib/compatibility.d.ts +0 -1
  25. package/lib/compatibility.d.ts.map +1 -1
  26. package/lib/gateway-protocol.d.ts +3 -3
  27. package/lib/gateway-protocol.d.ts.map +1 -1
  28. package/lib/grok-errors.d.ts +2 -2
  29. package/lib/grok-errors.d.ts.map +1 -1
  30. package/lib/http-json.d.ts +2 -2
  31. package/lib/http-json.d.ts.map +1 -1
  32. package/lib/ids.d.ts +4 -0
  33. package/lib/ids.d.ts.map +1 -1
  34. package/lib/index.d.ts +2 -1
  35. package/lib/index.d.ts.map +1 -1
  36. package/lib/index.js +679 -163
  37. package/lib/index.js.map +4 -4
  38. package/lib/kimi-errors.d.ts +2 -2
  39. package/lib/kimi-errors.d.ts.map +1 -1
  40. package/lib/oauth-session.d.ts +2 -0
  41. package/lib/oauth-session.d.ts.map +1 -1
  42. package/lib/session.d.ts +2 -0
  43. package/lib/session.d.ts.map +1 -1
  44. package/lib/store.d.ts +73 -1
  45. package/lib/store.d.ts.map +1 -1
  46. package/package.json +9 -8
  47. package/scripts/verify-adapter-host.mjs +70 -0
  48. package/lib/runtime/gateway-protocol.d.ts +0 -47
  49. package/lib/runtime/gateway-protocol.d.ts.map +0 -1
  50. package/lib/runtime/grok-errors.d.ts +0 -13
  51. package/lib/runtime/grok-errors.d.ts.map +0 -1
  52. package/lib/runtime/http-json.d.ts +0 -10
  53. package/lib/runtime/http-json.d.ts.map +0 -1
  54. package/lib/runtime/kimi-errors.d.ts +0 -13
  55. 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";
@@ -27040,6 +27130,8 @@ function profile(provider, displayName, piProvider, retryPolicy, headers) {
27040
27130
  "dsh-coding-subscription-oauth retryPolicy"
27041
27131
  ),
27042
27132
  configuredMaxTokens: /* @__PURE__ */ new Map(),
27133
+ // Host dsh-llm-pi-ai 0.1.5+ calls modelErrors.get during resolveModel (#38).
27134
+ modelErrors: /* @__PURE__ */ new Map(),
27043
27135
  ...REQUEST_IMAGE_POLICY,
27044
27136
  ...headers === void 0 ? {} : { headers },
27045
27137
  piProvider
@@ -27304,6 +27396,7 @@ async function importGrokAuth(store, filename = grokAuthPath()) {
27304
27396
  }
27305
27397
 
27306
27398
  // src/store.ts
27399
+ import { randomBytes as randomBytes2 } from "node:crypto";
27307
27400
  import { mkdir, rm } from "node:fs/promises";
27308
27401
  import { dirname, join as join3, resolve as resolve3 } from "node:path";
27309
27402
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
@@ -28061,25 +28154,17 @@ function nonEmptyString2(value) {
28061
28154
  }
28062
28155
 
28063
28156
  // 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"];
28157
+ var AUTH_FORMAT_VERSION = 2;
28158
+ var AUTH_FORMAT_VERSION_V1 = 1;
28159
+ var OAUTH_MAX_ACCOUNTS = 8;
28160
+ var MAX_ACCOUNTS = OAUTH_MAX_ACCOUNTS;
28161
+ var LEGACY_ACCOUNT_ID = "legacy";
28162
+ var ACCOUNT_ID_PATTERN = /^[A-Za-z0-9._:@+-]+$/u;
28163
+ var UNCHANGED = /* @__PURE__ */ Symbol("unchanged");
28164
+ function isValidAccountId(value) {
28165
+ return typeof value === "string" && value.length > 0 && value.length <= 128 && ACCOUNT_ID_PATTERN.test(value);
28166
+ }
28167
+ function parseOAuthCredential(raw, filename, label) {
28083
28168
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
28084
28169
  throw new Error(`${label}: ${filename} credential must be an object`);
28085
28170
  }
@@ -28088,23 +28173,178 @@ function parseDocument(text, filename, label) {
28088
28173
  if (Object.keys(credential).some((key) => !allowed.has(key))) {
28089
28174
  throw new Error(`${label}: ${filename} credential contains an unknown field`);
28090
28175
  }
28091
- if (credential["type"] !== "oauth") throw new Error(`${label}: ${filename} credential type must be oauth`);
28176
+ if (credential.type !== "oauth") throw new Error(`${label}: ${filename} credential type must be oauth`);
28092
28177
  for (const key of ["access", "refresh"]) {
28093
28178
  if (typeof credential[key] !== "string" || credential[key].length === 0) {
28094
28179
  throw new Error(`${label}: ${filename} credential ${key} must be a non-empty string`);
28095
28180
  }
28096
28181
  }
28097
- if (credential["accountId"] !== void 0 && (typeof credential["accountId"] !== "string" || credential["accountId"].length === 0)) {
28182
+ if (credential.accountId !== void 0 && (typeof credential.accountId !== "string" || credential.accountId.length === 0)) {
28098
28183
  throw new Error(`${label}: ${filename} credential accountId must be a non-empty string when present`);
28099
28184
  }
28100
- if (typeof credential["expires"] !== "number" || !Number.isFinite(credential["expires"]) || credential["expires"] <= 0) {
28185
+ if (typeof credential.expires !== "number" || !Number.isFinite(credential.expires) || credential.expires <= 0) {
28101
28186
  throw new Error(`${label}: ${filename} credential expires must be a positive finite number`);
28102
28187
  }
28103
- return { version: AUTH_FORMAT_VERSION, credential };
28188
+ return credential;
28189
+ }
28190
+ function parseAccountRecord(raw, filename, label, index) {
28191
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
28192
+ throw new Error(`${label}: ${filename} accounts[${String(index)}] must be an object`);
28193
+ }
28194
+ const record2 = raw;
28195
+ const allowed = /* @__PURE__ */ new Set(["id", "label", "credential", "createdAt"]);
28196
+ if (Object.keys(record2).some((key) => !allowed.has(key))) {
28197
+ throw new Error(`${label}: ${filename} accounts[${String(index)}] contains an unknown field`);
28198
+ }
28199
+ if (!isValidAccountId(record2.id)) {
28200
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].id is not a valid account id`);
28201
+ }
28202
+ if (record2.label !== void 0 && (typeof record2.label !== "string" || record2.label.length === 0)) {
28203
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].label must be a non-empty string when present`);
28204
+ }
28205
+ if (typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt) || record2.createdAt <= 0) {
28206
+ throw new Error(`${label}: ${filename} accounts[${String(index)}].createdAt must be a positive finite number`);
28207
+ }
28208
+ const credential = parseOAuthCredential(record2.credential, filename, label);
28209
+ return {
28210
+ id: record2.id,
28211
+ ...record2.label === void 0 ? {} : { label: record2.label },
28212
+ credential,
28213
+ createdAt: record2.createdAt
28214
+ };
28215
+ }
28216
+ function parseDocumentV2(document, filename, label) {
28217
+ if (Object.keys(document).some((key) => key !== "version" && key !== "activeAccountId" && key !== "accounts")) {
28218
+ throw new Error(`${label}: ${filename} contains an unknown top-level field`);
28219
+ }
28220
+ if (!isValidAccountId(document.activeAccountId)) {
28221
+ throw new Error(`${label}: ${filename} activeAccountId is not a valid account id`);
28222
+ }
28223
+ if (!Array.isArray(document.accounts)) {
28224
+ throw new Error(`${label}: ${filename} accounts must be an array`);
28225
+ }
28226
+ if (document.accounts.length < 1 || document.accounts.length > MAX_ACCOUNTS) {
28227
+ throw new Error(`${label}: ${filename} accounts must contain between 1 and ${String(MAX_ACCOUNTS)} entries`);
28228
+ }
28229
+ const accounts = document.accounts.map((entry, index) => parseAccountRecord(entry, filename, label, index));
28230
+ const ids = /* @__PURE__ */ new Set();
28231
+ for (const account of accounts) {
28232
+ if (ids.has(account.id)) {
28233
+ throw new Error(`${label}: ${filename} accounts contains a duplicate id`);
28234
+ }
28235
+ ids.add(account.id);
28236
+ }
28237
+ if (!ids.has(document.activeAccountId)) {
28238
+ throw new Error(`${label}: ${filename} activeAccountId does not match any account`);
28239
+ }
28240
+ return {
28241
+ version: AUTH_FORMAT_VERSION,
28242
+ activeAccountId: document.activeAccountId,
28243
+ accounts
28244
+ };
28245
+ }
28246
+ function parseDocumentV1(document, filename, label) {
28247
+ if (Object.keys(document).some((key) => key !== "version" && key !== "credential")) {
28248
+ throw new Error(`${label}: ${filename} contains an unknown top-level field`);
28249
+ }
28250
+ return parseOAuthCredential(document.credential, filename, label);
28251
+ }
28252
+ function parseDocument(text, filename, label) {
28253
+ let value;
28254
+ try {
28255
+ value = JSON.parse(text);
28256
+ } catch {
28257
+ throw new Error(`${label}: ${filename} is not valid JSON`);
28258
+ }
28259
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
28260
+ throw new Error(`${label}: ${filename} must contain an object`);
28261
+ }
28262
+ const document = value;
28263
+ if (document.version === AUTH_FORMAT_VERSION_V1) {
28264
+ return { document: migrateV1Credential(parseDocumentV1(document, filename, label)), migratedFromV1: true };
28265
+ }
28266
+ if (document.version === AUTH_FORMAT_VERSION) {
28267
+ return { document: parseDocumentV2(document, filename, label), migratedFromV1: false };
28268
+ }
28269
+ throw new Error(`${label}: ${filename} has unsupported auth format version ${String(document.version)}`);
28270
+ }
28271
+ function migrateV1Credential(credential) {
28272
+ const accountIdField = credential.accountId;
28273
+ const id = isValidAccountId(accountIdField) ? accountIdField : LEGACY_ACCOUNT_ID;
28274
+ return {
28275
+ version: AUTH_FORMAT_VERSION,
28276
+ activeAccountId: id,
28277
+ accounts: [
28278
+ {
28279
+ id,
28280
+ credential: cloneCredential2(credential),
28281
+ createdAt: Date.now()
28282
+ }
28283
+ ]
28284
+ };
28104
28285
  }
28105
28286
  function cloneCredential2(credential) {
28106
28287
  return structuredClone(credential);
28107
28288
  }
28289
+ function cloneDocument(document) {
28290
+ return structuredClone(document);
28291
+ }
28292
+ function activeCredential(document) {
28293
+ const active = document.accounts.find((account) => account.id === document.activeAccountId);
28294
+ if (active === void 0) {
28295
+ throw new Error("active account missing from document");
28296
+ }
28297
+ return cloneCredential2(active.credential);
28298
+ }
28299
+ function resolveAccountIdForCredential(credential) {
28300
+ const accountIdField = credential.accountId;
28301
+ if (isValidAccountId(accountIdField)) return accountIdField;
28302
+ return `acct-${randomBytes2(8).toString("hex")}`;
28303
+ }
28304
+ function documentFromCredential(credential) {
28305
+ const id = resolveAccountIdForCredential(credential);
28306
+ return {
28307
+ version: AUTH_FORMAT_VERSION,
28308
+ activeAccountId: id,
28309
+ accounts: [
28310
+ {
28311
+ id,
28312
+ credential: cloneCredential2(credential),
28313
+ createdAt: Date.now()
28314
+ }
28315
+ ]
28316
+ };
28317
+ }
28318
+ function withUpdatedActiveCredential(document, credential) {
28319
+ const accounts = document.accounts.map(
28320
+ (account) => account.id === document.activeAccountId ? { ...account, credential: cloneCredential2(credential) } : cloneAccount(account)
28321
+ );
28322
+ return {
28323
+ version: AUTH_FORMAT_VERSION,
28324
+ activeAccountId: document.activeAccountId,
28325
+ accounts
28326
+ };
28327
+ }
28328
+ function cloneAccount(account) {
28329
+ return {
28330
+ id: account.id,
28331
+ ...account.label === void 0 ? {} : { label: account.label },
28332
+ credential: cloneCredential2(account.credential),
28333
+ createdAt: account.createdAt
28334
+ };
28335
+ }
28336
+ function summarizeAccount(account) {
28337
+ const accountIdField = account.credential.accountId;
28338
+ return {
28339
+ id: account.id,
28340
+ ...account.label === void 0 ? {} : { label: account.label },
28341
+ expires: account.credential.expires,
28342
+ ...typeof accountIdField === "string" && accountIdField.length > 0 ? { accountId: accountIdField } : {}
28343
+ };
28344
+ }
28345
+ function validatedDocument(document, filename, label) {
28346
+ return parseDocument(JSON.stringify(document), filename, label).document;
28347
+ }
28108
28348
  function oauthCredentialPath(basename, dshHome) {
28109
28349
  if (basename.length === 0 || basename.length > 128 || basename === "." || basename === ".." || !/^\.?[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(basename)) {
28110
28350
  throw new TypeError("OAuth credential basename must be a safe local filename");
@@ -28123,6 +28363,7 @@ var OAuthCredentialFileStore = class {
28123
28363
  providerId;
28124
28364
  label;
28125
28365
  filename;
28366
+ loginPersist;
28126
28367
  async readCurrent() {
28127
28368
  return this.loadCurrent({ allowUnreadable: false });
28128
28369
  }
@@ -28132,6 +28373,10 @@ var OAuthCredentialFileStore = class {
28132
28373
  * Unsafe/symlink/wrong-owner/too-large files still throw.
28133
28374
  */
28134
28375
  async loadCurrent(options) {
28376
+ const loaded = await this.loadDocument({ allowUnreadable: options.allowUnreadable, persistMigration: false });
28377
+ return loaded === void 0 ? void 0 : activeCredential(loaded.document);
28378
+ }
28379
+ async loadDocument(options) {
28135
28380
  let text;
28136
28381
  try {
28137
28382
  text = (await readHardenedOAuthSourceFile(this.filename)).text;
@@ -28143,7 +28388,11 @@ var OAuthCredentialFileStore = class {
28143
28388
  throw error;
28144
28389
  }
28145
28390
  try {
28146
- return cloneCredential2(parseDocument(text, this.filename, this.label).credential);
28391
+ const parsed = parseDocument(text, this.filename, this.label);
28392
+ if (options.persistMigration && parsed.migratedFromV1) {
28393
+ await this.writeDocument(parsed.document);
28394
+ }
28395
+ return parsed;
28147
28396
  } catch (error) {
28148
28397
  if (options.allowUnreadable) return void 0;
28149
28398
  throw error;
@@ -28161,6 +28410,91 @@ var OAuthCredentialFileStore = class {
28161
28410
  throw error;
28162
28411
  }
28163
28412
  }
28413
+ async writeDocument(document) {
28414
+ const validated = validatedDocument(document, this.filename, this.label);
28415
+ await this.assertDestinationReplaceable();
28416
+ await writeFileAtomic(this.filename, `${JSON.stringify(validated, null, 2)}
28417
+ `, {
28418
+ mode: 384,
28419
+ dirMode: 448
28420
+ });
28421
+ return cloneDocument(validated);
28422
+ }
28423
+ nextDocumentAfterLogin(current, credential, options) {
28424
+ const parsed = parseOAuthCredential(credential, this.filename, this.label);
28425
+ if (options.mode === "overwrite-active") {
28426
+ if (current === void 0) return documentFromCredential(parsed);
28427
+ if (options.confirmOverwrite !== true) {
28428
+ throw new Error(`${this.label}: confirmOverwrite is required to replace the active account`);
28429
+ }
28430
+ return withUpdatedActiveCredential(current, parsed);
28431
+ }
28432
+ const id = resolveAccountIdForCredential(parsed);
28433
+ if (current === void 0) {
28434
+ return {
28435
+ version: AUTH_FORMAT_VERSION,
28436
+ activeAccountId: id,
28437
+ accounts: [
28438
+ {
28439
+ id,
28440
+ credential: cloneCredential2(parsed),
28441
+ createdAt: Date.now()
28442
+ }
28443
+ ]
28444
+ };
28445
+ }
28446
+ const existingIndex = current.accounts.findIndex((account) => account.id === id);
28447
+ if (existingIndex < 0 && current.accounts.length >= MAX_ACCOUNTS) {
28448
+ throw new Error(`${this.label}: at most ${String(MAX_ACCOUNTS)} accounts may be stored`);
28449
+ }
28450
+ const accounts = current.accounts.map(cloneAccount);
28451
+ if (existingIndex >= 0) {
28452
+ const existing = accounts[existingIndex];
28453
+ if (existing === void 0) {
28454
+ throw new Error(`${this.label}: account "${id}" is not stored`);
28455
+ }
28456
+ accounts[existingIndex] = {
28457
+ id,
28458
+ ...existing.label === void 0 ? {} : { label: existing.label },
28459
+ credential: cloneCredential2(parsed),
28460
+ createdAt: existing.createdAt
28461
+ };
28462
+ } else {
28463
+ accounts.push({
28464
+ id,
28465
+ credential: cloneCredential2(parsed),
28466
+ createdAt: Date.now()
28467
+ });
28468
+ }
28469
+ return {
28470
+ version: AUTH_FORMAT_VERSION,
28471
+ activeAccountId: id,
28472
+ accounts
28473
+ };
28474
+ }
28475
+ async mutateDocument(options, fn) {
28476
+ await mkdir(dirname(this.filename), { recursive: true, mode: 448 });
28477
+ return withFileLock(this.filename, async () => {
28478
+ const loaded = await this.loadDocument({
28479
+ allowUnreadable: options.allowUnreadable,
28480
+ persistMigration: false
28481
+ });
28482
+ const current = loaded === void 0 ? void 0 : loaded.document;
28483
+ const migratedFromV1 = loaded?.migratedFromV1 === true;
28484
+ const next = await fn(current === void 0 ? void 0 : cloneDocument(current));
28485
+ if (next === UNCHANGED) {
28486
+ if (migratedFromV1 && current !== void 0) {
28487
+ return this.writeDocument(current);
28488
+ }
28489
+ return current === void 0 ? void 0 : cloneDocument(current);
28490
+ }
28491
+ if (next === void 0) {
28492
+ await rm(this.filename, { force: true });
28493
+ return void 0;
28494
+ }
28495
+ return this.writeDocument(next);
28496
+ });
28497
+ }
28164
28498
  async read(providerId) {
28165
28499
  return providerId === this.providerId ? this.readCurrent() : void 0;
28166
28500
  }
@@ -28171,27 +28505,42 @@ var OAuthCredentialFileStore = class {
28171
28505
  if (providerId !== this.providerId) {
28172
28506
  throw new Error(`${this.label}: credential store does not own provider "${providerId}"`);
28173
28507
  }
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);
28508
+ const document = await this.mutateDocument({ allowUnreadable: true }, async (current) => {
28509
+ const active = current === void 0 ? void 0 : activeCredential(current);
28510
+ const candidate = await fn(active);
28511
+ if (candidate === void 0) return UNCHANGED;
28512
+ if (candidate.type !== "oauth") {
28513
+ throw new Error(`${this.label}: ${this.filename} credential type must be oauth`);
28514
+ }
28515
+ if (this.loginPersist !== void 0) {
28516
+ return this.nextDocumentAfterLogin(current, candidate, this.loginPersist);
28517
+ }
28518
+ if (current === void 0) return documentFromCredential(candidate);
28519
+ return withUpdatedActiveCredential(current, candidate);
28194
28520
  });
28521
+ return document === void 0 ? void 0 : activeCredential(document);
28522
+ }
28523
+ /**
28524
+ * Redirect `modify` writes (pi-ai `models.login`) through multi-account upsert
28525
+ * semantics for the duration of `fn`.
28526
+ */
28527
+ async runLoginPersist(options, fn) {
28528
+ if (this.loginPersist !== void 0) {
28529
+ throw new Error(`${this.label}: login persist already in progress`);
28530
+ }
28531
+ this.loginPersist = options;
28532
+ try {
28533
+ return await fn();
28534
+ } finally {
28535
+ this.loginPersist = void 0;
28536
+ }
28537
+ }
28538
+ /** Persist a login/import credential without going through pi-ai `modify`. */
28539
+ async persistLoginCredential(credential, options) {
28540
+ await this.mutateDocument(
28541
+ { allowUnreadable: true },
28542
+ async (current) => this.nextDocumentAfterLogin(current, credential, options)
28543
+ );
28195
28544
  }
28196
28545
  /**
28197
28546
  * Force the next `getAuth()` to refresh by backdating `expires` into the past.
@@ -28216,6 +28565,163 @@ var OAuthCredentialFileStore = class {
28216
28565
  await mkdir(dirname(this.filename), { recursive: true, mode: 448 });
28217
28566
  await withFileLock(this.filename, () => rm(this.filename, { force: true }));
28218
28567
  }
28568
+ /** Read-only account list for Settings / API. Never includes tokens. */
28569
+ async listAccounts() {
28570
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28571
+ if (current === void 0) return UNCHANGED;
28572
+ return UNCHANGED;
28573
+ });
28574
+ if (document === void 0) return [];
28575
+ return document.accounts.map(summarizeAccount);
28576
+ }
28577
+ async getActiveAccountId() {
28578
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28579
+ if (current === void 0) return UNCHANGED;
28580
+ return UNCHANGED;
28581
+ });
28582
+ return document?.activeAccountId;
28583
+ }
28584
+ /**
28585
+ * Read one account's credential without changing activeAccountId.
28586
+ * Used by the optional quota-aware pool proxy for sticky per-request routing.
28587
+ */
28588
+ async readAccount(id) {
28589
+ if (!isValidAccountId(id)) return void 0;
28590
+ const document = await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28591
+ if (current === void 0) return UNCHANGED;
28592
+ return UNCHANGED;
28593
+ });
28594
+ const account = document?.accounts.find((entry) => entry.id === id);
28595
+ return account === void 0 ? void 0 : cloneCredential2(account.credential);
28596
+ }
28597
+ /**
28598
+ * Serialized refresh/write for one account id. Does not move activeAccountId.
28599
+ * Returns undefined when the account is absent (caller treats as missing credential).
28600
+ */
28601
+ async modifyAccount(id, fn) {
28602
+ if (!isValidAccountId(id)) {
28603
+ throw new TypeError(`${this.label}: account id is invalid`);
28604
+ }
28605
+ const document = await this.mutateDocument({ allowUnreadable: true }, async (current) => {
28606
+ if (current === void 0) return UNCHANGED;
28607
+ const index = current.accounts.findIndex((account2) => account2.id === id);
28608
+ if (index < 0) return UNCHANGED;
28609
+ const existing = current.accounts[index];
28610
+ if (existing === void 0) return UNCHANGED;
28611
+ const candidate = await fn(cloneCredential2(existing.credential));
28612
+ if (candidate === void 0) return UNCHANGED;
28613
+ if (candidate.type !== "oauth") {
28614
+ throw new Error(`${this.label}: ${this.filename} credential type must be oauth`);
28615
+ }
28616
+ const accounts = current.accounts.map(cloneAccount);
28617
+ const target = accounts[index];
28618
+ if (target === void 0) return UNCHANGED;
28619
+ accounts[index] = {
28620
+ ...target,
28621
+ credential: cloneCredential2(candidate)
28622
+ };
28623
+ return {
28624
+ version: AUTH_FORMAT_VERSION,
28625
+ activeAccountId: current.activeAccountId,
28626
+ accounts
28627
+ };
28628
+ });
28629
+ if (document === void 0) return void 0;
28630
+ const account = document.accounts.find((entry) => entry.id === id);
28631
+ return account === void 0 ? void 0 : cloneCredential2(account.credential);
28632
+ }
28633
+ async setActiveAccount(id) {
28634
+ if (!isValidAccountId(id)) {
28635
+ throw new TypeError(`${this.label}: account id is invalid`);
28636
+ }
28637
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28638
+ if (current === void 0) {
28639
+ throw new Error(`${this.label}: no credential accounts are stored`);
28640
+ }
28641
+ if (!current.accounts.some((account) => account.id === id)) {
28642
+ throw new Error(`${this.label}: account "${id}" is not stored`);
28643
+ }
28644
+ if (current.activeAccountId === id) return UNCHANGED;
28645
+ return {
28646
+ version: AUTH_FORMAT_VERSION,
28647
+ activeAccountId: id,
28648
+ accounts: current.accounts.map(cloneAccount)
28649
+ };
28650
+ });
28651
+ }
28652
+ async upsertAccount(input) {
28653
+ if (!isValidAccountId(input.id)) {
28654
+ throw new TypeError(`${this.label}: account id is invalid`);
28655
+ }
28656
+ if (input.label !== void 0 && (typeof input.label !== "string" || input.label.length === 0)) {
28657
+ throw new TypeError(`${this.label}: account label must be a non-empty string when present`);
28658
+ }
28659
+ const credential = parseOAuthCredential(input.credential, this.filename, this.label);
28660
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28661
+ if (current === void 0) {
28662
+ return {
28663
+ version: AUTH_FORMAT_VERSION,
28664
+ activeAccountId: input.id,
28665
+ accounts: [
28666
+ {
28667
+ id: input.id,
28668
+ ...input.label === void 0 ? {} : { label: input.label },
28669
+ credential: cloneCredential2(credential),
28670
+ createdAt: Date.now()
28671
+ }
28672
+ ]
28673
+ };
28674
+ }
28675
+ const existingIndex = current.accounts.findIndex((account) => account.id === input.id);
28676
+ if (existingIndex < 0 && current.accounts.length >= MAX_ACCOUNTS) {
28677
+ throw new Error(`${this.label}: at most ${String(MAX_ACCOUNTS)} accounts may be stored`);
28678
+ }
28679
+ const accounts = current.accounts.map(cloneAccount);
28680
+ if (existingIndex >= 0) {
28681
+ const existing = accounts[existingIndex];
28682
+ if (existing === void 0) {
28683
+ throw new Error(`${this.label}: account "${input.id}" is not stored`);
28684
+ }
28685
+ accounts[existingIndex] = {
28686
+ id: input.id,
28687
+ ...input.label !== void 0 ? { label: input.label } : existing.label === void 0 ? {} : { label: existing.label },
28688
+ credential: cloneCredential2(credential),
28689
+ createdAt: existing.createdAt
28690
+ };
28691
+ } else {
28692
+ accounts.push({
28693
+ id: input.id,
28694
+ ...input.label === void 0 ? {} : { label: input.label },
28695
+ credential: cloneCredential2(credential),
28696
+ createdAt: Date.now()
28697
+ });
28698
+ }
28699
+ return {
28700
+ version: AUTH_FORMAT_VERSION,
28701
+ activeAccountId: input.makeActive === true ? input.id : current.activeAccountId,
28702
+ accounts
28703
+ };
28704
+ });
28705
+ }
28706
+ async removeAccount(id) {
28707
+ if (!isValidAccountId(id)) {
28708
+ throw new TypeError(`${this.label}: account id is invalid`);
28709
+ }
28710
+ await this.mutateDocument({ allowUnreadable: false }, async (current) => {
28711
+ if (current === void 0) return UNCHANGED;
28712
+ const accounts = current.accounts.filter((account) => account.id !== id).map(cloneAccount);
28713
+ if (accounts.length === current.accounts.length) return UNCHANGED;
28714
+ if (accounts.length === 0) return void 0;
28715
+ const fallback = accounts[0];
28716
+ if (fallback === void 0) return void 0;
28717
+ const activeAccountId = current.activeAccountId === id ? fallback.id : current.activeAccountId;
28718
+ return {
28719
+ version: AUTH_FORMAT_VERSION,
28720
+ activeAccountId,
28721
+ accounts
28722
+ };
28723
+ });
28724
+ }
28219
28725
  };
28220
28726
  var GrokBuildCredentialStore = class extends OAuthCredentialFileStore {
28221
28727
  constructor(filename = grokBuildAuthPath()) {
@@ -28248,85 +28754,9 @@ async function importGrokBuildSession(session, filename) {
28248
28754
  await session.refreshLiveCatalog();
28249
28755
  }
28250
28756
 
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
28757
  // src/oauth.ts
28328
28758
  import { Buffer as Buffer2 } from "node:buffer";
28329
- import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
28759
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "node:crypto";
28330
28760
  import { createServer } from "node:http";
28331
28761
  var GROK_BUILD_OAUTH_ISSUER = "https://auth.x.ai";
28332
28762
  var APPROVED_GROK_ISSUER_ORIGIN2 = "https://auth.x.ai";
@@ -28493,12 +28923,12 @@ async function discoverOAuthEndpoints(issuer, signal, options = {}) {
28493
28923
  return parsed;
28494
28924
  }
28495
28925
  function generatePkce() {
28496
- const verifier = randomBytes2(32).toString("base64url");
28926
+ const verifier = randomBytes3(32).toString("base64url");
28497
28927
  const challenge = createHash2("sha256").update(verifier).digest("base64url");
28498
28928
  return { verifier, challenge };
28499
28929
  }
28500
28930
  function randomToken() {
28501
- return randomBytes2(16).toString("base64url");
28931
+ return randomBytes3(16).toString("base64url");
28502
28932
  }
28503
28933
  function buildAuthorizeUrl(endpoints, params, redirectUri, challenge, state, nonce) {
28504
28934
  const url = new URL(endpoints.authorization_endpoint);
@@ -28983,6 +29413,14 @@ function waitForPromptAbort(prompt) {
28983
29413
  async function grokImportAvailable() {
28984
29414
  return (await probeGrokAuth()).available;
28985
29415
  }
29416
+ async function signedInAccountFields(store) {
29417
+ const accounts = [...await store.listAccounts()];
29418
+ const activeAccountId = await store.getActiveAccountId();
29419
+ if (activeAccountId === void 0 || accounts.length === 0) {
29420
+ throw new Error("signed-in status requires at least one stored account");
29421
+ }
29422
+ return { accounts, activeAccountId };
29423
+ }
28986
29424
  var GrokBuildWebAuth = class {
28987
29425
  constructor(session) {
28988
29426
  this.session = session;
@@ -29042,6 +29480,16 @@ var GrokBuildWebAuth = class {
29042
29480
  await this.session.setSelectedModels(ids);
29043
29481
  this.state = await this.readStoredStatus();
29044
29482
  }
29483
+ async setActiveAccount(id) {
29484
+ await this.session.store.setActiveAccount(id);
29485
+ this.session.notifyCredentialChange();
29486
+ this.state = await this.readStoredStatus();
29487
+ }
29488
+ async removeAccount(id) {
29489
+ await this.session.store.removeAccount(id);
29490
+ this.session.notifyCredentialChange();
29491
+ this.state = await this.readStoredStatus();
29492
+ }
29045
29493
  async signOut() {
29046
29494
  this.cancellation?.abort(new Error("grok-build: sign-in cancelled"));
29047
29495
  await this.operation?.catch(() => void 0);
@@ -29159,6 +29607,7 @@ var GrokBuildWebAuth = class {
29159
29607
  if (!stored.authenticated) return { status: "signed-out", grokImportAvailable: grok };
29160
29608
  const available = this.session.availableModels().map((model) => model.id);
29161
29609
  const selected = this.session.selectedModelIds();
29610
+ const accounts = await signedInAccountFields(this.session.store);
29162
29611
  return {
29163
29612
  status: "signed-in",
29164
29613
  models: this.session.visibleModels().map((model) => model.id),
@@ -29166,6 +29615,7 @@ var GrokBuildWebAuth = class {
29166
29615
  selected: selected ?? available,
29167
29616
  catalogSource: this.session.catalogSource,
29168
29617
  grokImportAvailable: grok,
29618
+ ...accounts,
29169
29619
  ...this.session.catalogError === void 0 ? {} : { catalogError: this.session.catalogError }
29170
29620
  };
29171
29621
  }
@@ -29248,6 +29698,16 @@ var SubscriptionWebAuth = class {
29248
29698
  await this.session.setSelectedModels(ids);
29249
29699
  this.state = await this.readStoredStatus();
29250
29700
  }
29701
+ async setActiveAccount(id) {
29702
+ await this.session.store.setActiveAccount(id);
29703
+ this.session.notifyCredentialChange();
29704
+ this.state = await this.readStoredStatus();
29705
+ }
29706
+ async removeAccount(id) {
29707
+ await this.session.store.removeAccount(id);
29708
+ this.session.notifyCredentialChange();
29709
+ this.state = await this.readStoredStatus();
29710
+ }
29251
29711
  async signOut() {
29252
29712
  this.cancellation?.abort(new Error(`${this.session.definition.route}: sign-in cancelled`));
29253
29713
  await this.operation?.catch(() => void 0);
@@ -29280,7 +29740,14 @@ var SubscriptionWebAuth = class {
29280
29740
  const base = this.baseStatus();
29281
29741
  try {
29282
29742
  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" };
29743
+ if (!stored.authenticated) return { ...base, status: "signed-out" };
29744
+ const accounts = await signedInAccountFields(this.session.store);
29745
+ return {
29746
+ ...base,
29747
+ status: "signed-in",
29748
+ ...accounts,
29749
+ ...stored.expiresAt === void 0 ? {} : { expiresAt: stored.expiresAt }
29750
+ };
29284
29751
  } catch (error) {
29285
29752
  return { ...base, status: "error", message: safeMessage(error) };
29286
29753
  }
@@ -29660,6 +30127,50 @@ function registerCodingOAuthRoutes(ctx, grokSession, subscriptionSessions, owner
29660
30127
  json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
29661
30128
  }
29662
30129
  }
30130
+ }),
30131
+ webServer.register({
30132
+ kind: "exact",
30133
+ path: CODING_OAUTH_ACCOUNTS_SET_ACTIVE_PATH,
30134
+ handler: async (req, res) => {
30135
+ if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
30136
+ const decision = ownerRequestPolicy.authorize(req);
30137
+ if (!decision.authorized || decision.accessMode === void 0) return json(res, 403, { error: "forbidden" });
30138
+ try {
30139
+ const body = recordBody(await readJsonRequest(req));
30140
+ const slug = providerSlug(body);
30141
+ const accountId = body["accountId"];
30142
+ if (typeof accountId !== "string" || accountId.trim().length === 0) {
30143
+ return json(res, 400, { error: "accountId must be a non-empty string" });
30144
+ }
30145
+ if (slug === "grok") await grok.setActiveAccount(accountId);
30146
+ else await subscription(slug).setActiveAccount(accountId);
30147
+ json(res, 200, await allStatus(decision.accessMode));
30148
+ } catch (error) {
30149
+ json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
30150
+ }
30151
+ }
30152
+ }),
30153
+ webServer.register({
30154
+ kind: "exact",
30155
+ path: CODING_OAUTH_ACCOUNTS_REMOVE_PATH,
30156
+ handler: async (req, res) => {
30157
+ if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
30158
+ const decision = ownerRequestPolicy.authorize(req);
30159
+ if (!decision.authorized || decision.accessMode === void 0) return json(res, 403, { error: "forbidden" });
30160
+ try {
30161
+ const body = recordBody(await readJsonRequest(req));
30162
+ const slug = providerSlug(body);
30163
+ const accountId = body["accountId"];
30164
+ if (typeof accountId !== "string" || accountId.trim().length === 0) {
30165
+ return json(res, 400, { error: "accountId must be a non-empty string" });
30166
+ }
30167
+ if (slug === "grok") await grok.removeAccount(accountId);
30168
+ else await subscription(slug).removeAccount(accountId);
30169
+ json(res, 200, await allStatus(decision.accessMode));
30170
+ } catch (error) {
30171
+ json(res, requestErrorStatus(error, 500), { error: safeMessage(error) });
30172
+ }
30173
+ }
29663
30174
  })
29664
30175
  ]);
29665
30176
  return releaseRoutes;
@@ -30596,7 +31107,7 @@ import { lookup as dnsLookup } from "node:dns/promises";
30596
31107
  import { isIP as isIP2 } from "node:net";
30597
31108
 
30598
31109
  // src/media-store.ts
30599
- import { createHash as createHash3, randomBytes as randomBytes3 } from "node:crypto";
31110
+ import { createHash as createHash3, randomBytes as randomBytes4 } from "node:crypto";
30600
31111
  import { constants as fsConstants } from "node:fs";
30601
31112
  import { chmod, link, lstat as lstat2, mkdir as mkdir2, open as open2, readdir, unlink } from "node:fs/promises";
30602
31113
  import { dirname as dirname2, isAbsolute, join as join4, relative, resolve as resolve4, sep } from "node:path";
@@ -30629,7 +31140,7 @@ function sha256Hex(data) {
30629
31140
  return createHash3("sha256").update(data).digest("hex");
30630
31141
  }
30631
31142
  function generateArtifactId() {
30632
- return randomBytes3(16).toString("hex");
31143
+ return randomBytes4(16).toString("hex");
30633
31144
  }
30634
31145
  function clampPositive(value, ceiling, label) {
30635
31146
  const raw = value ?? ceiling;
@@ -30816,7 +31327,7 @@ async function writeExclusiveFile(root, path, data) {
30816
31327
  throw collision;
30817
31328
  }
30818
31329
  const payload = typeof data === "string" ? Buffer.from(data) : data;
30819
- const temporary = join4(dirname2(target), `.tmp-${randomBytes3(8).toString("hex")}`);
31330
+ const temporary = join4(dirname2(target), `.tmp-${randomBytes4(8).toString("hex")}`);
30820
31331
  const handle = await openNoFollow(
30821
31332
  temporary,
30822
31333
  fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,
@@ -33429,7 +33940,6 @@ var DSH_EXACT_BOM = Object.freeze({
33429
33940
  "@deepseek-ai/dsh-atomic-write": "0.1.1-rc.2",
33430
33941
  "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
33431
33942
  "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
33432
- "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
33433
33943
  "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
33434
33944
  "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
33435
33945
  "@deepseek-ai/dsh-client-web": "0.1.1-rc.2",
@@ -33571,7 +34081,7 @@ function createDshHostAdapter(context) {
33571
34081
  }
33572
34082
 
33573
34083
  // src/gateway-auth.ts
33574
- import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
34084
+ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
33575
34085
  import { mkdir as mkdir3 } from "node:fs/promises";
33576
34086
  import { dirname as dirname3, join as join5, resolve as resolve5 } from "node:path";
33577
34087
  import { writeFileAtomic as writeFileAtomic3 } from "@deepseek-ai/dsh-atomic-write";
@@ -33581,7 +34091,7 @@ function gatewayKeyPath(dshHome) {
33581
34091
  return resolve5(join5(resolveDshHome2(dshHome), GATEWAY_KEY_FILENAME));
33582
34092
  }
33583
34093
  function generateGatewayApiKey() {
33584
- return randomBytes4(32).toString("base64url");
34094
+ return randomBytes5(32).toString("base64url");
33585
34095
  }
33586
34096
  function gatewayKeysEqual(left, right) {
33587
34097
  const a = Buffer.from(left);
@@ -33637,11 +34147,6 @@ async function persistGatewayKeyDocument(path, document) {
33637
34147
  `, { mode: 384 });
33638
34148
  }
33639
34149
 
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
34150
  // src/gateway-backend.ts
33646
34151
  var GatewayRequestError = class extends Error {
33647
34152
  constructor(status, code, message) {
@@ -35479,6 +35984,10 @@ var OAuthProviderSession = class {
35479
35984
  async invalidateAccessToken() {
35480
35985
  await this.store.invalidate(this.definition.nativeProviderId);
35481
35986
  }
35987
+ /** Refresh host discovery after an account switch/remove without a full logout. */
35988
+ notifyCredentialChange() {
35989
+ this.onCatalogChange?.();
35990
+ }
35482
35991
  async storedCredential() {
35483
35992
  const credential = await this.store.read(this.definition.nativeProviderId);
35484
35993
  return credential?.type === "oauth" ? credential : void 0;
@@ -35643,6 +36152,10 @@ var GrokBuildSession = class {
35643
36152
  async invalidateAccessToken() {
35644
36153
  await this.store.invalidate(XAI_PI_PROVIDER);
35645
36154
  }
36155
+ /** Refresh host discovery after an account switch/remove without a full logout. */
36156
+ notifyCredentialChange() {
36157
+ this.onCatalogChange?.();
36158
+ }
35646
36159
  async logout() {
35647
36160
  try {
35648
36161
  await this.store.delete(XAI_PI_PROVIDER);
@@ -36125,6 +36638,7 @@ export {
36125
36638
  KIMI_CODE_OAUTH_ROUTE,
36126
36639
  KIMI_PI_PROVIDER,
36127
36640
  LOOPBACK_OWNER_REQUEST_POLICY,
36641
+ OAUTH_MAX_ACCOUNTS,
36128
36642
  OAUTH_PROVIDER_DEFINITIONS,
36129
36643
  OAuthCredentialFileStore,
36130
36644
  OAuthProviderSession,
@@ -36163,6 +36677,7 @@ export {
36163
36677
  importGrokBuildFromGrok,
36164
36678
  importGrokBuildSession,
36165
36679
  inject,
36680
+ isValidAccountId,
36166
36681
  loginGrokBuild,
36167
36682
  loginGrokBuildPkce,
36168
36683
  loginGrokBuildSession,
@@ -36181,6 +36696,7 @@ export {
36181
36696
  refreshGrokBuildToken,
36182
36697
  registerCodingOAuthRoutes,
36183
36698
  registerGrokBuildAuthRoutes,
36699
+ resolveAccountIdForCredential,
36184
36700
  resolveOAuthParams,
36185
36701
  safeMessage,
36186
36702
  thinkingLevelMapFromLiveEfforts