unbrowse 6.3.0 → 6.4.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
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "6.3.0", BUILD_GIT_SHA = "ebf3580e8a7b", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi4zLjAiLCJnaXRfc2hhIjoiZWJmMzU4MGU4YTdiIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUBlYmYzNTgwZThhN2IiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTAxVDA1OjU0OjI4LjgyNloifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "eROrMsDX6qJfzkUSaa6C9my4wf18yIDN6v0qG57deps", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "6.4.0", BUILD_GIT_SHA = "5445eb4fd89e", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi40LjAiLCJnaXRfc2hhIjoiNTQ0NWViNGZkODllIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUA1NDQ1ZWI0ZmQ4OWUiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTAxVDA5OjQ5OjE0LjcyMFoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "3l8L_1jJ1G9weeuFgVBrYwcglXLwH_AnfmsnXqdPigg", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -515,8 +515,11 @@ var init_client = __esm(() => {
515
515
 
516
516
  // ../../src/marketplace/index.ts
517
517
  import { nanoid } from "nanoid";
518
+ var TTL_MS, marketplaceCache;
518
519
  var init_marketplace = __esm(() => {
519
520
  init_client();
521
+ TTL_MS = 5 * 60 * 1000;
522
+ marketplaceCache = new Map;
520
523
  });
521
524
 
522
525
  // ../../src/domain.ts
@@ -1633,6 +1636,102 @@ var init_schema_review = __esm(() => {
1633
1636
  init_sanitize();
1634
1637
  });
1635
1638
 
1639
+ // ../../src/config/contribution.ts
1640
+ import fs2 from "node:fs";
1641
+ import path6 from "node:path";
1642
+ import os3 from "node:os";
1643
+ function configPath() {
1644
+ return process.env.UNBROWSE_CONFIG_PATH || path6.join(os3.homedir(), ".unbrowse", "config.json");
1645
+ }
1646
+ function freshDefault() {
1647
+ return {
1648
+ contribution: { ...DEFAULT.contribution },
1649
+ rev_share: { ...DEFAULT.rev_share },
1650
+ notice_shown_count: 0
1651
+ };
1652
+ }
1653
+ function getContributionConfig() {
1654
+ if (cached)
1655
+ return cached;
1656
+ const p = configPath();
1657
+ let fileExisted = false;
1658
+ let raw = null;
1659
+ try {
1660
+ const content = fs2.readFileSync(p, "utf-8");
1661
+ fileExisted = true;
1662
+ if (content.trim().length > 0) {
1663
+ raw = JSON.parse(content);
1664
+ }
1665
+ } catch {}
1666
+ if (!raw) {
1667
+ cached = freshDefault();
1668
+ return cached;
1669
+ }
1670
+ const contributionRaw = raw.contribution ?? null;
1671
+ const revShareRaw = raw.rev_share ?? null;
1672
+ const isExistingUserMigration = fileExisted && !contributionRaw;
1673
+ cached = {
1674
+ contribution: {
1675
+ share_pointers: !!(contributionRaw?.share_pointers ?? DEFAULT.contribution.share_pointers),
1676
+ set_via: contributionRaw?.set_via ?? DEFAULT.contribution.set_via,
1677
+ ...contributionRaw?.set_at ? { set_at: String(contributionRaw.set_at) } : {}
1678
+ },
1679
+ rev_share: {
1680
+ opted_in: !!(revShareRaw?.opted_in ?? DEFAULT.rev_share.opted_in),
1681
+ ...revShareRaw?.wallet_address ? { wallet_address: String(revShareRaw.wallet_address) } : {}
1682
+ },
1683
+ notice_shown_count: typeof raw.notice_shown_count === "number" ? raw.notice_shown_count : isExistingUserMigration ? 5 : 0
1684
+ };
1685
+ if (isExistingUserMigration) {
1686
+ try {
1687
+ const merged = { ...raw, ...cached };
1688
+ fs2.mkdirSync(path6.dirname(p), { recursive: true });
1689
+ fs2.writeFileSync(p, JSON.stringify(merged, null, 2));
1690
+ } catch {}
1691
+ }
1692
+ return cached;
1693
+ }
1694
+ function setContributionConfig(updates) {
1695
+ const current = getContributionConfig();
1696
+ const next = {
1697
+ contribution: {
1698
+ ...current.contribution,
1699
+ ...updates.contribution ?? {},
1700
+ set_at: new Date().toISOString()
1701
+ },
1702
+ rev_share: {
1703
+ ...current.rev_share,
1704
+ ...updates.rev_share ?? {}
1705
+ },
1706
+ notice_shown_count: updates.notice_shown_count ?? current.notice_shown_count ?? 0
1707
+ };
1708
+ const p = configPath();
1709
+ let existing = {};
1710
+ try {
1711
+ const content = fs2.readFileSync(p, "utf-8");
1712
+ if (content.trim())
1713
+ existing = JSON.parse(content);
1714
+ } catch {}
1715
+ const merged = { ...existing, ...next };
1716
+ fs2.mkdirSync(path6.dirname(p), { recursive: true });
1717
+ fs2.writeFileSync(p, JSON.stringify(merged, null, 2));
1718
+ cached = next;
1719
+ }
1720
+ function decrementNoticeCounter() {
1721
+ const cfg = getContributionConfig();
1722
+ const next = Math.max(0, (cfg.notice_shown_count ?? 0) - 1);
1723
+ setContributionConfig({ notice_shown_count: next });
1724
+ return next;
1725
+ }
1726
+ var DEFAULT, cached = null;
1727
+ var init_contribution = __esm(() => {
1728
+ DEFAULT = {
1729
+ contribution: { share_pointers: false, set_via: "default" },
1730
+ rev_share: { opted_in: false },
1731
+ notice_shown_count: 0
1732
+ };
1733
+ });
1734
+
1636
1735
  // ../../src/indexer/index.ts
1637
1736
  import { join as join9 } from "node:path";
1638
1737
  var SKILL_SNAPSHOT_DIR, indexInFlight, pendingIndexJobs;
@@ -1648,6 +1747,7 @@ var init_indexer = __esm(async () => {
1648
1747
  init_settings();
1649
1748
  init_graph();
1650
1749
  init_schema_review();
1750
+ init_contribution();
1651
1751
  await init_orchestrator();
1652
1752
  SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join9(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
1653
1753
  indexInFlight = new Map;
@@ -1664,9 +1764,9 @@ var init_payments = __esm(() => {
1664
1764
  });
1665
1765
 
1666
1766
  // ../../src/execution/robots.ts
1667
- var TTL_MS, cache;
1767
+ var TTL_MS2, cache;
1668
1768
  var init_robots = __esm(() => {
1669
- TTL_MS = 24 * 60 * 60 * 1000;
1769
+ TTL_MS2 = 24 * 60 * 60 * 1000;
1670
1770
  cache = new Map;
1671
1771
  });
1672
1772
 
@@ -1871,6 +1971,13 @@ import { nanoid as nanoid8 } from "nanoid";
1871
1971
  var init_routing_telemetry = __esm(() => {
1872
1972
  init_telemetry();
1873
1973
  });
1974
+
1975
+ // ../../src/orchestrator/resolve-race.ts
1976
+ var init_resolve_race = __esm(async () => {
1977
+ init_probe();
1978
+ init_marketplace();
1979
+ await init_execution();
1980
+ });
1874
1981
  // ../../src/orchestrator/index.ts
1875
1982
  import { nanoid as nanoid9 } from "nanoid";
1876
1983
  import { existsSync as existsSync12, writeFileSync as writeFileSync4, readFileSync as readFileSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "node:fs";
@@ -1907,7 +2014,8 @@ var init_orchestrator = __esm(async () => {
1907
2014
  init_execution(),
1908
2015
  init_dag_advisor(),
1909
2016
  init_prefetch(),
1910
- init_runtime()
2017
+ init_runtime(),
2018
+ init_resolve_race()
1911
2019
  ]);
1912
2020
  LIVE_CAPTURE_TIMEOUT_MS = Number(process.env.UNBROWSE_LIVE_CAPTURE_TIMEOUT_MS ?? "120000");
1913
2021
  capturedDomainCache = new Map;
@@ -2291,9 +2399,9 @@ function getChromiumKeychainServiceName2(opts) {
2291
2399
  }
2292
2400
  function getChromiumDecryptionKey2(opts) {
2293
2401
  const service = getChromiumKeychainServiceName2(opts);
2294
- const cached = _chromiumKeyCache2.get(service);
2295
- if (cached)
2296
- return cached;
2402
+ const cached2 = _chromiumKeyCache2.get(service);
2403
+ if (cached2)
2404
+ return cached2;
2297
2405
  if (platform2() !== "darwin")
2298
2406
  return null;
2299
2407
  try {
@@ -3837,6 +3945,7 @@ init_publish();
3837
3945
  init_settings();
3838
3946
  init_graph();
3839
3947
  init_schema_review();
3948
+ init_contribution();
3840
3949
  import { join as join11 } from "node:path";
3841
3950
  var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join11(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
3842
3951
  var indexInFlight2 = new Map;
@@ -3877,23 +3986,23 @@ init_logger();
3877
3986
  init_wallet();
3878
3987
  import { execFileSync as execFileSync4 } from "node:child_process";
3879
3988
  import { existsSync as existsSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "node:fs";
3880
- import os4 from "node:os";
3881
- import path7 from "node:path";
3989
+ import os5 from "node:os";
3990
+ import path8 from "node:path";
3882
3991
 
3883
3992
  // ../../src/runtime/update-hints.ts
3884
3993
  init_paths();
3885
3994
  import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
3886
- import os3 from "node:os";
3887
- import path6 from "node:path";
3995
+ import os4 from "node:os";
3996
+ import path7 from "node:path";
3888
3997
  var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
3889
3998
  var CODEX_MARKER = "# Unbrowse update hints — managed by unbrowse setup";
3890
3999
  function getHomeDir() {
3891
- return process.env.HOME || os3.homedir();
4000
+ return process.env.HOME || os4.homedir();
3892
4001
  }
3893
4002
  function getConfigDir2() {
3894
4003
  if (process.env.UNBROWSE_CONFIG_DIR)
3895
4004
  return process.env.UNBROWSE_CONFIG_DIR;
3896
- return path6.join(getHomeDir(), ".unbrowse");
4005
+ return path7.join(getHomeDir(), ".unbrowse");
3897
4006
  }
3898
4007
  function ensureDir3(dir) {
3899
4008
  if (!existsSync13(dir))
@@ -3908,20 +4017,20 @@ function readJsonFile(file) {
3908
4017
  }
3909
4018
  }
3910
4019
  function writeJsonFile(file, value) {
3911
- ensureDir3(path6.dirname(file));
4020
+ ensureDir3(path7.dirname(file));
3912
4021
  writeFileSync5(file, `${JSON.stringify(value, null, 2)}
3913
4022
  `);
3914
4023
  }
3915
4024
  function getInstallSourcePath() {
3916
- return path6.join(getConfigDir2(), "install-source.json");
4025
+ return path7.join(getConfigDir2(), "install-source.json");
3917
4026
  }
3918
4027
  function detectRepoRoot(start2) {
3919
- let dir = path6.resolve(start2);
3920
- const root = path6.parse(dir).root;
4028
+ let dir = path7.resolve(start2);
4029
+ const root = path7.parse(dir).root;
3921
4030
  while (dir !== root) {
3922
- if (existsSync13(path6.join(dir, ".git")))
4031
+ if (existsSync13(path7.join(dir, ".git")))
3923
4032
  return dir;
3924
- dir = path6.dirname(dir);
4033
+ dir = path7.dirname(dir);
3925
4034
  }
3926
4035
  return;
3927
4036
  }
@@ -3930,7 +4039,7 @@ function detectInstallMethod(packageRoot) {
3930
4039
  return "repo-clone";
3931
4040
  if (process.env.UNBROWSE_SETUP_METHOD === "npm-global")
3932
4041
  return "npm-global";
3933
- if (packageRoot.includes(`${path6.sep}node_modules${path6.sep}`))
4042
+ if (packageRoot.includes(`${path7.sep}node_modules${path7.sep}`))
3934
4043
  return "npm-global";
3935
4044
  return detectRepoRoot(packageRoot) ? "repo-clone" : "unknown";
3936
4045
  }
@@ -3940,12 +4049,12 @@ function detectInstallHost(repoRoot) {
3940
4049
  return explicit;
3941
4050
  if (!repoRoot)
3942
4051
  return "unknown";
3943
- const codexHome = process.env.CODEX_HOME || path6.join(getHomeDir(), ".codex");
3944
- if (repoRoot === path6.join(codexHome, "skills", "unbrowse"))
4052
+ const codexHome = process.env.CODEX_HOME || path7.join(getHomeDir(), ".codex");
4053
+ if (repoRoot === path7.join(codexHome, "skills", "unbrowse"))
3945
4054
  return "codex";
3946
- if (repoRoot === path6.join(getHomeDir(), ".claude", "skills", "unbrowse"))
4055
+ if (repoRoot === path7.join(getHomeDir(), ".claude", "skills", "unbrowse"))
3947
4056
  return "claude";
3948
- if (repoRoot === path6.join(getHomeDir(), "unbrowse"))
4057
+ if (repoRoot === path7.join(getHomeDir(), "unbrowse"))
3949
4058
  return "off";
3950
4059
  return "unknown";
3951
4060
  }
@@ -3973,14 +4082,14 @@ function commandIncludesHook(command, marker) {
3973
4082
  return typeof command === "string" && command.includes(marker);
3974
4083
  }
3975
4084
  function getCodexConfigPath() {
3976
- const codexHome = process.env.CODEX_HOME || path6.join(getHomeDir(), ".codex");
3977
- return path6.join(codexHome, "config.toml");
4085
+ const codexHome = process.env.CODEX_HOME || path7.join(getHomeDir(), ".codex");
4086
+ return path7.join(codexHome, "config.toml");
3978
4087
  }
3979
4088
  function getClaudeSettingsPath() {
3980
- return path6.join(getHomeDir(), ".claude", "settings.json");
4089
+ return path7.join(getHomeDir(), ".claude", "settings.json");
3981
4090
  }
3982
4091
  function getHookScriptPath(metaUrl) {
3983
- return path6.join(getPackageRoot(metaUrl), "bin", "unbrowse-update-hint.mjs");
4092
+ return path7.join(getPackageRoot(metaUrl), "bin", "unbrowse-update-hint.mjs");
3984
4093
  }
3985
4094
  function ensureCodexHooksFeature(content) {
3986
4095
  if (/\bcodex_hooks\s*=\s*true\b/.test(content))
@@ -3997,14 +4106,14 @@ codex_hooks = true
3997
4106
  `;
3998
4107
  }
3999
4108
  function writeCodexHook(metaUrl) {
4000
- const configPath = getCodexConfigPath();
4001
- if (!existsSync13(path6.dirname(configPath))) {
4002
- return { host: "codex", action: "not-detected", config_file: configPath };
4109
+ const configPath2 = getCodexConfigPath();
4110
+ if (!existsSync13(path7.dirname(configPath2))) {
4111
+ return { host: "codex", action: "not-detected", config_file: configPath2 };
4003
4112
  }
4004
4113
  try {
4005
4114
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
4006
- const fileExistsBefore = existsSync13(configPath);
4007
- let content = fileExistsBefore ? readFileSync8(configPath, "utf8") : "";
4115
+ const fileExistsBefore = existsSync13(configPath2);
4116
+ let content = fileExistsBefore ? readFileSync8(configPath2, "utf8") : "";
4008
4117
  const previous = content;
4009
4118
  content = ensureCodexHooksFeature(content);
4010
4119
  if (!content.includes("unbrowse-update-hint.mjs")) {
@@ -4019,26 +4128,26 @@ command = ${JSON.stringify(command)}
4019
4128
  `;
4020
4129
  }
4021
4130
  if (content !== previous) {
4022
- writeFileSync5(configPath, content, "utf8");
4131
+ writeFileSync5(configPath2, content, "utf8");
4023
4132
  return {
4024
4133
  host: "codex",
4025
4134
  action: fileExistsBefore ? "updated" : "installed",
4026
- config_file: configPath
4135
+ config_file: configPath2
4027
4136
  };
4028
4137
  }
4029
- return { host: "codex", action: "already-installed", config_file: configPath };
4138
+ return { host: "codex", action: "already-installed", config_file: configPath2 };
4030
4139
  } catch (error) {
4031
4140
  return {
4032
4141
  host: "codex",
4033
4142
  action: "failed",
4034
- config_file: configPath,
4143
+ config_file: configPath2,
4035
4144
  message: error instanceof Error ? error.message : String(error)
4036
4145
  };
4037
4146
  }
4038
4147
  }
4039
4148
  function writeClaudeHook(metaUrl) {
4040
4149
  const settingsPath = getClaudeSettingsPath();
4041
- if (!existsSync13(path6.dirname(settingsPath))) {
4150
+ if (!existsSync13(path7.dirname(settingsPath))) {
4042
4151
  return { host: "claude", action: "not-detected", config_file: settingsPath };
4043
4152
  }
4044
4153
  try {
@@ -4098,18 +4207,18 @@ function detectPackageManagers() {
4098
4207
  }
4099
4208
  function resolveConfigHome() {
4100
4209
  if (process.platform === "win32") {
4101
- return process.env.APPDATA || path7.join(os4.homedir(), "AppData", "Roaming");
4210
+ return process.env.APPDATA || path8.join(os5.homedir(), "AppData", "Roaming");
4102
4211
  }
4103
- return process.env.XDG_CONFIG_HOME || path7.join(os4.homedir(), ".config");
4212
+ return process.env.XDG_CONFIG_HOME || path8.join(os5.homedir(), ".config");
4104
4213
  }
4105
4214
  function getOpenCodeGlobalCommandsDir() {
4106
- return path7.join(resolveConfigHome(), "opencode", "commands");
4215
+ return path8.join(resolveConfigHome(), "opencode", "commands");
4107
4216
  }
4108
4217
  function getOpenCodeProjectCommandsDir(cwd) {
4109
- return path7.join(cwd, ".opencode", "commands");
4218
+ return path8.join(cwd, ".opencode", "commands");
4110
4219
  }
4111
4220
  function detectOpenCode(cwd) {
4112
- return hasBinary("opencode") || existsSync14(path7.join(resolveConfigHome(), "opencode")) || existsSync14(path7.join(cwd, ".opencode"));
4221
+ return hasBinary("opencode") || existsSync14(path8.join(resolveConfigHome(), "opencode")) || existsSync14(path8.join(cwd, ".opencode"));
4113
4222
  }
4114
4223
  function renderOpenCodeCommand() {
4115
4224
  return `---
@@ -4137,12 +4246,12 @@ function writeOpenCodeCommand(scope, cwd) {
4137
4246
  if (scope === "auto" && !detected) {
4138
4247
  return { detected: false, action: "not-detected", scope: "off" };
4139
4248
  }
4140
- const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync14(path7.join(cwd, ".opencode")) ? "project" : "global";
4249
+ const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync14(path8.join(cwd, ".opencode")) ? "project" : "global";
4141
4250
  const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
4142
- const commandFile = path7.join(ensureDir2(commandsDir), "unbrowse.md");
4251
+ const commandFile = path8.join(ensureDir2(commandsDir), "unbrowse.md");
4143
4252
  const content = renderOpenCodeCommand();
4144
4253
  const action2 = existsSync14(commandFile) ? "updated" : "installed";
4145
- mkdirSync8(path7.dirname(commandFile), { recursive: true });
4254
+ mkdirSync8(path8.dirname(commandFile), { recursive: true });
4146
4255
  writeFileSync6(commandFile, content);
4147
4256
  return {
4148
4257
  detected: detected || scope !== "auto",
@@ -4156,7 +4265,7 @@ async function ensureBrowserEngineInstalled() {
4156
4265
  if (existsSync14(binary)) {
4157
4266
  return { installed: true, action: "already-installed" };
4158
4267
  }
4159
- const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync14(path7.join(candidate, "build.zig")));
4268
+ const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync14(path8.join(candidate, "build.zig")));
4160
4269
  if (!sourceDir) {
4161
4270
  return {
4162
4271
  installed: false,
@@ -4203,7 +4312,7 @@ async function runSetup(options) {
4203
4312
  const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
4204
4313
  const walletCheck = checkWalletConfigured();
4205
4314
  const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
4206
- let lobsterInstalled = hasBinary("lobstercash") || existsSync14(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
4315
+ let lobsterInstalled = hasBinary("lobstercash") || existsSync14(path8.join(os5.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
4207
4316
  if (!skipWalletSetup && !walletCheck.configured) {
4208
4317
  if (!lobsterInstalled) {
4209
4318
  console.log("[unbrowse] Setting up Crossmint wallet (required for earning + payments)...");
@@ -4243,7 +4352,7 @@ async function runSetup(options) {
4243
4352
  return {
4244
4353
  os: {
4245
4354
  platform: process.platform,
4246
- release: os4.release(),
4355
+ release: os5.release(),
4247
4356
  arch: process.arch
4248
4357
  },
4249
4358
  host_environment: hostEnv,
@@ -4258,17 +4367,17 @@ async function runSetup(options) {
4258
4367
  // ../../src/runtime/update-hints.ts
4259
4368
  init_paths();
4260
4369
  import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
4261
- import os5 from "node:os";
4262
- import path8 from "node:path";
4370
+ import os6 from "node:os";
4371
+ import path9 from "node:path";
4263
4372
  var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
4264
4373
  var DEFAULT_INTERVAL_MS2 = 12 * 60 * 60 * 1000;
4265
4374
  function getHomeDir2() {
4266
- return process.env.HOME || os5.homedir();
4375
+ return process.env.HOME || os6.homedir();
4267
4376
  }
4268
4377
  function getConfigDir3() {
4269
4378
  if (process.env.UNBROWSE_CONFIG_DIR)
4270
4379
  return process.env.UNBROWSE_CONFIG_DIR;
4271
- return path8.join(getHomeDir2(), ".unbrowse");
4380
+ return path9.join(getHomeDir2(), ".unbrowse");
4272
4381
  }
4273
4382
  function ensureDir4(dir) {
4274
4383
  if (!existsSync15(dir))
@@ -4283,23 +4392,23 @@ function readJsonFile2(file) {
4283
4392
  }
4284
4393
  }
4285
4394
  function writeJsonFile2(file, value) {
4286
- ensureDir4(path8.dirname(file));
4395
+ ensureDir4(path9.dirname(file));
4287
4396
  writeFileSync7(file, `${JSON.stringify(value, null, 2)}
4288
4397
  `);
4289
4398
  }
4290
4399
  function getInstallSourcePath2() {
4291
- return path8.join(getConfigDir3(), "install-source.json");
4400
+ return path9.join(getConfigDir3(), "install-source.json");
4292
4401
  }
4293
4402
  function getUpdateCheckStatePath() {
4294
- return path8.join(getConfigDir3(), "update-check.json");
4403
+ return path9.join(getConfigDir3(), "update-check.json");
4295
4404
  }
4296
4405
  function detectRepoRoot2(start2) {
4297
- let dir = path8.resolve(start2);
4298
- const root = path8.parse(dir).root;
4406
+ let dir = path9.resolve(start2);
4407
+ const root = path9.parse(dir).root;
4299
4408
  while (dir !== root) {
4300
- if (existsSync15(path8.join(dir, ".git")))
4409
+ if (existsSync15(path9.join(dir, ".git")))
4301
4410
  return dir;
4302
- dir = path8.dirname(dir);
4411
+ dir = path9.dirname(dir);
4303
4412
  }
4304
4413
  return;
4305
4414
  }
@@ -4308,7 +4417,7 @@ function detectInstallMethod2(packageRoot) {
4308
4417
  return "repo-clone";
4309
4418
  if (process.env.UNBROWSE_SETUP_METHOD === "npm-global")
4310
4419
  return "npm-global";
4311
- if (packageRoot.includes(`${path8.sep}node_modules${path8.sep}`))
4420
+ if (packageRoot.includes(`${path9.sep}node_modules${path9.sep}`))
4312
4421
  return "npm-global";
4313
4422
  return detectRepoRoot2(packageRoot) ? "repo-clone" : "unknown";
4314
4423
  }
@@ -4318,19 +4427,19 @@ function detectInstallHost2(repoRoot) {
4318
4427
  return explicit;
4319
4428
  if (!repoRoot)
4320
4429
  return "unknown";
4321
- const codexHome = process.env.CODEX_HOME || path8.join(getHomeDir2(), ".codex");
4322
- if (repoRoot === path8.join(codexHome, "skills", "unbrowse"))
4430
+ const codexHome = process.env.CODEX_HOME || path9.join(getHomeDir2(), ".codex");
4431
+ if (repoRoot === path9.join(codexHome, "skills", "unbrowse"))
4323
4432
  return "codex";
4324
- if (repoRoot === path8.join(getHomeDir2(), ".claude", "skills", "unbrowse"))
4433
+ if (repoRoot === path9.join(getHomeDir2(), ".claude", "skills", "unbrowse"))
4325
4434
  return "claude";
4326
- if (repoRoot === path8.join(getHomeDir2(), "unbrowse"))
4435
+ if (repoRoot === path9.join(getHomeDir2(), "unbrowse"))
4327
4436
  return "off";
4328
4437
  return "unknown";
4329
4438
  }
4330
4439
  function getInstalledVersion(metaUrl) {
4331
4440
  const packageRoot = getPackageRoot(metaUrl);
4332
4441
  try {
4333
- const pkg = JSON.parse(readFileSync9(path8.join(packageRoot, "package.json"), "utf8"));
4442
+ const pkg = JSON.parse(readFileSync9(path9.join(packageRoot, "package.json"), "utf8"));
4334
4443
  return pkg.version ?? "unknown";
4335
4444
  } catch {
4336
4445
  return "unknown";
@@ -4426,6 +4535,81 @@ function recordUpdateHint(latestVersion) {
4426
4535
  });
4427
4536
  }
4428
4537
 
4538
+ // ../../src/cli-setup.ts
4539
+ init_contribution();
4540
+ import { createInterface as createInterface2 } from "node:readline";
4541
+ async function defaultReadChoice(allowed, dflt) {
4542
+ const isInteractive = !!process.stdin.isTTY && !!process.stdout.isTTY && process.env.UNBROWSE_NON_INTERACTIVE !== "1";
4543
+ if (!isInteractive)
4544
+ return dflt;
4545
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
4546
+ try {
4547
+ for (;; ) {
4548
+ const answer = await new Promise((resolve) => {
4549
+ rl.question(`Choice [${dflt}]: `, resolve);
4550
+ });
4551
+ const trimmed = answer.trim();
4552
+ if (!trimmed)
4553
+ return dflt;
4554
+ const n = parseInt(trimmed, 10);
4555
+ if (allowed.includes(n))
4556
+ return n;
4557
+ console.log(`Please enter ${allowed.join(", ")} (or press Enter for ${dflt}).`);
4558
+ }
4559
+ } finally {
4560
+ rl.close();
4561
+ }
4562
+ }
4563
+ async function promptContributionMode(opts = {}) {
4564
+ const log2 = opts.log ?? ((msg) => console.log(msg));
4565
+ const cfg = getContributionConfig();
4566
+ if (!opts.force && cfg.contribution.set_via && cfg.contribution.set_via !== "default") {
4567
+ return null;
4568
+ }
4569
+ log2("");
4570
+ log2("How do you want unbrowse to handle the routes you discover?");
4571
+ log2("");
4572
+ log2(" [1] Private (default) — cache locally, never publish");
4573
+ log2(" [2] Share — contribute pointers to the marketplace");
4574
+ log2(" [3] Share + earn — contribute and add a wallet for x402 rev-share");
4575
+ log2("");
4576
+ const reader = opts.readChoice ?? defaultReadChoice;
4577
+ const choice = await reader([1, 2, 3], 1);
4578
+ const set_via = opts.force ? "mode-command" : "setup-prompt";
4579
+ if (choice === 1) {
4580
+ setContributionConfig({
4581
+ contribution: { share_pointers: false, set_via },
4582
+ rev_share: { opted_in: false }
4583
+ });
4584
+ log2("Private mode set. Run `unbrowse mode` anytime to change.");
4585
+ } else if (choice === 2) {
4586
+ setContributionConfig({
4587
+ contribution: { share_pointers: true, set_via },
4588
+ rev_share: { opted_in: false }
4589
+ });
4590
+ log2("Sharing pointers enabled. Add a wallet via `unbrowse mode` to opt into rev-share.");
4591
+ } else {
4592
+ setContributionConfig({
4593
+ contribution: { share_pointers: true, set_via },
4594
+ rev_share: { opted_in: true }
4595
+ });
4596
+ log2("Sharing + rev-share enabled. Run `unbrowse wallet` to add your wallet for payouts.");
4597
+ }
4598
+ return choice;
4599
+ }
4600
+ function maybeShowContributionNotice() {
4601
+ const cfg = getContributionConfig();
4602
+ if (cfg.contribution.set_via !== "default")
4603
+ return false;
4604
+ const remaining = cfg.notice_shown_count ?? 0;
4605
+ if (remaining <= 0)
4606
+ return false;
4607
+ process.stderr.write("[unbrowse] contribution mode is now `private` by default — captures stay on your machine.\n" + `[unbrowse] run \`unbrowse mode\` to opt into sharing + rev-share. (notice will repeat ${remaining - 1} more time${remaining - 1 === 1 ? "" : "s"})
4608
+ `);
4609
+ decrementNoticeCounter();
4610
+ return true;
4611
+ }
4612
+
4429
4613
  // ../../src/cli.ts
4430
4614
  loadEnv({ quiet: true });
4431
4615
  loadEnv({ path: ".env.runtime", quiet: true });
@@ -4471,8 +4655,8 @@ function parseArgs(argv) {
4471
4655
  }
4472
4656
  return { command, args: positional, flags, params };
4473
4657
  }
4474
- async function api2(method, path9, body) {
4475
- let target = `${BASE_URL}${path9}`;
4658
+ async function api2(method, path10, body) {
4659
+ let target = `${BASE_URL}${path10}`;
4476
4660
  let requestBody = body;
4477
4661
  if (method === "GET" && body && typeof body === "object") {
4478
4662
  const params = new URLSearchParams;
@@ -4714,6 +4898,7 @@ async function cmdResolve(flags) {
4714
4898
  const intent = flags.intent;
4715
4899
  if (!intent)
4716
4900
  die("--intent is required");
4901
+ maybeShowContributionNotice();
4717
4902
  const hostType = detectTelemetryHostType();
4718
4903
  await ensureCliInstallTracked(hostType);
4719
4904
  await recordFunnelTelemetryEvent("cli_invoked", {
@@ -4775,6 +4960,14 @@ async function cmdResolve(flags) {
4775
4960
  body.force_capture = true;
4776
4961
  if (flags["skip-robots"])
4777
4962
  body.skip_robots_check = true;
4963
+ const budgetFlag = flags.budget;
4964
+ if (typeof budgetFlag === "string") {
4965
+ const parsed = parseInt(budgetFlag, 10);
4966
+ if (Number.isFinite(parsed) && parsed > 0)
4967
+ body.budget_ms = parsed;
4968
+ } else if (typeof budgetFlag === "number" && Number.isFinite(budgetFlag) && budgetFlag > 0) {
4969
+ body.budget_ms = budgetFlag;
4970
+ }
4778
4971
  body.projection = { raw: true };
4779
4972
  const startedAt = Date.now();
4780
4973
  async function resolveOnce(message = "Still working. Searching cached routes...") {
@@ -4861,8 +5054,8 @@ async function cmdResolve(flags) {
4861
5054
  throw error;
4862
5055
  }
4863
5056
  }
4864
- function drillPath(data, path9) {
4865
- const segments = path9.split(/\./).flatMap((s) => {
5057
+ function drillPath(data, path10) {
5058
+ const segments = path10.split(/\./).flatMap((s) => {
4866
5059
  const m = s.match(/^(.+)\[\]$/);
4867
5060
  return m ? [m[1], "[]"] : [s];
4868
5061
  });
@@ -4889,9 +5082,9 @@ function drillPath(data, path9) {
4889
5082
  }
4890
5083
  return values;
4891
5084
  }
4892
- function resolveDotPath(obj, path9) {
5085
+ function resolveDotPath(obj, path10) {
4893
5086
  let cur = obj;
4894
- for (const key of path9.split(".")) {
5087
+ for (const key of path10.split(".")) {
4895
5088
  if (cur == null || typeof cur !== "object")
4896
5089
  return;
4897
5090
  cur = cur[key];
@@ -4908,8 +5101,8 @@ function applyExtract(items, extractSpec) {
4908
5101
  return items.map((item) => {
4909
5102
  const row = {};
4910
5103
  let hasValue = false;
4911
- for (const { alias, path: path9 } of fields) {
4912
- const val = resolveDotPath(item, path9);
5104
+ for (const { alias, path: path10 } of fields) {
5105
+ const val = resolveDotPath(item, path10);
4913
5106
  row[alias] = val ?? null;
4914
5107
  if (val != null)
4915
5108
  hasValue = true;
@@ -4940,6 +5133,7 @@ async function cmdExecute(flags) {
4940
5133
  const skillId = flags.skill;
4941
5134
  if (!skillId)
4942
5135
  die("--skill is required");
5136
+ maybeShowContributionNotice();
4943
5137
  const hostType = detectTelemetryHostType();
4944
5138
  await ensureCliInstallTracked(hostType);
4945
5139
  await recordFunnelTelemetryEvent("cli_invoked", {
@@ -5381,6 +5575,35 @@ async function cmdSetup(flags) {
5381
5575
  info(' unbrowse resolve --intent "list posts" --url "https://jsonplaceholder.typicode.com"');
5382
5576
  }
5383
5577
  }
5578
+ async function cmdMode(_flags) {
5579
+ await promptContributionMode({ force: true });
5580
+ }
5581
+ async function runPostSetupContributionPrompt() {
5582
+ try {
5583
+ await promptContributionMode({ force: false });
5584
+ } catch {}
5585
+ }
5586
+ async function cmdCapture(flags) {
5587
+ const url = flags.url;
5588
+ const intent = flags.intent || "capture";
5589
+ if (!url)
5590
+ die("--url is required");
5591
+ maybeShowContributionNotice();
5592
+ const t0 = Date.now();
5593
+ const result = await api2("POST", "/v1/capture", { url, intent });
5594
+ const endpoints = Array.isArray(result.endpoints) ? result.endpoints : Array.isArray(result.available_endpoints) ? result.available_endpoints : [];
5595
+ const skill = result.skill ?? null;
5596
+ const skillId = result.skill_id ?? skill?.skill_id ?? (typeof result.learned_skill_id === "string" ? result.learned_skill_id : undefined);
5597
+ const envelope = {
5598
+ skill_id: skillId,
5599
+ endpoints_discovered: typeof result.endpoints_discovered === "number" ? result.endpoints_discovered : endpoints.length,
5600
+ marketplace_published: !!result.marketplace_published,
5601
+ ms: typeof result.ms === "number" ? result.ms : Date.now() - t0,
5602
+ next_step: endpoints.length > 0 ? `unbrowse resolve --intent "${intent.replace(/"/g, "\\\"")}" --url "${url.replace(/"/g, "\\\"")}"` : "no endpoints discovered; site may need authentication or different intent",
5603
+ ...result.error ? { error: result.error } : {}
5604
+ };
5605
+ output(envelope, !!flags.pretty);
5606
+ }
5384
5607
  var CLI_REFERENCE = {
5385
5608
  commands: [
5386
5609
  { name: "health", usage: "", desc: "Server health check" },
@@ -5425,7 +5648,9 @@ var CLI_REFERENCE = {
5425
5648
  { name: "earnings", usage: "[--json]", desc: "Show your credit balance, earnings from indexing, and spending" },
5426
5649
  { name: "corpus-test", usage: "--url <url> [--id <id>] [--retries N]", desc: "Capture a single URL with retry logic; keeps best result across N attempts" },
5427
5650
  { name: "corpus-run", usage: "--corpus <file> --out <file> [--retries N]", desc: "Run corpus-test over all cases in a corpus JSON file and write a comparable snapshot" },
5428
- { name: "register", usage: "[--no-prompt]", desc: "Optional: register an API key to publish skills, check earnings, and access backend analytics" }
5651
+ { name: "register", usage: "[--no-prompt]", desc: "Optional: register an API key to publish skills, check earnings, and access backend analytics" },
5652
+ { name: "mode", usage: "", desc: "Re-prompt for contribution mode (private / share / share + earn)" },
5653
+ { name: "capture", usage: "--url <url> --intent <intent>", desc: "Live-browser capture for a single URL \u2014 discovers + indexes API endpoints. Marketplace publish gated by `unbrowse mode`." }
5429
5654
  ],
5430
5655
  globalFlags: [
5431
5656
  { flag: "--pretty", desc: "Indented JSON output" },
@@ -5882,6 +6107,7 @@ async function cmdGo(args, flags) {
5882
6107
  const url = args[0] ?? flags.url;
5883
6108
  if (!url)
5884
6109
  die("Usage: unbrowse go <url>");
6110
+ maybeShowContributionNotice();
5885
6111
  output(await api2("POST", "/v1/browse/go", {
5886
6112
  url,
5887
6113
  ...typeof flags.session === "string" ? { session_id: flags.session } : {}
@@ -6308,6 +6534,11 @@ async function main() {
6308
6534
  }
6309
6535
  if (command === "setup") {
6310
6536
  await cmdSetup(flags);
6537
+ await runPostSetupContributionPrompt();
6538
+ return;
6539
+ }
6540
+ if (command === "mode") {
6541
+ await cmdMode(flags);
6311
6542
  return;
6312
6543
  }
6313
6544
  if (command === "mcp")
@@ -6386,7 +6617,9 @@ async function main() {
6386
6617
  "corpus-run",
6387
6618
  "sessions-scan",
6388
6619
  "cache-clear",
6389
- "register"
6620
+ "register",
6621
+ "mode",
6622
+ "capture"
6390
6623
  ]);
6391
6624
  if (!KNOWN_COMMANDS.has(command)) {
6392
6625
  const pack = findSitePack(command);
@@ -6500,6 +6733,10 @@ async function main() {
6500
6733
  return cmdSessionsScan(flags);
6501
6734
  case "register":
6502
6735
  return cmdRegister(flags);
6736
+ case "mode":
6737
+ return cmdMode(flags);
6738
+ case "capture":
6739
+ return cmdCapture(flags);
6503
6740
  default:
6504
6741
  info(`Unknown command: ${command}`);
6505
6742
  printHelp();