unbrowse 3.3.4 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +128 -5
- package/dist/mcp.js +30 -5
- package/dist/server.js +149 -26
- package/package.json +1 -1
- package/scripts/postinstall.mjs +10 -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 = "3.
|
|
34
|
+
var BUILD_RELEASE_VERSION = "3.4.0", BUILD_GIT_SHA = "361b3d271f3d", BUILD_CODE_HASH = "656382fbb5d5", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy40LjAiLCJnaXRfc2hhIjoiMzYxYjNkMjcxZjNkIiwiY29kZV9oYXNoIjoiNjU2MzgyZmJiNWQ1IiwidHJhY2VfdmVyc2lvbiI6IjY1NjM4MmZiYjVkNUAzNjFiM2QyNzFmM2QiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA5VDAyOjUwOjI0LjI0NVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "_GYe4ccws1jOZQ13TD27_rBJwKd87JDzsXDQLQR3mZU", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
35
35
|
|
|
36
36
|
// ../../src/version.ts
|
|
37
37
|
import { createHash } from "crypto";
|
|
@@ -753,9 +753,16 @@ var init_reverse_engineer = __esm(() => {
|
|
|
753
753
|
"sec-ch-ua",
|
|
754
754
|
"sec-ch-ua-mobile",
|
|
755
755
|
"sec-ch-ua-platform",
|
|
756
|
+
"sec-ch-ua-full-version-list",
|
|
757
|
+
"sec-ch-ua-arch",
|
|
758
|
+
"sec-ch-ua-bitness",
|
|
759
|
+
"sec-ch-ua-model",
|
|
760
|
+
"sec-ch-ua-wow64",
|
|
756
761
|
"sec-fetch-dest",
|
|
757
762
|
"sec-fetch-mode",
|
|
758
763
|
"sec-fetch-site",
|
|
764
|
+
"sec-fetch-user",
|
|
765
|
+
"upgrade-insecure-requests",
|
|
759
766
|
"x-requested-with"
|
|
760
767
|
]);
|
|
761
768
|
AD_SCHEMA_KEYS = new Set([
|
|
@@ -1383,6 +1390,7 @@ import { join as join4 } from "path";
|
|
|
1383
1390
|
import { homedir as homedir3, hostname, release as osRelease } from "os";
|
|
1384
1391
|
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
1385
1392
|
import { createInterface } from "readline";
|
|
1393
|
+
import { execSync } from "child_process";
|
|
1386
1394
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
1387
1395
|
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
1388
1396
|
var recentLocalSkills = new Map;
|
|
@@ -1746,6 +1754,30 @@ async function apiRequest(method, path, body, opts) {
|
|
|
1746
1754
|
console.warn("[unbrowse] Please restart the unbrowse service to accept the new terms.");
|
|
1747
1755
|
throw new Error("ToS update required. Restart unbrowse to accept new terms.");
|
|
1748
1756
|
}
|
|
1757
|
+
if (res.status === 426 && !opts?.skipAutoUpdate) {
|
|
1758
|
+
const errCode = data.error;
|
|
1759
|
+
if (errCode === "client_update_required" || errCode === "client_verification_failed") {
|
|
1760
|
+
console.warn(`
|
|
1761
|
+
[unbrowse] Server requires a client update (${errCode}).`);
|
|
1762
|
+
console.warn("[unbrowse] Attempting automatic update...");
|
|
1763
|
+
try {
|
|
1764
|
+
const updateCmd = process.env.UNBROWSE_UPDATE_COMMAND || "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
1765
|
+
execSync(updateCmd, { stdio: "inherit", timeout: 120000 });
|
|
1766
|
+
console.warn("[unbrowse] Update installed. Restarting server...");
|
|
1767
|
+
try {
|
|
1768
|
+
execSync("pkill -9 -f 'unbrowse|kuri'", { stdio: "ignore", timeout: 5000 });
|
|
1769
|
+
} catch {}
|
|
1770
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
1771
|
+
console.warn("[unbrowse] Retrying request with updated client...");
|
|
1772
|
+
return apiRequest(method, path, body, { ...opts, skipAutoUpdate: true });
|
|
1773
|
+
} catch (updateErr) {
|
|
1774
|
+
console.warn(`[unbrowse] Auto-update failed: ${updateErr.message}`);
|
|
1775
|
+
const cmd = data.update_command ?? "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
1776
|
+
console.warn(`[unbrowse] Please update manually: ${cmd}`);
|
|
1777
|
+
throw new Error(`Client update required. Run: ${cmd}`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1749
1781
|
if (res.status === 402) {
|
|
1750
1782
|
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
1751
1783
|
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
|
@@ -3629,6 +3661,53 @@ async function cmdExecute(flags) {
|
|
|
3629
3661
|
endpoint_id: typeof flags.endpoint === "string" ? flags.endpoint : null
|
|
3630
3662
|
}
|
|
3631
3663
|
});
|
|
3664
|
+
if (flags.curl) {
|
|
3665
|
+
const endpointId = typeof flags.endpoint === "string" ? flags.endpoint : undefined;
|
|
3666
|
+
if (!endpointId)
|
|
3667
|
+
die("--curl requires --endpoint");
|
|
3668
|
+
const skill = await api2("GET", `/v1/skills/${skillId}`);
|
|
3669
|
+
const ep = (skill.endpoints ?? []).find((e) => e.endpoint_id === endpointId);
|
|
3670
|
+
if (!ep)
|
|
3671
|
+
die(`Endpoint ${endpointId} not found`);
|
|
3672
|
+
let method = ep.method ?? "GET";
|
|
3673
|
+
let url = ep.url_template;
|
|
3674
|
+
let headers = { ...ep.headers_template ?? {} };
|
|
3675
|
+
let body = ep.body;
|
|
3676
|
+
try {
|
|
3677
|
+
const preview = await api2("GET", `/v1/skills/${skillId}/request-preview/${endpointId}`);
|
|
3678
|
+
if (preview.headers)
|
|
3679
|
+
headers = preview.headers;
|
|
3680
|
+
if (preview.body)
|
|
3681
|
+
body = preview.body;
|
|
3682
|
+
if (preview.url)
|
|
3683
|
+
url = preview.url;
|
|
3684
|
+
if (preview.method)
|
|
3685
|
+
method = preview.method;
|
|
3686
|
+
} catch {}
|
|
3687
|
+
const parts = ["curl"];
|
|
3688
|
+
if (method !== "GET")
|
|
3689
|
+
parts.push(`-X ${method}`);
|
|
3690
|
+
parts.push(`'${url}'`);
|
|
3691
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
3692
|
+
if (/^(newrelic|traceparent|tracestate)$/i.test(k))
|
|
3693
|
+
continue;
|
|
3694
|
+
parts.push(`-H '${k}: ${v}'`);
|
|
3695
|
+
}
|
|
3696
|
+
if (body) {
|
|
3697
|
+
if (!headers["content-type"] && !headers["Content-Type"])
|
|
3698
|
+
parts.push(`-H 'Content-Type: application/json'`);
|
|
3699
|
+
parts.push(`-d '${JSON.stringify(body)}'`);
|
|
3700
|
+
}
|
|
3701
|
+
output({
|
|
3702
|
+
curl: parts.join(" \\\n "),
|
|
3703
|
+
endpoint_id: endpointId,
|
|
3704
|
+
method,
|
|
3705
|
+
url,
|
|
3706
|
+
...body ? { body } : {},
|
|
3707
|
+
headers
|
|
3708
|
+
}, !!flags.pretty);
|
|
3709
|
+
return;
|
|
3710
|
+
}
|
|
3632
3711
|
try {
|
|
3633
3712
|
const body = { params: {} };
|
|
3634
3713
|
if (flags.endpoint) {
|
|
@@ -3988,6 +4067,49 @@ async function cmdSetup(flags) {
|
|
|
3988
4067
|
output(report, true);
|
|
3989
4068
|
if (report.browser_engine.action === "failed")
|
|
3990
4069
|
process.exit(1);
|
|
4070
|
+
try {
|
|
4071
|
+
info("Trying your first resolve...");
|
|
4072
|
+
const demoUrl = "https://jsonplaceholder.typicode.com";
|
|
4073
|
+
const demoIntent = "list all posts";
|
|
4074
|
+
await recordFunnelTelemetryEvent("resolve_started", {
|
|
4075
|
+
source: "setup",
|
|
4076
|
+
hostType,
|
|
4077
|
+
properties: { command: "guided-first-resolve", intent: demoIntent, url: demoUrl }
|
|
4078
|
+
});
|
|
4079
|
+
const resolveResult = await api2("POST", "/v1/intent/resolve", {
|
|
4080
|
+
intent: demoIntent,
|
|
4081
|
+
params: { url: demoUrl },
|
|
4082
|
+
context: { url: demoUrl },
|
|
4083
|
+
projection: { raw: true }
|
|
4084
|
+
});
|
|
4085
|
+
if (isResolveSuccessResult(resolveResult)) {
|
|
4086
|
+
await recordFunnelTelemetryEvent("resolve_completed", {
|
|
4087
|
+
source: "setup",
|
|
4088
|
+
hostType,
|
|
4089
|
+
properties: { command: "guided-first-resolve", intent: demoIntent, url: demoUrl, source: resolveResult.source }
|
|
4090
|
+
});
|
|
4091
|
+
const endpoints = resolveResult.available_endpoints;
|
|
4092
|
+
if (endpoints && endpoints.length > 0) {
|
|
4093
|
+
info(`Found ${endpoints.length} API endpoint${endpoints.length > 1 ? "s" : ""} on ${demoUrl}:`);
|
|
4094
|
+
for (const ep of endpoints.slice(0, 5)) {
|
|
4095
|
+
const method = ep.method ?? "GET";
|
|
4096
|
+
const desc = ep.description ?? ep.url_template ?? ep.endpoint_id ?? "";
|
|
4097
|
+
info(` ${method} ${desc}`);
|
|
4098
|
+
}
|
|
4099
|
+
} else {
|
|
4100
|
+
info(`Resolve succeeded on ${demoUrl}`);
|
|
4101
|
+
}
|
|
4102
|
+
info("");
|
|
4103
|
+
info("That's unbrowse. Try your own:");
|
|
4104
|
+
info(' unbrowse resolve --intent "search for shoes" --url "https://amazon.com"');
|
|
4105
|
+
} else {
|
|
4106
|
+
info("Guided resolve returned no results \u2014 try manually:");
|
|
4107
|
+
info(' unbrowse resolve --intent "list posts" --url "https://jsonplaceholder.typicode.com"');
|
|
4108
|
+
}
|
|
4109
|
+
} catch {
|
|
4110
|
+
info("Setup complete. Try your first resolve:");
|
|
4111
|
+
info(' unbrowse resolve --intent "list posts" --url "https://jsonplaceholder.typicode.com"');
|
|
4112
|
+
}
|
|
3991
4113
|
}
|
|
3992
4114
|
var CLI_REFERENCE = {
|
|
3993
4115
|
commands: [
|
|
@@ -4044,6 +4166,7 @@ var CLI_REFERENCE = {
|
|
|
4044
4166
|
{ flag: "--limit N", desc: "Cap array output to N items" },
|
|
4045
4167
|
{ flag: "--endpoint-id ID", desc: "Pick a specific endpoint" },
|
|
4046
4168
|
{ flag: "--dry-run", desc: "Preview mutations" },
|
|
4169
|
+
{ flag: "--curl", desc: "Output the captured request as a curl command (no execution)" },
|
|
4047
4170
|
{ flag: "--params '{...}'", desc: "Extra params as JSON" }
|
|
4048
4171
|
],
|
|
4049
4172
|
examples: [
|
|
@@ -4542,7 +4665,7 @@ async function cmdClose(flags) {
|
|
|
4542
4665
|
output(await api2("POST", "/v1/browse/close", typeof flags.session === "string" ? { session_id: flags.session } : undefined), false);
|
|
4543
4666
|
}
|
|
4544
4667
|
async function cmdConnectChrome() {
|
|
4545
|
-
const { execSync, spawn: spawnProc } = __require("child_process");
|
|
4668
|
+
const { execSync: execSync2, spawn: spawnProc } = __require("child_process");
|
|
4546
4669
|
try {
|
|
4547
4670
|
const res = await fetch("http://127.0.0.1:9222/json/version", { signal: AbortSignal.timeout(1000) });
|
|
4548
4671
|
if (res.ok) {
|
|
@@ -4555,16 +4678,16 @@ async function cmdConnectChrome() {
|
|
|
4555
4678
|
}
|
|
4556
4679
|
} catch {}
|
|
4557
4680
|
try {
|
|
4558
|
-
|
|
4681
|
+
execSync2("pkill -f kuri/chrome-profile", { stdio: "ignore" });
|
|
4559
4682
|
} catch {}
|
|
4560
4683
|
console.log("Quitting Chrome to relaunch with remote debugging...");
|
|
4561
4684
|
if (process.platform === "darwin") {
|
|
4562
4685
|
try {
|
|
4563
|
-
|
|
4686
|
+
execSync2('osascript -e "quit app \\"Google Chrome\\""', { stdio: "ignore", timeout: 5000 });
|
|
4564
4687
|
} catch {}
|
|
4565
4688
|
} else {
|
|
4566
4689
|
try {
|
|
4567
|
-
|
|
4690
|
+
execSync2("pkill -f chrome", { stdio: "ignore" });
|
|
4568
4691
|
} catch {}
|
|
4569
4692
|
}
|
|
4570
4693
|
await new Promise((r) => setTimeout(r, 2000));
|
package/dist/mcp.js
CHANGED
|
@@ -225,11 +225,11 @@ import { dirname, join, parse } from "path";
|
|
|
225
225
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
226
226
|
|
|
227
227
|
// ../../src/build-info.generated.ts
|
|
228
|
-
var BUILD_RELEASE_VERSION = "3.
|
|
229
|
-
var BUILD_GIT_SHA = "
|
|
230
|
-
var BUILD_CODE_HASH = "
|
|
231
|
-
var BUILD_RELEASE_MANIFEST_BASE64 = "
|
|
232
|
-
var BUILD_RELEASE_MANIFEST_SIGNATURE = "
|
|
228
|
+
var BUILD_RELEASE_VERSION = "3.4.0";
|
|
229
|
+
var BUILD_GIT_SHA = "361b3d271f3d";
|
|
230
|
+
var BUILD_CODE_HASH = "656382fbb5d5";
|
|
231
|
+
var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy40LjAiLCJnaXRfc2hhIjoiMzYxYjNkMjcxZjNkIiwiY29kZV9oYXNoIjoiNjU2MzgyZmJiNWQ1IiwidHJhY2VfdmVyc2lvbiI6IjY1NjM4MmZiYjVkNUAzNjFiM2QyNzFmM2QiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA5VDAyOjUwOjI0LjI0NVoifQ";
|
|
232
|
+
var BUILD_RELEASE_MANIFEST_SIGNATURE = "_GYe4ccws1jOZQ13TD27_rBJwKd87JDzsXDQLQR3mZU";
|
|
233
233
|
var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
234
234
|
|
|
235
235
|
// ../../src/version.ts
|
|
@@ -748,6 +748,7 @@ function readImpactSummary() {
|
|
|
748
748
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
749
749
|
import { join as join5 } from "path";
|
|
750
750
|
import { homedir as homedir4, hostname, release as osRelease } from "os";
|
|
751
|
+
import { execSync } from "child_process";
|
|
751
752
|
|
|
752
753
|
// ../../src/payments/cascade.ts
|
|
753
754
|
import bs58 from "bs58";
|
|
@@ -856,6 +857,30 @@ async function apiRequest(method, path4, body, opts) {
|
|
|
856
857
|
console.warn("[unbrowse] Please restart the unbrowse service to accept the new terms.");
|
|
857
858
|
throw new Error("ToS update required. Restart unbrowse to accept new terms.");
|
|
858
859
|
}
|
|
860
|
+
if (res.status === 426 && !opts?.skipAutoUpdate) {
|
|
861
|
+
const errCode = data.error;
|
|
862
|
+
if (errCode === "client_update_required" || errCode === "client_verification_failed") {
|
|
863
|
+
console.warn(`
|
|
864
|
+
[unbrowse] Server requires a client update (${errCode}).`);
|
|
865
|
+
console.warn("[unbrowse] Attempting automatic update...");
|
|
866
|
+
try {
|
|
867
|
+
const updateCmd = process.env.UNBROWSE_UPDATE_COMMAND || "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
868
|
+
execSync(updateCmd, { stdio: "inherit", timeout: 120000 });
|
|
869
|
+
console.warn("[unbrowse] Update installed. Restarting server...");
|
|
870
|
+
try {
|
|
871
|
+
execSync("pkill -9 -f 'unbrowse|kuri'", { stdio: "ignore", timeout: 5000 });
|
|
872
|
+
} catch {}
|
|
873
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
874
|
+
console.warn("[unbrowse] Retrying request with updated client...");
|
|
875
|
+
return apiRequest(method, path4, body, { ...opts, skipAutoUpdate: true });
|
|
876
|
+
} catch (updateErr) {
|
|
877
|
+
console.warn(`[unbrowse] Auto-update failed: ${updateErr.message}`);
|
|
878
|
+
const cmd = data.update_command ?? "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
879
|
+
console.warn(`[unbrowse] Please update manually: ${cmd}`);
|
|
880
|
+
throw new Error(`Client update required. Run: ${cmd}`);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
859
884
|
if (res.status === 402) {
|
|
860
885
|
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
861
886
|
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
package/dist/server.js
CHANGED
|
@@ -788,7 +788,14 @@ async function findReusableIdleTab(state = defaultBrokerState) {
|
|
|
788
788
|
try {
|
|
789
789
|
const tabs = await kuriGet(state, "/tabs");
|
|
790
790
|
const candidate = tabs.find((tab) => /^(about:blank|chrome:\/\/newtab\/?)$/i.test(tab?.url ?? ""));
|
|
791
|
-
|
|
791
|
+
if (!candidate?.id)
|
|
792
|
+
return "";
|
|
793
|
+
try {
|
|
794
|
+
await kuriGet(state, "/evaluate", { tab_id: candidate.id, expression: "1" });
|
|
795
|
+
return candidate.id;
|
|
796
|
+
} catch {
|
|
797
|
+
return "";
|
|
798
|
+
}
|
|
792
799
|
} catch {
|
|
793
800
|
return "";
|
|
794
801
|
}
|
|
@@ -4265,7 +4272,15 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4265
4272
|
}).filter(Boolean)) : new Map;
|
|
4266
4273
|
for (const { req } of scored) {
|
|
4267
4274
|
const normalized = normalizeUrl(req.url);
|
|
4268
|
-
|
|
4275
|
+
let key = `${req.method}:${normalized}`;
|
|
4276
|
+
if (/graphql/i.test(req.url) && req.method !== "GET" && req.request_body) {
|
|
4277
|
+
try {
|
|
4278
|
+
const parsed = JSON.parse(req.request_body);
|
|
4279
|
+
const opName = parsed.operationName ?? parsed.query?.match(/(?:query|mutation)\s+(\w+)/)?.[1];
|
|
4280
|
+
if (opName)
|
|
4281
|
+
key += `#op=${opName}`;
|
|
4282
|
+
} catch {}
|
|
4283
|
+
}
|
|
4269
4284
|
if (seen.has(key))
|
|
4270
4285
|
continue;
|
|
4271
4286
|
seen.add(key);
|
|
@@ -5006,9 +5021,16 @@ var init_reverse_engineer = __esm(() => {
|
|
|
5006
5021
|
"sec-ch-ua",
|
|
5007
5022
|
"sec-ch-ua-mobile",
|
|
5008
5023
|
"sec-ch-ua-platform",
|
|
5024
|
+
"sec-ch-ua-full-version-list",
|
|
5025
|
+
"sec-ch-ua-arch",
|
|
5026
|
+
"sec-ch-ua-bitness",
|
|
5027
|
+
"sec-ch-ua-model",
|
|
5028
|
+
"sec-ch-ua-wow64",
|
|
5009
5029
|
"sec-fetch-dest",
|
|
5010
5030
|
"sec-fetch-mode",
|
|
5011
5031
|
"sec-fetch-site",
|
|
5032
|
+
"sec-fetch-user",
|
|
5033
|
+
"upgrade-insecure-requests",
|
|
5012
5034
|
"x-requested-with"
|
|
5013
5035
|
]);
|
|
5014
5036
|
SENSITIVE_HEADER_PATTERN = /token|key|secret|credential|password|session/i;
|
|
@@ -5547,7 +5569,7 @@ async function collectInterceptedRequests(tabId) {
|
|
|
5547
5569
|
}
|
|
5548
5570
|
async function injectInterceptor(tabId) {
|
|
5549
5571
|
const SETUP = `(function(){if(window.__unbrowse_interceptor_installed)return;window.__unbrowse_interceptor_installed=true;window.__unbrowse_intercepted=[];window.__UB_MAX=2*1024*1024;window.__UB_MAX_JS=2*1024*1024;window.__UB_MAX_N=500;})()`;
|
|
5550
|
-
const FETCH_PATCH = `(function(){if(!window.__unbrowse_interceptor_installed)return;var M=window.__UB_MAX,MJ=window.__UB_MAX_JS,MN=window.__UB_MAX_N;var oF=window.fetch;window.fetch=function(){var a=arguments,
|
|
5572
|
+
const FETCH_PATCH = `(function(){if(!window.__unbrowse_interceptor_installed)return;var M=window.__UB_MAX,MJ=window.__UB_MAX_JS,MN=window.__UB_MAX_N;var oF=window.fetch;window.fetch=function(){var a=arguments,isR=a[0]&&typeof a[0]==='object'&&typeof a[0].url==='string',u=typeof a[0]==='string'?a[0]:(isR?a[0].url:''),o=a[1]||{},m=(o.method||(isR?a[0].method:null)||'GET').toUpperCase(),rb=o.body?String(o.body).substring(0,M):void 0,rbP=null;if(!rb&&isR&&m!=='GET'&&m!=='HEAD'){try{var rc=a[0].clone();rbP=rc.text().then(function(t){return t?t.substring(0,M):void 0}).catch(function(){return void 0})}catch(e){}}var rh={};if(isR&&a[0].headers&&typeof a[0].headers.forEach==='function')a[0].headers.forEach(function(v,k){rh[k]=v});if(o.headers){if(typeof o.headers.forEach==='function')o.headers.forEach(function(v,k){rh[k]=v});else Object.keys(o.headers).forEach(function(k){rh[k]=o.headers[k]})}return oF.apply(this,a).then(function(r){if(window.__unbrowse_intercepted.length>=MN)return r;var ct=r.headers.get('content-type')||'';var isJ=ct.indexOf('javascript')!==-1||/\\.js(\\?|$)/.test(u);var isD=ct.indexOf('json')!==-1||ct.indexOf('x-protobuf')!==-1||ct.indexOf('text/plain')!==-1||u.indexOf('/api/')!==-1||u.indexOf('graphql')!==-1||u.indexOf('voyager')!==-1;if(!isJ&&!isD)return r;if(/\\.(css|woff2?|png|jpg|svg|ico)(\\?|$)/.test(u))return r;var c=r.clone();Promise.all([c.text(),rbP?rbP:Promise.resolve(rb)]).then(function(res){var b=res[0],rrb=res[1];var lim=isJ?MJ:M;if(b.length>lim)return;var rr={};r.headers.forEach(function(v,k){rr[k]=v});window.__unbrowse_intercepted.push({url:u,method:m,request_headers:rh,request_body:rrb,response_status:r.status,response_headers:rr,response_body:b,content_type:ct,is_js:isJ,timestamp:new Date().toISOString()})}).catch(function(){});return r}).catch(function(e){throw e})}})()`;
|
|
5551
5573
|
const XHR_PATCH = `(function(){if(!window.__unbrowse_interceptor_installed)return;var M=window.__UB_MAX,MJ=window.__UB_MAX_JS,MN=window.__UB_MAX_N;var oO=XMLHttpRequest.prototype.open,oS=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(m,u){this.__ub_m=m;this.__ub_u=u;this.__ub_h={};var oSH=this.setRequestHeader.bind(this);this.setRequestHeader=function(k,v){this.__ub_h[k]=v;oSH(k,v)}.bind(this);return oO.apply(this,arguments)};XMLHttpRequest.prototype.send=function(b){var x=this;x.addEventListener('load',function(){if(window.__unbrowse_intercepted.length>=MN)return;var ct=x.getResponseHeader('content-type')||'',u=x.__ub_u||'';var isJ=ct.indexOf('javascript')!==-1||/\\.js(\\?|$)/.test(u);var isD=ct.indexOf('json')!==-1||ct.indexOf('x-protobuf')!==-1||ct.indexOf('text/plain')!==-1||u.indexOf('/api/')!==-1||u.indexOf('graphql')!==-1||u.indexOf('voyager')!==-1;if(!isJ&&!isD)return;if(/\\.(css|woff2?|png|jpg|svg|ico)(\\?|$)/.test(u))return;var rb=x.responseText||'';var lim=isJ?MJ:M;if(rb.length>lim)return;window.__unbrowse_intercepted.push({url:u,method:(x.__ub_m||'GET').toUpperCase(),request_headers:x.__ub_h||{},request_body:b?String(b).substring(0,M):void 0,response_status:x.status,response_headers:{},response_body:rb,content_type:ct,is_js:isJ,timestamp:new Date().toISOString()})});return oS.apply(this,arguments)}})()`;
|
|
5552
5574
|
for (const chunk of [SETUP, FETCH_PATCH, XHR_PATCH]) {
|
|
5553
5575
|
await evaluate(tabId, chunk).catch(() => {});
|
|
@@ -5555,10 +5577,22 @@ async function injectInterceptor(tabId) {
|
|
|
5555
5577
|
}
|
|
5556
5578
|
function mergePassiveCaptureData(intercepted, harEntries, extensionEntries, responseBodies, performanceUrls = []) {
|
|
5557
5579
|
const seen = new Map;
|
|
5580
|
+
function graphqlDedup(url, requestBody) {
|
|
5581
|
+
if (!requestBody || !/graphql/i.test(url))
|
|
5582
|
+
return url;
|
|
5583
|
+
try {
|
|
5584
|
+
const parsed = JSON.parse(requestBody);
|
|
5585
|
+
const opName = parsed.operationName ?? parsed.query?.match(/(?:query|mutation)\s+(\w+)/)?.[1];
|
|
5586
|
+
if (opName)
|
|
5587
|
+
return `${url}#op=${opName}`;
|
|
5588
|
+
} catch {}
|
|
5589
|
+
return url;
|
|
5590
|
+
}
|
|
5558
5591
|
for (const entry of intercepted) {
|
|
5559
5592
|
if (entry.is_js)
|
|
5560
5593
|
continue;
|
|
5561
|
-
|
|
5594
|
+
const key = graphqlDedup(entry.url, entry.request_body);
|
|
5595
|
+
seen.set(key, {
|
|
5562
5596
|
url: entry.url,
|
|
5563
5597
|
method: entry.method,
|
|
5564
5598
|
request_headers: entry.request_headers,
|
|
@@ -5571,21 +5605,25 @@ function mergePassiveCaptureData(intercepted, harEntries, extensionEntries, resp
|
|
|
5571
5605
|
}
|
|
5572
5606
|
for (const entry of harEntries) {
|
|
5573
5607
|
const url = entry.request?.url;
|
|
5574
|
-
if (!url
|
|
5608
|
+
if (!url)
|
|
5575
5609
|
continue;
|
|
5576
5610
|
if (entry.request.method === "OPTIONS")
|
|
5577
5611
|
continue;
|
|
5612
|
+
const postBody = entry.request.postData?.text;
|
|
5613
|
+
const key = graphqlDedup(url, postBody);
|
|
5614
|
+
if (seen.has(key))
|
|
5615
|
+
continue;
|
|
5578
5616
|
const reqHeaders = {};
|
|
5579
5617
|
for (const h of entry.request.headers ?? [])
|
|
5580
5618
|
reqHeaders[h.name] = h.value;
|
|
5581
5619
|
const respHeaders = {};
|
|
5582
5620
|
for (const h of entry.response.headers ?? [])
|
|
5583
5621
|
respHeaders[h.name] = h.value;
|
|
5584
|
-
seen.set(
|
|
5622
|
+
seen.set(key, {
|
|
5585
5623
|
url,
|
|
5586
5624
|
method: entry.request.method,
|
|
5587
5625
|
request_headers: reqHeaders,
|
|
5588
|
-
request_body:
|
|
5626
|
+
request_body: postBody,
|
|
5589
5627
|
response_status: entry.response.status,
|
|
5590
5628
|
response_headers: respHeaders,
|
|
5591
5629
|
response_body: responseBodies.get(url) ?? entry.response.content?.text,
|
|
@@ -5599,14 +5637,19 @@ function mergePassiveCaptureData(intercepted, harEntries, extensionEntries, resp
|
|
|
5599
5637
|
const respHeaders = {};
|
|
5600
5638
|
for (const h of entry.responseHeaders ?? [])
|
|
5601
5639
|
respHeaders[h.name] = h.value;
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
existing.request_headers[k]
|
|
5640
|
+
let merged = false;
|
|
5641
|
+
for (const [key, existing] of seen) {
|
|
5642
|
+
if (existing.url === entry.url) {
|
|
5643
|
+
for (const [k, v] of Object.entries(reqHeaders)) {
|
|
5644
|
+
if (!existing.request_headers[k])
|
|
5645
|
+
existing.request_headers[k] = v;
|
|
5646
|
+
}
|
|
5647
|
+
merged = true;
|
|
5648
|
+
break;
|
|
5607
5649
|
}
|
|
5608
|
-
continue;
|
|
5609
5650
|
}
|
|
5651
|
+
if (merged)
|
|
5652
|
+
continue;
|
|
5610
5653
|
seen.set(entry.url, {
|
|
5611
5654
|
url: entry.url,
|
|
5612
5655
|
method: entry.method,
|
|
@@ -6763,13 +6806,23 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6763
6806
|
var origFetch = window.fetch;
|
|
6764
6807
|
window.fetch = function() {
|
|
6765
6808
|
var args = arguments;
|
|
6766
|
-
var
|
|
6809
|
+
var isRequestObj = args[0] && typeof args[0] === 'object' && typeof args[0].url === 'string';
|
|
6810
|
+
var url = typeof args[0] === 'string' ? args[0] : (isRequestObj ? args[0].url : '');
|
|
6767
6811
|
var opts = args[1] || {};
|
|
6768
|
-
|
|
6812
|
+
// Extract method: prefer explicit opts, then Request object, then default GET
|
|
6813
|
+
var method = (opts.method || (isRequestObj ? args[0].method : null) || 'GET').toUpperCase();
|
|
6814
|
+
// Extract body: prefer explicit opts.body, then clone+read Request body
|
|
6769
6815
|
var reqBody = opts.body ? String(opts.body).substring(0, MAX_BODY) : undefined;
|
|
6816
|
+
var reqBodyPromise = null;
|
|
6817
|
+
if (!reqBody && isRequestObj && method !== 'GET' && method !== 'HEAD') {
|
|
6818
|
+
try {
|
|
6819
|
+
var reqClone = args[0].clone();
|
|
6820
|
+
reqBodyPromise = reqClone.text().then(function(t) { return t ? t.substring(0, MAX_BODY) : undefined; }).catch(function() { return undefined; });
|
|
6821
|
+
} catch(e) { /* clone failed */ }
|
|
6822
|
+
}
|
|
6770
6823
|
var reqHeaders = {};
|
|
6771
6824
|
// Extract headers from Request object (first arg)
|
|
6772
|
-
if (
|
|
6825
|
+
if (isRequestObj && args[0].headers && typeof args[0].headers.forEach === 'function') {
|
|
6773
6826
|
args[0].headers.forEach(function(v, k) { reqHeaders[k] = v; });
|
|
6774
6827
|
}
|
|
6775
6828
|
// Override/merge with explicit opts.headers (second arg)
|
|
@@ -6792,7 +6845,13 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6792
6845
|
if (!isJs && !isData) return response;
|
|
6793
6846
|
if (/\\.(css|woff2?|png|jpg|svg|ico)(\\?|$)/.test(url)) return response;
|
|
6794
6847
|
var clone = response.clone();
|
|
6795
|
-
clone
|
|
6848
|
+
// Resolve request body (from Request object clone) and response body in parallel
|
|
6849
|
+
Promise.all([
|
|
6850
|
+
clone.text(),
|
|
6851
|
+
reqBodyPromise ? reqBodyPromise : Promise.resolve(reqBody)
|
|
6852
|
+
]).then(function(results) {
|
|
6853
|
+
var body = results[0];
|
|
6854
|
+
var resolvedReqBody = results[1];
|
|
6796
6855
|
var limit = isJs ? MAX_JS_BODY : MAX_BODY;
|
|
6797
6856
|
if (body.length > limit) return;
|
|
6798
6857
|
var respHeaders = {};
|
|
@@ -6801,7 +6860,7 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6801
6860
|
url: url,
|
|
6802
6861
|
method: method,
|
|
6803
6862
|
request_headers: reqHeaders,
|
|
6804
|
-
request_body:
|
|
6863
|
+
request_body: resolvedReqBody,
|
|
6805
6864
|
response_status: response.status,
|
|
6806
6865
|
response_headers: respHeaders,
|
|
6807
6866
|
response_body: body,
|
|
@@ -6884,7 +6943,7 @@ var init_capture = __esm(async () => {
|
|
|
6884
6943
|
});
|
|
6885
6944
|
|
|
6886
6945
|
// ../../src/build-info.generated.ts
|
|
6887
|
-
var BUILD_RELEASE_VERSION = "3.
|
|
6946
|
+
var BUILD_RELEASE_VERSION = "3.4.0", BUILD_GIT_SHA = "361b3d271f3d", BUILD_CODE_HASH = "656382fbb5d5", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy40LjAiLCJnaXRfc2hhIjoiMzYxYjNkMjcxZjNkIiwiY29kZV9oYXNoIjoiNjU2MzgyZmJiNWQ1IiwidHJhY2VfdmVyc2lvbiI6IjY1NjM4MmZiYjVkNUAzNjFiM2QyNzFmM2QiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA5VDAyOjUwOjI0LjI0NVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "_GYe4ccws1jOZQ13TD27_rBJwKd87JDzsXDQLQR3mZU", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
6888
6947
|
|
|
6889
6948
|
// ../../src/version.ts
|
|
6890
6949
|
import { createHash } from "crypto";
|
|
@@ -7399,6 +7458,7 @@ import { join as join6 } from "path";
|
|
|
7399
7458
|
import { homedir as homedir4, hostname, release as osRelease } from "os";
|
|
7400
7459
|
import { randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
|
|
7401
7460
|
import { createInterface } from "readline";
|
|
7461
|
+
import { execSync } from "child_process";
|
|
7402
7462
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
7403
7463
|
const manifest = manifestBase64.trim();
|
|
7404
7464
|
const sig = signature.trim();
|
|
@@ -7772,6 +7832,30 @@ async function apiRequest(method, path4, body, opts) {
|
|
|
7772
7832
|
console.warn("[unbrowse] Please restart the unbrowse service to accept the new terms.");
|
|
7773
7833
|
throw new Error("ToS update required. Restart unbrowse to accept new terms.");
|
|
7774
7834
|
}
|
|
7835
|
+
if (res.status === 426 && !opts?.skipAutoUpdate) {
|
|
7836
|
+
const errCode = data.error;
|
|
7837
|
+
if (errCode === "client_update_required" || errCode === "client_verification_failed") {
|
|
7838
|
+
console.warn(`
|
|
7839
|
+
[unbrowse] Server requires a client update (${errCode}).`);
|
|
7840
|
+
console.warn("[unbrowse] Attempting automatic update...");
|
|
7841
|
+
try {
|
|
7842
|
+
const updateCmd = process.env.UNBROWSE_UPDATE_COMMAND || "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
7843
|
+
execSync(updateCmd, { stdio: "inherit", timeout: 120000 });
|
|
7844
|
+
console.warn("[unbrowse] Update installed. Restarting server...");
|
|
7845
|
+
try {
|
|
7846
|
+
execSync("pkill -9 -f 'unbrowse|kuri'", { stdio: "ignore", timeout: 5000 });
|
|
7847
|
+
} catch {}
|
|
7848
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
7849
|
+
console.warn("[unbrowse] Retrying request with updated client...");
|
|
7850
|
+
return apiRequest(method, path4, body, { ...opts, skipAutoUpdate: true });
|
|
7851
|
+
} catch (updateErr) {
|
|
7852
|
+
console.warn(`[unbrowse] Auto-update failed: ${updateErr.message}`);
|
|
7853
|
+
const cmd = data.update_command ?? "curl -fsSL https://unbrowse.ai/install.sh | bash";
|
|
7854
|
+
console.warn(`[unbrowse] Please update manually: ${cmd}`);
|
|
7855
|
+
throw new Error(`Client update required. Run: ${cmd}`);
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7775
7859
|
if (res.status === 402) {
|
|
7776
7860
|
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
7777
7861
|
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
|
@@ -14450,6 +14534,7 @@ __export(exports_execution, {
|
|
|
14450
14534
|
templatizeQueryParams: () => templatizeQueryParams,
|
|
14451
14535
|
shouldIgnoreLearnedBrowserStrategy: () => shouldIgnoreLearnedBrowserStrategy,
|
|
14452
14536
|
resolveExecutionUrlTemplate: () => resolveExecutionUrlTemplate,
|
|
14537
|
+
reloadExecutionAuthState: () => reloadExecutionAuthState,
|
|
14453
14538
|
rankEndpoints: () => rankEndpoints,
|
|
14454
14539
|
projectResultForIntent: () => projectResultForIntent,
|
|
14455
14540
|
isCanonicalReplayEndpoint: () => isCanonicalReplayEndpoint,
|
|
@@ -16245,10 +16330,6 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
16245
16330
|
...sessionHeaders,
|
|
16246
16331
|
...normalizeReplayHeaders(extraHeaders)
|
|
16247
16332
|
};
|
|
16248
|
-
delete headers["sec-ch-ua"];
|
|
16249
|
-
delete headers["sec-ch-ua-mobile"];
|
|
16250
|
-
delete headers["sec-ch-ua-platform"];
|
|
16251
|
-
delete headers["upgrade-insecure-requests"];
|
|
16252
16333
|
if (cookies.length > 0) {
|
|
16253
16334
|
const cookieStr = cookies.map((c) => {
|
|
16254
16335
|
const v = c.value.startsWith('"') && c.value.endsWith('"') ? c.value.slice(1, -1) : c.value;
|
|
@@ -22857,6 +22938,10 @@ async function resolveRequestedBrowseSession(sessions, client, requestedSessionI
|
|
|
22857
22938
|
const session = sessions.get(requestedSessionId);
|
|
22858
22939
|
if (!session)
|
|
22859
22940
|
throw new BrowseSessionError("session_not_found");
|
|
22941
|
+
if (!await isBrowseSessionLive(session, client)) {
|
|
22942
|
+
removeBrowseSession(sessions, requestedSessionId);
|
|
22943
|
+
throw new BrowseSessionError("session_expired");
|
|
22944
|
+
}
|
|
22860
22945
|
return session;
|
|
22861
22946
|
}
|
|
22862
22947
|
const live = await listLiveBrowseSessions(sessions, client);
|
|
@@ -22927,7 +23012,7 @@ async function getOrCreateNavigateBrowseSession(sessions, client, injectIntercep
|
|
|
22927
23012
|
}
|
|
22928
23013
|
return createBrowseSession(sessions, client, injectInterceptor2);
|
|
22929
23014
|
}
|
|
22930
|
-
var BrowseSessionError, RECOVERABLE_BROWSE_FAILURES, LIVE_CHECK_RETRIES =
|
|
23015
|
+
var BrowseSessionError, RECOVERABLE_BROWSE_FAILURES, LIVE_CHECK_RETRIES = 3, LIVE_CHECK_RETRY_DELAY_MS = 100, sessionQueues;
|
|
22931
23016
|
var init_browse_session = __esm(() => {
|
|
22932
23017
|
init_client();
|
|
22933
23018
|
BrowseSessionError = class BrowseSessionError extends Error {
|
|
@@ -25541,6 +25626,39 @@ async function registerRoutes(app) {
|
|
|
25541
25626
|
return reply.code(500).send({ error: err.message });
|
|
25542
25627
|
}
|
|
25543
25628
|
});
|
|
25629
|
+
app.get("/v1/skills/:skill_id/request-preview/:endpoint_id", async (req, reply) => {
|
|
25630
|
+
const { skill_id, endpoint_id } = req.params;
|
|
25631
|
+
const skill = await loadSkillForMutation(skill_id);
|
|
25632
|
+
if (!skill)
|
|
25633
|
+
return reply.status(404).send({ error: "skill_not_found" });
|
|
25634
|
+
const ep = skill.endpoints.find((e) => e.endpoint_id === endpoint_id);
|
|
25635
|
+
if (!ep)
|
|
25636
|
+
return reply.status(404).send({ error: "endpoint_not_found" });
|
|
25637
|
+
let epDomain;
|
|
25638
|
+
try {
|
|
25639
|
+
epDomain = new URL(ep.url_template).hostname;
|
|
25640
|
+
} catch {
|
|
25641
|
+
epDomain = skill.domain;
|
|
25642
|
+
}
|
|
25643
|
+
const authHeaders = {};
|
|
25644
|
+
const cookies = [];
|
|
25645
|
+
const { reloadExecutionAuthState: reloadExecutionAuthState2 } = await init_execution().then(() => exports_execution);
|
|
25646
|
+
await reloadExecutionAuthState2(skill, epDomain, authHeaders, cookies);
|
|
25647
|
+
const allHeaders = { ...ep.headers_template ?? {}, ...authHeaders };
|
|
25648
|
+
if (cookies.length > 0) {
|
|
25649
|
+
allHeaders["Cookie"] = cookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
25650
|
+
}
|
|
25651
|
+
return reply.send({
|
|
25652
|
+
method: ep.method,
|
|
25653
|
+
url: ep.url_template,
|
|
25654
|
+
headers: allHeaders,
|
|
25655
|
+
body: ep.body ?? null,
|
|
25656
|
+
query: ep.query ?? null,
|
|
25657
|
+
path_params: ep.path_params ?? null,
|
|
25658
|
+
cookies: cookies.length > 0 ? cookies.map((c) => ({ name: c.name, domain: c.domain })) : null,
|
|
25659
|
+
trigger_url: ep.trigger_url ?? null
|
|
25660
|
+
});
|
|
25661
|
+
});
|
|
25544
25662
|
app.post("/v1/auth/steal", { config: { rateLimit: { max: 30, timeWindow: "1 minute" } } }, async (req, reply) => {
|
|
25545
25663
|
const {
|
|
25546
25664
|
url,
|
|
@@ -25850,6 +25968,11 @@ async function registerRoutes(app) {
|
|
|
25850
25968
|
session2.url = typeof finalUrl === "string" && finalUrl.startsWith("http") ? finalUrl : url;
|
|
25851
25969
|
session2.domain = profileName(session2.url);
|
|
25852
25970
|
await injectInterceptor(session2.tabId);
|
|
25971
|
+
try {
|
|
25972
|
+
await broker.evaluate(session2.tabId, "window.scrollTo(0, document.body.scrollHeight)");
|
|
25973
|
+
await new Promise((r) => setTimeout(r, 2500));
|
|
25974
|
+
await broker.evaluate(session2.tabId, "window.scrollTo(0, 0)");
|
|
25975
|
+
} catch {}
|
|
25853
25976
|
const stillLive = await isBrowseSessionLive(session2, browseClient).catch(() => false);
|
|
25854
25977
|
if (!stillLive)
|
|
25855
25978
|
throw { error: "CDP command failed" };
|
|
@@ -26222,7 +26345,7 @@ await __promiseAll([
|
|
|
26222
26345
|
init_capture(),
|
|
26223
26346
|
init_stale_cleanup_runner()
|
|
26224
26347
|
]);
|
|
26225
|
-
import { execSync as
|
|
26348
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
26226
26349
|
import { mkdirSync as mkdirSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
|
|
26227
26350
|
import path5 from "node:path";
|
|
26228
26351
|
import Fastify from "fastify";
|
|
@@ -26254,7 +26377,7 @@ async function startUnbrowseServer(options = {}) {
|
|
|
26254
26377
|
const pidFile = options.pidFile ?? process.env.UNBROWSE_PID_FILE;
|
|
26255
26378
|
updatePidFile(pidFile, host, port);
|
|
26256
26379
|
try {
|
|
26257
|
-
|
|
26380
|
+
execSync3("pkill -f chrome-headless-shell", { stdio: "ignore" });
|
|
26258
26381
|
} catch {}
|
|
26259
26382
|
startBackgroundRegistration();
|
|
26260
26383
|
const app = Fastify({ logger: options.logger ?? true });
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -59,18 +59,15 @@ function ensureExecutable(filePath) {
|
|
|
59
59
|
ensureExecutable(wrapperPath);
|
|
60
60
|
ensureExecutable(launcherPath);
|
|
61
61
|
|
|
62
|
-
// Skip binary download in CI build environments — the release pipeline builds
|
|
63
|
-
// binaries AFTER install, so the download would always 404 and fail.
|
|
64
|
-
if (process.env.CI && (process.env.GITHUB_ACTIONS || process.env.UNBROWSE_SKIP_BINARY_DOWNLOAD)) {
|
|
65
|
-
process.exit(0);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
62
|
// Skip if binary already exists (re-install)
|
|
69
63
|
if (existsSync(binaryPath)) {
|
|
70
64
|
ensureExecutable(binaryPath);
|
|
65
|
+
console.log(`[unbrowse] Binary already exists, skipping.`);
|
|
71
66
|
process.exit(0);
|
|
72
67
|
}
|
|
73
68
|
|
|
69
|
+
// Local binary override — used by smoke tests to inject a pre-built binary.
|
|
70
|
+
// Must run BEFORE the CI skip so packaged smoke tests work in GitHub Actions.
|
|
74
71
|
if (localBinaryPath) {
|
|
75
72
|
if (!existsSync(localBinaryPath)) {
|
|
76
73
|
console.warn(`[unbrowse] Local binary override not found: ${localBinaryPath}`);
|
|
@@ -83,6 +80,13 @@ if (localBinaryPath) {
|
|
|
83
80
|
process.exit(0);
|
|
84
81
|
}
|
|
85
82
|
|
|
83
|
+
// Skip binary download in CI build environments — the release pipeline builds
|
|
84
|
+
// binaries AFTER install, so the download would always 404 and fail.
|
|
85
|
+
// Placed after the local binary override so smoke tests still work.
|
|
86
|
+
if (process.env.CI && (process.env.GITHUB_ACTIONS || process.env.UNBROWSE_SKIP_BINARY_DOWNLOAD)) {
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
86
90
|
const platform = process.platform; // darwin, linux
|
|
87
91
|
const arch = process.arch; // arm64, x64
|
|
88
92
|
const target = `${platform}-${arch}`;
|