dsh-coding-subscription-oauth 0.6.4 → 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 (151) hide show
  1. package/CHANGELOG.md +291 -255
  2. package/CONTRIBUTING.md +138 -129
  3. package/INSTALL.md +261 -256
  4. package/LICENSE +19 -19
  5. package/NOTICE +11 -11
  6. package/README.de.md +303 -303
  7. package/README.es.md +304 -304
  8. package/README.fr.md +304 -304
  9. package/README.ja.md +304 -304
  10. package/README.ko.md +304 -304
  11. package/README.md +321 -320
  12. package/README.pt-BR.md +304 -304
  13. package/README.ru.md +304 -304
  14. package/README.zh-CN.md +319 -318
  15. package/compatibility/dsh-bom.json +36 -30
  16. package/cordis.patch.yml +13 -13
  17. package/docs/00-project-rules.md +213 -212
  18. package/docs/02-architecture.md +142 -138
  19. package/docs/02-architecture.zh-CN.md +142 -138
  20. package/lib/auth-routes.d.ts +10 -1
  21. package/lib/auth-routes.d.ts.map +1 -1
  22. package/lib/bin.js +483 -50
  23. package/lib/bin.js.map +4 -4
  24. package/lib/client.js +5 -5
  25. package/lib/client.js.map +4 -4
  26. package/lib/compatibility.d.ts +0 -1
  27. package/lib/compatibility.d.ts.map +1 -1
  28. package/lib/gateway-protocol.d.ts +3 -46
  29. package/lib/gateway-protocol.d.ts.map +1 -1
  30. package/lib/gateway-routes.d.ts.map +1 -1
  31. package/lib/grok-errors.d.ts +2 -12
  32. package/lib/grok-errors.d.ts.map +1 -1
  33. package/lib/grok-imagine/client.d.ts +73 -0
  34. package/lib/grok-imagine/client.d.ts.map +1 -0
  35. package/lib/grok-imagine/index.d.ts +8 -0
  36. package/lib/grok-imagine/index.d.ts.map +1 -0
  37. package/lib/grok-imagine/net.d.ts +10 -0
  38. package/lib/grok-imagine/net.d.ts.map +1 -0
  39. package/lib/grok-imagine/parse.d.ts +20 -0
  40. package/lib/grok-imagine/parse.d.ts.map +1 -0
  41. package/lib/grok-imagine/types.d.ts +191 -0
  42. package/lib/grok-imagine/types.d.ts.map +1 -0
  43. package/lib/grok-imagine.d.ts +3 -263
  44. package/lib/grok-imagine.d.ts.map +1 -1
  45. package/lib/http-json.d.ts +2 -9
  46. package/lib/http-json.d.ts.map +1 -1
  47. package/lib/ids.d.ts +4 -0
  48. package/lib/ids.d.ts.map +1 -1
  49. package/lib/index.d.ts +2 -1
  50. package/lib/index.d.ts.map +1 -1
  51. package/lib/index.js +730 -196
  52. package/lib/index.js.map +4 -4
  53. package/lib/invariant.js.map +1 -1
  54. package/lib/kimi-errors.d.ts +2 -12
  55. package/lib/kimi-errors.d.ts.map +1 -1
  56. package/lib/oauth-session.d.ts +2 -0
  57. package/lib/oauth-session.d.ts.map +1 -1
  58. package/lib/session.d.ts +2 -0
  59. package/lib/session.d.ts.map +1 -1
  60. package/lib/store.d.ts +73 -1
  61. package/lib/store.d.ts.map +1 -1
  62. package/media/en/settings_accounts.png +0 -0
  63. package/media/en/settings_capabilities.png +0 -0
  64. package/media/en/settings_gateway.png +0 -0
  65. package/media/settings_accounts.png +0 -0
  66. package/media/settings_capabilities.png +0 -0
  67. package/media/settings_gateway.png +0 -0
  68. package/media/settings_overview.png +0 -0
  69. package/media/zh-CN/settings_accounts.png +0 -0
  70. package/media/zh-CN/settings_capabilities.png +0 -0
  71. package/media/zh-CN/settings_gateway.png +0 -0
  72. package/package.json +224 -221
  73. package/patches/dsh-agy@0.1.2.patch +25 -25
  74. package/scripts/release.mjs +187 -186
  75. package/scripts/smoke-deployed-routes.mjs +146 -146
  76. package/scripts/verify-deployed-catalog.mjs +87 -87
  77. package/src/adapter.ts +0 -348
  78. package/src/alias-adapter.ts +0 -147
  79. package/src/auth-routes.ts +0 -921
  80. package/src/auth.ts +0 -67
  81. package/src/bin.ts +0 -350
  82. package/src/capability-routes.ts +0 -279
  83. package/src/capability-runtime.ts +0 -314
  84. package/src/capability-settings.ts +0 -671
  85. package/src/capability-tools.ts +0 -685
  86. package/src/catalog.ts +0 -271
  87. package/src/client/GrokBuildSettings.tsx +0 -771
  88. package/src/client/api.ts +0 -88
  89. package/src/client/components/AboutTab.tsx +0 -30
  90. package/src/client/components/AccountsTab.tsx +0 -241
  91. package/src/client/components/Badge.tsx +0 -33
  92. package/src/client/components/CapabilitiesTab.tsx +0 -265
  93. package/src/client/components/CliPullPreview.tsx +0 -116
  94. package/src/client/components/CopyButton.tsx +0 -57
  95. package/src/client/components/GatewayTab.tsx +0 -469
  96. package/src/client/components/NoticeBanner.tsx +0 -46
  97. package/src/client/components/ProgressBar.tsx +0 -53
  98. package/src/client/components/ProviderCard.tsx +0 -606
  99. package/src/client/components/SettingsTabs.tsx +0 -75
  100. package/src/client/components/ToggleSwitch.tsx +0 -71
  101. package/src/client/constants.ts +0 -230
  102. package/src/client/display.ts +0 -61
  103. package/src/client/dshClientAdapter.ts +0 -127
  104. package/src/client/gatewaySnippets.ts +0 -37
  105. package/src/client/index.tsx +0 -156
  106. package/src/client/locales.ts +0 -540
  107. package/src/client/microStyles.ts +0 -52
  108. package/src/client/parsers.ts +0 -398
  109. package/src/client/styles.ts +0 -325
  110. package/src/client/types.ts +0 -199
  111. package/src/codex-http.ts +0 -447
  112. package/src/codex-images.ts +0 -503
  113. package/src/codex-model-capabilities.ts +0 -320
  114. package/src/codex-search.ts +0 -245
  115. package/src/codex-usage.ts +0 -263
  116. package/src/compatibility.ts +0 -55
  117. package/src/dsh-host-adapter.ts +0 -173
  118. package/src/gateway-anthropic-messages.ts +0 -84
  119. package/src/gateway-auth.ts +0 -102
  120. package/src/gateway-backend.ts +0 -274
  121. package/src/gateway-body.ts +0 -49
  122. package/src/gateway-config.ts +0 -76
  123. package/src/gateway-http.ts +0 -104
  124. package/src/gateway-openai-chat.ts +0 -124
  125. package/src/gateway-openai-responses.ts +0 -53
  126. package/src/gateway-parse.ts +0 -224
  127. package/src/gateway-protocol.ts +0 -52
  128. package/src/gateway-routes.ts +0 -158
  129. package/src/gateway.ts +0 -258
  130. package/src/grok-errors.ts +0 -24
  131. package/src/grok-imagine.ts +0 -1627
  132. package/src/grok-import.ts +0 -151
  133. package/src/http-json.ts +0 -82
  134. package/src/ids.ts +0 -59
  135. package/src/imagine-routes.ts +0 -463
  136. package/src/index.ts +0 -735
  137. package/src/invariant.ts +0 -17
  138. package/src/kimi-errors.ts +0 -26
  139. package/src/media-store.ts +0 -927
  140. package/src/oauth-import-routes.ts +0 -324
  141. package/src/oauth-providers.ts +0 -152
  142. package/src/oauth-session.ts +0 -183
  143. package/src/oauth-sources.ts +0 -1104
  144. package/src/oauth.ts +0 -620
  145. package/src/provider.ts +0 -128
  146. package/src/proxy.ts +0 -11
  147. package/src/redact.ts +0 -72
  148. package/src/session.ts +0 -218
  149. package/src/store.ts +0 -217
  150. package/src/web-origin.ts +0 -296
  151. package/src/web-routes.ts +0 -38
package/lib/index.js CHANGED
@@ -14906,7 +14906,7 @@ var require_dump = __commonJS({
14906
14906
  var require_dns = __commonJS({
14907
14907
  "node_modules/.pnpm/undici@7.29.0/node_modules/undici/lib/interceptor/dns.js"(exports, module) {
14908
14908
  "use strict";
14909
- var { isIP: isIP2 } = __require("node:net");
14909
+ var { isIP: isIP3 } = __require("node:net");
14910
14910
  var { lookup } = __require("node:dns");
14911
14911
  var DecoratorHandler = require_decorator_handler();
14912
14912
  var { InvalidArgumentError, InformationalError } = require_errors();
@@ -15312,7 +15312,7 @@ var require_dns = __commonJS({
15312
15312
  return (dispatch) => {
15313
15313
  return function dnsInterceptor(origDispatchOpts, handler) {
15314
15314
  const origin = origDispatchOpts.origin.constructor === URL ? origDispatchOpts.origin : new URL(origDispatchOpts.origin);
15315
- if (isIP2(origin.hostname) !== 0) {
15315
+ if (isIP3(origin.hostname) !== 0) {
15316
15316
  return dispatch(origDispatchOpts, handler);
15317
15317
  }
15318
15318
  instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {
@@ -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/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/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/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;
@@ -30589,14 +31098,14 @@ function createCodexImageController(options) {
30589
31098
  };
30590
31099
  }
30591
31100
 
30592
- // src/grok-imagine.ts
31101
+ // src/grok-imagine/client.ts
30593
31102
  var import_undici2 = __toESM(require_undici());
30594
31103
  import { Buffer as Buffer3 } from "node:buffer";
30595
31104
  import { lookup as dnsLookup } from "node:dns/promises";
30596
- import { BlockList, isIP } from "node:net";
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,
@@ -31330,7 +31839,10 @@ var MediaStore = class {
31330
31839
  }
31331
31840
  };
31332
31841
 
31333
- // src/grok-imagine.ts
31842
+ // src/grok-imagine/net.ts
31843
+ import { BlockList, isIP } from "node:net";
31844
+
31845
+ // src/grok-imagine/types.ts
31334
31846
  var XAI_API_ORIGIN = "https://api.x.ai";
31335
31847
  var GROK_IMAGINE_IMAGE_PATH = "/v1/images/generations";
31336
31848
  var GROK_IMAGINE_VIDEO_START_PATH = "/v1/videos/generations";
@@ -31387,6 +31899,8 @@ var IMAGE_RESOLUTION_SET = new Set(IMAGINE_IMAGE_RESOLUTIONS);
31387
31899
  var VIDEO_RESOLUTION_SET = new Set(IMAGINE_VIDEO_RESOLUTIONS);
31388
31900
  var IMAGE_ASPECT_SET = new Set(IMAGINE_IMAGE_ASPECT_RATIOS);
31389
31901
  var VIDEO_ASPECT_SET = new Set(IMAGINE_VIDEO_ASPECT_RATIOS);
31902
+
31903
+ // src/grok-imagine/net.ts
31390
31904
  var blockedNetworks = new BlockList();
31391
31905
  blockedNetworks.addSubnet("0.0.0.0", 8, "ipv4");
31392
31906
  blockedNetworks.addSubnet("10.0.0.0", 8, "ipv4");
@@ -31418,16 +31932,6 @@ blockedNetworks.addSubnet("5f00::", 16, "ipv6");
31418
31932
  blockedNetworks.addSubnet("fc00::", 7, "ipv6");
31419
31933
  blockedNetworks.addSubnet("fe80::", 10, "ipv6");
31420
31934
  blockedNetworks.addSubnet("ff00::", 8, "ipv6");
31421
- function isRecord5(value) {
31422
- return typeof value === "object" && value !== null && !Array.isArray(value);
31423
- }
31424
- function redactImagineMessage(error) {
31425
- const withoutRemoteUrls = (error instanceof Error ? error.message : String(error)).replace(
31426
- /https?:\/\/[^\s"'<>\\]+/giu,
31427
- "[redacted-url]"
31428
- );
31429
- return safeMessage(withoutRemoteUrls);
31430
- }
31431
31935
  function dottedFromWords(high, low) {
31432
31936
  return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
31433
31937
  }
@@ -31520,6 +32024,18 @@ function isBlockedHostname(hostname) {
31520
32024
  function isAllowlistedImagineHost(hostname) {
31521
32025
  return FROZEN_OUTPUT_HOSTS.has(normalizeHostname(hostname));
31522
32026
  }
32027
+
32028
+ // src/grok-imagine/parse.ts
32029
+ function isRecord5(value) {
32030
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32031
+ }
32032
+ function redactImagineMessage(error) {
32033
+ const withoutRemoteUrls = (error instanceof Error ? error.message : String(error)).replace(
32034
+ /https?:\/\/[^\s"'<>\\]+/giu,
32035
+ "[redacted-url]"
32036
+ );
32037
+ return safeMessage(withoutRemoteUrls);
32038
+ }
31523
32039
  function grokImagineVideoStatusPath(requestId) {
31524
32040
  if (!VIDEO_REQUEST_ID_PATTERN.test(requestId)) {
31525
32041
  throw new GrokImagineError("INVALID_INPUT", "video request id is not a safe identifier");
@@ -31614,6 +32130,20 @@ function detectVideoMediaType(data) {
31614
32130
  }
31615
32131
  return void 0;
31616
32132
  }
32133
+ function parseVideoRequestId(value) {
32134
+ if (typeof value !== "string") {
32135
+ throw new GrokImagineError("INVALID_INPUT", "video request id must be a string");
32136
+ }
32137
+ if (!VIDEO_REQUEST_ID_PATTERN.test(value)) {
32138
+ throw new GrokImagineError(
32139
+ "INVALID_INPUT",
32140
+ "video request id must match ^[A-Za-z0-9_-]{1,256}$ and be a safe ASCII identifier"
32141
+ );
32142
+ }
32143
+ return value;
32144
+ }
32145
+
32146
+ // src/grok-imagine/client.ts
31617
32147
  function parseContentType(value) {
31618
32148
  if (value === null || value === void 0 || value.trim() === "") return void 0;
31619
32149
  return value.split(";", 1)[0]?.trim().toLowerCase();
@@ -31639,7 +32169,7 @@ async function assertSafeRemoteMediaUrl(raw, lookup) {
31639
32169
  throw new GrokImagineError("SSRF", "generated media URL must use port 443");
31640
32170
  }
31641
32171
  const host = normalizeHostname(url.hostname);
31642
- if (host === "" || isBlockedHostname(host) || isIP(host) !== 0) {
32172
+ if (host === "" || isBlockedHostname(host) || isIP2(host) !== 0) {
31643
32173
  throw new GrokImagineError("SSRF", "generated media host is not allowed");
31644
32174
  }
31645
32175
  if (!isAllowlistedImagineHost(host)) {
@@ -31658,7 +32188,7 @@ async function assertSafeRemoteMediaUrl(raw, lookup) {
31658
32188
  }
31659
32189
  async function resolvePublicAddresses(hostname, lookup) {
31660
32190
  const host = normalizeHostname(hostname);
31661
- if (host === "" || isBlockedHostname(host) || isIP(host) !== 0) {
32191
+ if (host === "" || isBlockedHostname(host) || isIP2(host) !== 0) {
31662
32192
  throw new GrokImagineError("SSRF", "host is not allowed");
31663
32193
  }
31664
32194
  const addresses = [...await lookup(host)];
@@ -31671,7 +32201,7 @@ async function resolvePublicAddresses(hostname, lookup) {
31671
32201
  function createPinnedAgent(hostname, addresses) {
31672
32202
  const expected = normalizeHostname(hostname);
31673
32203
  const records = addresses.map((address) => {
31674
- const family = isIP(address);
32204
+ const family = isIP2(address);
31675
32205
  if (family !== 4 && family !== 6) {
31676
32206
  throw new GrokImagineError("SSRF", "pinned address is not a valid IP");
31677
32207
  }
@@ -31926,18 +32456,6 @@ function requirePrompt(prompt) {
31926
32456
  }
31927
32457
  return prompt;
31928
32458
  }
31929
- function parseVideoRequestId(value) {
31930
- if (typeof value !== "string") {
31931
- throw new GrokImagineError("INVALID_INPUT", "video request id must be a string");
31932
- }
31933
- if (!VIDEO_REQUEST_ID_PATTERN.test(value)) {
31934
- throw new GrokImagineError(
31935
- "INVALID_INPUT",
31936
- "video request id must match ^[A-Za-z0-9_-]{1,256}$ and be a safe ASCII identifier"
31937
- );
31938
- }
31939
- return value;
31940
- }
31941
32459
  var ImagineAbortError = class extends Error {
31942
32460
  name = "ImagineAbortError";
31943
32461
  constructor(reason) {
@@ -33420,7 +33938,6 @@ var DSH_EXACT_BOM = Object.freeze({
33420
33938
  "@deepseek-ai/dsh-atomic-write": "0.1.1-rc.2",
33421
33939
  "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
33422
33940
  "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
33423
- "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
33424
33941
  "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
33425
33942
  "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
33426
33943
  "@deepseek-ai/dsh-client-web": "0.1.1-rc.2",
@@ -33562,7 +34079,7 @@ function createDshHostAdapter(context) {
33562
34079
  }
33563
34080
 
33564
34081
  // src/gateway-auth.ts
33565
- import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
34082
+ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
33566
34083
  import { mkdir as mkdir3 } from "node:fs/promises";
33567
34084
  import { dirname as dirname3, join as join5, resolve as resolve5 } from "node:path";
33568
34085
  import { writeFileAtomic as writeFileAtomic3 } from "@deepseek-ai/dsh-atomic-write";
@@ -33572,7 +34089,7 @@ function gatewayKeyPath(dshHome) {
33572
34089
  return resolve5(join5(resolveDshHome2(dshHome), GATEWAY_KEY_FILENAME));
33573
34090
  }
33574
34091
  function generateGatewayApiKey() {
33575
- return randomBytes4(32).toString("base64url");
34092
+ return randomBytes5(32).toString("base64url");
33576
34093
  }
33577
34094
  function gatewayKeysEqual(left, right) {
33578
34095
  const a = Buffer.from(left);
@@ -33628,11 +34145,6 @@ async function persistGatewayKeyDocument(path, document) {
33628
34145
  `, { mode: 384 });
33629
34146
  }
33630
34147
 
33631
- // src/gateway-protocol.ts
33632
- function isThinkingLevel(value) {
33633
- return value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max";
33634
- }
33635
-
33636
34148
  // src/gateway-backend.ts
33637
34149
  var GatewayRequestError = class extends Error {
33638
34150
  constructor(status, code, message) {
@@ -34645,6 +35157,17 @@ function createCodingOAuthGatewayController(options) {
34645
35157
  }
34646
35158
 
34647
35159
  // src/gateway-routes.ts
35160
+ function authorizeGatewayControl(req, ownerRequestPolicy) {
35161
+ return ownerRequestPolicy.authorize(req);
35162
+ }
35163
+ function authorizeGatewaySecret(req, ownerRequestPolicy) {
35164
+ const decision = ownerRequestPolicy.authorize(req);
35165
+ if (!decision.authorized) return decision;
35166
+ if (decision.accessMode !== "loopback") {
35167
+ return { authorized: false, reason: "loopback-only" };
35168
+ }
35169
+ return decision;
35170
+ }
34648
35171
  function registerGatewayRoutes(ctx, controller, ownerRequestPolicy = LOOPBACK_OWNER_REQUEST_POLICY) {
34649
35172
  let dispose = () => void 0;
34650
35173
  ctx.effect(() => {
@@ -34670,7 +35193,7 @@ function registerGatewayRoutes(ctx, controller, ownerRequestPolicy = LOOPBACK_OW
34670
35193
  return () => dispose();
34671
35194
  }
34672
35195
  async function handleGatewaySettings(req, res, controller, ownerRequestPolicy) {
34673
- if (!ownerRequestPolicy.authorize(req).authorized) {
35196
+ if (!authorizeGatewayControl(req, ownerRequestPolicy).authorized) {
34674
35197
  json3(res, 403, { error: "forbidden" });
34675
35198
  return;
34676
35199
  }
@@ -34715,7 +35238,7 @@ async function handleGatewaySettings(req, res, controller, ownerRequestPolicy) {
34715
35238
  }
34716
35239
  }
34717
35240
  async function handleGatewayReveal(req, res, controller, ownerRequestPolicy) {
34718
- if (!ownerRequestPolicy.authorize(req).authorized) {
35241
+ if (!authorizeGatewaySecret(req, ownerRequestPolicy).authorized) {
34719
35242
  json3(res, 403, { error: "forbidden" });
34720
35243
  return;
34721
35244
  }
@@ -34730,7 +35253,7 @@ async function handleGatewayReveal(req, res, controller, ownerRequestPolicy) {
34730
35253
  }
34731
35254
  }
34732
35255
  async function handleGatewayRotate(req, res, controller, ownerRequestPolicy) {
34733
- if (!ownerRequestPolicy.authorize(req).authorized) {
35256
+ if (!authorizeGatewaySecret(req, ownerRequestPolicy).authorized) {
34734
35257
  json3(res, 403, { error: "forbidden" });
34735
35258
  return;
34736
35259
  }
@@ -35459,6 +35982,10 @@ var OAuthProviderSession = class {
35459
35982
  async invalidateAccessToken() {
35460
35983
  await this.store.invalidate(this.definition.nativeProviderId);
35461
35984
  }
35985
+ /** Refresh host discovery after an account switch/remove without a full logout. */
35986
+ notifyCredentialChange() {
35987
+ this.onCatalogChange?.();
35988
+ }
35462
35989
  async storedCredential() {
35463
35990
  const credential = await this.store.read(this.definition.nativeProviderId);
35464
35991
  return credential?.type === "oauth" ? credential : void 0;
@@ -35623,6 +36150,10 @@ var GrokBuildSession = class {
35623
36150
  async invalidateAccessToken() {
35624
36151
  await this.store.invalidate(XAI_PI_PROVIDER);
35625
36152
  }
36153
+ /** Refresh host discovery after an account switch/remove without a full logout. */
36154
+ notifyCredentialChange() {
36155
+ this.onCatalogChange?.();
36156
+ }
35626
36157
  async logout() {
35627
36158
  try {
35628
36159
  await this.store.delete(XAI_PI_PROVIDER);
@@ -36105,6 +36636,7 @@ export {
36105
36636
  KIMI_CODE_OAUTH_ROUTE,
36106
36637
  KIMI_PI_PROVIDER,
36107
36638
  LOOPBACK_OWNER_REQUEST_POLICY,
36639
+ OAUTH_MAX_ACCOUNTS,
36108
36640
  OAUTH_PROVIDER_DEFINITIONS,
36109
36641
  OAuthCredentialFileStore,
36110
36642
  OAuthProviderSession,
@@ -36143,6 +36675,7 @@ export {
36143
36675
  importGrokBuildFromGrok,
36144
36676
  importGrokBuildSession,
36145
36677
  inject,
36678
+ isValidAccountId,
36146
36679
  loginGrokBuild,
36147
36680
  loginGrokBuildPkce,
36148
36681
  loginGrokBuildSession,
@@ -36161,6 +36694,7 @@ export {
36161
36694
  refreshGrokBuildToken,
36162
36695
  registerCodingOAuthRoutes,
36163
36696
  registerGrokBuildAuthRoutes,
36697
+ resolveAccountIdForCredential,
36164
36698
  resolveOAuthParams,
36165
36699
  safeMessage,
36166
36700
  thinkingLevelMapFromLiveEfforts