@detiq/crawler 1.1.21 → 1.1.23

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/Dockerfile CHANGED
@@ -36,6 +36,11 @@ COPY packages/vscode-extension/package.json packages/vscode-extension/
36
36
  COPY packages/web/package.json packages/web/
37
37
  RUN npm ci --include=dev
38
38
 
39
+ # Patch Stagehand's act() schema to add `additionalProperties: false` on the
40
+ # inner anyOf[0] object. OpenAI strict mode (v1/responses) requires it on every
41
+ # nested object; Stagehand ≤3.6 omits it, causing a 400 "invalid_json_schema".
42
+ RUN node -e "const fs=require('fs');['packages/crawler/node_modules/@browserbasehq/stagehand/dist/cjs/lib/inference.js','packages/crawler/node_modules/@browserbasehq/stagehand/dist/esm/lib/inference.js'].forEach(f=>{if(!fs.existsSync(f)){console.log('skip',f);return;}let c=fs.readFileSync(f,'utf8');if(c.includes('.strict()')){console.log('already patched',f);return;}c=c.replace(/\\.nullable\\(\\)\\n(\\s+)\\.describe\\(\"The element to act on/g,'\.strict()\n\$1.nullable()\n\$1.describe(\"The element to act on');fs.writeFileSync(f,c);console.log('patched',f);});"
43
+
39
44
  # Re-install chromium after npm ci so the browser revision matches the
40
45
  # exact playwright version resolved by the lockfile (not the base image's
41
46
  # bundled version, which may differ if the lockfile was updated).
package/dist/crawler.js CHANGED
@@ -73,7 +73,7 @@ const _crawlLLMCache = /* @__PURE__ */ new Map();
73
73
  async function getCachedCrawlLLM(tenantId) {
74
74
  if (_crawlLLMCache.has(tenantId)) return _crawlLLMCache.get(tenantId);
75
75
  const cfg = await getTaskAgentLLMConfig(tenantId, "crawl").catch(() => null);
76
- _crawlLLMCache.set(tenantId, cfg);
76
+ if (cfg) _crawlLLMCache.set(tenantId, cfg);
77
77
  return cfg;
78
78
  }
79
79
  function getGitInfo() {
@@ -88,7 +88,7 @@ function getGitInfo() {
88
88
  const ALLOWED_NUMERIC_PATHS = /* @__PURE__ */ new Set(["/2fa", "/404", "/500", "/403", "/401"]);
89
89
  const UUID_PATH_SEGMENT_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
90
90
  const CUID_PATH_SEGMENT_RE = /^c[a-z0-9]{20,}$/i;
91
- const NUMERIC_ID_PATH_SEGMENT_RE = /^\d[\d-]*$/;
91
+ const NUMERIC_ID_PATH_SEGMENT_RE = /^\d{4,}$/;
92
92
  const COMPOUND_NUMERIC_ID_PATH_SEGMENT_RE = /^\d+-\d+$/;
93
93
  function normalizePathForNumericGuard(pathname) {
94
94
  const normalized = pathname.replace(/\/+$/, "") || "/";
@@ -1598,6 +1598,7 @@ async function crawlProject(opts, onProgress) {
1598
1598
  };
1599
1599
  const edges = [];
1600
1600
  const pendingTransitions = [];
1601
+ const pendingSummaryWrites = [];
1601
1602
  const discovered = [];
1602
1603
  const performanceMetrics = [];
1603
1604
  const seenDomHashes = new Set(opts.knownDomHashes ?? []);
@@ -2932,6 +2933,7 @@ async function crawlProject(opts, onProgress) {
2932
2933
  const summaryPromise = (async () => {
2933
2934
  try {
2934
2935
  const llmCfg = await getCachedCrawlLLM(opts.tenantId);
2936
+ if (!llmCfg) return void 0;
2935
2937
  const snippet = extracted.markdown.slice(0, 1200);
2936
2938
  if (snippet.trim().length < 80) return void 0;
2937
2939
  const summarySeed = deterministicSeed(`${opts.tenantId}:${opts.projectId ?? ""}:page-summary:${url}`);
@@ -2972,10 +2974,15 @@ ${snippet}`,
2972
2974
  markdown: fitMarkdown ?? extracted.markdown,
2973
2975
  heading: extracted.title || name
2974
2976
  }).catch((err) => console.warn("[crawler] markdown extraction save failed:", err));
2975
- summaryPromise.then((aiSummary) => {
2976
- if (aiSummary) AppBrain.updateScreenAiSummary(opts.tenantId, screen.id, aiSummary).catch(() => {
2977
- });
2978
- });
2977
+ pendingSummaryWrites.push(
2978
+ summaryPromise.then((aiSummary) => {
2979
+ if (aiSummary) AppBrain.updateScreenAiSummary(opts.tenantId, screen.id, aiSummary).catch((e) => {
2980
+ logger.warn({ screenId: screen.id, err: String(e?.message ?? e) }, "[crawler] aiSummary DB save failed");
2981
+ });
2982
+ }).catch((e) => {
2983
+ logger.warn({ screenId: screen.id, err: String(e?.message ?? e) }, "[crawler] aiSummary generation failed");
2984
+ })
2985
+ );
2979
2986
  }
2980
2987
  const structuredJson = summarizeStructuredElements(atElements);
2981
2988
  const artifact = buildCrawlArtifactEnvelope({
@@ -3413,7 +3420,7 @@ ${snippet}`,
3413
3420
  if (liteWorkerPromises.length > 0) await Promise.all(liteWorkerPromises);
3414
3421
  if (opts.discoveryMode) {
3415
3422
  return {
3416
- discoveredUrls: Array.from(visited.values()),
3423
+ discoveredUrls: Array.from(visited.values()).filter((v) => v.startsWith("http")),
3417
3424
  screensFound: 0,
3418
3425
  screensErrored: 0,
3419
3426
  screensSkippedToNextSegment: 0,
@@ -4149,6 +4156,12 @@ ${snippet}`,
4149
4156
  }
4150
4157
  const screensSucceeded = discovered.filter((d) => !!d.screenId).length;
4151
4158
  const screensErrored = discovered.filter((d) => !!d.error && !d.screenId).length;
4159
+ if (pendingSummaryWrites.length > 0) {
4160
+ await Promise.race([
4161
+ Promise.allSettled(pendingSummaryWrites),
4162
+ new Promise((resolve) => setTimeout(resolve, Math.min(6e4, pendingSummaryWrites.length * 3e3)))
4163
+ ]);
4164
+ }
4152
4165
  if (screensSucceeded >= 3) {
4153
4166
  const screensWithElements = discovered.filter((d) => (d.elements ?? 0) > 0 && d.screenId).length;
4154
4167
  if (screensWithElements === 0) {