unbrowse 5.0.0 → 6.1.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "5.0.0", BUILD_GIT_SHA = "2b9b7ee612eb", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNS4wLjAiLCJnaXRfc2hhIjoiMmI5YjdlZTYxMmViIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAyYjliN2VlNjEyZWIiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTI1VDA3OjE0OjUyLjIwMloifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "155Lg0Zg_ksmzRr-c02IMZ2kGtPo6NuufCdEX_3KtvE", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "6.1.0-preview.0", BUILD_GIT_SHA = "7614847b21be", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi4xLjAtcHJldmlldy4wIiwiZ2l0X3NoYSI6Ijc2MTQ4NDdiMjFiZSIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANzYxNDg0N2IyMWJlIiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wMVQwMDowNzowOC45ODNaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "1WoERMqhUG__SvEmAB5KTnu2Wtt5a6ZGMCSxJganP0A", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -3109,6 +3109,10 @@ The Unbrowse Terms of Service have been updated.`);
3109
3109
  async function ensureRegistered(options) {
3110
3110
  if (LOCAL_ONLY)
3111
3111
  return;
3112
+ if (process.env.UNBROWSE_SKIP_TOS_CHECK === "1") {
3113
+ console.log("[unbrowse] ToS check skipped (non-interactive/server mode)");
3114
+ return;
3115
+ }
3112
3116
  const exitOnFailure = options?.exitOnFailure ?? true;
3113
3117
  const usableKey = await findUsableApiKey();
3114
3118
  if (usableKey) {
@@ -4428,12 +4432,22 @@ function parseArgs(argv) {
4428
4432
  const rest = command === "help" ? raw : raw.slice(1);
4429
4433
  const positional = [];
4430
4434
  const flags = {};
4435
+ const params = {};
4431
4436
  for (let i = 0;i < rest.length; i++) {
4432
4437
  const a = rest[i];
4433
- if (a.startsWith("--")) {
4438
+ if (a === "-p" || a === "--param") {
4439
+ const next = rest[i + 1];
4440
+ if (next && next.includes("=")) {
4441
+ const eq = next.indexOf("=");
4442
+ params[next.slice(0, eq)] = next.slice(eq + 1);
4443
+ i++;
4444
+ } else {
4445
+ die(`-p requires key=value, got: ${next ?? "<end of args>"}`);
4446
+ }
4447
+ } else if (a.startsWith("--")) {
4434
4448
  const key = a.slice(2);
4435
4449
  const next = rest[i + 1];
4436
- if (!next || next.startsWith("--")) {
4450
+ if (!next || next.startsWith("--") || next === "-p" || next === "--param") {
4437
4451
  flags[key] = true;
4438
4452
  } else {
4439
4453
  flags[key] = next;
@@ -4443,7 +4457,7 @@ function parseArgs(argv) {
4443
4457
  positional.push(a);
4444
4458
  }
4445
4459
  }
4446
- return { command, args: positional, flags };
4460
+ return { command, args: positional, flags, params };
4447
4461
  }
4448
4462
  async function api2(method, path9, body) {
4449
4463
  let target = `${BASE_URL}${path9}`;
@@ -4625,6 +4639,62 @@ function telemetryDomainFromInput(domain, url) {
4625
4639
  return null;
4626
4640
  }
4627
4641
  }
4642
+ async function cmdExplain(flags) {
4643
+ const intent = flags.intent;
4644
+ const url = flags.url;
4645
+ const top = parseInt(flags.top ?? "5", 10) || 5;
4646
+ if (!intent || !url) {
4647
+ process.stderr.write(`usage: unbrowse explain --intent "..." --url "..." [--top N]
4648
+ `);
4649
+ process.exit(2);
4650
+ }
4651
+ const body = {
4652
+ intent,
4653
+ params: { url },
4654
+ context: { url },
4655
+ projection: { raw: true }
4656
+ };
4657
+ let result;
4658
+ try {
4659
+ result = await api2("POST", "/v1/intent/resolve", body);
4660
+ } catch (err) {
4661
+ process.stderr.write(`explain failed: ${err.message}
4662
+ `);
4663
+ process.exit(1);
4664
+ }
4665
+ const r = result.result ?? result;
4666
+ const ae = r.available_endpoints ?? [];
4667
+ const ao = r.available_operations ?? [];
4668
+ const out = {
4669
+ intent,
4670
+ context_url: url,
4671
+ diagnostic: r.diagnostic,
4672
+ shortlist_for_judgment: ae.slice(0, top).map((ep, i) => ({
4673
+ rank: i,
4674
+ endpoint_id: ep.endpoint_id,
4675
+ method: ep.method,
4676
+ url: ep.url,
4677
+ score: ep.score,
4678
+ description: ep.description,
4679
+ input_params: ep.input_params,
4680
+ schema_summary: ep.schema_summary,
4681
+ example_fields: ep.example_fields,
4682
+ sample_values: ep.sample_values,
4683
+ needs_params: ep.needs_params,
4684
+ trigger_url: ep.trigger_url
4685
+ })),
4686
+ agent_facing_shortlist: ao.slice(0, top).map((op, i) => ({
4687
+ rank: i,
4688
+ endpoint_id: op.endpoint_id,
4689
+ method: op.method,
4690
+ url_template: op.url_template ?? op.url,
4691
+ description: op.description_out ?? op.description
4692
+ })),
4693
+ judgment_question: `Given the intent ${JSON.stringify(intent)} on ${JSON.stringify(url)}, ` + `which of the candidate endpoints in shortlist_for_judgment best satisfies the intent? ` + `Reply with the endpoint_id of the best match and a one-line reason. ` + `If none match, say defer_to_capture.`
4694
+ };
4695
+ process.stdout.write(JSON.stringify(out, null, 2) + `
4696
+ `);
4697
+ }
4628
4698
  async function cmdResolve(flags) {
4629
4699
  const intent = flags.intent;
4630
4700
  if (!intent)
@@ -4886,6 +4956,10 @@ async function cmdExecute(flags) {
4886
4956
  if (flags.params) {
4887
4957
  body.params = { ...body.params, ...JSON.parse(flags.params) };
4888
4958
  }
4959
+ const cliKv = flags._params;
4960
+ if (cliKv && Object.keys(cliKv).length > 0) {
4961
+ body.params = { ...body.params, ...cliKv };
4962
+ }
4889
4963
  if (flags.url) {
4890
4964
  body.context_url = flags.url;
4891
4965
  body.params.url = flags.url;
@@ -4961,16 +5035,17 @@ async function cmdExecute(flags) {
4961
5035
  output(out, !!flags.pretty);
4962
5036
  return;
4963
5037
  }
5038
+ const AUTOEXTRACT_HINT_THRESHOLD = 65536;
4964
5039
  if (!rawFlag && !pathFlag && !extractFlag && !schemaFlag) {
4965
5040
  const raw = JSON.stringify(result.result);
4966
- if (raw && raw.length > 2048) {
5041
+ if (raw && raw.length > AUTOEXTRACT_HINT_THRESHOLD) {
4967
5042
  const schema = schemaOf(result.result);
4968
5043
  output({
4969
5044
  trace: result.trace,
4970
5045
  ...result.impact ? { impact: result.impact } : {},
4971
5046
  ...result.next_actions ? { next_actions: result.next_actions } : {},
4972
5047
  extraction_hints: {
4973
- message: "Response is large. Use --path/--extract/--limit to filter, or --schema to see structure, or --raw for full response.",
5048
+ message: `Response is ${Math.round(raw.length / 1024)}KB (over ${AUTOEXTRACT_HINT_THRESHOLD / 1024}KB). Use --path/--extract/--limit to filter, --schema for structure, or --raw for full response.`,
4974
5049
  schema_tree: schema,
4975
5050
  response_bytes: raw.length
4976
5051
  }
@@ -5297,8 +5372,9 @@ var CLI_REFERENCE = {
5297
5372
  { name: "mcp", usage: "[--no-auto-start]", desc: "Run the stdio MCP server" },
5298
5373
  { name: "setup", usage: "[--opencode auto|global|project|off] [--no-start]", desc: "Bootstrap browser deps + Open Code command" },
5299
5374
  { name: "upgrade", usage: "", desc: "Check latest release and print the right upgrade command" },
5300
- { name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Search cached indexed/published routes and optionally execute the top trusted endpoint" },
5301
- { name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
5375
+ { name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Returns ranked shortlist of endpoints for an intent. Pick one and call execute. (Two tool calls is the contract \u2014 autoexec is opt-in via --execute, not the default.)" },
5376
+ { name: "explain", usage: '--intent "..." --url "..." [--top N]', desc: "Emit top-N candidate endpoints + evidence for an LLM judge to pick from (no heuristic verdict \u2014 primitives + agent judgment)" },
5377
+ { name: "execute", usage: "--skill ID --endpoint ID [-p key=val ...] [--params '{json}'] [opts]", desc: "Execute a specific endpoint. Pass replay params via repeated -p key=val flags or --params with a JSON object" },
5302
5378
  { name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
5303
5379
  { name: "annotate", usage: "--skill ID --endpoint ID --text 'tip' [--constraint 'param:rule:message']", desc: "Contribute best practices or constraints for an endpoint" },
5304
5380
  { name: "review", usage: "--skill ID --endpoints '[...]'", desc: "Push reviewed descriptions/schema metadata back to a captured skill before publish" },
@@ -6206,7 +6282,10 @@ async function cmdConnectChrome() {
6206
6282
  console.error("Could not connect to Chrome. Make sure all Chrome windows are closed and try again.");
6207
6283
  }
6208
6284
  async function main() {
6209
- const { command, args, flags } = parseArgs(process.argv);
6285
+ const { command, args, flags, params: cliParams } = parseArgs(process.argv);
6286
+ if (Object.keys(cliParams).length > 0) {
6287
+ flags._params = cliParams;
6288
+ }
6210
6289
  const noAutoStart = !!flags["no-auto-start"];
6211
6290
  if (command === "help" || flags.help) {
6212
6291
  printHelp();
@@ -6322,6 +6401,8 @@ async function main() {
6322
6401
  return cmdSetup(flags);
6323
6402
  case "resolve":
6324
6403
  return cmdResolve(flags);
6404
+ case "explain":
6405
+ return cmdExplain(flags);
6325
6406
  case "execute":
6326
6407
  case "exec":
6327
6408
  return cmdExecute(flags);
package/dist/mcp.js CHANGED
@@ -226,11 +226,11 @@ import { dirname, join, parse } from "path";
226
226
  import { fileURLToPath as fileURLToPath2 } from "url";
227
227
 
228
228
  // ../../src/build-info.generated.ts
229
- var BUILD_RELEASE_VERSION = "5.0.0";
230
- var BUILD_GIT_SHA = "2b9b7ee612eb";
229
+ var BUILD_RELEASE_VERSION = "6.1.0-preview.0";
230
+ var BUILD_GIT_SHA = "7614847b21be";
231
231
  var BUILD_CODE_HASH = "5d9ebf619c61";
232
- var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNS4wLjAiLCJnaXRfc2hhIjoiMmI5YjdlZTYxMmViIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAyYjliN2VlNjEyZWIiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTI1VDA3OjE0OjUyLjIwMloifQ";
233
- var BUILD_RELEASE_MANIFEST_SIGNATURE = "155Lg0Zg_ksmzRr-c02IMZ2kGtPo6NuufCdEX_3KtvE";
232
+ var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi4xLjAtcHJldmlldy4wIiwiZ2l0X3NoYSI6Ijc2MTQ4NDdiMjFiZSIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFANzYxNDg0N2IyMWJlIiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wMVQwMDowNzowOC45ODNaIn0";
233
+ var BUILD_RELEASE_MANIFEST_SIGNATURE = "1WoERMqhUG__SvEmAB5KTnu2Wtt5a6ZGMCSxJganP0A";
234
234
  var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
235
235
 
236
236
  // ../../src/version.ts
@@ -1665,7 +1665,7 @@ var tools = [
1665
1665
  },
1666
1666
  {
1667
1667
  name: "unbrowse_resolve",
1668
- description: "START HERE for every website task. Resolves an intent against cached/published routes. If endpoints are returned, pick one and call unbrowse_execute. If no_cached_match, proceed to unbrowse_go to browse and index the site. Do not call unbrowse_go or unbrowse_execute without calling this first.",
1668
+ description: "START HERE for every website task. Returns ranked shortlist of cached endpoints. Read the shortlist, pick the one that matches your intent (use the example_response_compact and requires fields as evidence), then call unbrowse_execute with that endpoint_id. Two tool calls is the contract \u2014 there is no auto-exec. If no_cached_match, fall through to unbrowse_go to capture fresh.",
1669
1669
  inputSchema: {
1670
1670
  type: "object",
1671
1671
  properties: {
@@ -2525,6 +2525,67 @@ var tools = [
2525
2525
  const result = await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body);
2526
2526
  return successResult(result, "Annotation saved. Other agents will see your contribution when using this endpoint.");
2527
2527
  }
2528
+ },
2529
+ {
2530
+ name: "unbrowse_diagnose",
2531
+ description: "Capture visual + structured context for diagnosing an unbrowse failure. Takes a screenshot of the current page and returns it alongside the current resolve diagnostic. Use when resolve/execute fails and you need to see what the page actually looks like (auth wall, loading spinner, empty state).",
2532
+ inputSchema: {
2533
+ type: "object",
2534
+ properties: {
2535
+ session_id: { type: "string", description: "Optional browse session id." },
2536
+ context: { type: "string", description: "Description of what was being attempted when it failed." }
2537
+ },
2538
+ additionalProperties: false
2539
+ },
2540
+ annotations: { readOnlyHint: true },
2541
+ handler: async (args) => {
2542
+ await ensureServerReady();
2543
+ const sessionId = typeof args.session_id === "string" ? args.session_id : undefined;
2544
+ const screenshot = await api2("GET", "/v1/browse/screenshot", sessionId ? { session_id: sessionId } : undefined);
2545
+ const diagnostic = await api2("GET", "/v1/stats/health", undefined);
2546
+ return successResult({
2547
+ screenshot: typeof screenshot.screenshot === "string" ? screenshot.screenshot : null,
2548
+ tab_id: screenshot.tab_id ?? null,
2549
+ diagnosis_context: args.context ?? null,
2550
+ status: diagnostic
2551
+ }, "Diagnosis capture complete. Screenshot + context returned.");
2552
+ }
2553
+ },
2554
+ {
2555
+ name: "unbrowse_trace",
2556
+ description: "Get the full execution trace for the most recent resolve/execute call, including diagnostic confidence scores, endpoint scores, and visual context. Use to understand WHY a specific endpoint was or wasn't selected.",
2557
+ inputSchema: {
2558
+ type: "object",
2559
+ properties: {
2560
+ trace_id: { type: "string", description: "Optional specific trace ID. Defaults to most recent." }
2561
+ },
2562
+ additionalProperties: false
2563
+ },
2564
+ annotations: { readOnlyHint: true },
2565
+ handler: async (args) => {
2566
+ await ensureServerReady();
2567
+ const result = await api2("GET", `/v1/trace/${args.trace_id ?? "latest"}`, undefined);
2568
+ return successResult(result, "Execution trace with diagnostic context.");
2569
+ }
2570
+ },
2571
+ {
2572
+ name: "unbrowse_validate",
2573
+ description: "Validate a captured skill's quality by taking screenshots of the page while exercising its endpoints. Helps diagnose if a skill's endpoints actually match the live page. Returns screenshots at key interaction points alongside endpoint response data.",
2574
+ inputSchema: {
2575
+ type: "object",
2576
+ properties: {
2577
+ skill_id: { type: "string", description: "Skill ID to validate." },
2578
+ url: { type: "string", description: "Page URL to validate against." }
2579
+ },
2580
+ required: ["skill_id"],
2581
+ additionalProperties: false
2582
+ },
2583
+ annotations: { openWorldHint: true },
2584
+ handler: async (args) => {
2585
+ await ensureServerReady();
2586
+ const result = await api2("GET", `/v1/skills/${args.skill_id}/validate`, args.url ? { url: args.url } : undefined);
2587
+ return successResult(result, "Skill validation complete. Returns screenshots + endpoint match quality.");
2588
+ }
2528
2589
  }
2529
2590
  ];
2530
2591
  var toolMap = new Map(tools.map((tool) => [tool.name, tool]));