@gajae-code/ai 0.18.11 → 0.18.12
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 +68 -0
- package/README.md +1 -1
- package/dist/types/auth-broker/wire-schemas.d.ts +30 -0
- package/dist/types/auth-storage.d.ts +80 -8
- package/dist/types/bedrock-claude-cache-policy.d.ts +1 -1
- package/dist/types/model-cache.d.ts +6 -4
- package/dist/types/model-manager.d.ts +2 -0
- package/dist/types/openai-completions-compat.d.ts +2 -1
- package/dist/types/providers/anthropic.d.ts +10 -0
- package/dist/types/providers/aws-credential-config.d.ts +5 -0
- package/dist/types/providers/google-auth.d.ts +7 -0
- package/dist/types/providers/google-gemini-headers.d.ts +10 -5
- package/dist/types/providers/kiro-api-key.d.ts +5 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +2 -1
- package/dist/types/providers/openai-completions.d.ts +1 -1
- package/dist/types/providers/openai-responses-shared.d.ts +2 -1
- package/dist/types/stream.d.ts +33 -1
- package/dist/types/types.d.ts +26 -3
- package/dist/types/usage.d.ts +3 -0
- package/dist/types/utils/discovery/antigravity.d.ts +11 -0
- package/dist/types/utils/idle-iterator.d.ts +3 -2
- package/dist/types/utils/oauth/types.d.ts +9 -0
- package/package.json +4 -4
- package/src/auth-broker/remote-store.ts +62 -11
- package/src/auth-broker/wire-schemas.ts +2 -0
- package/src/auth-gateway/server.ts +3 -2
- package/src/auth-storage.ts +647 -194
- package/src/bedrock-claude-cache-policy.d.ts +1 -1
- package/src/bedrock-claude-cache-policy.ts +2 -2
- package/src/model-cache.ts +45 -10
- package/src/model-manager.ts +18 -6
- package/src/model-thinking.ts +37 -7
- package/src/models.json +2931 -1670
- package/src/openai-completions-compat.d.ts +2 -1
- package/src/openai-completions-compat.ts +5 -0
- package/src/provider-models/google.ts +7 -1
- package/src/provider-models/openai-compat.ts +26 -0
- package/src/providers/amazon-bedrock.ts +20 -11
- package/src/providers/anthropic.d.ts +10 -0
- package/src/providers/anthropic.ts +139 -4
- package/src/providers/aws-credential-config.d.ts +5 -0
- package/src/providers/aws-credential-config.ts +29 -1
- package/src/providers/google-auth.d.ts +7 -0
- package/src/providers/google-auth.ts +165 -4
- package/src/providers/google-gemini-headers.d.ts +10 -5
- package/src/providers/google-gemini-headers.ts +11 -6
- package/src/providers/google-vertex.ts +2 -4
- package/src/providers/kiro-api-key.d.ts +5 -0
- package/src/providers/kiro-api-key.ts +379 -68
- package/src/providers/ollama.ts +19 -9
- package/src/providers/openai-anthropic-shim.ts +1 -0
- package/src/providers/openai-chat-server-schema.ts +1 -1
- package/src/providers/openai-chat-server.ts +14 -2
- package/src/providers/openai-codex-responses.ts +21 -14
- package/src/providers/openai-completions.d.ts +1 -1
- package/src/providers/openai-completions.ts +65 -4
- package/src/providers/openai-responses-server.ts +10 -1
- package/src/providers/openai-responses-shared.d.ts +2 -1
- package/src/providers/openai-responses-shared.ts +42 -1
- package/src/providers/openai-responses.ts +51 -10
- package/src/providers/pi-native-server.ts +1 -1
- package/src/stream.d.ts +33 -1
- package/src/stream.ts +107 -13
- package/src/types.d.ts +26 -3
- package/src/types.ts +55 -2
- package/src/usage/claude.ts +4 -2
- package/src/usage.ts +3 -0
- package/src/utils/discovery/antigravity.ts +144 -2
- package/src/utils/idle-iterator.d.ts +3 -2
- package/src/utils/idle-iterator.ts +30 -10
- package/src/utils/oauth/anthropic.ts +23 -22
- package/src/utils/oauth/api-key-validation.ts +34 -11
- package/src/utils/oauth/types.d.ts +9 -0
- package/src/utils/oauth/types.ts +9 -0
- package/src/utils/parse-bind.d.ts +35 -0
|
@@ -23,11 +23,12 @@ export declare function getStreamIdleTimeoutRemediationMs(): number;
|
|
|
23
23
|
* Returns the idle timeout used for OpenAI-family streaming transports.
|
|
24
24
|
*
|
|
25
25
|
* Honors `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` first (`PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is the legacy alias). Set `=0` to disable.
|
|
26
|
+
* A configured model timeout takes precedence over the built-in provider/model floors; environment overrides win over it.
|
|
26
27
|
* When `provider` is given, long-reasoning hosts (xAI Grok and Grok Build) use that floor instead of the 120s default.
|
|
27
28
|
* Grok models reached through other OpenAI-compatible hosts (`openrouter/x-ai/grok-*`, kilo, litellm, …) get the
|
|
28
29
|
* same floor keyed on the model id, because long-reasoning silence is a property of the model (#4797).
|
|
29
30
|
*/
|
|
30
|
-
export declare function getOpenAIStreamIdleTimeoutMs(provider?: string, modelId?: string): number | undefined;
|
|
31
|
+
export declare function getOpenAIStreamIdleTimeoutMs(provider?: string, modelId?: string, configuredTimeoutMs?: number): number | undefined;
|
|
31
32
|
/**
|
|
32
33
|
* Returns the timeout used while waiting for the first stream event.
|
|
33
34
|
* The first token can legitimately take longer than later inter-event gaps,
|
|
@@ -57,7 +58,7 @@ export declare function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fal
|
|
|
57
58
|
* window so a short post-connect first-event budget cannot kill legitimate
|
|
58
59
|
* slow setup.
|
|
59
60
|
*/
|
|
60
|
-
export declare function resolveOpenAISdkRequestTimeoutMs(provider: string, streamFirstEventTimeoutOverride?: number, modelId?: string): number | undefined;
|
|
61
|
+
export declare function resolveOpenAISdkRequestTimeoutMs(provider: string, streamFirstEventTimeoutOverride?: number, modelId?: string, configuredIdleTimeoutMs?: number, effectiveIdleTimeoutOverride?: number): number | undefined;
|
|
61
62
|
/**
|
|
62
63
|
* Resolves the Anthropic SDK client `timeout` so stalled-before-headers requests
|
|
63
64
|
* are bounded. The Anthropic first-event watchdog deliberately arms only once
|
|
@@ -46,7 +46,7 @@ export function getProviderFirstEventTimeoutFallbackMs(provider: string): number
|
|
|
46
46
|
return provider === "kimi-code" ? KIMI_CODE_FIRST_EVENT_TIMEOUT_MS : undefined;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function normalizeIdleTimeoutMs(value: string | undefined, fallback: number): number | undefined {
|
|
49
|
+
function normalizeIdleTimeoutMs(value: string | undefined, fallback: number | undefined): number | undefined {
|
|
50
50
|
if (value === undefined) return fallback;
|
|
51
51
|
const parsed = Number(value);
|
|
52
52
|
if (!Number.isFinite(parsed)) return fallback;
|
|
@@ -86,16 +86,29 @@ export function getStreamIdleTimeoutRemediationMs(): number {
|
|
|
86
86
|
* Returns the idle timeout used for OpenAI-family streaming transports.
|
|
87
87
|
*
|
|
88
88
|
* Honors `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` first (`PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is the legacy alias). Set `=0` to disable.
|
|
89
|
+
* A configured model timeout takes precedence over the built-in provider/model floors; environment overrides win over it.
|
|
89
90
|
* When `provider` is given, long-reasoning hosts (xAI Grok and Grok Build) use that floor instead of the 120s default.
|
|
90
91
|
* Grok models reached through other OpenAI-compatible hosts (`openrouter/x-ai/grok-*`, kilo, litellm, …) get the
|
|
91
92
|
* same floor keyed on the model id, because long-reasoning silence is a property of the model (#4797).
|
|
92
93
|
*/
|
|
93
|
-
export function getOpenAIStreamIdleTimeoutMs(
|
|
94
|
+
export function getOpenAIStreamIdleTimeoutMs(
|
|
95
|
+
provider?: string,
|
|
96
|
+
modelId?: string,
|
|
97
|
+
configuredTimeoutMs?: number,
|
|
98
|
+
): number | undefined {
|
|
99
|
+
const builtInFallback =
|
|
100
|
+
getProviderStreamIdleTimeoutFallbackMs(provider ?? "") ??
|
|
101
|
+
(isGrokModelId(modelId) ? LONG_REASONING_STREAM_IDLE_TIMEOUT_MS : undefined) ??
|
|
102
|
+
DEFAULT_STREAM_IDLE_TIMEOUT_MS;
|
|
103
|
+
let configuredFallback: number | undefined = builtInFallback;
|
|
104
|
+
if (configuredTimeoutMs === 0) {
|
|
105
|
+
configuredFallback = undefined;
|
|
106
|
+
} else if (configuredTimeoutMs !== undefined && Number.isFinite(configuredTimeoutMs) && configuredTimeoutMs > 0) {
|
|
107
|
+
configuredFallback = Math.max(1, Math.trunc(configuredTimeoutMs));
|
|
108
|
+
}
|
|
94
109
|
return normalizeIdleTimeoutMs(
|
|
95
110
|
$env.GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS,
|
|
96
|
-
|
|
97
|
-
(isGrokModelId(modelId) ? LONG_REASONING_STREAM_IDLE_TIMEOUT_MS : undefined) ??
|
|
98
|
-
DEFAULT_STREAM_IDLE_TIMEOUT_MS,
|
|
111
|
+
configuredFallback,
|
|
99
112
|
);
|
|
100
113
|
}
|
|
101
114
|
|
|
@@ -139,19 +152,26 @@ export function resolveOpenAISdkRequestTimeoutMs(
|
|
|
139
152
|
provider: string,
|
|
140
153
|
streamFirstEventTimeoutOverride?: number,
|
|
141
154
|
modelId?: string,
|
|
155
|
+
configuredIdleTimeoutMs?: number,
|
|
156
|
+
effectiveIdleTimeoutOverride?: number,
|
|
142
157
|
): number | undefined {
|
|
143
158
|
const providerFirstEventFallbackMs = getProviderFirstEventTimeoutFallbackMs(provider);
|
|
144
159
|
const envSdkTimeoutMs = getStreamFirstEventTimeoutMs(
|
|
145
|
-
getOpenAIStreamIdleTimeoutMs(provider, modelId),
|
|
160
|
+
effectiveIdleTimeoutOverride ?? getOpenAIStreamIdleTimeoutMs(provider, modelId, configuredIdleTimeoutMs),
|
|
146
161
|
providerFirstEventFallbackMs,
|
|
147
162
|
);
|
|
148
163
|
if (streamFirstEventTimeoutOverride === 0) return undefined;
|
|
164
|
+
let sdkTimeoutMs: number | undefined;
|
|
149
165
|
if (streamFirstEventTimeoutOverride !== undefined) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
166
|
+
sdkTimeoutMs =
|
|
167
|
+
providerFirstEventFallbackMs !== undefined
|
|
168
|
+
? streamFirstEventTimeoutOverride
|
|
169
|
+
: Math.max(envSdkTimeoutMs ?? 0, streamFirstEventTimeoutOverride);
|
|
170
|
+
} else {
|
|
171
|
+
sdkTimeoutMs = envSdkTimeoutMs;
|
|
153
172
|
}
|
|
154
|
-
return
|
|
173
|
+
if (sdkTimeoutMs === undefined || !Number.isFinite(sdkTimeoutMs) || sdkTimeoutMs <= 0) return undefined;
|
|
174
|
+
return Math.min(Math.trunc(sdkTimeoutMs), MAX_TIMER_DELAY_MS);
|
|
155
175
|
}
|
|
156
176
|
|
|
157
177
|
/**
|
|
@@ -62,15 +62,16 @@ async function postJson(url: string, body: Record<string, string | number>): Pro
|
|
|
62
62
|
/**
|
|
63
63
|
* Decoded shape of Anthropic's `/v1/oauth/token` response (both
|
|
64
64
|
* `authorization_code` exchange and `refresh_token` refresh return the same
|
|
65
|
-
* envelope). The `account`
|
|
66
|
-
* surface `accountId` / `email`
|
|
67
|
-
* `/api/oauth/profile` round-trip.
|
|
65
|
+
* envelope). The `account` and `organization` blocks are inlined alongside the
|
|
66
|
+
* tokens, so we can surface `accountId` / `email` / `organizationId` on
|
|
67
|
+
* {@link OAuthCredentials} without a separate `/api/oauth/profile` round-trip.
|
|
68
68
|
*/
|
|
69
69
|
interface AnthropicTokenResponse {
|
|
70
70
|
access_token: string;
|
|
71
71
|
refresh_token: string;
|
|
72
72
|
expires_in: number;
|
|
73
73
|
account?: { uuid?: string; email_address?: string };
|
|
74
|
+
organization?: { uuid?: string; name?: string };
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
function parseOAuthTokenResponse(responseBody: string, operation: string): AnthropicTokenResponse {
|
|
@@ -83,22 +84,26 @@ function parseOAuthTokenResponse(responseBody: string, operation: string): Anthr
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
88
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
/**
|
|
87
|
-
* Lift the OAuth response's `account: { uuid, email_address }`
|
|
88
|
-
* {@link OAuthCredentials} so
|
|
89
|
-
* `metadata.user_id.account_uuid`, usage
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* Lift the OAuth response's `account: { uuid, email_address }` and
|
|
93
|
+
* `organization: { uuid, name }` blocks onto {@link OAuthCredentials} so
|
|
94
|
+
* downstream identity propagation (e.g. `metadata.user_id.account_uuid`, usage
|
|
95
|
+
* tracking, one stored credential per organization) works without a separate
|
|
96
|
+
* `/api/oauth/profile` round-trip. Returns `undefined` for any field the
|
|
97
|
+
* response omits or carries as a non-string / empty value.
|
|
92
98
|
*/
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
} {
|
|
97
|
-
const accountUuid = data.account?.uuid;
|
|
98
|
-
const emailAddress = data.account?.email_address;
|
|
99
|
+
function extractIdentityFromTokenResponse(
|
|
100
|
+
data: AnthropicTokenResponse,
|
|
101
|
+
): Pick<OAuthCredentials, "accountId" | "email" | "organizationId" | "organizationName"> {
|
|
99
102
|
return {
|
|
100
|
-
accountId:
|
|
101
|
-
email:
|
|
103
|
+
accountId: nonEmptyString(data.account?.uuid),
|
|
104
|
+
email: nonEmptyString(data.account?.email_address),
|
|
105
|
+
organizationId: nonEmptyString(data.organization?.uuid),
|
|
106
|
+
organizationName: nonEmptyString(data.organization?.name),
|
|
102
107
|
};
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -182,14 +187,12 @@ export class AnthropicOAuthFlow extends OAuthCallbackFlow {
|
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
const tokenData = parseOAuthTokenResponse(responseBody, "token exchange");
|
|
185
|
-
const { accountId, email } = extractAccountFromTokenResponse(tokenData);
|
|
186
190
|
|
|
187
191
|
return {
|
|
188
192
|
refresh: tokenData.refresh_token,
|
|
189
193
|
access: tokenData.access_token,
|
|
190
194
|
expires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,
|
|
191
|
-
|
|
192
|
-
email,
|
|
195
|
+
...extractIdentityFromTokenResponse(tokenData),
|
|
193
196
|
};
|
|
194
197
|
}
|
|
195
198
|
}
|
|
@@ -221,13 +224,11 @@ export async function refreshAnthropicToken(refreshToken: string): Promise<OAuth
|
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
const data = parseOAuthTokenResponse(responseBody, "token refresh");
|
|
224
|
-
const { accountId, email } = extractAccountFromTokenResponse(data);
|
|
225
227
|
|
|
226
228
|
return {
|
|
227
229
|
refresh: data.refresh_token || refreshToken,
|
|
228
230
|
access: data.access_token,
|
|
229
231
|
expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
|
|
230
|
-
|
|
231
|
-
email,
|
|
232
|
+
...extractIdentityFromTokenResponse(data),
|
|
232
233
|
};
|
|
233
234
|
}
|
|
@@ -193,19 +193,42 @@ export async function validateOpenAICompatibleApiKey(options: OpenAICompatibleVa
|
|
|
193
193
|
typeof parsed === "object" && parsed !== null && "choices" in parsed
|
|
194
194
|
? (parsed as { choices?: unknown }).choices
|
|
195
195
|
: undefined;
|
|
196
|
-
|
|
197
|
-
const message =
|
|
198
|
-
typeof firstChoice === "object" &&
|
|
199
|
-
firstChoice !== null &&
|
|
200
|
-
"message" in firstChoice &&
|
|
201
|
-
typeof (firstChoice as { message?: unknown }).message === "object" &&
|
|
202
|
-
!Array.isArray((firstChoice as { message?: unknown }).message)
|
|
203
|
-
? (firstChoice as { message: Record<string, unknown> }).message
|
|
204
|
-
: undefined;
|
|
205
|
-
const content = message?.content;
|
|
206
|
-
if (!(typeof content === "string" && content.trim().length > 0)) {
|
|
196
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
207
197
|
throw validationFailure(options.provider, options.apiKey, "the inference probe returned no choices");
|
|
208
198
|
}
|
|
199
|
+
const firstChoice: unknown = choices[0];
|
|
200
|
+
if (
|
|
201
|
+
typeof firstChoice !== "object" ||
|
|
202
|
+
firstChoice === null ||
|
|
203
|
+
Array.isArray(firstChoice) ||
|
|
204
|
+
!("message" in firstChoice)
|
|
205
|
+
) {
|
|
206
|
+
throw validationFailure(
|
|
207
|
+
options.provider,
|
|
208
|
+
options.apiKey,
|
|
209
|
+
"the inference probe returned choices without a valid message",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const choice = firstChoice as Record<string, unknown>;
|
|
213
|
+
const rawMessage = choice.message;
|
|
214
|
+
if (typeof rawMessage !== "object" || rawMessage === null || Array.isArray(rawMessage)) {
|
|
215
|
+
throw validationFailure(
|
|
216
|
+
options.provider,
|
|
217
|
+
options.apiKey,
|
|
218
|
+
"the inference probe returned choices without a valid message",
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const message = rawMessage as Record<string, unknown>;
|
|
222
|
+
const hasContent = typeof message.content === "string" && message.content.trim().length > 0;
|
|
223
|
+
const hasReasoningContent =
|
|
224
|
+
typeof message.reasoning_content === "string" && message.reasoning_content.trim().length > 0;
|
|
225
|
+
if (!hasContent && !hasReasoningContent && choice.finish_reason !== "length") {
|
|
226
|
+
throw validationFailure(
|
|
227
|
+
options.provider,
|
|
228
|
+
options.apiKey,
|
|
229
|
+
"the inference probe returned choices with empty content",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
209
232
|
}
|
|
210
233
|
return;
|
|
211
234
|
}
|
|
@@ -7,6 +7,15 @@ export type OAuthCredentials = {
|
|
|
7
7
|
email?: string;
|
|
8
8
|
accountId?: string;
|
|
9
9
|
profileArn?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Organization the token is scoped to. One Anthropic account can belong to
|
|
12
|
+
* several organizations (e.g. a Team workspace and a personal Max plan); each
|
|
13
|
+
* login is bound to exactly one of them, so the organization is part of the
|
|
14
|
+
* credential's identity.
|
|
15
|
+
*/
|
|
16
|
+
organizationId?: string;
|
|
17
|
+
/** Display name of {@link organizationId}. */
|
|
18
|
+
organizationName?: string;
|
|
10
19
|
};
|
|
11
20
|
export type OAuthProvider = "kiro" | "alibaba-token-plan" | "anthropic" | "bizrouter" | "mara" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "commandcode-goat" | "opengateway" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sglang" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "opencodex" | "zai";
|
|
12
21
|
export type OAuthProviderId = OAuthProvider | (string & {});
|
package/src/utils/oauth/types.ts
CHANGED
|
@@ -7,6 +7,15 @@ export type OAuthCredentials = {
|
|
|
7
7
|
email?: string;
|
|
8
8
|
accountId?: string;
|
|
9
9
|
profileArn?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Organization the token is scoped to. One Anthropic account can belong to
|
|
12
|
+
* several organizations (e.g. a Team workspace and a personal Max plan); each
|
|
13
|
+
* login is bound to exactly one of them, so the organization is part of the
|
|
14
|
+
* credential's identity.
|
|
15
|
+
*/
|
|
16
|
+
organizationId?: string;
|
|
17
|
+
/** Display name of {@link organizationId}. */
|
|
18
|
+
organizationName?: string;
|
|
10
19
|
};
|
|
11
20
|
|
|
12
21
|
export type OAuthProvider =
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ParsedBind {
|
|
2
|
+
hostname: string;
|
|
3
|
+
port: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse a `host:port` (or bare `port`, which assumes loopback) string.
|
|
7
|
+
*
|
|
8
|
+
* Accepts:
|
|
9
|
+
* - `"4000"` → `127.0.0.1:4000`
|
|
10
|
+
* - `"0.0.0.0:4000"` → as written
|
|
11
|
+
* - `"[::1]:4000"` → as written (brackets retained, Bun handles them)
|
|
12
|
+
*
|
|
13
|
+
* Rejects:
|
|
14
|
+
* - empty input
|
|
15
|
+
* - empty hostname (`":4000"`)
|
|
16
|
+
* - non-integer / out-of-range port
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseBind(raw: string): ParsedBind;
|
|
19
|
+
/** True for loopback-only hostnames the auth servers may bind without credentials. */
|
|
20
|
+
export declare function isLoopbackHostname(hostname: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Fail closed when an unauthenticated auth server (empty bearer token set)
|
|
23
|
+
* would bind a non-loopback address: that exposes credential operations to the
|
|
24
|
+
* network with no proof of possession.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertAuthenticatedOrLoopback(bind: ParsedBind, bearerTokenCount: number, serverName: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Tokenless loopback servers accept a request only when its Host header is the
|
|
29
|
+
* bound hostname and effective port. Default-port forms that stay allowed:
|
|
30
|
+
* a Host with no port, or an explicit :80, on an HTTP listener on port 80;
|
|
31
|
+
* an explicit :443 on a listener on port 443. Any other omitted port is
|
|
32
|
+
* rejected. A DNS-rebound same-origin GET sends the attacker's name and no
|
|
33
|
+
* Origin header.
|
|
34
|
+
*/
|
|
35
|
+
export declare function hostHeaderMatchesBind(hostHeader: string | null, bind: ParsedBind): boolean;
|