unbrowse 6.4.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 +597 -79
- package/dist/mcp.js +8 -6
- package/dist/server.js +679 -149
- 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);
|
|
@@ -1324,6 +1327,20 @@ function extractBrowserCookies(domain, opts) {
|
|
|
1324
1327
|
}
|
|
1325
1328
|
const chrome = extractFromChrome(domain, { profile: opts?.chromeProfile });
|
|
1326
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
|
+
}
|
|
1327
1344
|
return chrome;
|
|
1328
1345
|
}
|
|
1329
1346
|
function scanAllBrowserSessions(domain) {
|
|
@@ -1490,22 +1507,25 @@ class LocalAuthRuntime {
|
|
|
1490
1507
|
if (session && session.expires > Date.now()) {
|
|
1491
1508
|
return { authenticated: true, session_token: session.token, method: "cached" };
|
|
1492
1509
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
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
|
+
}
|
|
1509
1529
|
if (dep.strategy === "refresh_session" && session) {
|
|
1510
1530
|
const refreshed = await this.refreshSession(dep.domain);
|
|
1511
1531
|
if (refreshed) {
|
|
@@ -1828,6 +1848,7 @@ var init_compile = () => {};
|
|
|
1828
1848
|
import { nanoid as nanoid6 } from "nanoid";
|
|
1829
1849
|
var VALID_VERIFICATION_STATUSES, STOPWORDS;
|
|
1830
1850
|
var init_execution = __esm(async () => {
|
|
1851
|
+
init_client2();
|
|
1831
1852
|
init_reverse_engineer();
|
|
1832
1853
|
init_bundle_scanner();
|
|
1833
1854
|
init_token_resolver();
|
|
@@ -1983,6 +2004,10 @@ import { nanoid as nanoid9 } from "nanoid";
|
|
|
1983
2004
|
import { existsSync as existsSync12, writeFileSync as writeFileSync4, readFileSync as readFileSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
1984
2005
|
import { dirname as dirname3, join as join10 } from "node:path";
|
|
1985
2006
|
function _writeRouteCacheToDisk() {
|
|
2007
|
+
if (!LOCAL_CACHES_ENABLED) {
|
|
2008
|
+
_routeCacheDirty = false;
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
1986
2011
|
try {
|
|
1987
2012
|
const dir = dirname3(ROUTE_CACHE_FILE);
|
|
1988
2013
|
if (!existsSync12(dir))
|
|
@@ -1992,7 +2017,7 @@ function _writeRouteCacheToDisk() {
|
|
|
1992
2017
|
} catch {}
|
|
1993
2018
|
_routeCacheDirty = false;
|
|
1994
2019
|
}
|
|
1995
|
-
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;
|
|
1996
2021
|
var init_orchestrator = __esm(async () => {
|
|
1997
2022
|
init_client();
|
|
1998
2023
|
init_client2();
|
|
@@ -2026,36 +2051,41 @@ var init_orchestrator = __esm(async () => {
|
|
|
2026
2051
|
SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join10(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
|
|
2027
2052
|
domainSkillCache = new Map;
|
|
2028
2053
|
DOMAIN_CACHE_FILE = join10(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2035
|
-
|
|
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
|
+
}
|
|
2036
2064
|
}
|
|
2065
|
+
console.error(`[domain-cache] loaded ${domainSkillCache.size} entries from disk`);
|
|
2037
2066
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
} catch {}
|
|
2067
|
+
} catch {}
|
|
2068
|
+
}
|
|
2041
2069
|
routeCacheFlushTimer = setInterval(() => {
|
|
2042
2070
|
if (!_routeCacheDirty)
|
|
2043
2071
|
return;
|
|
2044
2072
|
_writeRouteCacheToDisk();
|
|
2045
2073
|
}, 5000);
|
|
2046
2074
|
routeCacheFlushTimer.unref?.();
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
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
|
+
}
|
|
2054
2084
|
}
|
|
2085
|
+
console.error(`[route-cache] loaded ${skillRouteCache.size} entries from disk`);
|
|
2055
2086
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
} catch {}
|
|
2087
|
+
} catch {}
|
|
2088
|
+
}
|
|
2059
2089
|
routeResultCache = new Map;
|
|
2060
2090
|
ROUTE_CACHE_TTL = 24 * 60 * 60000;
|
|
2061
2091
|
MARKETPLACE_HYDRATE_LIMIT = Math.max(1, Number(process.env.UNBROWSE_MARKETPLACE_HYDRATE_LIMIT ?? 4));
|
|
@@ -2173,9 +2203,11 @@ function getWalletContext2() {
|
|
|
2173
2203
|
wallet_provider: asNonEmptyString2(process.env.AGENT_WALLET_PROVIDER)
|
|
2174
2204
|
};
|
|
2175
2205
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
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
|
+
}
|
|
2179
2211
|
}
|
|
2180
2212
|
return {};
|
|
2181
2213
|
}
|
|
@@ -2190,6 +2222,100 @@ function checkWalletConfigured2() {
|
|
|
2190
2222
|
}
|
|
2191
2223
|
var init_wallet2 = () => {};
|
|
2192
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
|
+
|
|
2193
2319
|
// ../../src/version.ts
|
|
2194
2320
|
var exports_version = {};
|
|
2195
2321
|
__export(exports_version, {
|
|
@@ -2202,6 +2328,7 @@ __export(exports_version, {
|
|
|
2202
2328
|
RELEASE_MANIFEST_BASE64: () => RELEASE_MANIFEST_BASE642,
|
|
2203
2329
|
PACKAGE_VERSION: () => PACKAGE_VERSION2,
|
|
2204
2330
|
GIT_SHA: () => GIT_SHA2,
|
|
2331
|
+
DEFAULT_PROFILE: () => DEFAULT_PROFILE2,
|
|
2205
2332
|
DEFAULT_BACKEND_URL: () => DEFAULT_BACKEND_URL2,
|
|
2206
2333
|
CODE_HASH: () => CODE_HASH2
|
|
2207
2334
|
});
|
|
@@ -2303,13 +2430,14 @@ function getPackageVersion2() {
|
|
|
2303
2430
|
return packageVersion;
|
|
2304
2431
|
return getEmbeddedReleaseVersion2() ?? "unknown";
|
|
2305
2432
|
}
|
|
2306
|
-
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;
|
|
2307
2434
|
var init_version2 = __esm(() => {
|
|
2308
2435
|
MODULE_DIR2 = dirname4(fileURLToPath4(import.meta.url));
|
|
2309
2436
|
CODE_HASH2 = BUILD_CODE_HASH?.trim() || computeCodeHash2();
|
|
2310
2437
|
GIT_SHA2 = getGitSha2();
|
|
2311
2438
|
PACKAGE_VERSION2 = getPackageVersion2();
|
|
2312
2439
|
DEFAULT_BACKEND_URL2 = BUILD_DEFAULT_BACKEND_URL?.trim() || "https://beta-api.unbrowse.ai";
|
|
2440
|
+
DEFAULT_PROFILE2 = BUILD_DEFAULT_PROFILE?.trim() || "";
|
|
2313
2441
|
TRACE_VERSION2 = `${CODE_HASH2}@${GIT_SHA2}`;
|
|
2314
2442
|
RELEASE_MANIFEST_BASE642 = BUILD_RELEASE_MANIFEST_BASE64?.trim() || "";
|
|
2315
2443
|
RELEASE_MANIFEST_SIGNATURE2 = BUILD_RELEASE_MANIFEST_SIGNATURE?.trim() || "";
|
|
@@ -2399,9 +2527,9 @@ function getChromiumKeychainServiceName2(opts) {
|
|
|
2399
2527
|
}
|
|
2400
2528
|
function getChromiumDecryptionKey2(opts) {
|
|
2401
2529
|
const service = getChromiumKeychainServiceName2(opts);
|
|
2402
|
-
const
|
|
2403
|
-
if (
|
|
2404
|
-
return
|
|
2530
|
+
const cached3 = _chromiumKeyCache2.get(service);
|
|
2531
|
+
if (cached3)
|
|
2532
|
+
return cached3;
|
|
2405
2533
|
if (platform2() !== "darwin")
|
|
2406
2534
|
return null;
|
|
2407
2535
|
try {
|
|
@@ -2620,6 +2748,20 @@ function extractBrowserCookies2(domain, opts) {
|
|
|
2620
2748
|
}
|
|
2621
2749
|
const chrome = extractFromChrome2(domain, { profile: opts?.chromeProfile });
|
|
2622
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
|
+
}
|
|
2623
2765
|
return chrome;
|
|
2624
2766
|
}
|
|
2625
2767
|
function scanAllBrowserSessions2(domain) {
|
|
@@ -2697,7 +2839,7 @@ import { randomBytes, createHash as createHash2 } from "crypto";
|
|
|
2697
2839
|
import { createInterface } from "readline";
|
|
2698
2840
|
import { execSync } from "child_process";
|
|
2699
2841
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
2700
|
-
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
2842
|
+
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? DEFAULT_PROFILE ?? "");
|
|
2701
2843
|
var recentLocalSkills = new Map;
|
|
2702
2844
|
var LOCAL_ONLY = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
2703
2845
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
@@ -3295,9 +3437,99 @@ You have $2.00 in free credits — start resolving to use them.`);
|
|
|
3295
3437
|
process.exit(1);
|
|
3296
3438
|
}
|
|
3297
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
|
+
}
|
|
3298
3514
|
async function getMyProfile() {
|
|
3299
3515
|
return api("GET", "/v1/agents/me", undefined);
|
|
3300
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
|
+
}
|
|
3301
3533
|
async function syncAgentWallet(wallet = getLocalWalletContext()) {
|
|
3302
3534
|
if (!wallet.wallet_address)
|
|
3303
3535
|
return;
|
|
@@ -3902,7 +4134,7 @@ function isBundledVirtualEntrypoint(entrypoint) {
|
|
|
3902
4134
|
}
|
|
3903
4135
|
function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
|
|
3904
4136
|
if (path3.extname(entrypoint) !== ".ts") {
|
|
3905
|
-
return [pathToFileURL2(entrypoint).href];
|
|
4137
|
+
return process.platform === "win32" ? [pathToFileURL2(entrypoint).href] : [entrypoint];
|
|
3906
4138
|
}
|
|
3907
4139
|
if (process.versions.bun)
|
|
3908
4140
|
return [entrypoint];
|
|
@@ -4610,6 +4842,94 @@ function maybeShowContributionNotice() {
|
|
|
4610
4842
|
return true;
|
|
4611
4843
|
}
|
|
4612
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
|
+
|
|
4613
4933
|
// ../../src/cli.ts
|
|
4614
4934
|
loadEnv({ quiet: true });
|
|
4615
4935
|
loadEnv({ path: ".env.runtime", quiet: true });
|
|
@@ -4641,8 +4961,12 @@ function parseArgs(argv) {
|
|
|
4641
4961
|
if (valueExpectedFlags.has(key)) {
|
|
4642
4962
|
if (next === undefined)
|
|
4643
4963
|
die(`--${key} requires a value`);
|
|
4644
|
-
|
|
4645
|
-
|
|
4964
|
+
if (next === "-p" || next === "--param" || next.startsWith("--")) {
|
|
4965
|
+
flags[key] = true;
|
|
4966
|
+
} else {
|
|
4967
|
+
flags[key] = next;
|
|
4968
|
+
i++;
|
|
4969
|
+
}
|
|
4646
4970
|
} else if (!next || next.startsWith("--") || next === "-p" || next === "--param") {
|
|
4647
4971
|
flags[key] = true;
|
|
4648
4972
|
} else {
|
|
@@ -4655,8 +4979,8 @@ function parseArgs(argv) {
|
|
|
4655
4979
|
}
|
|
4656
4980
|
return { command, args: positional, flags, params };
|
|
4657
4981
|
}
|
|
4658
|
-
async function api2(method,
|
|
4659
|
-
let target = `${BASE_URL}${
|
|
4982
|
+
async function api2(method, path11, body, opts) {
|
|
4983
|
+
let target = `${BASE_URL}${path11}`;
|
|
4660
4984
|
let requestBody = body;
|
|
4661
4985
|
if (method === "GET" && body && typeof body === "object") {
|
|
4662
4986
|
const params = new URLSearchParams;
|
|
@@ -4670,14 +4994,34 @@ async function api2(method, path10, body) {
|
|
|
4670
4994
|
target += `${target.includes("?") ? "&" : "?"}${query}`;
|
|
4671
4995
|
requestBody = undefined;
|
|
4672
4996
|
}
|
|
4673
|
-
const
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
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
|
+
}
|
|
4681
5025
|
if (!res.ok && res.headers.get("content-type")?.includes("json")) {
|
|
4682
5026
|
return res.json();
|
|
4683
5027
|
}
|
|
@@ -4971,7 +5315,8 @@ async function cmdResolve(flags) {
|
|
|
4971
5315
|
body.projection = { raw: true };
|
|
4972
5316
|
const startedAt = Date.now();
|
|
4973
5317
|
async function resolveOnce(message = "Still working. Searching cached routes...") {
|
|
4974
|
-
|
|
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);
|
|
4975
5320
|
}
|
|
4976
5321
|
let result = await resolveOnce();
|
|
4977
5322
|
const resultError = resolveResultError(result);
|
|
@@ -4991,8 +5336,13 @@ async function cmdResolve(flags) {
|
|
|
4991
5336
|
const skillId = resolveSkillId();
|
|
4992
5337
|
if (skillId && endpoints.length > 0) {
|
|
4993
5338
|
const bestEndpoint = endpoints[0];
|
|
4994
|
-
|
|
4995
|
-
|
|
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
|
+
}
|
|
4996
5346
|
}
|
|
4997
5347
|
}
|
|
4998
5348
|
if (Date.now() - startedAt > 3000 && result.source === "live-capture") {
|
|
@@ -5054,8 +5404,8 @@ async function cmdResolve(flags) {
|
|
|
5054
5404
|
throw error;
|
|
5055
5405
|
}
|
|
5056
5406
|
}
|
|
5057
|
-
function drillPath(data,
|
|
5058
|
-
const segments =
|
|
5407
|
+
function drillPath(data, path11) {
|
|
5408
|
+
const segments = path11.split(/\./).flatMap((s) => {
|
|
5059
5409
|
const m = s.match(/^(.+)\[\]$/);
|
|
5060
5410
|
return m ? [m[1], "[]"] : [s];
|
|
5061
5411
|
});
|
|
@@ -5082,9 +5432,9 @@ function drillPath(data, path10) {
|
|
|
5082
5432
|
}
|
|
5083
5433
|
return values;
|
|
5084
5434
|
}
|
|
5085
|
-
function resolveDotPath(obj,
|
|
5435
|
+
function resolveDotPath(obj, path11) {
|
|
5086
5436
|
let cur = obj;
|
|
5087
|
-
for (const key of
|
|
5437
|
+
for (const key of path11.split(".")) {
|
|
5088
5438
|
if (cur == null || typeof cur !== "object")
|
|
5089
5439
|
return;
|
|
5090
5440
|
cur = cur[key];
|
|
@@ -5101,8 +5451,8 @@ function applyExtract(items, extractSpec) {
|
|
|
5101
5451
|
return items.map((item) => {
|
|
5102
5452
|
const row = {};
|
|
5103
5453
|
let hasValue = false;
|
|
5104
|
-
for (const { alias, path:
|
|
5105
|
-
const val = resolveDotPath(item,
|
|
5454
|
+
for (const { alias, path: path11 } of fields) {
|
|
5455
|
+
const val = resolveDotPath(item, path11);
|
|
5106
5456
|
row[alias] = val ?? null;
|
|
5107
5457
|
if (val != null)
|
|
5108
5458
|
hasValue = true;
|
|
@@ -5419,7 +5769,67 @@ async function cmdCleanupStale(flags) {
|
|
|
5419
5769
|
output(await withPendingNotice(api2("POST", "/v1/stale/cleanup", body), "Cleaning stale endpoints..."), !!flags.pretty);
|
|
5420
5770
|
}
|
|
5421
5771
|
async function cmdSearch(flags) {
|
|
5422
|
-
|
|
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
|
+
}
|
|
5423
5833
|
}
|
|
5424
5834
|
async function cmdSessions(flags) {
|
|
5425
5835
|
const domain = flags.domain;
|
|
@@ -5577,6 +5987,22 @@ async function cmdSetup(flags) {
|
|
|
5577
5987
|
}
|
|
5578
5988
|
async function cmdMode(_flags) {
|
|
5579
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
|
+
}
|
|
5580
6006
|
}
|
|
5581
6007
|
async function runPostSetupContributionPrompt() {
|
|
5582
6008
|
try {
|
|
@@ -5594,16 +6020,68 @@ async function cmdCapture(flags) {
|
|
|
5594
6020
|
const endpoints = Array.isArray(result.endpoints) ? result.endpoints : Array.isArray(result.available_endpoints) ? result.available_endpoints : [];
|
|
5595
6021
|
const skill = result.skill ?? null;
|
|
5596
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, "\\\"");
|
|
5597
6033
|
const envelope = {
|
|
5598
6034
|
skill_id: skillId,
|
|
5599
6035
|
endpoints_discovered: typeof result.endpoints_discovered === "number" ? result.endpoints_discovered : endpoints.length,
|
|
5600
6036
|
marketplace_published: !!result.marketplace_published,
|
|
5601
6037
|
ms: typeof result.ms === "number" ? result.ms : Date.now() - t0,
|
|
5602
|
-
next_step: endpoints.length
|
|
5603
|
-
...
|
|
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 } : {}
|
|
5604
6045
|
};
|
|
5605
6046
|
output(envelope, !!flags.pretty);
|
|
5606
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
|
+
}
|
|
5607
6085
|
var CLI_REFERENCE = {
|
|
5608
6086
|
commands: [
|
|
5609
6087
|
{ name: "health", usage: "", desc: "Server health check" },
|
|
@@ -5648,9 +6126,10 @@ var CLI_REFERENCE = {
|
|
|
5648
6126
|
{ name: "earnings", usage: "[--json]", desc: "Show your credit balance, earnings from indexing, and spending" },
|
|
5649
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" },
|
|
5650
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" },
|
|
5651
|
-
{ 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." },
|
|
5652
6130
|
{ 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`." }
|
|
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." }
|
|
5654
6133
|
],
|
|
5655
6134
|
globalFlags: [
|
|
5656
6135
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
@@ -6245,6 +6724,43 @@ async function cmdClose(flags) {
|
|
|
6245
6724
|
output(await api2("POST", "/v1/browse/close", typeof flags.session === "string" ? { session_id: flags.session } : undefined), false);
|
|
6246
6725
|
}
|
|
6247
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
|
+
}
|
|
6248
6764
|
if (getApiKey()) {
|
|
6249
6765
|
info("Already registered. API key loaded from env or ~/.unbrowse/config.json");
|
|
6250
6766
|
return;
|
|
@@ -6737,6 +7253,8 @@ async function main() {
|
|
|
6737
7253
|
return cmdMode(flags);
|
|
6738
7254
|
case "capture":
|
|
6739
7255
|
return cmdCapture(flags);
|
|
7256
|
+
case "note":
|
|
7257
|
+
return cmdNote(flags, args);
|
|
6740
7258
|
default:
|
|
6741
7259
|
info(`Unknown command: ${command}`);
|
|
6742
7260
|
printHelp();
|