unbrowse 3.7.0 → 3.8.0-preview.1
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 +60 -18
- package/dist/mcp.js +4 -4
- package/dist/server.js +279 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
|
|
|
31
31
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
32
32
|
|
|
33
33
|
// ../../src/build-info.generated.ts
|
|
34
|
-
var BUILD_RELEASE_VERSION = "3.
|
|
34
|
+
var BUILD_RELEASE_VERSION = "3.8.0-preview.1", BUILD_GIT_SHA = "5b9633fd977c", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy44LjAtcHJldmlldy4xIiwiZ2l0X3NoYSI6IjViOTYzM2ZkOTc3YyIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANWI5NjMzZmQ5NzdjIiwiaXNzdWVkX2F0IjoiMjAyNi0wNC0xMFQxNjo0NjoxMy4yMTFaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "CR3byJyWyomilicbi2WBO0kBuXqbATk49Z5ZP9B0lz8", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
35
35
|
|
|
36
36
|
// ../../src/version.ts
|
|
37
37
|
import { createHash } from "crypto";
|
|
@@ -1183,6 +1183,9 @@ async function getDefaultTabOn(state) {
|
|
|
1183
1183
|
async function start(port, state = defaultBrokerState) {
|
|
1184
1184
|
return startOn(state, port);
|
|
1185
1185
|
}
|
|
1186
|
+
async function stop(state = defaultBrokerState) {
|
|
1187
|
+
return stopOn(state);
|
|
1188
|
+
}
|
|
1186
1189
|
async function getDefaultTab(state = defaultBrokerState) {
|
|
1187
1190
|
return getDefaultTabOn(state);
|
|
1188
1191
|
}
|
|
@@ -3375,43 +3378,59 @@ async function bootstrapAgentMailKey() {
|
|
|
3375
3378
|
let currentUrl = await getCurrentUrl(tabId);
|
|
3376
3379
|
let onDashboard = typeof currentUrl === "string" && currentUrl.includes("/dashboard");
|
|
3377
3380
|
if (!onDashboard) {
|
|
3378
|
-
log("bootstrap-agentmail", "not logged in — looking for
|
|
3379
|
-
const
|
|
3380
|
-
// Clerk
|
|
3381
|
+
log("bootstrap-agentmail", "not logged in — looking for social OAuth");
|
|
3382
|
+
const clickResult = await evaluate(tabId, `(() => {
|
|
3383
|
+
// Clerk's social button class pattern
|
|
3384
|
+
const socialBtns = document.querySelectorAll('.cl-socialButtonsBlockButton, [data-provider]');
|
|
3385
|
+
for (const btn of socialBtns) {
|
|
3386
|
+
const text = (btn.textContent || '').toLowerCase();
|
|
3387
|
+
const provider = btn.getAttribute('data-provider') || '';
|
|
3388
|
+
// Match any social provider — Google, GitHub, etc.
|
|
3389
|
+
if (text || provider) {
|
|
3390
|
+
btn.click();
|
|
3391
|
+
return 'clicked:' + (provider || text.slice(0, 20));
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
// Fallback: look for any button with a known OAuth provider name
|
|
3381
3395
|
const btns = document.querySelectorAll('button, a');
|
|
3382
3396
|
for (const btn of btns) {
|
|
3383
3397
|
const text = (btn.textContent || '').toLowerCase();
|
|
3384
|
-
|
|
3385
|
-
if (text.includes('github') || ariaLabel.includes('github') ||
|
|
3386
|
-
btn.querySelector('img[alt*="github" i], svg[aria-label*="github" i]')) {
|
|
3398
|
+
if (text.includes('google') || text.includes('github') || text.includes('continue with')) {
|
|
3387
3399
|
btn.click();
|
|
3388
|
-
return 'clicked';
|
|
3400
|
+
return 'clicked:' + text.slice(0, 30);
|
|
3389
3401
|
}
|
|
3390
3402
|
}
|
|
3391
|
-
// Also check for social login containers
|
|
3392
|
-
const social = document.querySelector('.cl-socialButtonsBlockButton__github, [data-provider="github"]');
|
|
3393
|
-
if (social) { social.click(); return 'clicked'; }
|
|
3394
3403
|
return 'not_found';
|
|
3395
3404
|
})()`);
|
|
3396
|
-
|
|
3397
|
-
|
|
3405
|
+
const clickStr = typeof clickResult === "string" ? clickResult : String(clickResult);
|
|
3406
|
+
log("bootstrap-agentmail", `social button result: ${clickStr}`);
|
|
3407
|
+
if (clickStr.startsWith("clicked")) {
|
|
3408
|
+
log("bootstrap-agentmail", "clicked social OAuth — waiting for redirect");
|
|
3398
3409
|
const start2 = Date.now();
|
|
3399
3410
|
while (Date.now() - start2 < AUTH_TIMEOUT_MS) {
|
|
3400
3411
|
await new Promise((r) => setTimeout(r, 2000));
|
|
3401
3412
|
currentUrl = await getCurrentUrl(tabId);
|
|
3402
3413
|
if (typeof currentUrl === "string" && currentUrl.includes("/dashboard")) {
|
|
3403
3414
|
onDashboard = true;
|
|
3404
|
-
log("bootstrap-agentmail", "
|
|
3415
|
+
log("bootstrap-agentmail", "OAuth completed — on dashboard");
|
|
3405
3416
|
break;
|
|
3406
3417
|
}
|
|
3407
3418
|
}
|
|
3408
3419
|
}
|
|
3409
3420
|
if (!onDashboard) {
|
|
3410
3421
|
log("bootstrap-agentmail", "could not auto-login — manual setup needed");
|
|
3422
|
+
const pageInfo = await evaluate(tabId, `JSON.stringify({
|
|
3423
|
+
url: window.location.href,
|
|
3424
|
+
title: document.title,
|
|
3425
|
+
visible_buttons: Array.from(document.querySelectorAll('button,a'))
|
|
3426
|
+
.map(e => (e.textContent || '').trim().slice(0, 30))
|
|
3427
|
+
.filter(t => t)
|
|
3428
|
+
.slice(0, 10),
|
|
3429
|
+
})`).catch(() => "{}");
|
|
3411
3430
|
return {
|
|
3412
3431
|
success: false,
|
|
3413
3432
|
method: "manual",
|
|
3414
|
-
error:
|
|
3433
|
+
error: `Could not auto-login to AgentMail console. Page state: ${pageInfo}. Sign up at https://console.agentmail.to and create an API key manually, then: export AGENTMAIL_API_KEY=<key>`
|
|
3415
3434
|
};
|
|
3416
3435
|
}
|
|
3417
3436
|
}
|
|
@@ -3483,7 +3502,11 @@ async function bootstrapAgentMailKey() {
|
|
|
3483
3502
|
success: false,
|
|
3484
3503
|
error: "Reached dashboard but could not extract API key. Create one manually at https://console.agentmail.to/dashboard/api-keys"
|
|
3485
3504
|
};
|
|
3486
|
-
} finally {
|
|
3505
|
+
} finally {
|
|
3506
|
+
try {
|
|
3507
|
+
await stop();
|
|
3508
|
+
} catch {}
|
|
3509
|
+
}
|
|
3487
3510
|
}
|
|
3488
3511
|
function persistApiKeyToShell(apiKey) {
|
|
3489
3512
|
const shell = process.env.SHELL ?? "/bin/zsh";
|
|
@@ -6792,6 +6815,11 @@ async function cmdSetup(flags) {
|
|
|
6792
6815
|
properties: { command: "setup" }
|
|
6793
6816
|
});
|
|
6794
6817
|
info("Running setup checks");
|
|
6818
|
+
try {
|
|
6819
|
+
await ensureRegistered({ promptForEmail: false, exitOnFailure: false });
|
|
6820
|
+
} catch (err) {
|
|
6821
|
+
info(`[setup] background registration issue: ${err instanceof Error ? err.message : err}`);
|
|
6822
|
+
}
|
|
6795
6823
|
const report = await runSetup({
|
|
6796
6824
|
cwd: process.cwd(),
|
|
6797
6825
|
opencode: normalizeSetupScope(flags.opencode),
|
|
@@ -7603,8 +7631,22 @@ async function cmdLoginAuto(args, flags) {
|
|
|
7603
7631
|
}
|
|
7604
7632
|
})();
|
|
7605
7633
|
const { autonomousEmailLogin: autonomousEmailLogin3, isAgentMailAvailable: isAgentMailAvailable3 } = await init_agent_mail2().then(() => exports_agent_mail2);
|
|
7606
|
-
if (!isAgentMailAvailable3())
|
|
7607
|
-
|
|
7634
|
+
if (!isAgentMailAvailable3()) {
|
|
7635
|
+
info(`[login-auto] AGENTMAIL_API_KEY not set \u2014 attempting auto-bootstrap via console.agentmail.to`);
|
|
7636
|
+
try {
|
|
7637
|
+
const { bootstrapAgentMailKey: bootstrapAgentMailKey2 } = await init_bootstrap_agentmail().then(() => exports_bootstrap_agentmail);
|
|
7638
|
+
const bootstrap = await bootstrapAgentMailKey2();
|
|
7639
|
+
if (bootstrap.success && bootstrap.api_key) {
|
|
7640
|
+
process.env.AGENTMAIL_API_KEY = bootstrap.api_key;
|
|
7641
|
+
info(`[login-auto] bootstrapped AgentMail key via ${bootstrap.method ?? "browser"}`);
|
|
7642
|
+
} else {
|
|
7643
|
+
return die(`AGENTMAIL_API_KEY not set and bootstrap failed: ${bootstrap.error ?? "unknown"}. Get a key at https://agentmail.to and run: export AGENTMAIL_API_KEY=<key>`);
|
|
7644
|
+
}
|
|
7645
|
+
} catch (err) {
|
|
7646
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7647
|
+
return die(`AGENTMAIL_API_KEY not set and bootstrap errored: ${msg}. Get a key at https://agentmail.to and run: export AGENTMAIL_API_KEY=<key>`);
|
|
7648
|
+
}
|
|
7649
|
+
}
|
|
7608
7650
|
info(`[login-auto] creating agent email for ${domain}...`);
|
|
7609
7651
|
const session = await autonomousEmailLogin3(domain);
|
|
7610
7652
|
info(`[login-auto] email: ${session.email}`);
|
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 = "
|
|
228
|
+
var BUILD_RELEASE_VERSION = "3.8.0-preview.1";
|
|
229
|
+
var BUILD_GIT_SHA = "5b9633fd977c";
|
|
230
230
|
var BUILD_CODE_HASH = "5d9ebf619c61";
|
|
231
|
-
var BUILD_RELEASE_MANIFEST_BASE64 = "
|
|
232
|
-
var BUILD_RELEASE_MANIFEST_SIGNATURE = "
|
|
231
|
+
var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy44LjAtcHJldmlldy4xIiwiZ2l0X3NoYSI6IjViOTYzM2ZkOTc3YyIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANWI5NjMzZmQ5NzdjIiwiaXNzdWVkX2F0IjoiMjAyNi0wNC0xMFQxNjo0NjoxMy4yMTFaIn0";
|
|
232
|
+
var BUILD_RELEASE_MANIFEST_SIGNATURE = "CR3byJyWyomilicbi2WBO0kBuXqbATk49Z5ZP9B0lz8";
|
|
233
233
|
var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
234
234
|
|
|
235
235
|
// ../../src/version.ts
|
package/dist/server.js
CHANGED
|
@@ -1910,6 +1910,22 @@ function deriveTemplateParamsFromContextUrl(urlTemplate, contextUrl) {
|
|
|
1910
1910
|
const actualValue = actualUrl.searchParams.get(key);
|
|
1911
1911
|
if (actualValue != null && actualValue !== "") {
|
|
1912
1912
|
out[placeholder] = actualValue;
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
const byPlaceholderName = actualUrl.searchParams.get(placeholder);
|
|
1916
|
+
if (byPlaceholderName != null && byPlaceholderName !== "") {
|
|
1917
|
+
out[placeholder] = byPlaceholderName;
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
const lowerPlaceholder = placeholder.toLowerCase();
|
|
1921
|
+
if (/^(q|query|search|text|keyword|keywords|term|terms)$/.test(lowerPlaceholder)) {
|
|
1922
|
+
for (const alias of ["q", "query", "search", "text", "keyword", "keywords", "term"]) {
|
|
1923
|
+
const v = actualUrl.searchParams.get(alias);
|
|
1924
|
+
if (v != null && v !== "") {
|
|
1925
|
+
out[placeholder] = v;
|
|
1926
|
+
break;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1913
1929
|
}
|
|
1914
1930
|
}
|
|
1915
1931
|
return out;
|
|
@@ -3827,6 +3843,13 @@ __export(exports_reverse_engineer, {
|
|
|
3827
3843
|
extractAuthHeaders: () => extractAuthHeaders
|
|
3828
3844
|
});
|
|
3829
3845
|
import { nanoid as nanoid2 } from "nanoid";
|
|
3846
|
+
import { createHash } from "node:crypto";
|
|
3847
|
+
function stableEndpointId(method, urlTemplate) {
|
|
3848
|
+
if (!method || !urlTemplate)
|
|
3849
|
+
return nanoid2();
|
|
3850
|
+
const hash = createHash("sha256").update(`${method}:${urlTemplate}`).digest("base64url");
|
|
3851
|
+
return hash.slice(0, 21);
|
|
3852
|
+
}
|
|
3830
3853
|
function extractGraphQLOperationName(url, requestBody) {
|
|
3831
3854
|
const urlMatch = url.match(/\/graphql\/[^/]+\/(\w+)/);
|
|
3832
3855
|
if (urlMatch)
|
|
@@ -4447,10 +4470,11 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4447
4470
|
});
|
|
4448
4471
|
const csrfPlan = inferCsrfPlan(req, parsedRequestBody);
|
|
4449
4472
|
const endpointGraphqlOp = /graphql/i.test(req.url) ? extractGraphQLOperationName(req.url, req.request_body) : undefined;
|
|
4473
|
+
const computedUrlTemplate = qTemplateStr ? `${pathTemplate}${pathTemplate.includes("?") ? "&" : "?"}${qTemplateStr}` : pathTemplate;
|
|
4450
4474
|
let endpoint = {
|
|
4451
|
-
endpoint_id:
|
|
4475
|
+
endpoint_id: stableEndpointId(req.method, computedUrlTemplate),
|
|
4452
4476
|
method: req.method,
|
|
4453
|
-
url_template:
|
|
4477
|
+
url_template: computedUrlTemplate,
|
|
4454
4478
|
description: buildEndpointDescription(req, sampleRequest, sampleResponse),
|
|
4455
4479
|
headers_template: sanitizeHeaders(req.request_headers),
|
|
4456
4480
|
query: sanitizedQParams,
|
|
@@ -4541,7 +4565,7 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4541
4565
|
response_schema = inferSchema(jsonSamples);
|
|
4542
4566
|
}
|
|
4543
4567
|
const endpoint = {
|
|
4544
|
-
endpoint_id:
|
|
4568
|
+
endpoint_id: stableEndpointId("WS", wsUrl),
|
|
4545
4569
|
method: "WS",
|
|
4546
4570
|
url_template: wsUrl,
|
|
4547
4571
|
idempotency: "safe",
|
|
@@ -7096,10 +7120,10 @@ var init_capture = __esm(async () => {
|
|
|
7096
7120
|
});
|
|
7097
7121
|
|
|
7098
7122
|
// ../../src/build-info.generated.ts
|
|
7099
|
-
var BUILD_RELEASE_VERSION = "3.
|
|
7123
|
+
var BUILD_RELEASE_VERSION = "3.8.0-preview.1", BUILD_GIT_SHA = "5b9633fd977c", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy44LjAtcHJldmlldy4xIiwiZ2l0X3NoYSI6IjViOTYzM2ZkOTc3YyIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANWI5NjMzZmQ5NzdjIiwiaXNzdWVkX2F0IjoiMjAyNi0wNC0xMFQxNjo0NjoxMy4yMTFaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "CR3byJyWyomilicbi2WBO0kBuXqbATk49Z5ZP9B0lz8", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
7100
7124
|
|
|
7101
7125
|
// ../../src/version.ts
|
|
7102
|
-
import { createHash } from "crypto";
|
|
7126
|
+
import { createHash as createHash2 } from "crypto";
|
|
7103
7127
|
import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
7104
7128
|
import { dirname, join as join3, parse } from "path";
|
|
7105
7129
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -7116,7 +7140,7 @@ function collectTsFiles(dir) {
|
|
|
7116
7140
|
return results;
|
|
7117
7141
|
}
|
|
7118
7142
|
function hashFiles(srcDir, files) {
|
|
7119
|
-
const hash =
|
|
7143
|
+
const hash = createHash2("sha256");
|
|
7120
7144
|
for (const file of files) {
|
|
7121
7145
|
hash.update(file.slice(srcDir.length));
|
|
7122
7146
|
hash.update(readFileSync2(file, "utf-8"));
|
|
@@ -7156,7 +7180,7 @@ function computeCodeHash() {
|
|
|
7156
7180
|
} catch {}
|
|
7157
7181
|
const pkgVersion = getPackageVersion();
|
|
7158
7182
|
if (pkgVersion !== "unknown") {
|
|
7159
|
-
return
|
|
7183
|
+
return createHash2("sha256").update(`package:${pkgVersion}`).digest("hex").slice(0, 12);
|
|
7160
7184
|
}
|
|
7161
7185
|
return "compiled";
|
|
7162
7186
|
}
|
|
@@ -7210,13 +7234,13 @@ var init_version = __esm(() => {
|
|
|
7210
7234
|
});
|
|
7211
7235
|
|
|
7212
7236
|
// ../../src/payments/cascade.ts
|
|
7213
|
-
import { createHash as
|
|
7237
|
+
import { createHash as createHash3 } from "crypto";
|
|
7214
7238
|
import bs58 from "bs58";
|
|
7215
7239
|
function payableContributors(skill) {
|
|
7216
7240
|
return (skill.contributors ?? []).filter((c) => !!c.wallet_address?.trim());
|
|
7217
7241
|
}
|
|
7218
7242
|
function cascadeLabel(skillId) {
|
|
7219
|
-
const digest =
|
|
7243
|
+
const digest = createHash3("sha256").update(skillId).digest("hex");
|
|
7220
7244
|
return `ubr-${digest.slice(0, 23)}`;
|
|
7221
7245
|
}
|
|
7222
7246
|
function decodeSecretKey(raw) {
|
|
@@ -7610,7 +7634,7 @@ __export(exports_client2, {
|
|
|
7610
7634
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync2, unlinkSync } from "fs";
|
|
7611
7635
|
import { join as join6 } from "path";
|
|
7612
7636
|
import { homedir as homedir4, hostname, release as osRelease } from "os";
|
|
7613
|
-
import { randomBytes as randomBytes2, createHash as
|
|
7637
|
+
import { randomBytes as randomBytes2, createHash as createHash4 } from "crypto";
|
|
7614
7638
|
import { createInterface } from "readline";
|
|
7615
7639
|
import { execSync } from "child_process";
|
|
7616
7640
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
@@ -7887,7 +7911,7 @@ function getApiKey() {
|
|
|
7887
7911
|
function hashApiKey(key) {
|
|
7888
7912
|
if (!key || key === "local-only")
|
|
7889
7913
|
return "";
|
|
7890
|
-
return
|
|
7914
|
+
return createHash4("sha256").update(key).digest("hex");
|
|
7891
7915
|
}
|
|
7892
7916
|
function getAgentId() {
|
|
7893
7917
|
const config = loadConfig();
|
|
@@ -9069,7 +9093,7 @@ var init_publish_admission = __esm(() => {
|
|
|
9069
9093
|
});
|
|
9070
9094
|
|
|
9071
9095
|
// ../../src/telemetry.ts
|
|
9072
|
-
import { createHash as
|
|
9096
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
9073
9097
|
import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9074
9098
|
import { join as join7 } from "node:path";
|
|
9075
9099
|
function getTraceDir() {
|
|
@@ -9079,7 +9103,7 @@ function isTracingEnabled() {
|
|
|
9079
9103
|
return process.env.UNBROWSE_DISABLE_TRACES !== "1";
|
|
9080
9104
|
}
|
|
9081
9105
|
function hashValue(value) {
|
|
9082
|
-
return
|
|
9106
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 16);
|
|
9083
9107
|
}
|
|
9084
9108
|
function isSensitiveName(name) {
|
|
9085
9109
|
return SENSITIVE_PATTERNS.some((re) => re.test(name));
|
|
@@ -9097,7 +9121,7 @@ function anonymizeUrl(url) {
|
|
|
9097
9121
|
}
|
|
9098
9122
|
function hashResponseBody(result) {
|
|
9099
9123
|
const str = typeof result === "string" ? result : JSON.stringify(result ?? "");
|
|
9100
|
-
return
|
|
9124
|
+
return createHash5("sha256").update(str).digest("hex").slice(0, 32);
|
|
9101
9125
|
}
|
|
9102
9126
|
function classifyFailure(error) {
|
|
9103
9127
|
if (!error)
|
|
@@ -12457,7 +12481,12 @@ function cleanDOM(html) {
|
|
|
12457
12481
|
const $ = cheerio.load(html);
|
|
12458
12482
|
for (const tag of STRIP_TAGS) {
|
|
12459
12483
|
if (tag === "script") {
|
|
12460
|
-
$("script").
|
|
12484
|
+
$("script").each((_, el) => {
|
|
12485
|
+
const type = $(el).attr("type") ?? "";
|
|
12486
|
+
if (type !== "application/ld+json") {
|
|
12487
|
+
$(el).remove();
|
|
12488
|
+
}
|
|
12489
|
+
});
|
|
12461
12490
|
} else {
|
|
12462
12491
|
$(tag).remove();
|
|
12463
12492
|
}
|
|
@@ -15495,7 +15524,12 @@ __export(exports_execution, {
|
|
|
15495
15524
|
buildCanonicalDocumentEndpoint: () => buildCanonicalDocumentEndpoint
|
|
15496
15525
|
});
|
|
15497
15526
|
import { nanoid as nanoid6 } from "nanoid";
|
|
15498
|
-
import { createHash as
|
|
15527
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
15528
|
+
function stableEndpointId2(method, urlTemplate) {
|
|
15529
|
+
if (!method || !urlTemplate)
|
|
15530
|
+
return nanoid6();
|
|
15531
|
+
return createHash6("sha256").update(`${method}:${urlTemplate}`).digest("base64url").slice(0, 21);
|
|
15532
|
+
}
|
|
15499
15533
|
function stampTrace(trace) {
|
|
15500
15534
|
trace.trace_version = TRACE_VERSION;
|
|
15501
15535
|
return trace;
|
|
@@ -16039,10 +16073,11 @@ function buildPageArtifactCapture(url, intent, html, authRequired = false) {
|
|
|
16039
16073
|
const searchForms = detectSearchForms(html);
|
|
16040
16074
|
const validSearchForm = searchForms.find((spec) => isStructuredSearchForm(spec));
|
|
16041
16075
|
const response_schema = inferSchema([extracted.data]);
|
|
16076
|
+
const computedTemplate = templatizeQueryParams(url);
|
|
16042
16077
|
const endpoint = {
|
|
16043
|
-
endpoint_id:
|
|
16078
|
+
endpoint_id: stableEndpointId2("GET", computedTemplate),
|
|
16044
16079
|
method: "GET",
|
|
16045
|
-
url_template:
|
|
16080
|
+
url_template: computedTemplate,
|
|
16046
16081
|
idempotency: "safe",
|
|
16047
16082
|
verification_status: "verified",
|
|
16048
16083
|
reliability_score: extracted.confidence,
|
|
@@ -16090,7 +16125,7 @@ function buildCanonicalDocumentEndpoint(url, intent, authRequired = false) {
|
|
|
16090
16125
|
const replayTemplate = deriveStructuredDataReplayTemplate(url);
|
|
16091
16126
|
if (replayUrl === url && replayTemplate === url)
|
|
16092
16127
|
return;
|
|
16093
|
-
const canonicalId =
|
|
16128
|
+
const canonicalId = createHash6("sha1").update(replayTemplate !== url ? replayTemplate : replayUrl).digest("base64url").slice(0, 21);
|
|
16094
16129
|
const endpoint = {
|
|
16095
16130
|
endpoint_id: canonicalId,
|
|
16096
16131
|
method: "GET",
|
|
@@ -16270,6 +16305,65 @@ async function trySeedPublicDocumentFetchSkill(skill, url, intent, targetDomain,
|
|
|
16270
16305
|
redirect: "follow"
|
|
16271
16306
|
});
|
|
16272
16307
|
const html = await response.text();
|
|
16308
|
+
const contentType = (response.headers.get("content-type") || "").toLowerCase();
|
|
16309
|
+
if (response.ok && (contentType.includes("application/json") || contentType.includes("text/json"))) {
|
|
16310
|
+
try {
|
|
16311
|
+
const parsed = JSON.parse(html);
|
|
16312
|
+
const urlObj = new URL(response.url || url);
|
|
16313
|
+
const pathTemplate = `${urlObj.origin}${urlObj.pathname}`;
|
|
16314
|
+
const responseSchema = inferSchema([parsed]);
|
|
16315
|
+
const endpoint = {
|
|
16316
|
+
endpoint_id: stableEndpointId2("GET", pathTemplate),
|
|
16317
|
+
method: "GET",
|
|
16318
|
+
url_template: pathTemplate,
|
|
16319
|
+
idempotency: "safe",
|
|
16320
|
+
verification_status: "verified",
|
|
16321
|
+
reliability_score: 0.95,
|
|
16322
|
+
description: `Direct JSON API for ${intent}`,
|
|
16323
|
+
response_schema: responseSchema
|
|
16324
|
+
};
|
|
16325
|
+
endpoint.semantic = inferEndpointSemantic(endpoint, {
|
|
16326
|
+
sampleResponse: parsed,
|
|
16327
|
+
observedAt: new Date().toISOString(),
|
|
16328
|
+
sampleRequestUrl: url
|
|
16329
|
+
});
|
|
16330
|
+
const domain2 = getRegistrableDomain(targetDomain);
|
|
16331
|
+
const existingSkill2 = findExistingSkillForDomain(domain2, intent);
|
|
16332
|
+
const localEndpoints2 = await prepareLearnedEndpoints(existingSkill2 ? mergeEndpoints(existingSkill2.endpoints, [endpoint]) : [endpoint], intent, domain2);
|
|
16333
|
+
const localDraft2 = {
|
|
16334
|
+
skill_id: existingSkill2?.skill_id ?? nanoid6(),
|
|
16335
|
+
version: "1.0.0",
|
|
16336
|
+
schema_version: "1",
|
|
16337
|
+
lifecycle: "active",
|
|
16338
|
+
execution_type: "http",
|
|
16339
|
+
created_at: existingSkill2?.created_at ?? new Date().toISOString(),
|
|
16340
|
+
updated_at: new Date().toISOString(),
|
|
16341
|
+
name: domain2,
|
|
16342
|
+
intent_signature: intent,
|
|
16343
|
+
domain: domain2,
|
|
16344
|
+
description: `API skill for ${domain2}`,
|
|
16345
|
+
owner_type: "agent",
|
|
16346
|
+
endpoints: localEndpoints2,
|
|
16347
|
+
operation_graph: buildSkillOperationGraph(localEndpoints2),
|
|
16348
|
+
intents: [intent]
|
|
16349
|
+
};
|
|
16350
|
+
try {
|
|
16351
|
+
cachePublishedSkill(localDraft2);
|
|
16352
|
+
} catch {}
|
|
16353
|
+
return {
|
|
16354
|
+
trace: stampTrace({
|
|
16355
|
+
trace_id: nanoid6(),
|
|
16356
|
+
skill_id: localDraft2.skill_id,
|
|
16357
|
+
endpoint_id: endpoint.endpoint_id,
|
|
16358
|
+
started_at: new Date().toISOString(),
|
|
16359
|
+
completed_at: new Date().toISOString(),
|
|
16360
|
+
success: true,
|
|
16361
|
+
status_code: response.status
|
|
16362
|
+
}),
|
|
16363
|
+
result: parsed
|
|
16364
|
+
};
|
|
16365
|
+
} catch {}
|
|
16366
|
+
}
|
|
16273
16367
|
if (!isHtml(html) || isSpaShell(html))
|
|
16274
16368
|
return;
|
|
16275
16369
|
const built = buildPageArtifactCapture(response.url || url, intent, html, usedStoredAuth);
|
|
@@ -16595,7 +16689,7 @@ async function executeBrowserCapture(skill, params, options) {
|
|
|
16595
16689
|
epUrl = `${route.url}?${qStr}`;
|
|
16596
16690
|
}
|
|
16597
16691
|
endpoints.push({
|
|
16598
|
-
endpoint_id:
|
|
16692
|
+
endpoint_id: stableEndpointId2("GET", epUrl),
|
|
16599
16693
|
method: "GET",
|
|
16600
16694
|
url_template: epUrl,
|
|
16601
16695
|
query: epQuery,
|
|
@@ -16745,6 +16839,56 @@ async function executeBrowserCapture(skill, params, options) {
|
|
|
16745
16839
|
}
|
|
16746
16840
|
};
|
|
16747
16841
|
}
|
|
16842
|
+
const antiBotSignal = (() => {
|
|
16843
|
+
const html = captured.html ?? "";
|
|
16844
|
+
if (!html)
|
|
16845
|
+
return "empty_capture";
|
|
16846
|
+
const lower = html.toLowerCase();
|
|
16847
|
+
const titleMatch = lower.match(/<title[^>]*>([^<]{0,200})<\/title>/);
|
|
16848
|
+
const title = titleMatch ? titleMatch[1].trim() : "";
|
|
16849
|
+
const blockTitles = [
|
|
16850
|
+
"robot check",
|
|
16851
|
+
"access denied",
|
|
16852
|
+
"just a moment",
|
|
16853
|
+
"attention required",
|
|
16854
|
+
"please verify",
|
|
16855
|
+
"security check",
|
|
16856
|
+
"are you a robot"
|
|
16857
|
+
];
|
|
16858
|
+
for (const marker of blockTitles) {
|
|
16859
|
+
if (title.includes(marker))
|
|
16860
|
+
return `anti_bot_title:${marker}`;
|
|
16861
|
+
}
|
|
16862
|
+
const authTitles = ["log in", "sign in", "log in to", "sign in to"];
|
|
16863
|
+
if (authTitles.some((t) => title === t || title.startsWith(t + " ")) || title === "login") {
|
|
16864
|
+
return `auth_wall_title:${title}`;
|
|
16865
|
+
}
|
|
16866
|
+
if (lower.includes("g-recaptcha") || lower.includes("hcaptcha.com") || lower.includes("cf-challenge-form")) {
|
|
16867
|
+
return "captcha_present";
|
|
16868
|
+
}
|
|
16869
|
+
if (html.length < 5000 && !lower.includes("<script")) {
|
|
16870
|
+
return `tiny_body:${html.length}b`;
|
|
16871
|
+
}
|
|
16872
|
+
return null;
|
|
16873
|
+
})();
|
|
16874
|
+
if (antiBotSignal) {
|
|
16875
|
+
const trace3 = stampTrace({
|
|
16876
|
+
trace_id: traceId,
|
|
16877
|
+
skill_id: skill.skill_id,
|
|
16878
|
+
endpoint_id: "browser-capture",
|
|
16879
|
+
started_at: startedAt,
|
|
16880
|
+
completed_at: new Date().toISOString(),
|
|
16881
|
+
success: false,
|
|
16882
|
+
error: "browser_block"
|
|
16883
|
+
});
|
|
16884
|
+
return {
|
|
16885
|
+
trace: trace3,
|
|
16886
|
+
result: {
|
|
16887
|
+
error: "browser_block",
|
|
16888
|
+
message: `Browser capture detected a block signal at ${url}: ${antiBotSignal}. The site served a degraded/challenge page to the headless browser. Real-browser cookies via autoExtract may be required.`
|
|
16889
|
+
}
|
|
16890
|
+
};
|
|
16891
|
+
}
|
|
16748
16892
|
const trace2 = stampTrace({
|
|
16749
16893
|
trace_id: traceId,
|
|
16750
16894
|
skill_id: skill.skill_id,
|
|
@@ -19117,10 +19261,10 @@ var init_timing_economics = __esm(() => {
|
|
|
19117
19261
|
});
|
|
19118
19262
|
|
|
19119
19263
|
// ../../src/routing-telemetry.ts
|
|
19120
|
-
import { createHash as
|
|
19264
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
19121
19265
|
import { nanoid as nanoid8 } from "nanoid";
|
|
19122
19266
|
function stableHash(value) {
|
|
19123
|
-
return
|
|
19267
|
+
return createHash7("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 24);
|
|
19124
19268
|
}
|
|
19125
19269
|
function sanitizeScalar(value) {
|
|
19126
19270
|
if (value == null)
|
|
@@ -19474,7 +19618,7 @@ function applyVerificationResults(skill, verification) {
|
|
|
19474
19618
|
import { nanoid as nanoid9 } from "nanoid";
|
|
19475
19619
|
import { existsSync as existsSync13, writeFileSync as writeFileSync8, readFileSync as readFileSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "node:fs";
|
|
19476
19620
|
import { dirname as dirname2, join as join12 } from "node:path";
|
|
19477
|
-
import { createHash as
|
|
19621
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
19478
19622
|
function summarizeSchema(schema, maxDepth = 3) {
|
|
19479
19623
|
function walk(s, depth) {
|
|
19480
19624
|
if (depth <= 0)
|
|
@@ -19602,7 +19746,7 @@ function scopedResolveCacheKeys(scope, key) {
|
|
|
19602
19746
|
return scope === "global" ? [scopedCacheKey("global", key)] : [scopedCacheKey(scope, key), scopedCacheKey("global", key)];
|
|
19603
19747
|
}
|
|
19604
19748
|
function snapshotPathForCacheKey(cacheKey) {
|
|
19605
|
-
const digest =
|
|
19749
|
+
const digest = createHash8("sha1").update(cacheKey).digest("hex");
|
|
19606
19750
|
return join12(SKILL_SNAPSHOT_DIR, `${digest}.json`);
|
|
19607
19751
|
}
|
|
19608
19752
|
function writeSkillSnapshot(cacheKey, skill) {
|
|
@@ -19953,7 +20097,7 @@ function buildLocalCanonicalReplaySkill(intent, contextUrl) {
|
|
|
19953
20097
|
const domain = new URL(contextUrl).hostname.replace(/^www\./, "");
|
|
19954
20098
|
const now = new Date().toISOString();
|
|
19955
20099
|
const skill = {
|
|
19956
|
-
skill_id: `canonical-${
|
|
20100
|
+
skill_id: `canonical-${createHash8("sha1").update(contextUrl).digest("hex").slice(0, 12)}`,
|
|
19957
20101
|
version: "1.0.0",
|
|
19958
20102
|
schema_version: "1",
|
|
19959
20103
|
name: `Canonical replay for ${domain}`,
|
|
@@ -22237,35 +22381,58 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
22237
22381
|
}
|
|
22238
22382
|
}
|
|
22239
22383
|
}
|
|
22240
|
-
if (context?.url
|
|
22384
|
+
if (context?.url) {
|
|
22241
22385
|
try {
|
|
22242
22386
|
const directRes = await fetch(context.url, {
|
|
22243
|
-
headers: { Accept: "application/json", "User-Agent": "unbrowse/1.0" },
|
|
22244
|
-
signal: AbortSignal.timeout(
|
|
22387
|
+
headers: { Accept: "application/json, text/html;q=0.5", "User-Agent": "unbrowse/1.0" },
|
|
22388
|
+
signal: AbortSignal.timeout(15000),
|
|
22245
22389
|
redirect: "follow"
|
|
22246
22390
|
});
|
|
22247
22391
|
const ct = directRes.headers.get("content-type") ?? "";
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22392
|
+
const ctSaysJson = ct.includes("application/json") || ct.includes("+json") || ct.includes("text/json");
|
|
22393
|
+
if (directRes.ok) {
|
|
22394
|
+
let data = undefined;
|
|
22395
|
+
if (ctSaysJson) {
|
|
22396
|
+
data = await directRes.json();
|
|
22397
|
+
} else {
|
|
22398
|
+
const bodyText = await directRes.text();
|
|
22399
|
+
if (bodyText.length < 2000000) {
|
|
22400
|
+
const trimmed = bodyText.trimStart();
|
|
22401
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
22402
|
+
try {
|
|
22403
|
+
const parsed = JSON.parse(bodyText);
|
|
22404
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
22405
|
+
data = parsed;
|
|
22406
|
+
console.log(`[direct-fetch] ${context.url} body-sniff hit: ct="${ct}" but body parses as JSON`);
|
|
22407
|
+
}
|
|
22408
|
+
} catch {}
|
|
22409
|
+
}
|
|
22410
|
+
}
|
|
22411
|
+
}
|
|
22412
|
+
if (data !== undefined) {
|
|
22413
|
+
const trace2 = {
|
|
22414
|
+
trace_id: nanoid9(),
|
|
22415
|
+
skill_id: "direct-fetch",
|
|
22416
|
+
endpoint_id: "direct-fetch",
|
|
22417
|
+
started_at: new Date().toISOString(),
|
|
22418
|
+
completed_at: new Date().toISOString(),
|
|
22419
|
+
success: true
|
|
22420
|
+
};
|
|
22421
|
+
const t = finalize("direct-fetch", data, "direct-fetch", undefined, trace2);
|
|
22422
|
+
console.log(`[direct-fetch] ${context.url} returned JSON directly — skipping browser`);
|
|
22423
|
+
return {
|
|
22424
|
+
result: data,
|
|
22425
|
+
trace: trace2,
|
|
22426
|
+
source: "direct-fetch",
|
|
22427
|
+
skill: undefined,
|
|
22428
|
+
timing: t
|
|
22429
|
+
};
|
|
22430
|
+
}
|
|
22267
22431
|
}
|
|
22268
|
-
} catch {
|
|
22432
|
+
} catch (err) {
|
|
22433
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22434
|
+
console.log(`[direct-fetch] ${context.url} skipped: ${msg.slice(0, 100)}`);
|
|
22435
|
+
}
|
|
22269
22436
|
}
|
|
22270
22437
|
if (process.env.UNBROWSE_LOCAL_ONLY === "1" && !forceCapture) {
|
|
22271
22438
|
return buildNoCachedMatch();
|
|
@@ -22424,6 +22591,32 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
22424
22591
|
throw error;
|
|
22425
22592
|
}
|
|
22426
22593
|
timing.execute_ms = Date.now() - te0;
|
|
22594
|
+
const captureErrCheck = result?.error;
|
|
22595
|
+
if (captureErrCheck === "connection_failed" || captureErrCheck === "capture_failed") {
|
|
22596
|
+
console.warn(`[capture] ${captureErrCheck} detected — restarting Kuri and retrying once`);
|
|
22597
|
+
try {
|
|
22598
|
+
const kuri = await Promise.resolve().then(() => (init_client(), exports_client));
|
|
22599
|
+
await kuri.stop().catch(() => {});
|
|
22600
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
22601
|
+
} catch {}
|
|
22602
|
+
try {
|
|
22603
|
+
const retryCaptureSkill = await getOrCreateBrowserCaptureSkill();
|
|
22604
|
+
const retryOut = await withAbortableOpTimeout("live_capture_retry", LIVE_CAPTURE_TIMEOUT_MS, (signal) => executeSkill(retryCaptureSkill, { ...params, url: context.url, intent }, undefined, {
|
|
22605
|
+
...options,
|
|
22606
|
+
intent,
|
|
22607
|
+
contextUrl: context?.url,
|
|
22608
|
+
signal
|
|
22609
|
+
}));
|
|
22610
|
+
if (retryOut.trace.success || !retryOut.result?.error) {
|
|
22611
|
+
trace = retryOut.trace;
|
|
22612
|
+
result = retryOut.result;
|
|
22613
|
+
learned_skill = retryOut.learned_skill;
|
|
22614
|
+
console.log(`[capture] retry after Kuri restart succeeded`);
|
|
22615
|
+
}
|
|
22616
|
+
} catch (retryErr) {
|
|
22617
|
+
console.warn(`[capture] retry failed: ${retryErr instanceof Error ? retryErr.message : retryErr}`);
|
|
22618
|
+
}
|
|
22619
|
+
}
|
|
22427
22620
|
const captureResult = result;
|
|
22428
22621
|
const authRecommended = captureResult?.auth_recommended === true;
|
|
22429
22622
|
const directDomCaptureResult = trace.success && trace.endpoint_id !== "browser-capture" && !!result && typeof result === "object" && "_extraction" in result;
|
|
@@ -24207,7 +24400,13 @@ var init_browse_session = __esm(() => {
|
|
|
24207
24400
|
|
|
24208
24401
|
// ../../src/api/browse-index.ts
|
|
24209
24402
|
import { nanoid as nanoid10 } from "nanoid";
|
|
24403
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
24210
24404
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
24405
|
+
function stableEndpointId3(method, urlTemplate) {
|
|
24406
|
+
if (!method || !urlTemplate)
|
|
24407
|
+
return nanoid10();
|
|
24408
|
+
return createHash9("sha256").update(`${method}:${urlTemplate}`).digest("base64url").slice(0, 21);
|
|
24409
|
+
}
|
|
24211
24410
|
function normalizeBrowseUrl(url, baseUrl) {
|
|
24212
24411
|
if (!url)
|
|
24213
24412
|
return url;
|
|
@@ -24385,7 +24584,7 @@ async function cacheBrowseRequests(params) {
|
|
|
24385
24584
|
return { domain, indexed: false, mode: "none", skill: null };
|
|
24386
24585
|
const urlTemplate = templatizeQueryParams2(sessionUrl);
|
|
24387
24586
|
const endpoint = {
|
|
24388
|
-
endpoint_id:
|
|
24587
|
+
endpoint_id: stableEndpointId3("GET", urlTemplate),
|
|
24389
24588
|
method: "GET",
|
|
24390
24589
|
url_template: urlTemplate,
|
|
24391
24590
|
idempotency: "safe",
|
|
@@ -26437,11 +26636,18 @@ async function registerRoutes(app) {
|
|
|
26437
26636
|
app.addHook("onRequest", async (req, reply) => {
|
|
26438
26637
|
if (req.url === "/health" || req.url === "/v1/stats" || req.url.startsWith("/v1/settings"))
|
|
26439
26638
|
return;
|
|
26440
|
-
|
|
26639
|
+
let key = getApiKey();
|
|
26640
|
+
if (!key) {
|
|
26641
|
+
try {
|
|
26642
|
+
const { waitForBackgroundRegistration: waitForBackgroundRegistration2 } = await Promise.resolve().then(() => (init_client2(), exports_client2));
|
|
26643
|
+
await waitForBackgroundRegistration2(1e4);
|
|
26644
|
+
key = getApiKey();
|
|
26645
|
+
} catch {}
|
|
26646
|
+
}
|
|
26441
26647
|
if (!key) {
|
|
26442
26648
|
return reply.code(401).send({
|
|
26443
26649
|
error: "api_key_required",
|
|
26444
|
-
message: "No API key configured.
|
|
26650
|
+
message: "No API key configured. Run `unbrowse setup` to register, or set UNBROWSE_API_KEY manually.",
|
|
26445
26651
|
docs_url: "https://unbrowse.ai"
|
|
26446
26652
|
});
|
|
26447
26653
|
}
|
|
@@ -26691,7 +26897,28 @@ async function registerRoutes(app) {
|
|
|
26691
26897
|
...context_url && typeof params?.url !== "string" ? { url: context_url } : {}
|
|
26692
26898
|
};
|
|
26693
26899
|
try {
|
|
26694
|
-
|
|
26900
|
+
let execResult = await executeSkill(skill, execParams, projection, { confirm_unsafe, confirm_third_party_terms, dry_run, skip_robots_check, intent, contextUrl: context_url, client_scope: clientScope });
|
|
26901
|
+
if (!execResult.trace.success && execResult.result?.error === "endpoint_not_found" && typeof execParams.endpoint_id === "string") {
|
|
26902
|
+
let recovered = false;
|
|
26903
|
+
const freshSkill = await getSkill2(skill_id, clientScope);
|
|
26904
|
+
if (freshSkill && freshSkill.endpoints.some((e) => e.endpoint_id === execParams.endpoint_id)) {
|
|
26905
|
+
console.log(`[exec] endpoint ${execParams.endpoint_id} found in fresh marketplace skill — retrying`);
|
|
26906
|
+
skill = freshSkill;
|
|
26907
|
+
recovered = true;
|
|
26908
|
+
}
|
|
26909
|
+
if (!recovered && context_url) {
|
|
26910
|
+
const { buildCanonicalDocumentEndpoint: buildCanonicalDocumentEndpoint2 } = await init_execution().then(() => exports_execution);
|
|
26911
|
+
const canonical = buildCanonicalDocumentEndpoint2(context_url, intent ?? "", false);
|
|
26912
|
+
if (canonical && canonical.endpoint_id === execParams.endpoint_id) {
|
|
26913
|
+
console.log(`[exec] endpoint ${execParams.endpoint_id} is a canonical replay — injecting into skill`);
|
|
26914
|
+
skill = { ...skill, endpoints: [canonical, ...skill.endpoints] };
|
|
26915
|
+
recovered = true;
|
|
26916
|
+
}
|
|
26917
|
+
}
|
|
26918
|
+
if (recovered) {
|
|
26919
|
+
execResult = await executeSkill(skill, execParams, projection, { confirm_unsafe, confirm_third_party_terms, dry_run, skip_robots_check, intent, contextUrl: context_url, client_scope: clientScope });
|
|
26920
|
+
}
|
|
26921
|
+
}
|
|
26695
26922
|
saveTrace(execResult.trace);
|
|
26696
26923
|
if (execResult.trace.endpoint_id) {
|
|
26697
26924
|
recordExecution(skill.skill_id, execResult.trace.endpoint_id, execResult.trace, skill).catch(() => {});
|