@jini-ai/agent-runtime 0.3.9 → 0.4.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 +103 -0
- package/README.md +34 -17
- package/dist/acp-model-probe.d.ts +6 -4
- package/dist/acp-model-probe.js +3 -3
- package/dist/agent-protocol/acp/account-failure.d.ts +4 -2
- package/dist/agent-protocol/acp/account-failure.js +1 -1
- package/dist/agent-protocol/acp/json.d.ts +6 -6
- package/dist/agent-protocol/acp/json.js +3 -3
- package/dist/agent-protocol/acp/models.d.ts +10 -5
- package/dist/agent-protocol/acp/models.js +32 -31
- package/dist/agent-protocol/acp/session-params.d.ts +3 -1
- package/dist/agent-protocol/acp/session-params.js +2 -1
- package/dist/agent-protocol/acp/session.d.ts +28 -22
- package/dist/agent-protocol/acp/session.js +181 -147
- package/dist/agent-protocol/acp/text-suppression.d.ts +1 -1
- package/dist/agent-protocol/acp/text-suppression.js +1 -1
- package/dist/agent-protocol/acp/types.d.ts +3 -2
- package/dist/agent-protocol/acp/updates.d.ts +11 -11
- package/dist/agent-protocol/acp/updates.js +2 -2
- package/dist/agent-protocol/core/json-line-stream.d.ts +24 -8
- package/dist/agent-protocol/core/json-line-stream.js +8 -8
- package/dist/agent-protocol/pi-rpc/events.d.ts +5 -1
- package/dist/agent-protocol/pi-rpc/events.js +36 -36
- package/dist/agent-protocol/pi-rpc/internal.d.ts +4 -1
- package/dist/agent-protocol/pi-rpc/models.d.ts +3 -1
- package/dist/agent-protocol/pi-rpc/models.js +1 -1
- package/dist/agent-protocol/pi-rpc/session.d.ts +1 -1
- package/dist/agent-protocol/pi-rpc/session.js +33 -32
- package/dist/amr-model-cache.d.ts +12 -4
- package/dist/amr-model-cache.js +17 -21
- package/dist/amr-profile-resolver.d.ts +3 -1
- package/dist/anthropic-live-models.js +0 -0
- package/dist/artifact-taxonomy.d.ts +6 -2
- package/dist/auth.d.ts +52 -15
- package/dist/auth.js +36 -36
- package/dist/claude-code-models.js +2 -2
- package/dist/claude-stream.d.ts +22 -3
- package/dist/claude-stream.js +24 -8
- package/dist/copilot-stream.d.ts +7 -3
- package/dist/copilot-stream.js +3 -3
- package/dist/defs/aider.d.ts +8 -1
- package/dist/defs/aider.js +2 -2
- package/dist/defs/amp.d.ts +8 -1
- package/dist/defs/amp.js +2 -2
- package/dist/defs/amr.d.ts +22 -6
- package/dist/defs/amr.js +23 -23
- package/dist/defs/antigravity.d.ts +15 -4
- package/dist/defs/antigravity.js +9 -8
- package/dist/defs/auggie.d.ts +4 -1
- package/dist/defs/auggie.js +1 -1
- package/dist/defs/claude.d.ts +16 -2
- package/dist/defs/claude.js +22 -4
- package/dist/defs/cline.d.ts +4 -1
- package/dist/defs/cline.js +1 -1
- package/dist/defs/codebuddy.d.ts +8 -1
- package/dist/defs/codebuddy.js +2 -2
- package/dist/defs/codex.d.ts +18 -4
- package/dist/defs/codex.js +7 -7
- package/dist/defs/copilot.d.ts +8 -1
- package/dist/defs/copilot.js +2 -2
- package/dist/defs/crush.d.ts +8 -1
- package/dist/defs/crush.js +1 -1
- package/dist/defs/cursor-agent.d.ts +14 -3
- package/dist/defs/cursor-agent.js +5 -5
- package/dist/defs/deepseek.d.ts +8 -1
- package/dist/defs/deepseek.js +2 -2
- package/dist/defs/devin.d.ts +12 -2
- package/dist/defs/devin.js +3 -3
- package/dist/defs/droid.d.ts +8 -1
- package/dist/defs/droid.js +1 -1
- package/dist/defs/gemini.d.ts +8 -1
- package/dist/defs/gemini.js +1 -1
- package/dist/defs/goose.d.ts +4 -1
- package/dist/defs/goose.js +1 -1
- package/dist/defs/grok-build.d.ts +11 -2
- package/dist/defs/grok-build.js +3 -3
- package/dist/defs/hermes.d.ts +4 -1
- package/dist/defs/hermes.js +2 -2
- package/dist/defs/kilo.d.ts +4 -1
- package/dist/defs/kilo.js +2 -2
- package/dist/defs/kimi.d.ts +4 -1
- package/dist/defs/kimi.js +2 -2
- package/dist/defs/kiro.d.ts +4 -1
- package/dist/defs/kiro.js +2 -2
- package/dist/defs/mimo.d.ts +8 -1
- package/dist/defs/mimo.js +2 -2
- package/dist/defs/opencode.d.ts +9 -2
- package/dist/defs/opencode.js +2 -2
- package/dist/defs/pi.d.ts +12 -2
- package/dist/defs/pi.js +9 -9
- package/dist/defs/qoder.d.ts +8 -1
- package/dist/defs/qoder.js +2 -2
- package/dist/defs/qwen.d.ts +8 -1
- package/dist/defs/qwen.js +2 -2
- package/dist/defs/reasonix.d.ts +4 -1
- package/dist/defs/reasonix.js +3 -3
- package/dist/defs/shared.d.ts +4 -2
- package/dist/defs/shared.js +2 -2
- package/dist/defs/trae-cli.d.ts +12 -2
- package/dist/defs/trae-cli.js +3 -3
- package/dist/defs/vibe.d.ts +4 -1
- package/dist/defs/vibe.js +2 -2
- package/dist/detection.d.ts +38 -3
- package/dist/detection.js +77 -37
- package/dist/diagnostics.d.ts +14 -3
- package/dist/diagnostics.js +5 -5
- package/dist/env.d.ts +16 -4
- package/dist/env.js +7 -7
- package/dist/executables.d.ts +24 -8
- package/dist/executables.js +13 -13
- package/dist/index.d.ts +228 -4
- package/dist/index.js +230 -6
- package/dist/invocation.d.ts +6 -1
- package/dist/invocation.js +2 -2
- package/dist/json-event-stream.d.ts +7 -2
- package/dist/json-event-stream.js +3 -3
- package/dist/launch.d.ts +12 -2
- package/dist/launch.js +3 -3
- package/dist/log-file.d.ts +4 -1
- package/dist/log-file.js +2 -2
- package/dist/mcp.d.ts +6 -3
- package/dist/mcp.js +1 -1
- package/dist/metadata.d.ts +7 -3
- package/dist/metadata.js +1 -1
- package/dist/mmd-routes.d.ts +22 -6
- package/dist/mmd-routes.js +14 -14
- package/dist/model-catalog-cache.d.ts +50 -0
- package/dist/model-catalog-cache.js +61 -0
- package/dist/model-loading-state.d.ts +10 -0
- package/dist/model-loading-state.js +23 -0
- package/dist/model-registry.d.ts +28 -7
- package/dist/model-registry.js +11 -8
- package/dist/models.d.ts +31 -6
- package/dist/models.js +6 -6
- package/dist/opencode-log.d.ts +12 -4
- package/dist/opencode-log.js +8 -8
- package/dist/paths.d.ts +6 -2
- package/dist/paths.js +3 -3
- package/dist/pi-models.d.ts +3 -1
- package/dist/pi-models.js +2 -2
- package/dist/prompt-budget.d.ts +17 -4
- package/dist/prompt-budget.js +3 -3
- package/dist/prompt-file.d.ts +5 -1
- package/dist/prompt-file.js +1 -1
- package/dist/providers/aihubmix.d.ts +34 -9
- package/dist/providers/aihubmix.js +26 -26
- package/dist/providers/anthropic-messages.d.ts +77 -20
- package/dist/providers/anthropic-messages.js +53 -50
- package/dist/providers/azure-chat.d.ts +21 -7
- package/dist/providers/azure-chat.js +18 -16
- package/dist/providers/connection-guard.d.ts +45 -31
- package/dist/providers/connection-guard.js +40 -143
- package/dist/providers/connection-test.d.ts +1 -1
- package/dist/providers/connection-test.js +13 -30
- package/dist/providers/elevenlabs.d.ts +7 -2
- package/dist/providers/elevenlabs.js +4 -4
- package/dist/providers/google-messages.d.ts +44 -10
- package/dist/providers/google-messages.js +36 -34
- package/dist/providers/google-schema.d.ts +26 -0
- package/dist/providers/google-schema.js +253 -0
- package/dist/providers/google.d.ts +21 -6
- package/dist/providers/google.js +10 -10
- package/dist/providers/index.d.ts +3 -2
- package/dist/providers/index.js +3 -2
- package/dist/providers/model-catalog.d.ts +1 -1
- package/dist/providers/model-catalog.js +14 -27
- package/dist/providers/oauth-callback-server.d.ts +1 -1
- package/dist/providers/oauth-callback-server.js +3 -2
- package/dist/providers/oauth-credentials.d.ts +7 -1
- package/dist/providers/oauth-credentials.js +5 -5
- package/dist/providers/oauth-provider.d.ts +2 -2
- package/dist/providers/oauth-provider.js +20 -25
- package/dist/providers/oauth-tokens.d.ts +26 -6
- package/dist/providers/oauth-tokens.js +9 -9
- package/dist/providers/ollama-chat.d.ts +66 -31
- package/dist/providers/ollama-chat.js +64 -61
- package/dist/providers/openai-chat.d.ts +77 -22
- package/dist/providers/openai-chat.js +71 -67
- package/dist/providers/pkce.d.ts +33 -20
- package/dist/providers/pkce.js +75 -146
- package/dist/providers/sse-decode.d.ts +3 -1
- package/dist/providers/sse-decode.js +1 -1
- package/dist/providers/token-params.d.ts +16 -5
- package/dist/providers/token-params.js +6 -6
- package/dist/providers/tool-turn-types.d.ts +89 -0
- package/dist/providers/tool-turn-types.js +2 -0
- package/dist/providers/tool-turn.d.ts +10 -0
- package/dist/providers/tool-turn.js +252 -0
- package/dist/qoder-stream.d.ts +7 -3
- package/dist/qoder-stream.js +3 -3
- package/dist/registry.d.ts +6 -2
- package/dist/registry.js +4 -4
- package/dist/resolution.d.ts +5 -1
- package/dist/resolution.js +3 -3
- package/dist/role-marker-guard.d.ts +9 -5
- package/dist/role-marker-guard.js +5 -5
- package/dist/terminal-launch.d.ts +6 -1
- package/dist/terminal-launch.js +2 -2
- package/dist/types.d.ts +37 -6
- package/package.json +43 -4
package/dist/providers/google.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* verbatim.
|
|
7
7
|
*/
|
|
8
8
|
/** Strips any trailing `/v1`/`/v1beta` API-version segment and trailing slashes from a configured base URL. */
|
|
9
|
-
export function googleGenerativeLanguageBaseUrl(baseUrl) {
|
|
9
|
+
export function googleGenerativeLanguageBaseUrl({ baseUrl }) {
|
|
10
10
|
const url = new URL(baseUrl);
|
|
11
11
|
url.search = '';
|
|
12
12
|
url.hash = '';
|
|
@@ -17,25 +17,25 @@ export function googleGenerativeLanguageBaseUrl(baseUrl) {
|
|
|
17
17
|
return url.toString().replace(/\/+$/, '');
|
|
18
18
|
}
|
|
19
19
|
/** Strips the `models/` prefix Google's API sometimes includes in a model id. */
|
|
20
|
-
export function normalizeGoogleModelId(model) {
|
|
20
|
+
export function normalizeGoogleModelId({ model }) {
|
|
21
21
|
const trimmed = model.trim();
|
|
22
22
|
return trimmed.startsWith('models/') ? trimmed.slice('models/'.length) : trimmed;
|
|
23
23
|
}
|
|
24
24
|
/** URL-encodes a normalized model id for use as a path segment. */
|
|
25
|
-
export function googleModelPathSegment(model) {
|
|
26
|
-
return encodeURIComponent(normalizeGoogleModelId(model));
|
|
25
|
+
export function googleModelPathSegment({ model }) {
|
|
26
|
+
return encodeURIComponent(normalizeGoogleModelId({ model: model }));
|
|
27
27
|
}
|
|
28
28
|
/** Builds the non-streaming `generateContent` endpoint URL for a model. */
|
|
29
|
-
export function googleGenerateContentUrl(baseUrl, model) {
|
|
30
|
-
return `${googleGenerativeLanguageBaseUrl(baseUrl)}/v1beta/models/${googleModelPathSegment(model)}:generateContent`;
|
|
29
|
+
export function googleGenerateContentUrl({ baseUrl, model }) {
|
|
30
|
+
return `${googleGenerativeLanguageBaseUrl({ baseUrl: baseUrl })}/v1beta/models/${googleModelPathSegment({ model: model })}:generateContent`;
|
|
31
31
|
}
|
|
32
32
|
/** Builds the SSE-streaming `streamGenerateContent` endpoint URL for a model. */
|
|
33
|
-
export function googleStreamGenerateContentUrl(baseUrl, model) {
|
|
34
|
-
return `${googleGenerativeLanguageBaseUrl(baseUrl)}/v1beta/models/${googleModelPathSegment(model)}:streamGenerateContent?alt=sse`;
|
|
33
|
+
export function googleStreamGenerateContentUrl({ baseUrl, model }) {
|
|
34
|
+
return `${googleGenerativeLanguageBaseUrl({ baseUrl: baseUrl })}/v1beta/models/${googleModelPathSegment({ model: model })}:streamGenerateContent?alt=sse`;
|
|
35
35
|
}
|
|
36
36
|
/** Builds the model-catalog listing endpoint URL, with the API key attached as a query param. */
|
|
37
|
-
export function googleProviderModelsUrl(baseUrl, apiKey) {
|
|
38
|
-
const url = new URL(`${googleGenerativeLanguageBaseUrl(baseUrl)}/v1beta/models`);
|
|
37
|
+
export function googleProviderModelsUrl({ baseUrl, apiKey }) {
|
|
38
|
+
const url = new URL(`${googleGenerativeLanguageBaseUrl({ baseUrl: baseUrl })}/v1beta/models`);
|
|
39
39
|
url.searchParams.set('key', apiKey);
|
|
40
40
|
return url.toString();
|
|
41
41
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module providers
|
|
3
3
|
*
|
|
4
4
|
* Generic LLM-provider integration material ported from OD's
|
|
5
|
-
* `apps/daemon/src/integrations/` (13 files). See `
|
|
5
|
+
* `apps/daemon/src/integrations/` (13 files). See `archived provenance ledger` for the
|
|
6
6
|
* full per-file provenance and the MIXED-classification split: OD's own
|
|
7
7
|
* AMR/vela provider adapter (`vela*.ts`, 4 files) is a specific provider's
|
|
8
8
|
* own daemon-coupled implementation, not generic material, and was left
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* `chat.ts` (this task did not have direct access to that file for the
|
|
16
16
|
* 2026-07-21 pass; the 2026-07-22 pass did read the real OD source for
|
|
17
17
|
* Azure's api-version default — see `azure-chat.ts`'s header) — see each
|
|
18
|
-
* module's own header and `
|
|
18
|
+
* module's own header and `archived provenance ledger`'s dated sections for the full
|
|
19
19
|
* rationale.
|
|
20
20
|
*/
|
|
21
21
|
export * from './types.js';
|
|
@@ -37,4 +37,5 @@ export * from './oauth-provider.js';
|
|
|
37
37
|
export * from './oauth-callback-server.js';
|
|
38
38
|
export * from './oauth-tokens.js';
|
|
39
39
|
export * from './oauth-credentials.js';
|
|
40
|
+
export * from './tool-turn.js';
|
|
40
41
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/providers/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module providers
|
|
3
3
|
*
|
|
4
4
|
* Generic LLM-provider integration material ported from OD's
|
|
5
|
-
* `apps/daemon/src/integrations/` (13 files). See `
|
|
5
|
+
* `apps/daemon/src/integrations/` (13 files). See `archived provenance ledger` for the
|
|
6
6
|
* full per-file provenance and the MIXED-classification split: OD's own
|
|
7
7
|
* AMR/vela provider adapter (`vela*.ts`, 4 files) is a specific provider's
|
|
8
8
|
* own daemon-coupled implementation, not generic material, and was left
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* `chat.ts` (this task did not have direct access to that file for the
|
|
16
16
|
* 2026-07-21 pass; the 2026-07-22 pass did read the real OD source for
|
|
17
17
|
* Azure's api-version default — see `azure-chat.ts`'s header) — see each
|
|
18
|
-
* module's own header and `
|
|
18
|
+
* module's own header and `archived provenance ledger`'s dated sections for the full
|
|
19
19
|
* rationale.
|
|
20
20
|
*/
|
|
21
21
|
export * from './types.js';
|
|
@@ -37,4 +37,5 @@ export * from './oauth-provider.js';
|
|
|
37
37
|
export * from './oauth-callback-server.js';
|
|
38
38
|
export * from './oauth-tokens.js';
|
|
39
39
|
export * from './oauth-credentials.js';
|
|
40
|
+
export * from './tool-turn.js';
|
|
40
41
|
//# sourceMappingURL=index.js.map
|
|
@@ -28,5 +28,5 @@ export type ProviderModelsInput = ProviderModelsRequest & {
|
|
|
28
28
|
* "unsupported" response for Azure), and normalizes every provider's
|
|
29
29
|
* response envelope into a de-duplicated, alphabetized `{ id, label }[]`.
|
|
30
30
|
*/
|
|
31
|
-
export declare function listProviderModels(
|
|
31
|
+
export declare function listProviderModels(requiredArgs: Pick<ProviderModelsInput, "protocol" | "baseUrl" | "apiKey">, optionalArgs?: Omit<ProviderModelsInput, "protocol" | "baseUrl" | "apiKey">): Promise<ProviderModelsResponse>;
|
|
32
32
|
//# sourceMappingURL=model-catalog.d.ts.map
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Live model-catalog discovery for BYOK providers (OpenAI-compatible,
|
|
5
|
-
* Anthropic, Google, AIHubMix, SenseAudio, plus static seeds for Bedrock and
|
|
6
|
-
* an explicit "unsupported" response for Azure). Ported from OD's
|
|
7
|
-
* `integrations/provider-models.ts` (`listProviderModels`) with its
|
|
8
|
-
* upstream contracts-package type imports repointed at this package's own
|
|
9
|
-
* vendored `types.ts`, and its `../connectionTest.js` SSRF-guard /
|
|
10
|
-
* secret-redaction imports repointed at this package's own vendored
|
|
11
|
-
* `connection-guard.ts` (see that module's doc comment for what was and
|
|
12
|
-
* wasn't pulled from the 2,600-line origin file). Logic is otherwise
|
|
13
|
-
* unchanged — product-neutral as found (a bare BYOK-protocol model lister,
|
|
14
|
-
* no OD-branded strings).
|
|
15
|
-
*/
|
|
16
|
-
import { defaultDnsLookup, redactSecrets, validateBaseUrlResolved } from './connection-guard.js';
|
|
1
|
+
import { redactSecrets } from '@jini-ai/core';
|
|
2
|
+
import { defaultDnsLookup, validateBaseUrlResolved } from './connection-guard.js';
|
|
17
3
|
import { aihubmixCatalogUrl, aihubmixHeaders, parseAIHubMixCatalog } from './aihubmix.js';
|
|
18
4
|
import { googleProviderModelsUrl, normalizeGoogleModelId } from './google.js';
|
|
19
5
|
const PROVIDER_MODELS_TIMEOUT_MS = 12_000;
|
|
@@ -164,11 +150,11 @@ function extractAnthropicModels(data) {
|
|
|
164
150
|
}
|
|
165
151
|
function googleModelId(rawName, rawBaseModelId) {
|
|
166
152
|
if (typeof rawBaseModelId === 'string' && rawBaseModelId.trim()) {
|
|
167
|
-
return normalizeGoogleModelId(rawBaseModelId);
|
|
153
|
+
return normalizeGoogleModelId({ model: rawBaseModelId });
|
|
168
154
|
}
|
|
169
155
|
if (typeof rawName !== 'string')
|
|
170
156
|
return '';
|
|
171
|
-
return normalizeGoogleModelId(rawName);
|
|
157
|
+
return normalizeGoogleModelId({ model: rawName });
|
|
172
158
|
}
|
|
173
159
|
function supportsGoogleGenerateContent(item) {
|
|
174
160
|
const methods = item
|
|
@@ -201,7 +187,7 @@ function providerModelsUrl(protocol, baseUrl, apiKey) {
|
|
|
201
187
|
if (protocol === 'aihubmix') {
|
|
202
188
|
// AIHubMix exposes its chat catalogue on a dedicated endpoint
|
|
203
189
|
// (GET /api/v1/models?type=llm), not the OpenAI /v1/models route.
|
|
204
|
-
return aihubmixCatalogUrl(baseUrl, 'llm');
|
|
190
|
+
return aihubmixCatalogUrl({ baseUrl: baseUrl, type: 'llm' });
|
|
205
191
|
}
|
|
206
192
|
if (protocol === 'openai' || protocol === 'senseaudio') {
|
|
207
193
|
return appendVersionedApiPath(baseUrl, '/models');
|
|
@@ -212,7 +198,7 @@ function providerModelsUrl(protocol, baseUrl, apiKey) {
|
|
|
212
198
|
return url.toString();
|
|
213
199
|
}
|
|
214
200
|
if (protocol === 'google') {
|
|
215
|
-
return googleProviderModelsUrl(baseUrl, apiKey);
|
|
201
|
+
return googleProviderModelsUrl({ baseUrl: baseUrl, apiKey: apiKey });
|
|
216
202
|
}
|
|
217
203
|
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
218
204
|
}
|
|
@@ -224,7 +210,7 @@ function providerModelsHeaders(protocol, apiKey) {
|
|
|
224
210
|
// The catalogue is public — only attach Bearer auth (+ APP-Code) when the
|
|
225
211
|
// caller actually supplied a key. An empty `Bearer ` would be rejected by
|
|
226
212
|
// some gateways, so send no headers when the key is blank.
|
|
227
|
-
return apiKey.trim() ? aihubmixHeaders(apiKey) : {};
|
|
213
|
+
return apiKey.trim() ? aihubmixHeaders({ apiKey: apiKey }) : {};
|
|
228
214
|
}
|
|
229
215
|
if (protocol === 'anthropic') {
|
|
230
216
|
return {
|
|
@@ -242,7 +228,7 @@ function extractModels(protocol, data) {
|
|
|
242
228
|
// (e.g. gpt-image-2 -> "image_generation,llm") would otherwise leak in. Those
|
|
243
229
|
// belong to the dedicated image/video/audio pickers.
|
|
244
230
|
if (protocol === 'aihubmix')
|
|
245
|
-
return parseAIHubMixCatalog(data, { chatOnly: true });
|
|
231
|
+
return parseAIHubMixCatalog({ data: data }, { chatOnly: true });
|
|
246
232
|
if (protocol === 'openai' || protocol === 'senseaudio')
|
|
247
233
|
return extractOpenAiModels(data);
|
|
248
234
|
if (protocol === 'anthropic')
|
|
@@ -256,7 +242,8 @@ function extractModels(protocol, data) {
|
|
|
256
242
|
* "unsupported" response for Azure), and normalizes every provider's
|
|
257
243
|
* response envelope into a de-duplicated, alphabetized `{ id, label }[]`.
|
|
258
244
|
*/
|
|
259
|
-
export async function listProviderModels(
|
|
245
|
+
export async function listProviderModels(requiredArgs, optionalArgs = {}) {
|
|
246
|
+
const input = { ...optionalArgs, ...requiredArgs };
|
|
260
247
|
const start = Date.now();
|
|
261
248
|
if (input.protocol === 'azure') {
|
|
262
249
|
return {
|
|
@@ -266,7 +253,7 @@ export async function listProviderModels(input) {
|
|
|
266
253
|
detail: 'Azure OpenAI deployment discovery is not supported from the inference endpoint.',
|
|
267
254
|
};
|
|
268
255
|
}
|
|
269
|
-
const validated = await validateBaseUrlResolved(input.baseUrl, input.dnsLookup ?? defaultDnsLookup);
|
|
256
|
+
const validated = await validateBaseUrlResolved({ baseUrl: input.baseUrl, lookup: input.dnsLookup ?? defaultDnsLookup });
|
|
270
257
|
if (validated.error || !validated.parsed) {
|
|
271
258
|
return {
|
|
272
259
|
ok: false,
|
|
@@ -362,7 +349,7 @@ export async function listProviderModels(input) {
|
|
|
362
349
|
kind: statusToKind(response.status),
|
|
363
350
|
latencyMs,
|
|
364
351
|
status: response.status,
|
|
365
|
-
detail: redactSecrets(detail, [input.apiKey]),
|
|
352
|
+
detail: redactSecrets({ input: detail }, { exactSecrets: [input.apiKey] }),
|
|
366
353
|
};
|
|
367
354
|
}
|
|
368
355
|
if (parseError) {
|
|
@@ -371,7 +358,7 @@ export async function listProviderModels(input) {
|
|
|
371
358
|
kind: 'unknown',
|
|
372
359
|
latencyMs,
|
|
373
360
|
status: response.status,
|
|
374
|
-
detail: redactSecrets(parseError, [input.apiKey]),
|
|
361
|
+
detail: redactSecrets({ input: parseError }, { exactSecrets: [input.apiKey] }),
|
|
375
362
|
};
|
|
376
363
|
}
|
|
377
364
|
// Safe: `url` above only resolves for the five protocols
|
|
@@ -403,7 +390,7 @@ export async function listProviderModels(input) {
|
|
|
403
390
|
ok: false,
|
|
404
391
|
kind,
|
|
405
392
|
latencyMs,
|
|
406
|
-
detail: redactSecrets(message, [input.apiKey]),
|
|
393
|
+
detail: redactSecrets({ input: message }, { exactSecrets: [input.apiKey] }),
|
|
407
394
|
};
|
|
408
395
|
}
|
|
409
396
|
finally {
|
|
@@ -31,5 +31,5 @@ export interface OAuthCallbackListener {
|
|
|
31
31
|
* via `onCallback`. The listener self-closes after the first matching
|
|
32
32
|
* callback OR after `timeoutMs` (default 30 min), whichever comes first.
|
|
33
33
|
*/
|
|
34
|
-
export declare function startOAuthCallbackListener(
|
|
34
|
+
export declare function startOAuthCallbackListener(requiredArgs: Pick<StartOAuthCallbackListenerInput, "host" | "port" | "path" | "expectedState" | "onCallback">, optionalArgs?: Omit<StartOAuthCallbackListenerInput, "host" | "port" | "path" | "expectedState" | "onCallback">): Promise<OAuthCallbackListener>;
|
|
35
35
|
//# sourceMappingURL=oauth-callback-server.d.ts.map
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Ported from OD's `integrations/xai-oauth-server.ts`, de-branded: the
|
|
10
10
|
* origin hardcoded xAI's fixed callback port/host/path as module constants
|
|
11
11
|
* and had the origin product's own name baked into the result-page HTML
|
|
12
|
-
* (see `
|
|
12
|
+
* (see `archived provenance ledger` for the exact original strings). Both are now
|
|
13
13
|
* caller-supplied — `host`/`port`/
|
|
14
14
|
* `path` are required input fields instead of defaulted constants (a
|
|
15
15
|
* provider whose OAuth client_id is locked to a specific redirect URI, like
|
|
@@ -27,7 +27,8 @@ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000; // 30 min
|
|
|
27
27
|
* via `onCallback`. The listener self-closes after the first matching
|
|
28
28
|
* callback OR after `timeoutMs` (default 30 min), whichever comes first.
|
|
29
29
|
*/
|
|
30
|
-
export async function startOAuthCallbackListener(
|
|
30
|
+
export async function startOAuthCallbackListener(requiredArgs, optionalArgs = {}) {
|
|
31
|
+
const input = { ...optionalArgs, ...requiredArgs };
|
|
31
32
|
const { host, port, path: callbackPath } = input;
|
|
32
33
|
const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
33
34
|
let consumed = false;
|
|
@@ -25,5 +25,11 @@ export interface ResolvedOAuthCredential {
|
|
|
25
25
|
* the refresh call fails. Callers should treat null as "no OAuth available,
|
|
26
26
|
* fall back to API key / re-login UI".
|
|
27
27
|
*/
|
|
28
|
-
export declare function resolveOAuthBearer(config
|
|
28
|
+
export declare function resolveOAuthBearer({ config, tokenFileName, dataDir }: {
|
|
29
|
+
config: OAuthPkceProviderConfig;
|
|
30
|
+
tokenFileName: string;
|
|
31
|
+
dataDir: string;
|
|
32
|
+
}, { fetchImpl }?: {
|
|
33
|
+
fetchImpl?: typeof fetch;
|
|
34
|
+
}): Promise<ResolvedOAuthCredential | null>;
|
|
29
35
|
//# sourceMappingURL=oauth-credentials.d.ts.map
|
|
@@ -21,11 +21,11 @@ import { getStoredOAuthToken, isOAuthTokenExpired, setStoredOAuthToken, } from '
|
|
|
21
21
|
* the refresh call fails. Callers should treat null as "no OAuth available,
|
|
22
22
|
* fall back to API key / re-login UI".
|
|
23
23
|
*/
|
|
24
|
-
export async function resolveOAuthBearer(config, tokenFileName, dataDir, fetchImpl) {
|
|
25
|
-
const stored = await getStoredOAuthToken(dataDir, tokenFileName);
|
|
24
|
+
export async function resolveOAuthBearer({ config, tokenFileName, dataDir }, { fetchImpl } = {}) {
|
|
25
|
+
const stored = await getStoredOAuthToken({ dataDir: dataDir, fileName: tokenFileName });
|
|
26
26
|
if (!stored)
|
|
27
27
|
return null;
|
|
28
|
-
if (!isOAuthTokenExpired(stored)) {
|
|
28
|
+
if (!isOAuthTokenExpired({ token: stored })) {
|
|
29
29
|
return { accessToken: stored.accessToken, source: 'stored' };
|
|
30
30
|
}
|
|
31
31
|
// Within skew window — try a refresh. If there's no refresh_token (some
|
|
@@ -34,7 +34,7 @@ export async function resolveOAuthBearer(config, tokenFileName, dataDir, fetchIm
|
|
|
34
34
|
if (!stored.refreshToken)
|
|
35
35
|
return null;
|
|
36
36
|
try {
|
|
37
|
-
const fresh = await refreshOAuthPkceToken({
|
|
37
|
+
const fresh = await (({ config, refreshToken, ...optionalArgs }) => refreshOAuthPkceToken({ config, refreshToken }, optionalArgs))({
|
|
38
38
|
config,
|
|
39
39
|
refreshToken: stored.refreshToken,
|
|
40
40
|
...(fetchImpl ? { fetchImpl } : {}),
|
|
@@ -57,7 +57,7 @@ export async function resolveOAuthBearer(config, tokenFileName, dataDir, fetchIm
|
|
|
57
57
|
}
|
|
58
58
|
if (fresh.scope)
|
|
59
59
|
next.scope = fresh.scope;
|
|
60
|
-
await setStoredOAuthToken(dataDir, tokenFileName, next);
|
|
60
|
+
await setStoredOAuthToken({ dataDir: dataDir, fileName: tokenFileName, token: next });
|
|
61
61
|
return { accessToken: next.accessToken, source: 'refreshed' };
|
|
62
62
|
}
|
|
63
63
|
catch {
|
|
@@ -61,7 +61,7 @@ export interface CompleteOAuthPkceInput {
|
|
|
61
61
|
* exchanges `code` for tokens. Throws if `state` is unknown, expired,
|
|
62
62
|
* already consumed, or was issued for a different provider.
|
|
63
63
|
*/
|
|
64
|
-
export declare function completeOAuthPkce(
|
|
64
|
+
export declare function completeOAuthPkce(requiredArgs: Pick<CompleteOAuthPkceInput, "config" | "pending" | "state" | "code">, optionalArgs?: Omit<CompleteOAuthPkceInput, "config" | "pending" | "state" | "code">): Promise<OAuthTokenResponse>;
|
|
65
65
|
export interface RefreshOAuthPkceInput {
|
|
66
66
|
config: OAuthPkceProviderConfig;
|
|
67
67
|
refreshToken: string;
|
|
@@ -72,7 +72,7 @@ export interface RefreshOAuthPkceInput {
|
|
|
72
72
|
* client_id that originally received it (RFC 6749 §6); since the same fixed
|
|
73
73
|
* client_id is always used, it doesn't need to be persisted per-token.
|
|
74
74
|
*/
|
|
75
|
-
export declare function refreshOAuthPkceToken(
|
|
75
|
+
export declare function refreshOAuthPkceToken(requiredArgs: Pick<RefreshOAuthPkceInput, "config" | "refreshToken">, optionalArgs?: Omit<RefreshOAuthPkceInput, "config" | "refreshToken">): Promise<OAuthTokenResponse>;
|
|
76
76
|
/** Stable provider id used to key an xAI token in any per-provider cache. */
|
|
77
77
|
export declare const XAI_OAUTH_PROVIDER_ID = "xai";
|
|
78
78
|
export declare const XAI_OAUTH_REDIRECT_HOST = "127.0.0.1";
|
|
@@ -39,16 +39,9 @@ function authServerMetadata(config) {
|
|
|
39
39
|
export function beginOAuthPkce(input) {
|
|
40
40
|
const { config, pending, redirectUri } = input;
|
|
41
41
|
const codeVerifier = generateCodeVerifier();
|
|
42
|
-
const codeChallenge = deriveCodeChallenge(codeVerifier);
|
|
42
|
+
const codeChallenge = deriveCodeChallenge({ verifier: codeVerifier });
|
|
43
43
|
const state = generateState();
|
|
44
|
-
const authorizeUrl = buildAuthorizeUrl({
|
|
45
|
-
authServer: authServerMetadata(config),
|
|
46
|
-
clientId: config.clientId,
|
|
47
|
-
redirectUri,
|
|
48
|
-
state,
|
|
49
|
-
codeChallenge,
|
|
50
|
-
scope: config.scope,
|
|
51
|
-
});
|
|
44
|
+
const authorizeUrl = buildAuthorizeUrl({ authServer: authServerMetadata(config), clientId: config.clientId, redirectUri, state, codeChallenge }, { scope: config.scope });
|
|
52
45
|
const pendingState = {
|
|
53
46
|
serverId: config.providerId,
|
|
54
47
|
authServerIssuer: config.issuer,
|
|
@@ -59,7 +52,7 @@ export function beginOAuthPkce(input) {
|
|
|
59
52
|
scope: config.scope,
|
|
60
53
|
createdAt: Date.now(),
|
|
61
54
|
};
|
|
62
|
-
pending.put(state, pendingState);
|
|
55
|
+
pending.put({ state: state, value: pendingState });
|
|
63
56
|
return { authorizeUrl, state };
|
|
64
57
|
}
|
|
65
58
|
/**
|
|
@@ -68,33 +61,35 @@ export function beginOAuthPkce(input) {
|
|
|
68
61
|
* exchanges `code` for tokens. Throws if `state` is unknown, expired,
|
|
69
62
|
* already consumed, or was issued for a different provider.
|
|
70
63
|
*/
|
|
71
|
-
export async function completeOAuthPkce(
|
|
72
|
-
const
|
|
64
|
+
export async function completeOAuthPkce(requiredArgs, optionalArgs = {}) {
|
|
65
|
+
const input = { ...optionalArgs, ...requiredArgs };
|
|
66
|
+
const consumed = input.pending.consume({ state: input.state });
|
|
73
67
|
if (!consumed) {
|
|
74
68
|
throw new Error(`${input.config.providerId} OAuth state not found or expired`);
|
|
75
69
|
}
|
|
76
70
|
if (consumed.serverId !== input.config.providerId) {
|
|
77
71
|
throw new Error(`${input.config.providerId} OAuth state mismatch: expected serverId=${input.config.providerId}, got ${consumed.serverId}`);
|
|
78
72
|
}
|
|
79
|
-
return exchangeCodeForToken({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
return exchangeCodeForToken({ input: {
|
|
74
|
+
tokenEndpoint: consumed.tokenEndpoint,
|
|
75
|
+
clientId: consumed.clientId,
|
|
76
|
+
redirectUri: consumed.redirectUri,
|
|
77
|
+
code: input.code,
|
|
78
|
+
codeVerifier: consumed.codeVerifier,
|
|
79
|
+
} }, { fetchImpl: input.fetchImpl ?? fetch });
|
|
86
80
|
}
|
|
87
81
|
/**
|
|
88
82
|
* Refreshes an existing access token. The refresh_token is bound to the
|
|
89
83
|
* client_id that originally received it (RFC 6749 §6); since the same fixed
|
|
90
84
|
* client_id is always used, it doesn't need to be persisted per-token.
|
|
91
85
|
*/
|
|
92
|
-
export async function refreshOAuthPkceToken(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
export async function refreshOAuthPkceToken(requiredArgs, optionalArgs = {}) {
|
|
87
|
+
const input = { ...optionalArgs, ...requiredArgs };
|
|
88
|
+
return refreshAccessToken({ input: {
|
|
89
|
+
tokenEndpoint: input.config.tokenEndpoint,
|
|
90
|
+
clientId: input.config.clientId,
|
|
91
|
+
refreshToken: input.refreshToken,
|
|
92
|
+
} }, { fetchImpl: input.fetchImpl ?? fetch });
|
|
98
93
|
}
|
|
99
94
|
// ---------------------------------------------------------------------------
|
|
100
95
|
// Concrete preset: the one OAuth+PKCE provider the origin actually shipped.
|
|
@@ -22,14 +22,34 @@ export interface OAuthTokenFile {
|
|
|
22
22
|
token?: StoredOAuthToken;
|
|
23
23
|
}
|
|
24
24
|
/** Coerces a freeform JSON blob into the typed shape, dropping anything that doesn't deserialize cleanly. Used both at read time and as a defensive pass when third-party tooling has hand-edited the file. */
|
|
25
|
-
export declare function sanitizeOAuthTokenFile(raw
|
|
26
|
-
|
|
25
|
+
export declare function sanitizeOAuthTokenFile({ raw }: {
|
|
26
|
+
raw: unknown;
|
|
27
|
+
}): OAuthTokenFile;
|
|
28
|
+
export declare function readOAuthTokenFile({ dataDir, fileName }: {
|
|
29
|
+
dataDir: string;
|
|
30
|
+
fileName: string;
|
|
31
|
+
}): Promise<OAuthTokenFile>;
|
|
27
32
|
/** Gets the current stored OAuth token for `fileName`, or null when none is stored (or the persisted entry is malformed). */
|
|
28
|
-
export declare function getStoredOAuthToken(dataDir
|
|
33
|
+
export declare function getStoredOAuthToken({ dataDir, fileName }: {
|
|
34
|
+
dataDir: string;
|
|
35
|
+
fileName: string;
|
|
36
|
+
}): Promise<StoredOAuthToken | null>;
|
|
29
37
|
/** Atomically replaces the stored OAuth token for `fileName`. */
|
|
30
|
-
export declare function setStoredOAuthToken(dataDir
|
|
38
|
+
export declare function setStoredOAuthToken({ dataDir, fileName, token }: {
|
|
39
|
+
dataDir: string;
|
|
40
|
+
fileName: string;
|
|
41
|
+
token: StoredOAuthToken;
|
|
42
|
+
}): Promise<void>;
|
|
31
43
|
/** Atomically deletes the stored OAuth token for `fileName`. No-op when absent. */
|
|
32
|
-
export declare function clearStoredOAuthToken(dataDir
|
|
44
|
+
export declare function clearStoredOAuthToken({ dataDir, fileName }: {
|
|
45
|
+
dataDir: string;
|
|
46
|
+
fileName: string;
|
|
47
|
+
}): Promise<void>;
|
|
33
48
|
/** True when the stored token is past its `expiresAt` (or within `skew` milliseconds of expiring). Returns false when no `expiresAt` is recorded — some providers issue non-expiring tokens. */
|
|
34
|
-
export declare function isOAuthTokenExpired(token
|
|
49
|
+
export declare function isOAuthTokenExpired({ token }: {
|
|
50
|
+
token: StoredOAuthToken;
|
|
51
|
+
}, { now, skew }?: {
|
|
52
|
+
now?: number;
|
|
53
|
+
skew?: number;
|
|
54
|
+
}): boolean;
|
|
35
55
|
//# sourceMappingURL=oauth-tokens.d.ts.map
|
|
@@ -22,7 +22,7 @@ function isPlainObject(v) {
|
|
|
22
22
|
return Boolean(v) && typeof v === 'object' && !Array.isArray(v);
|
|
23
23
|
}
|
|
24
24
|
/** Coerces a freeform JSON blob into the typed shape, dropping anything that doesn't deserialize cleanly. Used both at read time and as a defensive pass when third-party tooling has hand-edited the file. */
|
|
25
|
-
export function sanitizeOAuthTokenFile(raw) {
|
|
25
|
+
export function sanitizeOAuthTokenFile({ raw }) {
|
|
26
26
|
if (!isPlainObject(raw))
|
|
27
27
|
return {};
|
|
28
28
|
const tok = sanitizeToken(raw.token);
|
|
@@ -58,10 +58,10 @@ function sanitizeToken(raw) {
|
|
|
58
58
|
out.expiresAt = expiresAt;
|
|
59
59
|
return out;
|
|
60
60
|
}
|
|
61
|
-
export async function readOAuthTokenFile(dataDir, fileName) {
|
|
61
|
+
export async function readOAuthTokenFile({ dataDir, fileName }) {
|
|
62
62
|
try {
|
|
63
63
|
const raw = await readFile(tokenFilePath(dataDir, fileName), 'utf8');
|
|
64
|
-
return sanitizeOAuthTokenFile(JSON.parse(raw));
|
|
64
|
+
return sanitizeOAuthTokenFile({ raw: JSON.parse(raw) });
|
|
65
65
|
}
|
|
66
66
|
catch (err) {
|
|
67
67
|
const e = err;
|
|
@@ -108,27 +108,27 @@ async function writeTokenFile(dataDir, fileName, next) {
|
|
|
108
108
|
return next;
|
|
109
109
|
}
|
|
110
110
|
/** Gets the current stored OAuth token for `fileName`, or null when none is stored (or the persisted entry is malformed). */
|
|
111
|
-
export async function getStoredOAuthToken(dataDir, fileName) {
|
|
112
|
-
const file = await readOAuthTokenFile(dataDir, fileName);
|
|
111
|
+
export async function getStoredOAuthToken({ dataDir, fileName }) {
|
|
112
|
+
const file = await readOAuthTokenFile({ dataDir: dataDir, fileName: fileName });
|
|
113
113
|
return file.token ?? null;
|
|
114
114
|
}
|
|
115
115
|
/** Atomically replaces the stored OAuth token for `fileName`. */
|
|
116
|
-
export async function setStoredOAuthToken(dataDir, fileName, token) {
|
|
116
|
+
export async function setStoredOAuthToken({ dataDir, fileName, token }) {
|
|
117
117
|
await withLock(`${dataDir}\0${fileName}`, async () => {
|
|
118
118
|
await writeTokenFile(dataDir, fileName, { token });
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
/** Atomically deletes the stored OAuth token for `fileName`. No-op when absent. */
|
|
122
|
-
export async function clearStoredOAuthToken(dataDir, fileName) {
|
|
122
|
+
export async function clearStoredOAuthToken({ dataDir, fileName }) {
|
|
123
123
|
await withLock(`${dataDir}\0${fileName}`, async () => {
|
|
124
|
-
const file = await readOAuthTokenFile(dataDir, fileName);
|
|
124
|
+
const file = await readOAuthTokenFile({ dataDir: dataDir, fileName: fileName });
|
|
125
125
|
if (!file.token)
|
|
126
126
|
return;
|
|
127
127
|
await writeTokenFile(dataDir, fileName, {});
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
/** True when the stored token is past its `expiresAt` (or within `skew` milliseconds of expiring). Returns false when no `expiresAt` is recorded — some providers issue non-expiring tokens. */
|
|
131
|
-
export function isOAuthTokenExpired(token, now = Date.now(), skew = 120_000) {
|
|
131
|
+
export function isOAuthTokenExpired({ token }, { now = Date.now(), skew = 120_000 } = {}) {
|
|
132
132
|
if (typeof token.expiresAt !== 'number')
|
|
133
133
|
return false;
|
|
134
134
|
return token.expiresAt - skew <= now;
|