@xiaoso/dsh-tool-plus 0.1.0-beta.2 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +122 -0
- package/README.md +3 -2
- package/THIRD_PARTY_NOTICES.md +4 -35
- package/lib/abort-B7izMMna.mjs +51 -0
- package/lib/amazon-bedrock-D-shAr1X.mjs +917 -0
- package/lib/anthropic-BWsZFzU8.mjs +262 -0
- package/lib/anthropic-CMT1Qj1t.mjs +3115 -0
- package/lib/api-key-login-CpzSezsi.mjs +157 -0
- package/lib/aws-credentials-8Mgn5MAy.mjs +867 -0
- package/lib/azure-openai-responses-CNkHiLUh.mjs +274 -0
- package/lib/bun-shim.mjs +172 -7
- package/lib/bundled-references-CSuBco5p.mjs +55 -0
- package/lib/callback-server-BEFNf-jf.mjs +398 -0
- package/lib/client.js +110 -40
- package/lib/cursor-BIQfBC6V.mjs +135 -0
- package/lib/cursor-DNk6eXDd.mjs +5470 -0
- package/lib/cursor-MjSm74IJ.mjs +273 -0
- package/lib/device-code-OI_tQcmc.mjs +51 -0
- package/lib/devin-CldyPD8K.mjs +95 -0
- package/lib/devin-CzRpTUOA.mjs +114 -0
- package/lib/devin-DXOeNyU7.mjs +542 -0
- package/lib/error-B3bevhrs.mjs +1449 -0
- package/lib/event-stream-BDPi64mK.mjs +151 -0
- package/lib/github-copilot-4GaMtQmg.mjs +208 -0
- package/lib/github-copilot-B2HGSIJK.mjs +104 -0
- package/lib/gitlab-duo-akICyfeD.mjs +155 -0
- package/lib/gitlab-duo-workflow-CSEgYk3N.mjs +91 -0
- package/lib/google-antigravity-Dlkdhahq.mjs +198 -0
- package/lib/google-auth-DhqpOYrX.mjs +232 -0
- package/lib/google-gemini-cli-BG_LRdIV.mjs +850 -0
- package/lib/google-gemini-cli-zJhiOcND.mjs +211 -0
- package/lib/google-oauth-shared-CwNWYdmR.mjs +147 -0
- package/lib/google-shared-DzhCb9b0.mjs +760 -0
- package/lib/google-vertex-kscbkyRb.mjs +99 -0
- package/lib/google-zyDOG9x7.mjs +30 -0
- package/lib/identity-DnB63pS7.mjs +219 -0
- package/lib/idle-iterator-C6XbWcuB.mjs +366 -0
- package/lib/index.mjs +2865 -2745
- package/lib/kimi-DMVI-zrZ.mjs +236 -0
- package/lib/markit-M0jhziqU.mjs +1417 -0
- package/lib/minimax-code-BK-3eBbz.mjs +53 -0
- package/lib/models-CfT4coYT.mjs +104660 -0
- package/lib/ollama-BQMGPxtV.mjs +589 -0
- package/lib/openai-codex-CQL_kvYL.mjs +255 -0
- package/lib/openai-codex-responses-Cz-2qpC7.mjs +3144 -0
- package/lib/openai-completions-i8doczAk.mjs +1360 -0
- package/lib/openai-reasoning-fallback-DGVVLgd1.mjs +286 -0
- package/lib/openai-responses-DTkBiiy-.mjs +867 -0
- package/lib/opencode-DaJjcUbf.mjs +38 -0
- package/lib/perplexity-9b-RQ0mV.mjs +194 -0
- package/lib/proxy-ZtUcl6r5.mjs +190 -0
- package/lib/scrapers-CL-WgdU1.mjs +9198 -0
- package/lib/scrapers-CipyKyCH.mjs +9198 -0
- package/lib/scrapers-DGfrYhtw.mjs +9198 -0
- package/lib/scrapers-DSUPYoUF.mjs +9198 -0
- package/lib/scrapers-DZ2GXmp7.mjs +9198 -0
- package/lib/src-DTid0g4K.mjs +5001 -0
- package/lib/stream-C_RAoxJC.mjs +10236 -0
- package/lib/stream-markup-healing-2toHXWXM.mjs +491 -0
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/config/fields.d.ts +1 -1
- package/lib/types/config/settings.d.ts +16 -2
- package/lib/types/tools/omp/sdk.d.ts +6 -0
- package/lib/types/tools/omp/tools/fetch.d.ts +2 -0
- package/lib/types/tools/omp/tools/read.d.ts +2 -8
- package/lib/types/tools/omp/utils/image-loading.d.ts +10 -36
- package/lib/types/tools/read/adapter/index.d.ts +4 -1
- package/lib/types/tools/read-image/index.d.ts +4 -0
- package/lib/types/tools/shared/bun-shim.d.ts +35 -3
- package/lib/types/tools/shared/image-bridge.d.ts +98 -0
- package/lib/types/tools/shared/omp-prompt.d.ts +4 -2
- package/lib/utils-2Wq2eATG.mjs +8119 -0
- package/lib/utils-BnX8O3vi.mjs +12303 -0
- package/lib/utils-CE8N8P4Q.mjs +12315 -0
- package/lib/utils-C_MrQ_0z.mjs +12301 -0
- package/lib/utils-D0Z1fyoO.mjs +12299 -0
- package/lib/utils-DppGHXQB.mjs +51 -0
- package/lib/utils-W8WdSBpX.mjs +12315 -0
- package/lib/vision-guard-BDD4OrKo.mjs +92 -0
- package/lib/wafer-DRU0EjZX.mjs +17 -0
- package/lib/xai-oauth-DPHDXKD7.mjs +446 -0
- package/lib/xiaomi-Dd87KSg5.mjs +157 -0
- package/lib/zai-CFeOt-53.mjs +203 -0
- package/lib/zip-D6QXpvaJ.mjs +1316 -0
- package/package.json +14 -10
- package/presets/package.json +1 -1
- package/presets/tool-plus-ptc/preset.yml +1 -1
- package/presets/tool-plus-standard/preset.yml +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { M as isDashscopeCompatibleModeUrl, S as isQwenModelId, x as isOpenAIModelId } from "./models-CfT4coYT.mjs";
|
|
4
|
+
import "./identity-DnB63pS7.mjs";
|
|
5
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/types.ts
|
|
6
|
+
/**
|
|
7
|
+
* Ceiling on the output-token count omp requests from any OpenAI-family endpoint
|
|
8
|
+
* (openai-responses, azure/xai responses, and openai-completions). Mirrors
|
|
9
|
+
* Anthropic's {@link CLAUDE_CODE_MAX_OUTPUT_TOKENS}.
|
|
10
|
+
*
|
|
11
|
+
* Catalog `maxTokens` frequently reflects a model's context window rather than a
|
|
12
|
+
* given upstream's real per-request output cap. OpenRouter, for instance,
|
|
13
|
+
* advertises 131072 output tokens for `z-ai/glm-4.7`, but the Cerebras upstream
|
|
14
|
+
* only allows ~131072 tokens total — so requesting the full ceiling overflows
|
|
15
|
+
* with a 400. Requested output is clamped to this value (and to `model.maxTokens`).
|
|
16
|
+
*/
|
|
17
|
+
const OPENAI_MAX_OUTPUT_TOKENS = 64e3;
|
|
18
|
+
function isOpenAIServiceTierApi(api) {
|
|
19
|
+
return api === "openai-completions" || api === "openai-responses" || api === "openai-codex-responses";
|
|
20
|
+
}
|
|
21
|
+
function excludesInferredOpenAIServiceTier(provider) {
|
|
22
|
+
return provider === "fireworks" || provider === "github-copilot";
|
|
23
|
+
}
|
|
24
|
+
function isOpenAIServiceTierModel(model) {
|
|
25
|
+
return !excludesInferredOpenAIServiceTier(model.provider) && isOpenAIServiceTierApi(model.api) && isOpenAIModelId(model.id);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* True when the tier should be sent on the wire as the provider's service-tier
|
|
29
|
+
* request field. `auto` is never forwarded — it is OpenAI's implicit default, so
|
|
30
|
+
* omitting `service_tier` is identical to requesting `auto`, and the Codex
|
|
31
|
+
* (ChatGPT OAuth) endpoint rejects an explicit `auto` outright. OpenAI /
|
|
32
|
+
* OpenAI-Codex accept every other {@link ServiceTier}; Google (Gemini API +
|
|
33
|
+
* Vertex) and OpenRouter accept `flex`/`priority`; Fireworks Serverless
|
|
34
|
+
* realizes only its Priority serving path. Anthropic is absent because it
|
|
35
|
+
* realizes `priority` via `speed: "fast"`.
|
|
36
|
+
*/
|
|
37
|
+
function shouldSendServiceTier(serviceTier, target) {
|
|
38
|
+
if (!serviceTier || serviceTier === "auto") return false;
|
|
39
|
+
const provider = typeof target === "string" ? target : target?.provider;
|
|
40
|
+
if (provider === "openai" || provider === "openai-codex") return true;
|
|
41
|
+
if (provider === "openrouter") return serviceTier === "flex" || serviceTier === "scale" || serviceTier === "priority";
|
|
42
|
+
if (typeof target !== "string" && target && isOpenAIServiceTierModel(target)) return true;
|
|
43
|
+
if (provider === "google") return serviceTier === "flex" || serviceTier === "priority";
|
|
44
|
+
if (provider === "google-vertex" || provider === "fireworks") return serviceTier === "priority";
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/providers/vision-guard.ts
|
|
49
|
+
const NON_VISION_IMAGE_PLACEHOLDER = "[image omitted: model does not support vision]";
|
|
50
|
+
function partitionVisionContent(content, supportsImages) {
|
|
51
|
+
const textBlocks = content.filter((block) => block.type === "text");
|
|
52
|
+
const imageBlocks = content.filter((block) => block.type === "image");
|
|
53
|
+
return {
|
|
54
|
+
textBlocks,
|
|
55
|
+
imageBlocks: supportsImages ? imageBlocks : [],
|
|
56
|
+
omittedImages: !supportsImages && imageBlocks.length > 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function joinTextWithImagePlaceholder(text, omittedImages) {
|
|
60
|
+
const parts = [];
|
|
61
|
+
if (text.length > 0) parts.push(text);
|
|
62
|
+
if (omittedImages) parts.push(NON_VISION_IMAGE_PLACEHOLDER);
|
|
63
|
+
return parts.join("\n");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Detect known text-only Qwen models served via Alibaba DashScope's consumer
|
|
67
|
+
* `compatible-mode` endpoint that the upstream chat-completions API rejects
|
|
68
|
+
* multimodal content arrays for. The compatible-mode endpoint also serves
|
|
69
|
+
* multimodal Qwen SKUs without `vl` in the id (e.g. `qwen3.7-plus`), so this
|
|
70
|
+
* guard only covers families verified to be text-only for issue #1859:
|
|
71
|
+
* `qwen*-coder*` and `qwen*-max` up to and including `qwen3.7-max`.
|
|
72
|
+
*
|
|
73
|
+
* Qwen-Max became multimodal at `qwen3.8-max` (image input, issue #8019), so
|
|
74
|
+
* `-max` SKUs at version 3.8 or newer are excluded — otherwise the override
|
|
75
|
+
* would strip images from a genuinely vision-capable flagship (issue #8305).
|
|
76
|
+
*
|
|
77
|
+
* Used as a defensive override in `convertMessages` so a misconfigured custom
|
|
78
|
+
* provider (issue #1859) can't drive the request into an unrecoverable 400.
|
|
79
|
+
*/
|
|
80
|
+
function isDashscopeCompatibleModeTextOnlyQwen(model) {
|
|
81
|
+
if (!isDashscopeCompatibleModeUrl(model.baseUrl)) return false;
|
|
82
|
+
if (!isQwenModelId(model.id)) return false;
|
|
83
|
+
const id = model.id.toLowerCase();
|
|
84
|
+
if (/\bqwen(?:[\d.]+)?-coder\b/.test(id)) return true;
|
|
85
|
+
const maxMatch = id.match(/\bqwen(?:(\d+)(?:\.(\d+))?)?-max\b/);
|
|
86
|
+
if (!maxMatch) return false;
|
|
87
|
+
const major = maxMatch[1] ? Number.parseInt(maxMatch[1], 10) : 0;
|
|
88
|
+
const minor = maxMatch[2] ? Number.parseInt(maxMatch[2], 10) : 0;
|
|
89
|
+
return major < 3 || major === 3 && minor < 8;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { OPENAI_MAX_OUTPUT_TOKENS as a, partitionVisionContent as i, isDashscopeCompatibleModeTextOnlyQwen as n, shouldSendServiceTier as o, joinTextWithImagePlaceholder as r, NON_VISION_IMAGE_PLACEHOLDER as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { t as createApiKeyLogin } from "./api-key-login-CpzSezsi.mjs";
|
|
4
|
+
const loginWaferServerless = createApiKeyLogin({
|
|
5
|
+
providerLabel: "Wafer Serverless",
|
|
6
|
+
authUrl: "https://app.wafer.ai/usage",
|
|
7
|
+
instructions: "Create or copy your Wafer Serverless API key from the Wafer dashboard",
|
|
8
|
+
promptMessage: "Paste your Wafer Serverless API key",
|
|
9
|
+
placeholder: "wfr_...",
|
|
10
|
+
validation: {
|
|
11
|
+
kind: "models-endpoint",
|
|
12
|
+
provider: "Wafer Serverless",
|
|
13
|
+
modelsUrl: "https://pass.wafer.ai/v1/models"
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
//#endregion
|
|
17
|
+
export { loginWaferServerless };
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { r as __exportAll } from "./fs-promises-shim-B-9cMxz5.mjs";
|
|
4
|
+
import { c as OAuthError, h as LoginCancelledError } from "./error-B3bevhrs.mjs";
|
|
5
|
+
import { t as pollOAuthDeviceCodeFlow } from "./device-code-OI_tQcmc.mjs";
|
|
6
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/registry/oauth/xai-oauth.ts
|
|
7
|
+
/**
|
|
8
|
+
* xAI Grok OAuth device authorization flow.
|
|
9
|
+
*
|
|
10
|
+
* Requests an RFC 8628 device code, opens xAI's verification page, and polls
|
|
11
|
+
* the discovered token endpoint until the user approves the login.
|
|
12
|
+
*/
|
|
13
|
+
var xai_oauth_exports = /* @__PURE__ */ __exportAll({
|
|
14
|
+
buildXAICliBillingUrl: () => buildXAICliBillingUrl,
|
|
15
|
+
extractXAIAccessTokenSubject: () => extractXAIAccessTokenSubject,
|
|
16
|
+
fetchXAIOAuthIdentity: () => fetchXAIOAuthIdentity,
|
|
17
|
+
getXAICliBillingHeaders: () => getXAICliBillingHeaders,
|
|
18
|
+
loginXAIOAuth: () => loginXAIOAuth,
|
|
19
|
+
parseXAIAccessTokenPayload: () => parseXAIAccessTokenPayload,
|
|
20
|
+
refreshXAIOAuthToken: () => refreshXAIOAuthToken,
|
|
21
|
+
validateXAIBillingEndpoint: () => validateXAIBillingEndpoint,
|
|
22
|
+
validateXAIEndpoint: () => validateXAIEndpoint
|
|
23
|
+
});
|
|
24
|
+
const XAI_OAUTH_ISSUER = "https://auth.x.ai";
|
|
25
|
+
const XAI_OAUTH_DISCOVERY_URL = `${XAI_OAUTH_ISSUER}/.well-known/openid-configuration`;
|
|
26
|
+
const XAI_OAUTH_DEVICE_CODE_URL = `${XAI_OAUTH_ISSUER}/oauth2/device/code`;
|
|
27
|
+
const XAI_OAUTH_USERINFO_URL = `${XAI_OAUTH_ISSUER}/oauth2/userinfo`;
|
|
28
|
+
const XAI_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
29
|
+
const XAI_OAUTH_SCOPE = "openid profile email offline_access grok-cli:access api:access";
|
|
30
|
+
const XAI_CLI_BILLING_BASE_URL = "https://cli-chat-proxy.grok.com";
|
|
31
|
+
const XAI_CLI_BILLING_PATH = "/v1/billing";
|
|
32
|
+
const XAI_CLI_BILLING_FORMAT = "credits";
|
|
33
|
+
const ACCESS_TOKEN_CLIENT_SKEW_MS = 3e5;
|
|
34
|
+
const DISCOVERY_TIMEOUT_MS = 15e3;
|
|
35
|
+
const TOKEN_REQUEST_TIMEOUT_MS = 2e4;
|
|
36
|
+
function isRecord(value) {
|
|
37
|
+
return typeof value === "object" && value !== null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate an xAI OIDC endpoint against its scheme and host.
|
|
41
|
+
*
|
|
42
|
+
* The discovery response is long-lived and its token endpoint receives every
|
|
43
|
+
* future refresh token. Rejecting non-HTTPS or non-`x.ai` / `*.x.ai` hosts
|
|
44
|
+
* pins that endpoint to the xAI auth origin.
|
|
45
|
+
*
|
|
46
|
+
* @throws Error with message `Invalid xAI <field>: <url>` when the URL fails
|
|
47
|
+
* either scheme or host validation.
|
|
48
|
+
*/
|
|
49
|
+
function isXaiAuthHostname(host) {
|
|
50
|
+
return host === "x.ai" || host.endsWith(".x.ai");
|
|
51
|
+
}
|
|
52
|
+
/** SuperGrok CLI billing proxy host (`cli-chat-proxy.grok.com`), not the OIDC issuer. */
|
|
53
|
+
function isXaiBillingHostname(host) {
|
|
54
|
+
return host === "grok.com" || host.endsWith(".grok.com");
|
|
55
|
+
}
|
|
56
|
+
function validateXAIEndpoint(url, field) {
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = new URL(url);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
62
|
+
kind: "validation",
|
|
63
|
+
provider: "xai"
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (parsed.protocol !== "https:") throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
67
|
+
kind: "validation",
|
|
68
|
+
provider: "xai"
|
|
69
|
+
});
|
|
70
|
+
const host = parsed.hostname.toLowerCase();
|
|
71
|
+
if (!host || !isXaiAuthHostname(host)) throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
72
|
+
kind: "validation",
|
|
73
|
+
provider: "xai"
|
|
74
|
+
});
|
|
75
|
+
return url;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Pin SuperGrok billing URLs to HTTPS `grok.com` / `*.grok.com`.
|
|
79
|
+
* The CLI billing proxy is intentionally not on `*.x.ai`.
|
|
80
|
+
*/
|
|
81
|
+
function validateXAIBillingEndpoint(url, field = "billing_url") {
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = new URL(url);
|
|
85
|
+
} catch {
|
|
86
|
+
throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
87
|
+
kind: "validation",
|
|
88
|
+
provider: "xai"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (parsed.protocol !== "https:") throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
92
|
+
kind: "validation",
|
|
93
|
+
provider: "xai"
|
|
94
|
+
});
|
|
95
|
+
const host = parsed.hostname.toLowerCase();
|
|
96
|
+
if (!host || !isXaiBillingHostname(host)) throw new OAuthError(`Invalid xAI ${field}: ${url}`, {
|
|
97
|
+
kind: "validation",
|
|
98
|
+
provider: "xai"
|
|
99
|
+
});
|
|
100
|
+
return url;
|
|
101
|
+
}
|
|
102
|
+
/** Fetch xAI's OIDC discovery document and validate the token endpoint. */
|
|
103
|
+
async function xaiOAuthDiscovery(timeoutMs = DISCOVERY_TIMEOUT_MS, fetchOverride) {
|
|
104
|
+
const fetchImpl = fetchOverride ?? fetch;
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await fetchImpl(XAI_OAUTH_DISCOVERY_URL, {
|
|
108
|
+
method: "GET",
|
|
109
|
+
headers: { Accept: "application/json" },
|
|
110
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new OAuthError(`xAI OIDC discovery failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
114
|
+
kind: "discovery",
|
|
115
|
+
provider: "xai",
|
|
116
|
+
cause: error
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (response.status !== 200) throw new OAuthError(`xAI OIDC discovery returned status ${response.status}.`, {
|
|
120
|
+
kind: "discovery",
|
|
121
|
+
provider: "xai",
|
|
122
|
+
status: response.status
|
|
123
|
+
});
|
|
124
|
+
let payload;
|
|
125
|
+
try {
|
|
126
|
+
payload = await response.json();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new OAuthError(`xAI OIDC discovery returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`, {
|
|
129
|
+
kind: "validation",
|
|
130
|
+
provider: "xai",
|
|
131
|
+
cause: error
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (!isRecord(payload)) throw new OAuthError("xAI OIDC discovery response was not a JSON object.", {
|
|
135
|
+
kind: "validation",
|
|
136
|
+
provider: "xai"
|
|
137
|
+
});
|
|
138
|
+
const tokenEndpoint = typeof payload.token_endpoint === "string" ? payload.token_endpoint.trim() : "";
|
|
139
|
+
if (!tokenEndpoint) throw new OAuthError("xAI OIDC discovery response was missing token_endpoint.", {
|
|
140
|
+
kind: "validation",
|
|
141
|
+
provider: "xai"
|
|
142
|
+
});
|
|
143
|
+
validateXAIEndpoint(tokenEndpoint, "token_endpoint");
|
|
144
|
+
return { token_endpoint: tokenEndpoint };
|
|
145
|
+
}
|
|
146
|
+
/** Decode an xAI access-token JWT payload without verifying its signature. */
|
|
147
|
+
function parseXAIAccessTokenPayload(jwt) {
|
|
148
|
+
try {
|
|
149
|
+
if (typeof jwt !== "string" || !jwt.includes(".")) return null;
|
|
150
|
+
const parts = jwt.split(".");
|
|
151
|
+
if (parts.length < 2) return null;
|
|
152
|
+
const payloadPart = parts[1];
|
|
153
|
+
if (!payloadPart) return null;
|
|
154
|
+
const decoded = Buffer.from(payloadPart, "base64url").toString("utf8");
|
|
155
|
+
const payload = JSON.parse(decoded);
|
|
156
|
+
return isRecord(payload) && !Array.isArray(payload) ? payload : null;
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Extract the stable xAI subject UUID from an access token. */
|
|
162
|
+
function extractXAIAccessTokenSubject(jwt) {
|
|
163
|
+
const sub = parseXAIAccessTokenPayload(jwt)?.sub;
|
|
164
|
+
return typeof sub === "string" && sub.trim() ? sub.trim() : void 0;
|
|
165
|
+
}
|
|
166
|
+
/** Fetch optional OIDC userinfo for a valid xAI access token. */
|
|
167
|
+
async function fetchXAIOAuthIdentity(accessToken, fetchOverride, signal) {
|
|
168
|
+
const token = accessToken.trim();
|
|
169
|
+
if (!token) return null;
|
|
170
|
+
const fetchImpl = fetchOverride ?? fetch;
|
|
171
|
+
try {
|
|
172
|
+
const response = await fetchImpl(XAI_OAUTH_USERINFO_URL, {
|
|
173
|
+
method: "GET",
|
|
174
|
+
headers: {
|
|
175
|
+
Authorization: `Bearer ${token}`,
|
|
176
|
+
Accept: "application/json"
|
|
177
|
+
},
|
|
178
|
+
redirect: "error",
|
|
179
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)]) : AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
|
|
180
|
+
});
|
|
181
|
+
if (!response.ok) return null;
|
|
182
|
+
const payload = await response.json();
|
|
183
|
+
if (!isRecord(payload) || Array.isArray(payload)) return null;
|
|
184
|
+
const sub = typeof payload.sub === "string" && payload.sub.trim() ? payload.sub.trim() : void 0;
|
|
185
|
+
const email = typeof payload.email === "string" && payload.email.trim() ? payload.email.trim() : void 0;
|
|
186
|
+
const name = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim() : void 0;
|
|
187
|
+
if (!sub && !email && !name) return null;
|
|
188
|
+
return {
|
|
189
|
+
...sub ? { accountId: sub } : {},
|
|
190
|
+
...email ? { email: email.toLowerCase() } : {},
|
|
191
|
+
...name ? { name } : {}
|
|
192
|
+
};
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function withXAIOAuthIdentity(credentials, fetchOverride, signal) {
|
|
198
|
+
const identity = await fetchXAIOAuthIdentity(credentials.access, fetchOverride, signal);
|
|
199
|
+
const accountId = identity?.accountId ?? credentials.accountId ?? extractXAIAccessTokenSubject(credentials.access);
|
|
200
|
+
const email = identity?.email ?? credentials.email;
|
|
201
|
+
return {
|
|
202
|
+
...credentials,
|
|
203
|
+
...accountId ? { accountId } : {},
|
|
204
|
+
...email ? { email } : {}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** Build the SuperGrok CLI billing URL. Pass `""` to omit `format` (unified monthly payload). */
|
|
208
|
+
function buildXAICliBillingUrl(format = XAI_CLI_BILLING_FORMAT) {
|
|
209
|
+
const url = new URL(XAI_CLI_BILLING_PATH, XAI_CLI_BILLING_BASE_URL);
|
|
210
|
+
if (format) url.searchParams.set("format", format);
|
|
211
|
+
return validateXAIBillingEndpoint(url.toString());
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Headers for SuperGrok CLI billing (`cli-chat-proxy.grok.com`).
|
|
215
|
+
* Official Grok CLI also sends `X-XAI-Token-Auth: xai-grok-cli` on this host;
|
|
216
|
+
* include it so billing stays on the same product gate as chat inference.
|
|
217
|
+
*/
|
|
218
|
+
function getXAICliBillingHeaders(options) {
|
|
219
|
+
return {
|
|
220
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
221
|
+
Accept: "application/json",
|
|
222
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function parseXAIDeviceAuthorization(payload) {
|
|
226
|
+
if (!isRecord(payload)) throw new OAuthError("xAI device-code response was not a JSON object.", {
|
|
227
|
+
kind: "validation",
|
|
228
|
+
provider: "xai"
|
|
229
|
+
});
|
|
230
|
+
const deviceCode = typeof payload.device_code === "string" ? payload.device_code.trim() : "";
|
|
231
|
+
const userCode = typeof payload.user_code === "string" ? payload.user_code.trim() : "";
|
|
232
|
+
const verificationUri = typeof payload.verification_uri === "string" ? payload.verification_uri.trim() : "";
|
|
233
|
+
const verificationUriComplete = typeof payload.verification_uri_complete === "string" ? payload.verification_uri_complete.trim() : "";
|
|
234
|
+
const expiresInSeconds = payload.expires_in;
|
|
235
|
+
const intervalSeconds = payload.interval;
|
|
236
|
+
if (!deviceCode || !userCode || !verificationUri || !verificationUriComplete || typeof expiresInSeconds !== "number" || !Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0 || typeof intervalSeconds !== "number" || !Number.isFinite(intervalSeconds) || intervalSeconds <= 0) throw new OAuthError("xAI device-code response missing or invalid required fields.", {
|
|
237
|
+
kind: "validation",
|
|
238
|
+
provider: "xai"
|
|
239
|
+
});
|
|
240
|
+
validateXAIEndpoint(verificationUri, "verification_uri");
|
|
241
|
+
validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
|
|
242
|
+
return {
|
|
243
|
+
deviceCode,
|
|
244
|
+
userCode,
|
|
245
|
+
verificationUriComplete,
|
|
246
|
+
expiresInSeconds,
|
|
247
|
+
intervalSeconds
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function parseXAITokenResponse(payload, label, refreshTokenFallback) {
|
|
251
|
+
if (!isRecord(payload)) throw new OAuthError(`${label} was not a JSON object`, {
|
|
252
|
+
kind: "validation",
|
|
253
|
+
provider: "xai"
|
|
254
|
+
});
|
|
255
|
+
const accessToken = typeof payload.access_token === "string" ? payload.access_token : "";
|
|
256
|
+
const refreshToken = (typeof payload.refresh_token === "string" ? payload.refresh_token : "") || refreshTokenFallback || "";
|
|
257
|
+
const expiresInSeconds = payload.expires_in;
|
|
258
|
+
if (!accessToken) throw new OAuthError(`${label} missing access_token`, {
|
|
259
|
+
kind: "validation",
|
|
260
|
+
provider: "xai"
|
|
261
|
+
});
|
|
262
|
+
if (!refreshToken) throw new OAuthError(`${label} missing refresh_token`, {
|
|
263
|
+
kind: "validation",
|
|
264
|
+
provider: "xai"
|
|
265
|
+
});
|
|
266
|
+
if (typeof expiresInSeconds !== "number" || !Number.isFinite(expiresInSeconds)) throw new OAuthError(`${label} missing expires_in`, {
|
|
267
|
+
kind: "validation",
|
|
268
|
+
provider: "xai"
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
access: accessToken,
|
|
272
|
+
refresh: refreshToken,
|
|
273
|
+
expires: Date.now() + expiresInSeconds * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
async function requestXAIDeviceAuthorization(fetchImpl, signal) {
|
|
277
|
+
let response;
|
|
278
|
+
try {
|
|
279
|
+
const timeoutSignal = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS);
|
|
280
|
+
response = await fetchImpl(XAI_OAUTH_DEVICE_CODE_URL, {
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: {
|
|
283
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
284
|
+
Accept: "application/json"
|
|
285
|
+
},
|
|
286
|
+
body: new URLSearchParams({
|
|
287
|
+
client_id: XAI_OAUTH_CLIENT_ID,
|
|
288
|
+
scope: XAI_OAUTH_SCOPE
|
|
289
|
+
}),
|
|
290
|
+
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (signal?.aborted) throw new LoginCancelledError();
|
|
294
|
+
throw new OAuthError(`xAI device-code request failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
295
|
+
kind: "device-auth",
|
|
296
|
+
provider: "xai",
|
|
297
|
+
cause: error
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
let detail = "";
|
|
302
|
+
try {
|
|
303
|
+
detail = (await response.text()).trim();
|
|
304
|
+
} catch {}
|
|
305
|
+
throw new OAuthError(`xAI device-code request failed: ${response.status}${detail ? ` ${detail}` : ""}`, {
|
|
306
|
+
kind: "device-auth",
|
|
307
|
+
provider: "xai",
|
|
308
|
+
status: response.status
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
let payload;
|
|
312
|
+
try {
|
|
313
|
+
payload = await response.json();
|
|
314
|
+
} catch (error) {
|
|
315
|
+
throw new OAuthError(`xAI device-code response returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`, {
|
|
316
|
+
kind: "validation",
|
|
317
|
+
provider: "xai",
|
|
318
|
+
cause: error
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return parseXAIDeviceAuthorization(payload);
|
|
322
|
+
}
|
|
323
|
+
async function pollXAIDeviceToken(tokenEndpoint, deviceCode, fetchImpl, signal) {
|
|
324
|
+
let response;
|
|
325
|
+
try {
|
|
326
|
+
const timeoutSignal = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS);
|
|
327
|
+
response = await fetchImpl(tokenEndpoint, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers: {
|
|
330
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
331
|
+
Accept: "application/json"
|
|
332
|
+
},
|
|
333
|
+
body: new URLSearchParams({
|
|
334
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
335
|
+
client_id: XAI_OAUTH_CLIENT_ID,
|
|
336
|
+
device_code: deviceCode
|
|
337
|
+
}),
|
|
338
|
+
redirect: "error",
|
|
339
|
+
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
340
|
+
});
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (signal?.aborted) throw new LoginCancelledError();
|
|
343
|
+
throw new OAuthError(`xAI device-code token polling failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
344
|
+
kind: "polling",
|
|
345
|
+
provider: "xai",
|
|
346
|
+
cause: error
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
let payload;
|
|
350
|
+
try {
|
|
351
|
+
payload = await response.json();
|
|
352
|
+
} catch (error) {
|
|
353
|
+
throw new OAuthError(`xAI device-code token polling returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`, {
|
|
354
|
+
kind: "polling",
|
|
355
|
+
provider: "xai",
|
|
356
|
+
status: response.status,
|
|
357
|
+
cause: error
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (response.ok) return {
|
|
361
|
+
status: "complete",
|
|
362
|
+
value: parseXAITokenResponse(payload, "xAI device-code token response")
|
|
363
|
+
};
|
|
364
|
+
if (!isRecord(payload)) throw new OAuthError(`xAI device-code token polling failed: ${response.status}`, {
|
|
365
|
+
kind: "polling",
|
|
366
|
+
provider: "xai",
|
|
367
|
+
status: response.status
|
|
368
|
+
});
|
|
369
|
+
const errorCode = typeof payload.error === "string" ? payload.error : "";
|
|
370
|
+
if (errorCode === "authorization_pending") return { status: "pending" };
|
|
371
|
+
if (errorCode === "slow_down") return { status: "slow_down" };
|
|
372
|
+
const detail = (typeof payload.error_description === "string" ? payload.error_description : "") || errorCode || String(response.status);
|
|
373
|
+
throw new OAuthError(`xAI device-code token polling failed: ${detail}`, {
|
|
374
|
+
kind: "polling",
|
|
375
|
+
provider: "xai",
|
|
376
|
+
status: response.status
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/** Log in to xAI Grok with the RFC 8628 device authorization grant. */
|
|
380
|
+
async function loginXAIOAuth(ctrl) {
|
|
381
|
+
const fetchImpl = ctrl.fetch ?? fetch;
|
|
382
|
+
const discovery = await xaiOAuthDiscovery(DISCOVERY_TIMEOUT_MS, fetchImpl);
|
|
383
|
+
const device = await requestXAIDeviceAuthorization(fetchImpl, ctrl.signal);
|
|
384
|
+
ctrl.onAuth?.({
|
|
385
|
+
url: device.verificationUriComplete,
|
|
386
|
+
instructions: `Enter code: ${device.userCode}`
|
|
387
|
+
});
|
|
388
|
+
ctrl.onProgress?.("Waiting for xAI device authorization...");
|
|
389
|
+
return withXAIOAuthIdentity(await pollOAuthDeviceCodeFlow({
|
|
390
|
+
poll: () => pollXAIDeviceToken(discovery.token_endpoint, device.deviceCode, fetchImpl, ctrl.signal),
|
|
391
|
+
intervalSeconds: device.intervalSeconds,
|
|
392
|
+
expiresInSeconds: device.expiresInSeconds,
|
|
393
|
+
signal: ctrl.signal
|
|
394
|
+
}), fetchImpl, ctrl.signal);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Refresh an xAI OAuth access token using a stored refresh_token.
|
|
398
|
+
*
|
|
399
|
+
* Re-runs OIDC discovery and re-validates the token endpoint before sending
|
|
400
|
+
* the stored refresh token.
|
|
401
|
+
*/
|
|
402
|
+
async function refreshXAIOAuthToken(refreshToken, fetchOverride) {
|
|
403
|
+
const fetchImpl = fetchOverride ?? fetch;
|
|
404
|
+
if (typeof refreshToken !== "string" || !refreshToken.trim()) throw new OAuthError("missing refresh_token", {
|
|
405
|
+
kind: "validation",
|
|
406
|
+
provider: "xai"
|
|
407
|
+
});
|
|
408
|
+
const response = await fetchImpl(validateXAIEndpoint((await xaiOAuthDiscovery(DISCOVERY_TIMEOUT_MS, fetchImpl)).token_endpoint, "token_endpoint"), {
|
|
409
|
+
method: "POST",
|
|
410
|
+
headers: {
|
|
411
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
412
|
+
Accept: "application/json"
|
|
413
|
+
},
|
|
414
|
+
body: new URLSearchParams({
|
|
415
|
+
grant_type: "refresh_token",
|
|
416
|
+
client_id: XAI_OAUTH_CLIENT_ID,
|
|
417
|
+
refresh_token: refreshToken
|
|
418
|
+
}),
|
|
419
|
+
redirect: "error",
|
|
420
|
+
signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
|
|
421
|
+
});
|
|
422
|
+
if (!response.ok) {
|
|
423
|
+
let detail = "";
|
|
424
|
+
try {
|
|
425
|
+
detail = (await response.text()).trim();
|
|
426
|
+
} catch {}
|
|
427
|
+
throw new OAuthError(`xAI token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`, {
|
|
428
|
+
kind: "token-refresh",
|
|
429
|
+
provider: "xai",
|
|
430
|
+
status: response.status
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
let payload;
|
|
434
|
+
try {
|
|
435
|
+
payload = await response.json();
|
|
436
|
+
} catch (error) {
|
|
437
|
+
throw new OAuthError(`xAI token refresh returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`, {
|
|
438
|
+
kind: "validation",
|
|
439
|
+
provider: "xai",
|
|
440
|
+
cause: error
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
return withXAIOAuthIdentity(parseXAITokenResponse(payload, "xAI token refresh response", refreshToken), fetchImpl);
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
export { xai_oauth_exports as a, getXAICliBillingHeaders as i, extractXAIAccessTokenSubject as n, fetchXAIOAuthIdentity as r, buildXAICliBillingUrl as t };
|