unbrowse 2.8.3 → 2.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +161 -633
- package/package.json +1 -1
- package/runtime-src/api/routes.ts +26 -2
- package/runtime-src/cli.ts +103 -364
- package/runtime-src/execution/index.ts +21 -35
- package/runtime-src/orchestrator/index.ts +1 -35
- package/runtime-src/server.ts +2 -8
- 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/runtime-src/transform/schema-hints.ts +0 -358
package/dist/cli.js
CHANGED
|
@@ -624,14 +624,6 @@ async function executeInPageFetch(tabId, url, method, headers, body) {
|
|
|
624
624
|
return { status: 0, data: result };
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
|
-
async function health() {
|
|
628
|
-
try {
|
|
629
|
-
const result = await kuriGet("/health");
|
|
630
|
-
return { ok: result?.ok === true || result?.status === "ok", tabs: result?.tabs };
|
|
631
|
-
} catch {
|
|
632
|
-
return { ok: false };
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
627
|
async function action(tabId, actionType, ref, value) {
|
|
636
628
|
const params = { tab_id: tabId, action: actionType, ref };
|
|
637
629
|
if (value !== undefined)
|
|
@@ -6342,259 +6334,6 @@ var init_drift = __esm(() => {
|
|
|
6342
6334
|
init_transform();
|
|
6343
6335
|
});
|
|
6344
6336
|
|
|
6345
|
-
// ../../src/transform/schema-hints.ts
|
|
6346
|
-
function buildIntentProfile(intent) {
|
|
6347
|
-
const text = intent?.toLowerCase() ?? "";
|
|
6348
|
-
const profile = {
|
|
6349
|
-
preferredPaths: [],
|
|
6350
|
-
discouragedPaths: [],
|
|
6351
|
-
preferredFields: [],
|
|
6352
|
-
discouragedFields: [],
|
|
6353
|
-
wantsStructuredRecords: /\b(search|list|find|get|fetch|timeline|feed|trending)\b/.test(text)
|
|
6354
|
-
};
|
|
6355
|
-
if (/\b(repo|repos|repository|repositories|code|projects?)\b/.test(text)) {
|
|
6356
|
-
profile.preferredPaths.push("repositories", "repos", "results", "items", "data");
|
|
6357
|
-
profile.preferredFields.push("full_name", "name", "description", "stargazers_count", "stars", "language", "owner", "url");
|
|
6358
|
-
profile.discouragedPaths.push("accounts", "users", "hashtags", "topics");
|
|
6359
|
-
}
|
|
6360
|
-
if (/\b(post|posts|tweet|tweets|status|statuses|timeline|feed|thread|threads)\b/.test(text)) {
|
|
6361
|
-
profile.preferredPaths.push("statuses", "posts", "tweets", "timeline", "entries", "results");
|
|
6362
|
-
profile.preferredFields.push("content", "text", "body", "created_at", "url", "account", "username", "replies_count", "reblogs_count", "favourites_count");
|
|
6363
|
-
profile.discouragedPaths.push("accounts", "users", "people", "profiles", "hashtags");
|
|
6364
|
-
}
|
|
6365
|
-
if (/\b(person|people|user|users|profile|profiles|member|members|account|accounts)\b/.test(text)) {
|
|
6366
|
-
profile.preferredPaths.push("people", "users", "accounts", "profiles", "included", "elements");
|
|
6367
|
-
profile.preferredFields.push("name", "headline", "title", "public_identifier", "username", "handle", "url");
|
|
6368
|
-
profile.discouragedPaths.push("hashtags", "statuses", "posts");
|
|
6369
|
-
}
|
|
6370
|
-
if (/\b(trend|trending|topic|topics)\b/.test(text)) {
|
|
6371
|
-
profile.preferredPaths.push("trends", "topics", "timeline", "entries", "results", "data");
|
|
6372
|
-
profile.preferredFields.push("name", "query", "topic", "post_count", "tweet_volume", "url");
|
|
6373
|
-
profile.discouragedPaths.push("accounts", "users");
|
|
6374
|
-
}
|
|
6375
|
-
return profile;
|
|
6376
|
-
}
|
|
6377
|
-
function findArrayCandidates(schema, path5, depth, results) {
|
|
6378
|
-
if (schema.type === "array" && schema.items) {
|
|
6379
|
-
const items = schema.items;
|
|
6380
|
-
if (items.type === "object" && items.properties) {
|
|
6381
|
-
const fieldCount = Object.keys(items.properties).length;
|
|
6382
|
-
results.push({ path: path5 ? `${path5}[]` : "[]", itemSchema: items, fieldCount, depth });
|
|
6383
|
-
for (const [key, prop] of Object.entries(items.properties)) {
|
|
6384
|
-
const childPath = path5 ? `${path5}[].${key}` : `[].${key}`;
|
|
6385
|
-
findArrayCandidates(prop, childPath, depth + 1, results);
|
|
6386
|
-
}
|
|
6387
|
-
return;
|
|
6388
|
-
}
|
|
6389
|
-
if (items.type === "array") {
|
|
6390
|
-
findArrayCandidates(items, path5 ? `${path5}[]` : "[]", depth + 1, results);
|
|
6391
|
-
}
|
|
6392
|
-
return;
|
|
6393
|
-
}
|
|
6394
|
-
if (schema.type === "object" && schema.properties) {
|
|
6395
|
-
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
6396
|
-
const childPath = path5 ? `${path5}.${key}` : key;
|
|
6397
|
-
findArrayCandidates(prop, childPath, depth + 1, results);
|
|
6398
|
-
}
|
|
6399
|
-
}
|
|
6400
|
-
}
|
|
6401
|
-
function scoreField(name, schema) {
|
|
6402
|
-
let score = 0;
|
|
6403
|
-
const lower = name.toLowerCase();
|
|
6404
|
-
if (/^(id|name|title|label|slug)$/i.test(name))
|
|
6405
|
-
score += 10;
|
|
6406
|
-
if (/^(url|link|href|uri)$/i.test(name))
|
|
6407
|
-
score += 8;
|
|
6408
|
-
if (/^(description|text|content|body|summary|bio)$/i.test(name))
|
|
6409
|
-
score += 7;
|
|
6410
|
-
if (/^(email|username|handle|screen.?name)$/i.test(name))
|
|
6411
|
-
score += 7;
|
|
6412
|
-
if (/date|time|created|updated|start|end/i.test(lower))
|
|
6413
|
-
score += 5;
|
|
6414
|
-
if (/count|total|price|amount|score|rating|likes|views|followers/i.test(lower))
|
|
6415
|
-
score += 5;
|
|
6416
|
-
if (/status|state|type|category|kind|tag/i.test(lower))
|
|
6417
|
-
score += 4;
|
|
6418
|
-
if (/city|address|location|lat|lng|geo|place|venue/i.test(lower))
|
|
6419
|
-
score += 4;
|
|
6420
|
-
if (/image|photo|avatar|thumbnail|cover|logo|icon/i.test(lower))
|
|
6421
|
-
score += 3;
|
|
6422
|
-
if (schema.type === "string" || schema.type === "integer" || schema.type === "number" || schema.type === "boolean") {
|
|
6423
|
-
score += 2;
|
|
6424
|
-
}
|
|
6425
|
-
if (/urn|tracking|internal|hash|token|cursor|pagination|__/i.test(lower))
|
|
6426
|
-
score -= 5;
|
|
6427
|
-
if (name.startsWith("$") || name.startsWith("_"))
|
|
6428
|
-
score -= 3;
|
|
6429
|
-
return score;
|
|
6430
|
-
}
|
|
6431
|
-
function selectBestArray(candidates, intent) {
|
|
6432
|
-
if (candidates.length === 0)
|
|
6433
|
-
return null;
|
|
6434
|
-
const profile = buildIntentProfile(intent);
|
|
6435
|
-
const scored = candidates.map((c) => {
|
|
6436
|
-
let score = c.fieldCount * 2;
|
|
6437
|
-
if (c.depth >= 1 && c.depth <= 3)
|
|
6438
|
-
score += 5;
|
|
6439
|
-
if (c.depth === 0)
|
|
6440
|
-
score += 2;
|
|
6441
|
-
const pathLower = c.path.toLowerCase();
|
|
6442
|
-
if (/data|results|items|entries|elements|records|list|feed|posts|events|users/i.test(pathLower)) {
|
|
6443
|
-
score += 8;
|
|
6444
|
-
}
|
|
6445
|
-
if (/included|nodes|edges/i.test(pathLower))
|
|
6446
|
-
score += 6;
|
|
6447
|
-
for (const token of profile.preferredPaths) {
|
|
6448
|
-
if (pathLower.includes(token))
|
|
6449
|
-
score += 14;
|
|
6450
|
-
}
|
|
6451
|
-
for (const token of profile.discouragedPaths) {
|
|
6452
|
-
if (pathLower.includes(token))
|
|
6453
|
-
score -= 18;
|
|
6454
|
-
}
|
|
6455
|
-
const fieldNames = Object.keys(c.itemSchema.properties ?? {}).map((name) => name.toLowerCase());
|
|
6456
|
-
for (const token of profile.preferredFields) {
|
|
6457
|
-
if (fieldNames.includes(token.toLowerCase()))
|
|
6458
|
-
score += 7;
|
|
6459
|
-
}
|
|
6460
|
-
for (const token of profile.discouragedFields) {
|
|
6461
|
-
if (fieldNames.includes(token.toLowerCase()))
|
|
6462
|
-
score -= 8;
|
|
6463
|
-
}
|
|
6464
|
-
if (profile.wantsStructuredRecords && c.fieldCount < 3)
|
|
6465
|
-
score -= 12;
|
|
6466
|
-
if (fieldNames.length > 0 && fieldNames.every((name) => /^(link|title|label|text|value)$/i.test(name))) {
|
|
6467
|
-
score -= 16;
|
|
6468
|
-
}
|
|
6469
|
-
if (c.fieldCount < 3)
|
|
6470
|
-
score -= 5;
|
|
6471
|
-
return { candidate: c, score };
|
|
6472
|
-
});
|
|
6473
|
-
scored.sort((a, b) => b.score - a.score);
|
|
6474
|
-
return scored[0]?.candidate ?? null;
|
|
6475
|
-
}
|
|
6476
|
-
function schemaToTree(schema, maxDepth = 3) {
|
|
6477
|
-
const tree = {};
|
|
6478
|
-
function walk(s, path5, depth) {
|
|
6479
|
-
if (depth > maxDepth)
|
|
6480
|
-
return;
|
|
6481
|
-
if (s.type === "object" && s.properties) {
|
|
6482
|
-
for (const [key, prop] of Object.entries(s.properties)) {
|
|
6483
|
-
const childPath = path5 ? `${path5}.${key}` : key;
|
|
6484
|
-
if (prop.type === "array" && prop.items) {
|
|
6485
|
-
if (prop.items.type === "object" && prop.items.properties) {
|
|
6486
|
-
const count = Object.keys(prop.items.properties).length;
|
|
6487
|
-
tree[`${childPath}[]`] = `array<object> (${count} fields)`;
|
|
6488
|
-
walk(prop.items, `${childPath}[]`, depth + 1);
|
|
6489
|
-
} else {
|
|
6490
|
-
tree[`${childPath}[]`] = `array<${prop.items.type}>`;
|
|
6491
|
-
}
|
|
6492
|
-
} else if (prop.type === "object" && prop.properties) {
|
|
6493
|
-
const count = Object.keys(prop.properties).length;
|
|
6494
|
-
tree[childPath] = `object (${count} fields)`;
|
|
6495
|
-
walk(prop, childPath, depth + 1);
|
|
6496
|
-
} else {
|
|
6497
|
-
tree[childPath] = prop.type;
|
|
6498
|
-
}
|
|
6499
|
-
}
|
|
6500
|
-
}
|
|
6501
|
-
}
|
|
6502
|
-
if (schema.type === "array" && schema.items) {
|
|
6503
|
-
tree["[]"] = `array<${schema.items.type}>`;
|
|
6504
|
-
if (schema.items.type === "object") {
|
|
6505
|
-
walk(schema.items, "[]", 1);
|
|
6506
|
-
}
|
|
6507
|
-
} else {
|
|
6508
|
-
walk(schema, "", 0);
|
|
6509
|
-
}
|
|
6510
|
-
return tree;
|
|
6511
|
-
}
|
|
6512
|
-
function generateExtractionHints(schema, intent) {
|
|
6513
|
-
if (schema.type !== "object" && schema.type !== "array")
|
|
6514
|
-
return null;
|
|
6515
|
-
const profile = buildIntentProfile(intent);
|
|
6516
|
-
if (schema.type === "object" && schema.properties) {
|
|
6517
|
-
for (const token of profile.preferredPaths) {
|
|
6518
|
-
const prop = schema.properties[token];
|
|
6519
|
-
if (prop?.type === "array" && (!prop.items || prop.items.type !== "object")) {
|
|
6520
|
-
return finalize({
|
|
6521
|
-
path: `${token}[]`,
|
|
6522
|
-
fields: [],
|
|
6523
|
-
item_field_count: 0,
|
|
6524
|
-
confidence: "medium"
|
|
6525
|
-
}, schema);
|
|
6526
|
-
}
|
|
6527
|
-
}
|
|
6528
|
-
}
|
|
6529
|
-
const candidates = [];
|
|
6530
|
-
findArrayCandidates(schema, "", 0, candidates);
|
|
6531
|
-
if (candidates.length === 0) {
|
|
6532
|
-
if (schema.type === "object" && schema.properties) {
|
|
6533
|
-
const propCount = Object.keys(schema.properties).length;
|
|
6534
|
-
if (propCount <= 5)
|
|
6535
|
-
return null;
|
|
6536
|
-
}
|
|
6537
|
-
if (schema.type === "object" && schema.properties) {
|
|
6538
|
-
const fields = Object.entries(schema.properties).map(([name, prop]) => ({ name, score: scoreField(name, prop) })).filter((f) => f.score > 0).sort((a, b) => b.score - a.score).slice(0, 8).map((f) => f.name);
|
|
6539
|
-
if (fields.length >= 2) {
|
|
6540
|
-
return finalize({ path: "", fields, item_field_count: Object.keys(schema.properties).length, confidence: "low" }, schema);
|
|
6541
|
-
}
|
|
6542
|
-
}
|
|
6543
|
-
return null;
|
|
6544
|
-
}
|
|
6545
|
-
const best = selectBestArray(candidates, intent);
|
|
6546
|
-
if (!best)
|
|
6547
|
-
return null;
|
|
6548
|
-
const itemProps = best.itemSchema.properties ?? {};
|
|
6549
|
-
const scoredFields = Object.entries(itemProps).map(([name, prop]) => ({ name, score: scoreField(name, prop), type: prop.type })).sort((a, b) => b.score - a.score);
|
|
6550
|
-
const topFields = scoredFields.filter((f) => f.score > 0).slice(0, 10).map((f) => f.name);
|
|
6551
|
-
if (intent) {
|
|
6552
|
-
const intentWords = intent.toLowerCase().split(/\s+/);
|
|
6553
|
-
for (const field of scoredFields) {
|
|
6554
|
-
if (topFields.includes(field.name))
|
|
6555
|
-
continue;
|
|
6556
|
-
const fieldLower = field.name.toLowerCase();
|
|
6557
|
-
if (intentWords.some((w) => fieldLower.includes(w) || w.includes(fieldLower))) {
|
|
6558
|
-
topFields.push(field.name);
|
|
6559
|
-
}
|
|
6560
|
-
}
|
|
6561
|
-
}
|
|
6562
|
-
if (topFields.length < 2) {
|
|
6563
|
-
const primitiveFields = scoredFields.filter((f) => f.type === "string" || f.type === "integer" || f.type === "number").slice(0, 5).map((f) => f.name);
|
|
6564
|
-
if (primitiveFields.length < 2)
|
|
6565
|
-
return null;
|
|
6566
|
-
return finalize({
|
|
6567
|
-
path: best.path,
|
|
6568
|
-
fields: primitiveFields,
|
|
6569
|
-
item_field_count: best.fieldCount,
|
|
6570
|
-
confidence: "low"
|
|
6571
|
-
}, schema);
|
|
6572
|
-
}
|
|
6573
|
-
const confidence = best.fieldCount >= 5 ? "high" : best.fieldCount >= 3 ? "medium" : "low";
|
|
6574
|
-
return finalize({
|
|
6575
|
-
path: best.path,
|
|
6576
|
-
fields: topFields,
|
|
6577
|
-
item_field_count: best.fieldCount,
|
|
6578
|
-
confidence
|
|
6579
|
-
}, schema);
|
|
6580
|
-
}
|
|
6581
|
-
function finalize(hint, schema) {
|
|
6582
|
-
hint.cli_args = hintsToCliArgs(hint);
|
|
6583
|
-
hint.schema_tree = schemaToTree(schema, 2);
|
|
6584
|
-
return hint;
|
|
6585
|
-
}
|
|
6586
|
-
function hintsToCliArgs(hints) {
|
|
6587
|
-
const parts = [];
|
|
6588
|
-
if (hints.path) {
|
|
6589
|
-
parts.push(`--path "${hints.path}"`);
|
|
6590
|
-
}
|
|
6591
|
-
if (hints.fields.length > 0) {
|
|
6592
|
-
parts.push(`--extract "${hints.fields.join(",")}"`);
|
|
6593
|
-
}
|
|
6594
|
-
parts.push("--limit 10");
|
|
6595
|
-
return parts.join(" ");
|
|
6596
|
-
}
|
|
6597
|
-
|
|
6598
6337
|
// ../../src/execution/retry.ts
|
|
6599
6338
|
async function withRetry(fn, isRetryable, opts) {
|
|
6600
6339
|
const maxRetries = opts?.maxRetries ?? MAX_RETRIES;
|
|
@@ -9760,7 +9499,7 @@ function sanitizeNavigationQueryParams(url) {
|
|
|
9760
9499
|
return out;
|
|
9761
9500
|
}
|
|
9762
9501
|
function restoreTemplatePlaceholderEncoding(url) {
|
|
9763
|
-
return url.replace(/%7B
|
|
9502
|
+
return url.replace(/%7B(\w+)%7D/gi, "{$1}");
|
|
9764
9503
|
}
|
|
9765
9504
|
function compactSchemaSample(value, depth = 0) {
|
|
9766
9505
|
if (depth >= 4)
|
|
@@ -10807,6 +10546,15 @@ async function tryHttpFetch(url, authHeaders, cookies) {
|
|
|
10807
10546
|
return null;
|
|
10808
10547
|
}
|
|
10809
10548
|
}
|
|
10549
|
+
function flattenExtracted(data) {
|
|
10550
|
+
if (!Array.isArray(data))
|
|
10551
|
+
return data;
|
|
10552
|
+
const first = data[0];
|
|
10553
|
+
if (first && typeof first === "object" && "type" in first && "data" in first && "relevance_score" in first) {
|
|
10554
|
+
return data.reduce((best, cur) => (cur.relevance_score ?? 0) > (best.relevance_score ?? 0) ? cur : best).data;
|
|
10555
|
+
}
|
|
10556
|
+
return data;
|
|
10557
|
+
}
|
|
10810
10558
|
async function executeDomExtractionEndpoint(endpoint, url, intent, authHeaders, cookies) {
|
|
10811
10559
|
const ssrResult = await tryHttpFetch(url, authHeaders, cookies);
|
|
10812
10560
|
if (ssrResult) {
|
|
@@ -10818,16 +10566,7 @@ async function executeDomExtractionEndpoint(endpoint, url, intent, authHeaders,
|
|
|
10818
10566
|
if (ssrSemantic.verdict !== "fail") {
|
|
10819
10567
|
console.log(`[ssr-fast] hit — extracted via HTTP fetch`);
|
|
10820
10568
|
return {
|
|
10821
|
-
data:
|
|
10822
|
-
data: ssrExtracted.data,
|
|
10823
|
-
_extraction: {
|
|
10824
|
-
method: ssrExtracted.extraction_method,
|
|
10825
|
-
confidence: ssrExtracted.confidence,
|
|
10826
|
-
source: "ssr-fast",
|
|
10827
|
-
final_url: ssrResult.final_url,
|
|
10828
|
-
...ssrExtracted.selector ? { selector: ssrExtracted.selector } : {}
|
|
10829
|
-
}
|
|
10830
|
-
},
|
|
10569
|
+
data: flattenExtracted(ssrExtracted.data),
|
|
10831
10570
|
status: 200,
|
|
10832
10571
|
trace_id: nanoid5()
|
|
10833
10572
|
};
|
|
@@ -10865,16 +10604,7 @@ async function executeDomExtractionEndpoint(endpoint, url, intent, authHeaders,
|
|
|
10865
10604
|
};
|
|
10866
10605
|
}
|
|
10867
10606
|
return {
|
|
10868
|
-
data:
|
|
10869
|
-
data: extracted.data,
|
|
10870
|
-
_extraction: {
|
|
10871
|
-
method: extracted.extraction_method,
|
|
10872
|
-
confidence: extracted.confidence,
|
|
10873
|
-
source: "rendered-dom",
|
|
10874
|
-
final_url: captured.final_url,
|
|
10875
|
-
...extracted.selector ? { selector: extracted.selector } : {}
|
|
10876
|
-
}
|
|
10877
|
-
},
|
|
10607
|
+
data: flattenExtracted(extracted.data),
|
|
10878
10608
|
status: 200,
|
|
10879
10609
|
trace_id: nanoid5()
|
|
10880
10610
|
};
|
|
@@ -10933,9 +10663,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
10933
10663
|
}
|
|
10934
10664
|
return {
|
|
10935
10665
|
trace: trace2,
|
|
10936
|
-
result: resultData2
|
|
10937
|
-
...endpoint.response_schema ? { response_schema: endpoint.response_schema } : {},
|
|
10938
|
-
...endpoint.response_schema ? { extraction_hints: generateExtractionHints(endpoint.response_schema, skill.intent_signature) ?? undefined } : {}
|
|
10666
|
+
result: resultData2
|
|
10939
10667
|
};
|
|
10940
10668
|
} catch (err) {
|
|
10941
10669
|
const trace2 = stampTrace({
|
|
@@ -11183,6 +10911,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
11183
10911
|
let last = { data: null, status: 0 };
|
|
11184
10912
|
for (const replayUrl of replayUrls) {
|
|
11185
10913
|
const replayHeaders = buildStructuredReplayHeaders(url, replayUrl, headers);
|
|
10914
|
+
log("exec", `server-fetch: ${endpoint.method} ${replayUrl.substring(0, 80)} csrf=${replayHeaders["x-csrf-token"]?.substring(0, 10)}... cookies=${replayHeaders["cookie"]?.length ?? 0}chars`);
|
|
11186
10915
|
const res = await fetch(replayUrl, {
|
|
11187
10916
|
method: endpoint.method,
|
|
11188
10917
|
headers: replayHeaders,
|
|
@@ -11260,7 +10989,8 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
11260
10989
|
try {
|
|
11261
10990
|
result = await serverFetch();
|
|
11262
10991
|
if (result.status >= 200 && result.status < 400) {
|
|
11263
|
-
|
|
10992
|
+
const isApiEndpoint = /\/(api|graphql)\b/i.test(endpoint.url_template) || /\.(json)(\?|$)/.test(endpoint.url_template);
|
|
10993
|
+
if (!isApiEndpoint && shouldFallbackToBrowserReplay(result.data, endpoint, options?.intent ?? skill.intent_signature, options?.contextUrl)) {
|
|
11264
10994
|
result = await withRetry(browserCall, (r) => isRetryableStatus(r.status));
|
|
11265
10995
|
strategy = "browser";
|
|
11266
10996
|
} else {
|
|
@@ -11453,12 +11183,9 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
11453
11183
|
} else if (trace.success) {
|
|
11454
11184
|
resultData = projectResultForIntent(data, effectiveIntent);
|
|
11455
11185
|
}
|
|
11456
|
-
const rawResultShape = resultData === data;
|
|
11457
11186
|
return {
|
|
11458
11187
|
trace,
|
|
11459
|
-
result: resultData
|
|
11460
|
-
...endpoint.response_schema && rawResultShape ? { response_schema: endpoint.response_schema } : {},
|
|
11461
|
-
...endpoint.response_schema && rawResultShape ? { extraction_hints: generateExtractionHints(endpoint.response_schema, effectiveIntent) ?? undefined } : {}
|
|
11188
|
+
result: resultData
|
|
11462
11189
|
};
|
|
11463
11190
|
}
|
|
11464
11191
|
function templatizeQueryParams(url) {
|
|
@@ -13274,14 +13001,12 @@ function cacheResolvedSkill(cacheKey, skill, endpointId) {
|
|
|
13274
13001
|
});
|
|
13275
13002
|
persistRouteCache();
|
|
13276
13003
|
}
|
|
13277
|
-
function promoteResultSnapshot(cacheKey, skill, endpointId, result, trace
|
|
13004
|
+
function promoteResultSnapshot(cacheKey, skill, endpointId, result, trace) {
|
|
13278
13005
|
routeResultCache.set(cacheKey, {
|
|
13279
13006
|
skill,
|
|
13280
13007
|
endpointId,
|
|
13281
13008
|
result,
|
|
13282
13009
|
trace,
|
|
13283
|
-
response_schema,
|
|
13284
|
-
extraction_hints,
|
|
13285
13010
|
expires: Date.now() + ROUTE_CACHE_TTL
|
|
13286
13011
|
});
|
|
13287
13012
|
}
|
|
@@ -13299,9 +13024,7 @@ function buildCachedResultResponse(cached, source, timing) {
|
|
|
13299
13024
|
},
|
|
13300
13025
|
source,
|
|
13301
13026
|
skill: cached.skill,
|
|
13302
|
-
timing
|
|
13303
|
-
response_schema: cached.response_schema,
|
|
13304
|
-
extraction_hints: cached.extraction_hints
|
|
13027
|
+
timing
|
|
13305
13028
|
};
|
|
13306
13029
|
}
|
|
13307
13030
|
function invalidateResolveCacheEntries(cacheKeys, domainKeys = []) {
|
|
@@ -14349,7 +14072,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14349
14072
|
routeResultCache.delete(k);
|
|
14350
14073
|
}
|
|
14351
14074
|
}
|
|
14352
|
-
function
|
|
14075
|
+
function finalize(source, result2, skillId, skill, trace2) {
|
|
14353
14076
|
timing.total_ms = Date.now() - t0;
|
|
14354
14077
|
timing.source = source;
|
|
14355
14078
|
timing.skill_id = skillId;
|
|
@@ -14505,7 +14228,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14505
14228
|
trace: deferTrace,
|
|
14506
14229
|
source,
|
|
14507
14230
|
skill: resolvedSkill,
|
|
14508
|
-
timing:
|
|
14231
|
+
timing: finalize(source, null, resolvedSkill.skill_id, resolvedSkill, deferTrace)
|
|
14509
14232
|
};
|
|
14510
14233
|
}
|
|
14511
14234
|
function missingTemplateParams(endpoint, boundParams) {
|
|
@@ -14835,7 +14558,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14835
14558
|
skill_id: skill.skill_id,
|
|
14836
14559
|
selected_endpoint_id: candidate.endpoint.endpoint_id
|
|
14837
14560
|
});
|
|
14838
|
-
promoteResultSnapshot(cacheKey, skill, candidate.endpoint.endpoint_id, execOut.result, execOut.trace
|
|
14561
|
+
promoteResultSnapshot(cacheKey, skill, candidate.endpoint.endpoint_id, execOut.result, execOut.trace);
|
|
14839
14562
|
try {
|
|
14840
14563
|
const endpointSeq = decisionTrace.autoexec_attempts.map((a) => a.endpoint_id);
|
|
14841
14564
|
storeExecutionTrace({
|
|
@@ -14892,7 +14615,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14892
14615
|
trace: execOut.trace,
|
|
14893
14616
|
source,
|
|
14894
14617
|
skill,
|
|
14895
|
-
timing:
|
|
14618
|
+
timing: finalize(source, null, skill.skill_id, skill, execOut.trace)
|
|
14896
14619
|
};
|
|
14897
14620
|
}
|
|
14898
14621
|
}
|
|
@@ -14913,9 +14636,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14913
14636
|
trace: execOut.trace,
|
|
14914
14637
|
source,
|
|
14915
14638
|
skill,
|
|
14916
|
-
timing:
|
|
14917
|
-
response_schema: execOut.response_schema,
|
|
14918
|
-
extraction_hints: execOut.extraction_hints
|
|
14639
|
+
timing: finalize(source, execOut.result, skill.skill_id, skill, execOut.trace)
|
|
14919
14640
|
};
|
|
14920
14641
|
}
|
|
14921
14642
|
decisionTrace.autoexec_attempts.push({
|
|
@@ -14979,7 +14700,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14979
14700
|
skill_id: cachedResult.skill.skill_id,
|
|
14980
14701
|
selected_endpoint_id: cachedResult.endpointId ?? cachedResult.trace.endpoint_id
|
|
14981
14702
|
});
|
|
14982
|
-
return buildCachedResultResponse(cachedResult, "marketplace",
|
|
14703
|
+
return buildCachedResultResponse(cachedResult, "marketplace", finalize("route-cache", cachedResult.result, cachedResult.skill.skill_id, cachedResult.skill, cachedResult.trace));
|
|
14983
14704
|
}
|
|
14984
14705
|
}
|
|
14985
14706
|
}
|
|
@@ -15093,15 +14814,13 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15093
14814
|
timing.execute_ms = Date.now() - te02;
|
|
15094
14815
|
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15095
14816
|
timing.cache_hit = true;
|
|
15096
|
-
promoteResultSnapshot(cacheKey, skill, params.endpoint_id ?? cached.entry.endpointId, execOut.result, execOut.trace
|
|
14817
|
+
promoteResultSnapshot(cacheKey, skill, params.endpoint_id ?? cached.entry.endpointId, execOut.result, execOut.trace);
|
|
15097
14818
|
return {
|
|
15098
14819
|
result: execOut.result,
|
|
15099
14820
|
trace: execOut.trace,
|
|
15100
14821
|
source: "marketplace",
|
|
15101
14822
|
skill,
|
|
15102
|
-
timing:
|
|
15103
|
-
response_schema: execOut.response_schema,
|
|
15104
|
-
extraction_hints: execOut.extraction_hints
|
|
14823
|
+
timing: finalize("route-cache", execOut.result, cached.entry.skillId, skill, execOut.trace)
|
|
15105
14824
|
};
|
|
15106
14825
|
}
|
|
15107
14826
|
} catch {
|
|
@@ -15208,15 +14927,13 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15208
14927
|
])));
|
|
15209
14928
|
timing.execute_ms = Date.now() - te02;
|
|
15210
14929
|
cacheResolvedSkill(cacheKey, winner.candidate.skill, winner.trace.endpoint_id);
|
|
15211
|
-
promoteResultSnapshot(cacheKey, winner.candidate.skill, winner.trace.endpoint_id, winner.result, winner.trace
|
|
14930
|
+
promoteResultSnapshot(cacheKey, winner.candidate.skill, winner.trace.endpoint_id, winner.result, winner.trace);
|
|
15212
14931
|
return {
|
|
15213
14932
|
result: winner.result,
|
|
15214
14933
|
trace: winner.trace,
|
|
15215
14934
|
source: "marketplace",
|
|
15216
14935
|
skill: winner.candidate.skill,
|
|
15217
|
-
timing:
|
|
15218
|
-
response_schema: winner.response_schema,
|
|
15219
|
-
extraction_hints: winner.extraction_hints
|
|
14936
|
+
timing: finalize("marketplace", winner.result, winner.candidate.skill.skill_id, winner.candidate.skill, winner.trace)
|
|
15220
14937
|
};
|
|
15221
14938
|
} catch (err) {
|
|
15222
14939
|
console.log(`[race] all candidates failed after ${Date.now() - te02}ms: ${err.message}`);
|
|
@@ -15253,7 +14970,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15253
14970
|
completed_at: new Date().toISOString(),
|
|
15254
14971
|
success: true
|
|
15255
14972
|
};
|
|
15256
|
-
const t =
|
|
14973
|
+
const t = finalize("direct-fetch", data, "direct-fetch", undefined, trace2);
|
|
15257
14974
|
console.log(`[direct-fetch] ${context.url} returned JSON directly — skipping browser`);
|
|
15258
14975
|
return {
|
|
15259
14976
|
result: data,
|
|
@@ -15285,7 +15002,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15285
15002
|
success: true,
|
|
15286
15003
|
network_events: firstPassResult.interceptedEntries
|
|
15287
15004
|
};
|
|
15288
|
-
const t =
|
|
15005
|
+
const t = finalize("first-pass", firstPassResult.result, firstPassResult.miniSkill.skill_id, firstPassResult.miniSkill, trace2);
|
|
15289
15006
|
return {
|
|
15290
15007
|
result: firstPassResult.result,
|
|
15291
15008
|
trace: trace2,
|
|
@@ -15321,7 +15038,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15321
15038
|
completed_at: fpNow,
|
|
15322
15039
|
success: true
|
|
15323
15040
|
};
|
|
15324
|
-
const t =
|
|
15041
|
+
const t = finalize("browse-session", null, "browse-session", undefined, trace2);
|
|
15325
15042
|
return {
|
|
15326
15043
|
result: {
|
|
15327
15044
|
status: "browse_session_open",
|
|
@@ -15359,15 +15076,13 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15359
15076
|
if (agentChoseEndpoint) {
|
|
15360
15077
|
const execOut = await executeSkill(domainHit.skill, { ...params, endpoint_id: params.endpoint_id ?? domainHit.endpointId, ...queryIntent !== intent ? { intent: queryIntent } : {} }, projection, { ...options, intent: queryIntent, contextUrl: context?.url });
|
|
15361
15078
|
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15362
|
-
promoteResultSnapshot(cacheKey, domainHit.skill, params.endpoint_id ?? domainHit.endpointId, execOut.result, execOut.trace
|
|
15079
|
+
promoteResultSnapshot(cacheKey, domainHit.skill, params.endpoint_id ?? domainHit.endpointId, execOut.result, execOut.trace);
|
|
15363
15080
|
return {
|
|
15364
15081
|
result: execOut.result,
|
|
15365
15082
|
trace: execOut.trace,
|
|
15366
15083
|
source: "marketplace",
|
|
15367
15084
|
skill: domainHit.skill,
|
|
15368
|
-
timing:
|
|
15369
|
-
response_schema: execOut.response_schema,
|
|
15370
|
-
extraction_hints: execOut.extraction_hints
|
|
15085
|
+
timing: finalize("marketplace", execOut.result, domainHit.skill.skill_id, domainHit.skill, execOut.trace)
|
|
15371
15086
|
};
|
|
15372
15087
|
}
|
|
15373
15088
|
invalidateResolveCacheEntries([cacheKey], requestedDomainCacheKey ? [requestedDomainCacheKey] : []);
|
|
@@ -15403,7 +15118,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15403
15118
|
trace,
|
|
15404
15119
|
source: "live-capture",
|
|
15405
15120
|
skill: await getOrCreateBrowserCaptureSkill(),
|
|
15406
|
-
timing:
|
|
15121
|
+
timing: finalize("live-capture", result, undefined, undefined, trace)
|
|
15407
15122
|
};
|
|
15408
15123
|
}
|
|
15409
15124
|
if (learned_skill) {
|
|
@@ -15422,7 +15137,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15422
15137
|
trace,
|
|
15423
15138
|
source: "live-capture",
|
|
15424
15139
|
skill: await getOrCreateBrowserCaptureSkill(),
|
|
15425
|
-
timing:
|
|
15140
|
+
timing: finalize("live-capture", result, undefined, undefined, trace)
|
|
15426
15141
|
};
|
|
15427
15142
|
}
|
|
15428
15143
|
}
|
|
@@ -15512,7 +15227,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15512
15227
|
trace: rejectedTrace,
|
|
15513
15228
|
source: "live-capture",
|
|
15514
15229
|
skill: captureSkill,
|
|
15515
|
-
timing:
|
|
15230
|
+
timing: finalize("live-capture", result, undefined, undefined, rejectedTrace)
|
|
15516
15231
|
};
|
|
15517
15232
|
}
|
|
15518
15233
|
if (learned_skill && learnedSkillUsable) {
|
|
@@ -15546,7 +15261,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15546
15261
|
trace,
|
|
15547
15262
|
source: "live-capture",
|
|
15548
15263
|
skill: captureSkill,
|
|
15549
|
-
timing:
|
|
15264
|
+
timing: finalize("live-capture", result, undefined, undefined, trace)
|
|
15550
15265
|
};
|
|
15551
15266
|
}
|
|
15552
15267
|
const hasNonDomApiEndpoints = !!learned_skill?.endpoints?.some((ep) => !ep.dom_extraction && ep.method !== "WS");
|
|
@@ -15560,7 +15275,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15560
15275
|
trace,
|
|
15561
15276
|
source: directExtractionSource === "html-embedded" ? "live-capture" : "dom-fallback",
|
|
15562
15277
|
skill: learned_skill,
|
|
15563
|
-
timing:
|
|
15278
|
+
timing: finalize(directExtractionSource === "html-embedded" ? "live-capture" : "dom-fallback", result, learned_skill.skill_id, learned_skill, trace)
|
|
15564
15279
|
};
|
|
15565
15280
|
queuePassivePublishIfExecuted(learned_skill, direct, parityBaseline);
|
|
15566
15281
|
return direct;
|
|
@@ -15570,7 +15285,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15570
15285
|
trace,
|
|
15571
15286
|
source: "dom-fallback",
|
|
15572
15287
|
skill: captureSkill,
|
|
15573
|
-
timing:
|
|
15288
|
+
timing: finalize("dom-fallback", result, undefined, undefined, trace)
|
|
15574
15289
|
};
|
|
15575
15290
|
}
|
|
15576
15291
|
if (!learned_skill) {
|
|
@@ -15579,7 +15294,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15579
15294
|
trace,
|
|
15580
15295
|
source: "live-capture",
|
|
15581
15296
|
skill: captureSkill,
|
|
15582
|
-
timing:
|
|
15297
|
+
timing: finalize("live-capture", result, undefined, undefined, trace)
|
|
15583
15298
|
};
|
|
15584
15299
|
}
|
|
15585
15300
|
if (agentChoseEndpoint && learned_skill) {
|
|
@@ -15598,22 +15313,18 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15598
15313
|
trace: execOut.trace,
|
|
15599
15314
|
source: "live-capture",
|
|
15600
15315
|
skill: learned_skill,
|
|
15601
|
-
timing:
|
|
15602
|
-
response_schema: execOut.response_schema,
|
|
15603
|
-
extraction_hints: execOut.extraction_hints
|
|
15316
|
+
timing: finalize("live-capture", execOut.result, learned_skill.skill_id, learned_skill, execOut.trace)
|
|
15604
15317
|
}, parityBaseline);
|
|
15605
15318
|
}
|
|
15606
15319
|
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15607
|
-
promoteResultSnapshot(cacheKey, learned_skill, execOut.trace.endpoint_id, execOut.result, execOut.trace
|
|
15320
|
+
promoteResultSnapshot(cacheKey, learned_skill, execOut.trace.endpoint_id, execOut.result, execOut.trace);
|
|
15608
15321
|
}
|
|
15609
15322
|
return {
|
|
15610
15323
|
result: execOut.result,
|
|
15611
15324
|
trace: execOut.trace,
|
|
15612
15325
|
source: "live-capture",
|
|
15613
15326
|
skill: learned_skill,
|
|
15614
|
-
timing:
|
|
15615
|
-
response_schema: execOut.response_schema,
|
|
15616
|
-
extraction_hints: execOut.extraction_hints
|
|
15327
|
+
timing: finalize("live-capture", execOut.result, learned_skill.skill_id, learned_skill, execOut.trace)
|
|
15617
15328
|
};
|
|
15618
15329
|
}
|
|
15619
15330
|
const deferred = await buildDeferralWithAutoExec(learned_skill, "live-capture", authRecommended ? {
|
|
@@ -16699,7 +16410,19 @@ async function registerRoutes(app) {
|
|
|
16699
16410
|
app.get("/v1/skills/:skill_id", async (req, reply) => {
|
|
16700
16411
|
const clientScope = clientScopeFor(req);
|
|
16701
16412
|
const { skill_id } = req.params;
|
|
16702
|
-
|
|
16413
|
+
let skill = getRecentLocalSkill(skill_id, clientScope);
|
|
16414
|
+
if (!skill) {
|
|
16415
|
+
for (const [, entry] of domainSkillCache) {
|
|
16416
|
+
if (entry.skillId === skill_id && entry.localSkillPath) {
|
|
16417
|
+
try {
|
|
16418
|
+
skill = JSON.parse(__require("fs").readFileSync(entry.localSkillPath, "utf-8"));
|
|
16419
|
+
} catch {}
|
|
16420
|
+
break;
|
|
16421
|
+
}
|
|
16422
|
+
}
|
|
16423
|
+
}
|
|
16424
|
+
if (!skill)
|
|
16425
|
+
skill = await getSkill2(skill_id, clientScope);
|
|
16703
16426
|
if (!skill)
|
|
16704
16427
|
return reply.code(404).send({ error: "Skill not found" });
|
|
16705
16428
|
return reply.send(skill);
|
|
@@ -16741,7 +16464,20 @@ async function registerRoutes(app) {
|
|
|
16741
16464
|
const clientScope = clientScopeFor(req);
|
|
16742
16465
|
const { skill_id } = req.params;
|
|
16743
16466
|
const { params, projection, confirm_unsafe, dry_run, intent, context_url } = req.body;
|
|
16744
|
-
|
|
16467
|
+
let skill = getRecentLocalSkill(skill_id, clientScope);
|
|
16468
|
+
if (!skill) {
|
|
16469
|
+
const { findExistingSkillForDomain: findLocal } = await Promise.resolve().then(() => (init_client2(), exports_client2));
|
|
16470
|
+
for (const [, entry] of domainSkillCache) {
|
|
16471
|
+
if (entry.skillId === skill_id && entry.localSkillPath) {
|
|
16472
|
+
try {
|
|
16473
|
+
skill = JSON.parse(__require("fs").readFileSync(entry.localSkillPath, "utf-8"));
|
|
16474
|
+
} catch {}
|
|
16475
|
+
break;
|
|
16476
|
+
}
|
|
16477
|
+
}
|
|
16478
|
+
}
|
|
16479
|
+
if (!skill)
|
|
16480
|
+
skill = await getSkill2(skill_id, clientScope);
|
|
16745
16481
|
if (!skill)
|
|
16746
16482
|
return reply.code(404).send({ error: "Skill not found" });
|
|
16747
16483
|
const execParams = {
|
|
@@ -17248,13 +16984,6 @@ async function startUnbrowseServer(options = {}) {
|
|
|
17248
16984
|
try {
|
|
17249
16985
|
execSync3("pkill -f chrome-headless-shell", { stdio: "ignore" });
|
|
17250
16986
|
} catch {}
|
|
17251
|
-
try {
|
|
17252
|
-
await start();
|
|
17253
|
-
const h = await health();
|
|
17254
|
-
console.log(`[startup] Kuri ready — ${h.tabs ?? 0} tabs`);
|
|
17255
|
-
} catch (err) {
|
|
17256
|
-
console.warn(`[startup] WARNING: Kuri not available. Capture will start it on demand. ${err instanceof Error ? err.message : err}`);
|
|
17257
|
-
}
|
|
17258
16987
|
await ensureRegistered2();
|
|
17259
16988
|
const app = Fastify({ logger: options.logger ?? true });
|
|
17260
16989
|
await app.register(cors, { origin: true });
|
|
@@ -17285,7 +17014,6 @@ var init_server = __esm(async () => {
|
|
|
17285
17014
|
init_verification();
|
|
17286
17015
|
init_client2();
|
|
17287
17016
|
init_capture();
|
|
17288
|
-
init_client();
|
|
17289
17017
|
await init_routes();
|
|
17290
17018
|
});
|
|
17291
17019
|
|
|
@@ -18281,184 +18009,6 @@ function normalizeSetupScope(value) {
|
|
|
18281
18009
|
return normalized;
|
|
18282
18010
|
return "auto";
|
|
18283
18011
|
}
|
|
18284
|
-
function buildEntityIndex(items) {
|
|
18285
|
-
const index = new Map;
|
|
18286
|
-
for (const item of items) {
|
|
18287
|
-
if (item != null && typeof item === "object") {
|
|
18288
|
-
const urn = item.entityUrn;
|
|
18289
|
-
if (typeof urn === "string")
|
|
18290
|
-
index.set(urn, item);
|
|
18291
|
-
}
|
|
18292
|
-
}
|
|
18293
|
-
return index;
|
|
18294
|
-
}
|
|
18295
|
-
function detectEntityIndex(data) {
|
|
18296
|
-
if (data == null || typeof data !== "object")
|
|
18297
|
-
return null;
|
|
18298
|
-
let best = null;
|
|
18299
|
-
const check = (arr) => {
|
|
18300
|
-
if (arr.length < 2)
|
|
18301
|
-
return;
|
|
18302
|
-
const sample = arr.slice(0, 10);
|
|
18303
|
-
const withUrn = sample.filter((i) => i != null && typeof i === "object" && typeof i.entityUrn === "string").length;
|
|
18304
|
-
if (withUrn >= sample.length * 0.5 && (!best || arr.length > best.length)) {
|
|
18305
|
-
best = arr;
|
|
18306
|
-
}
|
|
18307
|
-
};
|
|
18308
|
-
const obj = data;
|
|
18309
|
-
for (const val of Object.values(obj)) {
|
|
18310
|
-
if (Array.isArray(val)) {
|
|
18311
|
-
check(val);
|
|
18312
|
-
} else if (val != null && typeof val === "object" && !Array.isArray(val)) {
|
|
18313
|
-
for (const nested of Object.values(val)) {
|
|
18314
|
-
if (Array.isArray(nested))
|
|
18315
|
-
check(nested);
|
|
18316
|
-
}
|
|
18317
|
-
}
|
|
18318
|
-
}
|
|
18319
|
-
return best ? buildEntityIndex(best) : null;
|
|
18320
|
-
}
|
|
18321
|
-
function resolvePath2(obj, path9, entityIndex) {
|
|
18322
|
-
if (!path9 || obj == null)
|
|
18323
|
-
return obj;
|
|
18324
|
-
const segments = path9.split(".");
|
|
18325
|
-
let cur = obj;
|
|
18326
|
-
for (let i = 0;i < segments.length; i++) {
|
|
18327
|
-
if (cur == null)
|
|
18328
|
-
return;
|
|
18329
|
-
const seg = segments[i];
|
|
18330
|
-
if (seg.endsWith("[]")) {
|
|
18331
|
-
const key = seg.slice(0, -2);
|
|
18332
|
-
const arr = key ? cur[key] : cur;
|
|
18333
|
-
if (!Array.isArray(arr))
|
|
18334
|
-
return;
|
|
18335
|
-
const remaining = segments.slice(i + 1).join(".");
|
|
18336
|
-
if (!remaining)
|
|
18337
|
-
return arr;
|
|
18338
|
-
return arr.flatMap((item) => {
|
|
18339
|
-
const v = resolvePath2(item, remaining, entityIndex);
|
|
18340
|
-
return v === undefined ? [] : Array.isArray(v) ? v : [v];
|
|
18341
|
-
});
|
|
18342
|
-
}
|
|
18343
|
-
const indexMatch = seg.match(/^(.+?)\[(\d+)\]$/);
|
|
18344
|
-
if (indexMatch) {
|
|
18345
|
-
const key = indexMatch[1];
|
|
18346
|
-
const idx = parseInt(indexMatch[2], 10);
|
|
18347
|
-
const arr = key ? cur[key] : cur;
|
|
18348
|
-
if (!Array.isArray(arr) || idx >= arr.length)
|
|
18349
|
-
return;
|
|
18350
|
-
cur = arr[idx];
|
|
18351
|
-
continue;
|
|
18352
|
-
}
|
|
18353
|
-
const rec = cur;
|
|
18354
|
-
let val = rec[seg];
|
|
18355
|
-
if (val == null && entityIndex) {
|
|
18356
|
-
const ref = rec[`*${seg}`];
|
|
18357
|
-
if (typeof ref === "string") {
|
|
18358
|
-
val = entityIndex.get(ref);
|
|
18359
|
-
}
|
|
18360
|
-
}
|
|
18361
|
-
cur = val;
|
|
18362
|
-
}
|
|
18363
|
-
return cur;
|
|
18364
|
-
}
|
|
18365
|
-
function extractFields(data, fields, entityIndex) {
|
|
18366
|
-
if (data == null)
|
|
18367
|
-
return data;
|
|
18368
|
-
function mapItem(item) {
|
|
18369
|
-
const out = {};
|
|
18370
|
-
for (const f of fields) {
|
|
18371
|
-
const colonIdx = f.indexOf(":");
|
|
18372
|
-
const alias = colonIdx >= 0 ? f.slice(0, colonIdx) : f.split(".").pop();
|
|
18373
|
-
const path9 = colonIdx >= 0 ? f.slice(colonIdx + 1) : f;
|
|
18374
|
-
const resolved = resolvePath2(item, path9, entityIndex ?? undefined) ?? [];
|
|
18375
|
-
out[alias] = Array.isArray(resolved) ? resolved.length === 0 ? null : resolved.length === 1 ? resolved[0] : resolved : resolved;
|
|
18376
|
-
}
|
|
18377
|
-
return out;
|
|
18378
|
-
}
|
|
18379
|
-
function hasValue(v) {
|
|
18380
|
-
if (v == null)
|
|
18381
|
-
return false;
|
|
18382
|
-
if (Array.isArray(v))
|
|
18383
|
-
return v.length > 0;
|
|
18384
|
-
return true;
|
|
18385
|
-
}
|
|
18386
|
-
if (Array.isArray(data)) {
|
|
18387
|
-
return data.map(mapItem).filter((row) => Object.values(row).some(hasValue));
|
|
18388
|
-
}
|
|
18389
|
-
return mapItem(data);
|
|
18390
|
-
}
|
|
18391
|
-
function hasMeaningfulValue(value) {
|
|
18392
|
-
if (value == null)
|
|
18393
|
-
return false;
|
|
18394
|
-
if (typeof value === "string")
|
|
18395
|
-
return value.trim().length > 0;
|
|
18396
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
18397
|
-
return true;
|
|
18398
|
-
if (Array.isArray(value))
|
|
18399
|
-
return value.some((item) => hasMeaningfulValue(item));
|
|
18400
|
-
if (typeof value === "object")
|
|
18401
|
-
return Object.values(value).some((item) => hasMeaningfulValue(item));
|
|
18402
|
-
return false;
|
|
18403
|
-
}
|
|
18404
|
-
function isPlainRecord(value) {
|
|
18405
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
18406
|
-
}
|
|
18407
|
-
function isScalarLike(value) {
|
|
18408
|
-
if (value == null)
|
|
18409
|
-
return false;
|
|
18410
|
-
if (typeof value === "string")
|
|
18411
|
-
return value.trim().length > 0;
|
|
18412
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
18413
|
-
return true;
|
|
18414
|
-
if (Array.isArray(value)) {
|
|
18415
|
-
return value.length > 0 && value.every((item) => item == null || typeof item === "string" || typeof item === "number" || typeof item === "boolean");
|
|
18416
|
-
}
|
|
18417
|
-
return false;
|
|
18418
|
-
}
|
|
18419
|
-
function looksStructuredForDirectOutput(value) {
|
|
18420
|
-
if (Array.isArray(value)) {
|
|
18421
|
-
const sample = value.filter(isPlainRecord).slice(0, 3);
|
|
18422
|
-
if (sample.length === 0)
|
|
18423
|
-
return false;
|
|
18424
|
-
const simpleRows = sample.filter((row) => {
|
|
18425
|
-
const keys2 = Object.keys(row);
|
|
18426
|
-
const scalarFields2 = Object.values(row).filter(isScalarLike).length;
|
|
18427
|
-
return keys2.length > 0 && keys2.length <= 20 && scalarFields2 >= 2;
|
|
18428
|
-
});
|
|
18429
|
-
return simpleRows.length >= Math.ceil(sample.length / 2);
|
|
18430
|
-
}
|
|
18431
|
-
if (!isPlainRecord(value))
|
|
18432
|
-
return false;
|
|
18433
|
-
const keys = Object.keys(value);
|
|
18434
|
-
if (keys.length === 0 || keys.length > 20)
|
|
18435
|
-
return false;
|
|
18436
|
-
const scalarFields = Object.values(value).filter(isScalarLike).length;
|
|
18437
|
-
return scalarFields >= 2;
|
|
18438
|
-
}
|
|
18439
|
-
function applyTransforms(result, flags) {
|
|
18440
|
-
let data = result;
|
|
18441
|
-
const entityIndex = detectEntityIndex(result);
|
|
18442
|
-
const pathFlag = flags.path;
|
|
18443
|
-
if (pathFlag) {
|
|
18444
|
-
data = resolvePath2(data, pathFlag, entityIndex);
|
|
18445
|
-
if (data === undefined) {
|
|
18446
|
-
process.stderr.write(`[unbrowse] warning: --path "${pathFlag}" resolved to undefined. Check path against response structure.
|
|
18447
|
-
`);
|
|
18448
|
-
return [];
|
|
18449
|
-
}
|
|
18450
|
-
}
|
|
18451
|
-
const extractFlag = flags.extract;
|
|
18452
|
-
if (extractFlag) {
|
|
18453
|
-
const fields = extractFlag.split(",").map((f) => f.trim());
|
|
18454
|
-
data = extractFields(data, fields, entityIndex);
|
|
18455
|
-
}
|
|
18456
|
-
const limitFlag = flags.limit;
|
|
18457
|
-
if (limitFlag && Array.isArray(data)) {
|
|
18458
|
-
data = data.slice(0, Number(limitFlag));
|
|
18459
|
-
}
|
|
18460
|
-
return data;
|
|
18461
|
-
}
|
|
18462
18012
|
function slimTrace(obj) {
|
|
18463
18013
|
const trace = obj.trace;
|
|
18464
18014
|
const out = {
|
|
@@ -18474,67 +18024,14 @@ function slimTrace(obj) {
|
|
|
18474
18024
|
};
|
|
18475
18025
|
if ("result" in obj)
|
|
18476
18026
|
out.result = obj.result;
|
|
18027
|
+
if (obj.available_endpoints)
|
|
18028
|
+
out.available_endpoints = obj.available_endpoints;
|
|
18029
|
+
if (obj.source)
|
|
18030
|
+
out.source = obj.source;
|
|
18031
|
+
if (obj.skill)
|
|
18032
|
+
out.skill = obj.skill;
|
|
18477
18033
|
return out;
|
|
18478
18034
|
}
|
|
18479
|
-
function wrapWithHints(obj) {
|
|
18480
|
-
const hints = obj.extraction_hints;
|
|
18481
|
-
if (!hints)
|
|
18482
|
-
return obj;
|
|
18483
|
-
const resultStr = JSON.stringify(obj.result ?? "");
|
|
18484
|
-
if (resultStr.length < 2000)
|
|
18485
|
-
return obj;
|
|
18486
|
-
const trace = obj.trace;
|
|
18487
|
-
return {
|
|
18488
|
-
trace: trace ? {
|
|
18489
|
-
trace_id: trace.trace_id,
|
|
18490
|
-
skill_id: trace.skill_id,
|
|
18491
|
-
endpoint_id: trace.endpoint_id,
|
|
18492
|
-
success: trace.success,
|
|
18493
|
-
status_code: trace.status_code
|
|
18494
|
-
} : undefined,
|
|
18495
|
-
_response_too_large: `${resultStr.length} bytes \u2014 use extraction flags below to get structured data`,
|
|
18496
|
-
extraction_hints: hints
|
|
18497
|
-
};
|
|
18498
|
-
}
|
|
18499
|
-
function schemaOnly(obj) {
|
|
18500
|
-
const trace = obj.trace;
|
|
18501
|
-
return {
|
|
18502
|
-
trace: trace ? { trace_id: trace.trace_id, skill_id: trace.skill_id, endpoint_id: trace.endpoint_id, success: trace.success } : undefined,
|
|
18503
|
-
extraction_hints: obj.extraction_hints ?? null,
|
|
18504
|
-
response_schema: obj.response_schema ?? null
|
|
18505
|
-
};
|
|
18506
|
-
}
|
|
18507
|
-
function autoExtractOrWrap(obj) {
|
|
18508
|
-
const hints = obj.extraction_hints;
|
|
18509
|
-
const resultStr = JSON.stringify(obj.result ?? "");
|
|
18510
|
-
if (resultStr.length < 2000)
|
|
18511
|
-
return obj;
|
|
18512
|
-
if (looksStructuredForDirectOutput(obj.result)) {
|
|
18513
|
-
return slimTrace({ ...obj, extraction_hints: undefined, response_schema: undefined });
|
|
18514
|
-
}
|
|
18515
|
-
if (!hints)
|
|
18516
|
-
return obj;
|
|
18517
|
-
if (hints.confidence === "high") {
|
|
18518
|
-
const syntheticFlags = {};
|
|
18519
|
-
if (hints.path)
|
|
18520
|
-
syntheticFlags.path = hints.path;
|
|
18521
|
-
if (hints.fields.length > 0)
|
|
18522
|
-
syntheticFlags.extract = hints.fields.join(",");
|
|
18523
|
-
syntheticFlags.limit = "20";
|
|
18524
|
-
const extracted = applyTransforms(obj.result, syntheticFlags);
|
|
18525
|
-
if (!hasMeaningfulValue(extracted))
|
|
18526
|
-
return wrapWithHints(obj);
|
|
18527
|
-
const slimmed = slimTrace({ ...obj, result: extracted });
|
|
18528
|
-
slimmed._auto_extracted = {
|
|
18529
|
-
applied: hints.cli_args,
|
|
18530
|
-
confidence: hints.confidence,
|
|
18531
|
-
all_fields: hints.schema_tree,
|
|
18532
|
-
note: "Auto-extracted using response_schema. Add/remove fields with --extract, change array with --path, or use --raw for full response."
|
|
18533
|
-
};
|
|
18534
|
-
return slimmed;
|
|
18535
|
-
}
|
|
18536
|
-
return wrapWithHints(obj);
|
|
18537
|
-
}
|
|
18538
18035
|
async function cmdHealth(flags) {
|
|
18539
18036
|
output(await api3("GET", "/health"), !!flags.pretty);
|
|
18540
18037
|
}
|
|
@@ -18546,6 +18043,8 @@ async function cmdResolve(flags) {
|
|
|
18546
18043
|
const url = flags.url;
|
|
18547
18044
|
const domain = flags.domain;
|
|
18548
18045
|
const explicitEndpointId = flags["endpoint-id"];
|
|
18046
|
+
const autoExecute = !!flags.execute;
|
|
18047
|
+
const extraParams = flags.params ? JSON.parse(flags.params) : {};
|
|
18549
18048
|
if (url) {
|
|
18550
18049
|
body.params = { url };
|
|
18551
18050
|
body.context = { url };
|
|
@@ -18557,14 +18056,19 @@ async function cmdResolve(flags) {
|
|
|
18557
18056
|
body.params = { ...body.params ?? {}, endpoint_id: explicitEndpointId };
|
|
18558
18057
|
}
|
|
18559
18058
|
if (flags.params) {
|
|
18560
|
-
body.params = { ...body.params ?? {}, ...
|
|
18059
|
+
body.params = { ...body.params ?? {}, ...extraParams };
|
|
18561
18060
|
}
|
|
18562
18061
|
if (flags["dry-run"])
|
|
18563
18062
|
body.dry_run = true;
|
|
18564
18063
|
if (flags["force-capture"])
|
|
18565
18064
|
body.force_capture = true;
|
|
18566
|
-
const hasTransforms = !!(flags.path || flags.extract);
|
|
18567
18065
|
body.projection = { raw: true };
|
|
18066
|
+
function execBody(endpointId) {
|
|
18067
|
+
return { params: { endpoint_id: endpointId, ...extraParams }, intent, projection: { raw: true } };
|
|
18068
|
+
}
|
|
18069
|
+
function resolveSkillId() {
|
|
18070
|
+
return result.skill?.skill_id ?? result.skill_id;
|
|
18071
|
+
}
|
|
18568
18072
|
const startedAt = Date.now();
|
|
18569
18073
|
let result = await withPendingNotice(api3("POST", "/v1/intent/resolve", body), "Still working. First-time capture/indexing for a site can take 20-80s. Waiting is usually better than falling back.");
|
|
18570
18074
|
const resultError = result.result?.error ?? result.error;
|
|
@@ -18582,14 +18086,18 @@ async function cmdResolve(flags) {
|
|
|
18582
18086
|
}
|
|
18583
18087
|
}
|
|
18584
18088
|
if (explicitEndpointId && result.available_endpoints) {
|
|
18585
|
-
const skillId =
|
|
18089
|
+
const skillId = resolveSkillId();
|
|
18586
18090
|
if (skillId) {
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18091
|
+
result = await withPendingNotice(api3("POST", `/v1/skills/${skillId}/execute`, execBody(explicitEndpointId)), "Executing selected endpoint...");
|
|
18092
|
+
}
|
|
18093
|
+
}
|
|
18094
|
+
if (autoExecute && result.available_endpoints && !result.result) {
|
|
18095
|
+
const endpoints = result.available_endpoints;
|
|
18096
|
+
const skillId = resolveSkillId();
|
|
18097
|
+
if (skillId && endpoints.length > 0) {
|
|
18098
|
+
const bestEndpoint = endpoints[0];
|
|
18099
|
+
info(`Auto-executing endpoint: ${bestEndpoint.description ?? bestEndpoint.endpoint_id}`);
|
|
18100
|
+
result = await withPendingNotice(api3("POST", `/v1/skills/${skillId}/execute`, execBody(bestEndpoint.endpoint_id)), "Executing best endpoint...");
|
|
18593
18101
|
}
|
|
18594
18102
|
}
|
|
18595
18103
|
const resultObj = result.result;
|
|
@@ -18606,13 +18114,7 @@ async function cmdResolve(flags) {
|
|
|
18606
18114
|
if (Date.now() - startedAt > 3000 && result.source === "live-capture") {
|
|
18607
18115
|
info("Live capture finished. Future runs against this site should be much faster.");
|
|
18608
18116
|
}
|
|
18609
|
-
|
|
18610
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18611
|
-
return;
|
|
18612
|
-
}
|
|
18613
|
-
if (hasTransforms && result.result != null) {
|
|
18614
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18615
|
-
}
|
|
18117
|
+
result = slimTrace(result);
|
|
18616
18118
|
const skill = result.skill;
|
|
18617
18119
|
const trace = result.trace;
|
|
18618
18120
|
if (skill?.skill_id && trace) {
|
|
@@ -18641,16 +18143,9 @@ async function cmdExecute(flags) {
|
|
|
18641
18143
|
body.dry_run = true;
|
|
18642
18144
|
if (flags["confirm-unsafe"])
|
|
18643
18145
|
body.confirm_unsafe = true;
|
|
18644
|
-
const hasTransforms = !!(flags.path || flags.extract);
|
|
18645
18146
|
body.projection = { raw: true };
|
|
18646
18147
|
let result = await withPendingNotice(api3("POST", `/v1/skills/${skillId}/execute`, body), "Still working. This endpoint may require browser replay or first-time auth/capture setup.");
|
|
18647
|
-
|
|
18648
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18649
|
-
return;
|
|
18650
|
-
}
|
|
18651
|
-
if (hasTransforms && result.result != null) {
|
|
18652
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18653
|
-
}
|
|
18148
|
+
result = slimTrace(result);
|
|
18654
18149
|
output(result, !!flags.pretty);
|
|
18655
18150
|
}
|
|
18656
18151
|
async function cmdFeedback(flags) {
|
|
@@ -18745,7 +18240,7 @@ var CLI_REFERENCE = {
|
|
|
18745
18240
|
commands: [
|
|
18746
18241
|
{ name: "health", usage: "", desc: "Server health check" },
|
|
18747
18242
|
{ name: "setup", usage: "[--opencode auto|global|project|off] [--no-start]", desc: "Bootstrap browser deps + Open Code command" },
|
|
18748
|
-
{ name: "resolve", usage: '--intent "..." --url "..." [opts]', desc: "Resolve intent \u2192
|
|
18243
|
+
{ name: "resolve", usage: '--intent "..." --url "..." [opts]', desc: "Resolve intent \u2192 find skill + execute" },
|
|
18749
18244
|
{ name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
|
|
18750
18245
|
{ name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
|
|
18751
18246
|
{ name: "login", usage: '--url "..."', desc: "Interactive browser login" },
|
|
@@ -18773,15 +18268,11 @@ var CLI_REFERENCE = {
|
|
|
18773
18268
|
globalFlags: [
|
|
18774
18269
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
18775
18270
|
{ flag: "--no-auto-start", desc: "Don't auto-start server" },
|
|
18776
|
-
{ flag: "--raw", desc: "Return raw response data (skip server-side projection)" },
|
|
18777
18271
|
{ flag: "--skip-browser", desc: "setup: skip browser-engine install" },
|
|
18778
18272
|
{ flag: "--opencode auto|global|project|off", desc: "setup: install /unbrowse command for Open Code" }
|
|
18779
18273
|
],
|
|
18780
18274
|
resolveExecuteFlags: [
|
|
18781
|
-
{ flag: "--
|
|
18782
|
-
{ flag: '--path "data.items[]"', desc: "Drill into result before extract/output" },
|
|
18783
|
-
{ flag: '--extract "field1,alias:deep.path.to.val"', desc: "Pick specific fields (no piping needed)" },
|
|
18784
|
-
{ flag: "--limit N", desc: "Cap array output to N items" },
|
|
18275
|
+
{ flag: "--execute", desc: "Auto-pick best endpoint and return data (resolve only)" },
|
|
18785
18276
|
{ flag: "--endpoint-id ID", desc: "Pick a specific endpoint" },
|
|
18786
18277
|
{ flag: "--dry-run", desc: "Preview mutations" },
|
|
18787
18278
|
{ flag: "--force-capture", desc: "Bypass caches, re-capture" },
|
|
@@ -18789,10 +18280,9 @@ var CLI_REFERENCE = {
|
|
|
18789
18280
|
],
|
|
18790
18281
|
examples: [
|
|
18791
18282
|
"unbrowse setup",
|
|
18283
|
+
'unbrowse resolve --intent "top stories" --url "https://news.ycombinator.com" --execute',
|
|
18792
18284
|
'unbrowse resolve --intent "get timeline" --url "https://x.com"',
|
|
18793
18285
|
"unbrowse execute --skill abc --endpoint def --pretty",
|
|
18794
|
-
'unbrowse execute --skill abc --endpoint def --extract "user,text,likes" --limit 10',
|
|
18795
|
-
'unbrowse execute --skill abc --endpoint def --path "data.included[]" --extract "name:actor.name,text:commentary.text" --limit 20',
|
|
18796
18286
|
"unbrowse feedback --skill abc --endpoint def --rating 5"
|
|
18797
18287
|
]
|
|
18798
18288
|
};
|
|
@@ -18913,9 +18403,7 @@ async function cmdSiteTask(pack, taskName, flags) {
|
|
|
18913
18403
|
body.dry_run = true;
|
|
18914
18404
|
if (flags["force-capture"])
|
|
18915
18405
|
body.force_capture = true;
|
|
18916
|
-
|
|
18917
|
-
if (flags.raw || hasTransforms)
|
|
18918
|
-
body.projection = { raw: true };
|
|
18406
|
+
body.projection = { raw: true };
|
|
18919
18407
|
const startedAt = Date.now();
|
|
18920
18408
|
let result = await withPendingNotice(api3("POST", "/v1/intent/resolve", body), "Still working. First-time capture/indexing for a site can take 20-80s.");
|
|
18921
18409
|
if (result && typeof result === "object" && result.error === "auth_required") {
|
|
@@ -18924,15 +18412,7 @@ async function cmdSiteTask(pack, taskName, flags) {
|
|
|
18924
18412
|
output({ ...result, _deps: { ...deps2, requires: ["login"] }, _next: [`unbrowse ${pack.site} login`] }, !!flags.pretty);
|
|
18925
18413
|
process.exit(2);
|
|
18926
18414
|
}
|
|
18927
|
-
|
|
18928
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18929
|
-
return;
|
|
18930
|
-
}
|
|
18931
|
-
if (hasTransforms && result.result != null) {
|
|
18932
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18933
|
-
} else if (!flags.raw && result.result != null) {
|
|
18934
|
-
result = autoExtractOrWrap(result);
|
|
18935
|
-
}
|
|
18415
|
+
result = slimTrace(result);
|
|
18936
18416
|
const deps = buildDepsMetadata(pack, taskName);
|
|
18937
18417
|
result._deps = deps;
|
|
18938
18418
|
result._shortcut = `${pack.site} ${taskName}`;
|
|
@@ -18964,14 +18444,9 @@ async function cmdSiteBatch(pack, batchArg, flags) {
|
|
|
18964
18444
|
};
|
|
18965
18445
|
if (flags["force-capture"])
|
|
18966
18446
|
body.force_capture = true;
|
|
18967
|
-
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
let res = await api3("POST", "/v1/intent/resolve", body);
|
|
18971
|
-
if (!flags.raw && res.result != null) {
|
|
18972
|
-
res = autoExtractOrWrap(res);
|
|
18973
|
-
}
|
|
18974
|
-
return { task, result: res };
|
|
18447
|
+
body.projection = { raw: true };
|
|
18448
|
+
const res = await api3("POST", "/v1/intent/resolve", body);
|
|
18449
|
+
return { task, result: slimTrace(res) };
|
|
18975
18450
|
});
|
|
18976
18451
|
const waveResult = await Promise.all(promises);
|
|
18977
18452
|
waveResults.push({
|
|
@@ -19072,6 +18547,54 @@ async function cmdForward() {
|
|
|
19072
18547
|
async function cmdClose() {
|
|
19073
18548
|
output(await api3("POST", "/v1/browse/close"), false);
|
|
19074
18549
|
}
|
|
18550
|
+
async function cmdConnectChrome() {
|
|
18551
|
+
const { execSync: execSync4, spawn: spawnProc } = __require("child_process");
|
|
18552
|
+
try {
|
|
18553
|
+
const res = await fetch("http://127.0.0.1:9222/json/version", { signal: AbortSignal.timeout(1000) });
|
|
18554
|
+
if (res.ok) {
|
|
18555
|
+
const data = await res.json();
|
|
18556
|
+
if (!data["User-Agent"]?.includes("Headless")) {
|
|
18557
|
+
console.log("Your Chrome is already connected with CDP on port 9222.");
|
|
18558
|
+
console.log("Browse commands will use your real browser with all your sessions.");
|
|
18559
|
+
return;
|
|
18560
|
+
}
|
|
18561
|
+
}
|
|
18562
|
+
} catch {}
|
|
18563
|
+
try {
|
|
18564
|
+
execSync4("pkill -f kuri/chrome-profile", { stdio: "ignore" });
|
|
18565
|
+
} catch {}
|
|
18566
|
+
console.log("Quitting Chrome to relaunch with remote debugging...");
|
|
18567
|
+
if (process.platform === "darwin") {
|
|
18568
|
+
try {
|
|
18569
|
+
execSync4('osascript -e "quit app \\"Google Chrome\\""', { stdio: "ignore", timeout: 5000 });
|
|
18570
|
+
} catch {}
|
|
18571
|
+
} else {
|
|
18572
|
+
try {
|
|
18573
|
+
execSync4("pkill -f chrome", { stdio: "ignore" });
|
|
18574
|
+
} catch {}
|
|
18575
|
+
}
|
|
18576
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
18577
|
+
console.log("Launching Chrome with remote debugging on port 9222...");
|
|
18578
|
+
if (process.platform === "darwin") {
|
|
18579
|
+
spawnProc("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", ["--remote-debugging-port=9222", "--no-first-run", "--no-default-browser-check"], { stdio: "ignore", detached: true }).unref();
|
|
18580
|
+
} else {
|
|
18581
|
+
spawnProc("google-chrome", ["--remote-debugging-port=9222"], { stdio: "ignore", detached: true }).unref();
|
|
18582
|
+
}
|
|
18583
|
+
const deadline = Date.now() + 15000;
|
|
18584
|
+
while (Date.now() < deadline) {
|
|
18585
|
+
try {
|
|
18586
|
+
const res = await fetch("http://127.0.0.1:9222/json/version", { signal: AbortSignal.timeout(500) });
|
|
18587
|
+
if (res.ok) {
|
|
18588
|
+
console.log("Connected. Your real Chrome is now available for browse commands.");
|
|
18589
|
+
console.log("All your logged-in sessions (LinkedIn, X, etc.) will work.");
|
|
18590
|
+
console.log('Run: unbrowse go "https://linkedin.com/feed/"');
|
|
18591
|
+
return;
|
|
18592
|
+
}
|
|
18593
|
+
} catch {}
|
|
18594
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
18595
|
+
}
|
|
18596
|
+
console.error("Could not connect to Chrome. Make sure all Chrome windows are closed and try again.");
|
|
18597
|
+
}
|
|
19075
18598
|
async function main() {
|
|
19076
18599
|
const { command, args, flags } = parseArgs(process.argv);
|
|
19077
18600
|
const noAutoStart = !!flags["no-auto-start"];
|
|
@@ -19093,6 +18616,8 @@ async function main() {
|
|
|
19093
18616
|
return cmdRestart(flags);
|
|
19094
18617
|
if (command === "upgrade" || command === "update")
|
|
19095
18618
|
return cmdUpgrade(flags);
|
|
18619
|
+
if (command === "connect-chrome")
|
|
18620
|
+
return cmdConnectChrome();
|
|
19096
18621
|
const KNOWN_COMMANDS = new Set([
|
|
19097
18622
|
"health",
|
|
19098
18623
|
"setup",
|
|
@@ -19126,7 +18651,8 @@ async function main() {
|
|
|
19126
18651
|
"eval",
|
|
19127
18652
|
"back",
|
|
19128
18653
|
"forward",
|
|
19129
|
-
"close"
|
|
18654
|
+
"close",
|
|
18655
|
+
"connect-chrome"
|
|
19130
18656
|
]);
|
|
19131
18657
|
if (!KNOWN_COMMANDS.has(command)) {
|
|
19132
18658
|
const pack = findSitePack(command);
|
|
@@ -19202,6 +18728,8 @@ async function main() {
|
|
|
19202
18728
|
return cmdForward();
|
|
19203
18729
|
case "close":
|
|
19204
18730
|
return cmdClose();
|
|
18731
|
+
case "connect-chrome":
|
|
18732
|
+
return cmdConnectChrome();
|
|
19205
18733
|
default:
|
|
19206
18734
|
info(`Unknown command: ${command}`);
|
|
19207
18735
|
printHelp();
|