unbrowse 3.1.0-experiments.9b17005 → 3.1.0-experiments.9cbcb13

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 = "3.1.0-experiments.9b17005", BUILD_GIT_SHA = "9b17005b5aae", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWIxNzAwNSIsImdpdF9zaGEiOiI5YjE3MDA1YjVhYWUiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDliMTcwMDViNWFhZSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDQ6MDA6NTkuNTM0WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "hkh9PmEQoF8Xxv8n8F-gxqI6nusA4RIZ0FNIoMEOgRg", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
34
+ var BUILD_RELEASE_VERSION = "3.1.0-experiments.9cbcb13", BUILD_GIT_SHA = "9cbcb1312b26", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWNiY2IxMyIsImdpdF9zaGEiOiI5Y2JjYjEzMTJiMjYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDljYmNiMTMxMmIyNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDY6MjM6MDQuMzU5WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "CVnzRBAw3oxxU_-oG-sOC9vJWGI0-k2PtTw4Q5SYueY", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -802,7 +802,6 @@ var init_token_sources = () => {};
802
802
  // ../../src/execution/token-resolver.ts
803
803
  var init_token_resolver = __esm(() => {
804
804
  init_token_sources();
805
- init_client2();
806
805
  });
807
806
 
808
807
  // ../../src/vault/index.ts
@@ -3741,6 +3740,25 @@ async function cmdFeedback(flags) {
3741
3740
  body.diagnostics = JSON.parse(flags.diagnostics);
3742
3741
  output(await api2("POST", "/v1/feedback", body), !!flags.pretty);
3743
3742
  }
3743
+ async function cmdAnnotate(flags) {
3744
+ const skillId = flags.skill;
3745
+ const endpointId = flags.endpoint;
3746
+ if (!skillId || !endpointId)
3747
+ die("--skill and --endpoint are required");
3748
+ const body = {};
3749
+ if (flags.text) {
3750
+ body.annotations = [{ text: flags.text }];
3751
+ }
3752
+ if (flags.constraint) {
3753
+ const parts = flags.constraint.split(":");
3754
+ if (parts.length >= 3) {
3755
+ body.constraints = [{ param: parts[0], rule: parts[1], message: parts.slice(2).join(":") }];
3756
+ }
3757
+ }
3758
+ if (!body.annotations && !body.constraints)
3759
+ die("--text or --constraint required");
3760
+ output(await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body), !!flags.pretty);
3761
+ }
3744
3762
  async function cmdReview(flags) {
3745
3763
  const skillId = flags.skill;
3746
3764
  if (!skillId)
@@ -3955,6 +3973,7 @@ var CLI_REFERENCE = {
3955
3973
  { name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Search cached indexed/published routes and optionally execute the top trusted endpoint" },
3956
3974
  { name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
3957
3975
  { name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
3976
+ { name: "annotate", usage: "--skill ID --endpoint ID --text 'tip' [--constraint 'param:rule:message']", desc: "Contribute best practices or constraints for an endpoint" },
3958
3977
  { name: "review", usage: "--skill ID --endpoints '[...]'", desc: "Push reviewed descriptions/schema metadata back to a captured skill before publish" },
3959
3978
  { name: "index", usage: "--skill ID", desc: "Recompute local graph/contracts/export from cached skill state only" },
3960
3979
  { name: "publish", usage: "--skill ID [--confirm-publish] [--endpoints '[...]']", desc: "Re-index locally, inspect publish-review metadata, then publish/share from cached skill state" },
@@ -4581,6 +4600,7 @@ async function main() {
4581
4600
  "exec",
4582
4601
  "feedback",
4583
4602
  "fb",
4603
+ "annotate",
4584
4604
  "review",
4585
4605
  "index",
4586
4606
  "publish",
@@ -4652,6 +4672,8 @@ async function main() {
4652
4672
  case "feedback":
4653
4673
  case "fb":
4654
4674
  return cmdFeedback(flags);
4675
+ case "annotate":
4676
+ return cmdAnnotate(flags);
4655
4677
  case "review":
4656
4678
  return cmdReview(flags);
4657
4679
  case "index":
package/dist/mcp.js CHANGED
@@ -225,11 +225,11 @@ import { dirname, join, parse } from "path";
225
225
  import { fileURLToPath as fileURLToPath2 } from "url";
226
226
 
227
227
  // ../../src/build-info.generated.ts
228
- var BUILD_RELEASE_VERSION = "3.1.0-experiments.9b17005";
229
- var BUILD_GIT_SHA = "9b17005b5aae";
228
+ var BUILD_RELEASE_VERSION = "3.1.0-experiments.9cbcb13";
229
+ var BUILD_GIT_SHA = "9cbcb1312b26";
230
230
  var BUILD_CODE_HASH = "1488fc1d92b7";
231
- var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWIxNzAwNSIsImdpdF9zaGEiOiI5YjE3MDA1YjVhYWUiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDliMTcwMDViNWFhZSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDQ6MDA6NTkuNTM0WiJ9";
232
- var BUILD_RELEASE_MANIFEST_SIGNATURE = "hkh9PmEQoF8Xxv8n8F-gxqI6nusA4RIZ0FNIoMEOgRg";
231
+ var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWNiY2IxMyIsImdpdF9zaGEiOiI5Y2JjYjEzMTJiMjYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDljYmNiMTMxMmIyNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDY6MjM6MDQuMzU5WiJ9";
232
+ var BUILD_RELEASE_MANIFEST_SIGNATURE = "CVnzRBAw3oxxU_-oG-sOC9vJWGI0-k2PtTw4Q5SYueY";
233
233
  var BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
234
234
 
235
235
  // ../../src/version.ts
@@ -1410,12 +1410,23 @@ function addExecuteNextStepHints(result, args) {
1410
1410
  hints.feedback_skill = skillId;
1411
1411
  if (endpointId)
1412
1412
  hints.feedback_endpoint = endpointId;
1413
+ const constraints = isPlainObject(nested) && Array.isArray(nested.constraints) ? nested.constraints : undefined;
1414
+ if (constraints?.length) {
1415
+ hints.known_constraints = constraints;
1416
+ }
1417
+ const annotations = isPlainObject(nested) && Array.isArray(nested.annotations) ? nested.annotations : undefined;
1418
+ if (annotations?.length) {
1419
+ hints.community_notes = annotations;
1420
+ }
1413
1421
  const desc = isPlainObject(nested) && typeof nested.description === "string" ? nested.description : "";
1414
1422
  const looksGeneric = !desc || desc.startsWith("Captured ") || desc.startsWith("Returns results");
1415
1423
  if (looksGeneric) {
1416
1424
  hints.first_use_review_needed = true;
1417
1425
  hints.review_step = "After feedback, call unbrowse_review to write proper endpoint descriptions, then unbrowse_publish to share to marketplace.";
1418
1426
  }
1427
+ if (skillId && endpointId) {
1428
+ hints.contribute = "If you learned something about this endpoint (required params, gotchas, best practices), call unbrowse_annotate to share it with other agents.";
1429
+ }
1419
1430
  return { ...result, _workflow_hints: hints };
1420
1431
  }
1421
1432
  function addCaptureNextStepHints(result, _args) {
@@ -2452,6 +2463,42 @@ var tools = [
2452
2463
  const withHints = addCaptureNextStepHints(result, args);
2453
2464
  return successResult(withHints, "Browse session closed. See _workflow_hints for required next steps: call unbrowse_review then unbrowse_publish.");
2454
2465
  }
2466
+ },
2467
+ {
2468
+ name: "unbrowse_annotate",
2469
+ description: "Contribute constraints or best practices for an endpoint. Call this after executing an endpoint to share what you learned (required params, gotchas, tips) with other agents.",
2470
+ parameters: {
2471
+ type: "object",
2472
+ properties: {
2473
+ skill: { type: "string", description: "Skill ID" },
2474
+ endpoint: { type: "string", description: "Endpoint ID" },
2475
+ constraints: {
2476
+ type: "array",
2477
+ description: "Learned constraints (required params, deprecated fields, format rules)",
2478
+ items: { type: "object", properties: { param: { type: "string" }, rule: { type: "string" }, message: { type: "string" } }, required: ["param", "rule", "message"] }
2479
+ },
2480
+ annotations: {
2481
+ type: "array",
2482
+ description: "Free-text best practices, tips, or gotchas",
2483
+ items: { type: "object", properties: { text: { type: "string" } }, required: ["text"] }
2484
+ }
2485
+ },
2486
+ required: ["skill", "endpoint"]
2487
+ },
2488
+ handler: async (args) => {
2489
+ await ensureServerReady();
2490
+ const skillId = args.skill;
2491
+ const endpointId = args.endpoint;
2492
+ const body = {};
2493
+ if (Array.isArray(args.constraints))
2494
+ body.constraints = args.constraints;
2495
+ if (Array.isArray(args.annotations))
2496
+ body.annotations = args.annotations;
2497
+ if (!body.constraints && !body.annotations)
2498
+ return errorResult("Provide constraints and/or annotations");
2499
+ const result = await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body);
2500
+ return successResult(result, "Annotation saved. Other agents will see your contribution when using this endpoint.");
2501
+ }
2455
2502
  }
2456
2503
  ];
2457
2504
  var toolMap = new Map(tools.map((tool) => [tool.name, tool]));
package/dist/server.js CHANGED
@@ -76,6 +76,88 @@ function getPackageRoot(metaUrl) {
76
76
  var init_paths = () => {};
77
77
 
78
78
  // ../../src/kuri/client.ts
79
+ var exports_client = {};
80
+ __export(exports_client, {
81
+ waitForSelector: () => waitForSelector,
82
+ waitForLoad: () => waitForLoad,
83
+ waitForCloudflare: () => waitForCloudflare,
84
+ stop: () => stop,
85
+ start: () => start,
86
+ snapshot: () => snapshot,
87
+ shouldReuseManagedChrome: () => shouldReuseManagedChrome,
88
+ setViewport: () => setViewport,
89
+ setUserAgent: () => setUserAgent,
90
+ setHeaders: () => setHeaders,
91
+ setCredentials: () => setCredentials,
92
+ setCookies: () => setCookies,
93
+ setCookie: () => setCookie,
94
+ setCdpPortForTests: () => setCdpPortForTests,
95
+ sessionSave: () => sessionSave,
96
+ sessionLoad: () => sessionLoad,
97
+ sessionList: () => sessionList,
98
+ select: () => select,
99
+ scrollIntoView: () => scrollIntoView,
100
+ scroll: () => scroll,
101
+ scriptInject: () => scriptInject,
102
+ screenshot: () => screenshot,
103
+ reuseHealthyBrokerIfPossible: () => reuseHealthyBrokerIfPossible,
104
+ resolveKuriPort: () => resolveKuriPort,
105
+ resolveKuriLaunchConfig: () => resolveKuriLaunchConfig,
106
+ reload: () => reload,
107
+ press: () => press,
108
+ newTab: () => newTab,
109
+ networkEnable: () => networkEnable,
110
+ navigate: () => navigate,
111
+ keyboardType: () => keyboardType,
112
+ keyboardInsertText: () => keyboardInsertText,
113
+ keyUp: () => keyUp,
114
+ keyDown: () => keyDown,
115
+ isReady: () => isReady,
116
+ interceptStart: () => interceptStart,
117
+ health: () => health,
118
+ hasCloudflareChallenge: () => hasCloudflareChallenge,
119
+ harStop: () => harStop,
120
+ harStart: () => harStart,
121
+ goForward: () => goForward,
122
+ goBack: () => goBack,
123
+ getText: () => getText,
124
+ getPort: () => getPort,
125
+ getPerfLcp: () => getPerfLcp,
126
+ getPageHtml: () => getPageHtml,
127
+ getNetworkEvents: () => getNetworkEvents,
128
+ getMarkdown: () => getMarkdown,
129
+ getLinks: () => getLinks,
130
+ getKuriSourceCandidates: () => getKuriSourceCandidates,
131
+ getKuriErrorMessage: () => getKuriErrorMessage,
132
+ getKuriClient: () => getKuriClient,
133
+ getKuriBinaryCandidates: () => getKuriBinaryCandidates,
134
+ getErrors: () => getErrors,
135
+ getDefaultTab: () => getDefaultTab,
136
+ getCurrentUrl: () => getCurrentUrl,
137
+ getCookies: () => getCookies,
138
+ getConsole: () => getConsole,
139
+ getCdpPort: () => getCdpPort,
140
+ findText: () => findText,
141
+ findKuriBinary: () => findKuriBinary,
142
+ fill: () => fill,
143
+ extractLoadPluginsFromHtml: () => extractLoadPluginsFromHtml,
144
+ extractLoadPlugins: () => extractLoadPlugins,
145
+ executeInPageFetch: () => executeInPageFetch,
146
+ evaluate: () => evaluate,
147
+ drag: () => drag,
148
+ domQuery: () => domQuery,
149
+ domHtml: () => domHtml,
150
+ domAttributes: () => domAttributes,
151
+ discoverTabs: () => discoverTabs,
152
+ closeTab: () => closeTab,
153
+ click: () => click,
154
+ bestEffortRehydratePlugins: () => bestEffortRehydratePlugins,
155
+ authProfileSave: () => authProfileSave,
156
+ authProfileLoad: () => authProfileLoad,
157
+ authProfileList: () => authProfileList,
158
+ authProfileDelete: () => authProfileDelete,
159
+ action: () => action
160
+ });
79
161
  import { execFileSync, spawn } from "node:child_process";
80
162
  import { existsSync as existsSync2 } from "node:fs";
81
163
  import net from "node:net";
@@ -977,6 +1059,19 @@ async function getPageHtml(tabId, state = defaultBrokerState) {
977
1059
  const result = await evaluate(tabId, "document.documentElement.outerHTML", state);
978
1060
  return String(result ?? "");
979
1061
  }
1062
+ function extractLoadPlugins(value) {
1063
+ if (typeof value !== "string")
1064
+ return [];
1065
+ return Array.from(new Set(value.split(/[\s,;]+/).map((part) => part.trim()).filter(Boolean)));
1066
+ }
1067
+ function extractLoadPluginsFromHtml(html) {
1068
+ const modules = [];
1069
+ const pattern = /data-load-plugins=(["'])(.*?)\1/gi;
1070
+ for (const match of html.matchAll(pattern)) {
1071
+ modules.push(...extractLoadPlugins(match[2]));
1072
+ }
1073
+ return Array.from(new Set(modules));
1074
+ }
980
1075
  async function bestEffortRehydratePlugins(tabId, state = defaultBrokerState) {
981
1076
  const result = await evaluate(tabId, `(async function() {
982
1077
  function splitPlugins(value) {
@@ -1118,6 +1213,9 @@ function getPort(state = defaultBrokerState) {
1118
1213
  function getCdpPort() {
1119
1214
  return kuriCdpPort;
1120
1215
  }
1216
+ function setCdpPortForTests(port) {
1217
+ kuriCdpPort = port;
1218
+ }
1121
1219
  function isReady(state = defaultBrokerState) {
1122
1220
  return state.ready;
1123
1221
  }
@@ -6380,7 +6478,7 @@ var init_capture = __esm(() => {
6380
6478
  });
6381
6479
 
6382
6480
  // ../../src/build-info.generated.ts
6383
- var BUILD_RELEASE_VERSION = "3.1.0-experiments.9b17005", BUILD_GIT_SHA = "9b17005b5aae", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWIxNzAwNSIsImdpdF9zaGEiOiI5YjE3MDA1YjVhYWUiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDliMTcwMDViNWFhZSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDQ6MDA6NTkuNTM0WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "hkh9PmEQoF8Xxv8n8F-gxqI6nusA4RIZ0FNIoMEOgRg", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
6481
+ var BUILD_RELEASE_VERSION = "3.1.0-experiments.9cbcb13", BUILD_GIT_SHA = "9cbcb1312b26", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuOWNiY2IxMyIsImdpdF9zaGEiOiI5Y2JjYjEzMTJiMjYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDljYmNiMTMxMmIyNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDY6MjM6MDQuMzU5WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "CVnzRBAw3oxxU_-oG-sOC9vJWGI0-k2PtTw4Q5SYueY", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
6384
6482
 
6385
6483
  // ../../src/version.ts
6386
6484
  import { createHash } from "crypto";
@@ -8818,121 +8916,206 @@ async function resolveAuthTokens(endpoint, cookies, existingAuthHeaders) {
8818
8916
  const triggerUrl = endpoint.trigger_url;
8819
8917
  if (!triggerUrl)
8820
8918
  return {};
8821
- const headerBindings = bindings.filter((b) => b.param_location === "header");
8919
+ const headerBindings = bindings.filter((b) => b.param_location === "header" && !existingAuthHeaders[b.param_name]);
8822
8920
  if (headerBindings.length === 0)
8823
8921
  return {};
8824
8922
  const resolved = {};
8825
- try {
8826
- const tabId = await openResolverTab(triggerUrl, cookies);
8827
- if (!tabId)
8828
- return {};
8829
- try {
8830
- await waitForLoad2(tabId);
8831
- const html = await getPageHtml(tabId).catch(() => "");
8832
- if (typeof html !== "string" || !html.startsWith("<")) {
8833
- return {};
8834
- }
8835
- for (const binding of headerBindings) {
8836
- let value = await resolveBinding(binding, html, cookies);
8837
- if (!value && binding.sources.some((s) => s.kind === "js-bundle")) {
8838
- value = await scanAllScriptResources(tabId, binding);
8839
- }
8840
- if (value) {
8841
- resolved[binding.param_name] = binding.param_name.toLowerCase() === "authorization" ? value.startsWith("Bearer ") ? value : `Bearer ${value}` : value;
8923
+ for (const binding of headerBindings) {
8924
+ const cookieSources = binding.sources.filter((s) => s.kind === "cookie");
8925
+ if (cookieSources.length === 0)
8926
+ continue;
8927
+ for (const source of cookieSources) {
8928
+ const names = source.cookie_names ?? [];
8929
+ for (const name of names) {
8930
+ const cookie = cookies.find((c) => c.name === name);
8931
+ if (cookie?.value && cookie.value.length >= 8) {
8932
+ resolved[binding.param_name] = cookie.value;
8933
+ break;
8842
8934
  }
8843
8935
  }
8844
- } finally {
8845
- await closeTab(tabId).catch(() => {});
8936
+ if (resolved[binding.param_name])
8937
+ break;
8846
8938
  }
8847
- } catch {}
8939
+ }
8940
+ const remaining = headerBindings.filter((b) => !resolved[b.param_name]);
8941
+ if (remaining.length === 0)
8942
+ return formatHeaders(resolved);
8943
+ if (!hasResolvableSources(remaining))
8944
+ return formatHeaders(resolved);
8945
+ const html = await fetchHtml(triggerUrl, cookies);
8946
+ if (html) {
8947
+ const fromHtml = await resolveFromHtml(remaining, html);
8948
+ Object.assign(resolved, fromHtml);
8949
+ const stillMissing = remaining.filter((b) => !resolved[b.param_name]);
8950
+ if (stillMissing.length === 0)
8951
+ return formatHeaders(resolved);
8952
+ if (Object.keys(fromHtml).length > 0 && hasHtmlResolvableSources(stillMissing)) {
8953
+ const browserHtml = await fetchHtmlViaBrowser(triggerUrl, cookies);
8954
+ if (browserHtml) {
8955
+ Object.assign(resolved, await resolveFromHtml(stillMissing, browserHtml));
8956
+ }
8957
+ }
8958
+ return formatHeaders(resolved);
8959
+ }
8960
+ if (hasHtmlResolvableSources(remaining)) {
8961
+ const browserHtml = await fetchHtmlViaBrowser(triggerUrl, cookies);
8962
+ if (browserHtml) {
8963
+ Object.assign(resolved, await resolveFromHtml(remaining, browserHtml));
8964
+ }
8965
+ }
8966
+ return formatHeaders(resolved);
8967
+ }
8968
+ function hasResolvableSources(bindings) {
8969
+ for (const b of bindings) {
8970
+ for (const s of b.sources) {
8971
+ if (s.kind === "html-meta" || s.kind === "html-inline-script")
8972
+ return true;
8973
+ if (s.kind === "js-bundle" && s.bundle_url_pattern && s.bundle_regex)
8974
+ return true;
8975
+ }
8976
+ }
8977
+ return false;
8978
+ }
8979
+ function hasHtmlResolvableSources(bindings) {
8980
+ for (const b of bindings) {
8981
+ for (const s of b.sources) {
8982
+ if (s.kind === "html-meta" || s.kind === "html-inline-script")
8983
+ return true;
8984
+ }
8985
+ }
8986
+ return false;
8987
+ }
8988
+ function formatHeaders(raw) {
8989
+ const out = {};
8990
+ for (const [k, v] of Object.entries(raw)) {
8991
+ if (k.toLowerCase() === "authorization" && !v.startsWith("Bearer ")) {
8992
+ out[k] = `Bearer ${v}`;
8993
+ } else {
8994
+ out[k] = v;
8995
+ }
8996
+ }
8997
+ return out;
8998
+ }
8999
+ async function resolveFromHtml(bindings, html) {
9000
+ const resolved = {};
9001
+ for (const binding of bindings) {
9002
+ const value = await resolveBinding(binding, html);
9003
+ if (value)
9004
+ resolved[binding.param_name] = value;
9005
+ }
8848
9006
  return resolved;
8849
9007
  }
8850
- async function resolveBinding(binding, html, cookies) {
9008
+ async function resolveBinding(binding, html) {
8851
9009
  for (const source of binding.sources) {
8852
9010
  let value;
8853
- if (source.kind === "cookie" && source.cookie_names?.length) {
8854
- for (const name of source.cookie_names) {
8855
- const cookie = cookies.find((c) => c.name === name);
8856
- if (cookie?.value) {
8857
- value = cookie.value;
8858
- break;
8859
- }
8860
- }
8861
- } else if (source.kind === "html-meta" || source.kind === "html-inline-script") {
9011
+ if (source.kind === "html-meta" || source.kind === "html-inline-script") {
8862
9012
  value = extractTokenFromHtml(source, html);
8863
- } else if (source.kind === "js-bundle" && source.bundle_url_pattern) {
8864
- try {
8865
- const resp = await fetch(source.bundle_url_pattern);
8866
- if (resp.ok) {
8867
- const body = await resp.text();
8868
- value = extractTokenFromBundle(source, body);
8869
- }
8870
- } catch {}
9013
+ } else if (source.kind === "js-bundle" && source.bundle_url_pattern && source.bundle_regex) {
9014
+ value = await resolveJsBundle(source, html);
8871
9015
  }
8872
9016
  if (value && value.length >= 8)
8873
9017
  return value;
8874
9018
  }
8875
9019
  return;
8876
9020
  }
8877
- async function openResolverTab(url, cookies) {
9021
+ async function resolveJsBundle(source, html) {
9022
+ const pattern = source.bundle_url_pattern;
9023
+ const scriptSrcRe = /<script[^>]+src=["']([^"']+)["']/gi;
9024
+ let match;
9025
+ while ((match = scriptSrcRe.exec(html)) !== null) {
9026
+ const src = match[1];
9027
+ if (!src.includes(pattern))
9028
+ continue;
9029
+ try {
9030
+ const url = src.startsWith("http") ? src : `https:${src}`;
9031
+ const controller = new AbortController;
9032
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
9033
+ const res = await fetch(url, {
9034
+ headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" },
9035
+ signal: controller.signal
9036
+ });
9037
+ clearTimeout(timeout);
9038
+ if (!res.ok)
9039
+ continue;
9040
+ const bundleContent = await res.text();
9041
+ const extracted = extractTokenFromBundle(source, bundleContent);
9042
+ if (extracted && extracted.length >= 8)
9043
+ return extracted;
9044
+ } catch {}
9045
+ }
9046
+ return;
9047
+ }
9048
+ async function fetchHtml(url, cookies) {
8878
9049
  try {
8879
- const tab = await newTab(url);
8880
- const tabId = typeof tab === "string" ? tab : tab?.tab_id;
8881
- if (!tabId)
8882
- return;
8883
- if (cookies.length > 0) {
8884
- for (const c of cookies) {
8885
- await setCookie(tabId, c.name, c.value, c.domain).catch(() => {});
8886
- }
8887
- await navigate(tabId, url).catch(() => {});
8888
- }
8889
- return tabId;
9050
+ const cookieHeader = cookies.map((c) => `${c.name}=${c.value}`).join("; ");
9051
+ const controller = new AbortController;
9052
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
9053
+ const res = await fetch(url, {
9054
+ headers: {
9055
+ Cookie: cookieHeader,
9056
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
9057
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
9058
+ "Accept-Language": "en-US,en;q=0.9"
9059
+ },
9060
+ redirect: "follow",
9061
+ signal: controller.signal
9062
+ });
9063
+ clearTimeout(timeout);
9064
+ if (!res.ok)
9065
+ return null;
9066
+ const ct = res.headers.get("content-type") ?? "";
9067
+ if (!ct.includes("text/html") && !ct.includes("text/plain") && !ct.includes("application/xhtml"))
9068
+ return null;
9069
+ const html = await res.text();
9070
+ if (!html || html.length < 200 || !html.includes("<"))
9071
+ return null;
9072
+ return html;
8890
9073
  } catch {
8891
- return;
9074
+ return null;
8892
9075
  }
8893
9076
  }
8894
- async function waitForLoad2(tabId) {
8895
- const start2 = Date.now();
8896
- while (Date.now() - start2 < RESOLVE_TIMEOUT_MS) {
8897
- try {
8898
- const state = await evaluate(tabId, "document.readyState");
8899
- if (state === "complete" || state === "interactive")
8900
- return;
8901
- } catch {}
8902
- await new Promise((r) => setTimeout(r, 500));
9077
+ async function fetchHtmlViaBrowser(url, cookies) {
9078
+ let kuri;
9079
+ try {
9080
+ kuri = await Promise.resolve().then(() => (init_client(), exports_client));
9081
+ } catch {
9082
+ return null;
8903
9083
  }
8904
- }
8905
- async function scanAllScriptResources(tabId, binding) {
9084
+ let tabId;
8906
9085
  try {
8907
- const raw = await evaluate(tabId, `
8908
- JSON.stringify(
8909
- performance.getEntriesByType('resource')
8910
- .filter(function(e) { return e.initiatorType === 'script'; })
8911
- .map(function(e) { return e.name; })
8912
- )
8913
- `);
8914
- if (typeof raw !== "string" || !raw.startsWith("["))
8915
- return;
8916
- const urls = JSON.parse(raw);
8917
- const tokenPattern = /AAAAAAAAAAAAAAAAAAA[A-Za-z0-9+/=_%-]{20,}/;
8918
- for (const url of urls) {
9086
+ const tab = await kuri.newTab(url);
9087
+ tabId = typeof tab === "string" ? tab : tab?.tab_id;
9088
+ if (!tabId)
9089
+ return null;
9090
+ if (cookies.length > 0) {
9091
+ for (const c of cookies) {
9092
+ await kuri.setCookie(tabId, c.name, c.value, c.domain).catch(() => {});
9093
+ }
9094
+ await kuri.navigate(tabId, url).catch(() => {});
9095
+ }
9096
+ const start2 = Date.now();
9097
+ while (Date.now() - start2 < 12000) {
8919
9098
  try {
8920
- const resp = await fetch(url);
8921
- if (!resp.ok)
8922
- continue;
8923
- const body = await resp.text();
8924
- const m = body.match(tokenPattern);
8925
- if (m && m[0].length >= 20)
8926
- return m[0];
9099
+ const state = await kuri.evaluate(tabId, "document.readyState");
9100
+ if (state === "complete" || state === "interactive")
9101
+ break;
8927
9102
  } catch {}
9103
+ await new Promise((r) => setTimeout(r, 500));
8928
9104
  }
8929
- } catch {}
8930
- return;
9105
+ const html = await kuri.getPageHtml(tabId).catch(() => "");
9106
+ if (typeof html !== "string" || !html.startsWith("<"))
9107
+ return null;
9108
+ return html;
9109
+ } catch {
9110
+ return null;
9111
+ } finally {
9112
+ if (tabId)
9113
+ await kuri.closeTab(tabId).catch(() => {});
9114
+ }
8931
9115
  }
8932
- var RESOLVE_TIMEOUT_MS = 12000;
9116
+ var FETCH_TIMEOUT_MS = 8000;
8933
9117
  var init_token_resolver = __esm(() => {
8934
9118
  init_token_sources();
8935
- init_client();
8936
9119
  });
8937
9120
 
8938
9121
  // ../../src/vault/index.ts
@@ -15822,6 +16005,23 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
15822
16005
  data2 = text;
15823
16006
  }
15824
16007
  last = { data: data2, status: res.status };
16008
+ if ((res.status === 400 || res.status === 422) && data2 && typeof data2 === "object") {
16009
+ const errors = data2.errors;
16010
+ if (errors?.length) {
16011
+ if (!endpoint.constraints)
16012
+ endpoint.constraints = [];
16013
+ const now = new Date().toISOString();
16014
+ for (const err of errors) {
16015
+ if (!err.message || !err.parameter)
16016
+ continue;
16017
+ const rule = err.code === "MISSING_PARAMETER" ? "required" : err.message.includes("deprecated") ? "deprecated" : err.message.includes("not allowed") ? "forbidden_in_body" : "format";
16018
+ if (!endpoint.constraints.some((c) => c.param === err.parameter && c.rule === rule)) {
16019
+ endpoint.constraints.push({ param: err.parameter, rule, message: err.message, source: "api_error", learned_at: now });
16020
+ log("exec", `learned constraint: ${rule} ${err.parameter} - ${err.message}`);
16021
+ }
16022
+ }
16023
+ }
16024
+ }
15825
16025
  if (res.ok && !(typeof data2 === "string" && isHtml(data2))) {
15826
16026
  return { data: data2, status: res.status, trace_id: nanoid6() };
15827
16027
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbrowse",
3
- "version": "3.1.0-experiments.9b17005",
3
+ "version": "3.1.0-experiments.9cbcb13",
4
4
  "description": "Reverse-engineer any website into reusable API skills. Zero-dep single binary with embedded browser engine.",
5
5
  "type": "module",
6
6
  "bin": {
Binary file
Binary file
Binary file
Binary file
@@ -2,24 +2,27 @@
2
2
  "repo_url": "https://github.com/justrach/kuri.git",
3
3
  "branch": "adding-extensions",
4
4
  "source_sha": "eadfaa5f921f7152e1762aed5ed64b3a4fbefbf3",
5
- "built_at": "2026-04-06T04:00:59.590Z",
5
+ "built_at": "2026-04-05T06:43:57.212Z",
6
6
  "binaries": {
7
7
  "darwin-arm64": {
8
8
  "zig_target": "aarch64-macos",
9
- "sha256": "1796501e393403016723c6b69266b834e2db04ba2559f51c84c957bd85c3927b",
10
- "source": "prebuilt"
9
+ "sha256": "1553633e722d18059dedffa8a52d55ed6c052e4961fd2753ee0b62be60b241bf"
11
10
  },
12
11
  "darwin-x64": {
13
12
  "zig_target": "x86_64-macos",
14
- "sha256": "5ce0dced30e64cb4c580495974f11a136d106d70004683b72300f27bd9d3c0b1"
13
+ "sha256": "b5eb07e631c6ddad64019c8d0c86c32cb76a74ff0791ac5611a3aa3550767ec8"
15
14
  },
16
15
  "linux-arm64": {
17
16
  "zig_target": "aarch64-linux",
18
- "sha256": "adc3bdfb7642f0e7e21c249dc570da9d30403703837aa072bc1c2bf5bb88f950"
17
+ "sha256": "ea88a26f7b335d5842b0c1d83bfa4066bed0a119284560f6bd3833f1d240cce2"
19
18
  },
20
19
  "linux-x64": {
21
20
  "zig_target": "x86_64-linux",
22
- "sha256": "8e033bdf050019e700801a5a6bf5298c2ad54914d7620385bfa8fd14fe77c4da"
21
+ "sha256": "175a7c59e458e952a26974f0fb5c2ce374e56f2c4c352903b481b5aa5a16978f"
22
+ },
23
+ "win-x64": {
24
+ "zig_target": "x86_64-windows",
25
+ "sha256": "176291ad9827a183ba7322ddb56cc1fa5edc7c214a264ecdf8a1d5d18366d686"
23
26
  }
24
27
  }
25
28
  }
Binary file