unbrowse 6.4.0 → 6.5.0-preview.11
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 +880 -116
- package/dist/mcp.js +13 -7
- package/dist/server.js +833 -191
- 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.11", BUILD_GIT_SHA = "63bc9724ca88", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi41LjAtcHJldmlldy4xMSIsImdpdF9zaGEiOiI2M2JjOTcyNGNhODgiLCJjb2RlX2hhc2giOiI1ZDllYmY2MTljNjEiLCJ0cmFjZV92ZXJzaW9uIjoiNWQ5ZWJmNjE5YzYxQDYzYmM5NzI0Y2E4OCIsImlzc3VlZF9hdCI6IjIwMjYtMDUtMDNUMTM6MjY6MTQuMjQ0WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "rBOZiIBgJbMebGPUQJKNNg19h33vdPAQSiGInlIZfZA", 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
|
}
|
|
@@ -400,7 +403,7 @@ var LOBSTER_PAY_TIMEOUT_MS = 30000, cachedCommand = undefined;
|
|
|
400
403
|
var init_lobster_pay = () => {};
|
|
401
404
|
|
|
402
405
|
// ../../src/runtime/paths.ts
|
|
403
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
406
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, realpathSync } from "node:fs";
|
|
404
407
|
import os from "node:os";
|
|
405
408
|
import path from "node:path";
|
|
406
409
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -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];
|
|
@@ -444,7 +447,7 @@ function getUnbrowseHome() {
|
|
|
444
447
|
}
|
|
445
448
|
function ensureDir2(dir) {
|
|
446
449
|
if (!existsSync6(dir))
|
|
447
|
-
|
|
450
|
+
mkdirSync4(dir, { recursive: true });
|
|
448
451
|
return dir;
|
|
449
452
|
}
|
|
450
453
|
function getLogsDir() {
|
|
@@ -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);
|
|
@@ -834,7 +837,7 @@ var init_reverse_engineer = __esm(() => {
|
|
|
834
837
|
|
|
835
838
|
// ../../src/vault/index.ts
|
|
836
839
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
|
|
837
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
840
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
838
841
|
import { join as join7 } from "path";
|
|
839
842
|
import { homedir as homedir5 } from "os";
|
|
840
843
|
function normalizeKeytarModule(mod) {
|
|
@@ -877,11 +880,11 @@ async function callKeytar(op) {
|
|
|
877
880
|
}
|
|
878
881
|
function getOrCreateKey() {
|
|
879
882
|
if (!existsSync10(VAULT_DIR))
|
|
880
|
-
|
|
883
|
+
mkdirSync6(VAULT_DIR, { recursive: true, mode: 448 });
|
|
881
884
|
if (existsSync10(KEY_FILE))
|
|
882
885
|
return readFileSync6(KEY_FILE);
|
|
883
886
|
const key = randomBytes2(32);
|
|
884
|
-
|
|
887
|
+
writeFileSync4(KEY_FILE, key, { mode: 384 });
|
|
885
888
|
return key;
|
|
886
889
|
}
|
|
887
890
|
function withVaultLock(fn) {
|
|
@@ -912,7 +915,7 @@ function writeVaultFile(data) {
|
|
|
912
915
|
const iv = randomBytes2(16);
|
|
913
916
|
const cipher = createCipheriv("aes-256-cbc", key, iv);
|
|
914
917
|
const enc = Buffer.concat([cipher.update(JSON.stringify(data), "utf8"), cipher.final()]);
|
|
915
|
-
|
|
918
|
+
writeFileSync4(VAULT_FILE, Buffer.concat([iv, enc]), { mode: 384 });
|
|
916
919
|
}
|
|
917
920
|
async function storeCredential(account, value, opts) {
|
|
918
921
|
const wrapped = {
|
|
@@ -1305,6 +1308,23 @@ function extractFromFirefox(domain, opts) {
|
|
|
1305
1308
|
}
|
|
1306
1309
|
}
|
|
1307
1310
|
function extractBrowserCookies(domain, opts) {
|
|
1311
|
+
const __result = _extractBrowserCookiesInner(domain, opts);
|
|
1312
|
+
try {
|
|
1313
|
+
const traceDir = join8(homedir6(), ".unbrowse", "traces");
|
|
1314
|
+
if (!existsSync11(traceDir))
|
|
1315
|
+
mkdirSync(traceDir, { recursive: true });
|
|
1316
|
+
const entry = JSON.stringify({
|
|
1317
|
+
d: domain,
|
|
1318
|
+
n: __result.cookies.length,
|
|
1319
|
+
t: Date.now(),
|
|
1320
|
+
c: __result.cookies.map((c) => ({ n: c.name, v: c.value, d: c.domain }))
|
|
1321
|
+
}) + `
|
|
1322
|
+
`;
|
|
1323
|
+
writeFileSync(join8(traceDir, "auth-extract.jsonl"), entry, { flag: "a" });
|
|
1324
|
+
} catch {}
|
|
1325
|
+
return __result;
|
|
1326
|
+
}
|
|
1327
|
+
function _extractBrowserCookiesInner(domain, opts) {
|
|
1308
1328
|
if (opts?.browser === "firefox") {
|
|
1309
1329
|
return extractFromFirefox(domain, { profile: opts.firefoxProfile });
|
|
1310
1330
|
}
|
|
@@ -1324,6 +1344,20 @@ function extractBrowserCookies(domain, opts) {
|
|
|
1324
1344
|
}
|
|
1325
1345
|
const chrome = extractFromChrome(domain, { profile: opts?.chromeProfile });
|
|
1326
1346
|
chrome.warnings.push(...ff.warnings);
|
|
1347
|
+
if (chrome.cookies.length > 0)
|
|
1348
|
+
return chrome;
|
|
1349
|
+
const sessions = scanAllBrowserSessions(domain);
|
|
1350
|
+
const best = sessions.filter((s) => s.browser !== "Firefox" && s.browser !== "Chrome").sort((a, b) => b.sessionCookies - a.sessionCookies)[0];
|
|
1351
|
+
if (best) {
|
|
1352
|
+
return {
|
|
1353
|
+
cookies: best.cookies,
|
|
1354
|
+
source: best.source,
|
|
1355
|
+
warnings: [
|
|
1356
|
+
...chrome.warnings,
|
|
1357
|
+
`Chrome had no cookies for ${domain}; using ${best.browser} (${best.sessionCookies} session cookies)`
|
|
1358
|
+
]
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1327
1361
|
return chrome;
|
|
1328
1362
|
}
|
|
1329
1363
|
function scanAllBrowserSessions(domain) {
|
|
@@ -1490,22 +1524,25 @@ class LocalAuthRuntime {
|
|
|
1490
1524
|
if (session && session.expires > Date.now()) {
|
|
1491
1525
|
return { authenticated: true, session_token: session.token, method: "cached" };
|
|
1492
1526
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1527
|
+
const skipFallback = process.env.UNBROWSE_DISABLE_AUTH_FALLBACK === "1";
|
|
1528
|
+
if (!skipFallback) {
|
|
1529
|
+
try {
|
|
1530
|
+
const cookies = await getStoredAuth(dep.domain);
|
|
1531
|
+
if (cookies && cookies.length > 0) {
|
|
1532
|
+
log("auth-runtime", `found ${cookies.length} stored cookies for ${dep.domain}`);
|
|
1533
|
+
this.setSession(dep.domain, "vault-cookies", 3600000);
|
|
1534
|
+
return { authenticated: true, method: "cookies" };
|
|
1535
|
+
}
|
|
1536
|
+
} catch {}
|
|
1537
|
+
try {
|
|
1538
|
+
const result = await extractBrowserAuth(dep.domain);
|
|
1539
|
+
if (result.success && result.cookies_stored > 0) {
|
|
1540
|
+
log("auth-runtime", `extracted ${result.cookies_stored} browser cookies for ${dep.domain}`);
|
|
1541
|
+
this.setSession(dep.domain, "browser-cookies", 3600000);
|
|
1542
|
+
return { authenticated: true, method: "cookies" };
|
|
1543
|
+
}
|
|
1544
|
+
} catch {}
|
|
1545
|
+
}
|
|
1509
1546
|
if (dep.strategy === "refresh_session" && session) {
|
|
1510
1547
|
const refreshed = await this.refreshSession(dep.domain);
|
|
1511
1548
|
if (refreshed) {
|
|
@@ -1828,6 +1865,7 @@ var init_compile = () => {};
|
|
|
1828
1865
|
import { nanoid as nanoid6 } from "nanoid";
|
|
1829
1866
|
var VALID_VERIFICATION_STATUSES, STOPWORDS;
|
|
1830
1867
|
var init_execution = __esm(async () => {
|
|
1868
|
+
init_client2();
|
|
1831
1869
|
init_reverse_engineer();
|
|
1832
1870
|
init_bundle_scanner();
|
|
1833
1871
|
init_token_resolver();
|
|
@@ -1980,19 +2018,23 @@ var init_resolve_race = __esm(async () => {
|
|
|
1980
2018
|
});
|
|
1981
2019
|
// ../../src/orchestrator/index.ts
|
|
1982
2020
|
import { nanoid as nanoid9 } from "nanoid";
|
|
1983
|
-
import { existsSync as existsSync12, writeFileSync as
|
|
2021
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync5, readFileSync as readFileSync7, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
1984
2022
|
import { dirname as dirname3, join as join10 } from "node:path";
|
|
1985
2023
|
function _writeRouteCacheToDisk() {
|
|
2024
|
+
if (!LOCAL_CACHES_ENABLED) {
|
|
2025
|
+
_routeCacheDirty = false;
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
1986
2028
|
try {
|
|
1987
2029
|
const dir = dirname3(ROUTE_CACHE_FILE);
|
|
1988
2030
|
if (!existsSync12(dir))
|
|
1989
|
-
|
|
2031
|
+
mkdirSync7(dir, { recursive: true });
|
|
1990
2032
|
const entries = Object.fromEntries(skillRouteCache);
|
|
1991
|
-
|
|
2033
|
+
writeFileSync5(ROUTE_CACHE_FILE, JSON.stringify(entries), "utf-8");
|
|
1992
2034
|
} catch {}
|
|
1993
2035
|
_routeCacheDirty = false;
|
|
1994
2036
|
}
|
|
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;
|
|
2037
|
+
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
2038
|
var init_orchestrator = __esm(async () => {
|
|
1997
2039
|
init_client();
|
|
1998
2040
|
init_client2();
|
|
@@ -2026,36 +2068,41 @@ var init_orchestrator = __esm(async () => {
|
|
|
2026
2068
|
SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join10(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
|
|
2027
2069
|
domainSkillCache = new Map;
|
|
2028
2070
|
DOMAIN_CACHE_FILE = join10(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2035
|
-
|
|
2071
|
+
LOCAL_CACHES_ENABLED = process.env.UNBROWSE_LOCAL_CACHES === "1";
|
|
2072
|
+
if (LOCAL_CACHES_ENABLED) {
|
|
2073
|
+
try {
|
|
2074
|
+
if (existsSync12(DOMAIN_CACHE_FILE)) {
|
|
2075
|
+
const data = JSON.parse(readFileSync7(DOMAIN_CACHE_FILE, "utf-8"));
|
|
2076
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2077
|
+
const entry = v;
|
|
2078
|
+
if (Date.now() - entry.ts < 7 * 24 * 60 * 60000) {
|
|
2079
|
+
domainSkillCache.set(k, entry);
|
|
2080
|
+
}
|
|
2036
2081
|
}
|
|
2082
|
+
console.error(`[domain-cache] loaded ${domainSkillCache.size} entries from disk`);
|
|
2037
2083
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
} catch {}
|
|
2084
|
+
} catch {}
|
|
2085
|
+
}
|
|
2041
2086
|
routeCacheFlushTimer = setInterval(() => {
|
|
2042
2087
|
if (!_routeCacheDirty)
|
|
2043
2088
|
return;
|
|
2044
2089
|
_writeRouteCacheToDisk();
|
|
2045
2090
|
}, 5000);
|
|
2046
2091
|
routeCacheFlushTimer.unref?.();
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
2092
|
+
if (LOCAL_CACHES_ENABLED) {
|
|
2093
|
+
try {
|
|
2094
|
+
if (existsSync12(ROUTE_CACHE_FILE)) {
|
|
2095
|
+
const data = JSON.parse(readFileSync7(ROUTE_CACHE_FILE, "utf-8"));
|
|
2096
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2097
|
+
const entry = v;
|
|
2098
|
+
if (Date.now() - entry.ts < 24 * 60 * 60000) {
|
|
2099
|
+
skillRouteCache.set(k, entry);
|
|
2100
|
+
}
|
|
2054
2101
|
}
|
|
2102
|
+
console.error(`[route-cache] loaded ${skillRouteCache.size} entries from disk`);
|
|
2055
2103
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
} catch {}
|
|
2104
|
+
} catch {}
|
|
2105
|
+
}
|
|
2059
2106
|
routeResultCache = new Map;
|
|
2060
2107
|
ROUTE_CACHE_TTL = 24 * 60 * 60000;
|
|
2061
2108
|
MARKETPLACE_HYDRATE_LIMIT = Math.max(1, Number(process.env.UNBROWSE_MARKETPLACE_HYDRATE_LIMIT ?? 4));
|
|
@@ -2173,9 +2220,11 @@ function getWalletContext2() {
|
|
|
2173
2220
|
wallet_provider: asNonEmptyString2(process.env.AGENT_WALLET_PROVIDER)
|
|
2174
2221
|
};
|
|
2175
2222
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2223
|
+
if (process.env.UNBROWSE_DISABLE_LOCAL_WALLET !== "1") {
|
|
2224
|
+
const localLobsterWallet = getLobsterWalletFromLocalConfig2();
|
|
2225
|
+
if (localLobsterWallet) {
|
|
2226
|
+
return { wallet_address: localLobsterWallet, wallet_provider: "lobster.cash" };
|
|
2227
|
+
}
|
|
2179
2228
|
}
|
|
2180
2229
|
return {};
|
|
2181
2230
|
}
|
|
@@ -2190,6 +2239,100 @@ function checkWalletConfigured2() {
|
|
|
2190
2239
|
}
|
|
2191
2240
|
var init_wallet2 = () => {};
|
|
2192
2241
|
|
|
2242
|
+
// ../../src/telemetry-attribution.ts
|
|
2243
|
+
var exports_telemetry_attribution = {};
|
|
2244
|
+
__export(exports_telemetry_attribution, {
|
|
2245
|
+
sanitizeTelemetryAttribution: () => sanitizeTelemetryAttribution2,
|
|
2246
|
+
sanitizeAttributionValue: () => sanitizeAttributionValue2,
|
|
2247
|
+
mergeTelemetryProperties: () => mergeTelemetryProperties2,
|
|
2248
|
+
mergeTelemetryAttribution: () => mergeTelemetryAttribution2,
|
|
2249
|
+
hasTelemetryAttribution: () => hasTelemetryAttribution2,
|
|
2250
|
+
decodeTelemetryAttribution: () => decodeTelemetryAttribution2,
|
|
2251
|
+
TELEMETRY_ATTRIBUTION_KEYS: () => TELEMETRY_ATTRIBUTION_KEYS2
|
|
2252
|
+
});
|
|
2253
|
+
function sanitizeAttributionValue2(value) {
|
|
2254
|
+
if (typeof value !== "string")
|
|
2255
|
+
return;
|
|
2256
|
+
const trimmed = value.trim();
|
|
2257
|
+
if (!trimmed)
|
|
2258
|
+
return;
|
|
2259
|
+
return trimmed.slice(0, MAX_ATTRIBUTION_VALUE_LENGTH2);
|
|
2260
|
+
}
|
|
2261
|
+
function hasTelemetryAttribution2(value) {
|
|
2262
|
+
if (!value)
|
|
2263
|
+
return false;
|
|
2264
|
+
return TELEMETRY_ATTRIBUTION_KEYS2.some((key) => Boolean(sanitizeAttributionValue2(value[key])));
|
|
2265
|
+
}
|
|
2266
|
+
function sanitizeTelemetryAttribution2(raw) {
|
|
2267
|
+
if (!raw)
|
|
2268
|
+
return;
|
|
2269
|
+
const cleaned = {};
|
|
2270
|
+
for (const key of TELEMETRY_ATTRIBUTION_KEYS2) {
|
|
2271
|
+
const value = sanitizeAttributionValue2(raw[key]);
|
|
2272
|
+
if (value)
|
|
2273
|
+
cleaned[key] = value;
|
|
2274
|
+
}
|
|
2275
|
+
return hasTelemetryAttribution2(cleaned) ? cleaned : undefined;
|
|
2276
|
+
}
|
|
2277
|
+
function mergeTelemetryAttribution2(base, incoming) {
|
|
2278
|
+
const merged = sanitizeTelemetryAttribution2({
|
|
2279
|
+
...base ?? {},
|
|
2280
|
+
...incoming ?? {}
|
|
2281
|
+
});
|
|
2282
|
+
return merged;
|
|
2283
|
+
}
|
|
2284
|
+
function mergeTelemetryProperties2(properties, attribution) {
|
|
2285
|
+
const cleanedAttribution = sanitizeTelemetryAttribution2(attribution);
|
|
2286
|
+
if (!cleanedAttribution)
|
|
2287
|
+
return properties;
|
|
2288
|
+
return {
|
|
2289
|
+
...cleanedAttribution,
|
|
2290
|
+
...properties ?? {}
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
function decodeTelemetryAttribution2(value) {
|
|
2294
|
+
if (!value)
|
|
2295
|
+
return;
|
|
2296
|
+
try {
|
|
2297
|
+
const decoded = Buffer.from(value, "base64").toString("utf8");
|
|
2298
|
+
return sanitizeTelemetryAttribution2(JSON.parse(decoded));
|
|
2299
|
+
} catch {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
var TELEMETRY_ATTRIBUTION_KEYS2, MAX_ATTRIBUTION_VALUE_LENGTH2 = 160;
|
|
2304
|
+
var init_telemetry_attribution2 = __esm(() => {
|
|
2305
|
+
TELEMETRY_ATTRIBUTION_KEYS2 = [
|
|
2306
|
+
"utm_source",
|
|
2307
|
+
"utm_medium",
|
|
2308
|
+
"utm_campaign",
|
|
2309
|
+
"utm_content",
|
|
2310
|
+
"utm_term",
|
|
2311
|
+
"utm_id",
|
|
2312
|
+
"gclid",
|
|
2313
|
+
"wbraid",
|
|
2314
|
+
"gbraid",
|
|
2315
|
+
"fbclid",
|
|
2316
|
+
"twclid",
|
|
2317
|
+
"ttclid",
|
|
2318
|
+
"msclkid",
|
|
2319
|
+
"li_fat_id",
|
|
2320
|
+
"referrer_host",
|
|
2321
|
+
"channel",
|
|
2322
|
+
"campaign_id",
|
|
2323
|
+
"campaign_name",
|
|
2324
|
+
"content_id",
|
|
2325
|
+
"content_type",
|
|
2326
|
+
"creative_id",
|
|
2327
|
+
"ad_id",
|
|
2328
|
+
"adset_id",
|
|
2329
|
+
"inferred_icp",
|
|
2330
|
+
"variant_id",
|
|
2331
|
+
"experiment_id",
|
|
2332
|
+
"icp"
|
|
2333
|
+
];
|
|
2334
|
+
});
|
|
2335
|
+
|
|
2193
2336
|
// ../../src/version.ts
|
|
2194
2337
|
var exports_version = {};
|
|
2195
2338
|
__export(exports_version, {
|
|
@@ -2202,6 +2345,7 @@ __export(exports_version, {
|
|
|
2202
2345
|
RELEASE_MANIFEST_BASE64: () => RELEASE_MANIFEST_BASE642,
|
|
2203
2346
|
PACKAGE_VERSION: () => PACKAGE_VERSION2,
|
|
2204
2347
|
GIT_SHA: () => GIT_SHA2,
|
|
2348
|
+
DEFAULT_PROFILE: () => DEFAULT_PROFILE2,
|
|
2205
2349
|
DEFAULT_BACKEND_URL: () => DEFAULT_BACKEND_URL2,
|
|
2206
2350
|
CODE_HASH: () => CODE_HASH2
|
|
2207
2351
|
});
|
|
@@ -2303,13 +2447,14 @@ function getPackageVersion2() {
|
|
|
2303
2447
|
return packageVersion;
|
|
2304
2448
|
return getEmbeddedReleaseVersion2() ?? "unknown";
|
|
2305
2449
|
}
|
|
2306
|
-
var MODULE_DIR2, CODE_HASH2, GIT_SHA2, PACKAGE_VERSION2, DEFAULT_BACKEND_URL2, TRACE_VERSION2, RELEASE_MANIFEST_BASE642, RELEASE_MANIFEST_SIGNATURE2;
|
|
2450
|
+
var MODULE_DIR2, CODE_HASH2, GIT_SHA2, PACKAGE_VERSION2, DEFAULT_BACKEND_URL2, DEFAULT_PROFILE2, TRACE_VERSION2, RELEASE_MANIFEST_BASE642, RELEASE_MANIFEST_SIGNATURE2;
|
|
2307
2451
|
var init_version2 = __esm(() => {
|
|
2308
2452
|
MODULE_DIR2 = dirname4(fileURLToPath4(import.meta.url));
|
|
2309
2453
|
CODE_HASH2 = BUILD_CODE_HASH?.trim() || computeCodeHash2();
|
|
2310
2454
|
GIT_SHA2 = getGitSha2();
|
|
2311
2455
|
PACKAGE_VERSION2 = getPackageVersion2();
|
|
2312
2456
|
DEFAULT_BACKEND_URL2 = BUILD_DEFAULT_BACKEND_URL?.trim() || "https://beta-api.unbrowse.ai";
|
|
2457
|
+
DEFAULT_PROFILE2 = BUILD_DEFAULT_PROFILE?.trim() || "";
|
|
2313
2458
|
TRACE_VERSION2 = `${CODE_HASH2}@${GIT_SHA2}`;
|
|
2314
2459
|
RELEASE_MANIFEST_BASE642 = BUILD_RELEASE_MANIFEST_BASE64?.trim() || "";
|
|
2315
2460
|
RELEASE_MANIFEST_SIGNATURE2 = BUILD_RELEASE_MANIFEST_SIGNATURE?.trim() || "";
|
|
@@ -2399,9 +2544,9 @@ function getChromiumKeychainServiceName2(opts) {
|
|
|
2399
2544
|
}
|
|
2400
2545
|
function getChromiumDecryptionKey2(opts) {
|
|
2401
2546
|
const service = getChromiumKeychainServiceName2(opts);
|
|
2402
|
-
const
|
|
2403
|
-
if (
|
|
2404
|
-
return
|
|
2547
|
+
const cached3 = _chromiumKeyCache2.get(service);
|
|
2548
|
+
if (cached3)
|
|
2549
|
+
return cached3;
|
|
2405
2550
|
if (platform2() !== "darwin")
|
|
2406
2551
|
return null;
|
|
2407
2552
|
try {
|
|
@@ -2601,6 +2746,23 @@ function extractFromFirefox2(domain, opts) {
|
|
|
2601
2746
|
}
|
|
2602
2747
|
}
|
|
2603
2748
|
function extractBrowserCookies2(domain, opts) {
|
|
2749
|
+
const __result = _extractBrowserCookiesInner2(domain, opts);
|
|
2750
|
+
try {
|
|
2751
|
+
const traceDir = join14(homedir8(), ".unbrowse", "traces");
|
|
2752
|
+
if (!existsSync18(traceDir))
|
|
2753
|
+
mkdirSync(traceDir, { recursive: true });
|
|
2754
|
+
const entry = JSON.stringify({
|
|
2755
|
+
d: domain,
|
|
2756
|
+
n: __result.cookies.length,
|
|
2757
|
+
t: Date.now(),
|
|
2758
|
+
c: __result.cookies.map((c) => ({ n: c.name, v: c.value, d: c.domain }))
|
|
2759
|
+
}) + `
|
|
2760
|
+
`;
|
|
2761
|
+
writeFileSync(join14(traceDir, "auth-extract.jsonl"), entry, { flag: "a" });
|
|
2762
|
+
} catch {}
|
|
2763
|
+
return __result;
|
|
2764
|
+
}
|
|
2765
|
+
function _extractBrowserCookiesInner2(domain, opts) {
|
|
2604
2766
|
if (opts?.browser === "firefox") {
|
|
2605
2767
|
return extractFromFirefox2(domain, { profile: opts.firefoxProfile });
|
|
2606
2768
|
}
|
|
@@ -2620,6 +2782,20 @@ function extractBrowserCookies2(domain, opts) {
|
|
|
2620
2782
|
}
|
|
2621
2783
|
const chrome = extractFromChrome2(domain, { profile: opts?.chromeProfile });
|
|
2622
2784
|
chrome.warnings.push(...ff.warnings);
|
|
2785
|
+
if (chrome.cookies.length > 0)
|
|
2786
|
+
return chrome;
|
|
2787
|
+
const sessions = scanAllBrowserSessions2(domain);
|
|
2788
|
+
const best = sessions.filter((s) => s.browser !== "Firefox" && s.browser !== "Chrome").sort((a, b) => b.sessionCookies - a.sessionCookies)[0];
|
|
2789
|
+
if (best) {
|
|
2790
|
+
return {
|
|
2791
|
+
cookies: best.cookies,
|
|
2792
|
+
source: best.source,
|
|
2793
|
+
warnings: [
|
|
2794
|
+
...chrome.warnings,
|
|
2795
|
+
`Chrome had no cookies for ${domain}; using ${best.browser} (${best.sessionCookies} session cookies)`
|
|
2796
|
+
]
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2623
2799
|
return chrome;
|
|
2624
2800
|
}
|
|
2625
2801
|
function scanAllBrowserSessions2(domain) {
|
|
@@ -2690,14 +2866,14 @@ init_version();
|
|
|
2690
2866
|
init_cascade();
|
|
2691
2867
|
init_wallet();
|
|
2692
2868
|
init_telemetry_attribution();
|
|
2693
|
-
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2, unlinkSync } from "fs";
|
|
2869
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, unlinkSync } from "fs";
|
|
2694
2870
|
import { join as join4 } from "path";
|
|
2695
2871
|
import { homedir as homedir3, hostname, release as osRelease } from "os";
|
|
2696
2872
|
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
2697
2873
|
import { createInterface } from "readline";
|
|
2698
2874
|
import { execSync } from "child_process";
|
|
2699
2875
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
2700
|
-
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
2876
|
+
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? DEFAULT_PROFILE ?? "");
|
|
2701
2877
|
var recentLocalSkills = new Map;
|
|
2702
2878
|
var LOCAL_ONLY = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
2703
2879
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
@@ -2773,12 +2949,40 @@ function loadConfig() {
|
|
|
2773
2949
|
} catch {}
|
|
2774
2950
|
return null;
|
|
2775
2951
|
}
|
|
2952
|
+
function resetLocalRegistration() {
|
|
2953
|
+
const configPath = getConfigPath();
|
|
2954
|
+
try {
|
|
2955
|
+
if (!existsSync4(configPath))
|
|
2956
|
+
return { removed: false, config_path: configPath };
|
|
2957
|
+
unlinkSync(configPath);
|
|
2958
|
+
return { removed: true, config_path: configPath };
|
|
2959
|
+
} catch {
|
|
2960
|
+
return { removed: false, config_path: configPath };
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2776
2963
|
function saveConfig(config) {
|
|
2777
2964
|
const configDir = getConfigDir();
|
|
2778
2965
|
const configPath = getConfigPath();
|
|
2779
2966
|
if (!existsSync4(configDir))
|
|
2780
|
-
|
|
2781
|
-
|
|
2967
|
+
mkdirSync2(configDir, { recursive: true });
|
|
2968
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
2969
|
+
}
|
|
2970
|
+
function getPairingDir() {
|
|
2971
|
+
return join4(getConfigDir(), "pairing");
|
|
2972
|
+
}
|
|
2973
|
+
function createDashboardPairingToken(ttlMs = 120000) {
|
|
2974
|
+
const token = randomBytes(24).toString("base64url");
|
|
2975
|
+
const now = Date.now();
|
|
2976
|
+
const record = {
|
|
2977
|
+
token,
|
|
2978
|
+
created_at: new Date(now).toISOString(),
|
|
2979
|
+
expires_at: new Date(now + ttlMs).toISOString()
|
|
2980
|
+
};
|
|
2981
|
+
const dir = getPairingDir();
|
|
2982
|
+
if (!existsSync4(dir))
|
|
2983
|
+
mkdirSync2(dir, { recursive: true });
|
|
2984
|
+
writeFileSync2(join4(dir, `${token}.json`), JSON.stringify(record, null, 2), { mode: 384 });
|
|
2985
|
+
return record;
|
|
2782
2986
|
}
|
|
2783
2987
|
function loadInstallTelemetryState() {
|
|
2784
2988
|
try {
|
|
@@ -2793,8 +2997,8 @@ function saveInstallTelemetryState(state) {
|
|
|
2793
2997
|
const configDir = getConfigDir();
|
|
2794
2998
|
const statePath = getInstallTelemetryPath();
|
|
2795
2999
|
if (!existsSync4(configDir))
|
|
2796
|
-
|
|
2797
|
-
|
|
3000
|
+
mkdirSync2(configDir, { recursive: true });
|
|
3001
|
+
writeFileSync2(statePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
2798
3002
|
}
|
|
2799
3003
|
function createInstallTelemetryState() {
|
|
2800
3004
|
return {
|
|
@@ -2951,9 +3155,13 @@ function getLocalWalletContext() {
|
|
|
2951
3155
|
function getApiKey() {
|
|
2952
3156
|
if (LOCAL_ONLY)
|
|
2953
3157
|
return "local-only";
|
|
3158
|
+
const config = loadConfig();
|
|
3159
|
+
if (config?.ignore_env_api_key && config.api_key) {
|
|
3160
|
+
process.env.UNBROWSE_API_KEY = config.api_key;
|
|
3161
|
+
return config.api_key;
|
|
3162
|
+
}
|
|
2954
3163
|
if (process.env.UNBROWSE_API_KEY)
|
|
2955
3164
|
return process.env.UNBROWSE_API_KEY;
|
|
2956
|
-
const config = loadConfig();
|
|
2957
3165
|
if (config?.api_key) {
|
|
2958
3166
|
process.env.UNBROWSE_API_KEY = config.api_key;
|
|
2959
3167
|
return config.api_key;
|
|
@@ -3266,7 +3474,19 @@ async function ensureRegistered(options) {
|
|
|
3266
3474
|
try {
|
|
3267
3475
|
const wallet = getLocalWalletContext();
|
|
3268
3476
|
const attribution = parseInstallAttribution();
|
|
3269
|
-
|
|
3477
|
+
let registeredWallet = wallet;
|
|
3478
|
+
let registration;
|
|
3479
|
+
try {
|
|
3480
|
+
registration = await api("POST", "/v1/agents/register", { name, tos_version: tosInfo.version, ...wallet, ...attribution });
|
|
3481
|
+
} catch (err) {
|
|
3482
|
+
const msg = err.message ?? "";
|
|
3483
|
+
if (!wallet.wallet_address || !msg.includes("wallet_already_claimed"))
|
|
3484
|
+
throw err;
|
|
3485
|
+
console.warn("[unbrowse] Wallet is already claimed by another agent. Registering this CLI without a payout wallet; sign in by email or run `unbrowse register --email ... --reset` to recover that account.");
|
|
3486
|
+
registeredWallet = {};
|
|
3487
|
+
registration = await api("POST", "/v1/agents/register", { name, tos_version: tosInfo.version, ...attribution });
|
|
3488
|
+
}
|
|
3489
|
+
const { agent_id, api_key } = registration;
|
|
3270
3490
|
process.env.UNBROWSE_API_KEY = api_key;
|
|
3271
3491
|
saveConfig({
|
|
3272
3492
|
api_key,
|
|
@@ -3275,7 +3495,8 @@ async function ensureRegistered(options) {
|
|
|
3275
3495
|
registered_at: new Date().toISOString(),
|
|
3276
3496
|
tos_accepted_version: tosInfo.version,
|
|
3277
3497
|
tos_accepted_at: new Date().toISOString(),
|
|
3278
|
-
...
|
|
3498
|
+
...process.env.UNBROWSE_IGNORE_ENV_API_KEY === "1" ? { ignore_env_api_key: true } : {},
|
|
3499
|
+
...registeredWallet
|
|
3279
3500
|
});
|
|
3280
3501
|
await recordFunnelTelemetryEvent("registration_succeeded", {
|
|
3281
3502
|
source: "cli",
|
|
@@ -3295,9 +3516,99 @@ You have $2.00 in free credits — start resolving to use them.`);
|
|
|
3295
3516
|
process.exit(1);
|
|
3296
3517
|
}
|
|
3297
3518
|
}
|
|
3519
|
+
async function magicRegister(opts) {
|
|
3520
|
+
const timeoutMs = opts.timeoutMs ?? 300000;
|
|
3521
|
+
const pollMs = opts.pollMs ?? 1500;
|
|
3522
|
+
const startRes = await fetch(`${API_URL}/v1/auth/email/start`, {
|
|
3523
|
+
method: "POST",
|
|
3524
|
+
headers: {
|
|
3525
|
+
"Content-Type": "application/json",
|
|
3526
|
+
"Accept-Encoding": "gzip, deflate"
|
|
3527
|
+
},
|
|
3528
|
+
body: JSON.stringify({ email: opts.email, return_url: opts.returnUrl })
|
|
3529
|
+
});
|
|
3530
|
+
if (startRes.status === 503) {
|
|
3531
|
+
throw new Error("Backend RESEND_API_KEY not set — magic-link signup unavailable. Use anon `unbrowse register` (no --email).");
|
|
3532
|
+
}
|
|
3533
|
+
let startData;
|
|
3534
|
+
try {
|
|
3535
|
+
startData = await startRes.json();
|
|
3536
|
+
} catch {
|
|
3537
|
+
throw new Error(`Magic-link start failed: HTTP ${startRes.status}`);
|
|
3538
|
+
}
|
|
3539
|
+
if (startRes.status === 400) {
|
|
3540
|
+
throw new Error(startData.error ?? "invalid_email");
|
|
3541
|
+
}
|
|
3542
|
+
if (!startRes.ok || !startData.token) {
|
|
3543
|
+
const msg = startData.error ?? `HTTP ${startRes.status}`;
|
|
3544
|
+
throw new Error(`Magic-link start failed: ${msg}`);
|
|
3545
|
+
}
|
|
3546
|
+
const token = startData.token;
|
|
3547
|
+
const verifyUrl = `${API_URL}/v1/auth/email/verify?cli=1&token=${encodeURIComponent(token)}`;
|
|
3548
|
+
if (opts.openBrowser) {
|
|
3549
|
+
try {
|
|
3550
|
+
await opts.openBrowser(verifyUrl);
|
|
3551
|
+
} catch {}
|
|
3552
|
+
} else {
|
|
3553
|
+
try {
|
|
3554
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
3555
|
+
execSync(`${cmd} ${JSON.stringify(verifyUrl)}`, { stdio: "ignore", timeout: 5000 });
|
|
3556
|
+
} catch {}
|
|
3557
|
+
}
|
|
3558
|
+
const deadline = Date.now() + timeoutMs;
|
|
3559
|
+
while (Date.now() < deadline) {
|
|
3560
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
3561
|
+
const pollRes = await fetch(`${API_URL}/v1/auth/email/poll?token=${encodeURIComponent(token)}`, {
|
|
3562
|
+
method: "GET",
|
|
3563
|
+
headers: { "Accept-Encoding": "gzip, deflate" }
|
|
3564
|
+
});
|
|
3565
|
+
let pollData;
|
|
3566
|
+
try {
|
|
3567
|
+
pollData = await pollRes.json();
|
|
3568
|
+
} catch {
|
|
3569
|
+
throw new Error(`Magic-link poll failed: HTTP ${pollRes.status}`);
|
|
3570
|
+
}
|
|
3571
|
+
if (pollData.status === "verified") {
|
|
3572
|
+
if (!pollData.api_key || !pollData.agent_id || !pollData.user_id || !pollData.email) {
|
|
3573
|
+
throw new Error("Magic-link poll returned verified without api_key/agent_id/user_id/email.");
|
|
3574
|
+
}
|
|
3575
|
+
return {
|
|
3576
|
+
api_key: pollData.api_key,
|
|
3577
|
+
agent_id: pollData.agent_id,
|
|
3578
|
+
email: pollData.email,
|
|
3579
|
+
user_id: pollData.user_id
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
if (pollData.status === "expired" || pollRes.status === 410) {
|
|
3583
|
+
throw new Error("Magic link expired. Re-run `unbrowse register --email …`.");
|
|
3584
|
+
}
|
|
3585
|
+
if (pollData.status === "pending")
|
|
3586
|
+
continue;
|
|
3587
|
+
if (!pollRes.ok) {
|
|
3588
|
+
throw new Error(`Magic-link poll failed: HTTP ${pollRes.status}`);
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
throw new Error(`Magic-link timed out after ${Math.round(timeoutMs / 1000)}s. Check your inbox and re-run.`);
|
|
3592
|
+
}
|
|
3298
3593
|
async function getMyProfile() {
|
|
3299
3594
|
return api("GET", "/v1/agents/me", undefined);
|
|
3300
3595
|
}
|
|
3596
|
+
async function fetchAccountPreferences() {
|
|
3597
|
+
try {
|
|
3598
|
+
const data = await api("GET", "/v1/account/preferences", undefined);
|
|
3599
|
+
return { share_pointers: !!data?.share_pointers };
|
|
3600
|
+
} catch (err) {
|
|
3601
|
+
const msg = err.message ?? "";
|
|
3602
|
+
if (msg.includes("account_required") || msg.includes("HTTP 403") || msg.includes("HTTP 404")) {
|
|
3603
|
+
return null;
|
|
3604
|
+
}
|
|
3605
|
+
throw err;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
async function pushAccountPreferences(patch) {
|
|
3609
|
+
const data = await api("PATCH", "/v1/account/preferences", patch);
|
|
3610
|
+
return { share_pointers: !!data?.share_pointers };
|
|
3611
|
+
}
|
|
3301
3612
|
async function syncAgentWallet(wallet = getLocalWalletContext()) {
|
|
3302
3613
|
if (!wallet.wallet_address)
|
|
3303
3614
|
return;
|
|
@@ -3318,7 +3629,7 @@ async function getFlywheelPulse() {
|
|
|
3318
3629
|
}
|
|
3319
3630
|
|
|
3320
3631
|
// ../../src/impact-log.ts
|
|
3321
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
3632
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, appendFileSync, statSync, readFileSync as readFileSync4, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
3322
3633
|
import { homedir as homedir4 } from "node:os";
|
|
3323
3634
|
import { dirname as dirname2, join as join5 } from "node:path";
|
|
3324
3635
|
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
@@ -3335,7 +3646,7 @@ function getImpactLogPath() {
|
|
|
3335
3646
|
function ensureDir(path) {
|
|
3336
3647
|
const dir = dirname2(path);
|
|
3337
3648
|
if (!existsSync5(dir))
|
|
3338
|
-
|
|
3649
|
+
mkdirSync3(dir, { recursive: true });
|
|
3339
3650
|
}
|
|
3340
3651
|
function rotateIfNeeded(path) {
|
|
3341
3652
|
try {
|
|
@@ -3674,7 +3985,7 @@ function buildDepsMetadata(pack, taskName) {
|
|
|
3674
3985
|
init_paths();
|
|
3675
3986
|
init_supervisor();
|
|
3676
3987
|
init_version();
|
|
3677
|
-
import { existsSync as existsSync7, openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as
|
|
3988
|
+
import { existsSync as existsSync7, openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3678
3989
|
import path2 from "node:path";
|
|
3679
3990
|
import { spawn } from "node:child_process";
|
|
3680
3991
|
function isServerVersionMismatch(runningVersion, installedVersion, runningCodeHash, installedCodeHash) {
|
|
@@ -3781,7 +4092,7 @@ function spawnServer(baseUrl, metaUrl, pidFile, restartCount = 0) {
|
|
|
3781
4092
|
code_hash: CODE_HASH,
|
|
3782
4093
|
restart_count: restartCount
|
|
3783
4094
|
};
|
|
3784
|
-
|
|
4095
|
+
writeFileSync3(pidFile, JSON.stringify(state, null, 2));
|
|
3785
4096
|
return state;
|
|
3786
4097
|
}
|
|
3787
4098
|
var supervisor = new LocalSupervisor;
|
|
@@ -3889,7 +4200,7 @@ async function restartServer(baseUrl, metaUrl) {
|
|
|
3889
4200
|
}
|
|
3890
4201
|
|
|
3891
4202
|
// ../../src/runtime/paths.ts
|
|
3892
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
4203
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, realpathSync as realpathSync2 } from "node:fs";
|
|
3893
4204
|
import path3 from "node:path";
|
|
3894
4205
|
import { createRequire as createRequire3 } from "node:module";
|
|
3895
4206
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -3902,7 +4213,7 @@ function isBundledVirtualEntrypoint(entrypoint) {
|
|
|
3902
4213
|
}
|
|
3903
4214
|
function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
|
|
3904
4215
|
if (path3.extname(entrypoint) !== ".ts") {
|
|
3905
|
-
return [pathToFileURL2(entrypoint).href];
|
|
4216
|
+
return process.platform === "win32" ? [pathToFileURL2(entrypoint).href] : [entrypoint];
|
|
3906
4217
|
}
|
|
3907
4218
|
if (process.versions.bun)
|
|
3908
4219
|
return [entrypoint];
|
|
@@ -3985,13 +4296,13 @@ init_client2();
|
|
|
3985
4296
|
init_logger();
|
|
3986
4297
|
init_wallet();
|
|
3987
4298
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
3988
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
4299
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3989
4300
|
import os5 from "node:os";
|
|
3990
4301
|
import path8 from "node:path";
|
|
3991
4302
|
|
|
3992
4303
|
// ../../src/runtime/update-hints.ts
|
|
3993
4304
|
init_paths();
|
|
3994
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
4305
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
3995
4306
|
import os4 from "node:os";
|
|
3996
4307
|
import path7 from "node:path";
|
|
3997
4308
|
var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
@@ -4006,7 +4317,7 @@ function getConfigDir2() {
|
|
|
4006
4317
|
}
|
|
4007
4318
|
function ensureDir3(dir) {
|
|
4008
4319
|
if (!existsSync13(dir))
|
|
4009
|
-
|
|
4320
|
+
mkdirSync8(dir, { recursive: true });
|
|
4010
4321
|
return dir;
|
|
4011
4322
|
}
|
|
4012
4323
|
function readJsonFile(file) {
|
|
@@ -4018,7 +4329,7 @@ function readJsonFile(file) {
|
|
|
4018
4329
|
}
|
|
4019
4330
|
function writeJsonFile(file, value) {
|
|
4020
4331
|
ensureDir3(path7.dirname(file));
|
|
4021
|
-
|
|
4332
|
+
writeFileSync6(file, `${JSON.stringify(value, null, 2)}
|
|
4022
4333
|
`);
|
|
4023
4334
|
}
|
|
4024
4335
|
function getInstallSourcePath() {
|
|
@@ -4105,6 +4416,10 @@ function ensureCodexHooksFeature(content) {
|
|
|
4105
4416
|
codex_hooks = true
|
|
4106
4417
|
`;
|
|
4107
4418
|
}
|
|
4419
|
+
function repairManagedCodexHookTable(content) {
|
|
4420
|
+
const marker = CODEX_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4421
|
+
return content.replace(new RegExp(`(${marker}\\r?\\n)\\[\\[?hooks\\]?\\]?(?=\\r?\\n)`, "g"), "$1[hooks]");
|
|
4422
|
+
}
|
|
4108
4423
|
function writeCodexHook(metaUrl) {
|
|
4109
4424
|
const configPath2 = getCodexConfigPath();
|
|
4110
4425
|
if (!existsSync13(path7.dirname(configPath2))) {
|
|
@@ -4116,19 +4431,20 @@ function writeCodexHook(metaUrl) {
|
|
|
4116
4431
|
let content = fileExistsBefore ? readFileSync8(configPath2, "utf8") : "";
|
|
4117
4432
|
const previous = content;
|
|
4118
4433
|
content = ensureCodexHooksFeature(content);
|
|
4434
|
+
content = repairManagedCodexHookTable(content);
|
|
4119
4435
|
if (!content.includes("unbrowse-update-hint.mjs")) {
|
|
4120
4436
|
const command = `node "${hookScript}"`;
|
|
4121
4437
|
const prefix = content && !content.endsWith(`
|
|
4122
4438
|
`) ? `
|
|
4123
4439
|
` : "";
|
|
4124
4440
|
content += `${prefix}${CODEX_MARKER}
|
|
4125
|
-
[
|
|
4441
|
+
[hooks]
|
|
4126
4442
|
event = "SessionStart"
|
|
4127
4443
|
command = ${JSON.stringify(command)}
|
|
4128
4444
|
`;
|
|
4129
4445
|
}
|
|
4130
4446
|
if (content !== previous) {
|
|
4131
|
-
|
|
4447
|
+
writeFileSync6(configPath2, content, "utf8");
|
|
4132
4448
|
return {
|
|
4133
4449
|
host: "codex",
|
|
4134
4450
|
action: fileExistsBefore ? "updated" : "installed",
|
|
@@ -4251,8 +4567,8 @@ function writeOpenCodeCommand(scope, cwd) {
|
|
|
4251
4567
|
const commandFile = path8.join(ensureDir2(commandsDir), "unbrowse.md");
|
|
4252
4568
|
const content = renderOpenCodeCommand();
|
|
4253
4569
|
const action2 = existsSync14(commandFile) ? "updated" : "installed";
|
|
4254
|
-
|
|
4255
|
-
|
|
4570
|
+
mkdirSync9(path8.dirname(commandFile), { recursive: true });
|
|
4571
|
+
writeFileSync7(commandFile, content);
|
|
4256
4572
|
return {
|
|
4257
4573
|
detected: detected || scope !== "auto",
|
|
4258
4574
|
action: action2,
|
|
@@ -4366,7 +4682,7 @@ async function runSetup(options) {
|
|
|
4366
4682
|
|
|
4367
4683
|
// ../../src/runtime/update-hints.ts
|
|
4368
4684
|
init_paths();
|
|
4369
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
4685
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
4370
4686
|
import os6 from "node:os";
|
|
4371
4687
|
import path9 from "node:path";
|
|
4372
4688
|
var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
|
|
@@ -4381,7 +4697,7 @@ function getConfigDir3() {
|
|
|
4381
4697
|
}
|
|
4382
4698
|
function ensureDir4(dir) {
|
|
4383
4699
|
if (!existsSync15(dir))
|
|
4384
|
-
|
|
4700
|
+
mkdirSync10(dir, { recursive: true });
|
|
4385
4701
|
return dir;
|
|
4386
4702
|
}
|
|
4387
4703
|
function readJsonFile2(file) {
|
|
@@ -4393,7 +4709,7 @@ function readJsonFile2(file) {
|
|
|
4393
4709
|
}
|
|
4394
4710
|
function writeJsonFile2(file, value) {
|
|
4395
4711
|
ensureDir4(path9.dirname(file));
|
|
4396
|
-
|
|
4712
|
+
writeFileSync8(file, `${JSON.stringify(value, null, 2)}
|
|
4397
4713
|
`);
|
|
4398
4714
|
}
|
|
4399
4715
|
function getInstallSourcePath2() {
|
|
@@ -4560,12 +4876,20 @@ async function defaultReadChoice(allowed, dflt) {
|
|
|
4560
4876
|
rl.close();
|
|
4561
4877
|
}
|
|
4562
4878
|
}
|
|
4879
|
+
function shouldSkipInteractivePrompt(opts) {
|
|
4880
|
+
if (opts.readChoice)
|
|
4881
|
+
return false;
|
|
4882
|
+
return process.env.UNBROWSE_NON_INTERACTIVE === "1" || !process.stdin.isTTY || !process.stdout.isTTY;
|
|
4883
|
+
}
|
|
4563
4884
|
async function promptContributionMode(opts = {}) {
|
|
4564
4885
|
const log2 = opts.log ?? ((msg) => console.log(msg));
|
|
4565
4886
|
const cfg = getContributionConfig();
|
|
4566
4887
|
if (!opts.force && cfg.contribution.set_via && cfg.contribution.set_via !== "default") {
|
|
4567
4888
|
return null;
|
|
4568
4889
|
}
|
|
4890
|
+
if (shouldSkipInteractivePrompt(opts)) {
|
|
4891
|
+
return null;
|
|
4892
|
+
}
|
|
4569
4893
|
log2("");
|
|
4570
4894
|
log2("How do you want unbrowse to handle the routes you discover?");
|
|
4571
4895
|
log2("");
|
|
@@ -4610,11 +4934,100 @@ function maybeShowContributionNotice() {
|
|
|
4610
4934
|
return true;
|
|
4611
4935
|
}
|
|
4612
4936
|
|
|
4937
|
+
// ../../src/config/contribution.ts
|
|
4938
|
+
import fs3 from "node:fs";
|
|
4939
|
+
import path10 from "node:path";
|
|
4940
|
+
import os7 from "node:os";
|
|
4941
|
+
function configPath2() {
|
|
4942
|
+
return process.env.UNBROWSE_CONFIG_PATH || path10.join(os7.homedir(), ".unbrowse", "config.json");
|
|
4943
|
+
}
|
|
4944
|
+
var DEFAULT2 = {
|
|
4945
|
+
contribution: { share_pointers: false, set_via: "default" },
|
|
4946
|
+
rev_share: { opted_in: false },
|
|
4947
|
+
notice_shown_count: 0
|
|
4948
|
+
};
|
|
4949
|
+
function freshDefault2() {
|
|
4950
|
+
return {
|
|
4951
|
+
contribution: { ...DEFAULT2.contribution },
|
|
4952
|
+
rev_share: { ...DEFAULT2.rev_share },
|
|
4953
|
+
notice_shown_count: 0
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4956
|
+
var cached2 = null;
|
|
4957
|
+
function getContributionConfig2() {
|
|
4958
|
+
if (cached2)
|
|
4959
|
+
return cached2;
|
|
4960
|
+
const p = configPath2();
|
|
4961
|
+
let fileExisted = false;
|
|
4962
|
+
let raw = null;
|
|
4963
|
+
try {
|
|
4964
|
+
const content = fs3.readFileSync(p, "utf-8");
|
|
4965
|
+
fileExisted = true;
|
|
4966
|
+
if (content.trim().length > 0) {
|
|
4967
|
+
raw = JSON.parse(content);
|
|
4968
|
+
}
|
|
4969
|
+
} catch {}
|
|
4970
|
+
if (!raw) {
|
|
4971
|
+
cached2 = freshDefault2();
|
|
4972
|
+
return cached2;
|
|
4973
|
+
}
|
|
4974
|
+
const contributionRaw = raw.contribution ?? null;
|
|
4975
|
+
const revShareRaw = raw.rev_share ?? null;
|
|
4976
|
+
const isExistingUserMigration = fileExisted && !contributionRaw;
|
|
4977
|
+
cached2 = {
|
|
4978
|
+
contribution: {
|
|
4979
|
+
share_pointers: !!(contributionRaw?.share_pointers ?? DEFAULT2.contribution.share_pointers),
|
|
4980
|
+
set_via: contributionRaw?.set_via ?? DEFAULT2.contribution.set_via,
|
|
4981
|
+
...contributionRaw?.set_at ? { set_at: String(contributionRaw.set_at) } : {}
|
|
4982
|
+
},
|
|
4983
|
+
rev_share: {
|
|
4984
|
+
opted_in: !!(revShareRaw?.opted_in ?? DEFAULT2.rev_share.opted_in),
|
|
4985
|
+
...revShareRaw?.wallet_address ? { wallet_address: String(revShareRaw.wallet_address) } : {}
|
|
4986
|
+
},
|
|
4987
|
+
notice_shown_count: typeof raw.notice_shown_count === "number" ? raw.notice_shown_count : isExistingUserMigration ? 5 : 0
|
|
4988
|
+
};
|
|
4989
|
+
if (isExistingUserMigration) {
|
|
4990
|
+
try {
|
|
4991
|
+
const merged = { ...raw, ...cached2 };
|
|
4992
|
+
fs3.mkdirSync(path10.dirname(p), { recursive: true });
|
|
4993
|
+
fs3.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
4994
|
+
} catch {}
|
|
4995
|
+
}
|
|
4996
|
+
return cached2;
|
|
4997
|
+
}
|
|
4998
|
+
function setContributionConfig2(updates) {
|
|
4999
|
+
const current = getContributionConfig2();
|
|
5000
|
+
const next = {
|
|
5001
|
+
contribution: {
|
|
5002
|
+
...current.contribution,
|
|
5003
|
+
...updates.contribution ?? {},
|
|
5004
|
+
set_at: new Date().toISOString()
|
|
5005
|
+
},
|
|
5006
|
+
rev_share: {
|
|
5007
|
+
...current.rev_share,
|
|
5008
|
+
...updates.rev_share ?? {}
|
|
5009
|
+
},
|
|
5010
|
+
notice_shown_count: updates.notice_shown_count ?? current.notice_shown_count ?? 0
|
|
5011
|
+
};
|
|
5012
|
+
const p = configPath2();
|
|
5013
|
+
let existing = {};
|
|
5014
|
+
try {
|
|
5015
|
+
const content = fs3.readFileSync(p, "utf-8");
|
|
5016
|
+
if (content.trim())
|
|
5017
|
+
existing = JSON.parse(content);
|
|
5018
|
+
} catch {}
|
|
5019
|
+
const merged = { ...existing, ...next };
|
|
5020
|
+
fs3.mkdirSync(path10.dirname(p), { recursive: true });
|
|
5021
|
+
fs3.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
5022
|
+
cached2 = next;
|
|
5023
|
+
}
|
|
5024
|
+
|
|
4613
5025
|
// ../../src/cli.ts
|
|
4614
5026
|
loadEnv({ quiet: true });
|
|
4615
5027
|
loadEnv({ path: ".env.runtime", quiet: true });
|
|
4616
5028
|
var BASE_URL = process.env.UNBROWSE_URL || "http://localhost:6969";
|
|
4617
5029
|
var CLI_CLIENT_ID = process.env.UNBROWSE_CLIENT_ID || `cli-${process.ppid || process.pid}`;
|
|
5030
|
+
var FRONTEND_URL = (process.env.UNBROWSE_FRONTEND_URL || process.env.PUBLIC_FRONTEND_URL || "https://www.unbrowse.ai").replace(/\/+$/, "");
|
|
4618
5031
|
var walletNudgeShown = false;
|
|
4619
5032
|
function parseArgs(argv) {
|
|
4620
5033
|
const raw = argv.slice(2);
|
|
@@ -4641,8 +5054,12 @@ function parseArgs(argv) {
|
|
|
4641
5054
|
if (valueExpectedFlags.has(key)) {
|
|
4642
5055
|
if (next === undefined)
|
|
4643
5056
|
die(`--${key} requires a value`);
|
|
4644
|
-
|
|
4645
|
-
|
|
5057
|
+
if (next === "-p" || next === "--param" || next.startsWith("--")) {
|
|
5058
|
+
flags[key] = true;
|
|
5059
|
+
} else {
|
|
5060
|
+
flags[key] = next;
|
|
5061
|
+
i++;
|
|
5062
|
+
}
|
|
4646
5063
|
} else if (!next || next.startsWith("--") || next === "-p" || next === "--param") {
|
|
4647
5064
|
flags[key] = true;
|
|
4648
5065
|
} else {
|
|
@@ -4655,8 +5072,8 @@ function parseArgs(argv) {
|
|
|
4655
5072
|
}
|
|
4656
5073
|
return { command, args: positional, flags, params };
|
|
4657
5074
|
}
|
|
4658
|
-
async function api2(method,
|
|
4659
|
-
let target = `${BASE_URL}${
|
|
5075
|
+
async function api2(method, path11, body, opts) {
|
|
5076
|
+
let target = `${BASE_URL}${path11}`;
|
|
4660
5077
|
let requestBody = body;
|
|
4661
5078
|
if (method === "GET" && body && typeof body === "object") {
|
|
4662
5079
|
const params = new URLSearchParams;
|
|
@@ -4670,14 +5087,34 @@ async function api2(method, path10, body) {
|
|
|
4670
5087
|
target += `${target.includes("?") ? "&" : "?"}${query}`;
|
|
4671
5088
|
requestBody = undefined;
|
|
4672
5089
|
}
|
|
4673
|
-
const
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
5090
|
+
const ctrl = new AbortController;
|
|
5091
|
+
const timeoutMs = opts?.timeoutMs;
|
|
5092
|
+
const timer = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => ctrl.abort(), timeoutMs) : null;
|
|
5093
|
+
let res;
|
|
5094
|
+
try {
|
|
5095
|
+
res = await fetch(target, {
|
|
5096
|
+
method,
|
|
5097
|
+
headers: {
|
|
5098
|
+
...requestBody ? { "Content-Type": "application/json" } : {},
|
|
5099
|
+
"x-unbrowse-client-id": CLI_CLIENT_ID
|
|
5100
|
+
},
|
|
5101
|
+
body: requestBody ? JSON.stringify(requestBody) : undefined,
|
|
5102
|
+
signal: ctrl.signal
|
|
5103
|
+
});
|
|
5104
|
+
} catch (err) {
|
|
5105
|
+
if (timer)
|
|
5106
|
+
clearTimeout(timer);
|
|
5107
|
+
if (err?.name === "AbortError") {
|
|
5108
|
+
return {
|
|
5109
|
+
error: "cli_timeout",
|
|
5110
|
+
message: `CLI gave up waiting on local server after ${timeoutMs}ms. Try: pkill -9 -f 'unbrowse|kuri'`
|
|
5111
|
+
};
|
|
5112
|
+
}
|
|
5113
|
+
throw err;
|
|
5114
|
+
} finally {
|
|
5115
|
+
if (timer)
|
|
5116
|
+
clearTimeout(timer);
|
|
5117
|
+
}
|
|
4681
5118
|
if (!res.ok && res.headers.get("content-type")?.includes("json")) {
|
|
4682
5119
|
return res.json();
|
|
4683
5120
|
}
|
|
@@ -4698,6 +5135,14 @@ function info(msg) {
|
|
|
4698
5135
|
process.stderr.write(`[unbrowse] ${msg}
|
|
4699
5136
|
`);
|
|
4700
5137
|
}
|
|
5138
|
+
function openUrl(url) {
|
|
5139
|
+
if (process.env.UNBROWSE_OPEN_BROWSER === "0")
|
|
5140
|
+
return;
|
|
5141
|
+
try {
|
|
5142
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
5143
|
+
spawn3(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
5144
|
+
} catch {}
|
|
5145
|
+
}
|
|
4701
5146
|
function resolveResultError(result) {
|
|
4702
5147
|
return result.result?.error ?? result.error;
|
|
4703
5148
|
}
|
|
@@ -4707,6 +5152,9 @@ function resolveLoginUrl(result, fallbackUrl) {
|
|
|
4707
5152
|
function isResolveSuccessResult(result) {
|
|
4708
5153
|
if (resolveResultError(result))
|
|
4709
5154
|
return false;
|
|
5155
|
+
const status = result.result?.status;
|
|
5156
|
+
if (status === "no_match" || status === "auth_required" || status === "error")
|
|
5157
|
+
return false;
|
|
4710
5158
|
return !!result.result || Array.isArray(result.available_endpoints);
|
|
4711
5159
|
}
|
|
4712
5160
|
async function withPendingNotice(promise, message, delayMs = 3000) {
|
|
@@ -4971,7 +5419,8 @@ async function cmdResolve(flags) {
|
|
|
4971
5419
|
body.projection = { raw: true };
|
|
4972
5420
|
const startedAt = Date.now();
|
|
4973
5421
|
async function resolveOnce(message = "Still working. Searching cached routes...") {
|
|
4974
|
-
|
|
5422
|
+
const cliTimeoutMs = (typeof body.budget_ms === "number" ? body.budget_ms : 8000) + 30000;
|
|
5423
|
+
return withPendingNotice(api2("POST", "/v1/intent/resolve", body, { timeoutMs: cliTimeoutMs }), message);
|
|
4975
5424
|
}
|
|
4976
5425
|
let result = await resolveOnce();
|
|
4977
5426
|
const resultError = resolveResultError(result);
|
|
@@ -4991,8 +5440,13 @@ async function cmdResolve(flags) {
|
|
|
4991
5440
|
const skillId = resolveSkillId();
|
|
4992
5441
|
if (skillId && endpoints.length > 0) {
|
|
4993
5442
|
const bestEndpoint = endpoints[0];
|
|
4994
|
-
|
|
4995
|
-
|
|
5443
|
+
if (endpointNeedsThirdPartyTermsConfirmation(bestEndpoint) && !flags["confirm-third-party-terms"]) {
|
|
5444
|
+
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.
|
|
5445
|
+
`);
|
|
5446
|
+
} else {
|
|
5447
|
+
info(`Auto-executing endpoint: ${bestEndpoint.description ?? bestEndpoint.endpoint_id}`);
|
|
5448
|
+
result = await withPendingNotice(api2("POST", `/v1/skills/${skillId}/execute`, execBody(bestEndpoint.endpoint_id)), "Executing best endpoint...");
|
|
5449
|
+
}
|
|
4996
5450
|
}
|
|
4997
5451
|
}
|
|
4998
5452
|
if (Date.now() - startedAt > 3000 && result.source === "live-capture") {
|
|
@@ -5054,8 +5508,8 @@ async function cmdResolve(flags) {
|
|
|
5054
5508
|
throw error;
|
|
5055
5509
|
}
|
|
5056
5510
|
}
|
|
5057
|
-
function drillPath(data,
|
|
5058
|
-
const segments =
|
|
5511
|
+
function drillPath(data, path11) {
|
|
5512
|
+
const segments = path11.split(/\./).flatMap((s) => {
|
|
5059
5513
|
const m = s.match(/^(.+)\[\]$/);
|
|
5060
5514
|
return m ? [m[1], "[]"] : [s];
|
|
5061
5515
|
});
|
|
@@ -5082,9 +5536,9 @@ function drillPath(data, path10) {
|
|
|
5082
5536
|
}
|
|
5083
5537
|
return values;
|
|
5084
5538
|
}
|
|
5085
|
-
function resolveDotPath(obj,
|
|
5539
|
+
function resolveDotPath(obj, path11) {
|
|
5086
5540
|
let cur = obj;
|
|
5087
|
-
for (const key of
|
|
5541
|
+
for (const key of path11.split(".")) {
|
|
5088
5542
|
if (cur == null || typeof cur !== "object")
|
|
5089
5543
|
return;
|
|
5090
5544
|
cur = cur[key];
|
|
@@ -5101,8 +5555,8 @@ function applyExtract(items, extractSpec) {
|
|
|
5101
5555
|
return items.map((item) => {
|
|
5102
5556
|
const row = {};
|
|
5103
5557
|
let hasValue = false;
|
|
5104
|
-
for (const { alias, path:
|
|
5105
|
-
const val = resolveDotPath(item,
|
|
5558
|
+
for (const { alias, path: path11 } of fields) {
|
|
5559
|
+
const val = resolveDotPath(item, path11);
|
|
5106
5560
|
row[alias] = val ?? null;
|
|
5107
5561
|
if (val != null)
|
|
5108
5562
|
hasValue = true;
|
|
@@ -5419,7 +5873,67 @@ async function cmdCleanupStale(flags) {
|
|
|
5419
5873
|
output(await withPendingNotice(api2("POST", "/v1/stale/cleanup", body), "Cleaning stale endpoints..."), !!flags.pretty);
|
|
5420
5874
|
}
|
|
5421
5875
|
async function cmdSearch(flags) {
|
|
5422
|
-
|
|
5876
|
+
const intent = flags.intent;
|
|
5877
|
+
const hostType = detectTelemetryHostType();
|
|
5878
|
+
const { decodeTelemetryAttribution: decodeTelemetryAttribution3 } = await Promise.resolve().then(() => (init_telemetry_attribution2(), exports_telemetry_attribution));
|
|
5879
|
+
const attr = decodeTelemetryAttribution3(process.env.UNBROWSE_ATTRIBUTION_B64) ?? {};
|
|
5880
|
+
const attrProps = {};
|
|
5881
|
+
for (const k of ["channel", "campaign_id", "content_id", "variant_id"]) {
|
|
5882
|
+
const v = attr[k];
|
|
5883
|
+
if (v != null)
|
|
5884
|
+
attrProps[k] = v;
|
|
5885
|
+
}
|
|
5886
|
+
const domain = telemetryDomainFromInput(flags.domain, flags.url);
|
|
5887
|
+
if (intent) {
|
|
5888
|
+
await recordFunnelTelemetryEvent("search_started", {
|
|
5889
|
+
source: "cli",
|
|
5890
|
+
hostType,
|
|
5891
|
+
properties: {
|
|
5892
|
+
command: "search",
|
|
5893
|
+
intent,
|
|
5894
|
+
domain,
|
|
5895
|
+
url: typeof flags.url === "string" ? flags.url : null,
|
|
5896
|
+
...attrProps
|
|
5897
|
+
}
|
|
5898
|
+
});
|
|
5899
|
+
}
|
|
5900
|
+
let resultCount = 0;
|
|
5901
|
+
try {
|
|
5902
|
+
if (intent && domain) {
|
|
5903
|
+
try {
|
|
5904
|
+
const searchRes = await api2("GET", `/v1/search/domain?intent=${encodeURIComponent(intent)}&domain=${encodeURIComponent(domain)}`);
|
|
5905
|
+
resultCount = Array.isArray(searchRes?.results) ? searchRes.results.length : 0;
|
|
5906
|
+
} catch {}
|
|
5907
|
+
}
|
|
5908
|
+
await cmdResolve(flags);
|
|
5909
|
+
if (intent) {
|
|
5910
|
+
await recordFunnelTelemetryEvent("search_completed", {
|
|
5911
|
+
source: "cli",
|
|
5912
|
+
hostType,
|
|
5913
|
+
properties: {
|
|
5914
|
+
command: "search",
|
|
5915
|
+
intent,
|
|
5916
|
+
domain,
|
|
5917
|
+
result_count: resultCount,
|
|
5918
|
+
...attrProps
|
|
5919
|
+
}
|
|
5920
|
+
});
|
|
5921
|
+
}
|
|
5922
|
+
} catch (err) {
|
|
5923
|
+
if (intent) {
|
|
5924
|
+
await recordFunnelTelemetryEvent("search_failed", {
|
|
5925
|
+
source: "cli",
|
|
5926
|
+
hostType,
|
|
5927
|
+
properties: {
|
|
5928
|
+
command: "search",
|
|
5929
|
+
intent,
|
|
5930
|
+
error: err instanceof Error ? err.message : String(err),
|
|
5931
|
+
...attrProps
|
|
5932
|
+
}
|
|
5933
|
+
});
|
|
5934
|
+
}
|
|
5935
|
+
throw err;
|
|
5936
|
+
}
|
|
5423
5937
|
}
|
|
5424
5938
|
async function cmdSessions(flags) {
|
|
5425
5939
|
const domain = flags.domain;
|
|
@@ -5461,7 +5975,7 @@ async function cmdSetup(flags) {
|
|
|
5461
5975
|
info("Wallet not paired \u2014 you won't earn when other agents use routes you discovered.");
|
|
5462
5976
|
info("Run: npx @crossmint/lobster-cli setup");
|
|
5463
5977
|
} else {
|
|
5464
|
-
info("No wallet configured \u2014
|
|
5978
|
+
info("No wallet configured \u2014 local indexing works, but payout needs a wallet.");
|
|
5465
5979
|
info("Set up a wallet to start earning:");
|
|
5466
5980
|
info(" npx @crossmint/lobster-cli setup");
|
|
5467
5981
|
}
|
|
@@ -5531,6 +6045,10 @@ async function cmdSetup(flags) {
|
|
|
5531
6045
|
output(report, true);
|
|
5532
6046
|
if (report.browser_engine.action === "failed")
|
|
5533
6047
|
process.exit(1);
|
|
6048
|
+
if (getApiKey()) {
|
|
6049
|
+
info("Dashboard connected:");
|
|
6050
|
+
info(" unbrowse dashboard");
|
|
6051
|
+
}
|
|
5534
6052
|
try {
|
|
5535
6053
|
info("Trying your first resolve...");
|
|
5536
6054
|
const demoUrl = "https://jsonplaceholder.typicode.com";
|
|
@@ -5567,8 +6085,15 @@ async function cmdSetup(flags) {
|
|
|
5567
6085
|
info("That's unbrowse. Try your own:");
|
|
5568
6086
|
info(' unbrowse resolve --intent "search for shoes" --url "https://amazon.com"');
|
|
5569
6087
|
} else {
|
|
5570
|
-
|
|
5571
|
-
|
|
6088
|
+
const inner = resolveResult.result;
|
|
6089
|
+
const nextStep = inner?.next_step;
|
|
6090
|
+
const command = typeof nextStep?.command === "string" ? nextStep.command : 'unbrowse capture --url "https://jsonplaceholder.typicode.com" --intent "list all posts"';
|
|
6091
|
+
info("No reusable route found on the demo site yet.");
|
|
6092
|
+
info("Next step:");
|
|
6093
|
+
info(` ${command}`);
|
|
6094
|
+
info("");
|
|
6095
|
+
info("Try your own:");
|
|
6096
|
+
info(' unbrowse resolve --intent "search for shoes" --url "https://amazon.com"');
|
|
5572
6097
|
}
|
|
5573
6098
|
} catch {
|
|
5574
6099
|
info("Setup complete. Try your first resolve:");
|
|
@@ -5577,6 +6102,107 @@ async function cmdSetup(flags) {
|
|
|
5577
6102
|
}
|
|
5578
6103
|
async function cmdMode(_flags) {
|
|
5579
6104
|
await promptContributionMode({ force: true });
|
|
6105
|
+
await syncContributionPreferenceToServer();
|
|
6106
|
+
}
|
|
6107
|
+
async function syncContributionPreferenceToServer() {
|
|
6108
|
+
const cfg = loadConfig();
|
|
6109
|
+
if (!cfg?.api_key)
|
|
6110
|
+
return;
|
|
6111
|
+
const share_pointers = !!getContributionConfig2().contribution.share_pointers;
|
|
6112
|
+
try {
|
|
6113
|
+
await pushAccountPreferences({ share_pointers });
|
|
6114
|
+
info("Synced preference to your account.");
|
|
6115
|
+
} catch (err) {
|
|
6116
|
+
const msg = err.message ?? "";
|
|
6117
|
+
if (msg.includes("account_required") || msg.includes("HTTP 403"))
|
|
6118
|
+
return;
|
|
6119
|
+
info(`Local mode set, but server sync failed: ${msg}`);
|
|
6120
|
+
}
|
|
6121
|
+
}
|
|
6122
|
+
async function refreshContributionPreferenceFromServer(verbose = false) {
|
|
6123
|
+
const cfg = loadConfig();
|
|
6124
|
+
if (!cfg?.api_key)
|
|
6125
|
+
return false;
|
|
6126
|
+
try {
|
|
6127
|
+
const serverPrefs = await fetchAccountPreferences();
|
|
6128
|
+
if (!serverPrefs)
|
|
6129
|
+
return false;
|
|
6130
|
+
const local = getContributionConfig2();
|
|
6131
|
+
if (local.contribution.share_pointers !== serverPrefs.share_pointers) {
|
|
6132
|
+
setContributionConfig2({
|
|
6133
|
+
contribution: { share_pointers: serverPrefs.share_pointers, set_via: "mode-command" }
|
|
6134
|
+
});
|
|
6135
|
+
if (verbose)
|
|
6136
|
+
info(`Synced auto-publish from dashboard: ${serverPrefs.share_pointers ? "ON" : "off"}.`);
|
|
6137
|
+
}
|
|
6138
|
+
return true;
|
|
6139
|
+
} catch (err) {
|
|
6140
|
+
if (verbose)
|
|
6141
|
+
info(`Dashboard preference sync failed: ${err.message}`);
|
|
6142
|
+
return false;
|
|
6143
|
+
}
|
|
6144
|
+
}
|
|
6145
|
+
async function cmdAccount(flags) {
|
|
6146
|
+
if (flags["reset-key"]) {
|
|
6147
|
+
await cmdRegister({
|
|
6148
|
+
reset: true,
|
|
6149
|
+
email: typeof flags.email === "string" ? flags.email : undefined,
|
|
6150
|
+
"no-prompt": flags["no-prompt"]
|
|
6151
|
+
});
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6154
|
+
await refreshContributionPreferenceFromServer(false);
|
|
6155
|
+
const cfg = loadConfig();
|
|
6156
|
+
const contribution = getContributionConfig2();
|
|
6157
|
+
const payload = {
|
|
6158
|
+
signed_in: !!cfg?.api_key,
|
|
6159
|
+
agent_id: cfg?.agent_id ?? null,
|
|
6160
|
+
agent_name: cfg?.agent_name ?? null,
|
|
6161
|
+
email: cfg?.email ?? null,
|
|
6162
|
+
user_id: cfg?.user_id ?? null,
|
|
6163
|
+
wallet_address: cfg?.wallet_address ?? null,
|
|
6164
|
+
wallet_provider: cfg?.wallet_provider ?? null,
|
|
6165
|
+
dashboard_url: `${FRONTEND_URL}/dashboard`,
|
|
6166
|
+
local_server: BASE_URL,
|
|
6167
|
+
auto_publish: contribution.contribution.share_pointers,
|
|
6168
|
+
rev_share: contribution.rev_share.opted_in
|
|
6169
|
+
};
|
|
6170
|
+
if (flags.json || flags.pretty) {
|
|
6171
|
+
output(payload, !!flags.pretty);
|
|
6172
|
+
return;
|
|
6173
|
+
}
|
|
6174
|
+
info("Unbrowse account");
|
|
6175
|
+
info(` signed_in: ${payload.signed_in ? "yes" : "no"}`);
|
|
6176
|
+
info(` email: ${payload.email ?? "(none)"}`);
|
|
6177
|
+
info(` agent_id: ${payload.agent_id ?? "(none)"}`);
|
|
6178
|
+
info(` wallet: ${payload.wallet_address ?? "(none)"}`);
|
|
6179
|
+
info(` auto_publish: ${payload.auto_publish ? "on" : "off"}`);
|
|
6180
|
+
info(` dashboard: ${payload.dashboard_url}`);
|
|
6181
|
+
output(payload, false);
|
|
6182
|
+
}
|
|
6183
|
+
async function cmdDashboard(flags) {
|
|
6184
|
+
await refreshContributionPreferenceFromServer(false);
|
|
6185
|
+
const cfg = loadConfig();
|
|
6186
|
+
if (!cfg?.api_key) {
|
|
6187
|
+
const loginUrl = `${FRONTEND_URL}/login`;
|
|
6188
|
+
info("No account-bound CLI key found. Opening website sign-in.");
|
|
6189
|
+
if (!flags["no-open"])
|
|
6190
|
+
openUrl(loginUrl);
|
|
6191
|
+
output({ status: "login_required", url: loginUrl }, !!flags.pretty);
|
|
6192
|
+
return;
|
|
6193
|
+
}
|
|
6194
|
+
await ensureLocalServer(BASE_URL, false, import.meta.url);
|
|
6195
|
+
const pair = createDashboardPairingToken();
|
|
6196
|
+
const url = `${FRONTEND_URL}/login?local=${encodeURIComponent(BASE_URL)}&pair=${encodeURIComponent(pair.token)}`;
|
|
6197
|
+
if (!flags["no-open"])
|
|
6198
|
+
openUrl(url);
|
|
6199
|
+
info("Opening dashboard and pairing this CLI install.");
|
|
6200
|
+
output({
|
|
6201
|
+
status: "pairing_started",
|
|
6202
|
+
url,
|
|
6203
|
+
local_server: BASE_URL,
|
|
6204
|
+
expires_at: pair.expires_at
|
|
6205
|
+
}, !!flags.pretty);
|
|
5580
6206
|
}
|
|
5581
6207
|
async function runPostSetupContributionPrompt() {
|
|
5582
6208
|
try {
|
|
@@ -5594,16 +6220,68 @@ async function cmdCapture(flags) {
|
|
|
5594
6220
|
const endpoints = Array.isArray(result.endpoints) ? result.endpoints : Array.isArray(result.available_endpoints) ? result.available_endpoints : [];
|
|
5595
6221
|
const skill = result.skill ?? null;
|
|
5596
6222
|
const skillId = result.skill_id ?? skill?.skill_id ?? (typeof result.learned_skill_id === "string" ? result.learned_skill_id : undefined);
|
|
6223
|
+
const isThinDocumentOnly = endpoints.length === 1 && (() => {
|
|
6224
|
+
const e0 = endpoints[0];
|
|
6225
|
+
if (!e0 || e0.method !== "GET")
|
|
6226
|
+
return false;
|
|
6227
|
+
const tmpl = (e0.url_template ?? "").split("?")[0].replace(/\/$/, "");
|
|
6228
|
+
const target = url.split("?")[0].replace(/\/$/, "");
|
|
6229
|
+
return tmpl === target;
|
|
6230
|
+
})();
|
|
6231
|
+
const escapedIntent = intent.replace(/"/g, "\\\"");
|
|
6232
|
+
const escapedUrl = url.replace(/"/g, "\\\"");
|
|
5597
6233
|
const envelope = {
|
|
5598
6234
|
skill_id: skillId,
|
|
5599
6235
|
endpoints_discovered: typeof result.endpoints_discovered === "number" ? result.endpoints_discovered : endpoints.length,
|
|
5600
6236
|
marketplace_published: !!result.marketplace_published,
|
|
5601
6237
|
ms: typeof result.ms === "number" ? result.ms : Date.now() - t0,
|
|
5602
|
-
next_step: endpoints.length
|
|
5603
|
-
...
|
|
6238
|
+
next_step: endpoints.length === 0 ? "no endpoints discovered; site may need authentication or different intent" : `unbrowse resolve --intent "${escapedIntent}" --url "${escapedUrl}"`,
|
|
6239
|
+
...isThinDocumentOnly ? { capture_pattern: "doc_only", capture_observation: "only the input URL was captured (1 GET, no XHR fired during the auto-capture window)" } : {},
|
|
6240
|
+
...result.error ? { error: result.error } : {},
|
|
6241
|
+
...result.captured_meta ? { captured_meta: result.captured_meta } : {},
|
|
6242
|
+
...typeof result.capture_path === "string" ? { capture_path: result.capture_path } : {},
|
|
6243
|
+
...result.prior_domain_note ? { prior_domain_note: result.prior_domain_note } : {},
|
|
6244
|
+
...result.note_evidence ? { note_evidence: result.note_evidence } : {}
|
|
5604
6245
|
};
|
|
5605
6246
|
output(envelope, !!flags.pretty);
|
|
5606
6247
|
}
|
|
6248
|
+
async function cmdNote(flags, args) {
|
|
6249
|
+
const subRaw = args?.[0] ?? flags.action;
|
|
6250
|
+
const sub = (typeof subRaw === "string" ? subRaw : "").toLowerCase();
|
|
6251
|
+
if (!sub || !["read", "write", "list"].includes(sub)) {
|
|
6252
|
+
die('usage: unbrowse note <read|write|list> --domain <domain> [--body "..."]');
|
|
6253
|
+
}
|
|
6254
|
+
if (sub === "list") {
|
|
6255
|
+
const { homedir: homedir9 } = await import("os");
|
|
6256
|
+
const { join: join15 } = await import("path");
|
|
6257
|
+
const { readdirSync: readdirSync7, existsSync: existsSync19 } = await import("fs");
|
|
6258
|
+
const profile = process.env.UNBROWSE_PROFILE ?? "";
|
|
6259
|
+
const dir = process.env.UNBROWSE_DOMAIN_NOTES_DIR ?? (profile ? join15(homedir9(), ".unbrowse", "profiles", profile, "domain-notes") : join15(homedir9(), ".unbrowse", "domain-notes"));
|
|
6260
|
+
if (!existsSync19(dir)) {
|
|
6261
|
+
output({ notes: [], dir }, !!flags.pretty);
|
|
6262
|
+
return;
|
|
6263
|
+
}
|
|
6264
|
+
const files = readdirSync7(dir).filter((f) => f.endsWith(".md"));
|
|
6265
|
+
output({ dir, notes: files.map((f) => f.replace(/\.md$/, "")) }, !!flags.pretty);
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
const domain = flags.domain;
|
|
6269
|
+
if (!domain)
|
|
6270
|
+
die("--domain required");
|
|
6271
|
+
if (sub === "read") {
|
|
6272
|
+
const res2 = await api2("GET", `/v1/domain-notes/${encodeURIComponent(domain)}`).catch((err) => ({
|
|
6273
|
+
error: err.message
|
|
6274
|
+
}));
|
|
6275
|
+
output(res2, !!flags.pretty);
|
|
6276
|
+
return;
|
|
6277
|
+
}
|
|
6278
|
+
const body = flags.body;
|
|
6279
|
+
if (typeof body !== "string" || body.trim().length === 0) {
|
|
6280
|
+
die("--body required (non-empty markdown string)");
|
|
6281
|
+
}
|
|
6282
|
+
const res = await api2("POST", `/v1/domain-notes/${encodeURIComponent(domain)}`, { body });
|
|
6283
|
+
output(res, !!flags.pretty);
|
|
6284
|
+
}
|
|
5607
6285
|
var CLI_REFERENCE = {
|
|
5608
6286
|
commands: [
|
|
5609
6287
|
{ name: "health", usage: "", desc: "Server health check" },
|
|
@@ -5648,9 +6326,12 @@ var CLI_REFERENCE = {
|
|
|
5648
6326
|
{ name: "earnings", usage: "[--json]", desc: "Show your credit balance, earnings from indexing, and spending" },
|
|
5649
6327
|
{ 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
6328
|
{ 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: "
|
|
6329
|
+
{ name: "register", usage: "[--email lewis@example.com] [--reset] [--no-prompt]", desc: "Register an API key. With --reset, discard the local cached key first; with --email, mint an account-bound key." },
|
|
6330
|
+
{ name: "account", usage: "[--json] [--pretty] [--reset-key] [--email lewis@example.com]", desc: "Show local account, dashboard link, wallet, and contribution mode; --reset-key forces local key reset." },
|
|
6331
|
+
{ name: "dashboard", usage: "[--no-open] [--pretty]", desc: "Open the website dashboard and pair it to this CLI install through localhost" },
|
|
5652
6332
|
{ 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`." }
|
|
6333
|
+
{ 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`." },
|
|
6334
|
+
{ 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
6335
|
],
|
|
5655
6336
|
globalFlags: [
|
|
5656
6337
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
@@ -6245,13 +6926,83 @@ async function cmdClose(flags) {
|
|
|
6245
6926
|
output(await api2("POST", "/v1/browse/close", typeof flags.session === "string" ? { session_id: flags.session } : undefined), false);
|
|
6246
6927
|
}
|
|
6247
6928
|
async function cmdRegister(flags) {
|
|
6248
|
-
|
|
6929
|
+
const reset = flags.reset === true || flags.force === true || flags["reset-key"] === true;
|
|
6930
|
+
const previousConfig = reset ? loadConfig() : null;
|
|
6931
|
+
let ignoredEnvApiKey = false;
|
|
6932
|
+
const stopServerAfterReset = () => {
|
|
6933
|
+
if (!reset)
|
|
6934
|
+
return;
|
|
6935
|
+
if (stopServer(BASE_URL)) {
|
|
6936
|
+
info("Stopped local server so the next command starts with the fresh key.");
|
|
6937
|
+
}
|
|
6938
|
+
};
|
|
6939
|
+
if (reset) {
|
|
6940
|
+
const envKey = process.env.UNBROWSE_API_KEY?.trim();
|
|
6941
|
+
const result = resetLocalRegistration();
|
|
6942
|
+
delete process.env.UNBROWSE_API_KEY;
|
|
6943
|
+
if (envKey) {
|
|
6944
|
+
ignoredEnvApiKey = true;
|
|
6945
|
+
process.env.UNBROWSE_IGNORE_ENV_API_KEY = "1";
|
|
6946
|
+
}
|
|
6947
|
+
info(`${result.removed ? "Removed" : "No"} local API key cache at ${result.config_path}.`);
|
|
6948
|
+
if (envKey) {
|
|
6949
|
+
info("Ignoring UNBROWSE_API_KEY for this reset run. Future Unbrowse commands will prefer the fresh saved key; still remove or update that env var in your shell.");
|
|
6950
|
+
}
|
|
6951
|
+
if (typeof flags.email !== "string" && previousConfig?.email) {
|
|
6952
|
+
flags.email = previousConfig.email;
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
if (typeof flags.email === "string" && flags.email.length > 0) {
|
|
6956
|
+
const email = flags.email;
|
|
6957
|
+
if (!reset && getApiKey()) {
|
|
6958
|
+
info("Already registered. Re-running with --email will mint a new key and overwrite ~/.unbrowse/config.json.");
|
|
6959
|
+
}
|
|
6960
|
+
info(`Sending magic link to ${email}\u2026`);
|
|
6961
|
+
const result = await magicRegister({
|
|
6962
|
+
email,
|
|
6963
|
+
openBrowser: (url) => {
|
|
6964
|
+
info(`Opening browser: ${url}`);
|
|
6965
|
+
try {
|
|
6966
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
6967
|
+
spawn3(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
6968
|
+
} catch {}
|
|
6969
|
+
}
|
|
6970
|
+
});
|
|
6971
|
+
saveConfig({
|
|
6972
|
+
api_key: result.api_key,
|
|
6973
|
+
agent_id: result.agent_id,
|
|
6974
|
+
agent_name: result.email,
|
|
6975
|
+
registered_at: new Date().toISOString(),
|
|
6976
|
+
tos_accepted_version: null,
|
|
6977
|
+
tos_accepted_at: null,
|
|
6978
|
+
email: result.email,
|
|
6979
|
+
user_id: result.user_id,
|
|
6980
|
+
...ignoredEnvApiKey ? { ignore_env_api_key: true } : {}
|
|
6981
|
+
});
|
|
6982
|
+
process.env.UNBROWSE_API_KEY = result.api_key;
|
|
6983
|
+
info(`Signed in as ${result.email}. API key saved to ~/.unbrowse/config.json.`);
|
|
6984
|
+
info("Open your dashboard:");
|
|
6985
|
+
info(" unbrowse dashboard");
|
|
6986
|
+
try {
|
|
6987
|
+
const serverPrefs = await fetchAccountPreferences();
|
|
6988
|
+
if (serverPrefs) {
|
|
6989
|
+
setContributionConfig2({
|
|
6990
|
+
contribution: { share_pointers: serverPrefs.share_pointers, set_via: "mode-command" }
|
|
6991
|
+
});
|
|
6992
|
+
info(`Auto-publish to marketplace: ${serverPrefs.share_pointers ? "ON" : "off"} (synced from your account).`);
|
|
6993
|
+
}
|
|
6994
|
+
} catch {}
|
|
6995
|
+
stopServerAfterReset();
|
|
6996
|
+
return;
|
|
6997
|
+
}
|
|
6998
|
+
if (!reset && getApiKey()) {
|
|
6249
6999
|
info("Already registered. API key loaded from env or ~/.unbrowse/config.json");
|
|
6250
7000
|
return;
|
|
6251
7001
|
}
|
|
6252
7002
|
await ensureRegistered({ promptForEmail: !flags["no-prompt"], exitOnFailure: false });
|
|
6253
7003
|
if (getApiKey()) {
|
|
6254
7004
|
info("Registration complete. You can now publish skills and check earnings.");
|
|
7005
|
+
stopServerAfterReset();
|
|
6255
7006
|
} else {
|
|
6256
7007
|
info("Registration skipped or failed. Unbrowse still works locally \u2014 publish/earnings are disabled.");
|
|
6257
7008
|
}
|
|
@@ -6541,6 +7292,10 @@ async function main() {
|
|
|
6541
7292
|
await cmdMode(flags);
|
|
6542
7293
|
return;
|
|
6543
7294
|
}
|
|
7295
|
+
if (command === "account")
|
|
7296
|
+
return cmdAccount(flags);
|
|
7297
|
+
if (command === "dashboard")
|
|
7298
|
+
return cmdDashboard(flags);
|
|
6544
7299
|
if (command === "mcp")
|
|
6545
7300
|
return cmdMcp(flags);
|
|
6546
7301
|
if (command === "status")
|
|
@@ -6565,6 +7320,7 @@ async function main() {
|
|
|
6565
7320
|
return cmdSessionsScan(flags);
|
|
6566
7321
|
if (command === "register")
|
|
6567
7322
|
return cmdRegister(flags);
|
|
7323
|
+
await refreshContributionPreferenceFromServer(false);
|
|
6568
7324
|
const KNOWN_COMMANDS = new Set([
|
|
6569
7325
|
"health",
|
|
6570
7326
|
"mcp",
|
|
@@ -6619,6 +7375,8 @@ async function main() {
|
|
|
6619
7375
|
"cache-clear",
|
|
6620
7376
|
"register",
|
|
6621
7377
|
"mode",
|
|
7378
|
+
"account",
|
|
7379
|
+
"dashboard",
|
|
6622
7380
|
"capture"
|
|
6623
7381
|
]);
|
|
6624
7382
|
if (!KNOWN_COMMANDS.has(command)) {
|
|
@@ -6735,8 +7493,14 @@ async function main() {
|
|
|
6735
7493
|
return cmdRegister(flags);
|
|
6736
7494
|
case "mode":
|
|
6737
7495
|
return cmdMode(flags);
|
|
7496
|
+
case "account":
|
|
7497
|
+
return cmdAccount(flags);
|
|
7498
|
+
case "dashboard":
|
|
7499
|
+
return cmdDashboard(flags);
|
|
6738
7500
|
case "capture":
|
|
6739
7501
|
return cmdCapture(flags);
|
|
7502
|
+
case "note":
|
|
7503
|
+
return cmdNote(flags, args);
|
|
6740
7504
|
default:
|
|
6741
7505
|
info(`Unknown command: ${command}`);
|
|
6742
7506
|
printHelp();
|