clawmux 0.2.1 → 0.2.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/dist/cli.cjs CHANGED
@@ -317,7 +317,7 @@ function getLogDir() {
317
317
  }
318
318
 
319
319
  // src/cli.ts
320
- var VERSION2 = process.env.npm_package_version ?? "0.1.0";
320
+ var VERSION2 = process.env.npm_package_version ?? "0.2.3";
321
321
  var SERVICE_NAME = "clawmux";
322
322
  var HELP = `Usage: clawmux <command>
323
323
 
package/dist/index.cjs CHANGED
@@ -1892,6 +1892,98 @@ class OllamaAdapter {
1892
1892
  var ollamaAdapter = new OllamaAdapter;
1893
1893
  registerAdapter(ollamaAdapter);
1894
1894
 
1895
+ // src/utils/aws-sigv4.ts
1896
+ var import_node_crypto = require("node:crypto");
1897
+ var SERVICE = "bedrock";
1898
+ function sha256(data) {
1899
+ return import_node_crypto.createHash("sha256").update(data).digest("hex");
1900
+ }
1901
+ function hmacSha256(key, data) {
1902
+ return import_node_crypto.createHmac("sha256", key).update(data).digest();
1903
+ }
1904
+ function hmacSha256Hex(key, data) {
1905
+ return import_node_crypto.createHmac("sha256", key).update(data).digest("hex");
1906
+ }
1907
+ function awsUriEncode(str) {
1908
+ return encodeURIComponent(str).replace(/!/g, "%21").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A");
1909
+ }
1910
+ function buildCanonicalUri(pathname) {
1911
+ if (pathname === "" || pathname === "/")
1912
+ return "/";
1913
+ const segments = pathname.split("/");
1914
+ return segments.map((segment) => segment === "" ? "" : awsUriEncode(awsUriEncode(segment))).join("/");
1915
+ }
1916
+ function formatDateStamp(date) {
1917
+ return date.toISOString().slice(0, 10).replace(/-/g, "");
1918
+ }
1919
+ function formatAmzDate(date) {
1920
+ return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
1921
+ }
1922
+ function deriveSigningKey(secretKey, dateStamp, region, service) {
1923
+ const kDate = hmacSha256(`AWS4${secretKey}`, dateStamp);
1924
+ const kRegion = hmacSha256(kDate, region);
1925
+ const kService = hmacSha256(kRegion, service);
1926
+ return hmacSha256(kService, "aws4_request");
1927
+ }
1928
+ function signRequest(request, credentials, now) {
1929
+ const date = now ?? new Date;
1930
+ const dateStamp = formatDateStamp(date);
1931
+ const amzDate = formatAmzDate(date);
1932
+ const url = new URL(request.url);
1933
+ const canonicalUri = buildCanonicalUri(url.pathname);
1934
+ const sortedParams = [...url.searchParams.entries()].sort((a, b) => a[0].localeCompare(b[0]));
1935
+ const canonicalQueryString = sortedParams.map(([k, v]) => `${awsUriEncode(k)}=${awsUriEncode(v)}`).join("&");
1936
+ const payloadHash = sha256(request.body);
1937
+ const headersToSign = {};
1938
+ for (const [k, v] of Object.entries(request.headers)) {
1939
+ headersToSign[k.toLowerCase()] = v.trim();
1940
+ }
1941
+ headersToSign["host"] = url.host;
1942
+ headersToSign["x-amz-date"] = amzDate;
1943
+ headersToSign["x-amz-content-sha256"] = payloadHash;
1944
+ if (credentials.sessionToken) {
1945
+ headersToSign["x-amz-security-token"] = credentials.sessionToken;
1946
+ }
1947
+ const sortedHeaderKeys = Object.keys(headersToSign).sort();
1948
+ const canonicalHeaders = sortedHeaderKeys.map((k) => `${k}:${headersToSign[k]}`).join(`
1949
+ `) + `
1950
+ `;
1951
+ const signedHeaders = sortedHeaderKeys.join(";");
1952
+ const canonicalRequest = [
1953
+ request.method,
1954
+ canonicalUri,
1955
+ canonicalQueryString,
1956
+ canonicalHeaders,
1957
+ signedHeaders,
1958
+ payloadHash
1959
+ ].join(`
1960
+ `);
1961
+ const credentialScope = `${dateStamp}/${credentials.region}/${SERVICE}/aws4_request`;
1962
+ const stringToSign = [
1963
+ "AWS4-HMAC-SHA256",
1964
+ amzDate,
1965
+ credentialScope,
1966
+ sha256(canonicalRequest)
1967
+ ].join(`
1968
+ `);
1969
+ const signingKey = deriveSigningKey(credentials.secretAccessKey, dateStamp, credentials.region, SERVICE);
1970
+ const signature = hmacSha256Hex(signingKey, stringToSign);
1971
+ const authorization = `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}, ` + `SignedHeaders=${signedHeaders}, Signature=${signature}`;
1972
+ const result = {
1973
+ Authorization: authorization,
1974
+ "x-amz-date": amzDate,
1975
+ "x-amz-content-sha256": payloadHash
1976
+ };
1977
+ if (credentials.sessionToken) {
1978
+ result["x-amz-security-token"] = credentials.sessionToken;
1979
+ }
1980
+ return result;
1981
+ }
1982
+ function extractRegionFromUrl(url) {
1983
+ const match = url.match(/\.([a-z0-9-]+)\.amazonaws\.com/);
1984
+ return match?.[1];
1985
+ }
1986
+
1895
1987
  // src/adapters/bedrock.ts
1896
1988
  function bedrockMessagesToStandard(messages) {
1897
1989
  return messages.map((m) => {
@@ -1988,18 +2080,23 @@ class BedrockAdapter {
1988
2080
  maxTokens: parsed.maxTokens
1989
2081
  };
1990
2082
  }
2083
+ const url = `${baseUrl}/model/${targetModel}/converse-stream`;
2084
+ const body = JSON.stringify(requestBody);
1991
2085
  const headers = {
1992
2086
  "Content-Type": "application/json"
1993
2087
  };
1994
- if (auth.apiKey) {
2088
+ if (auth.awsAccessKeyId && auth.awsSecretKey && auth.awsRegion) {
2089
+ const sigv4Headers = signRequest({ method: "POST", url, headers, body }, {
2090
+ accessKeyId: auth.awsAccessKeyId,
2091
+ secretAccessKey: auth.awsSecretKey,
2092
+ sessionToken: auth.awsSessionToken,
2093
+ region: auth.awsRegion
2094
+ });
2095
+ Object.assign(headers, sigv4Headers);
2096
+ } else if (auth.apiKey) {
1995
2097
  headers[auth.headerName || "Authorization"] = auth.headerValue || auth.apiKey;
1996
2098
  }
1997
- return {
1998
- url: `${baseUrl}/model/${targetModel}/converse-stream`,
1999
- method: "POST",
2000
- headers,
2001
- body: JSON.stringify(requestBody)
2002
- };
2099
+ return { url, method: "POST", headers, body };
2003
2100
  }
2004
2101
  modifyMessages(rawBody, compressedMessages) {
2005
2102
  return {
@@ -2726,10 +2823,17 @@ function formatAuth(apiKey, providerConfig) {
2726
2823
  return { apiKey, headerName: "x-goog-api-key", headerValue: apiKey };
2727
2824
  }
2728
2825
  if (api === "bedrock-converse-stream") {
2826
+ const secretKey = process.env.AWS_SECRET_ACCESS_KEY ?? "";
2827
+ const sessionToken = process.env.AWS_SESSION_TOKEN;
2828
+ const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? extractRegionFromUrl(providerConfig?.baseUrl ?? "") ?? "us-east-1";
2729
2829
  return {
2730
2830
  apiKey,
2731
2831
  headerName: "Authorization",
2732
- headerValue: "AWS4-HMAC-SHA256 Credential=placeholder"
2832
+ headerValue: "",
2833
+ awsAccessKeyId: apiKey,
2834
+ awsSecretKey: secretKey,
2835
+ awsSessionToken: sessionToken,
2836
+ awsRegion: region
2733
2837
  };
2734
2838
  }
2735
2839
  return { apiKey, headerName: "Authorization", headerValue: `Bearer ${apiKey}` };
@@ -3365,7 +3469,11 @@ async function handleApiRequest(req, body, apiType, config, openclawConfig, auth
3365
3469
  const authInfo = {
3366
3470
  apiKey: auth.apiKey,
3367
3471
  headerName: auth.headerName,
3368
- headerValue: auth.headerValue
3472
+ headerValue: auth.headerValue,
3473
+ awsAccessKeyId: auth.awsAccessKeyId,
3474
+ awsSecretKey: auth.awsSecretKey,
3475
+ awsSessionToken: auth.awsSessionToken,
3476
+ awsRegion: auth.awsRegion
3369
3477
  };
3370
3478
  const actualModelId = decision.model.split("/").slice(1).join("/");
3371
3479
  const isCrossProvider = targetApiType !== "" && targetApiType !== apiType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmux",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Smart model routing + context compression proxy for OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "start:node": "node --import tsx src/index.ts",
23
23
  "test": "bun test",
24
24
  "typecheck": "tsc --noEmit",
25
- "build": "bun build src/cli.ts --outfile dist/cli.cjs --target node --format cjs --external @huggingface/transformers && bun build src/index.ts --outfile dist/index.cjs --target node --format cjs --external @huggingface/transformers",
25
+ "build": "bun build src/cli.ts --outfile dist/cli.cjs --target node --format cjs --external @huggingface/transformers && bun build src/index.ts --outfile dist/index.cjs --target node --format cjs --external @huggingface/transformers && node -e \"const fs=require('fs');const v=require('./package.json').version;for(const f of['dist/cli.cjs','dist/index.cjs']){fs.writeFileSync(f,fs.readFileSync(f,'utf-8').replace('__CLAWMUX_VERSION__',v))}\"",
26
26
  "build:check": "node dist/cli.cjs version",
27
27
  "prepublishOnly": "npm run typecheck && npm run build && npm run build:check"
28
28
  },