@blockrun/clawrouter 0.12.281 → 0.12.282

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.
Files changed (3) hide show
  1. package/dist/cli.js +276 -122
  2. package/dist/index.js +241 -87
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -65931,6 +65931,156 @@ ${lines.join("\n")}`;
65931
65931
  }
65932
65932
  });
65933
65933
 
65934
+ // src/local-guard.ts
65935
+ import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync } from "fs";
65936
+ import { homedir as homedir7 } from "os";
65937
+ import { join as join10 } from "path";
65938
+ function isLocalHostname(hostname) {
65939
+ const h = hostname.toLowerCase().replace(/\.$/, "");
65940
+ return h === "localhost" || h === "127.0.0.1" || h === "[::1]" || h === "::1";
65941
+ }
65942
+ function untrustedRequestReason(req) {
65943
+ const host = req.headers.host;
65944
+ if (host) {
65945
+ let hostname;
65946
+ try {
65947
+ hostname = new URL(`http://${host}`).hostname;
65948
+ } catch {
65949
+ return "malformed Host header";
65950
+ }
65951
+ if (!isLocalHostname(hostname)) return `non-local Host header (${hostname})`;
65952
+ }
65953
+ const origin2 = req.headers.origin;
65954
+ if (Array.isArray(origin2)) return "multiple Origin headers";
65955
+ if (origin2 !== void 0) {
65956
+ if (origin2 === "null") return "opaque Origin";
65957
+ let o3;
65958
+ try {
65959
+ o3 = new URL(origin2);
65960
+ } catch {
65961
+ return "malformed Origin header";
65962
+ }
65963
+ if (o3.protocol !== "http:" && o3.protocol !== "https:" || !isLocalHostname(o3.hostname)) {
65964
+ return `cross-site Origin (${origin2})`;
65965
+ }
65966
+ }
65967
+ const site = req.headers["sec-fetch-site"];
65968
+ if (site === "cross-site") return "cross-site browser request";
65969
+ return null;
65970
+ }
65971
+ function v4FromHex(hi, lo) {
65972
+ return `${hi >>> 8 & 255}.${hi & 255}.${lo >>> 8 & 255}.${lo & 255}`;
65973
+ }
65974
+ function isBlockedSsrfHost(hostname) {
65975
+ const h = hostname.toLowerCase().replace(/^\[/, "").replace(/\]$/, "").replace(/\.$/, "");
65976
+ if (!h) return true;
65977
+ if (h === "localhost" || h.endsWith(".localhost") || h.endsWith(".local")) return true;
65978
+ if (h.endsWith(".internal")) return true;
65979
+ if (METADATA_HOSTS.has(h)) return true;
65980
+ if (h.includes(":")) {
65981
+ if (h === "::1" || h === "::") return true;
65982
+ if (h.startsWith("fe80:") || h.startsWith("fc") || h.startsWith("fd")) return true;
65983
+ const sixToFour = h.match(/^2002:([0-9a-f]{1,4}):([0-9a-f]{1,4}):/);
65984
+ if (sixToFour) {
65985
+ return isBlockedSsrfHost(v4FromHex(parseInt(sixToFour[1], 16), parseInt(sixToFour[2], 16)));
65986
+ }
65987
+ const embedded = h.match(/^64:ff9b::(.+)$/) ?? h.match(/::ffff:(.+)$/);
65988
+ if (embedded) {
65989
+ const tail = embedded[1];
65990
+ if (tail.includes(".")) return isBlockedSsrfHost(tail);
65991
+ const seg = tail.split(":");
65992
+ if (seg.length === 2)
65993
+ return isBlockedSsrfHost(v4FromHex(parseInt(seg[0], 16), parseInt(seg[1], 16)));
65994
+ }
65995
+ return false;
65996
+ }
65997
+ const m = h.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
65998
+ if (m) {
65999
+ const a = Number(m[1]);
66000
+ const b = Number(m[2]);
66001
+ const c = Number(m[3]);
66002
+ if (a === 127 || a === 0 || a === 10) return true;
66003
+ if (a === 169 && b === 254) return true;
66004
+ if (a === 172 && b >= 16 && b <= 31) return true;
66005
+ if (a === 192 && b === 168) return true;
66006
+ if (a === 100 && b >= 64 && b <= 127) return true;
66007
+ if (a === 192 && b === 0 && c === 0) return true;
66008
+ }
66009
+ return false;
66010
+ }
66011
+ async function ssrfSafeFetch(url2, init2 = {}, maxHops = 5) {
66012
+ const { allowPrivate, ...fetchInit } = init2;
66013
+ let current = url2;
66014
+ for (let hop = 0; hop <= maxHops; hop++) {
66015
+ const u = new URL(current);
66016
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
66017
+ throw new Error(`refusing to fetch non-http(s) URL (${u.protocol})`);
66018
+ }
66019
+ if (!allowPrivate && isBlockedSsrfHost(u.hostname)) {
66020
+ throw new Error(`refusing to fetch a private/loopback/metadata address: ${u.hostname}`);
66021
+ }
66022
+ const res = await fetch(current, { ...fetchInit, redirect: "manual" });
66023
+ if (res.status >= 300 && res.status < 400) {
66024
+ const loc = res.headers.get("location");
66025
+ if (!loc) return res;
66026
+ current = new URL(loc, current).href;
66027
+ continue;
66028
+ }
66029
+ return res;
66030
+ }
66031
+ throw new Error("too many redirects");
66032
+ }
66033
+ function readLocalImageAsDataUri(filePath) {
66034
+ const expanded = filePath.startsWith("~/") ? join10(homedir7(), filePath.slice(2)) : filePath;
66035
+ const refuse = () => new Error(`Not a readable PNG, JPEG or WebP image: ${filePath}`);
66036
+ let fd;
66037
+ try {
66038
+ fd = openSync2(realpathSync(expanded), "r");
66039
+ } catch {
66040
+ throw refuse();
66041
+ }
66042
+ try {
66043
+ const st = fstatSync2(fd);
66044
+ if (!st.isFile() || st.size === 0 || st.size > MAX_LOCAL_IMAGE_BYTES) throw refuse();
66045
+ const head = Buffer.alloc(12);
66046
+ readSync2(fd, head, 0, 12, 0);
66047
+ const sig = IMAGE_SIGNATURES.find((s3) => s3.test(head));
66048
+ if (!sig) throw refuse();
66049
+ const data = Buffer.alloc(st.size);
66050
+ let off = 0;
66051
+ while (off < st.size) {
66052
+ const n = readSync2(fd, data, off, st.size - off, off);
66053
+ if (n === 0) break;
66054
+ off += n;
66055
+ }
66056
+ return `data:${sig.mime};base64,${data.subarray(0, off).toString("base64")}`;
66057
+ } finally {
66058
+ closeSync2(fd);
66059
+ }
66060
+ }
66061
+ var METADATA_HOSTS, MAX_LOCAL_IMAGE_BYTES, IMAGE_SIGNATURES;
66062
+ var init_local_guard = __esm({
66063
+ "src/local-guard.ts"() {
66064
+ "use strict";
66065
+ METADATA_HOSTS = /* @__PURE__ */ new Set(["metadata", "metadata.goog", "instance-data"]);
66066
+ MAX_LOCAL_IMAGE_BYTES = 25 * 1024 * 1024;
66067
+ IMAGE_SIGNATURES = [
66068
+ {
66069
+ mime: "image/png",
66070
+ test: (b) => b.length >= 8 && b.readUInt32BE(0) === 2303741511 && b.readUInt32BE(4) === 218765834
66071
+ },
66072
+ {
66073
+ mime: "image/jpeg",
66074
+ test: (b) => b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
66075
+ },
66076
+ {
66077
+ mime: "image/webp",
66078
+ test: (b) => b.length >= 12 && b.toString("ascii", 0, 4) === "RIFF" && b.toString("ascii", 8, 12) === "WEBP"
66079
+ }
66080
+ ];
66081
+ }
66082
+ });
66083
+
65934
66084
  // node_modules/undici/lib/core/symbols.js
65935
66085
  var require_symbols = __commonJS({
65936
66086
  "node_modules/undici/lib/core/symbols.js"(exports, module) {
@@ -93324,8 +93474,8 @@ var init_textual_tool_calls = __esm({
93324
93474
 
93325
93475
  // src/response-store.ts
93326
93476
  import { appendFile as appendFile2, mkdir as mkdir5, readFile, readdir as readdir2 } from "fs/promises";
93327
- import { homedir as homedir7 } from "os";
93328
- import { join as join10 } from "path";
93477
+ import { homedir as homedir8 } from "os";
93478
+ import { join as join11 } from "path";
93329
93479
  import { randomBytes as randomBytes7 } from "crypto";
93330
93480
  function isEnabled() {
93331
93481
  const v = process.env.BLOCKRUN_RESPONSE_STORE;
@@ -93338,7 +93488,7 @@ async function ensureDir2() {
93338
93488
  }
93339
93489
  function dailyFile(date = /* @__PURE__ */ new Date()) {
93340
93490
  const iso = date.toISOString().slice(0, 10);
93341
- return join10(STORE_DIR, `responses-${iso}.jsonl`);
93491
+ return join11(STORE_DIR, `responses-${iso}.jsonl`);
93342
93492
  }
93343
93493
  function genId() {
93344
93494
  return `resp_${Date.now()}_${randomBytes7(3).toString("hex")}`;
@@ -93388,7 +93538,7 @@ async function listRecent(limit = 20, daysBack = 7) {
93388
93538
  d.setUTCDate(d.getUTCDate() - i);
93389
93539
  const iso = d.toISOString().slice(0, 10);
93390
93540
  const name = `responses-${iso}.jsonl`;
93391
- if (files.includes(name)) candidateFiles.push(join10(STORE_DIR, name));
93541
+ if (files.includes(name)) candidateFiles.push(join11(STORE_DIR, name));
93392
93542
  }
93393
93543
  const all3 = [];
93394
93544
  for (const f of candidateFiles) {
@@ -93417,7 +93567,7 @@ var STORE_DIR, dirReady2;
93417
93567
  var init_response_store = __esm({
93418
93568
  "src/response-store.ts"() {
93419
93569
  "use strict";
93420
- STORE_DIR = join10(homedir7(), ".openclaw", "blockrun", "responses");
93570
+ STORE_DIR = join11(homedir8(), ".openclaw", "blockrun", "responses");
93421
93571
  dirReady2 = false;
93422
93572
  }
93423
93573
  });
@@ -95054,10 +95204,10 @@ import { createHmac } from "crypto";
95054
95204
  import { createServer } from "http";
95055
95205
  import { finished, Readable } from "stream";
95056
95206
  import { pipeline } from "stream/promises";
95057
- import { homedir as homedir8 } from "os";
95058
- import { join as join11 } from "path";
95207
+ import { homedir as homedir9 } from "os";
95208
+ import { join as join12 } from "path";
95059
95209
  import { mkdir as mkdir6, writeFile as writeFile4, readFile as readFile2, stat as fsStat } from "fs/promises";
95060
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
95210
+ import { readFileSync as readFileSync2 } from "fs";
95061
95211
  async function loadGatewayCatalog(apiBase, apiKey) {
95062
95212
  try {
95063
95213
  const controller = new AbortController();
@@ -95951,22 +96101,6 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
95951
96101
  }).catch(() => {
95952
96102
  });
95953
96103
  }
95954
- function readImageFileAsDataUri(filePath) {
95955
- const resolved = filePath.startsWith("~/") ? join11(homedir8(), filePath.slice(2)) : filePath;
95956
- if (!existsSync2(resolved)) {
95957
- throw new Error(`Image file not found: ${resolved}`);
95958
- }
95959
- const ext = resolved.split(".").pop()?.toLowerCase() ?? "png";
95960
- const mimeMap = {
95961
- png: "image/png",
95962
- jpg: "image/jpeg",
95963
- jpeg: "image/jpeg",
95964
- webp: "image/webp"
95965
- };
95966
- const mime = mimeMap[ext] ?? "image/png";
95967
- const data = readFileSync2(resolved);
95968
- return `data:${mime};base64,${data.toString("base64")}`;
95969
- }
95970
96104
  async function uploadDataUriToHost(dataUri) {
95971
96105
  const match = dataUri.match(/^data:(image\/\w+);base64,(.+)$/);
95972
96106
  if (!match) throw new Error("Invalid data URI format");
@@ -96243,6 +96377,13 @@ async function startProxy(options) {
96243
96377
  console.error(`[ClawRouter] Request finished with error: ${err.message}`);
96244
96378
  }
96245
96379
  });
96380
+ const untrusted = untrustedRequestReason(req);
96381
+ if (untrusted) {
96382
+ console.warn(`[ClawRouter] Refused request to ${req.url}: ${untrusted}`);
96383
+ res.writeHead(403, { "Content-Type": "application/json" });
96384
+ res.end(JSON.stringify({ error: "forbidden", details: untrusted }));
96385
+ return;
96386
+ }
96246
96387
  if (req.url === "/health" || req.url?.startsWith("/health?")) {
96247
96388
  const url2 = new URL(req.url, "http://localhost");
96248
96389
  const full = url2.searchParams.get("full") === "true";
@@ -96471,7 +96612,7 @@ async function startProxy(options) {
96471
96612
  res.end("Bad request");
96472
96613
  return;
96473
96614
  }
96474
- const filePath = join11(IMAGE_DIR, filename);
96615
+ const filePath = join12(IMAGE_DIR, filename);
96475
96616
  try {
96476
96617
  const s3 = await fsStat(filePath);
96477
96618
  if (!s3.isFile()) throw new Error("not a file");
@@ -96502,7 +96643,7 @@ async function startProxy(options) {
96502
96643
  res.end("Bad request");
96503
96644
  return;
96504
96645
  }
96505
- const filePath = join11(AUDIO_DIR, filename);
96646
+ const filePath = join12(AUDIO_DIR, filename);
96506
96647
  try {
96507
96648
  const s3 = await fsStat(filePath);
96508
96649
  if (!s3.isFile()) throw new Error("not a file");
@@ -96532,7 +96673,7 @@ async function startProxy(options) {
96532
96673
  res.end("Bad request");
96533
96674
  return;
96534
96675
  }
96535
- const filePath = join11(VIDEO_DIR, filename);
96676
+ const filePath = join12(VIDEO_DIR, filename);
96536
96677
  try {
96537
96678
  const s3 = await fsStat(filePath);
96538
96679
  if (!s3.isFile()) throw new Error("not a file");
@@ -96674,7 +96815,7 @@ async function startProxy(options) {
96674
96815
  const [, mimeType, b64] = dataUriMatch;
96675
96816
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
96676
96817
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
96677
- await writeFile4(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
96818
+ await writeFile4(join12(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
96678
96819
  img.url = `http://localhost:${port2}/images/${filename}`;
96679
96820
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
96680
96821
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -96685,7 +96826,7 @@ async function startProxy(options) {
96685
96826
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
96686
96827
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
96687
96828
  const buf = Buffer.from(await imgResp.arrayBuffer());
96688
- await writeFile4(join11(IMAGE_DIR, filename), buf);
96829
+ await writeFile4(join12(IMAGE_DIR, filename), buf);
96689
96830
  img.url = `http://localhost:${port2}/images/${filename}`;
96690
96831
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
96691
96832
  }
@@ -96748,7 +96889,10 @@ async function startProxy(options) {
96748
96889
  if (typeof val !== "string" || !val) continue;
96749
96890
  if (val.startsWith("data:")) {
96750
96891
  } else if (val.startsWith("https://") || val.startsWith("http://")) {
96751
- const imgResp = await fetch(val, { signal: clientAbort.signal });
96892
+ const imgResp = await ssrfSafeFetch(val, {
96893
+ signal: clientAbort.signal,
96894
+ allowPrivate: process.env.CLAWROUTER_ALLOW_PRIVATE_FETCH === "1"
96895
+ });
96752
96896
  if (!imgResp.ok)
96753
96897
  throw new Error(`Failed to download ${field} from ${val}: HTTP ${imgResp.status}`);
96754
96898
  const contentType = imgResp.headers.get("content-type") ?? "image/png";
@@ -96758,7 +96902,7 @@ async function startProxy(options) {
96758
96902
  `[ClawRouter] img2img: downloaded ${field} URL \u2192 data URI (${buf.length} bytes)`
96759
96903
  );
96760
96904
  } else {
96761
- parsed[field] = readImageFileAsDataUri(val);
96905
+ parsed[field] = readLocalImageAsDataUri(val);
96762
96906
  console.log(`[ClawRouter] img2img: read ${field} file \u2192 data URI`);
96763
96907
  }
96764
96908
  }
@@ -96807,7 +96951,7 @@ async function startProxy(options) {
96807
96951
  const [, mimeType, b64] = dataUriMatch;
96808
96952
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
96809
96953
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
96810
- await writeFile4(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
96954
+ await writeFile4(join12(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
96811
96955
  img.url = `http://localhost:${port2}/images/${filename}`;
96812
96956
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
96813
96957
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -96818,7 +96962,7 @@ async function startProxy(options) {
96818
96962
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
96819
96963
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
96820
96964
  const buf = Buffer.from(await imgResp.arrayBuffer());
96821
- await writeFile4(join11(IMAGE_DIR, filename), buf);
96965
+ await writeFile4(join12(IMAGE_DIR, filename), buf);
96822
96966
  img.url = `http://localhost:${port2}/images/${filename}`;
96823
96967
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
96824
96968
  }
@@ -96914,7 +97058,7 @@ async function startProxy(options) {
96914
97058
  const ext = contentType.includes("wav") ? "wav" : "mp3";
96915
97059
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
96916
97060
  const buf = Buffer.from(await audioResp.arrayBuffer());
96917
- await writeFile4(join11(AUDIO_DIR, filename), buf);
97061
+ await writeFile4(join12(AUDIO_DIR, filename), buf);
96918
97062
  track.url = `http://localhost:${port2}/audio/${filename}`;
96919
97063
  console.log(`[ClawRouter] Audio saved \u2192 ${track.url}`);
96920
97064
  }
@@ -97081,7 +97225,7 @@ async function startProxy(options) {
97081
97225
  const ext = contentType.includes("webm") ? "webm" : contentType.includes("quicktime") ? "mov" : "mp4";
97082
97226
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
97083
97227
  const buf = Buffer.from(await videoResp.arrayBuffer());
97084
- await writeFile4(join11(VIDEO_DIR, filename), buf);
97228
+ await writeFile4(join12(VIDEO_DIR, filename), buf);
97085
97229
  clip.url = `http://localhost:${port2}/videos/${filename}`;
97086
97230
  console.log(`[ClawRouter] Video saved \u2192 ${clip.url}`);
97087
97231
  }
@@ -98047,8 +98191,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
98047
98191
  let imageDataUri;
98048
98192
  let maskDataUri;
98049
98193
  try {
98050
- imageDataUri = readImageFileAsDataUri(imagePath);
98051
- if (maskPath) maskDataUri = readImageFileAsDataUri(maskPath);
98194
+ imageDataUri = readLocalImageAsDataUri(imagePath);
98195
+ if (maskPath) maskDataUri = readLocalImageAsDataUri(maskPath);
98052
98196
  } catch (fileErr) {
98053
98197
  const fileErrMsg = fileErr instanceof Error ? fileErr.message : String(fileErr);
98054
98198
  sendImg2ImgText(`Failed to read image file: ${fileErrMsg}`);
@@ -99786,6 +99930,7 @@ var init_proxy = __esm({
99786
99930
  init_outcome_memory();
99787
99931
  init_config();
99788
99932
  init_journal();
99933
+ init_local_guard();
99789
99934
  init_upstream_proxy();
99790
99935
  init_textual_tool_calls();
99791
99936
  init_response_store();
@@ -99794,10 +99939,10 @@ var init_proxy = __esm({
99794
99939
  paymentStore = new AsyncLocalStorage();
99795
99940
  BLOCKRUN_API = "https://blockrun.ai/api";
99796
99941
  BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
99797
- DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ?? join11(homedir8(), ".clawrouter-desktop", "service-token");
99798
- IMAGE_DIR = join11(homedir8(), ".openclaw", "blockrun", "images");
99799
- AUDIO_DIR = join11(homedir8(), ".openclaw", "blockrun", "audio");
99800
- VIDEO_DIR = join11(homedir8(), ".openclaw", "blockrun", "videos");
99942
+ DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ?? join12(homedir9(), ".clawrouter-desktop", "service-token");
99943
+ IMAGE_DIR = join12(homedir9(), ".openclaw", "blockrun", "images");
99944
+ AUDIO_DIR = join12(homedir9(), ".openclaw", "blockrun", "audio");
99945
+ VIDEO_DIR = join12(homedir9(), ".openclaw", "blockrun", "videos");
99801
99946
  AUTO_MODEL = "blockrun/auto";
99802
99947
  ROUTING_PROFILES = /* @__PURE__ */ new Set([
99803
99948
  "blockrun/eco",
@@ -101178,7 +101323,13 @@ function buildTool(service, proxyBaseUrl) {
101178
101323
  if (service.proxyPath === "/pm/__dynamic__") {
101179
101324
  const rawPath = typeof params.path === "string" ? params.path : "";
101180
101325
  const normalized = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
101181
- if (!normalized.startsWith("/pm/") || normalized.includes("..")) {
101326
+ let resolved;
101327
+ try {
101328
+ resolved = new URL(`/v1${normalized}`, "http://x").pathname;
101329
+ } catch {
101330
+ resolved = "";
101331
+ }
101332
+ if (!normalized.startsWith("/pm/") || normalized.includes("..") || /%2e|%2f|%5c|\\/i.test(normalized) || !resolved.startsWith("/v1/pm/")) {
101182
101333
  throw new Error(
101183
101334
  `predexon_endpoint_call: invalid path '${rawPath}' \u2014 must begin with '/pm/' and contain no '..'`
101184
101335
  );
@@ -122018,8 +122169,8 @@ var init_constants3 = __esm({
122018
122169
 
122019
122170
  // src/polymarket/wallet-adapter.ts
122020
122171
  import fs2 from "fs";
122021
- import { homedir as homedir9 } from "os";
122022
- import { join as join12 } from "path";
122172
+ import { homedir as homedir10 } from "os";
122173
+ import { join as join13 } from "path";
122023
122174
  function getOrCreateWalletKey() {
122024
122175
  const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
122025
122176
  if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
@@ -122067,7 +122218,7 @@ var init_wallet_adapter = __esm({
122067
122218
  init_esm();
122068
122219
  init_chains();
122069
122220
  init_constants3();
122070
- WALLET_FILE2 = join12(homedir9(), ".openclaw", "blockrun", "wallet.key");
122221
+ WALLET_FILE2 = join13(homedir10(), ".openclaw", "blockrun", "wallet.key");
122071
122222
  BASE_RPC_URLS = [
122072
122223
  "https://mainnet.base.org",
122073
122224
  "https://base.llamarpc.com",
@@ -232583,7 +232734,7 @@ var init_retry = __esm({
232583
232734
  // src/index.ts
232584
232735
  import {
232585
232736
  writeFileSync as writeFileSync3,
232586
- existsSync as existsSync3,
232737
+ existsSync as existsSync2,
232587
232738
  readdirSync,
232588
232739
  mkdirSync as mkdirSync3,
232589
232740
  copyFileSync,
@@ -232592,9 +232743,9 @@ import {
232592
232743
  unlinkSync
232593
232744
  } from "fs";
232594
232745
  import { readFile as readFileAsync } from "fs/promises";
232595
- import { homedir as homedir14 } from "os";
232746
+ import { homedir as homedir15 } from "os";
232596
232747
  import { randomUUID } from "crypto";
232597
- import { join as join17 } from "path";
232748
+ import { join as join18 } from "path";
232598
232749
  async function waitForProxyHealth(port, timeoutMs = 3e3) {
232599
232750
  const start = Date.now();
232600
232751
  while (Date.now() - start < timeoutMs) {
@@ -232614,12 +232765,12 @@ function writePrivateJsonSync(path5, value) {
232614
232765
  renameSync2(temporary, path5);
232615
232766
  chmodSync(path5, 384);
232616
232767
  } finally {
232617
- if (existsSync3(temporary)) unlinkSync(temporary);
232768
+ if (existsSync2(temporary)) unlinkSync(temporary);
232618
232769
  }
232619
232770
  }
232620
232771
  function hasConfiguredWallet() {
232621
232772
  return Boolean(
232622
- process["env"].BLOCKRUN_WALLET_KEY || existsSync3(CORE_WALLET_FILE) || existsSync3(WALLET_FILE)
232773
+ process["env"].BLOCKRUN_WALLET_KEY || existsSync2(CORE_WALLET_FILE) || existsSync2(WALLET_FILE)
232623
232774
  );
232624
232775
  }
232625
232776
  function isCompletionMode() {
@@ -232637,11 +232788,11 @@ function isBlockrunWebSearchDisabled(config) {
232637
232788
  }
232638
232789
  function legacyPackageIsBlockRun() {
232639
232790
  for (const dir of [
232640
- join17(homedir14(), ".openclaw", "extensions", "clawrouter"),
232641
- join17(homedir14(), ".openclaw", "npm", "node_modules", "@blockrun", "clawrouter")
232791
+ join18(homedir15(), ".openclaw", "extensions", "clawrouter"),
232792
+ join18(homedir15(), ".openclaw", "npm", "node_modules", "@blockrun", "clawrouter")
232642
232793
  ]) {
232643
232794
  try {
232644
- const metadata = JSON.parse(readTextFileSync(join17(dir, "package.json")));
232795
+ const metadata = JSON.parse(readTextFileSync(join18(dir, "package.json")));
232645
232796
  if (metadata.name === "@blockrun/clawrouter") return true;
232646
232797
  } catch {
232647
232798
  }
@@ -232649,11 +232800,11 @@ function legacyPackageIsBlockRun() {
232649
232800
  return false;
232650
232801
  }
232651
232802
  function injectModelsConfig(logger48, options = {}) {
232652
- const configDir = join17(homedir14(), ".openclaw");
232653
- const configPath = join17(configDir, "openclaw.json");
232803
+ const configDir = join18(homedir15(), ".openclaw");
232804
+ const configPath = join18(configDir, "openclaw.json");
232654
232805
  let config = {};
232655
232806
  let needsWrite = false;
232656
- if (!existsSync3(configDir)) {
232807
+ if (!existsSync2(configDir)) {
232657
232808
  try {
232658
232809
  mkdirSync3(configDir, { recursive: true });
232659
232810
  logger48.info("Created OpenClaw config directory");
@@ -232664,7 +232815,7 @@ function injectModelsConfig(logger48, options = {}) {
232664
232815
  return;
232665
232816
  }
232666
232817
  }
232667
- if (existsSync3(configPath)) {
232818
+ if (existsSync2(configPath)) {
232668
232819
  try {
232669
232820
  const content = readTextFileSync(configPath).trim();
232670
232821
  if (content) {
@@ -232871,8 +233022,8 @@ function injectModelsConfig(logger48, options = {}) {
232871
233022
  }
232872
233023
  function syncAgentModelCache(logger48, options = {}) {
232873
233024
  if (!isGatewayMode() && !options.forceWrite) return;
232874
- const agentsDir = join17(homedir14(), ".openclaw", "agents");
232875
- if (!existsSync3(agentsDir)) return;
233025
+ const agentsDir = join18(homedir15(), ".openclaw", "agents");
233026
+ if (!existsSync2(agentsDir)) return;
232876
233027
  let agentDirs;
232877
233028
  try {
232878
233029
  agentDirs = readdirSync(agentsDir);
@@ -232881,8 +233032,8 @@ function syncAgentModelCache(logger48, options = {}) {
232881
233032
  }
232882
233033
  const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
232883
233034
  for (const agent of agentDirs) {
232884
- const cachePath = join17(agentsDir, agent, "agent", "models.json");
232885
- if (!existsSync3(cachePath)) continue;
233035
+ const cachePath = join18(agentsDir, agent, "agent", "models.json");
233036
+ if (!existsSync2(cachePath)) continue;
232886
233037
  try {
232887
233038
  const raw = readTextFileSync(cachePath).trim();
232888
233039
  if (!raw) continue;
@@ -232910,8 +233061,8 @@ function syncAgentModelCache(logger48, options = {}) {
232910
233061
  }
232911
233062
  }
232912
233063
  function injectAuthProfile(logger48) {
232913
- const agentsDir = join17(homedir14(), ".openclaw", "agents");
232914
- if (!existsSync3(agentsDir)) {
233064
+ const agentsDir = join18(homedir15(), ".openclaw", "agents");
233065
+ if (!existsSync2(agentsDir)) {
232915
233066
  try {
232916
233067
  mkdirSync3(agentsDir, { recursive: true });
232917
233068
  } catch (err) {
@@ -232927,10 +233078,10 @@ function injectAuthProfile(logger48) {
232927
233078
  agents = ["main", ...agents];
232928
233079
  }
232929
233080
  for (const agentId of agents) {
232930
- const authDir = join17(agentsDir, agentId, "agent");
232931
- const authPath = join17(authDir, "auth-profiles.json");
232932
- const sqlitePath = join17(authDir, "openclaw-agent.sqlite");
232933
- if (existsSync3(sqlitePath)) {
233081
+ const authDir = join18(agentsDir, agentId, "agent");
233082
+ const authPath = join18(authDir, "auth-profiles.json");
233083
+ const sqlitePath = join18(authDir, "openclaw-agent.sqlite");
233084
+ if (existsSync2(sqlitePath)) {
232934
233085
  removeInjectedAuthPlaceholder(authPath, logger48, agentId);
232935
233086
  continue;
232936
233087
  }
@@ -232938,7 +233089,7 @@ function injectAuthProfile(logger48) {
232938
233089
  removeInjectedAuthPlaceholder(authPath, logger48, agentId);
232939
233090
  continue;
232940
233091
  }
232941
- if (!existsSync3(authDir)) {
233092
+ if (!existsSync2(authDir)) {
232942
233093
  try {
232943
233094
  mkdirSync3(authDir, { recursive: true });
232944
233095
  } catch {
@@ -232949,7 +233100,7 @@ function injectAuthProfile(logger48) {
232949
233100
  version: 1,
232950
233101
  profiles: {}
232951
233102
  };
232952
- if (existsSync3(authPath)) {
233103
+ if (existsSync2(authPath)) {
232953
233104
  try {
232954
233105
  const existing = JSON.parse(readTextFileSync(authPath));
232955
233106
  if (existing.version && existing.profiles) {
@@ -232982,7 +233133,7 @@ function injectAuthProfile(logger48) {
232982
233133
  }
232983
233134
  function removeInjectedAuthPlaceholder(authPath, logger48, agentId) {
232984
233135
  try {
232985
- if (!existsSync3(authPath)) return;
233136
+ if (!existsSync2(authPath)) return;
232986
233137
  const parsed = JSON.parse(readTextFileSync(authPath));
232987
233138
  const profiles = parsed?.profiles;
232988
233139
  if (!profiles || typeof profiles !== "object" || Array.isArray(profiles)) return;
@@ -233437,7 +233588,7 @@ function buildImageGenerationProvider() {
233437
233588
  (result.data ?? []).map(async (img) => {
233438
233589
  const filename = img.url?.split("/images/").pop();
233439
233590
  if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
233440
- const filePath = join17(IMAGE_DIR2, filename);
233591
+ const filePath = join18(IMAGE_DIR2, filename);
233441
233592
  const buffer2 = await readFileAsync(filePath);
233442
233593
  const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
233443
233594
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
@@ -233489,7 +233640,7 @@ function buildMusicGenerationProvider() {
233489
233640
  (result.data ?? []).map(async (track) => {
233490
233641
  const filename = track.url?.split("/audio/").pop();
233491
233642
  if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
233492
- const filePath = join17(AUDIO_DIR2, filename);
233643
+ const filePath = join18(AUDIO_DIR2, filename);
233493
233644
  const buffer2 = await readFileAsync(filePath);
233494
233645
  const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
233495
233646
  const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
@@ -233565,7 +233716,7 @@ function buildVideoGenerationProvider() {
233565
233716
  (result.data ?? []).map(async (clip) => {
233566
233717
  const filename = clip.url?.split("/videos/").pop();
233567
233718
  if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
233568
- const filePath = join17(VIDEO_DIR2, filename);
233719
+ const filePath = join18(VIDEO_DIR2, filename);
233569
233720
  const buffer2 = await readFileAsync(filePath);
233570
233721
  const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
233571
233722
  const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
@@ -233666,7 +233817,7 @@ function createWalletCommand(api) {
233666
233817
  ];
233667
233818
  let hasMnemonic3 = false;
233668
233819
  try {
233669
- if (existsSync3(MNEMONIC_FILE)) {
233820
+ if (existsSync2(MNEMONIC_FILE)) {
233670
233821
  const mnemonic = readTextFileSync(MNEMONIC_FILE).trim();
233671
233822
  if (mnemonic) {
233672
233823
  hasMnemonic3 = true;
@@ -233737,7 +233888,7 @@ function createWalletCommand(api) {
233737
233888
  ].join("\n")
233738
233889
  };
233739
233890
  }
233740
- if (existsSync3(MNEMONIC_FILE)) {
233891
+ if (existsSync2(MNEMONIC_FILE)) {
233741
233892
  const existingMnemonic = readTextFileSync(MNEMONIC_FILE).trim();
233742
233893
  if (existingMnemonic) {
233743
233894
  await savePaymentChain("solana");
@@ -233929,9 +234080,9 @@ var init_index = __esm({
233929
234080
  init_partners();
233930
234081
  activeProxyHandle = null;
233931
234082
  pendingConfiguredStartupApi = null;
233932
- IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
233933
- AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
233934
- VIDEO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "videos");
234083
+ IMAGE_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "images");
234084
+ AUDIO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "audio");
234085
+ VIDEO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "videos");
233935
234086
  plugin = {
233936
234087
  // NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
233937
234088
  // 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
@@ -234071,7 +234222,8 @@ var init_index = __esm({
234071
234222
  name: "cr-imagegen",
234072
234223
  description: "Generate an image (BlockRun image models, paid via wallet)",
234073
234224
  acceptsArgs: true,
234074
- requireAuth: false,
234225
+ // Paid from the wallet: only authorized senders.
234226
+ requireAuth: true,
234075
234227
  handler: async (ctx) => {
234076
234228
  const parsed = parseGenArgs(ctx.args ?? "");
234077
234229
  if (!parsed.prompt) {
@@ -234123,7 +234275,8 @@ ${errText}`
234123
234275
  name: "videogen",
234124
234276
  description: "Generate a short video (Grok Imagine / Seedance, paid via wallet)",
234125
234277
  acceptsArgs: true,
234126
- requireAuth: false,
234278
+ // Paid from the wallet: only authorized senders.
234279
+ requireAuth: true,
234127
234280
  handler: async (ctx) => {
234128
234281
  const parsed = parseGenArgs(ctx.args ?? "");
234129
234282
  if (!parsed.prompt) {
@@ -234172,7 +234325,8 @@ ${errText}`
234172
234325
  name: "cr-call",
234173
234326
  description: "Place an AI voice call via BlockRun + Bland.ai (paid from wallet, $0.54/call)",
234174
234327
  acceptsArgs: true,
234175
- requireAuth: false,
234328
+ // Paid from the wallet: only authorized senders.
234329
+ requireAuth: true,
234176
234330
  handler: async (ctx) => {
234177
234331
  const parsed = parseCallArgs(ctx.args ?? "");
234178
234332
  if (!parsed.to || !parsed.task) {
@@ -234369,8 +234523,8 @@ ${errText}`
234369
234523
  }
234370
234524
  resetProxyStartupState();
234371
234525
  try {
234372
- const configPath = join17(homedir14(), ".openclaw", "openclaw.json");
234373
- if (existsSync3(configPath)) {
234526
+ const configPath = join18(homedir15(), ".openclaw", "openclaw.json");
234527
+ if (existsSync2(configPath)) {
234374
234528
  const config = JSON.parse(readTextFileSync(configPath));
234375
234529
  if (config.models?.providers?.blockrun) {
234376
234530
  delete config.models.providers.blockrun;
@@ -234411,12 +234565,12 @@ ${errText}`
234411
234565
  api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
234412
234566
  }
234413
234567
  try {
234414
- const agentsDir = join17(homedir14(), ".openclaw", "agents");
234415
- if (existsSync3(agentsDir)) {
234568
+ const agentsDir = join18(homedir15(), ".openclaw", "agents");
234569
+ if (existsSync2(agentsDir)) {
234416
234570
  for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
234417
234571
  if (!entry.isDirectory()) continue;
234418
- const authPath = join17(agentsDir, entry.name, "agent", "auth-profiles.json");
234419
- if (!existsSync3(authPath)) continue;
234572
+ const authPath = join18(agentsDir, entry.name, "agent", "auth-profiles.json");
234573
+ if (!existsSync2(authPath)) continue;
234420
234574
  try {
234421
234575
  const store = JSON.parse(readTextFileSync(authPath));
234422
234576
  if (store.profiles?.["blockrun:default"]) {