unbrowse 3.0.2 → 3.1.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/dist/cli.js CHANGED
@@ -17,12 +17,21 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  });
18
18
  return to;
19
19
  };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
20
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
21
30
  var __promiseAll = (args) => Promise.all(args);
22
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
23
32
 
24
33
  // ../../src/build-info.generated.ts
25
- var BUILD_RELEASE_VERSION = "3.0.2", BUILD_GIT_SHA = "25aed2ccf282", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4wLjIiLCJnaXRfc2hhIjoiMjVhZWQyY2NmMjgyIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0AyNWFlZDJjY2YyODIiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA0VDE0OjExOjM3LjQ4NloifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "KPLG1erp1N-qP2bkczhQp8g-pod6ObDr845_DElQzdM", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "3.1.0", BUILD_GIT_SHA = "c9f9ce0e5cb1", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAiLCJnaXRfc2hhIjoiYzlmOWNlMGU1Y2IxIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0BjOWY5Y2UwZTVjYjEiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA1VDA2OjUwOjA4LjQzNVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "_2pdxyoIEYkKUHO0nXaXTubP5SrZCa-8CqStcPBsXxw", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
26
35
 
27
36
  // ../../src/version.ts
28
37
  import { createHash } from "crypto";
@@ -284,8 +293,114 @@ var init_telemetry_attribution = __esm(() => {
284
293
  ];
285
294
  });
286
295
 
296
+ // ../../src/payments/lobster-pay.ts
297
+ var exports_lobster_pay = {};
298
+ __export(exports_lobster_pay, {
299
+ payAndRetry: () => payAndRetry,
300
+ lobsterX402Fetch: () => lobsterX402Fetch,
301
+ isLobsterAvailable: () => isLobsterAvailable
302
+ });
303
+ import { execFile, execFileSync } from "node:child_process";
304
+ import { existsSync as existsSync3 } from "node:fs";
305
+ import { homedir as homedir2 } from "node:os";
306
+ import { join as join3 } from "node:path";
307
+ function getLobsterCommand() {
308
+ try {
309
+ execFileSync("lobstercash", ["--version"], { stdio: "ignore", timeout: 3000 });
310
+ return { cmd: "lobstercash", prefix: [] };
311
+ } catch (_e) {}
312
+ try {
313
+ const npmPrefix = execFileSync("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
314
+ const lobsterPath = join3(npmPrefix, "bin", "lobstercash");
315
+ if (existsSync3(lobsterPath)) {
316
+ execFileSync(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
317
+ return { cmd: lobsterPath, prefix: [] };
318
+ }
319
+ } catch (_e) {}
320
+ return null;
321
+ }
322
+ function lobsterCmd() {
323
+ if (cachedCommand === undefined)
324
+ cachedCommand = getLobsterCommand();
325
+ return cachedCommand;
326
+ }
327
+ function isLobsterAvailable() {
328
+ const agentsPath = join3(process.env.HOME || homedir2(), ".lobster", "agents.json");
329
+ return existsSync3(agentsPath);
330
+ }
331
+ function lobsterX402Fetch(url, options) {
332
+ return new Promise((resolve) => {
333
+ const resolved = lobsterCmd();
334
+ if (!resolved) {
335
+ resolve({ success: false, body: "", error: "lobstercash CLI not in PATH" });
336
+ return;
337
+ }
338
+ const { cmd, prefix } = resolved;
339
+ const args = [...prefix, "x402", "fetch", url, "--debug"];
340
+ if (options?.jsonBody) {
341
+ args.push("--json", options.jsonBody);
342
+ }
343
+ if (options?.headers) {
344
+ for (const [key, value] of Object.entries(options.headers)) {
345
+ args.push("--header", `${key}:${value}`);
346
+ }
347
+ }
348
+ const timeout = options?.timeoutMs ?? LOBSTER_PAY_TIMEOUT_MS;
349
+ args.push("--timeout", String(timeout));
350
+ execFile(cmd, args, { timeout: timeout + 5000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
351
+ if (err) {
352
+ const msg = stderr?.trim() || err.message;
353
+ console.warn(`[lobster-pay] x402 fetch failed: ${msg}`);
354
+ resolve({ success: false, body: "", error: msg });
355
+ return;
356
+ }
357
+ if (stderr) {
358
+ for (const line of stderr.split(`
359
+ `).filter(Boolean)) {
360
+ console.log(`[lobster-pay] ${line}`);
361
+ }
362
+ }
363
+ const statusMatch = stdout.match(/^Status:\s*(\d+)/m);
364
+ const statusCode = statusMatch ? parseInt(statusMatch[1], 10) : undefined;
365
+ if (statusCode && statusCode >= 400) {
366
+ resolve({ success: false, body: stdout, statusCode, error: `HTTP ${statusCode}` });
367
+ return;
368
+ }
369
+ resolve({ success: true, body: stdout, statusCode });
370
+ });
371
+ });
372
+ }
373
+ async function payAndRetry(fullUrl, options) {
374
+ if (!isLobsterAvailable()) {
375
+ console.log("[lobster-pay] lobster.cash not configured — skipping payment");
376
+ return null;
377
+ }
378
+ console.log(`[lobster-pay] attempting x402 payment for ${fullUrl}`);
379
+ const result = await lobsterX402Fetch(fullUrl, {
380
+ jsonBody: options?.body ? JSON.stringify(options.body) : undefined,
381
+ headers: options?.headers
382
+ });
383
+ if (!result.success) {
384
+ console.warn(`[lobster-pay] payment failed: ${result.error}`);
385
+ return null;
386
+ }
387
+ try {
388
+ const raw = result.body;
389
+ const jsonStart = Math.min(...[raw.indexOf("{"), raw.indexOf("[")].filter((i) => i >= 0));
390
+ const jsonStr = jsonStart >= 0 ? raw.slice(jsonStart) : raw;
391
+ const data = JSON.parse(jsonStr);
392
+ console.log("[lobster-pay] payment successful — got paid response");
393
+ return { data, paid: true };
394
+ } catch (_e) {
395
+ console.warn("[lobster-pay] paid response was not valid JSON");
396
+ return null;
397
+ }
398
+ }
399
+ var LOBSTER_PAY_TIMEOUT_MS = 30000, cachedCommand = undefined;
400
+ var init_lobster_pay = () => {};
401
+
287
402
  // ../../src/runtime/paths.ts
288
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, realpathSync } from "node:fs";
403
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, realpathSync } from "node:fs";
289
404
  import os from "node:os";
290
405
  import path from "node:path";
291
406
  import { createRequire as createRequire2 } from "node:module";
@@ -299,7 +414,7 @@ function getPackageRoot(metaUrl) {
299
414
  let dir = getModuleDir(metaUrl);
300
415
  const root = path.parse(dir).root;
301
416
  while (dir !== root) {
302
- if (existsSync4(path.join(dir, "package.json")))
417
+ if (existsSync5(path.join(dir, "package.json")))
303
418
  return dir;
304
419
  dir = path.dirname(dir);
305
420
  }
@@ -318,7 +433,7 @@ function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
318
433
  const req = createRequire2(metaUrl);
319
434
  const tsxPkg = req.resolve("tsx/package.json");
320
435
  const tsxLoader = path.join(path.dirname(tsxPkg), "dist", "loader.mjs");
321
- if (existsSync4(tsxLoader))
436
+ if (existsSync5(tsxLoader))
322
437
  return ["--import", pathToFileURL(tsxLoader).href, entrypoint];
323
438
  } catch {}
324
439
  return ["--import", "tsx", entrypoint];
@@ -327,7 +442,7 @@ function getUnbrowseHome() {
327
442
  return path.join(os.homedir(), ".unbrowse");
328
443
  }
329
444
  function ensureDir(dir) {
330
- if (!existsSync4(dir))
445
+ if (!existsSync5(dir))
331
446
  mkdirSync2(dir, { recursive: true });
332
447
  return dir;
333
448
  }
@@ -470,8 +585,8 @@ var init_logger = __esm(() => {
470
585
  });
471
586
 
472
587
  // ../../src/kuri/client.ts
473
- import { execFileSync, spawn as spawn2 } from "node:child_process";
474
- import { existsSync as existsSync6 } from "node:fs";
588
+ import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
589
+ import { existsSync as existsSync7 } from "node:fs";
475
590
  import path5 from "node:path";
476
591
  function createBrokerState(port = KURI_DEFAULT_PORT) {
477
592
  return {
@@ -496,12 +611,14 @@ function currentBundledKuriTarget() {
496
611
  return "linux-arm64";
497
612
  if (process.platform === "linux" && process.arch === "x64")
498
613
  return "linux-x64";
614
+ if (process.platform === "win32" && process.arch === "x64")
615
+ return "win-x64";
499
616
  return null;
500
617
  }
501
618
  function resolveBinaryOnPath(name) {
502
619
  const checker = process.platform === "win32" ? "where" : "which";
503
620
  try {
504
- const output = execFileSync(checker, [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
621
+ const output = execFileSync2(checker, [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
505
622
  const match = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
506
623
  return match || null;
507
624
  } catch {
@@ -544,7 +661,7 @@ function findKuriBinary() {
544
661
  if (process.env.KURI_BIN)
545
662
  return process.env.KURI_BIN;
546
663
  const candidates = getKuriBinaryCandidates();
547
- return candidates.find((candidate) => existsSync6(candidate)) ?? candidates[0] ?? kuriBinaryName();
664
+ return candidates.find((candidate) => existsSync7(candidate)) ?? candidates[0] ?? kuriBinaryName();
548
665
  }
549
666
  var KURI_DEFAULT_PORT = 7700, defaultBrokerState, brokerClients;
550
667
  var init_client2 = __esm(() => {
@@ -584,11 +701,11 @@ var init_transform = __esm(() => {
584
701
  });
585
702
 
586
703
  // ../../src/debug-trace.ts
587
- import { join as join4 } from "node:path";
704
+ import { join as join5 } from "node:path";
588
705
  import { nanoid as nanoid3 } from "nanoid";
589
706
  var TRACE_DIR;
590
707
  var init_debug_trace = __esm(() => {
591
- TRACE_DIR = process.env.TRACES_DIR ?? join4(process.cwd(), "traces");
708
+ TRACE_DIR = process.env.TRACES_DIR ?? join5(process.cwd(), "traces");
592
709
  });
593
710
 
594
711
  // ../../src/publish/sanitize.ts
@@ -678,8 +795,8 @@ var init_bundle_scanner = __esm(() => {
678
795
  });
679
796
 
680
797
  // ../../src/vault/index.ts
681
- import { join as join5 } from "path";
682
- import { homedir as homedir3 } from "os";
798
+ import { join as join6 } from "path";
799
+ import { homedir as homedir4 } from "os";
683
800
  function normalizeKeytarModule(mod) {
684
801
  let candidate = mod;
685
802
  for (let depth = 0;depth < 3; depth++) {
@@ -701,9 +818,9 @@ var init_vault = __esm(async () => {
701
818
  try {
702
819
  keytar = normalizeKeytarModule(await import("keytar"));
703
820
  } catch {}
704
- VAULT_DIR = join5(homedir3(), ".unbrowse", "vault");
705
- VAULT_FILE = join5(VAULT_DIR, "credentials.enc");
706
- KEY_FILE = join5(VAULT_DIR, ".key");
821
+ VAULT_DIR = join6(homedir4(), ".unbrowse", "vault");
822
+ VAULT_FILE = join6(VAULT_DIR, "credentials.enc");
823
+ KEY_FILE = join6(VAULT_DIR, ".key");
707
824
  vaultLock = Promise.resolve();
708
825
  });
709
826
 
@@ -854,7 +971,7 @@ var init_schema_review = __esm(() => {
854
971
  });
855
972
 
856
973
  // ../../src/indexer/index.ts
857
- import { join as join6 } from "node:path";
974
+ import { join as join7 } from "node:path";
858
975
  var SKILL_SNAPSHOT_DIR, indexInFlight, pendingIndexJobs;
859
976
  var init_indexer = __esm(async () => {
860
977
  init_graph();
@@ -869,7 +986,7 @@ var init_indexer = __esm(async () => {
869
986
  init_graph();
870
987
  init_schema_review();
871
988
  await init_orchestrator();
872
- SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join6(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
989
+ SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join7(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
873
990
  indexInFlight = new Map;
874
991
  pendingIndexJobs = new Map;
875
992
  });
@@ -1046,8 +1163,8 @@ var init_routing_telemetry = __esm(() => {
1046
1163
  });
1047
1164
  // ../../src/orchestrator/index.ts
1048
1165
  import { nanoid as nanoid9 } from "nanoid";
1049
- import { existsSync as existsSync7, writeFileSync as writeFileSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "node:fs";
1050
- import { dirname as dirname2, join as join7 } from "node:path";
1166
+ import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "node:fs";
1167
+ import { dirname as dirname2, join as join8 } from "node:path";
1051
1168
  var LIVE_CAPTURE_TIMEOUT_MS, capturedDomainCache, captureInFlight, captureDomainLocks, skillRouteCache, ROUTE_CACHE_FILE, SKILL_SNAPSHOT_DIR2, domainSkillCache, DOMAIN_CACHE_FILE, _routeCacheDirty = false, routeCacheFlushTimer, routeResultCache, ROUTE_CACHE_TTL, MARKETPLACE_HYDRATE_LIMIT, MARKETPLACE_GET_SKILL_TIMEOUT_MS, MARKETPLACE_DOMAIN_SEARCH_K, MARKETPLACE_GLOBAL_SEARCH_K, SEARCH_INTENT_STOPWORDS;
1052
1169
  var init_orchestrator = __esm(async () => {
1053
1170
  init_client();
@@ -1077,12 +1194,12 @@ var init_orchestrator = __esm(async () => {
1077
1194
  captureInFlight = new Map;
1078
1195
  captureDomainLocks = new Map;
1079
1196
  skillRouteCache = new Map;
1080
- ROUTE_CACHE_FILE = join7(process.env.HOME ?? "/tmp", ".unbrowse", "route-cache.json");
1081
- SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join7(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
1197
+ ROUTE_CACHE_FILE = join8(process.env.HOME ?? "/tmp", ".unbrowse", "route-cache.json");
1198
+ SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join8(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
1082
1199
  domainSkillCache = new Map;
1083
- DOMAIN_CACHE_FILE = join7(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
1200
+ DOMAIN_CACHE_FILE = join8(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
1084
1201
  try {
1085
- if (existsSync7(DOMAIN_CACHE_FILE)) {
1202
+ if (existsSync8(DOMAIN_CACHE_FILE)) {
1086
1203
  const data = JSON.parse(readFileSync5(DOMAIN_CACHE_FILE, "utf-8"));
1087
1204
  for (const [k, v] of Object.entries(data)) {
1088
1205
  const entry = v;
@@ -1099,7 +1216,7 @@ var init_orchestrator = __esm(async () => {
1099
1216
  _routeCacheDirty = false;
1100
1217
  try {
1101
1218
  const dir = dirname2(ROUTE_CACHE_FILE);
1102
- if (!existsSync7(dir))
1219
+ if (!existsSync8(dir))
1103
1220
  mkdirSync4(dir, { recursive: true });
1104
1221
  const entries = Object.fromEntries(skillRouteCache);
1105
1222
  writeFileSync3(ROUTE_CACHE_FILE, JSON.stringify(entries), "utf-8");
@@ -1107,7 +1224,7 @@ var init_orchestrator = __esm(async () => {
1107
1224
  }, 5000);
1108
1225
  routeCacheFlushTimer.unref?.();
1109
1226
  try {
1110
- if (existsSync7(ROUTE_CACHE_FILE)) {
1227
+ if (existsSync8(ROUTE_CACHE_FILE)) {
1111
1228
  const data = JSON.parse(readFileSync5(ROUTE_CACHE_FILE, "utf-8"));
1112
1229
  for (const [k, v] of Object.entries(data)) {
1113
1230
  const entry = v;
@@ -1198,6 +1315,60 @@ var init_orchestrator = __esm(async () => {
1198
1315
  ]);
1199
1316
  });
1200
1317
 
1318
+ // ../../src/payments/wallet.ts
1319
+ var exports_wallet = {};
1320
+ __export(exports_wallet, {
1321
+ getWalletContext: () => getWalletContext2,
1322
+ checkWalletConfigured: () => checkWalletConfigured2
1323
+ });
1324
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
1325
+ import { homedir as homedir5 } from "node:os";
1326
+ import { join as join10 } from "node:path";
1327
+ function asNonEmptyString2(value) {
1328
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
1329
+ }
1330
+ function getLobsterWalletFromLocalConfig2() {
1331
+ const agentsPath = join10(process.env.HOME || homedir5(), ".lobster", "agents.json");
1332
+ if (!existsSync12(agentsPath))
1333
+ return;
1334
+ try {
1335
+ const raw = JSON.parse(readFileSync8(agentsPath, "utf8"));
1336
+ const activeAgentId = asNonEmptyString2(raw.activeAgentId);
1337
+ const activeAgent = Array.isArray(raw.agents) ? raw.agents.find((agent) => asNonEmptyString2(agent.id) === activeAgentId) : activeAgentId ? raw.agents?.[activeAgentId] : undefined;
1338
+ return asNonEmptyString2(activeAgent?.authorizedWallets?.solana) ?? asNonEmptyString2(activeAgent?.walletAddress) ?? asNonEmptyString2(activeAgent?.wallet_address);
1339
+ } catch {
1340
+ return;
1341
+ }
1342
+ }
1343
+ function getWalletContext2() {
1344
+ const lobsterWallet = asNonEmptyString2(process.env.LOBSTER_WALLET_ADDRESS);
1345
+ if (lobsterWallet) {
1346
+ return { wallet_address: lobsterWallet, wallet_provider: "lobster.cash" };
1347
+ }
1348
+ const genericWallet = asNonEmptyString2(process.env.AGENT_WALLET_ADDRESS);
1349
+ if (genericWallet) {
1350
+ return {
1351
+ wallet_address: genericWallet,
1352
+ wallet_provider: asNonEmptyString2(process.env.AGENT_WALLET_PROVIDER)
1353
+ };
1354
+ }
1355
+ const localLobsterWallet = getLobsterWalletFromLocalConfig2();
1356
+ if (localLobsterWallet) {
1357
+ return { wallet_address: localLobsterWallet, wallet_provider: "lobster.cash" };
1358
+ }
1359
+ return {};
1360
+ }
1361
+ function checkWalletConfigured2() {
1362
+ const wallet = getWalletContext2();
1363
+ if (!wallet.wallet_address)
1364
+ return { configured: false };
1365
+ return {
1366
+ configured: true,
1367
+ provider: wallet.wallet_provider ?? "unknown"
1368
+ };
1369
+ }
1370
+ var init_wallet2 = () => {};
1371
+
1201
1372
  // ../../src/cli.ts
1202
1373
  import { config as loadEnv } from "dotenv";
1203
1374
  import { spawn as spawn3 } from "child_process";
@@ -1207,9 +1378,9 @@ init_version();
1207
1378
  init_cascade();
1208
1379
  init_wallet();
1209
1380
  init_telemetry_attribution();
1210
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2 } from "fs";
1211
- import { join as join3 } from "path";
1212
- import { homedir as homedir2, hostname } from "os";
1381
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2 } from "fs";
1382
+ import { join as join4 } from "path";
1383
+ import { homedir as homedir3, hostname } from "os";
1213
1384
  import { randomBytes, createHash as createHash2 } from "crypto";
1214
1385
  import { createInterface } from "readline";
1215
1386
  var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
@@ -1244,13 +1415,13 @@ function decodeBase64Json(value) {
1244
1415
  function getConfigDir() {
1245
1416
  if (process.env.UNBROWSE_CONFIG_DIR)
1246
1417
  return process.env.UNBROWSE_CONFIG_DIR;
1247
- return PROFILE_NAME ? join3(homedir2(), ".unbrowse", "profiles", PROFILE_NAME) : join3(homedir2(), ".unbrowse");
1418
+ return PROFILE_NAME ? join4(homedir3(), ".unbrowse", "profiles", PROFILE_NAME) : join4(homedir3(), ".unbrowse");
1248
1419
  }
1249
1420
  function getConfigPath() {
1250
- return join3(getConfigDir(), "config.json");
1421
+ return join4(getConfigDir(), "config.json");
1251
1422
  }
1252
1423
  function getInstallTelemetryPath() {
1253
- return join3(getConfigDir(), "install-state.json");
1424
+ return join4(getConfigDir(), "install-state.json");
1254
1425
  }
1255
1426
  function getLandingToken() {
1256
1427
  const token = process.env.UNBROWSE_LANDING_TOKEN?.trim();
@@ -1265,7 +1436,7 @@ function getActiveProfile() {
1265
1436
  function loadConfig() {
1266
1437
  try {
1267
1438
  const configPath = getConfigPath();
1268
- if (existsSync3(configPath)) {
1439
+ if (existsSync4(configPath)) {
1269
1440
  return JSON.parse(readFileSync3(configPath, "utf-8"));
1270
1441
  }
1271
1442
  } catch {}
@@ -1274,14 +1445,14 @@ function loadConfig() {
1274
1445
  function saveConfig(config) {
1275
1446
  const configDir = getConfigDir();
1276
1447
  const configPath = getConfigPath();
1277
- if (!existsSync3(configDir))
1448
+ if (!existsSync4(configDir))
1278
1449
  mkdirSync(configDir, { recursive: true });
1279
1450
  writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
1280
1451
  }
1281
1452
  function loadInstallTelemetryState() {
1282
1453
  try {
1283
1454
  const statePath = getInstallTelemetryPath();
1284
- if (existsSync3(statePath)) {
1455
+ if (existsSync4(statePath)) {
1285
1456
  return JSON.parse(readFileSync3(statePath, "utf-8"));
1286
1457
  }
1287
1458
  } catch {}
@@ -1290,7 +1461,7 @@ function loadInstallTelemetryState() {
1290
1461
  function saveInstallTelemetryState(state) {
1291
1462
  const configDir = getConfigDir();
1292
1463
  const statePath = getInstallTelemetryPath();
1293
- if (!existsSync3(configDir))
1464
+ if (!existsSync4(configDir))
1294
1465
  mkdirSync(configDir, { recursive: true });
1295
1466
  writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 384 });
1296
1467
  }
@@ -1547,6 +1718,26 @@ async function apiRequest(method, path, body, opts) {
1547
1718
  const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
1548
1719
  const legacyPaymentTerms = res.headers.get("X-Payment-Required");
1549
1720
  const terms = paymentRequired ? decodeBase64Json(paymentRequired) : legacyPaymentTerms ? JSON.parse(legacyPaymentTerms) : data.terms;
1721
+ try {
1722
+ const { isLobsterAvailable: isLobsterAvailable2, payAndRetry: payAndRetry2 } = await Promise.resolve().then(() => (init_lobster_pay(), exports_lobster_pay));
1723
+ if (isLobsterAvailable2()) {
1724
+ const fullUrl = `${API_URL}${path}`;
1725
+ const paidResult = await payAndRetry2(fullUrl, {
1726
+ body,
1727
+ headers: {
1728
+ "Content-Type": "application/json",
1729
+ "Accept-Encoding": "gzip, deflate",
1730
+ ...releaseAttestationHeaders,
1731
+ ...key ? { Authorization: `Bearer ${key}` } : {}
1732
+ }
1733
+ });
1734
+ if (paidResult) {
1735
+ return { data: paidResult.data, headers: new Headers };
1736
+ }
1737
+ }
1738
+ } catch (payErr) {
1739
+ console.warn(`[x402] lobster pay-and-retry failed: ${payErr.message}`);
1740
+ }
1550
1741
  const err = new Error(`Payment required: ${data.error ?? "This skill requires payment"}`);
1551
1742
  err.x402 = true;
1552
1743
  err.terms = terms;
@@ -1565,19 +1756,10 @@ async function api(method, path, body, opts) {
1565
1756
  return data;
1566
1757
  }
1567
1758
  function parseInstallAttribution() {
1568
- const result = {};
1569
- const b64 = process.env.UNBROWSE_ATTRIBUTION_B64;
1570
- if (b64) {
1571
- try {
1572
- const decoded = JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
1573
- if (decoded && typeof decoded === "object")
1574
- result.install_attribution = decoded;
1575
- } catch {}
1576
- }
1577
1759
  const token = process.env.UNBROWSE_LANDING_TOKEN;
1578
1760
  if (token && token.length < 2048)
1579
- result.landing_token = token;
1580
- return result;
1761
+ return { landing_token: token };
1762
+ return {};
1581
1763
  }
1582
1764
  async function promptTosAcceptance(summary, tosUrl) {
1583
1765
  if (process.env.UNBROWSE_NON_INTERACTIVE === "1") {
@@ -2159,7 +2341,7 @@ async function restartServer(baseUrl, metaUrl) {
2159
2341
  }
2160
2342
 
2161
2343
  // ../../src/runtime/paths.ts
2162
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, realpathSync as realpathSync2 } from "node:fs";
2344
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, realpathSync as realpathSync2 } from "node:fs";
2163
2345
  import path3 from "node:path";
2164
2346
  import { createRequire as createRequire3 } from "node:module";
2165
2347
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
@@ -2179,7 +2361,7 @@ function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
2179
2361
  const req = createRequire3(metaUrl);
2180
2362
  const tsxPkg = req.resolve("tsx/package.json");
2181
2363
  const tsxLoader = path3.join(path3.dirname(tsxPkg), "dist", "loader.mjs");
2182
- if (existsSync5(tsxLoader))
2364
+ if (existsSync6(tsxLoader))
2183
2365
  return ["--import", pathToFileURL2(tsxLoader).href, entrypoint];
2184
2366
  } catch {}
2185
2367
  return ["--import", "tsx", entrypoint];
@@ -2209,8 +2391,8 @@ init_publish();
2209
2391
  init_settings();
2210
2392
  init_graph();
2211
2393
  init_schema_review();
2212
- import { join as join8 } from "node:path";
2213
- var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join8(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
2394
+ import { join as join9 } from "node:path";
2395
+ var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join9(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
2214
2396
  var indexInFlight2 = new Map;
2215
2397
  var pendingIndexJobs2 = new Map;
2216
2398
  async function drainPendingIndexJobs() {
@@ -2247,14 +2429,14 @@ init_paths();
2247
2429
  init_client2();
2248
2430
  init_logger();
2249
2431
  init_wallet();
2250
- import { execFileSync as execFileSync2 } from "node:child_process";
2251
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2432
+ import { execFileSync as execFileSync3 } from "node:child_process";
2433
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2252
2434
  import os4 from "node:os";
2253
2435
  import path7 from "node:path";
2254
2436
 
2255
2437
  // ../../src/runtime/update-hints.ts
2256
2438
  init_paths();
2257
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
2439
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
2258
2440
  import os3 from "node:os";
2259
2441
  import path6 from "node:path";
2260
2442
  var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
@@ -2268,7 +2450,7 @@ function getConfigDir2() {
2268
2450
  return path6.join(getHomeDir(), ".unbrowse");
2269
2451
  }
2270
2452
  function ensureDir2(dir) {
2271
- if (!existsSync8(dir))
2453
+ if (!existsSync9(dir))
2272
2454
  mkdirSync5(dir, { recursive: true });
2273
2455
  return dir;
2274
2456
  }
@@ -2291,7 +2473,7 @@ function detectRepoRoot(start2) {
2291
2473
  let dir = path6.resolve(start2);
2292
2474
  const root = path6.parse(dir).root;
2293
2475
  while (dir !== root) {
2294
- if (existsSync8(path6.join(dir, ".git")))
2476
+ if (existsSync9(path6.join(dir, ".git")))
2295
2477
  return dir;
2296
2478
  dir = path6.dirname(dir);
2297
2479
  }
@@ -2370,12 +2552,12 @@ codex_hooks = true
2370
2552
  }
2371
2553
  function writeCodexHook(metaUrl) {
2372
2554
  const configPath = getCodexConfigPath();
2373
- if (!existsSync8(path6.dirname(configPath))) {
2555
+ if (!existsSync9(path6.dirname(configPath))) {
2374
2556
  return { host: "codex", action: "not-detected", config_file: configPath };
2375
2557
  }
2376
2558
  try {
2377
2559
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2378
- const fileExistsBefore = existsSync8(configPath);
2560
+ const fileExistsBefore = existsSync9(configPath);
2379
2561
  let content = fileExistsBefore ? readFileSync6(configPath, "utf8") : "";
2380
2562
  const previous = content;
2381
2563
  content = ensureCodexHooksFeature(content);
@@ -2410,13 +2592,13 @@ command = ${JSON.stringify(command)}
2410
2592
  }
2411
2593
  function writeClaudeHook(metaUrl) {
2412
2594
  const settingsPath = getClaudeSettingsPath();
2413
- if (!existsSync8(path6.dirname(settingsPath))) {
2595
+ if (!existsSync9(path6.dirname(settingsPath))) {
2414
2596
  return { host: "claude", action: "not-detected", config_file: settingsPath };
2415
2597
  }
2416
2598
  try {
2417
2599
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2418
2600
  const command = `node "${hookScript}"`;
2419
- const fileExistsBefore = existsSync8(settingsPath);
2601
+ const fileExistsBefore = existsSync9(settingsPath);
2420
2602
  const settings = readJsonFile(settingsPath) ?? {};
2421
2603
  settings.hooks ??= {};
2422
2604
  settings.hooks.SessionStart ??= [];
@@ -2454,7 +2636,7 @@ function configureUpdateHintHooks(metaUrl, install) {
2454
2636
  function hasBinary(name) {
2455
2637
  const checker = process.platform === "win32" ? "where" : "which";
2456
2638
  try {
2457
- execFileSync2(checker, [name], { stdio: "ignore" });
2639
+ execFileSync3(checker, [name], { stdio: "ignore" });
2458
2640
  return true;
2459
2641
  } catch {
2460
2642
  return false;
@@ -2481,7 +2663,7 @@ function getOpenCodeProjectCommandsDir(cwd) {
2481
2663
  return path7.join(cwd, ".opencode", "commands");
2482
2664
  }
2483
2665
  function detectOpenCode(cwd) {
2484
- return hasBinary("opencode") || existsSync9(path7.join(resolveConfigHome(), "opencode")) || existsSync9(path7.join(cwd, ".opencode"));
2666
+ return hasBinary("opencode") || existsSync10(path7.join(resolveConfigHome(), "opencode")) || existsSync10(path7.join(cwd, ".opencode"));
2485
2667
  }
2486
2668
  function renderOpenCodeCommand() {
2487
2669
  return `---
@@ -2509,11 +2691,11 @@ function writeOpenCodeCommand(scope, cwd) {
2509
2691
  if (scope === "auto" && !detected) {
2510
2692
  return { detected: false, action: "not-detected", scope: "off" };
2511
2693
  }
2512
- const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync9(path7.join(cwd, ".opencode")) ? "project" : "global";
2694
+ const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync10(path7.join(cwd, ".opencode")) ? "project" : "global";
2513
2695
  const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
2514
2696
  const commandFile = path7.join(ensureDir(commandsDir), "unbrowse.md");
2515
2697
  const content = renderOpenCodeCommand();
2516
- const action2 = existsSync9(commandFile) ? "updated" : "installed";
2698
+ const action2 = existsSync10(commandFile) ? "updated" : "installed";
2517
2699
  mkdirSync6(path7.dirname(commandFile), { recursive: true });
2518
2700
  writeFileSync5(commandFile, content);
2519
2701
  return {
@@ -2525,10 +2707,10 @@ function writeOpenCodeCommand(scope, cwd) {
2525
2707
  }
2526
2708
  async function ensureBrowserEngineInstalled() {
2527
2709
  const binary = findKuriBinary();
2528
- if (existsSync9(binary)) {
2710
+ if (existsSync10(binary)) {
2529
2711
  return { installed: true, action: "already-installed" };
2530
2712
  }
2531
- const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync9(path7.join(candidate, "build.zig")));
2713
+ const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync10(path7.join(candidate, "build.zig")));
2532
2714
  if (!sourceDir) {
2533
2715
  return {
2534
2716
  installed: false,
@@ -2544,13 +2726,13 @@ async function ensureBrowserEngineInstalled() {
2544
2726
  };
2545
2727
  }
2546
2728
  try {
2547
- execFileSync2("zig", ["build", "-Doptimize=ReleaseFast"], {
2729
+ execFileSync3("zig", ["build", "-Doptimize=ReleaseFast"], {
2548
2730
  cwd: sourceDir,
2549
2731
  stdio: "inherit",
2550
2732
  timeout: 300000
2551
2733
  });
2552
2734
  const builtBinary = findKuriBinary();
2553
- if (existsSync9(builtBinary)) {
2735
+ if (existsSync10(builtBinary)) {
2554
2736
  return {
2555
2737
  installed: true,
2556
2738
  action: "installed",
@@ -2575,11 +2757,11 @@ async function runSetup(options) {
2575
2757
  const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
2576
2758
  const walletCheck = checkWalletConfigured();
2577
2759
  const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
2578
- const lobsterInstalled = hasBinary("lobstercash") || existsSync9(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2760
+ const lobsterInstalled = hasBinary("lobstercash") || existsSync10(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2579
2761
  if (!skipWalletSetup && !walletCheck.configured && lobsterInstalled) {
2580
2762
  console.log("[unbrowse] Crossmint lobster.cash detected but wallet not configured — running wallet setup...");
2581
2763
  try {
2582
- execFileSync2("npx", ["@crossmint/lobster-cli", "setup"], {
2764
+ execFileSync3("npx", ["@crossmint/lobster-cli", "setup"], {
2583
2765
  stdio: "inherit",
2584
2766
  timeout: 60000
2585
2767
  });
@@ -2615,7 +2797,7 @@ async function runSetup(options) {
2615
2797
 
2616
2798
  // ../../src/runtime/update-hints.ts
2617
2799
  init_paths();
2618
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
2800
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
2619
2801
  import os5 from "node:os";
2620
2802
  import path8 from "node:path";
2621
2803
  var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
@@ -2629,7 +2811,7 @@ function getConfigDir3() {
2629
2811
  return path8.join(getHomeDir2(), ".unbrowse");
2630
2812
  }
2631
2813
  function ensureDir3(dir) {
2632
- if (!existsSync10(dir))
2814
+ if (!existsSync11(dir))
2633
2815
  mkdirSync7(dir, { recursive: true });
2634
2816
  return dir;
2635
2817
  }
@@ -2655,7 +2837,7 @@ function detectRepoRoot2(start2) {
2655
2837
  let dir = path8.resolve(start2);
2656
2838
  const root = path8.parse(dir).root;
2657
2839
  while (dir !== root) {
2658
- if (existsSync10(path8.join(dir, ".git")))
2840
+ if (existsSync11(path8.join(dir, ".git")))
2659
2841
  return dir;
2660
2842
  dir = path8.dirname(dir);
2661
2843
  }
@@ -2789,6 +2971,7 @@ loadEnv({ quiet: true });
2789
2971
  loadEnv({ path: ".env.runtime", quiet: true });
2790
2972
  var BASE_URL = process.env.UNBROWSE_URL || "http://localhost:6969";
2791
2973
  var CLI_CLIENT_ID = process.env.UNBROWSE_CLIENT_ID || `cli-${process.ppid || process.pid}`;
2974
+ var walletNudgeShown = false;
2792
2975
  function parseArgs(argv) {
2793
2976
  const raw = argv.slice(2);
2794
2977
  const command = raw[0] && !raw[0].startsWith("--") ? raw[0] : "help";
@@ -3098,6 +3281,16 @@ async function cmdResolve(flags) {
3098
3281
  emitNextActionSummary(result);
3099
3282
  const skill = result.skill;
3100
3283
  const trace = result.trace;
3284
+ if (trace?.success && !walletNudgeShown) {
3285
+ try {
3286
+ const { checkWalletConfigured: checkWalletConfigured3 } = await Promise.resolve().then(() => (init_wallet2(), exports_wallet));
3287
+ const wallet = checkWalletConfigured3();
3288
+ if (!wallet.configured) {
3289
+ info("You're indexing routes but have no payout wallet. Run: npx @crossmint/lobster-cli setup");
3290
+ walletNudgeShown = true;
3291
+ }
3292
+ } catch (_e) {}
3293
+ }
3101
3294
  if (skill?.skill_id && trace) {
3102
3295
  result._feedback = `unbrowse feedback --skill ${skill.skill_id} --endpoint ${trace.endpoint_id || "?"} --rating <1-5>`;
3103
3296
  }
@@ -3481,6 +3674,16 @@ async function cmdSetup(flags) {
3481
3674
  info(`${hook.host} update hint hook ${hook.action} at ${hook.config_file}`);
3482
3675
  }
3483
3676
  }
3677
+ if (report.wallet.configured) {
3678
+ info(`Wallet configured (${report.wallet.provider}): ${report.wallet.wallet_address ?? "linked"}`);
3679
+ } else if (report.wallet.lobster_installed) {
3680
+ info("Wallet not paired \u2014 your indexed routes won't earn payouts.");
3681
+ info("Run: npx @crossmint/lobster-cli setup");
3682
+ } else {
3683
+ info("No wallet configured \u2014 you're indexing routes for free.");
3684
+ info("Set up a wallet so you earn when agents use your routes:");
3685
+ info(" npx @crossmint/lobster-cli setup");
3686
+ }
3484
3687
  await recordInstallTelemetryEvent("setup", {
3485
3688
  hostType,
3486
3689
  status: report.browser_engine.action === "failed" ? "failed" : "installed",