@axlsdk/axl 0.20.1 → 0.21.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/README.md +7 -0
- package/dist/index.cjs +74 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -50
- package/dist/index.d.ts +64 -50
- package/dist/index.js +74 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -71,6 +71,13 @@ Presets: `openrouter`, `azure`, `xai`, `deepseek`, `mistral`, `groq`, `bedrock`,
|
|
|
71
71
|
|
|
72
72
|
Each provider also accepts an opt-in `rateLimit` (`{ maxConcurrent?, minIntervalMs?, acquireTimeoutMs? }`) for proactive client-side pacing on top of the automatic 429/503/529 backoff — useful when a large fan-out (e.g. an eval) shares one API key. It caps in-flight request concurrency (not token throughput) for that provider's chat calls. See [Providers → Rate limiting](../../docs/providers.md#rate-limiting-opt-in).
|
|
73
73
|
|
|
74
|
+
Built-in providers require HTTPS for remote endpoints and never follow redirects.
|
|
75
|
+
Literal loopback HTTP remains automatic for local inference; Docker service names,
|
|
76
|
+
private-network URLs, and other remote HTTP endpoints require the provider-local
|
|
77
|
+
`dangerouslyAllowInsecureHttp: true` acknowledgement. Keep Axl and proprietary
|
|
78
|
+
system prompts on your trusted backend—see
|
|
79
|
+
[Prompt confidentiality](../../docs/security.md#prompt-confidentiality-and-the-trusted-backend).
|
|
80
|
+
|
|
74
81
|
State store options: `'memory'` (default), `'sqlite'` (requires `better-sqlite3`), or a `RedisStore` instance for multi-process deployments. See [State Stores](#state-stores).
|
|
75
82
|
|
|
76
83
|
### Tools, Agents, and Workflows
|
package/dist/index.cjs
CHANGED
|
@@ -705,7 +705,7 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
705
705
|
for (let attempt = 0; ; attempt++) {
|
|
706
706
|
let res;
|
|
707
707
|
try {
|
|
708
|
-
res = await fetch(input, init);
|
|
708
|
+
res = await fetch(input, { ...init, redirect: "manual" });
|
|
709
709
|
} catch (err) {
|
|
710
710
|
if (isAbortError2(err, init?.signal ?? void 0)) throw err;
|
|
711
711
|
if (attempt >= maxRetries) {
|
|
@@ -911,6 +911,38 @@ function isBuiltinTablePricingEligible(profile, context) {
|
|
|
911
911
|
return typeof effectiveTier === "string" && policy.supportedServiceTiers.includes(effectiveTier);
|
|
912
912
|
}
|
|
913
913
|
|
|
914
|
+
// src/http-transport.ts
|
|
915
|
+
function assertSafeProviderBaseUrl(baseUrl, surface, dangerouslyAllowInsecureHttp = false) {
|
|
916
|
+
assertSafeHttpEndpoint(baseUrl, surface, dangerouslyAllowInsecureHttp, "prompts and credentials");
|
|
917
|
+
}
|
|
918
|
+
function assertSafeHttpEndpoint(endpoint, surface, dangerouslyAllowInsecureHttp = false, exposedData = "sensitive data") {
|
|
919
|
+
let url;
|
|
920
|
+
try {
|
|
921
|
+
url = new URL(endpoint);
|
|
922
|
+
} catch {
|
|
923
|
+
throw unsafeTransportError(surface, "a valid absolute HTTP(S) URL", exposedData);
|
|
924
|
+
}
|
|
925
|
+
if (url.protocol === "https:") return;
|
|
926
|
+
if (url.protocol !== "http:") {
|
|
927
|
+
throw unsafeTransportError(surface, "an HTTPS URL", exposedData);
|
|
928
|
+
}
|
|
929
|
+
if (dangerouslyAllowInsecureHttp || isLiteralLoopbackHost(url.hostname)) return;
|
|
930
|
+
throw unsafeTransportError(
|
|
931
|
+
surface,
|
|
932
|
+
"an HTTPS URL, or set dangerouslyAllowInsecureHttp: true only for a deliberately insecure endpoint",
|
|
933
|
+
exposedData
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
function isLiteralLoopbackHost(hostname) {
|
|
937
|
+
return hostname === "localhost" || hostname === "localhost." || /^127(?:\.\d{1,3}){3}$/.test(hostname) || hostname === "[::1]";
|
|
938
|
+
}
|
|
939
|
+
function unsafeTransportError(surface, remediation, exposedData) {
|
|
940
|
+
return new AxlError(
|
|
941
|
+
"UNSAFE_TRANSPORT",
|
|
942
|
+
`${surface} requires ${remediation}. Remote HTTP can expose ${exposedData} in transit.`
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
|
|
914
946
|
// src/providers/openai-compatible.ts
|
|
915
947
|
function resolvePerModel(value, model, fallback) {
|
|
916
948
|
if (value === void 0) return fallback;
|
|
@@ -1054,6 +1086,11 @@ var OpenAICompatibleProvider = class {
|
|
|
1054
1086
|
const envBase = p.envBaseUrl ? process.env[p.envBaseUrl] : void 0;
|
|
1055
1087
|
const explicitBase = options.baseUrl ?? envBase;
|
|
1056
1088
|
this.baseUrl = (explicitBase ?? p.defaultBaseUrl).replace(/\/$/, "");
|
|
1089
|
+
assertSafeProviderBaseUrl(
|
|
1090
|
+
this.baseUrl,
|
|
1091
|
+
`${p.label ?? p.name} provider`,
|
|
1092
|
+
options.dangerouslyAllowInsecureHttp
|
|
1093
|
+
);
|
|
1057
1094
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1058
1095
|
const label = p.label ?? p.name;
|
|
1059
1096
|
if (p.requireExplicitBaseUrl && explicitBase === void 0) {
|
|
@@ -1954,6 +1991,11 @@ var OpenAIResponsesProvider = class {
|
|
|
1954
1991
|
constructor(options = {}) {
|
|
1955
1992
|
this.apiKeySource = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
|
|
1956
1993
|
this.baseUrl = (options.baseUrl ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
1994
|
+
assertSafeProviderBaseUrl(
|
|
1995
|
+
this.baseUrl,
|
|
1996
|
+
"OpenAI Responses provider",
|
|
1997
|
+
options.dangerouslyAllowInsecureHttp
|
|
1998
|
+
);
|
|
1957
1999
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1958
2000
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1959
2001
|
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
|
|
@@ -2709,6 +2751,11 @@ var AnthropicProvider = class {
|
|
|
2709
2751
|
constructor(options = {}) {
|
|
2710
2752
|
this.apiKeySource = options.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
|
|
2711
2753
|
this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
2754
|
+
assertSafeProviderBaseUrl(
|
|
2755
|
+
this.baseUrl,
|
|
2756
|
+
"Anthropic provider",
|
|
2757
|
+
options.dangerouslyAllowInsecureHttp
|
|
2758
|
+
);
|
|
2712
2759
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
2713
2760
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
2714
2761
|
throw new Error(
|
|
@@ -3399,6 +3446,11 @@ var GeminiProvider = class {
|
|
|
3399
3446
|
/\/$/,
|
|
3400
3447
|
""
|
|
3401
3448
|
);
|
|
3449
|
+
assertSafeProviderBaseUrl(
|
|
3450
|
+
this.baseUrl,
|
|
3451
|
+
"Google provider",
|
|
3452
|
+
options.dangerouslyAllowInsecureHttp
|
|
3453
|
+
);
|
|
3402
3454
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
3403
3455
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
3404
3456
|
throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
|
|
@@ -4286,6 +4338,7 @@ var builtinFactories = {
|
|
|
4286
4338
|
return new OpenAIProvider({
|
|
4287
4339
|
apiKey: opts.apiKey,
|
|
4288
4340
|
baseUrl: opts.baseUrl,
|
|
4341
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
4289
4342
|
rateLimit: opts.rateLimit
|
|
4290
4343
|
});
|
|
4291
4344
|
},
|
|
@@ -4298,6 +4351,7 @@ var builtinFactories = {
|
|
|
4298
4351
|
return new OpenAIResponsesProvider({
|
|
4299
4352
|
apiKey: opts.apiKey,
|
|
4300
4353
|
baseUrl: opts.baseUrl,
|
|
4354
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
4301
4355
|
rateLimit: opts.rateLimit
|
|
4302
4356
|
});
|
|
4303
4357
|
},
|
|
@@ -4306,6 +4360,7 @@ var builtinFactories = {
|
|
|
4306
4360
|
return new AnthropicProvider({
|
|
4307
4361
|
apiKey: opts.apiKey,
|
|
4308
4362
|
baseUrl: opts.baseUrl,
|
|
4363
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
4309
4364
|
rateLimit: opts.rateLimit
|
|
4310
4365
|
});
|
|
4311
4366
|
},
|
|
@@ -4314,6 +4369,7 @@ var builtinFactories = {
|
|
|
4314
4369
|
return new GeminiProvider({
|
|
4315
4370
|
apiKey: opts.apiKey,
|
|
4316
4371
|
baseUrl: opts.baseUrl,
|
|
4372
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
4317
4373
|
rateLimit: opts.rateLimit
|
|
4318
4374
|
});
|
|
4319
4375
|
}
|
|
@@ -4325,6 +4381,7 @@ function presetFactory(profile) {
|
|
|
4325
4381
|
profile,
|
|
4326
4382
|
apiKey: opts.apiKey,
|
|
4327
4383
|
baseUrl: opts.baseUrl,
|
|
4384
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
4328
4385
|
authHeader: opts.authHeader,
|
|
4329
4386
|
rateLimit: opts.rateLimit
|
|
4330
4387
|
});
|
|
@@ -11335,6 +11392,14 @@ var HttpMcpClient = class {
|
|
|
11335
11392
|
this.config = config;
|
|
11336
11393
|
this.name = config.name;
|
|
11337
11394
|
this.baseUrl = (config.uri ?? "").replace(/\/$/, "");
|
|
11395
|
+
if (this.baseUrl) {
|
|
11396
|
+
assertSafeHttpEndpoint(
|
|
11397
|
+
this.baseUrl,
|
|
11398
|
+
`MCP server "${this.name}"`,
|
|
11399
|
+
config.dangerouslyAllowInsecureHttp,
|
|
11400
|
+
"tool arguments and results"
|
|
11401
|
+
);
|
|
11402
|
+
}
|
|
11338
11403
|
}
|
|
11339
11404
|
name;
|
|
11340
11405
|
tools = [];
|
|
@@ -11364,7 +11429,9 @@ var HttpMcpClient = class {
|
|
|
11364
11429
|
method: "POST",
|
|
11365
11430
|
headers: { "Content-Type": "application/json" },
|
|
11366
11431
|
body: JSON.stringify(request),
|
|
11367
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
11432
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
11433
|
+
// Tool calls can be non-idempotent: never forward or retry their body.
|
|
11434
|
+
redirect: "manual"
|
|
11368
11435
|
});
|
|
11369
11436
|
if (!res.ok) {
|
|
11370
11437
|
throw new Error(`MCP HTTP error (${res.status}): ${await res.text()}`);
|
|
@@ -14156,6 +14223,11 @@ var OpenAIEmbedder = class {
|
|
|
14156
14223
|
}
|
|
14157
14224
|
this.model = options.model ?? "text-embedding-3-small";
|
|
14158
14225
|
this.baseUrl = options.baseUrl ?? "https://api.openai.com";
|
|
14226
|
+
assertSafeProviderBaseUrl(
|
|
14227
|
+
this.baseUrl,
|
|
14228
|
+
"OpenAI embedder",
|
|
14229
|
+
options.dangerouslyAllowInsecureHttp
|
|
14230
|
+
);
|
|
14159
14231
|
this.dimensions = options.dimensions ?? 1536;
|
|
14160
14232
|
}
|
|
14161
14233
|
async embed(texts, signal) {
|