unbrowse 2.8.4 → 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 +147 -632
- package/package.json +1 -1
- package/runtime-src/api/routes.ts +11 -1
- 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);
|
|
@@ -17261,13 +16984,6 @@ async function startUnbrowseServer(options = {}) {
|
|
|
17261
16984
|
try {
|
|
17262
16985
|
execSync3("pkill -f chrome-headless-shell", { stdio: "ignore" });
|
|
17263
16986
|
} catch {}
|
|
17264
|
-
try {
|
|
17265
|
-
await start();
|
|
17266
|
-
const h = await health();
|
|
17267
|
-
console.log(`[startup] Kuri ready — ${h.tabs ?? 0} tabs`);
|
|
17268
|
-
} catch (err) {
|
|
17269
|
-
console.warn(`[startup] WARNING: Kuri not available. Capture will start it on demand. ${err instanceof Error ? err.message : err}`);
|
|
17270
|
-
}
|
|
17271
16987
|
await ensureRegistered2();
|
|
17272
16988
|
const app = Fastify({ logger: options.logger ?? true });
|
|
17273
16989
|
await app.register(cors, { origin: true });
|
|
@@ -17298,7 +17014,6 @@ var init_server = __esm(async () => {
|
|
|
17298
17014
|
init_verification();
|
|
17299
17015
|
init_client2();
|
|
17300
17016
|
init_capture();
|
|
17301
|
-
init_client();
|
|
17302
17017
|
await init_routes();
|
|
17303
17018
|
});
|
|
17304
17019
|
|
|
@@ -18294,184 +18009,6 @@ function normalizeSetupScope(value) {
|
|
|
18294
18009
|
return normalized;
|
|
18295
18010
|
return "auto";
|
|
18296
18011
|
}
|
|
18297
|
-
function buildEntityIndex(items) {
|
|
18298
|
-
const index = new Map;
|
|
18299
|
-
for (const item of items) {
|
|
18300
|
-
if (item != null && typeof item === "object") {
|
|
18301
|
-
const urn = item.entityUrn;
|
|
18302
|
-
if (typeof urn === "string")
|
|
18303
|
-
index.set(urn, item);
|
|
18304
|
-
}
|
|
18305
|
-
}
|
|
18306
|
-
return index;
|
|
18307
|
-
}
|
|
18308
|
-
function detectEntityIndex(data) {
|
|
18309
|
-
if (data == null || typeof data !== "object")
|
|
18310
|
-
return null;
|
|
18311
|
-
let best = null;
|
|
18312
|
-
const check = (arr) => {
|
|
18313
|
-
if (arr.length < 2)
|
|
18314
|
-
return;
|
|
18315
|
-
const sample = arr.slice(0, 10);
|
|
18316
|
-
const withUrn = sample.filter((i) => i != null && typeof i === "object" && typeof i.entityUrn === "string").length;
|
|
18317
|
-
if (withUrn >= sample.length * 0.5 && (!best || arr.length > best.length)) {
|
|
18318
|
-
best = arr;
|
|
18319
|
-
}
|
|
18320
|
-
};
|
|
18321
|
-
const obj = data;
|
|
18322
|
-
for (const val of Object.values(obj)) {
|
|
18323
|
-
if (Array.isArray(val)) {
|
|
18324
|
-
check(val);
|
|
18325
|
-
} else if (val != null && typeof val === "object" && !Array.isArray(val)) {
|
|
18326
|
-
for (const nested of Object.values(val)) {
|
|
18327
|
-
if (Array.isArray(nested))
|
|
18328
|
-
check(nested);
|
|
18329
|
-
}
|
|
18330
|
-
}
|
|
18331
|
-
}
|
|
18332
|
-
return best ? buildEntityIndex(best) : null;
|
|
18333
|
-
}
|
|
18334
|
-
function resolvePath2(obj, path9, entityIndex) {
|
|
18335
|
-
if (!path9 || obj == null)
|
|
18336
|
-
return obj;
|
|
18337
|
-
const segments = path9.split(".");
|
|
18338
|
-
let cur = obj;
|
|
18339
|
-
for (let i = 0;i < segments.length; i++) {
|
|
18340
|
-
if (cur == null)
|
|
18341
|
-
return;
|
|
18342
|
-
const seg = segments[i];
|
|
18343
|
-
if (seg.endsWith("[]")) {
|
|
18344
|
-
const key = seg.slice(0, -2);
|
|
18345
|
-
const arr = key ? cur[key] : cur;
|
|
18346
|
-
if (!Array.isArray(arr))
|
|
18347
|
-
return;
|
|
18348
|
-
const remaining = segments.slice(i + 1).join(".");
|
|
18349
|
-
if (!remaining)
|
|
18350
|
-
return arr;
|
|
18351
|
-
return arr.flatMap((item) => {
|
|
18352
|
-
const v = resolvePath2(item, remaining, entityIndex);
|
|
18353
|
-
return v === undefined ? [] : Array.isArray(v) ? v : [v];
|
|
18354
|
-
});
|
|
18355
|
-
}
|
|
18356
|
-
const indexMatch = seg.match(/^(.+?)\[(\d+)\]$/);
|
|
18357
|
-
if (indexMatch) {
|
|
18358
|
-
const key = indexMatch[1];
|
|
18359
|
-
const idx = parseInt(indexMatch[2], 10);
|
|
18360
|
-
const arr = key ? cur[key] : cur;
|
|
18361
|
-
if (!Array.isArray(arr) || idx >= arr.length)
|
|
18362
|
-
return;
|
|
18363
|
-
cur = arr[idx];
|
|
18364
|
-
continue;
|
|
18365
|
-
}
|
|
18366
|
-
const rec = cur;
|
|
18367
|
-
let val = rec[seg];
|
|
18368
|
-
if (val == null && entityIndex) {
|
|
18369
|
-
const ref = rec[`*${seg}`];
|
|
18370
|
-
if (typeof ref === "string") {
|
|
18371
|
-
val = entityIndex.get(ref);
|
|
18372
|
-
}
|
|
18373
|
-
}
|
|
18374
|
-
cur = val;
|
|
18375
|
-
}
|
|
18376
|
-
return cur;
|
|
18377
|
-
}
|
|
18378
|
-
function extractFields(data, fields, entityIndex) {
|
|
18379
|
-
if (data == null)
|
|
18380
|
-
return data;
|
|
18381
|
-
function mapItem(item) {
|
|
18382
|
-
const out = {};
|
|
18383
|
-
for (const f of fields) {
|
|
18384
|
-
const colonIdx = f.indexOf(":");
|
|
18385
|
-
const alias = colonIdx >= 0 ? f.slice(0, colonIdx) : f.split(".").pop();
|
|
18386
|
-
const path9 = colonIdx >= 0 ? f.slice(colonIdx + 1) : f;
|
|
18387
|
-
const resolved = resolvePath2(item, path9, entityIndex ?? undefined) ?? [];
|
|
18388
|
-
out[alias] = Array.isArray(resolved) ? resolved.length === 0 ? null : resolved.length === 1 ? resolved[0] : resolved : resolved;
|
|
18389
|
-
}
|
|
18390
|
-
return out;
|
|
18391
|
-
}
|
|
18392
|
-
function hasValue(v) {
|
|
18393
|
-
if (v == null)
|
|
18394
|
-
return false;
|
|
18395
|
-
if (Array.isArray(v))
|
|
18396
|
-
return v.length > 0;
|
|
18397
|
-
return true;
|
|
18398
|
-
}
|
|
18399
|
-
if (Array.isArray(data)) {
|
|
18400
|
-
return data.map(mapItem).filter((row) => Object.values(row).some(hasValue));
|
|
18401
|
-
}
|
|
18402
|
-
return mapItem(data);
|
|
18403
|
-
}
|
|
18404
|
-
function hasMeaningfulValue(value) {
|
|
18405
|
-
if (value == null)
|
|
18406
|
-
return false;
|
|
18407
|
-
if (typeof value === "string")
|
|
18408
|
-
return value.trim().length > 0;
|
|
18409
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
18410
|
-
return true;
|
|
18411
|
-
if (Array.isArray(value))
|
|
18412
|
-
return value.some((item) => hasMeaningfulValue(item));
|
|
18413
|
-
if (typeof value === "object")
|
|
18414
|
-
return Object.values(value).some((item) => hasMeaningfulValue(item));
|
|
18415
|
-
return false;
|
|
18416
|
-
}
|
|
18417
|
-
function isPlainRecord(value) {
|
|
18418
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
18419
|
-
}
|
|
18420
|
-
function isScalarLike(value) {
|
|
18421
|
-
if (value == null)
|
|
18422
|
-
return false;
|
|
18423
|
-
if (typeof value === "string")
|
|
18424
|
-
return value.trim().length > 0;
|
|
18425
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
18426
|
-
return true;
|
|
18427
|
-
if (Array.isArray(value)) {
|
|
18428
|
-
return value.length > 0 && value.every((item) => item == null || typeof item === "string" || typeof item === "number" || typeof item === "boolean");
|
|
18429
|
-
}
|
|
18430
|
-
return false;
|
|
18431
|
-
}
|
|
18432
|
-
function looksStructuredForDirectOutput(value) {
|
|
18433
|
-
if (Array.isArray(value)) {
|
|
18434
|
-
const sample = value.filter(isPlainRecord).slice(0, 3);
|
|
18435
|
-
if (sample.length === 0)
|
|
18436
|
-
return false;
|
|
18437
|
-
const simpleRows = sample.filter((row) => {
|
|
18438
|
-
const keys2 = Object.keys(row);
|
|
18439
|
-
const scalarFields2 = Object.values(row).filter(isScalarLike).length;
|
|
18440
|
-
return keys2.length > 0 && keys2.length <= 20 && scalarFields2 >= 2;
|
|
18441
|
-
});
|
|
18442
|
-
return simpleRows.length >= Math.ceil(sample.length / 2);
|
|
18443
|
-
}
|
|
18444
|
-
if (!isPlainRecord(value))
|
|
18445
|
-
return false;
|
|
18446
|
-
const keys = Object.keys(value);
|
|
18447
|
-
if (keys.length === 0 || keys.length > 20)
|
|
18448
|
-
return false;
|
|
18449
|
-
const scalarFields = Object.values(value).filter(isScalarLike).length;
|
|
18450
|
-
return scalarFields >= 2;
|
|
18451
|
-
}
|
|
18452
|
-
function applyTransforms(result, flags) {
|
|
18453
|
-
let data = result;
|
|
18454
|
-
const entityIndex = detectEntityIndex(result);
|
|
18455
|
-
const pathFlag = flags.path;
|
|
18456
|
-
if (pathFlag) {
|
|
18457
|
-
data = resolvePath2(data, pathFlag, entityIndex);
|
|
18458
|
-
if (data === undefined) {
|
|
18459
|
-
process.stderr.write(`[unbrowse] warning: --path "${pathFlag}" resolved to undefined. Check path against response structure.
|
|
18460
|
-
`);
|
|
18461
|
-
return [];
|
|
18462
|
-
}
|
|
18463
|
-
}
|
|
18464
|
-
const extractFlag = flags.extract;
|
|
18465
|
-
if (extractFlag) {
|
|
18466
|
-
const fields = extractFlag.split(",").map((f) => f.trim());
|
|
18467
|
-
data = extractFields(data, fields, entityIndex);
|
|
18468
|
-
}
|
|
18469
|
-
const limitFlag = flags.limit;
|
|
18470
|
-
if (limitFlag && Array.isArray(data)) {
|
|
18471
|
-
data = data.slice(0, Number(limitFlag));
|
|
18472
|
-
}
|
|
18473
|
-
return data;
|
|
18474
|
-
}
|
|
18475
18012
|
function slimTrace(obj) {
|
|
18476
18013
|
const trace = obj.trace;
|
|
18477
18014
|
const out = {
|
|
@@ -18487,67 +18024,14 @@ function slimTrace(obj) {
|
|
|
18487
18024
|
};
|
|
18488
18025
|
if ("result" in obj)
|
|
18489
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;
|
|
18490
18033
|
return out;
|
|
18491
18034
|
}
|
|
18492
|
-
function wrapWithHints(obj) {
|
|
18493
|
-
const hints = obj.extraction_hints;
|
|
18494
|
-
if (!hints)
|
|
18495
|
-
return obj;
|
|
18496
|
-
const resultStr = JSON.stringify(obj.result ?? "");
|
|
18497
|
-
if (resultStr.length < 2000)
|
|
18498
|
-
return obj;
|
|
18499
|
-
const trace = obj.trace;
|
|
18500
|
-
return {
|
|
18501
|
-
trace: trace ? {
|
|
18502
|
-
trace_id: trace.trace_id,
|
|
18503
|
-
skill_id: trace.skill_id,
|
|
18504
|
-
endpoint_id: trace.endpoint_id,
|
|
18505
|
-
success: trace.success,
|
|
18506
|
-
status_code: trace.status_code
|
|
18507
|
-
} : undefined,
|
|
18508
|
-
_response_too_large: `${resultStr.length} bytes \u2014 use extraction flags below to get structured data`,
|
|
18509
|
-
extraction_hints: hints
|
|
18510
|
-
};
|
|
18511
|
-
}
|
|
18512
|
-
function schemaOnly(obj) {
|
|
18513
|
-
const trace = obj.trace;
|
|
18514
|
-
return {
|
|
18515
|
-
trace: trace ? { trace_id: trace.trace_id, skill_id: trace.skill_id, endpoint_id: trace.endpoint_id, success: trace.success } : undefined,
|
|
18516
|
-
extraction_hints: obj.extraction_hints ?? null,
|
|
18517
|
-
response_schema: obj.response_schema ?? null
|
|
18518
|
-
};
|
|
18519
|
-
}
|
|
18520
|
-
function autoExtractOrWrap(obj) {
|
|
18521
|
-
const hints = obj.extraction_hints;
|
|
18522
|
-
const resultStr = JSON.stringify(obj.result ?? "");
|
|
18523
|
-
if (resultStr.length < 2000)
|
|
18524
|
-
return obj;
|
|
18525
|
-
if (looksStructuredForDirectOutput(obj.result)) {
|
|
18526
|
-
return slimTrace({ ...obj, extraction_hints: undefined, response_schema: undefined });
|
|
18527
|
-
}
|
|
18528
|
-
if (!hints)
|
|
18529
|
-
return obj;
|
|
18530
|
-
if (hints.confidence === "high") {
|
|
18531
|
-
const syntheticFlags = {};
|
|
18532
|
-
if (hints.path)
|
|
18533
|
-
syntheticFlags.path = hints.path;
|
|
18534
|
-
if (hints.fields.length > 0)
|
|
18535
|
-
syntheticFlags.extract = hints.fields.join(",");
|
|
18536
|
-
syntheticFlags.limit = "20";
|
|
18537
|
-
const extracted = applyTransforms(obj.result, syntheticFlags);
|
|
18538
|
-
if (!hasMeaningfulValue(extracted))
|
|
18539
|
-
return wrapWithHints(obj);
|
|
18540
|
-
const slimmed = slimTrace({ ...obj, result: extracted });
|
|
18541
|
-
slimmed._auto_extracted = {
|
|
18542
|
-
applied: hints.cli_args,
|
|
18543
|
-
confidence: hints.confidence,
|
|
18544
|
-
all_fields: hints.schema_tree,
|
|
18545
|
-
note: "Auto-extracted using response_schema. Add/remove fields with --extract, change array with --path, or use --raw for full response."
|
|
18546
|
-
};
|
|
18547
|
-
return slimmed;
|
|
18548
|
-
}
|
|
18549
|
-
return wrapWithHints(obj);
|
|
18550
|
-
}
|
|
18551
18035
|
async function cmdHealth(flags) {
|
|
18552
18036
|
output(await api3("GET", "/health"), !!flags.pretty);
|
|
18553
18037
|
}
|
|
@@ -18559,6 +18043,8 @@ async function cmdResolve(flags) {
|
|
|
18559
18043
|
const url = flags.url;
|
|
18560
18044
|
const domain = flags.domain;
|
|
18561
18045
|
const explicitEndpointId = flags["endpoint-id"];
|
|
18046
|
+
const autoExecute = !!flags.execute;
|
|
18047
|
+
const extraParams = flags.params ? JSON.parse(flags.params) : {};
|
|
18562
18048
|
if (url) {
|
|
18563
18049
|
body.params = { url };
|
|
18564
18050
|
body.context = { url };
|
|
@@ -18570,14 +18056,19 @@ async function cmdResolve(flags) {
|
|
|
18570
18056
|
body.params = { ...body.params ?? {}, endpoint_id: explicitEndpointId };
|
|
18571
18057
|
}
|
|
18572
18058
|
if (flags.params) {
|
|
18573
|
-
body.params = { ...body.params ?? {}, ...
|
|
18059
|
+
body.params = { ...body.params ?? {}, ...extraParams };
|
|
18574
18060
|
}
|
|
18575
18061
|
if (flags["dry-run"])
|
|
18576
18062
|
body.dry_run = true;
|
|
18577
18063
|
if (flags["force-capture"])
|
|
18578
18064
|
body.force_capture = true;
|
|
18579
|
-
const hasTransforms = !!(flags.path || flags.extract);
|
|
18580
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
|
+
}
|
|
18581
18072
|
const startedAt = Date.now();
|
|
18582
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.");
|
|
18583
18074
|
const resultError = result.result?.error ?? result.error;
|
|
@@ -18595,14 +18086,18 @@ async function cmdResolve(flags) {
|
|
|
18595
18086
|
}
|
|
18596
18087
|
}
|
|
18597
18088
|
if (explicitEndpointId && result.available_endpoints) {
|
|
18598
|
-
const skillId =
|
|
18089
|
+
const skillId = resolveSkillId();
|
|
18599
18090
|
if (skillId) {
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
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...");
|
|
18606
18101
|
}
|
|
18607
18102
|
}
|
|
18608
18103
|
const resultObj = result.result;
|
|
@@ -18619,13 +18114,7 @@ async function cmdResolve(flags) {
|
|
|
18619
18114
|
if (Date.now() - startedAt > 3000 && result.source === "live-capture") {
|
|
18620
18115
|
info("Live capture finished. Future runs against this site should be much faster.");
|
|
18621
18116
|
}
|
|
18622
|
-
|
|
18623
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18624
|
-
return;
|
|
18625
|
-
}
|
|
18626
|
-
if (hasTransforms && result.result != null) {
|
|
18627
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18628
|
-
}
|
|
18117
|
+
result = slimTrace(result);
|
|
18629
18118
|
const skill = result.skill;
|
|
18630
18119
|
const trace = result.trace;
|
|
18631
18120
|
if (skill?.skill_id && trace) {
|
|
@@ -18654,16 +18143,9 @@ async function cmdExecute(flags) {
|
|
|
18654
18143
|
body.dry_run = true;
|
|
18655
18144
|
if (flags["confirm-unsafe"])
|
|
18656
18145
|
body.confirm_unsafe = true;
|
|
18657
|
-
const hasTransforms = !!(flags.path || flags.extract);
|
|
18658
18146
|
body.projection = { raw: true };
|
|
18659
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.");
|
|
18660
|
-
|
|
18661
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18662
|
-
return;
|
|
18663
|
-
}
|
|
18664
|
-
if (hasTransforms && result.result != null) {
|
|
18665
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18666
|
-
}
|
|
18148
|
+
result = slimTrace(result);
|
|
18667
18149
|
output(result, !!flags.pretty);
|
|
18668
18150
|
}
|
|
18669
18151
|
async function cmdFeedback(flags) {
|
|
@@ -18758,7 +18240,7 @@ var CLI_REFERENCE = {
|
|
|
18758
18240
|
commands: [
|
|
18759
18241
|
{ name: "health", usage: "", desc: "Server health check" },
|
|
18760
18242
|
{ name: "setup", usage: "[--opencode auto|global|project|off] [--no-start]", desc: "Bootstrap browser deps + Open Code command" },
|
|
18761
|
-
{ name: "resolve", usage: '--intent "..." --url "..." [opts]', desc: "Resolve intent \u2192
|
|
18243
|
+
{ name: "resolve", usage: '--intent "..." --url "..." [opts]', desc: "Resolve intent \u2192 find skill + execute" },
|
|
18762
18244
|
{ name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
|
|
18763
18245
|
{ name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
|
|
18764
18246
|
{ name: "login", usage: '--url "..."', desc: "Interactive browser login" },
|
|
@@ -18786,15 +18268,11 @@ var CLI_REFERENCE = {
|
|
|
18786
18268
|
globalFlags: [
|
|
18787
18269
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
18788
18270
|
{ flag: "--no-auto-start", desc: "Don't auto-start server" },
|
|
18789
|
-
{ flag: "--raw", desc: "Return raw response data (skip server-side projection)" },
|
|
18790
18271
|
{ flag: "--skip-browser", desc: "setup: skip browser-engine install" },
|
|
18791
18272
|
{ flag: "--opencode auto|global|project|off", desc: "setup: install /unbrowse command for Open Code" }
|
|
18792
18273
|
],
|
|
18793
18274
|
resolveExecuteFlags: [
|
|
18794
|
-
{ flag: "--
|
|
18795
|
-
{ flag: '--path "data.items[]"', desc: "Drill into result before extract/output" },
|
|
18796
|
-
{ flag: '--extract "field1,alias:deep.path.to.val"', desc: "Pick specific fields (no piping needed)" },
|
|
18797
|
-
{ flag: "--limit N", desc: "Cap array output to N items" },
|
|
18275
|
+
{ flag: "--execute", desc: "Auto-pick best endpoint and return data (resolve only)" },
|
|
18798
18276
|
{ flag: "--endpoint-id ID", desc: "Pick a specific endpoint" },
|
|
18799
18277
|
{ flag: "--dry-run", desc: "Preview mutations" },
|
|
18800
18278
|
{ flag: "--force-capture", desc: "Bypass caches, re-capture" },
|
|
@@ -18802,10 +18280,9 @@ var CLI_REFERENCE = {
|
|
|
18802
18280
|
],
|
|
18803
18281
|
examples: [
|
|
18804
18282
|
"unbrowse setup",
|
|
18283
|
+
'unbrowse resolve --intent "top stories" --url "https://news.ycombinator.com" --execute',
|
|
18805
18284
|
'unbrowse resolve --intent "get timeline" --url "https://x.com"',
|
|
18806
18285
|
"unbrowse execute --skill abc --endpoint def --pretty",
|
|
18807
|
-
'unbrowse execute --skill abc --endpoint def --extract "user,text,likes" --limit 10',
|
|
18808
|
-
'unbrowse execute --skill abc --endpoint def --path "data.included[]" --extract "name:actor.name,text:commentary.text" --limit 20',
|
|
18809
18286
|
"unbrowse feedback --skill abc --endpoint def --rating 5"
|
|
18810
18287
|
]
|
|
18811
18288
|
};
|
|
@@ -18926,9 +18403,7 @@ async function cmdSiteTask(pack, taskName, flags) {
|
|
|
18926
18403
|
body.dry_run = true;
|
|
18927
18404
|
if (flags["force-capture"])
|
|
18928
18405
|
body.force_capture = true;
|
|
18929
|
-
|
|
18930
|
-
if (flags.raw || hasTransforms)
|
|
18931
|
-
body.projection = { raw: true };
|
|
18406
|
+
body.projection = { raw: true };
|
|
18932
18407
|
const startedAt = Date.now();
|
|
18933
18408
|
let result = await withPendingNotice(api3("POST", "/v1/intent/resolve", body), "Still working. First-time capture/indexing for a site can take 20-80s.");
|
|
18934
18409
|
if (result && typeof result === "object" && result.error === "auth_required") {
|
|
@@ -18937,15 +18412,7 @@ async function cmdSiteTask(pack, taskName, flags) {
|
|
|
18937
18412
|
output({ ...result, _deps: { ...deps2, requires: ["login"] }, _next: [`unbrowse ${pack.site} login`] }, !!flags.pretty);
|
|
18938
18413
|
process.exit(2);
|
|
18939
18414
|
}
|
|
18940
|
-
|
|
18941
|
-
output(schemaOnly(result), !!flags.pretty);
|
|
18942
|
-
return;
|
|
18943
|
-
}
|
|
18944
|
-
if (hasTransforms && result.result != null) {
|
|
18945
|
-
result = slimTrace({ ...result, result: applyTransforms(result.result, flags) });
|
|
18946
|
-
} else if (!flags.raw && result.result != null) {
|
|
18947
|
-
result = autoExtractOrWrap(result);
|
|
18948
|
-
}
|
|
18415
|
+
result = slimTrace(result);
|
|
18949
18416
|
const deps = buildDepsMetadata(pack, taskName);
|
|
18950
18417
|
result._deps = deps;
|
|
18951
18418
|
result._shortcut = `${pack.site} ${taskName}`;
|
|
@@ -18977,14 +18444,9 @@ async function cmdSiteBatch(pack, batchArg, flags) {
|
|
|
18977
18444
|
};
|
|
18978
18445
|
if (flags["force-capture"])
|
|
18979
18446
|
body.force_capture = true;
|
|
18980
|
-
|
|
18981
|
-
|
|
18982
|
-
|
|
18983
|
-
let res = await api3("POST", "/v1/intent/resolve", body);
|
|
18984
|
-
if (!flags.raw && res.result != null) {
|
|
18985
|
-
res = autoExtractOrWrap(res);
|
|
18986
|
-
}
|
|
18987
|
-
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) };
|
|
18988
18450
|
});
|
|
18989
18451
|
const waveResult = await Promise.all(promises);
|
|
18990
18452
|
waveResults.push({
|
|
@@ -19085,6 +18547,54 @@ async function cmdForward() {
|
|
|
19085
18547
|
async function cmdClose() {
|
|
19086
18548
|
output(await api3("POST", "/v1/browse/close"), false);
|
|
19087
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
|
+
}
|
|
19088
18598
|
async function main() {
|
|
19089
18599
|
const { command, args, flags } = parseArgs(process.argv);
|
|
19090
18600
|
const noAutoStart = !!flags["no-auto-start"];
|
|
@@ -19106,6 +18616,8 @@ async function main() {
|
|
|
19106
18616
|
return cmdRestart(flags);
|
|
19107
18617
|
if (command === "upgrade" || command === "update")
|
|
19108
18618
|
return cmdUpgrade(flags);
|
|
18619
|
+
if (command === "connect-chrome")
|
|
18620
|
+
return cmdConnectChrome();
|
|
19109
18621
|
const KNOWN_COMMANDS = new Set([
|
|
19110
18622
|
"health",
|
|
19111
18623
|
"setup",
|
|
@@ -19139,7 +18651,8 @@ async function main() {
|
|
|
19139
18651
|
"eval",
|
|
19140
18652
|
"back",
|
|
19141
18653
|
"forward",
|
|
19142
|
-
"close"
|
|
18654
|
+
"close",
|
|
18655
|
+
"connect-chrome"
|
|
19143
18656
|
]);
|
|
19144
18657
|
if (!KNOWN_COMMANDS.has(command)) {
|
|
19145
18658
|
const pack = findSitePack(command);
|
|
@@ -19215,6 +18728,8 @@ async function main() {
|
|
|
19215
18728
|
return cmdForward();
|
|
19216
18729
|
case "close":
|
|
19217
18730
|
return cmdClose();
|
|
18731
|
+
case "connect-chrome":
|
|
18732
|
+
return cmdConnectChrome();
|
|
19218
18733
|
default:
|
|
19219
18734
|
info(`Unknown command: ${command}`);
|
|
19220
18735
|
printHelp();
|