unbrowse 6.3.0 → 6.5.0-preview.5
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 +894 -139
- package/dist/mcp.js +8 -6
- package/dist/server.js +1273 -781
- package/package.json +1 -1
- package/vendor/kuri/darwin-arm64/kuri +0 -0
- package/vendor/kuri/darwin-x64/kuri +0 -0
- package/vendor/kuri/linux-arm64/kuri +0 -0
- package/vendor/kuri/linux-x64/kuri +0 -0
- package/vendor/kuri/manifest.json +6 -6
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.5.0-preview.5", BUILD_GIT_SHA = "764b78e2d7f4", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi41LjAtcHJldmlldy41IiwiZ2l0X3NoYSI6Ijc2NGI3OGUyZDdmNCIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANzY0Yjc4ZTJkN2Y0IiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wM1QwNzo0NDozOS41MzZaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "3QFhQrBBJLGFXH5FxGlgs6hJtyMTYy7GCXeJoovWc5s", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
|
|
35
35
|
|
|
36
36
|
// ../../src/version.ts
|
|
37
37
|
import { createHash } from "crypto";
|
|
@@ -132,13 +132,14 @@ function getPackageVersion() {
|
|
|
132
132
|
return packageVersion;
|
|
133
133
|
return getEmbeddedReleaseVersion() ?? "unknown";
|
|
134
134
|
}
|
|
135
|
-
var MODULE_DIR, CODE_HASH, GIT_SHA, PACKAGE_VERSION, DEFAULT_BACKEND_URL, TRACE_VERSION, RELEASE_MANIFEST_BASE64, RELEASE_MANIFEST_SIGNATURE;
|
|
135
|
+
var MODULE_DIR, CODE_HASH, GIT_SHA, PACKAGE_VERSION, DEFAULT_BACKEND_URL, DEFAULT_PROFILE, TRACE_VERSION, RELEASE_MANIFEST_BASE64, RELEASE_MANIFEST_SIGNATURE;
|
|
136
136
|
var init_version = __esm(() => {
|
|
137
137
|
MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
138
138
|
CODE_HASH = BUILD_CODE_HASH?.trim() || computeCodeHash();
|
|
139
139
|
GIT_SHA = getGitSha();
|
|
140
140
|
PACKAGE_VERSION = getPackageVersion();
|
|
141
141
|
DEFAULT_BACKEND_URL = BUILD_DEFAULT_BACKEND_URL?.trim() || "https://beta-api.unbrowse.ai";
|
|
142
|
+
DEFAULT_PROFILE = BUILD_DEFAULT_PROFILE?.trim() || "";
|
|
142
143
|
TRACE_VERSION = `${CODE_HASH}@${GIT_SHA}`;
|
|
143
144
|
RELEASE_MANIFEST_BASE64 = BUILD_RELEASE_MANIFEST_BASE64?.trim() || "";
|
|
144
145
|
RELEASE_MANIFEST_SIGNATURE = BUILD_RELEASE_MANIFEST_SIGNATURE?.trim() || "";
|
|
@@ -180,9 +181,11 @@ function getWalletContext() {
|
|
|
180
181
|
wallet_provider: asNonEmptyString(process.env.AGENT_WALLET_PROVIDER)
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
if (process.env.UNBROWSE_DISABLE_LOCAL_WALLET !== "1") {
|
|
185
|
+
const localLobsterWallet = getLobsterWalletFromLocalConfig();
|
|
186
|
+
if (localLobsterWallet) {
|
|
187
|
+
return { wallet_address: localLobsterWallet, wallet_provider: "lobster.cash" };
|
|
188
|
+
}
|
|
186
189
|
}
|
|
187
190
|
return {};
|
|
188
191
|
}
|
|
@@ -426,7 +429,7 @@ function resolveSiblingEntrypoint(metaUrl, basename) {
|
|
|
426
429
|
}
|
|
427
430
|
function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
|
|
428
431
|
if (path.extname(entrypoint) !== ".ts") {
|
|
429
|
-
return [pathToFileURL(entrypoint).href];
|
|
432
|
+
return process.platform === "win32" ? [pathToFileURL(entrypoint).href] : [entrypoint];
|
|
430
433
|
}
|
|
431
434
|
if (process.versions.bun)
|
|
432
435
|
return [entrypoint];
|
|
@@ -506,7 +509,7 @@ var init_client = __esm(() => {
|
|
|
506
509
|
init_wallet();
|
|
507
510
|
init_telemetry_attribution();
|
|
508
511
|
API_URL2 = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
509
|
-
PROFILE_NAME2 = sanitizeProfileName2(process.env.UNBROWSE_PROFILE ?? "");
|
|
512
|
+
PROFILE_NAME2 = sanitizeProfileName2(process.env.UNBROWSE_PROFILE ?? DEFAULT_PROFILE ?? "");
|
|
510
513
|
recentLocalSkills2 = new Map;
|
|
511
514
|
LOCAL_ONLY2 = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
512
515
|
API_TIMEOUT_MS2 = parseInt(process.env.UNBROWSE_API_TIMEOUT ?? "8000", 10);
|
|
@@ -515,8 +518,11 @@ var init_client = __esm(() => {
|
|
|
515
518
|
|
|
516
519
|
// ../../src/marketplace/index.ts
|
|
517
520
|
import { nanoid } from "nanoid";
|
|
521
|
+
var TTL_MS, marketplaceCache;
|
|
518
522
|
var init_marketplace = __esm(() => {
|
|
519
523
|
init_client();
|
|
524
|
+
TTL_MS = 5 * 60 * 1000;
|
|
525
|
+
marketplaceCache = new Map;
|
|
520
526
|
});
|
|
521
527
|
|
|
522
528
|
// ../../src/domain.ts
|
|
@@ -1321,6 +1327,20 @@ function extractBrowserCookies(domain, opts) {
|
|
|
1321
1327
|
}
|
|
1322
1328
|
const chrome = extractFromChrome(domain, { profile: opts?.chromeProfile });
|
|
1323
1329
|
chrome.warnings.push(...ff.warnings);
|
|
1330
|
+
if (chrome.cookies.length > 0)
|
|
1331
|
+
return chrome;
|
|
1332
|
+
const sessions = scanAllBrowserSessions(domain);
|
|
1333
|
+
const best = sessions.filter((s) => s.browser !== "Firefox" && s.browser !== "Chrome").sort((a, b) => b.sessionCookies - a.sessionCookies)[0];
|
|
1334
|
+
if (best) {
|
|
1335
|
+
return {
|
|
1336
|
+
cookies: best.cookies,
|
|
1337
|
+
source: best.source,
|
|
1338
|
+
warnings: [
|
|
1339
|
+
...chrome.warnings,
|
|
1340
|
+
`Chrome had no cookies for ${domain}; using ${best.browser} (${best.sessionCookies} session cookies)`
|
|
1341
|
+
]
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1324
1344
|
return chrome;
|
|
1325
1345
|
}
|
|
1326
1346
|
function scanAllBrowserSessions(domain) {
|
|
@@ -1487,22 +1507,25 @@ class LocalAuthRuntime {
|
|
|
1487
1507
|
if (session && session.expires > Date.now()) {
|
|
1488
1508
|
return { authenticated: true, session_token: session.token, method: "cached" };
|
|
1489
1509
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1510
|
+
const skipFallback = process.env.UNBROWSE_DISABLE_AUTH_FALLBACK === "1";
|
|
1511
|
+
if (!skipFallback) {
|
|
1512
|
+
try {
|
|
1513
|
+
const cookies = await getStoredAuth(dep.domain);
|
|
1514
|
+
if (cookies && cookies.length > 0) {
|
|
1515
|
+
log("auth-runtime", `found ${cookies.length} stored cookies for ${dep.domain}`);
|
|
1516
|
+
this.setSession(dep.domain, "vault-cookies", 3600000);
|
|
1517
|
+
return { authenticated: true, method: "cookies" };
|
|
1518
|
+
}
|
|
1519
|
+
} catch {}
|
|
1520
|
+
try {
|
|
1521
|
+
const result = await extractBrowserAuth(dep.domain);
|
|
1522
|
+
if (result.success && result.cookies_stored > 0) {
|
|
1523
|
+
log("auth-runtime", `extracted ${result.cookies_stored} browser cookies for ${dep.domain}`);
|
|
1524
|
+
this.setSession(dep.domain, "browser-cookies", 3600000);
|
|
1525
|
+
return { authenticated: true, method: "cookies" };
|
|
1526
|
+
}
|
|
1527
|
+
} catch {}
|
|
1528
|
+
}
|
|
1506
1529
|
if (dep.strategy === "refresh_session" && session) {
|
|
1507
1530
|
const refreshed = await this.refreshSession(dep.domain);
|
|
1508
1531
|
if (refreshed) {
|
|
@@ -1633,6 +1656,102 @@ var init_schema_review = __esm(() => {
|
|
|
1633
1656
|
init_sanitize();
|
|
1634
1657
|
});
|
|
1635
1658
|
|
|
1659
|
+
// ../../src/config/contribution.ts
|
|
1660
|
+
import fs2 from "node:fs";
|
|
1661
|
+
import path6 from "node:path";
|
|
1662
|
+
import os3 from "node:os";
|
|
1663
|
+
function configPath() {
|
|
1664
|
+
return process.env.UNBROWSE_CONFIG_PATH || path6.join(os3.homedir(), ".unbrowse", "config.json");
|
|
1665
|
+
}
|
|
1666
|
+
function freshDefault() {
|
|
1667
|
+
return {
|
|
1668
|
+
contribution: { ...DEFAULT.contribution },
|
|
1669
|
+
rev_share: { ...DEFAULT.rev_share },
|
|
1670
|
+
notice_shown_count: 0
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function getContributionConfig() {
|
|
1674
|
+
if (cached)
|
|
1675
|
+
return cached;
|
|
1676
|
+
const p = configPath();
|
|
1677
|
+
let fileExisted = false;
|
|
1678
|
+
let raw = null;
|
|
1679
|
+
try {
|
|
1680
|
+
const content = fs2.readFileSync(p, "utf-8");
|
|
1681
|
+
fileExisted = true;
|
|
1682
|
+
if (content.trim().length > 0) {
|
|
1683
|
+
raw = JSON.parse(content);
|
|
1684
|
+
}
|
|
1685
|
+
} catch {}
|
|
1686
|
+
if (!raw) {
|
|
1687
|
+
cached = freshDefault();
|
|
1688
|
+
return cached;
|
|
1689
|
+
}
|
|
1690
|
+
const contributionRaw = raw.contribution ?? null;
|
|
1691
|
+
const revShareRaw = raw.rev_share ?? null;
|
|
1692
|
+
const isExistingUserMigration = fileExisted && !contributionRaw;
|
|
1693
|
+
cached = {
|
|
1694
|
+
contribution: {
|
|
1695
|
+
share_pointers: !!(contributionRaw?.share_pointers ?? DEFAULT.contribution.share_pointers),
|
|
1696
|
+
set_via: contributionRaw?.set_via ?? DEFAULT.contribution.set_via,
|
|
1697
|
+
...contributionRaw?.set_at ? { set_at: String(contributionRaw.set_at) } : {}
|
|
1698
|
+
},
|
|
1699
|
+
rev_share: {
|
|
1700
|
+
opted_in: !!(revShareRaw?.opted_in ?? DEFAULT.rev_share.opted_in),
|
|
1701
|
+
...revShareRaw?.wallet_address ? { wallet_address: String(revShareRaw.wallet_address) } : {}
|
|
1702
|
+
},
|
|
1703
|
+
notice_shown_count: typeof raw.notice_shown_count === "number" ? raw.notice_shown_count : isExistingUserMigration ? 5 : 0
|
|
1704
|
+
};
|
|
1705
|
+
if (isExistingUserMigration) {
|
|
1706
|
+
try {
|
|
1707
|
+
const merged = { ...raw, ...cached };
|
|
1708
|
+
fs2.mkdirSync(path6.dirname(p), { recursive: true });
|
|
1709
|
+
fs2.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
1710
|
+
} catch {}
|
|
1711
|
+
}
|
|
1712
|
+
return cached;
|
|
1713
|
+
}
|
|
1714
|
+
function setContributionConfig(updates) {
|
|
1715
|
+
const current = getContributionConfig();
|
|
1716
|
+
const next = {
|
|
1717
|
+
contribution: {
|
|
1718
|
+
...current.contribution,
|
|
1719
|
+
...updates.contribution ?? {},
|
|
1720
|
+
set_at: new Date().toISOString()
|
|
1721
|
+
},
|
|
1722
|
+
rev_share: {
|
|
1723
|
+
...current.rev_share,
|
|
1724
|
+
...updates.rev_share ?? {}
|
|
1725
|
+
},
|
|
1726
|
+
notice_shown_count: updates.notice_shown_count ?? current.notice_shown_count ?? 0
|
|
1727
|
+
};
|
|
1728
|
+
const p = configPath();
|
|
1729
|
+
let existing = {};
|
|
1730
|
+
try {
|
|
1731
|
+
const content = fs2.readFileSync(p, "utf-8");
|
|
1732
|
+
if (content.trim())
|
|
1733
|
+
existing = JSON.parse(content);
|
|
1734
|
+
} catch {}
|
|
1735
|
+
const merged = { ...existing, ...next };
|
|
1736
|
+
fs2.mkdirSync(path6.dirname(p), { recursive: true });
|
|
1737
|
+
fs2.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
1738
|
+
cached = next;
|
|
1739
|
+
}
|
|
1740
|
+
function decrementNoticeCounter() {
|
|
1741
|
+
const cfg = getContributionConfig();
|
|
1742
|
+
const next = Math.max(0, (cfg.notice_shown_count ?? 0) - 1);
|
|
1743
|
+
setContributionConfig({ notice_shown_count: next });
|
|
1744
|
+
return next;
|
|
1745
|
+
}
|
|
1746
|
+
var DEFAULT, cached = null;
|
|
1747
|
+
var init_contribution = __esm(() => {
|
|
1748
|
+
DEFAULT = {
|
|
1749
|
+
contribution: { share_pointers: false, set_via: "default" },
|
|
1750
|
+
rev_share: { opted_in: false },
|
|
1751
|
+
notice_shown_count: 0
|
|
1752
|
+
};
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1636
1755
|
// ../../src/indexer/index.ts
|
|
1637
1756
|
import { join as join9 } from "node:path";
|
|
1638
1757
|
var SKILL_SNAPSHOT_DIR, indexInFlight, pendingIndexJobs;
|
|
@@ -1648,6 +1767,7 @@ var init_indexer = __esm(async () => {
|
|
|
1648
1767
|
init_settings();
|
|
1649
1768
|
init_graph();
|
|
1650
1769
|
init_schema_review();
|
|
1770
|
+
init_contribution();
|
|
1651
1771
|
await init_orchestrator();
|
|
1652
1772
|
SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join9(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
|
|
1653
1773
|
indexInFlight = new Map;
|
|
@@ -1664,9 +1784,9 @@ var init_payments = __esm(() => {
|
|
|
1664
1784
|
});
|
|
1665
1785
|
|
|
1666
1786
|
// ../../src/execution/robots.ts
|
|
1667
|
-
var
|
|
1787
|
+
var TTL_MS2, cache;
|
|
1668
1788
|
var init_robots = __esm(() => {
|
|
1669
|
-
|
|
1789
|
+
TTL_MS2 = 24 * 60 * 60 * 1000;
|
|
1670
1790
|
cache = new Map;
|
|
1671
1791
|
});
|
|
1672
1792
|
|
|
@@ -1728,6 +1848,7 @@ var init_compile = () => {};
|
|
|
1728
1848
|
import { nanoid as nanoid6 } from "nanoid";
|
|
1729
1849
|
var VALID_VERIFICATION_STATUSES, STOPWORDS;
|
|
1730
1850
|
var init_execution = __esm(async () => {
|
|
1851
|
+
init_client2();
|
|
1731
1852
|
init_reverse_engineer();
|
|
1732
1853
|
init_bundle_scanner();
|
|
1733
1854
|
init_token_resolver();
|
|
@@ -1871,11 +1992,22 @@ import { nanoid as nanoid8 } from "nanoid";
|
|
|
1871
1992
|
var init_routing_telemetry = __esm(() => {
|
|
1872
1993
|
init_telemetry();
|
|
1873
1994
|
});
|
|
1995
|
+
|
|
1996
|
+
// ../../src/orchestrator/resolve-race.ts
|
|
1997
|
+
var init_resolve_race = __esm(async () => {
|
|
1998
|
+
init_probe();
|
|
1999
|
+
init_marketplace();
|
|
2000
|
+
await init_execution();
|
|
2001
|
+
});
|
|
1874
2002
|
// ../../src/orchestrator/index.ts
|
|
1875
2003
|
import { nanoid as nanoid9 } from "nanoid";
|
|
1876
2004
|
import { existsSync as existsSync12, writeFileSync as writeFileSync4, readFileSync as readFileSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
1877
2005
|
import { dirname as dirname3, join as join10 } from "node:path";
|
|
1878
2006
|
function _writeRouteCacheToDisk() {
|
|
2007
|
+
if (!LOCAL_CACHES_ENABLED) {
|
|
2008
|
+
_routeCacheDirty = false;
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
1879
2011
|
try {
|
|
1880
2012
|
const dir = dirname3(ROUTE_CACHE_FILE);
|
|
1881
2013
|
if (!existsSync12(dir))
|
|
@@ -1885,7 +2017,7 @@ function _writeRouteCacheToDisk() {
|
|
|
1885
2017
|
} catch {}
|
|
1886
2018
|
_routeCacheDirty = false;
|
|
1887
2019
|
}
|
|
1888
|
-
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;
|
|
2020
|
+
var LIVE_CAPTURE_TIMEOUT_MS, capturedDomainCache, captureInFlight, captureDomainLocks, skillRouteCache, ROUTE_CACHE_FILE, SKILL_SNAPSHOT_DIR2, domainSkillCache, DOMAIN_CACHE_FILE, LOCAL_CACHES_ENABLED, _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;
|
|
1889
2021
|
var init_orchestrator = __esm(async () => {
|
|
1890
2022
|
init_client();
|
|
1891
2023
|
init_client2();
|
|
@@ -1907,7 +2039,8 @@ var init_orchestrator = __esm(async () => {
|
|
|
1907
2039
|
init_execution(),
|
|
1908
2040
|
init_dag_advisor(),
|
|
1909
2041
|
init_prefetch(),
|
|
1910
|
-
init_runtime()
|
|
2042
|
+
init_runtime(),
|
|
2043
|
+
init_resolve_race()
|
|
1911
2044
|
]);
|
|
1912
2045
|
LIVE_CAPTURE_TIMEOUT_MS = Number(process.env.UNBROWSE_LIVE_CAPTURE_TIMEOUT_MS ?? "120000");
|
|
1913
2046
|
capturedDomainCache = new Map;
|
|
@@ -1918,36 +2051,41 @@ var init_orchestrator = __esm(async () => {
|
|
|
1918
2051
|
SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join10(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
|
|
1919
2052
|
domainSkillCache = new Map;
|
|
1920
2053
|
DOMAIN_CACHE_FILE = join10(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
const
|
|
1926
|
-
|
|
1927
|
-
|
|
2054
|
+
LOCAL_CACHES_ENABLED = process.env.UNBROWSE_LOCAL_CACHES === "1";
|
|
2055
|
+
if (LOCAL_CACHES_ENABLED) {
|
|
2056
|
+
try {
|
|
2057
|
+
if (existsSync12(DOMAIN_CACHE_FILE)) {
|
|
2058
|
+
const data = JSON.parse(readFileSync7(DOMAIN_CACHE_FILE, "utf-8"));
|
|
2059
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2060
|
+
const entry = v;
|
|
2061
|
+
if (Date.now() - entry.ts < 7 * 24 * 60 * 60000) {
|
|
2062
|
+
domainSkillCache.set(k, entry);
|
|
2063
|
+
}
|
|
1928
2064
|
}
|
|
2065
|
+
console.error(`[domain-cache] loaded ${domainSkillCache.size} entries from disk`);
|
|
1929
2066
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
} catch {}
|
|
2067
|
+
} catch {}
|
|
2068
|
+
}
|
|
1933
2069
|
routeCacheFlushTimer = setInterval(() => {
|
|
1934
2070
|
if (!_routeCacheDirty)
|
|
1935
2071
|
return;
|
|
1936
2072
|
_writeRouteCacheToDisk();
|
|
1937
2073
|
}, 5000);
|
|
1938
2074
|
routeCacheFlushTimer.unref?.();
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
const
|
|
1944
|
-
|
|
1945
|
-
|
|
2075
|
+
if (LOCAL_CACHES_ENABLED) {
|
|
2076
|
+
try {
|
|
2077
|
+
if (existsSync12(ROUTE_CACHE_FILE)) {
|
|
2078
|
+
const data = JSON.parse(readFileSync7(ROUTE_CACHE_FILE, "utf-8"));
|
|
2079
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2080
|
+
const entry = v;
|
|
2081
|
+
if (Date.now() - entry.ts < 24 * 60 * 60000) {
|
|
2082
|
+
skillRouteCache.set(k, entry);
|
|
2083
|
+
}
|
|
1946
2084
|
}
|
|
2085
|
+
console.error(`[route-cache] loaded ${skillRouteCache.size} entries from disk`);
|
|
1947
2086
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
} catch {}
|
|
2087
|
+
} catch {}
|
|
2088
|
+
}
|
|
1951
2089
|
routeResultCache = new Map;
|
|
1952
2090
|
ROUTE_CACHE_TTL = 24 * 60 * 60000;
|
|
1953
2091
|
MARKETPLACE_HYDRATE_LIMIT = Math.max(1, Number(process.env.UNBROWSE_MARKETPLACE_HYDRATE_LIMIT ?? 4));
|
|
@@ -2065,9 +2203,11 @@ function getWalletContext2() {
|
|
|
2065
2203
|
wallet_provider: asNonEmptyString2(process.env.AGENT_WALLET_PROVIDER)
|
|
2066
2204
|
};
|
|
2067
2205
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2206
|
+
if (process.env.UNBROWSE_DISABLE_LOCAL_WALLET !== "1") {
|
|
2207
|
+
const localLobsterWallet = getLobsterWalletFromLocalConfig2();
|
|
2208
|
+
if (localLobsterWallet) {
|
|
2209
|
+
return { wallet_address: localLobsterWallet, wallet_provider: "lobster.cash" };
|
|
2210
|
+
}
|
|
2071
2211
|
}
|
|
2072
2212
|
return {};
|
|
2073
2213
|
}
|
|
@@ -2082,6 +2222,100 @@ function checkWalletConfigured2() {
|
|
|
2082
2222
|
}
|
|
2083
2223
|
var init_wallet2 = () => {};
|
|
2084
2224
|
|
|
2225
|
+
// ../../src/telemetry-attribution.ts
|
|
2226
|
+
var exports_telemetry_attribution = {};
|
|
2227
|
+
__export(exports_telemetry_attribution, {
|
|
2228
|
+
sanitizeTelemetryAttribution: () => sanitizeTelemetryAttribution2,
|
|
2229
|
+
sanitizeAttributionValue: () => sanitizeAttributionValue2,
|
|
2230
|
+
mergeTelemetryProperties: () => mergeTelemetryProperties2,
|
|
2231
|
+
mergeTelemetryAttribution: () => mergeTelemetryAttribution2,
|
|
2232
|
+
hasTelemetryAttribution: () => hasTelemetryAttribution2,
|
|
2233
|
+
decodeTelemetryAttribution: () => decodeTelemetryAttribution2,
|
|
2234
|
+
TELEMETRY_ATTRIBUTION_KEYS: () => TELEMETRY_ATTRIBUTION_KEYS2
|
|
2235
|
+
});
|
|
2236
|
+
function sanitizeAttributionValue2(value) {
|
|
2237
|
+
if (typeof value !== "string")
|
|
2238
|
+
return;
|
|
2239
|
+
const trimmed = value.trim();
|
|
2240
|
+
if (!trimmed)
|
|
2241
|
+
return;
|
|
2242
|
+
return trimmed.slice(0, MAX_ATTRIBUTION_VALUE_LENGTH2);
|
|
2243
|
+
}
|
|
2244
|
+
function hasTelemetryAttribution2(value) {
|
|
2245
|
+
if (!value)
|
|
2246
|
+
return false;
|
|
2247
|
+
return TELEMETRY_ATTRIBUTION_KEYS2.some((key) => Boolean(sanitizeAttributionValue2(value[key])));
|
|
2248
|
+
}
|
|
2249
|
+
function sanitizeTelemetryAttribution2(raw) {
|
|
2250
|
+
if (!raw)
|
|
2251
|
+
return;
|
|
2252
|
+
const cleaned = {};
|
|
2253
|
+
for (const key of TELEMETRY_ATTRIBUTION_KEYS2) {
|
|
2254
|
+
const value = sanitizeAttributionValue2(raw[key]);
|
|
2255
|
+
if (value)
|
|
2256
|
+
cleaned[key] = value;
|
|
2257
|
+
}
|
|
2258
|
+
return hasTelemetryAttribution2(cleaned) ? cleaned : undefined;
|
|
2259
|
+
}
|
|
2260
|
+
function mergeTelemetryAttribution2(base, incoming) {
|
|
2261
|
+
const merged = sanitizeTelemetryAttribution2({
|
|
2262
|
+
...base ?? {},
|
|
2263
|
+
...incoming ?? {}
|
|
2264
|
+
});
|
|
2265
|
+
return merged;
|
|
2266
|
+
}
|
|
2267
|
+
function mergeTelemetryProperties2(properties, attribution) {
|
|
2268
|
+
const cleanedAttribution = sanitizeTelemetryAttribution2(attribution);
|
|
2269
|
+
if (!cleanedAttribution)
|
|
2270
|
+
return properties;
|
|
2271
|
+
return {
|
|
2272
|
+
...cleanedAttribution,
|
|
2273
|
+
...properties ?? {}
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
function decodeTelemetryAttribution2(value) {
|
|
2277
|
+
if (!value)
|
|
2278
|
+
return;
|
|
2279
|
+
try {
|
|
2280
|
+
const decoded = Buffer.from(value, "base64").toString("utf8");
|
|
2281
|
+
return sanitizeTelemetryAttribution2(JSON.parse(decoded));
|
|
2282
|
+
} catch {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
var TELEMETRY_ATTRIBUTION_KEYS2, MAX_ATTRIBUTION_VALUE_LENGTH2 = 160;
|
|
2287
|
+
var init_telemetry_attribution2 = __esm(() => {
|
|
2288
|
+
TELEMETRY_ATTRIBUTION_KEYS2 = [
|
|
2289
|
+
"utm_source",
|
|
2290
|
+
"utm_medium",
|
|
2291
|
+
"utm_campaign",
|
|
2292
|
+
"utm_content",
|
|
2293
|
+
"utm_term",
|
|
2294
|
+
"utm_id",
|
|
2295
|
+
"gclid",
|
|
2296
|
+
"wbraid",
|
|
2297
|
+
"gbraid",
|
|
2298
|
+
"fbclid",
|
|
2299
|
+
"twclid",
|
|
2300
|
+
"ttclid",
|
|
2301
|
+
"msclkid",
|
|
2302
|
+
"li_fat_id",
|
|
2303
|
+
"referrer_host",
|
|
2304
|
+
"channel",
|
|
2305
|
+
"campaign_id",
|
|
2306
|
+
"campaign_name",
|
|
2307
|
+
"content_id",
|
|
2308
|
+
"content_type",
|
|
2309
|
+
"creative_id",
|
|
2310
|
+
"ad_id",
|
|
2311
|
+
"adset_id",
|
|
2312
|
+
"inferred_icp",
|
|
2313
|
+
"variant_id",
|
|
2314
|
+
"experiment_id",
|
|
2315
|
+
"icp"
|
|
2316
|
+
];
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2085
2319
|
// ../../src/version.ts
|
|
2086
2320
|
var exports_version = {};
|
|
2087
2321
|
__export(exports_version, {
|
|
@@ -2094,6 +2328,7 @@ __export(exports_version, {
|
|
|
2094
2328
|
RELEASE_MANIFEST_BASE64: () => RELEASE_MANIFEST_BASE642,
|
|
2095
2329
|
PACKAGE_VERSION: () => PACKAGE_VERSION2,
|
|
2096
2330
|
GIT_SHA: () => GIT_SHA2,
|
|
2331
|
+
DEFAULT_PROFILE: () => DEFAULT_PROFILE2,
|
|
2097
2332
|
DEFAULT_BACKEND_URL: () => DEFAULT_BACKEND_URL2,
|
|
2098
2333
|
CODE_HASH: () => CODE_HASH2
|
|
2099
2334
|
});
|
|
@@ -2195,13 +2430,14 @@ function getPackageVersion2() {
|
|
|
2195
2430
|
return packageVersion;
|
|
2196
2431
|
return getEmbeddedReleaseVersion2() ?? "unknown";
|
|
2197
2432
|
}
|
|
2198
|
-
var MODULE_DIR2, CODE_HASH2, GIT_SHA2, PACKAGE_VERSION2, DEFAULT_BACKEND_URL2, TRACE_VERSION2, RELEASE_MANIFEST_BASE642, RELEASE_MANIFEST_SIGNATURE2;
|
|
2433
|
+
var MODULE_DIR2, CODE_HASH2, GIT_SHA2, PACKAGE_VERSION2, DEFAULT_BACKEND_URL2, DEFAULT_PROFILE2, TRACE_VERSION2, RELEASE_MANIFEST_BASE642, RELEASE_MANIFEST_SIGNATURE2;
|
|
2199
2434
|
var init_version2 = __esm(() => {
|
|
2200
2435
|
MODULE_DIR2 = dirname4(fileURLToPath4(import.meta.url));
|
|
2201
2436
|
CODE_HASH2 = BUILD_CODE_HASH?.trim() || computeCodeHash2();
|
|
2202
2437
|
GIT_SHA2 = getGitSha2();
|
|
2203
2438
|
PACKAGE_VERSION2 = getPackageVersion2();
|
|
2204
2439
|
DEFAULT_BACKEND_URL2 = BUILD_DEFAULT_BACKEND_URL?.trim() || "https://beta-api.unbrowse.ai";
|
|
2440
|
+
DEFAULT_PROFILE2 = BUILD_DEFAULT_PROFILE?.trim() || "";
|
|
2205
2441
|
TRACE_VERSION2 = `${CODE_HASH2}@${GIT_SHA2}`;
|
|
2206
2442
|
RELEASE_MANIFEST_BASE642 = BUILD_RELEASE_MANIFEST_BASE64?.trim() || "";
|
|
2207
2443
|
RELEASE_MANIFEST_SIGNATURE2 = BUILD_RELEASE_MANIFEST_SIGNATURE?.trim() || "";
|
|
@@ -2291,9 +2527,9 @@ function getChromiumKeychainServiceName2(opts) {
|
|
|
2291
2527
|
}
|
|
2292
2528
|
function getChromiumDecryptionKey2(opts) {
|
|
2293
2529
|
const service = getChromiumKeychainServiceName2(opts);
|
|
2294
|
-
const
|
|
2295
|
-
if (
|
|
2296
|
-
return
|
|
2530
|
+
const cached3 = _chromiumKeyCache2.get(service);
|
|
2531
|
+
if (cached3)
|
|
2532
|
+
return cached3;
|
|
2297
2533
|
if (platform2() !== "darwin")
|
|
2298
2534
|
return null;
|
|
2299
2535
|
try {
|
|
@@ -2512,6 +2748,20 @@ function extractBrowserCookies2(domain, opts) {
|
|
|
2512
2748
|
}
|
|
2513
2749
|
const chrome = extractFromChrome2(domain, { profile: opts?.chromeProfile });
|
|
2514
2750
|
chrome.warnings.push(...ff.warnings);
|
|
2751
|
+
if (chrome.cookies.length > 0)
|
|
2752
|
+
return chrome;
|
|
2753
|
+
const sessions = scanAllBrowserSessions2(domain);
|
|
2754
|
+
const best = sessions.filter((s) => s.browser !== "Firefox" && s.browser !== "Chrome").sort((a, b) => b.sessionCookies - a.sessionCookies)[0];
|
|
2755
|
+
if (best) {
|
|
2756
|
+
return {
|
|
2757
|
+
cookies: best.cookies,
|
|
2758
|
+
source: best.source,
|
|
2759
|
+
warnings: [
|
|
2760
|
+
...chrome.warnings,
|
|
2761
|
+
`Chrome had no cookies for ${domain}; using ${best.browser} (${best.sessionCookies} session cookies)`
|
|
2762
|
+
]
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2515
2765
|
return chrome;
|
|
2516
2766
|
}
|
|
2517
2767
|
function scanAllBrowserSessions2(domain) {
|
|
@@ -2589,7 +2839,7 @@ import { randomBytes, createHash as createHash2 } from "crypto";
|
|
|
2589
2839
|
import { createInterface } from "readline";
|
|
2590
2840
|
import { execSync } from "child_process";
|
|
2591
2841
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
2592
|
-
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
2842
|
+
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? DEFAULT_PROFILE ?? "");
|
|
2593
2843
|
var recentLocalSkills = new Map;
|
|
2594
2844
|
var LOCAL_ONLY = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
2595
2845
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
@@ -3187,9 +3437,99 @@ You have $2.00 in free credits — start resolving to use them.`);
|
|
|
3187
3437
|
process.exit(1);
|
|
3188
3438
|
}
|
|
3189
3439
|
}
|
|
3440
|
+
async function magicRegister(opts) {
|
|
3441
|
+
const timeoutMs = opts.timeoutMs ?? 300000;
|
|
3442
|
+
const pollMs = opts.pollMs ?? 1500;
|
|
3443
|
+
const startRes = await fetch(`${API_URL}/v1/auth/email/start`, {
|
|
3444
|
+
method: "POST",
|
|
3445
|
+
headers: {
|
|
3446
|
+
"Content-Type": "application/json",
|
|
3447
|
+
"Accept-Encoding": "gzip, deflate"
|
|
3448
|
+
},
|
|
3449
|
+
body: JSON.stringify({ email: opts.email, return_url: opts.returnUrl })
|
|
3450
|
+
});
|
|
3451
|
+
if (startRes.status === 503) {
|
|
3452
|
+
throw new Error("Backend RESEND_API_KEY not set — magic-link signup unavailable. Use anon `unbrowse register` (no --email).");
|
|
3453
|
+
}
|
|
3454
|
+
let startData;
|
|
3455
|
+
try {
|
|
3456
|
+
startData = await startRes.json();
|
|
3457
|
+
} catch {
|
|
3458
|
+
throw new Error(`Magic-link start failed: HTTP ${startRes.status}`);
|
|
3459
|
+
}
|
|
3460
|
+
if (startRes.status === 400) {
|
|
3461
|
+
throw new Error(startData.error ?? "invalid_email");
|
|
3462
|
+
}
|
|
3463
|
+
if (!startRes.ok || !startData.token) {
|
|
3464
|
+
const msg = startData.error ?? `HTTP ${startRes.status}`;
|
|
3465
|
+
throw new Error(`Magic-link start failed: ${msg}`);
|
|
3466
|
+
}
|
|
3467
|
+
const token = startData.token;
|
|
3468
|
+
const verifyUrl = `${API_URL}/v1/auth/email/verify?token=${encodeURIComponent(token)}`;
|
|
3469
|
+
if (opts.openBrowser) {
|
|
3470
|
+
try {
|
|
3471
|
+
await opts.openBrowser(verifyUrl);
|
|
3472
|
+
} catch {}
|
|
3473
|
+
} else {
|
|
3474
|
+
try {
|
|
3475
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
3476
|
+
execSync(`${cmd} ${JSON.stringify(verifyUrl)}`, { stdio: "ignore", timeout: 5000 });
|
|
3477
|
+
} catch {}
|
|
3478
|
+
}
|
|
3479
|
+
const deadline = Date.now() + timeoutMs;
|
|
3480
|
+
while (Date.now() < deadline) {
|
|
3481
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
3482
|
+
const pollRes = await fetch(`${API_URL}/v1/auth/email/poll?token=${encodeURIComponent(token)}`, {
|
|
3483
|
+
method: "GET",
|
|
3484
|
+
headers: { "Accept-Encoding": "gzip, deflate" }
|
|
3485
|
+
});
|
|
3486
|
+
let pollData;
|
|
3487
|
+
try {
|
|
3488
|
+
pollData = await pollRes.json();
|
|
3489
|
+
} catch {
|
|
3490
|
+
throw new Error(`Magic-link poll failed: HTTP ${pollRes.status}`);
|
|
3491
|
+
}
|
|
3492
|
+
if (pollData.status === "verified") {
|
|
3493
|
+
if (!pollData.api_key || !pollData.user_id || !pollData.email) {
|
|
3494
|
+
throw new Error("Magic-link poll returned verified without api_key/user_id/email.");
|
|
3495
|
+
}
|
|
3496
|
+
return {
|
|
3497
|
+
api_key: pollData.api_key,
|
|
3498
|
+
agent_id: pollData.user_id,
|
|
3499
|
+
email: pollData.email,
|
|
3500
|
+
user_id: pollData.user_id
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
if (pollData.status === "expired" || pollRes.status === 410) {
|
|
3504
|
+
throw new Error("Magic link expired. Re-run `unbrowse register --email …`.");
|
|
3505
|
+
}
|
|
3506
|
+
if (pollData.status === "pending")
|
|
3507
|
+
continue;
|
|
3508
|
+
if (!pollRes.ok) {
|
|
3509
|
+
throw new Error(`Magic-link poll failed: HTTP ${pollRes.status}`);
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
throw new Error(`Magic-link timed out after ${Math.round(timeoutMs / 1000)}s. Check your inbox and re-run.`);
|
|
3513
|
+
}
|
|
3190
3514
|
async function getMyProfile() {
|
|
3191
3515
|
return api("GET", "/v1/agents/me", undefined);
|
|
3192
3516
|
}
|
|
3517
|
+
async function fetchAccountPreferences() {
|
|
3518
|
+
try {
|
|
3519
|
+
const data = await api("GET", "/v1/account/preferences", undefined);
|
|
3520
|
+
return { share_pointers: !!data?.share_pointers };
|
|
3521
|
+
} catch (err) {
|
|
3522
|
+
const msg = err.message ?? "";
|
|
3523
|
+
if (msg.includes("account_required") || msg.includes("HTTP 403") || msg.includes("HTTP 404")) {
|
|
3524
|
+
return null;
|
|
3525
|
+
}
|
|
3526
|
+
throw err;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
async function pushAccountPreferences(patch) {
|
|
3530
|
+
const data = await api("PATCH", "/v1/account/preferences", patch);
|
|
3531
|
+
return { share_pointers: !!data?.share_pointers };
|
|
3532
|
+
}
|
|
3193
3533
|
async function syncAgentWallet(wallet = getLocalWalletContext()) {
|
|
3194
3534
|
if (!wallet.wallet_address)
|
|
3195
3535
|
return;
|
|
@@ -3794,7 +4134,7 @@ function isBundledVirtualEntrypoint(entrypoint) {
|
|
|
3794
4134
|
}
|
|
3795
4135
|
function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
|
|
3796
4136
|
if (path3.extname(entrypoint) !== ".ts") {
|
|
3797
|
-
return [pathToFileURL2(entrypoint).href];
|
|
4137
|
+
return process.platform === "win32" ? [pathToFileURL2(entrypoint).href] : [entrypoint];
|
|
3798
4138
|
}
|
|
3799
4139
|
if (process.versions.bun)
|
|
3800
4140
|
return [entrypoint];
|
|
@@ -3837,6 +4177,7 @@ init_publish();
|
|
|
3837
4177
|
init_settings();
|
|
3838
4178
|
init_graph();
|
|
3839
4179
|
init_schema_review();
|
|
4180
|
+
init_contribution();
|
|
3840
4181
|
import { join as join11 } from "node:path";
|
|
3841
4182
|
var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join11(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
|
|
3842
4183
|
var indexInFlight2 = new Map;
|
|
@@ -3877,23 +4218,23 @@ init_logger();
|
|
|
3877
4218
|
init_wallet();
|
|
3878
4219
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
3879
4220
|
import { existsSync as existsSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
3880
|
-
import
|
|
3881
|
-
import
|
|
4221
|
+
import os5 from "node:os";
|
|
4222
|
+
import path8 from "node:path";
|
|
3882
4223
|
|
|
3883
4224
|
// ../../src/runtime/update-hints.ts
|
|
3884
4225
|
init_paths();
|
|
3885
4226
|
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3886
|
-
import
|
|
3887
|
-
import
|
|
4227
|
+
import os4 from "node:os";
|
|
4228
|
+
import path7 from "node:path";
|
|
3888
4229
|
var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
3889
4230
|
var CODEX_MARKER = "# Unbrowse update hints — managed by unbrowse setup";
|
|
3890
4231
|
function getHomeDir() {
|
|
3891
|
-
return process.env.HOME ||
|
|
4232
|
+
return process.env.HOME || os4.homedir();
|
|
3892
4233
|
}
|
|
3893
4234
|
function getConfigDir2() {
|
|
3894
4235
|
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
3895
4236
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
3896
|
-
return
|
|
4237
|
+
return path7.join(getHomeDir(), ".unbrowse");
|
|
3897
4238
|
}
|
|
3898
4239
|
function ensureDir3(dir) {
|
|
3899
4240
|
if (!existsSync13(dir))
|
|
@@ -3908,20 +4249,20 @@ function readJsonFile(file) {
|
|
|
3908
4249
|
}
|
|
3909
4250
|
}
|
|
3910
4251
|
function writeJsonFile(file, value) {
|
|
3911
|
-
ensureDir3(
|
|
4252
|
+
ensureDir3(path7.dirname(file));
|
|
3912
4253
|
writeFileSync5(file, `${JSON.stringify(value, null, 2)}
|
|
3913
4254
|
`);
|
|
3914
4255
|
}
|
|
3915
4256
|
function getInstallSourcePath() {
|
|
3916
|
-
return
|
|
4257
|
+
return path7.join(getConfigDir2(), "install-source.json");
|
|
3917
4258
|
}
|
|
3918
4259
|
function detectRepoRoot(start2) {
|
|
3919
|
-
let dir =
|
|
3920
|
-
const root =
|
|
4260
|
+
let dir = path7.resolve(start2);
|
|
4261
|
+
const root = path7.parse(dir).root;
|
|
3921
4262
|
while (dir !== root) {
|
|
3922
|
-
if (existsSync13(
|
|
4263
|
+
if (existsSync13(path7.join(dir, ".git")))
|
|
3923
4264
|
return dir;
|
|
3924
|
-
dir =
|
|
4265
|
+
dir = path7.dirname(dir);
|
|
3925
4266
|
}
|
|
3926
4267
|
return;
|
|
3927
4268
|
}
|
|
@@ -3930,7 +4271,7 @@ function detectInstallMethod(packageRoot) {
|
|
|
3930
4271
|
return "repo-clone";
|
|
3931
4272
|
if (process.env.UNBROWSE_SETUP_METHOD === "npm-global")
|
|
3932
4273
|
return "npm-global";
|
|
3933
|
-
if (packageRoot.includes(`${
|
|
4274
|
+
if (packageRoot.includes(`${path7.sep}node_modules${path7.sep}`))
|
|
3934
4275
|
return "npm-global";
|
|
3935
4276
|
return detectRepoRoot(packageRoot) ? "repo-clone" : "unknown";
|
|
3936
4277
|
}
|
|
@@ -3940,12 +4281,12 @@ function detectInstallHost(repoRoot) {
|
|
|
3940
4281
|
return explicit;
|
|
3941
4282
|
if (!repoRoot)
|
|
3942
4283
|
return "unknown";
|
|
3943
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
3944
|
-
if (repoRoot ===
|
|
4284
|
+
const codexHome = process.env.CODEX_HOME || path7.join(getHomeDir(), ".codex");
|
|
4285
|
+
if (repoRoot === path7.join(codexHome, "skills", "unbrowse"))
|
|
3945
4286
|
return "codex";
|
|
3946
|
-
if (repoRoot ===
|
|
4287
|
+
if (repoRoot === path7.join(getHomeDir(), ".claude", "skills", "unbrowse"))
|
|
3947
4288
|
return "claude";
|
|
3948
|
-
if (repoRoot ===
|
|
4289
|
+
if (repoRoot === path7.join(getHomeDir(), "unbrowse"))
|
|
3949
4290
|
return "off";
|
|
3950
4291
|
return "unknown";
|
|
3951
4292
|
}
|
|
@@ -3973,14 +4314,14 @@ function commandIncludesHook(command, marker) {
|
|
|
3973
4314
|
return typeof command === "string" && command.includes(marker);
|
|
3974
4315
|
}
|
|
3975
4316
|
function getCodexConfigPath() {
|
|
3976
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
3977
|
-
return
|
|
4317
|
+
const codexHome = process.env.CODEX_HOME || path7.join(getHomeDir(), ".codex");
|
|
4318
|
+
return path7.join(codexHome, "config.toml");
|
|
3978
4319
|
}
|
|
3979
4320
|
function getClaudeSettingsPath() {
|
|
3980
|
-
return
|
|
4321
|
+
return path7.join(getHomeDir(), ".claude", "settings.json");
|
|
3981
4322
|
}
|
|
3982
4323
|
function getHookScriptPath(metaUrl) {
|
|
3983
|
-
return
|
|
4324
|
+
return path7.join(getPackageRoot(metaUrl), "bin", "unbrowse-update-hint.mjs");
|
|
3984
4325
|
}
|
|
3985
4326
|
function ensureCodexHooksFeature(content) {
|
|
3986
4327
|
if (/\bcodex_hooks\s*=\s*true\b/.test(content))
|
|
@@ -3997,14 +4338,14 @@ codex_hooks = true
|
|
|
3997
4338
|
`;
|
|
3998
4339
|
}
|
|
3999
4340
|
function writeCodexHook(metaUrl) {
|
|
4000
|
-
const
|
|
4001
|
-
if (!existsSync13(
|
|
4002
|
-
return { host: "codex", action: "not-detected", config_file:
|
|
4341
|
+
const configPath2 = getCodexConfigPath();
|
|
4342
|
+
if (!existsSync13(path7.dirname(configPath2))) {
|
|
4343
|
+
return { host: "codex", action: "not-detected", config_file: configPath2 };
|
|
4003
4344
|
}
|
|
4004
4345
|
try {
|
|
4005
4346
|
const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
|
|
4006
|
-
const fileExistsBefore = existsSync13(
|
|
4007
|
-
let content = fileExistsBefore ? readFileSync8(
|
|
4347
|
+
const fileExistsBefore = existsSync13(configPath2);
|
|
4348
|
+
let content = fileExistsBefore ? readFileSync8(configPath2, "utf8") : "";
|
|
4008
4349
|
const previous = content;
|
|
4009
4350
|
content = ensureCodexHooksFeature(content);
|
|
4010
4351
|
if (!content.includes("unbrowse-update-hint.mjs")) {
|
|
@@ -4019,26 +4360,26 @@ command = ${JSON.stringify(command)}
|
|
|
4019
4360
|
`;
|
|
4020
4361
|
}
|
|
4021
4362
|
if (content !== previous) {
|
|
4022
|
-
writeFileSync5(
|
|
4363
|
+
writeFileSync5(configPath2, content, "utf8");
|
|
4023
4364
|
return {
|
|
4024
4365
|
host: "codex",
|
|
4025
4366
|
action: fileExistsBefore ? "updated" : "installed",
|
|
4026
|
-
config_file:
|
|
4367
|
+
config_file: configPath2
|
|
4027
4368
|
};
|
|
4028
4369
|
}
|
|
4029
|
-
return { host: "codex", action: "already-installed", config_file:
|
|
4370
|
+
return { host: "codex", action: "already-installed", config_file: configPath2 };
|
|
4030
4371
|
} catch (error) {
|
|
4031
4372
|
return {
|
|
4032
4373
|
host: "codex",
|
|
4033
4374
|
action: "failed",
|
|
4034
|
-
config_file:
|
|
4375
|
+
config_file: configPath2,
|
|
4035
4376
|
message: error instanceof Error ? error.message : String(error)
|
|
4036
4377
|
};
|
|
4037
4378
|
}
|
|
4038
4379
|
}
|
|
4039
4380
|
function writeClaudeHook(metaUrl) {
|
|
4040
4381
|
const settingsPath = getClaudeSettingsPath();
|
|
4041
|
-
if (!existsSync13(
|
|
4382
|
+
if (!existsSync13(path7.dirname(settingsPath))) {
|
|
4042
4383
|
return { host: "claude", action: "not-detected", config_file: settingsPath };
|
|
4043
4384
|
}
|
|
4044
4385
|
try {
|
|
@@ -4098,18 +4439,18 @@ function detectPackageManagers() {
|
|
|
4098
4439
|
}
|
|
4099
4440
|
function resolveConfigHome() {
|
|
4100
4441
|
if (process.platform === "win32") {
|
|
4101
|
-
return process.env.APPDATA ||
|
|
4442
|
+
return process.env.APPDATA || path8.join(os5.homedir(), "AppData", "Roaming");
|
|
4102
4443
|
}
|
|
4103
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
4444
|
+
return process.env.XDG_CONFIG_HOME || path8.join(os5.homedir(), ".config");
|
|
4104
4445
|
}
|
|
4105
4446
|
function getOpenCodeGlobalCommandsDir() {
|
|
4106
|
-
return
|
|
4447
|
+
return path8.join(resolveConfigHome(), "opencode", "commands");
|
|
4107
4448
|
}
|
|
4108
4449
|
function getOpenCodeProjectCommandsDir(cwd) {
|
|
4109
|
-
return
|
|
4450
|
+
return path8.join(cwd, ".opencode", "commands");
|
|
4110
4451
|
}
|
|
4111
4452
|
function detectOpenCode(cwd) {
|
|
4112
|
-
return hasBinary("opencode") || existsSync14(
|
|
4453
|
+
return hasBinary("opencode") || existsSync14(path8.join(resolveConfigHome(), "opencode")) || existsSync14(path8.join(cwd, ".opencode"));
|
|
4113
4454
|
}
|
|
4114
4455
|
function renderOpenCodeCommand() {
|
|
4115
4456
|
return `---
|
|
@@ -4137,12 +4478,12 @@ function writeOpenCodeCommand(scope, cwd) {
|
|
|
4137
4478
|
if (scope === "auto" && !detected) {
|
|
4138
4479
|
return { detected: false, action: "not-detected", scope: "off" };
|
|
4139
4480
|
}
|
|
4140
|
-
const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync14(
|
|
4481
|
+
const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync14(path8.join(cwd, ".opencode")) ? "project" : "global";
|
|
4141
4482
|
const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
|
|
4142
|
-
const commandFile =
|
|
4483
|
+
const commandFile = path8.join(ensureDir2(commandsDir), "unbrowse.md");
|
|
4143
4484
|
const content = renderOpenCodeCommand();
|
|
4144
4485
|
const action2 = existsSync14(commandFile) ? "updated" : "installed";
|
|
4145
|
-
mkdirSync8(
|
|
4486
|
+
mkdirSync8(path8.dirname(commandFile), { recursive: true });
|
|
4146
4487
|
writeFileSync6(commandFile, content);
|
|
4147
4488
|
return {
|
|
4148
4489
|
detected: detected || scope !== "auto",
|
|
@@ -4156,7 +4497,7 @@ async function ensureBrowserEngineInstalled() {
|
|
|
4156
4497
|
if (existsSync14(binary)) {
|
|
4157
4498
|
return { installed: true, action: "already-installed" };
|
|
4158
4499
|
}
|
|
4159
|
-
const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync14(
|
|
4500
|
+
const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync14(path8.join(candidate, "build.zig")));
|
|
4160
4501
|
if (!sourceDir) {
|
|
4161
4502
|
return {
|
|
4162
4503
|
installed: false,
|
|
@@ -4203,7 +4544,7 @@ async function runSetup(options) {
|
|
|
4203
4544
|
const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
|
|
4204
4545
|
const walletCheck = checkWalletConfigured();
|
|
4205
4546
|
const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
|
|
4206
|
-
let lobsterInstalled = hasBinary("lobstercash") || existsSync14(
|
|
4547
|
+
let lobsterInstalled = hasBinary("lobstercash") || existsSync14(path8.join(os5.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
|
|
4207
4548
|
if (!skipWalletSetup && !walletCheck.configured) {
|
|
4208
4549
|
if (!lobsterInstalled) {
|
|
4209
4550
|
console.log("[unbrowse] Setting up Crossmint wallet (required for earning + payments)...");
|
|
@@ -4243,7 +4584,7 @@ async function runSetup(options) {
|
|
|
4243
4584
|
return {
|
|
4244
4585
|
os: {
|
|
4245
4586
|
platform: process.platform,
|
|
4246
|
-
release:
|
|
4587
|
+
release: os5.release(),
|
|
4247
4588
|
arch: process.arch
|
|
4248
4589
|
},
|
|
4249
4590
|
host_environment: hostEnv,
|
|
@@ -4258,17 +4599,17 @@ async function runSetup(options) {
|
|
|
4258
4599
|
// ../../src/runtime/update-hints.ts
|
|
4259
4600
|
init_paths();
|
|
4260
4601
|
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4261
|
-
import
|
|
4262
|
-
import
|
|
4602
|
+
import os6 from "node:os";
|
|
4603
|
+
import path9 from "node:path";
|
|
4263
4604
|
var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
|
|
4264
4605
|
var DEFAULT_INTERVAL_MS2 = 12 * 60 * 60 * 1000;
|
|
4265
4606
|
function getHomeDir2() {
|
|
4266
|
-
return process.env.HOME ||
|
|
4607
|
+
return process.env.HOME || os6.homedir();
|
|
4267
4608
|
}
|
|
4268
4609
|
function getConfigDir3() {
|
|
4269
4610
|
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
4270
4611
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
4271
|
-
return
|
|
4612
|
+
return path9.join(getHomeDir2(), ".unbrowse");
|
|
4272
4613
|
}
|
|
4273
4614
|
function ensureDir4(dir) {
|
|
4274
4615
|
if (!existsSync15(dir))
|
|
@@ -4283,23 +4624,23 @@ function readJsonFile2(file) {
|
|
|
4283
4624
|
}
|
|
4284
4625
|
}
|
|
4285
4626
|
function writeJsonFile2(file, value) {
|
|
4286
|
-
ensureDir4(
|
|
4627
|
+
ensureDir4(path9.dirname(file));
|
|
4287
4628
|
writeFileSync7(file, `${JSON.stringify(value, null, 2)}
|
|
4288
4629
|
`);
|
|
4289
4630
|
}
|
|
4290
4631
|
function getInstallSourcePath2() {
|
|
4291
|
-
return
|
|
4632
|
+
return path9.join(getConfigDir3(), "install-source.json");
|
|
4292
4633
|
}
|
|
4293
4634
|
function getUpdateCheckStatePath() {
|
|
4294
|
-
return
|
|
4635
|
+
return path9.join(getConfigDir3(), "update-check.json");
|
|
4295
4636
|
}
|
|
4296
4637
|
function detectRepoRoot2(start2) {
|
|
4297
|
-
let dir =
|
|
4298
|
-
const root =
|
|
4638
|
+
let dir = path9.resolve(start2);
|
|
4639
|
+
const root = path9.parse(dir).root;
|
|
4299
4640
|
while (dir !== root) {
|
|
4300
|
-
if (existsSync15(
|
|
4641
|
+
if (existsSync15(path9.join(dir, ".git")))
|
|
4301
4642
|
return dir;
|
|
4302
|
-
dir =
|
|
4643
|
+
dir = path9.dirname(dir);
|
|
4303
4644
|
}
|
|
4304
4645
|
return;
|
|
4305
4646
|
}
|
|
@@ -4308,7 +4649,7 @@ function detectInstallMethod2(packageRoot) {
|
|
|
4308
4649
|
return "repo-clone";
|
|
4309
4650
|
if (process.env.UNBROWSE_SETUP_METHOD === "npm-global")
|
|
4310
4651
|
return "npm-global";
|
|
4311
|
-
if (packageRoot.includes(`${
|
|
4652
|
+
if (packageRoot.includes(`${path9.sep}node_modules${path9.sep}`))
|
|
4312
4653
|
return "npm-global";
|
|
4313
4654
|
return detectRepoRoot2(packageRoot) ? "repo-clone" : "unknown";
|
|
4314
4655
|
}
|
|
@@ -4318,19 +4659,19 @@ function detectInstallHost2(repoRoot) {
|
|
|
4318
4659
|
return explicit;
|
|
4319
4660
|
if (!repoRoot)
|
|
4320
4661
|
return "unknown";
|
|
4321
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
4322
|
-
if (repoRoot ===
|
|
4662
|
+
const codexHome = process.env.CODEX_HOME || path9.join(getHomeDir2(), ".codex");
|
|
4663
|
+
if (repoRoot === path9.join(codexHome, "skills", "unbrowse"))
|
|
4323
4664
|
return "codex";
|
|
4324
|
-
if (repoRoot ===
|
|
4665
|
+
if (repoRoot === path9.join(getHomeDir2(), ".claude", "skills", "unbrowse"))
|
|
4325
4666
|
return "claude";
|
|
4326
|
-
if (repoRoot ===
|
|
4667
|
+
if (repoRoot === path9.join(getHomeDir2(), "unbrowse"))
|
|
4327
4668
|
return "off";
|
|
4328
4669
|
return "unknown";
|
|
4329
4670
|
}
|
|
4330
4671
|
function getInstalledVersion(metaUrl) {
|
|
4331
4672
|
const packageRoot = getPackageRoot(metaUrl);
|
|
4332
4673
|
try {
|
|
4333
|
-
const pkg = JSON.parse(readFileSync9(
|
|
4674
|
+
const pkg = JSON.parse(readFileSync9(path9.join(packageRoot, "package.json"), "utf8"));
|
|
4334
4675
|
return pkg.version ?? "unknown";
|
|
4335
4676
|
} catch {
|
|
4336
4677
|
return "unknown";
|
|
@@ -4426,6 +4767,169 @@ function recordUpdateHint(latestVersion) {
|
|
|
4426
4767
|
});
|
|
4427
4768
|
}
|
|
4428
4769
|
|
|
4770
|
+
// ../../src/cli-setup.ts
|
|
4771
|
+
init_contribution();
|
|
4772
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
4773
|
+
async function defaultReadChoice(allowed, dflt) {
|
|
4774
|
+
const isInteractive = !!process.stdin.isTTY && !!process.stdout.isTTY && process.env.UNBROWSE_NON_INTERACTIVE !== "1";
|
|
4775
|
+
if (!isInteractive)
|
|
4776
|
+
return dflt;
|
|
4777
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
4778
|
+
try {
|
|
4779
|
+
for (;; ) {
|
|
4780
|
+
const answer = await new Promise((resolve) => {
|
|
4781
|
+
rl.question(`Choice [${dflt}]: `, resolve);
|
|
4782
|
+
});
|
|
4783
|
+
const trimmed = answer.trim();
|
|
4784
|
+
if (!trimmed)
|
|
4785
|
+
return dflt;
|
|
4786
|
+
const n = parseInt(trimmed, 10);
|
|
4787
|
+
if (allowed.includes(n))
|
|
4788
|
+
return n;
|
|
4789
|
+
console.log(`Please enter ${allowed.join(", ")} (or press Enter for ${dflt}).`);
|
|
4790
|
+
}
|
|
4791
|
+
} finally {
|
|
4792
|
+
rl.close();
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4795
|
+
async function promptContributionMode(opts = {}) {
|
|
4796
|
+
const log2 = opts.log ?? ((msg) => console.log(msg));
|
|
4797
|
+
const cfg = getContributionConfig();
|
|
4798
|
+
if (!opts.force && cfg.contribution.set_via && cfg.contribution.set_via !== "default") {
|
|
4799
|
+
return null;
|
|
4800
|
+
}
|
|
4801
|
+
log2("");
|
|
4802
|
+
log2("How do you want unbrowse to handle the routes you discover?");
|
|
4803
|
+
log2("");
|
|
4804
|
+
log2(" [1] Private (default) — cache locally, never publish");
|
|
4805
|
+
log2(" [2] Share — contribute pointers to the marketplace");
|
|
4806
|
+
log2(" [3] Share + earn — contribute and add a wallet for x402 rev-share");
|
|
4807
|
+
log2("");
|
|
4808
|
+
const reader = opts.readChoice ?? defaultReadChoice;
|
|
4809
|
+
const choice = await reader([1, 2, 3], 1);
|
|
4810
|
+
const set_via = opts.force ? "mode-command" : "setup-prompt";
|
|
4811
|
+
if (choice === 1) {
|
|
4812
|
+
setContributionConfig({
|
|
4813
|
+
contribution: { share_pointers: false, set_via },
|
|
4814
|
+
rev_share: { opted_in: false }
|
|
4815
|
+
});
|
|
4816
|
+
log2("Private mode set. Run `unbrowse mode` anytime to change.");
|
|
4817
|
+
} else if (choice === 2) {
|
|
4818
|
+
setContributionConfig({
|
|
4819
|
+
contribution: { share_pointers: true, set_via },
|
|
4820
|
+
rev_share: { opted_in: false }
|
|
4821
|
+
});
|
|
4822
|
+
log2("Sharing pointers enabled. Add a wallet via `unbrowse mode` to opt into rev-share.");
|
|
4823
|
+
} else {
|
|
4824
|
+
setContributionConfig({
|
|
4825
|
+
contribution: { share_pointers: true, set_via },
|
|
4826
|
+
rev_share: { opted_in: true }
|
|
4827
|
+
});
|
|
4828
|
+
log2("Sharing + rev-share enabled. Run `unbrowse wallet` to add your wallet for payouts.");
|
|
4829
|
+
}
|
|
4830
|
+
return choice;
|
|
4831
|
+
}
|
|
4832
|
+
function maybeShowContributionNotice() {
|
|
4833
|
+
const cfg = getContributionConfig();
|
|
4834
|
+
if (cfg.contribution.set_via !== "default")
|
|
4835
|
+
return false;
|
|
4836
|
+
const remaining = cfg.notice_shown_count ?? 0;
|
|
4837
|
+
if (remaining <= 0)
|
|
4838
|
+
return false;
|
|
4839
|
+
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"})
|
|
4840
|
+
`);
|
|
4841
|
+
decrementNoticeCounter();
|
|
4842
|
+
return true;
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
// ../../src/config/contribution.ts
|
|
4846
|
+
import fs3 from "node:fs";
|
|
4847
|
+
import path10 from "node:path";
|
|
4848
|
+
import os7 from "node:os";
|
|
4849
|
+
function configPath2() {
|
|
4850
|
+
return process.env.UNBROWSE_CONFIG_PATH || path10.join(os7.homedir(), ".unbrowse", "config.json");
|
|
4851
|
+
}
|
|
4852
|
+
var DEFAULT2 = {
|
|
4853
|
+
contribution: { share_pointers: false, set_via: "default" },
|
|
4854
|
+
rev_share: { opted_in: false },
|
|
4855
|
+
notice_shown_count: 0
|
|
4856
|
+
};
|
|
4857
|
+
function freshDefault2() {
|
|
4858
|
+
return {
|
|
4859
|
+
contribution: { ...DEFAULT2.contribution },
|
|
4860
|
+
rev_share: { ...DEFAULT2.rev_share },
|
|
4861
|
+
notice_shown_count: 0
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4864
|
+
var cached2 = null;
|
|
4865
|
+
function getContributionConfig2() {
|
|
4866
|
+
if (cached2)
|
|
4867
|
+
return cached2;
|
|
4868
|
+
const p = configPath2();
|
|
4869
|
+
let fileExisted = false;
|
|
4870
|
+
let raw = null;
|
|
4871
|
+
try {
|
|
4872
|
+
const content = fs3.readFileSync(p, "utf-8");
|
|
4873
|
+
fileExisted = true;
|
|
4874
|
+
if (content.trim().length > 0) {
|
|
4875
|
+
raw = JSON.parse(content);
|
|
4876
|
+
}
|
|
4877
|
+
} catch {}
|
|
4878
|
+
if (!raw) {
|
|
4879
|
+
cached2 = freshDefault2();
|
|
4880
|
+
return cached2;
|
|
4881
|
+
}
|
|
4882
|
+
const contributionRaw = raw.contribution ?? null;
|
|
4883
|
+
const revShareRaw = raw.rev_share ?? null;
|
|
4884
|
+
const isExistingUserMigration = fileExisted && !contributionRaw;
|
|
4885
|
+
cached2 = {
|
|
4886
|
+
contribution: {
|
|
4887
|
+
share_pointers: !!(contributionRaw?.share_pointers ?? DEFAULT2.contribution.share_pointers),
|
|
4888
|
+
set_via: contributionRaw?.set_via ?? DEFAULT2.contribution.set_via,
|
|
4889
|
+
...contributionRaw?.set_at ? { set_at: String(contributionRaw.set_at) } : {}
|
|
4890
|
+
},
|
|
4891
|
+
rev_share: {
|
|
4892
|
+
opted_in: !!(revShareRaw?.opted_in ?? DEFAULT2.rev_share.opted_in),
|
|
4893
|
+
...revShareRaw?.wallet_address ? { wallet_address: String(revShareRaw.wallet_address) } : {}
|
|
4894
|
+
},
|
|
4895
|
+
notice_shown_count: typeof raw.notice_shown_count === "number" ? raw.notice_shown_count : isExistingUserMigration ? 5 : 0
|
|
4896
|
+
};
|
|
4897
|
+
if (isExistingUserMigration) {
|
|
4898
|
+
try {
|
|
4899
|
+
const merged = { ...raw, ...cached2 };
|
|
4900
|
+
fs3.mkdirSync(path10.dirname(p), { recursive: true });
|
|
4901
|
+
fs3.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
4902
|
+
} catch {}
|
|
4903
|
+
}
|
|
4904
|
+
return cached2;
|
|
4905
|
+
}
|
|
4906
|
+
function setContributionConfig2(updates) {
|
|
4907
|
+
const current = getContributionConfig2();
|
|
4908
|
+
const next = {
|
|
4909
|
+
contribution: {
|
|
4910
|
+
...current.contribution,
|
|
4911
|
+
...updates.contribution ?? {},
|
|
4912
|
+
set_at: new Date().toISOString()
|
|
4913
|
+
},
|
|
4914
|
+
rev_share: {
|
|
4915
|
+
...current.rev_share,
|
|
4916
|
+
...updates.rev_share ?? {}
|
|
4917
|
+
},
|
|
4918
|
+
notice_shown_count: updates.notice_shown_count ?? current.notice_shown_count ?? 0
|
|
4919
|
+
};
|
|
4920
|
+
const p = configPath2();
|
|
4921
|
+
let existing = {};
|
|
4922
|
+
try {
|
|
4923
|
+
const content = fs3.readFileSync(p, "utf-8");
|
|
4924
|
+
if (content.trim())
|
|
4925
|
+
existing = JSON.parse(content);
|
|
4926
|
+
} catch {}
|
|
4927
|
+
const merged = { ...existing, ...next };
|
|
4928
|
+
fs3.mkdirSync(path10.dirname(p), { recursive: true });
|
|
4929
|
+
fs3.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
4930
|
+
cached2 = next;
|
|
4931
|
+
}
|
|
4932
|
+
|
|
4429
4933
|
// ../../src/cli.ts
|
|
4430
4934
|
loadEnv({ quiet: true });
|
|
4431
4935
|
loadEnv({ path: ".env.runtime", quiet: true });
|
|
@@ -4457,8 +4961,12 @@ function parseArgs(argv) {
|
|
|
4457
4961
|
if (valueExpectedFlags.has(key)) {
|
|
4458
4962
|
if (next === undefined)
|
|
4459
4963
|
die(`--${key} requires a value`);
|
|
4460
|
-
|
|
4461
|
-
|
|
4964
|
+
if (next === "-p" || next === "--param" || next.startsWith("--")) {
|
|
4965
|
+
flags[key] = true;
|
|
4966
|
+
} else {
|
|
4967
|
+
flags[key] = next;
|
|
4968
|
+
i++;
|
|
4969
|
+
}
|
|
4462
4970
|
} else if (!next || next.startsWith("--") || next === "-p" || next === "--param") {
|
|
4463
4971
|
flags[key] = true;
|
|
4464
4972
|
} else {
|
|
@@ -4471,8 +4979,8 @@ function parseArgs(argv) {
|
|
|
4471
4979
|
}
|
|
4472
4980
|
return { command, args: positional, flags, params };
|
|
4473
4981
|
}
|
|
4474
|
-
async function api2(method,
|
|
4475
|
-
let target = `${BASE_URL}${
|
|
4982
|
+
async function api2(method, path11, body, opts) {
|
|
4983
|
+
let target = `${BASE_URL}${path11}`;
|
|
4476
4984
|
let requestBody = body;
|
|
4477
4985
|
if (method === "GET" && body && typeof body === "object") {
|
|
4478
4986
|
const params = new URLSearchParams;
|
|
@@ -4486,14 +4994,34 @@ async function api2(method, path9, body) {
|
|
|
4486
4994
|
target += `${target.includes("?") ? "&" : "?"}${query}`;
|
|
4487
4995
|
requestBody = undefined;
|
|
4488
4996
|
}
|
|
4489
|
-
const
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4997
|
+
const ctrl = new AbortController;
|
|
4998
|
+
const timeoutMs = opts?.timeoutMs;
|
|
4999
|
+
const timer = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => ctrl.abort(), timeoutMs) : null;
|
|
5000
|
+
let res;
|
|
5001
|
+
try {
|
|
5002
|
+
res = await fetch(target, {
|
|
5003
|
+
method,
|
|
5004
|
+
headers: {
|
|
5005
|
+
...requestBody ? { "Content-Type": "application/json" } : {},
|
|
5006
|
+
"x-unbrowse-client-id": CLI_CLIENT_ID
|
|
5007
|
+
},
|
|
5008
|
+
body: requestBody ? JSON.stringify(requestBody) : undefined,
|
|
5009
|
+
signal: ctrl.signal
|
|
5010
|
+
});
|
|
5011
|
+
} catch (err) {
|
|
5012
|
+
if (timer)
|
|
5013
|
+
clearTimeout(timer);
|
|
5014
|
+
if (err?.name === "AbortError") {
|
|
5015
|
+
return {
|
|
5016
|
+
error: "cli_timeout",
|
|
5017
|
+
message: `CLI gave up waiting on local server after ${timeoutMs}ms. Try: pkill -9 -f 'unbrowse|kuri'`
|
|
5018
|
+
};
|
|
5019
|
+
}
|
|
5020
|
+
throw err;
|
|
5021
|
+
} finally {
|
|
5022
|
+
if (timer)
|
|
5023
|
+
clearTimeout(timer);
|
|
5024
|
+
}
|
|
4497
5025
|
if (!res.ok && res.headers.get("content-type")?.includes("json")) {
|
|
4498
5026
|
return res.json();
|
|
4499
5027
|
}
|
|
@@ -4714,6 +5242,7 @@ async function cmdResolve(flags) {
|
|
|
4714
5242
|
const intent = flags.intent;
|
|
4715
5243
|
if (!intent)
|
|
4716
5244
|
die("--intent is required");
|
|
5245
|
+
maybeShowContributionNotice();
|
|
4717
5246
|
const hostType = detectTelemetryHostType();
|
|
4718
5247
|
await ensureCliInstallTracked(hostType);
|
|
4719
5248
|
await recordFunnelTelemetryEvent("cli_invoked", {
|
|
@@ -4775,10 +5304,19 @@ async function cmdResolve(flags) {
|
|
|
4775
5304
|
body.force_capture = true;
|
|
4776
5305
|
if (flags["skip-robots"])
|
|
4777
5306
|
body.skip_robots_check = true;
|
|
5307
|
+
const budgetFlag = flags.budget;
|
|
5308
|
+
if (typeof budgetFlag === "string") {
|
|
5309
|
+
const parsed = parseInt(budgetFlag, 10);
|
|
5310
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
5311
|
+
body.budget_ms = parsed;
|
|
5312
|
+
} else if (typeof budgetFlag === "number" && Number.isFinite(budgetFlag) && budgetFlag > 0) {
|
|
5313
|
+
body.budget_ms = budgetFlag;
|
|
5314
|
+
}
|
|
4778
5315
|
body.projection = { raw: true };
|
|
4779
5316
|
const startedAt = Date.now();
|
|
4780
5317
|
async function resolveOnce(message = "Still working. Searching cached routes...") {
|
|
4781
|
-
|
|
5318
|
+
const cliTimeoutMs = (typeof body.budget_ms === "number" ? body.budget_ms : 8000) + 30000;
|
|
5319
|
+
return withPendingNotice(api2("POST", "/v1/intent/resolve", body, { timeoutMs: cliTimeoutMs }), message);
|
|
4782
5320
|
}
|
|
4783
5321
|
let result = await resolveOnce();
|
|
4784
5322
|
const resultError = resolveResultError(result);
|
|
@@ -4798,8 +5336,13 @@ async function cmdResolve(flags) {
|
|
|
4798
5336
|
const skillId = resolveSkillId();
|
|
4799
5337
|
if (skillId && endpoints.length > 0) {
|
|
4800
5338
|
const bestEndpoint = endpoints[0];
|
|
4801
|
-
|
|
4802
|
-
|
|
5339
|
+
if (endpointNeedsThirdPartyTermsConfirmation(bestEndpoint) && !flags["confirm-third-party-terms"]) {
|
|
5340
|
+
process.stderr.write(`Skipping auto-execute: endpoint ${bestEndpoint.endpoint_id ?? "?"} ` + `requires explicit third-party terms confirmation. ` + `Re-run with --confirm-third-party-terms to proceed.
|
|
5341
|
+
`);
|
|
5342
|
+
} else {
|
|
5343
|
+
info(`Auto-executing endpoint: ${bestEndpoint.description ?? bestEndpoint.endpoint_id}`);
|
|
5344
|
+
result = await withPendingNotice(api2("POST", `/v1/skills/${skillId}/execute`, execBody(bestEndpoint.endpoint_id)), "Executing best endpoint...");
|
|
5345
|
+
}
|
|
4803
5346
|
}
|
|
4804
5347
|
}
|
|
4805
5348
|
if (Date.now() - startedAt > 3000 && result.source === "live-capture") {
|
|
@@ -4861,8 +5404,8 @@ async function cmdResolve(flags) {
|
|
|
4861
5404
|
throw error;
|
|
4862
5405
|
}
|
|
4863
5406
|
}
|
|
4864
|
-
function drillPath(data,
|
|
4865
|
-
const segments =
|
|
5407
|
+
function drillPath(data, path11) {
|
|
5408
|
+
const segments = path11.split(/\./).flatMap((s) => {
|
|
4866
5409
|
const m = s.match(/^(.+)\[\]$/);
|
|
4867
5410
|
return m ? [m[1], "[]"] : [s];
|
|
4868
5411
|
});
|
|
@@ -4889,9 +5432,9 @@ function drillPath(data, path9) {
|
|
|
4889
5432
|
}
|
|
4890
5433
|
return values;
|
|
4891
5434
|
}
|
|
4892
|
-
function resolveDotPath(obj,
|
|
5435
|
+
function resolveDotPath(obj, path11) {
|
|
4893
5436
|
let cur = obj;
|
|
4894
|
-
for (const key of
|
|
5437
|
+
for (const key of path11.split(".")) {
|
|
4895
5438
|
if (cur == null || typeof cur !== "object")
|
|
4896
5439
|
return;
|
|
4897
5440
|
cur = cur[key];
|
|
@@ -4908,8 +5451,8 @@ function applyExtract(items, extractSpec) {
|
|
|
4908
5451
|
return items.map((item) => {
|
|
4909
5452
|
const row = {};
|
|
4910
5453
|
let hasValue = false;
|
|
4911
|
-
for (const { alias, path:
|
|
4912
|
-
const val = resolveDotPath(item,
|
|
5454
|
+
for (const { alias, path: path11 } of fields) {
|
|
5455
|
+
const val = resolveDotPath(item, path11);
|
|
4913
5456
|
row[alias] = val ?? null;
|
|
4914
5457
|
if (val != null)
|
|
4915
5458
|
hasValue = true;
|
|
@@ -4940,6 +5483,7 @@ async function cmdExecute(flags) {
|
|
|
4940
5483
|
const skillId = flags.skill;
|
|
4941
5484
|
if (!skillId)
|
|
4942
5485
|
die("--skill is required");
|
|
5486
|
+
maybeShowContributionNotice();
|
|
4943
5487
|
const hostType = detectTelemetryHostType();
|
|
4944
5488
|
await ensureCliInstallTracked(hostType);
|
|
4945
5489
|
await recordFunnelTelemetryEvent("cli_invoked", {
|
|
@@ -5225,7 +5769,67 @@ async function cmdCleanupStale(flags) {
|
|
|
5225
5769
|
output(await withPendingNotice(api2("POST", "/v1/stale/cleanup", body), "Cleaning stale endpoints..."), !!flags.pretty);
|
|
5226
5770
|
}
|
|
5227
5771
|
async function cmdSearch(flags) {
|
|
5228
|
-
|
|
5772
|
+
const intent = flags.intent;
|
|
5773
|
+
const hostType = detectTelemetryHostType();
|
|
5774
|
+
const { decodeTelemetryAttribution: decodeTelemetryAttribution3 } = await Promise.resolve().then(() => (init_telemetry_attribution2(), exports_telemetry_attribution));
|
|
5775
|
+
const attr = decodeTelemetryAttribution3(process.env.UNBROWSE_ATTRIBUTION_B64) ?? {};
|
|
5776
|
+
const attrProps = {};
|
|
5777
|
+
for (const k of ["channel", "campaign_id", "content_id", "variant_id"]) {
|
|
5778
|
+
const v = attr[k];
|
|
5779
|
+
if (v != null)
|
|
5780
|
+
attrProps[k] = v;
|
|
5781
|
+
}
|
|
5782
|
+
const domain = telemetryDomainFromInput(flags.domain, flags.url);
|
|
5783
|
+
if (intent) {
|
|
5784
|
+
await recordFunnelTelemetryEvent("search_started", {
|
|
5785
|
+
source: "cli",
|
|
5786
|
+
hostType,
|
|
5787
|
+
properties: {
|
|
5788
|
+
command: "search",
|
|
5789
|
+
intent,
|
|
5790
|
+
domain,
|
|
5791
|
+
url: typeof flags.url === "string" ? flags.url : null,
|
|
5792
|
+
...attrProps
|
|
5793
|
+
}
|
|
5794
|
+
});
|
|
5795
|
+
}
|
|
5796
|
+
let resultCount = 0;
|
|
5797
|
+
try {
|
|
5798
|
+
if (intent && domain) {
|
|
5799
|
+
try {
|
|
5800
|
+
const searchRes = await api2("GET", `/v1/search/domain?intent=${encodeURIComponent(intent)}&domain=${encodeURIComponent(domain)}`);
|
|
5801
|
+
resultCount = Array.isArray(searchRes?.results) ? searchRes.results.length : 0;
|
|
5802
|
+
} catch {}
|
|
5803
|
+
}
|
|
5804
|
+
await cmdResolve(flags);
|
|
5805
|
+
if (intent) {
|
|
5806
|
+
await recordFunnelTelemetryEvent("search_completed", {
|
|
5807
|
+
source: "cli",
|
|
5808
|
+
hostType,
|
|
5809
|
+
properties: {
|
|
5810
|
+
command: "search",
|
|
5811
|
+
intent,
|
|
5812
|
+
domain,
|
|
5813
|
+
result_count: resultCount,
|
|
5814
|
+
...attrProps
|
|
5815
|
+
}
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5818
|
+
} catch (err) {
|
|
5819
|
+
if (intent) {
|
|
5820
|
+
await recordFunnelTelemetryEvent("search_failed", {
|
|
5821
|
+
source: "cli",
|
|
5822
|
+
hostType,
|
|
5823
|
+
properties: {
|
|
5824
|
+
command: "search",
|
|
5825
|
+
intent,
|
|
5826
|
+
error: err instanceof Error ? err.message : String(err),
|
|
5827
|
+
...attrProps
|
|
5828
|
+
}
|
|
5829
|
+
});
|
|
5830
|
+
}
|
|
5831
|
+
throw err;
|
|
5832
|
+
}
|
|
5229
5833
|
}
|
|
5230
5834
|
async function cmdSessions(flags) {
|
|
5231
5835
|
const domain = flags.domain;
|
|
@@ -5381,6 +5985,103 @@ async function cmdSetup(flags) {
|
|
|
5381
5985
|
info(' unbrowse resolve --intent "list posts" --url "https://jsonplaceholder.typicode.com"');
|
|
5382
5986
|
}
|
|
5383
5987
|
}
|
|
5988
|
+
async function cmdMode(_flags) {
|
|
5989
|
+
await promptContributionMode({ force: true });
|
|
5990
|
+
await syncContributionPreferenceToServer();
|
|
5991
|
+
}
|
|
5992
|
+
async function syncContributionPreferenceToServer() {
|
|
5993
|
+
const cfg = loadConfig();
|
|
5994
|
+
if (!cfg?.api_key)
|
|
5995
|
+
return;
|
|
5996
|
+
const share_pointers = !!getContributionConfig2().contribution.share_pointers;
|
|
5997
|
+
try {
|
|
5998
|
+
await pushAccountPreferences({ share_pointers });
|
|
5999
|
+
info("Synced preference to your account.");
|
|
6000
|
+
} catch (err) {
|
|
6001
|
+
const msg = err.message ?? "";
|
|
6002
|
+
if (msg.includes("account_required") || msg.includes("HTTP 403"))
|
|
6003
|
+
return;
|
|
6004
|
+
info(`Local mode set, but server sync failed: ${msg}`);
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
async function runPostSetupContributionPrompt() {
|
|
6008
|
+
try {
|
|
6009
|
+
await promptContributionMode({ force: false });
|
|
6010
|
+
} catch {}
|
|
6011
|
+
}
|
|
6012
|
+
async function cmdCapture(flags) {
|
|
6013
|
+
const url = flags.url;
|
|
6014
|
+
const intent = flags.intent || "capture";
|
|
6015
|
+
if (!url)
|
|
6016
|
+
die("--url is required");
|
|
6017
|
+
maybeShowContributionNotice();
|
|
6018
|
+
const t0 = Date.now();
|
|
6019
|
+
const result = await api2("POST", "/v1/capture", { url, intent });
|
|
6020
|
+
const endpoints = Array.isArray(result.endpoints) ? result.endpoints : Array.isArray(result.available_endpoints) ? result.available_endpoints : [];
|
|
6021
|
+
const skill = result.skill ?? null;
|
|
6022
|
+
const skillId = result.skill_id ?? skill?.skill_id ?? (typeof result.learned_skill_id === "string" ? result.learned_skill_id : undefined);
|
|
6023
|
+
const isThinDocumentOnly = endpoints.length === 1 && (() => {
|
|
6024
|
+
const e0 = endpoints[0];
|
|
6025
|
+
if (!e0 || e0.method !== "GET")
|
|
6026
|
+
return false;
|
|
6027
|
+
const tmpl = (e0.url_template ?? "").split("?")[0].replace(/\/$/, "");
|
|
6028
|
+
const target = url.split("?")[0].replace(/\/$/, "");
|
|
6029
|
+
return tmpl === target;
|
|
6030
|
+
})();
|
|
6031
|
+
const escapedIntent = intent.replace(/"/g, "\\\"");
|
|
6032
|
+
const escapedUrl = url.replace(/"/g, "\\\"");
|
|
6033
|
+
const envelope = {
|
|
6034
|
+
skill_id: skillId,
|
|
6035
|
+
endpoints_discovered: typeof result.endpoints_discovered === "number" ? result.endpoints_discovered : endpoints.length,
|
|
6036
|
+
marketplace_published: !!result.marketplace_published,
|
|
6037
|
+
ms: typeof result.ms === "number" ? result.ms : Date.now() - t0,
|
|
6038
|
+
next_step: endpoints.length === 0 ? "no endpoints discovered; site may need authentication or different intent" : `unbrowse resolve --intent "${escapedIntent}" --url "${escapedUrl}"`,
|
|
6039
|
+
...isThinDocumentOnly ? { capture_pattern: "doc_only", capture_observation: "only the input URL was captured (1 GET, no XHR fired during the auto-capture window)" } : {},
|
|
6040
|
+
...result.error ? { error: result.error } : {},
|
|
6041
|
+
...result.captured_meta ? { captured_meta: result.captured_meta } : {},
|
|
6042
|
+
...typeof result.capture_path === "string" ? { capture_path: result.capture_path } : {},
|
|
6043
|
+
...result.prior_domain_note ? { prior_domain_note: result.prior_domain_note } : {},
|
|
6044
|
+
...result.note_evidence ? { note_evidence: result.note_evidence } : {}
|
|
6045
|
+
};
|
|
6046
|
+
output(envelope, !!flags.pretty);
|
|
6047
|
+
}
|
|
6048
|
+
async function cmdNote(flags, args) {
|
|
6049
|
+
const subRaw = args?.[0] ?? flags.action;
|
|
6050
|
+
const sub = (typeof subRaw === "string" ? subRaw : "").toLowerCase();
|
|
6051
|
+
if (!sub || !["read", "write", "list"].includes(sub)) {
|
|
6052
|
+
die('usage: unbrowse note <read|write|list> --domain <domain> [--body "..."]');
|
|
6053
|
+
}
|
|
6054
|
+
if (sub === "list") {
|
|
6055
|
+
const { homedir: homedir9 } = await import("os");
|
|
6056
|
+
const { join: join15 } = await import("path");
|
|
6057
|
+
const { readdirSync: readdirSync7, existsSync: existsSync19 } = await import("fs");
|
|
6058
|
+
const profile = process.env.UNBROWSE_PROFILE ?? "";
|
|
6059
|
+
const dir = process.env.UNBROWSE_DOMAIN_NOTES_DIR ?? (profile ? join15(homedir9(), ".unbrowse", "profiles", profile, "domain-notes") : join15(homedir9(), ".unbrowse", "domain-notes"));
|
|
6060
|
+
if (!existsSync19(dir)) {
|
|
6061
|
+
output({ notes: [], dir }, !!flags.pretty);
|
|
6062
|
+
return;
|
|
6063
|
+
}
|
|
6064
|
+
const files = readdirSync7(dir).filter((f) => f.endsWith(".md"));
|
|
6065
|
+
output({ dir, notes: files.map((f) => f.replace(/\.md$/, "")) }, !!flags.pretty);
|
|
6066
|
+
return;
|
|
6067
|
+
}
|
|
6068
|
+
const domain = flags.domain;
|
|
6069
|
+
if (!domain)
|
|
6070
|
+
die("--domain required");
|
|
6071
|
+
if (sub === "read") {
|
|
6072
|
+
const res2 = await api2("GET", `/v1/domain-notes/${encodeURIComponent(domain)}`).catch((err) => ({
|
|
6073
|
+
error: err.message
|
|
6074
|
+
}));
|
|
6075
|
+
output(res2, !!flags.pretty);
|
|
6076
|
+
return;
|
|
6077
|
+
}
|
|
6078
|
+
const body = flags.body;
|
|
6079
|
+
if (typeof body !== "string" || body.trim().length === 0) {
|
|
6080
|
+
die("--body required (non-empty markdown string)");
|
|
6081
|
+
}
|
|
6082
|
+
const res = await api2("POST", `/v1/domain-notes/${encodeURIComponent(domain)}`, { body });
|
|
6083
|
+
output(res, !!flags.pretty);
|
|
6084
|
+
}
|
|
5384
6085
|
var CLI_REFERENCE = {
|
|
5385
6086
|
commands: [
|
|
5386
6087
|
{ name: "health", usage: "", desc: "Server health check" },
|
|
@@ -5425,7 +6126,10 @@ var CLI_REFERENCE = {
|
|
|
5425
6126
|
{ name: "earnings", usage: "[--json]", desc: "Show your credit balance, earnings from indexing, and spending" },
|
|
5426
6127
|
{ 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
6128
|
{ 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: "
|
|
6129
|
+
{ name: "register", usage: "[--email lewis@example.com] [--no-prompt]", desc: "Register an API key. With --email, sends a magic link via Resend; otherwise creates an anonymous key." },
|
|
6130
|
+
{ name: "mode", usage: "", desc: "Re-prompt for contribution mode (private / share / share + earn)" },
|
|
6131
|
+
{ 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`." },
|
|
6132
|
+
{ name: "note", usage: '<read|write|list> --domain <domain> [--body "..."]', desc: "Read/write per-domain LLM-prose notes consumed by augment on next capture. Agent populates after reading capture's note_evidence." }
|
|
5429
6133
|
],
|
|
5430
6134
|
globalFlags: [
|
|
5431
6135
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
@@ -5882,6 +6586,7 @@ async function cmdGo(args, flags) {
|
|
|
5882
6586
|
const url = args[0] ?? flags.url;
|
|
5883
6587
|
if (!url)
|
|
5884
6588
|
die("Usage: unbrowse go <url>");
|
|
6589
|
+
maybeShowContributionNotice();
|
|
5885
6590
|
output(await api2("POST", "/v1/browse/go", {
|
|
5886
6591
|
url,
|
|
5887
6592
|
...typeof flags.session === "string" ? { session_id: flags.session } : {}
|
|
@@ -6019,6 +6724,43 @@ async function cmdClose(flags) {
|
|
|
6019
6724
|
output(await api2("POST", "/v1/browse/close", typeof flags.session === "string" ? { session_id: flags.session } : undefined), false);
|
|
6020
6725
|
}
|
|
6021
6726
|
async function cmdRegister(flags) {
|
|
6727
|
+
if (typeof flags.email === "string" && flags.email.length > 0) {
|
|
6728
|
+
const email = flags.email;
|
|
6729
|
+
if (getApiKey()) {
|
|
6730
|
+
info("Already registered. Re-running with --email will mint a new key and overwrite ~/.unbrowse/config.json.");
|
|
6731
|
+
}
|
|
6732
|
+
info(`Sending magic link to ${email}\u2026`);
|
|
6733
|
+
const result = await magicRegister({
|
|
6734
|
+
email,
|
|
6735
|
+
openBrowser: (url) => {
|
|
6736
|
+
info(`Opening browser: ${url}`);
|
|
6737
|
+
try {
|
|
6738
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
6739
|
+
spawn3(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
6740
|
+
} catch {}
|
|
6741
|
+
}
|
|
6742
|
+
});
|
|
6743
|
+
saveConfig({
|
|
6744
|
+
api_key: result.api_key,
|
|
6745
|
+
agent_id: result.agent_id,
|
|
6746
|
+
agent_name: result.email,
|
|
6747
|
+
registered_at: new Date().toISOString(),
|
|
6748
|
+
tos_accepted_version: null,
|
|
6749
|
+
tos_accepted_at: null
|
|
6750
|
+
});
|
|
6751
|
+
process.env.UNBROWSE_API_KEY = result.api_key;
|
|
6752
|
+
info(`Signed in as ${result.email}. API key saved to ~/.unbrowse/config.json.`);
|
|
6753
|
+
try {
|
|
6754
|
+
const serverPrefs = await fetchAccountPreferences();
|
|
6755
|
+
if (serverPrefs) {
|
|
6756
|
+
setContributionConfig2({
|
|
6757
|
+
contribution: { share_pointers: serverPrefs.share_pointers, set_via: "mode-command" }
|
|
6758
|
+
});
|
|
6759
|
+
info(`Auto-publish to marketplace: ${serverPrefs.share_pointers ? "ON" : "off"} (synced from your account).`);
|
|
6760
|
+
}
|
|
6761
|
+
} catch {}
|
|
6762
|
+
return;
|
|
6763
|
+
}
|
|
6022
6764
|
if (getApiKey()) {
|
|
6023
6765
|
info("Already registered. API key loaded from env or ~/.unbrowse/config.json");
|
|
6024
6766
|
return;
|
|
@@ -6308,6 +7050,11 @@ async function main() {
|
|
|
6308
7050
|
}
|
|
6309
7051
|
if (command === "setup") {
|
|
6310
7052
|
await cmdSetup(flags);
|
|
7053
|
+
await runPostSetupContributionPrompt();
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
if (command === "mode") {
|
|
7057
|
+
await cmdMode(flags);
|
|
6311
7058
|
return;
|
|
6312
7059
|
}
|
|
6313
7060
|
if (command === "mcp")
|
|
@@ -6386,7 +7133,9 @@ async function main() {
|
|
|
6386
7133
|
"corpus-run",
|
|
6387
7134
|
"sessions-scan",
|
|
6388
7135
|
"cache-clear",
|
|
6389
|
-
"register"
|
|
7136
|
+
"register",
|
|
7137
|
+
"mode",
|
|
7138
|
+
"capture"
|
|
6390
7139
|
]);
|
|
6391
7140
|
if (!KNOWN_COMMANDS.has(command)) {
|
|
6392
7141
|
const pack = findSitePack(command);
|
|
@@ -6500,6 +7249,12 @@ async function main() {
|
|
|
6500
7249
|
return cmdSessionsScan(flags);
|
|
6501
7250
|
case "register":
|
|
6502
7251
|
return cmdRegister(flags);
|
|
7252
|
+
case "mode":
|
|
7253
|
+
return cmdMode(flags);
|
|
7254
|
+
case "capture":
|
|
7255
|
+
return cmdCapture(flags);
|
|
7256
|
+
case "note":
|
|
7257
|
+
return cmdNote(flags, args);
|
|
6503
7258
|
default:
|
|
6504
7259
|
info(`Unknown command: ${command}`);
|
|
6505
7260
|
printHelp();
|