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.
- package/CHANGELOG.md +291 -255
- package/CONTRIBUTING.md +138 -129
- package/INSTALL.md +261 -256
- package/LICENSE +19 -19
- package/NOTICE +11 -11
- package/README.de.md +303 -303
- package/README.es.md +304 -304
- package/README.fr.md +304 -304
- package/README.ja.md +304 -304
- package/README.ko.md +304 -304
- package/README.md +321 -320
- package/README.pt-BR.md +304 -304
- package/README.ru.md +304 -304
- package/README.zh-CN.md +319 -318
- package/compatibility/dsh-bom.json +36 -30
- package/cordis.patch.yml +13 -13
- package/docs/00-project-rules.md +213 -212
- package/docs/02-architecture.md +142 -138
- package/docs/02-architecture.zh-CN.md +142 -138
- package/lib/auth-routes.d.ts +10 -1
- package/lib/auth-routes.d.ts.map +1 -1
- package/lib/bin.js +483 -50
- package/lib/bin.js.map +4 -4
- package/lib/client.js +5 -5
- package/lib/client.js.map +4 -4
- package/lib/compatibility.d.ts +0 -1
- package/lib/compatibility.d.ts.map +1 -1
- package/lib/gateway-protocol.d.ts +3 -46
- package/lib/gateway-protocol.d.ts.map +1 -1
- package/lib/gateway-routes.d.ts.map +1 -1
- package/lib/grok-errors.d.ts +2 -12
- package/lib/grok-errors.d.ts.map +1 -1
- package/lib/grok-imagine/client.d.ts +73 -0
- package/lib/grok-imagine/client.d.ts.map +1 -0
- package/lib/grok-imagine/index.d.ts +8 -0
- package/lib/grok-imagine/index.d.ts.map +1 -0
- package/lib/grok-imagine/net.d.ts +10 -0
- package/lib/grok-imagine/net.d.ts.map +1 -0
- package/lib/grok-imagine/parse.d.ts +20 -0
- package/lib/grok-imagine/parse.d.ts.map +1 -0
- package/lib/grok-imagine/types.d.ts +191 -0
- package/lib/grok-imagine/types.d.ts.map +1 -0
- package/lib/grok-imagine.d.ts +3 -263
- package/lib/grok-imagine.d.ts.map +1 -1
- package/lib/http-json.d.ts +2 -9
- package/lib/http-json.d.ts.map +1 -1
- package/lib/ids.d.ts +4 -0
- package/lib/ids.d.ts.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +730 -196
- package/lib/index.js.map +4 -4
- package/lib/invariant.js.map +1 -1
- package/lib/kimi-errors.d.ts +2 -12
- package/lib/kimi-errors.d.ts.map +1 -1
- package/lib/oauth-session.d.ts +2 -0
- package/lib/oauth-session.d.ts.map +1 -1
- package/lib/session.d.ts +2 -0
- package/lib/session.d.ts.map +1 -1
- package/lib/store.d.ts +73 -1
- package/lib/store.d.ts.map +1 -1
- package/media/en/settings_accounts.png +0 -0
- package/media/en/settings_capabilities.png +0 -0
- package/media/en/settings_gateway.png +0 -0
- package/media/settings_accounts.png +0 -0
- package/media/settings_capabilities.png +0 -0
- package/media/settings_gateway.png +0 -0
- package/media/settings_overview.png +0 -0
- package/media/zh-CN/settings_accounts.png +0 -0
- package/media/zh-CN/settings_capabilities.png +0 -0
- package/media/zh-CN/settings_gateway.png +0 -0
- package/package.json +224 -221
- package/patches/dsh-agy@0.1.2.patch +25 -25
- package/scripts/release.mjs +187 -186
- package/scripts/smoke-deployed-routes.mjs +146 -146
- package/scripts/verify-deployed-catalog.mjs +87 -87
- package/src/adapter.ts +0 -348
- package/src/alias-adapter.ts +0 -147
- package/src/auth-routes.ts +0 -921
- package/src/auth.ts +0 -67
- package/src/bin.ts +0 -350
- package/src/capability-routes.ts +0 -279
- package/src/capability-runtime.ts +0 -314
- package/src/capability-settings.ts +0 -671
- package/src/capability-tools.ts +0 -685
- package/src/catalog.ts +0 -271
- package/src/client/GrokBuildSettings.tsx +0 -771
- package/src/client/api.ts +0 -88
- package/src/client/components/AboutTab.tsx +0 -30
- package/src/client/components/AccountsTab.tsx +0 -241
- package/src/client/components/Badge.tsx +0 -33
- package/src/client/components/CapabilitiesTab.tsx +0 -265
- package/src/client/components/CliPullPreview.tsx +0 -116
- package/src/client/components/CopyButton.tsx +0 -57
- package/src/client/components/GatewayTab.tsx +0 -469
- package/src/client/components/NoticeBanner.tsx +0 -46
- package/src/client/components/ProgressBar.tsx +0 -53
- package/src/client/components/ProviderCard.tsx +0 -606
- package/src/client/components/SettingsTabs.tsx +0 -75
- package/src/client/components/ToggleSwitch.tsx +0 -71
- package/src/client/constants.ts +0 -230
- package/src/client/display.ts +0 -61
- package/src/client/dshClientAdapter.ts +0 -127
- package/src/client/gatewaySnippets.ts +0 -37
- package/src/client/index.tsx +0 -156
- package/src/client/locales.ts +0 -540
- package/src/client/microStyles.ts +0 -52
- package/src/client/parsers.ts +0 -398
- package/src/client/styles.ts +0 -325
- package/src/client/types.ts +0 -199
- package/src/codex-http.ts +0 -447
- package/src/codex-images.ts +0 -503
- package/src/codex-model-capabilities.ts +0 -320
- package/src/codex-search.ts +0 -245
- package/src/codex-usage.ts +0 -263
- package/src/compatibility.ts +0 -55
- package/src/dsh-host-adapter.ts +0 -173
- package/src/gateway-anthropic-messages.ts +0 -84
- package/src/gateway-auth.ts +0 -102
- package/src/gateway-backend.ts +0 -274
- package/src/gateway-body.ts +0 -49
- package/src/gateway-config.ts +0 -76
- package/src/gateway-http.ts +0 -104
- package/src/gateway-openai-chat.ts +0 -124
- package/src/gateway-openai-responses.ts +0 -53
- package/src/gateway-parse.ts +0 -224
- package/src/gateway-protocol.ts +0 -52
- package/src/gateway-routes.ts +0 -158
- package/src/gateway.ts +0 -258
- package/src/grok-errors.ts +0 -24
- package/src/grok-imagine.ts +0 -1627
- package/src/grok-import.ts +0 -151
- package/src/http-json.ts +0 -82
- package/src/ids.ts +0 -59
- package/src/imagine-routes.ts +0 -463
- package/src/index.ts +0 -735
- package/src/invariant.ts +0 -17
- package/src/kimi-errors.ts +0 -26
- package/src/media-store.ts +0 -927
- package/src/oauth-import-routes.ts +0 -324
- package/src/oauth-providers.ts +0 -152
- package/src/oauth-session.ts +0 -183
- package/src/oauth-sources.ts +0 -1104
- package/src/oauth.ts +0 -620
- package/src/provider.ts +0 -128
- package/src/proxy.ts +0 -11
- package/src/redact.ts +0 -72
- package/src/session.ts +0 -218
- package/src/store.ts +0 -217
- package/src/web-origin.ts +0 -296
- 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:
|
|
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 (
|
|
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/
|
|
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 =
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
28175
|
-
|
|
28176
|
-
const
|
|
28177
|
-
|
|
28178
|
-
if (candidate
|
|
28179
|
-
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
|
|
28183
|
-
|
|
28184
|
-
|
|
28185
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 {
|
|
31105
|
+
import { isIP as isIP2 } from "node:net";
|
|
30597
31106
|
|
|
30598
31107
|
// src/media-store.ts
|
|
30599
|
-
import { createHash as createHash3, randomBytes as
|
|
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
|
|
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-${
|
|
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) ||
|
|
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) ||
|
|
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 =
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|