la-machina-engine 0.14.0 → 0.15.1

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/dist/index.cjs CHANGED
@@ -1271,6 +1271,21 @@ var ApiEndpointSchema = import_zod.z.object({
1271
1271
  inputSchema: import_zod.z.unknown().optional(),
1272
1272
  outputHint: import_zod.z.string().optional()
1273
1273
  }).strict();
1274
+ var HEADER_NAME_RE = /^[!#$%&'*+\-.0-9A-Z^_`a-z|~]+$/;
1275
+ var RESERVED_HEADER_NAMES = /* @__PURE__ */ new Set([
1276
+ "authorization",
1277
+ "cookie",
1278
+ "set-cookie",
1279
+ "proxy-authorization"
1280
+ ]);
1281
+ var SECRET_PATTERN_RE = /(-secret|-token|-key)$/i;
1282
+ var RESERVED_DEFAULT_HEADER_EXACT = /* @__PURE__ */ new Set([
1283
+ "authorization",
1284
+ "cookie",
1285
+ "set-cookie",
1286
+ "proxy-authorization",
1287
+ "x-auth-token"
1288
+ ]);
1274
1289
  var ApiServiceSchema = import_zod.z.object({
1275
1290
  name: import_zod.z.string().min(1),
1276
1291
  description: import_zod.z.string().optional(),
@@ -1282,8 +1297,67 @@ var ApiServiceSchema = import_zod.z.object({
1282
1297
  allowedMethods: import_zod.z.array(ApiHttpMethodEnum).optional(),
1283
1298
  defaultHeaders: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
1284
1299
  maxBodyBytes: import_zod.z.number().int().positive().optional(),
1285
- endpoints: import_zod.z.array(ApiEndpointSchema).optional()
1286
- }).strict();
1300
+ endpoints: import_zod.z.array(ApiEndpointSchema).optional(),
1301
+ secretHeaders: import_zod.z.record(import_zod.z.string(), import_zod.z.string().min(1)).optional()
1302
+ }).strict().superRefine((svc, ctx) => {
1303
+ if (svc.defaultHeaders !== void 0) {
1304
+ for (const headerName of Object.keys(svc.defaultHeaders)) {
1305
+ if (!HEADER_NAME_RE.test(headerName)) {
1306
+ ctx.addIssue({
1307
+ code: "custom",
1308
+ message: `defaultHeaders key "${headerName}" is not a valid HTTP header name (RFC 7230 token charset)`,
1309
+ path: ["defaultHeaders", headerName]
1310
+ });
1311
+ continue;
1312
+ }
1313
+ const lower = headerName.toLowerCase();
1314
+ if (RESERVED_DEFAULT_HEADER_EXACT.has(lower)) {
1315
+ ctx.addIssue({
1316
+ code: "custom",
1317
+ message: `defaultHeaders key "${headerName}" is reserved \u2014 move secret-bearing values to secretHeaders or primary auth`,
1318
+ path: ["defaultHeaders", headerName]
1319
+ });
1320
+ continue;
1321
+ }
1322
+ if (SECRET_PATTERN_RE.test(headerName)) {
1323
+ ctx.addIssue({
1324
+ code: "custom",
1325
+ message: `defaultHeaders key "${headerName}" looks like a secret-bearing name (matches *-secret/*-token/*-key). Move to secretHeaders so the value is vault-resolved + scrubbed.`,
1326
+ path: ["defaultHeaders", headerName]
1327
+ });
1328
+ }
1329
+ }
1330
+ }
1331
+ if (svc.secretHeaders === void 0) return;
1332
+ const lowerDefault = /* @__PURE__ */ new Set();
1333
+ for (const k of Object.keys(svc.defaultHeaders ?? {})) lowerDefault.add(k.toLowerCase());
1334
+ for (const headerName of Object.keys(svc.secretHeaders)) {
1335
+ if (!HEADER_NAME_RE.test(headerName)) {
1336
+ ctx.addIssue({
1337
+ code: "custom",
1338
+ message: `secretHeaders key "${headerName}" is not a valid HTTP header name (RFC 7230 token charset)`,
1339
+ path: ["secretHeaders", headerName]
1340
+ });
1341
+ continue;
1342
+ }
1343
+ const lower = headerName.toLowerCase();
1344
+ if (RESERVED_HEADER_NAMES.has(lower)) {
1345
+ ctx.addIssue({
1346
+ code: "custom",
1347
+ message: `secretHeaders key "${headerName}" is reserved \u2014 primary \`auth\` owns ${headerName}`,
1348
+ path: ["secretHeaders", headerName]
1349
+ });
1350
+ continue;
1351
+ }
1352
+ if (lowerDefault.has(lower)) {
1353
+ ctx.addIssue({
1354
+ code: "custom",
1355
+ message: `secretHeaders key "${headerName}" collides with defaultHeaders (case-insensitive). Move the secret-bearing version to secretHeaders only.`,
1356
+ path: ["secretHeaders", headerName]
1357
+ });
1358
+ }
1359
+ }
1360
+ });
1287
1361
  var ApiCatalogModeEnum = import_zod.z.enum(["eager", "lazy", "auto"]);
1288
1362
  var ApiConfigResolved = import_zod.z.object({
1289
1363
  services: import_zod.z.array(ApiServiceSchema),
@@ -8380,6 +8454,12 @@ function createApiCallTool(opts) {
8380
8454
  `createApiCallTool: service "${svc.name}" uses custom auth (id: ${svc.auth.id}) but no resolveAuth was supplied`
8381
8455
  );
8382
8456
  }
8457
+ const hasSecretHeaders = svc.secretHeaders !== void 0 && Object.keys(svc.secretHeaders).length > 0;
8458
+ if (hasSecretHeaders && opts.resolveAuth === void 0) {
8459
+ throw new Error(
8460
+ `createApiCallTool: service "${svc.name}" declares secretHeaders but no resolveAuth was supplied (the resolver is required to look up the secret values)`
8461
+ );
8462
+ }
8383
8463
  }
8384
8464
  const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
8385
8465
  const maxResponseBytes = opts.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
@@ -8444,13 +8524,20 @@ function createApiCallTool(opts) {
8444
8524
  return errResult(`ERR_API_BODY_TOO_LARGE: exceeds ${cap} bytes`);
8445
8525
  }
8446
8526
  }
8527
+ const secretHeaderRefs = svc.secretHeaders !== void 0 && Object.keys(svc.secretHeaders).length > 0 ? svc.secretHeaders : void 0;
8447
8528
  let authHeaders;
8448
8529
  try {
8449
8530
  authHeaders = await resolveAuth({
8450
8531
  auth: svc.auth ?? { type: "none" },
8451
8532
  env: opts.env,
8452
8533
  resolver: opts.resolveAuth,
8453
- ctx: { serviceName: svc.name, method: input.method, path: input.path }
8534
+ ctx: {
8535
+ serviceName: svc.name,
8536
+ method: input.method,
8537
+ path: input.path,
8538
+ ...secretHeaderRefs !== void 0 ? { secretHeaderRefs } : {}
8539
+ },
8540
+ forceResolve: secretHeaderRefs !== void 0
8454
8541
  });
8455
8542
  } catch (err) {
8456
8543
  const raw2 = err instanceof Error ? err.message : String(err);
@@ -8559,22 +8646,27 @@ function sanitizeHeaders(user, auth) {
8559
8646
  return out;
8560
8647
  }
8561
8648
  async function resolveAuth(args) {
8562
- const { auth, env, resolver, ctx } = args;
8649
+ const { auth, env, resolver, ctx, forceResolve } = args;
8650
+ let primary;
8563
8651
  switch (auth.type) {
8564
8652
  case "none":
8565
- return {};
8653
+ primary = {};
8654
+ break;
8566
8655
  case "bearer": {
8567
8656
  const token = envLookup(env, auth.tokenRef);
8568
- return { Authorization: `Bearer ${token}` };
8657
+ primary = { Authorization: `Bearer ${token}` };
8658
+ break;
8569
8659
  }
8570
8660
  case "header": {
8571
8661
  const value = envLookup(env, auth.valueRef);
8572
- return { [auth.name]: value };
8662
+ primary = { [auth.name]: value };
8663
+ break;
8573
8664
  }
8574
8665
  case "basic": {
8575
8666
  const u = envLookup(env, auth.userRef);
8576
8667
  const p = envLookup(env, auth.passRef);
8577
- return { Authorization: `Basic ${base64(`${u}:${p}`)}` };
8668
+ primary = { Authorization: `Basic ${base64(`${u}:${p}`)}` };
8669
+ break;
8578
8670
  }
8579
8671
  case "custom":
8580
8672
  if (resolver === void 0) {
@@ -8582,6 +8674,14 @@ async function resolveAuth(args) {
8582
8674
  }
8583
8675
  return resolver(auth, ctx);
8584
8676
  }
8677
+ if (forceResolve === true) {
8678
+ if (resolver === void 0) {
8679
+ throw new Error("secretHeaders require a resolveAuth callback (host did not supply one)");
8680
+ }
8681
+ const extra = await resolver(auth, ctx);
8682
+ return { ...primary, ...extra };
8683
+ }
8684
+ return primary;
8585
8685
  }
8586
8686
  function envLookup(env, ref) {
8587
8687
  if (env === void 0 || env[ref] === void 0) {