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 +313 -76
- package/dist/mcp.js +4 -4
- package/dist/server.js +566 -604
- package/package.json +1 -1
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.
|
|
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
|
|
1767
|
+
var TTL_MS2, cache;
|
|
1668
1768
|
var init_robots = __esm(() => {
|
|
1669
|
-
|
|
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
|
|
2295
|
-
if (
|
|
2296
|
-
return
|
|
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
|
|
3881
|
-
import
|
|
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
|
|
3887
|
-
import
|
|
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 ||
|
|
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
|
|
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(
|
|
4020
|
+
ensureDir3(path7.dirname(file));
|
|
3912
4021
|
writeFileSync5(file, `${JSON.stringify(value, null, 2)}
|
|
3913
4022
|
`);
|
|
3914
4023
|
}
|
|
3915
4024
|
function getInstallSourcePath() {
|
|
3916
|
-
return
|
|
4025
|
+
return path7.join(getConfigDir2(), "install-source.json");
|
|
3917
4026
|
}
|
|
3918
4027
|
function detectRepoRoot(start2) {
|
|
3919
|
-
let dir =
|
|
3920
|
-
const root =
|
|
4028
|
+
let dir = path7.resolve(start2);
|
|
4029
|
+
const root = path7.parse(dir).root;
|
|
3921
4030
|
while (dir !== root) {
|
|
3922
|
-
if (existsSync13(
|
|
4031
|
+
if (existsSync13(path7.join(dir, ".git")))
|
|
3923
4032
|
return dir;
|
|
3924
|
-
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(`${
|
|
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 ||
|
|
3944
|
-
if (repoRoot ===
|
|
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 ===
|
|
4055
|
+
if (repoRoot === path7.join(getHomeDir(), ".claude", "skills", "unbrowse"))
|
|
3947
4056
|
return "claude";
|
|
3948
|
-
if (repoRoot ===
|
|
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 ||
|
|
3977
|
-
return
|
|
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
|
|
4089
|
+
return path7.join(getHomeDir(), ".claude", "settings.json");
|
|
3981
4090
|
}
|
|
3982
4091
|
function getHookScriptPath(metaUrl) {
|
|
3983
|
-
return
|
|
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
|
|
4001
|
-
if (!existsSync13(
|
|
4002
|
-
return { host: "codex", action: "not-detected", config_file:
|
|
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(
|
|
4007
|
-
let content = fileExistsBefore ? readFileSync8(
|
|
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(
|
|
4131
|
+
writeFileSync5(configPath2, content, "utf8");
|
|
4023
4132
|
return {
|
|
4024
4133
|
host: "codex",
|
|
4025
4134
|
action: fileExistsBefore ? "updated" : "installed",
|
|
4026
|
-
config_file:
|
|
4135
|
+
config_file: configPath2
|
|
4027
4136
|
};
|
|
4028
4137
|
}
|
|
4029
|
-
return { host: "codex", action: "already-installed", config_file:
|
|
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:
|
|
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(
|
|
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 ||
|
|
4210
|
+
return process.env.APPDATA || path8.join(os5.homedir(), "AppData", "Roaming");
|
|
4102
4211
|
}
|
|
4103
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
4212
|
+
return process.env.XDG_CONFIG_HOME || path8.join(os5.homedir(), ".config");
|
|
4104
4213
|
}
|
|
4105
4214
|
function getOpenCodeGlobalCommandsDir() {
|
|
4106
|
-
return
|
|
4215
|
+
return path8.join(resolveConfigHome(), "opencode", "commands");
|
|
4107
4216
|
}
|
|
4108
4217
|
function getOpenCodeProjectCommandsDir(cwd) {
|
|
4109
|
-
return
|
|
4218
|
+
return path8.join(cwd, ".opencode", "commands");
|
|
4110
4219
|
}
|
|
4111
4220
|
function detectOpenCode(cwd) {
|
|
4112
|
-
return hasBinary("opencode") || existsSync14(
|
|
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(
|
|
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 =
|
|
4251
|
+
const commandFile = path8.join(ensureDir2(commandsDir), "unbrowse.md");
|
|
4143
4252
|
const content = renderOpenCodeCommand();
|
|
4144
4253
|
const action2 = existsSync14(commandFile) ? "updated" : "installed";
|
|
4145
|
-
mkdirSync8(
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
|
4262
|
-
import
|
|
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 ||
|
|
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
|
|
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(
|
|
4395
|
+
ensureDir4(path9.dirname(file));
|
|
4287
4396
|
writeFileSync7(file, `${JSON.stringify(value, null, 2)}
|
|
4288
4397
|
`);
|
|
4289
4398
|
}
|
|
4290
4399
|
function getInstallSourcePath2() {
|
|
4291
|
-
return
|
|
4400
|
+
return path9.join(getConfigDir3(), "install-source.json");
|
|
4292
4401
|
}
|
|
4293
4402
|
function getUpdateCheckStatePath() {
|
|
4294
|
-
return
|
|
4403
|
+
return path9.join(getConfigDir3(), "update-check.json");
|
|
4295
4404
|
}
|
|
4296
4405
|
function detectRepoRoot2(start2) {
|
|
4297
|
-
let dir =
|
|
4298
|
-
const root =
|
|
4406
|
+
let dir = path9.resolve(start2);
|
|
4407
|
+
const root = path9.parse(dir).root;
|
|
4299
4408
|
while (dir !== root) {
|
|
4300
|
-
if (existsSync15(
|
|
4409
|
+
if (existsSync15(path9.join(dir, ".git")))
|
|
4301
4410
|
return dir;
|
|
4302
|
-
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(`${
|
|
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 ||
|
|
4322
|
-
if (repoRoot ===
|
|
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 ===
|
|
4433
|
+
if (repoRoot === path9.join(getHomeDir2(), ".claude", "skills", "unbrowse"))
|
|
4325
4434
|
return "claude";
|
|
4326
|
-
if (repoRoot ===
|
|
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(
|
|
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,
|
|
4475
|
-
let target = `${BASE_URL}${
|
|
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,
|
|
4865
|
-
const segments =
|
|
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,
|
|
5085
|
+
function resolveDotPath(obj, path10) {
|
|
4893
5086
|
let cur = obj;
|
|
4894
|
-
for (const key of
|
|
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:
|
|
4912
|
-
const val = resolveDotPath(item,
|
|
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();
|