mcp-scraper 0.40.2 → 0.41.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/README.md +2 -2
- package/dist/bin/api-server.cjs +2264 -337
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +180 -92
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +6 -5
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.cjs +31 -9
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +4 -4
- package/dist/{chunk-XORPNO3Z.js → chunk-27DUCRAZ.js} +2 -2
- package/dist/{chunk-44HZLHDV.js → chunk-5UN33CGU.js} +38 -1
- package/dist/chunk-5UN33CGU.js.map +1 -0
- package/dist/{chunk-X2LKCX6H.js → chunk-6J57U6HA.js} +3 -3
- package/dist/chunk-BBI7RGOT.js +99 -0
- package/dist/chunk-BBI7RGOT.js.map +1 -0
- package/dist/{chunk-NVXNEOUQ.js → chunk-CS4HE6QY.js} +47 -11
- package/dist/chunk-CS4HE6QY.js.map +1 -0
- package/dist/{chunk-3PIWJS6Y.js → chunk-EHES33KB.js} +2 -2
- package/dist/{chunk-GUVKHCKE.js → chunk-IQTT7CAB.js} +94 -3
- package/dist/chunk-IQTT7CAB.js.map +1 -0
- package/dist/{chunk-7XBBFBYY.js → chunk-JVIW4GK2.js} +44 -12
- package/dist/chunk-JVIW4GK2.js.map +1 -0
- package/dist/chunk-OOB35KFT.js +7 -0
- package/dist/chunk-OOB35KFT.js.map +1 -0
- package/dist/{chunk-LP6E462I.js → chunk-SIE5LZ2V.js} +74 -106
- package/dist/chunk-SIE5LZ2V.js.map +1 -0
- package/dist/{db-W3CP562I.js → db-LVVU6NOK.js} +16 -2
- package/dist/{extract-bundle-K4PG3RZJ.js → extract-bundle-SKXEDG4Z.js} +4 -4
- package/dist/index.cjs +31 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/{location-data-repository-RLQX6SNM.js → location-data-repository-NBTCOA7B.js} +3 -3
- package/dist/{server-KM3CFWCF.js → server-2JJPCZH4.js} +1812 -162
- package/dist/server-2JJPCZH4.js.map +1 -0
- package/dist/{site-extract-repository-2SMMFKKL.js → site-extract-repository-6GX72U4N.js} +4 -4
- package/dist/{worker-FXAGFYOE.js → worker-5RWLFSAJ.js} +27 -28
- package/dist/worker-5RWLFSAJ.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-44HZLHDV.js.map +0 -1
- package/dist/chunk-7XBBFBYY.js.map +0 -1
- package/dist/chunk-GUVKHCKE.js.map +0 -1
- package/dist/chunk-LP6E462I.js.map +0 -1
- package/dist/chunk-NVXNEOUQ.js.map +0 -1
- package/dist/chunk-ZUJLSICT.js +0 -7
- package/dist/chunk-ZUJLSICT.js.map +0 -1
- package/dist/server-KM3CFWCF.js.map +0 -1
- package/dist/worker-FXAGFYOE.js.map +0 -1
- /package/dist/{chunk-XORPNO3Z.js.map → chunk-27DUCRAZ.js.map} +0 -0
- /package/dist/{chunk-X2LKCX6H.js.map → chunk-6J57U6HA.js.map} +0 -0
- /package/dist/{chunk-3PIWJS6Y.js.map → chunk-EHES33KB.js.map} +0 -0
- /package/dist/{db-W3CP562I.js.map → db-LVVU6NOK.js.map} +0 -0
- /package/dist/{extract-bundle-K4PG3RZJ.js.map → extract-bundle-SKXEDG4Z.js.map} +0 -0
- /package/dist/{location-data-repository-RLQX6SNM.js.map → location-data-repository-NBTCOA7B.js.map} +0 -0
- /package/dist/{site-extract-repository-2SMMFKKL.js.map → site-extract-repository-6GX72U4N.js.map} +0 -0
package/dist/bin/paa-harvest.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
harvest
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-JVIW4GK2.js";
|
|
5
5
|
import {
|
|
6
6
|
browserServiceApiKey
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-EHES33KB.js";
|
|
8
8
|
import "../chunk-K443GQY5.js";
|
|
9
9
|
import "../chunk-CB5C3BPB.js";
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-5UN33CGU.js";
|
|
11
|
+
import "../chunk-IQTT7CAB.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { Command } from "commander";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-G3P3ZDB4.js";
|
|
4
4
|
import {
|
|
5
5
|
getDb
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IQTT7CAB.js";
|
|
7
7
|
|
|
8
8
|
// src/api/location-data-repository.ts
|
|
9
9
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -681,4 +681,4 @@ export {
|
|
|
681
681
|
importHostedCensusPlacesCsv,
|
|
682
682
|
queryHostedLocationMarkets
|
|
683
683
|
};
|
|
684
|
-
//# sourceMappingURL=chunk-
|
|
684
|
+
//# sourceMappingURL=chunk-27DUCRAZ.js.map
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var RECAPTCHA_INSTRUCTIONS = "Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.";
|
|
3
|
+
var PUBLIC_SERVICE_UNAVAILABLE_MESSAGE = "This operation is temporarily unavailable. Please retry later.";
|
|
4
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
5
|
+
"request_aborted",
|
|
6
|
+
"captcha_exhausted",
|
|
7
|
+
"captcha_or_blocked",
|
|
8
|
+
"location_mismatch",
|
|
9
|
+
"proxy_tunnel_failed",
|
|
10
|
+
"proxy_unavailable",
|
|
11
|
+
"harvest_timeout",
|
|
12
|
+
"mcp_request_timeout",
|
|
13
|
+
"service_unavailable"
|
|
14
|
+
]);
|
|
15
|
+
function normalizePublicErrorCode(code) {
|
|
16
|
+
return typeof code === "string" && PUBLIC_ERROR_CODES.has(code) ? code : "service_unavailable";
|
|
17
|
+
}
|
|
18
|
+
function publicErrorMessage(code) {
|
|
19
|
+
switch (normalizePublicErrorCode(code)) {
|
|
20
|
+
case "request_aborted":
|
|
21
|
+
return "The request was cancelled before it completed.";
|
|
22
|
+
case "captcha_exhausted":
|
|
23
|
+
case "captcha_or_blocked":
|
|
24
|
+
return "The target site returned a verification challenge. Please retry later.";
|
|
25
|
+
case "location_mismatch":
|
|
26
|
+
return "The target site returned results for a different location. Please retry.";
|
|
27
|
+
case "proxy_tunnel_failed":
|
|
28
|
+
case "proxy_unavailable":
|
|
29
|
+
return "The web request could not establish a working connection. Please retry later.";
|
|
30
|
+
case "harvest_timeout":
|
|
31
|
+
case "mcp_request_timeout":
|
|
32
|
+
return "The request timed out before it completed. Please retry with a smaller request.";
|
|
33
|
+
default:
|
|
34
|
+
return PUBLIC_SERVICE_UNAVAILABLE_MESSAGE;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
3
37
|
function sanitizeVendorName(message) {
|
|
4
38
|
return message.replace(/kernel\.sh\s+sessions?/gi, "sessions").replace(/kernel\.sh\s+session/gi, "this session").replace(/kernel\.sh/gi, "the service").replace(/kernel\s+sessions?/gi, "sessions").replace(/kernel\s+session/gi, "this session").replace(/\bkernel\b/gi, "the service").replace(/ +/g, " ").trim();
|
|
5
39
|
}
|
|
@@ -42,6 +76,9 @@ var LocationMismatchError = class extends Error {
|
|
|
42
76
|
|
|
43
77
|
export {
|
|
44
78
|
RECAPTCHA_INSTRUCTIONS,
|
|
79
|
+
PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,
|
|
80
|
+
normalizePublicErrorCode,
|
|
81
|
+
publicErrorMessage,
|
|
45
82
|
sanitizeVendorName,
|
|
46
83
|
CaptchaError,
|
|
47
84
|
ExtractionError,
|
|
@@ -49,4 +86,4 @@ export {
|
|
|
49
86
|
RequestAbortedError,
|
|
50
87
|
LocationMismatchError
|
|
51
88
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
89
|
+
//# sourceMappingURL=chunk-5UN33CGU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["export const RECAPTCHA_INSTRUCTIONS = 'Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.'\nexport const PUBLIC_SERVICE_UNAVAILABLE_MESSAGE = 'This operation is temporarily unavailable. Please retry later.'\n\nconst PUBLIC_ERROR_CODES = new Set([\n 'request_aborted',\n 'captcha_exhausted',\n 'captcha_or_blocked',\n 'location_mismatch',\n 'proxy_tunnel_failed',\n 'proxy_unavailable',\n 'harvest_timeout',\n 'mcp_request_timeout',\n 'service_unavailable',\n])\n\nexport function normalizePublicErrorCode(code: unknown): string {\n return typeof code === 'string' && PUBLIC_ERROR_CODES.has(code)\n ? code\n : 'service_unavailable'\n}\n\nexport function publicErrorMessage(code: unknown): string {\n switch (normalizePublicErrorCode(code)) {\n case 'request_aborted':\n return 'The request was cancelled before it completed.'\n case 'captcha_exhausted':\n case 'captcha_or_blocked':\n return 'The target site returned a verification challenge. Please retry later.'\n case 'location_mismatch':\n return 'The target site returned results for a different location. Please retry.'\n case 'proxy_tunnel_failed':\n case 'proxy_unavailable':\n return 'The web request could not establish a working connection. Please retry later.'\n case 'harvest_timeout':\n case 'mcp_request_timeout':\n return 'The request timed out before it completed. Please retry with a smaller request.'\n default:\n return PUBLIC_SERVICE_UNAVAILABLE_MESSAGE\n }\n}\n\nexport function sanitizeVendorName(message: string): string {\n return message\n .replace(/kernel\\.sh\\s+sessions?/gi, 'sessions')\n .replace(/kernel\\.sh\\s+session/gi, 'this session')\n .replace(/kernel\\.sh/gi, 'the service')\n .replace(/kernel\\s+sessions?/gi, 'sessions')\n .replace(/kernel\\s+session/gi, 'this session')\n .replace(/\\bkernel\\b/gi, 'the service')\n .replace(/ +/g, ' ')\n .trim()\n}\n\nexport class CaptchaError extends Error {\n readonly name = 'CaptchaError'\n constructor(public readonly instructions: string) {\n super(`CAPTCHA detected. ${instructions}`)\n }\n}\n\nexport class ExtractionError extends Error {\n readonly name = 'ExtractionError'\n constructor(message: string, public readonly cause?: unknown) {\n super(message)\n }\n}\n\n/**\n * Google Maps sometimes finishes navigation without rendering either result\n * cards or its explicit no-results message. Treating that page as a genuine\n * empty search loses data, so callers should retry it with a fresh browser (and\n * a fresh proxy when location rotation is enabled).\n */\nexport class MapsSearchIncompletePageError extends Error {\n readonly name = 'MapsSearchIncompletePageError'\n\n constructor(public readonly pageState: string) {\n super(`Google Maps search did not reach a complete result state (${pageState})`)\n }\n}\n\nexport class RequestAbortedError extends Error {\n readonly name = 'RequestAbortedError'\n constructor(message = 'Request aborted before harvest completed') {\n super(message)\n }\n}\n\nexport class LocationMismatchError extends Error {\n readonly name = 'LocationMismatchError'\n constructor(message = 'Google returned results for a different location than requested') {\n super(message)\n }\n}\n"],"mappings":";AAAO,IAAM,yBAAyB;AAC/B,IAAM,qCAAqC;AAElD,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,yBAAyB,MAAuB;AAC9D,SAAO,OAAO,SAAS,YAAY,mBAAmB,IAAI,IAAI,IAC1D,OACA;AACN;AAEO,SAAS,mBAAmB,MAAuB;AACxD,UAAQ,yBAAyB,IAAI,GAAG;AAAA,IACtC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,mBAAmB,SAAyB;AAC1D,SAAO,QACJ,QAAQ,4BAA4B,UAAU,EAC9C,QAAQ,0BAA0B,cAAc,EAChD,QAAQ,gBAAgB,aAAa,EACrC,QAAQ,wBAAwB,UAAU,EAC1C,QAAQ,sBAAsB,cAAc,EAC5C,QAAQ,gBAAgB,aAAa,EACrC,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAEtC,YAA4B,cAAsB;AAChD,UAAM,qBAAqB,YAAY,EAAE;AADf;AAAA,EAE5B;AAAA,EAF4B;AAAA,EADnB,OAAO;AAIlB;AAEO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAEzC,YAAY,SAAiC,OAAiB;AAC5D,UAAM,OAAO;AAD8B;AAAA,EAE7C;AAAA,EAF6C;AAAA,EADpC,OAAO;AAIlB;AAQO,IAAM,gCAAN,cAA4C,MAAM;AAAA,EAGvD,YAA4B,WAAmB;AAC7C,UAAM,6DAA6D,SAAS,GAAG;AADrD;AAAA,EAE5B;AAAA,EAF4B;AAAA,EAFnB,OAAO;AAKlB;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EACpC,OAAO;AAAA,EAChB,YAAY,UAAU,4CAA4C;AAChE,UAAM,OAAO;AAAA,EACf;AACF;AAEO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EACtC,OAAO;AAAA,EAChB,YAAY,UAAU,mEAAmE;AACvF,UAAM,OAAO;AAAA,EACf;AACF;","names":[]}
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
} from "./chunk-SBLGBZZB.js";
|
|
4
4
|
import {
|
|
5
5
|
sanitizeVendorName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5UN33CGU.js";
|
|
7
7
|
import {
|
|
8
8
|
CREDIT_LOT_TTL,
|
|
9
9
|
getDb,
|
|
10
10
|
settleDebitMcIdempotent
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IQTT7CAB.js";
|
|
12
12
|
|
|
13
13
|
// src/api/site-extract-repository.ts
|
|
14
14
|
function extractJobLimitInfo(job) {
|
|
@@ -496,4 +496,4 @@ export {
|
|
|
496
496
|
failUnfundedExtractJob,
|
|
497
497
|
settleExtractJob
|
|
498
498
|
};
|
|
499
|
-
//# sourceMappingURL=chunk-
|
|
499
|
+
//# sourceMappingURL=chunk-6J57U6HA.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
publicErrorMessage
|
|
3
|
+
} from "./chunk-5UN33CGU.js";
|
|
4
|
+
|
|
5
|
+
// src/api/outbound-sanitize.ts
|
|
6
|
+
var KEY_RENAMES = {
|
|
7
|
+
kernel: "browserRuntime"
|
|
8
|
+
};
|
|
9
|
+
var SANITIZED_VALUE_KEYS = /error|message/i;
|
|
10
|
+
var INTERNAL_DIAGNOSTIC_KEYS = /* @__PURE__ */ new Set([
|
|
11
|
+
"kernel_session_id",
|
|
12
|
+
"browser_session_id",
|
|
13
|
+
"kernelSessionId",
|
|
14
|
+
"browserSessionId",
|
|
15
|
+
"sessionId",
|
|
16
|
+
"kernel_delete_started",
|
|
17
|
+
"session_cleanup_started",
|
|
18
|
+
"kernelDeleteStarted",
|
|
19
|
+
"sessionCleanupStarted",
|
|
20
|
+
"kernel_delete_succeeded",
|
|
21
|
+
"session_cleanup_succeeded",
|
|
22
|
+
"kernelDeleteSucceeded",
|
|
23
|
+
"sessionCleanupSucceeded",
|
|
24
|
+
"kernel_delete_error",
|
|
25
|
+
"session_cleanup_error",
|
|
26
|
+
"kernelDeleteError",
|
|
27
|
+
"sessionCleanupError",
|
|
28
|
+
"kernelProxyId",
|
|
29
|
+
"proxyId",
|
|
30
|
+
"requestedProxyIdSuffix",
|
|
31
|
+
"retrievedProxyIdSuffix",
|
|
32
|
+
"provider",
|
|
33
|
+
"providerName",
|
|
34
|
+
"service",
|
|
35
|
+
"serviceName",
|
|
36
|
+
"vendor",
|
|
37
|
+
"vendorName",
|
|
38
|
+
"organization",
|
|
39
|
+
"organizationId",
|
|
40
|
+
"accountId"
|
|
41
|
+
]);
|
|
42
|
+
var VENDOR_URL_KEYS = /* @__PURE__ */ new Set([
|
|
43
|
+
"hosted_url",
|
|
44
|
+
"hostedUrl",
|
|
45
|
+
"live_view_url",
|
|
46
|
+
"liveViewUrl",
|
|
47
|
+
"cdp_ws_url",
|
|
48
|
+
"cdpWsUrl",
|
|
49
|
+
"browser_live_view_url"
|
|
50
|
+
]);
|
|
51
|
+
var VENDOR_URL_RE = /\b(?:wss?|https?):\/\/[^\s"'<>]*\bonkernel\.com[^\s"'<>]*/gi;
|
|
52
|
+
function redactVendorUrls(value) {
|
|
53
|
+
return value.replace(VENDOR_URL_RE, "[browser-service]");
|
|
54
|
+
}
|
|
55
|
+
function sanitizeOutboundDiagnostics(value, parentKey = "") {
|
|
56
|
+
if (typeof value === "string") {
|
|
57
|
+
let out = redactVendorUrls(value);
|
|
58
|
+
if (SANITIZED_VALUE_KEYS.test(parentKey)) {
|
|
59
|
+
out = publicErrorMessage("service_unavailable");
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(value)) return value.map((v) => sanitizeOutboundDiagnostics(v, parentKey));
|
|
64
|
+
if (value !== null && typeof value === "object") {
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const [key, val] of Object.entries(value)) {
|
|
67
|
+
if (INTERNAL_DIAGNOSTIC_KEYS.has(key)) continue;
|
|
68
|
+
const renamed = KEY_RENAMES[key] ?? key;
|
|
69
|
+
if (VENDOR_URL_KEYS.has(key)) {
|
|
70
|
+
out[renamed] = null;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
out[renamed] = sanitizeOutboundDiagnostics(val, key);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function sanitizeAttempts(attempts) {
|
|
80
|
+
return attempts.map((a) => sanitizeOutboundDiagnostics(a));
|
|
81
|
+
}
|
|
82
|
+
function sanitizeHarvestResult(result) {
|
|
83
|
+
const diagnostics = result?.diagnostics;
|
|
84
|
+
if (!diagnostics?.debug) return result;
|
|
85
|
+
return {
|
|
86
|
+
...result,
|
|
87
|
+
diagnostics: {
|
|
88
|
+
...diagnostics,
|
|
89
|
+
debug: sanitizeOutboundDiagnostics(diagnostics.debug)
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
sanitizeOutboundDiagnostics,
|
|
96
|
+
sanitizeAttempts,
|
|
97
|
+
sanitizeHarvestResult
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-BBI7RGOT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/outbound-sanitize.ts"],"sourcesContent":["import { publicErrorMessage } from '../errors.js'\n\nconst KEY_RENAMES: Record<string, string> = {\n kernel: 'browserRuntime',\n}\n\nconst SANITIZED_VALUE_KEYS = /error|message/i\nconst INTERNAL_DIAGNOSTIC_KEYS = new Set([\n 'kernel_session_id',\n 'browser_session_id',\n 'kernelSessionId',\n 'browserSessionId',\n 'sessionId',\n 'kernel_delete_started',\n 'session_cleanup_started',\n 'kernelDeleteStarted',\n 'sessionCleanupStarted',\n 'kernel_delete_succeeded',\n 'session_cleanup_succeeded',\n 'kernelDeleteSucceeded',\n 'sessionCleanupSucceeded',\n 'kernel_delete_error',\n 'session_cleanup_error',\n 'kernelDeleteError',\n 'sessionCleanupError',\n 'kernelProxyId',\n 'proxyId',\n 'requestedProxyIdSuffix',\n 'retrievedProxyIdSuffix',\n 'provider',\n 'providerName',\n 'service',\n 'serviceName',\n 'vendor',\n 'vendorName',\n 'organization',\n 'organizationId',\n 'accountId',\n])\n\nconst VENDOR_URL_KEYS = new Set([\n 'hosted_url',\n 'hostedUrl',\n 'live_view_url',\n 'liveViewUrl',\n 'cdp_ws_url',\n 'cdpWsUrl',\n 'browser_live_view_url',\n])\nconst VENDOR_URL_RE = /\\b(?:wss?|https?):\\/\\/[^\\s\"'<>]*\\bonkernel\\.com[^\\s\"'<>]*/gi\n\nfunction redactVendorUrls(value: string): string {\n return value.replace(VENDOR_URL_RE, '[browser-service]')\n}\n\nexport function sanitizeOutboundDiagnostics<T>(value: T, parentKey = ''): T {\n if (typeof value === 'string') {\n let out = redactVendorUrls(value)\n if (SANITIZED_VALUE_KEYS.test(parentKey)) {\n out = publicErrorMessage('service_unavailable')\n }\n return out as T\n }\n if (Array.isArray(value)) return value.map(v => sanitizeOutboundDiagnostics(v, parentKey)) as T\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n if (INTERNAL_DIAGNOSTIC_KEYS.has(key)) continue\n const renamed = KEY_RENAMES[key] ?? key\n if (VENDOR_URL_KEYS.has(key)) {\n out[renamed] = null\n continue\n }\n out[renamed] = sanitizeOutboundDiagnostics(val, key)\n }\n return out as T\n }\n return value\n}\n\nexport function sanitizeAttempts<T>(attempts: T[]): T[] {\n return attempts.map(a => sanitizeOutboundDiagnostics(a))\n}\n\nexport function sanitizeHarvestResult<T extends Record<string, unknown>>(result: T): T {\n const diagnostics = result?.diagnostics as Record<string, unknown> | undefined\n if (!diagnostics?.debug) return result\n return {\n ...result,\n diagnostics: {\n ...diagnostics,\n debug: sanitizeOutboundDiagnostics(diagnostics.debug),\n },\n }\n}\n"],"mappings":";;;;;AAEA,IAAM,cAAsC;AAAA,EAC1C,QAAQ;AACV;AAEA,IAAM,uBAAuB;AAC7B,IAAM,2BAA2B,oBAAI,IAAI;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,gBAAgB;AAEtB,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,MAAM,QAAQ,eAAe,mBAAmB;AACzD;AAEO,SAAS,4BAA+B,OAAU,YAAY,IAAO;AAC1E,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,MAAM,iBAAiB,KAAK;AAChC,QAAI,qBAAqB,KAAK,SAAS,GAAG;AACxC,YAAM,mBAAmB,qBAAqB;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,OAAK,4BAA4B,GAAG,SAAS,CAAC;AACzF,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACzE,UAAI,yBAAyB,IAAI,GAAG,EAAG;AACvC,YAAM,UAAU,YAAY,GAAG,KAAK;AACpC,UAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,YAAI,OAAO,IAAI;AACf;AAAA,MACF;AACA,UAAI,OAAO,IAAI,4BAA4B,KAAK,GAAG;AAAA,IACrD;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,iBAAoB,UAAoB;AACtD,SAAO,SAAS,IAAI,OAAK,4BAA4B,CAAC,CAAC;AACzD;AAEO,SAAS,sBAAyD,QAAc;AACrF,QAAM,cAAc,QAAQ;AAC5B,MAAI,CAAC,aAAa,MAAO,QAAO;AAChC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aAAa;AAAA,MACX,GAAG;AAAA,MACH,OAAO,4BAA4B,YAAY,KAAK;AAAA,IACtD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CaptchaError,
|
|
3
3
|
LocationMismatchError,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,
|
|
5
|
+
RequestAbortedError,
|
|
6
|
+
publicErrorMessage
|
|
7
|
+
} from "./chunk-5UN33CGU.js";
|
|
6
8
|
import {
|
|
7
9
|
finishHarvestAttempt,
|
|
8
10
|
startHarvestAttempt
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IQTT7CAB.js";
|
|
12
|
+
|
|
13
|
+
// src/api/webhook.ts
|
|
14
|
+
async function deliverWebhook(url, payload, retries = 3) {
|
|
15
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(url, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "content-type": "application/json" },
|
|
20
|
+
body: JSON.stringify(payload),
|
|
21
|
+
signal: AbortSignal.timeout(1e4)
|
|
22
|
+
});
|
|
23
|
+
if (res.ok) return;
|
|
24
|
+
console.warn(`[webhook] attempt ${attempt} \u2192 ${res.status} from ${url}`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn(`[webhook] attempt ${attempt} failed:`, err instanceof Error ? err.message : err);
|
|
27
|
+
}
|
|
28
|
+
if (attempt < retries) await new Promise((r) => setTimeout(r, 1e3 * attempt * 2));
|
|
29
|
+
}
|
|
30
|
+
console.error(`[webhook] gave up after ${retries} attempts for ${url}`);
|
|
31
|
+
}
|
|
10
32
|
|
|
11
33
|
// src/api/harvest-problems.ts
|
|
12
34
|
function errorMessage(err) {
|
|
@@ -25,13 +47,16 @@ function looksLikeProxyTunnelFailure(message) {
|
|
|
25
47
|
function looksLikeProxyUnavailable(message) {
|
|
26
48
|
return /proxy unavailable|proxy_unavailable|connection_test_failed|did not return a proxy id|configured fallback/i.test(message);
|
|
27
49
|
}
|
|
50
|
+
function looksLikeInternalServiceUnavailable(message) {
|
|
51
|
+
return /(?:spending|billing|organization|account).{0,80}(?:cap|limit|blocked|disabled)|(?:quota|capacity).{0,40}(?:reached|exceeded)|https?:\/\/\S*dashboard/i.test(message);
|
|
52
|
+
}
|
|
28
53
|
function classifyHarvestProblem(err) {
|
|
29
54
|
const message = errorMessage(err);
|
|
30
55
|
if (err instanceof RequestAbortedError) {
|
|
31
56
|
return {
|
|
32
57
|
error_code: "request_aborted",
|
|
33
58
|
error_type: "request_aborted",
|
|
34
|
-
message,
|
|
59
|
+
message: publicErrorMessage("request_aborted"),
|
|
35
60
|
retryable: true,
|
|
36
61
|
httpStatus: 408,
|
|
37
62
|
terminalStatus: "cancelled"
|
|
@@ -41,7 +66,7 @@ function classifyHarvestProblem(err) {
|
|
|
41
66
|
return {
|
|
42
67
|
error_code: "captcha_exhausted",
|
|
43
68
|
error_type: "captcha",
|
|
44
|
-
message,
|
|
69
|
+
message: publicErrorMessage("captcha_exhausted"),
|
|
45
70
|
retryable: true,
|
|
46
71
|
httpStatus: 503,
|
|
47
72
|
terminalStatus: "failed"
|
|
@@ -51,7 +76,7 @@ function classifyHarvestProblem(err) {
|
|
|
51
76
|
return {
|
|
52
77
|
error_code: "location_mismatch",
|
|
53
78
|
error_type: "location_mismatch",
|
|
54
|
-
message,
|
|
79
|
+
message: publicErrorMessage("location_mismatch"),
|
|
55
80
|
retryable: true,
|
|
56
81
|
httpStatus: 503,
|
|
57
82
|
terminalStatus: "failed"
|
|
@@ -61,7 +86,7 @@ function classifyHarvestProblem(err) {
|
|
|
61
86
|
return {
|
|
62
87
|
error_code: "proxy_tunnel_failed",
|
|
63
88
|
error_type: "proxy_tunnel_failed",
|
|
64
|
-
message,
|
|
89
|
+
message: publicErrorMessage("proxy_tunnel_failed"),
|
|
65
90
|
retryable: true,
|
|
66
91
|
httpStatus: 503,
|
|
67
92
|
terminalStatus: "failed"
|
|
@@ -71,7 +96,7 @@ function classifyHarvestProblem(err) {
|
|
|
71
96
|
return {
|
|
72
97
|
error_code: "proxy_unavailable",
|
|
73
98
|
error_type: "proxy_unavailable",
|
|
74
|
-
message,
|
|
99
|
+
message: publicErrorMessage("proxy_unavailable"),
|
|
75
100
|
retryable: true,
|
|
76
101
|
httpStatus: 503,
|
|
77
102
|
terminalStatus: "failed"
|
|
@@ -81,16 +106,26 @@ function classifyHarvestProblem(err) {
|
|
|
81
106
|
return {
|
|
82
107
|
error_code: "harvest_timeout",
|
|
83
108
|
error_type: "timeout",
|
|
84
|
-
message,
|
|
109
|
+
message: publicErrorMessage("harvest_timeout"),
|
|
85
110
|
retryable: true,
|
|
86
111
|
httpStatus: 504,
|
|
87
112
|
terminalStatus: "failed"
|
|
88
113
|
};
|
|
89
114
|
}
|
|
115
|
+
if (looksLikeInternalServiceUnavailable(message)) {
|
|
116
|
+
return {
|
|
117
|
+
error_code: "service_unavailable",
|
|
118
|
+
error_type: "service_unavailable",
|
|
119
|
+
message: PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,
|
|
120
|
+
retryable: true,
|
|
121
|
+
httpStatus: 503,
|
|
122
|
+
terminalStatus: "failed"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
90
125
|
return {
|
|
91
126
|
error_code: "extraction_failed",
|
|
92
127
|
error_type: "extraction",
|
|
93
|
-
message,
|
|
128
|
+
message: PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,
|
|
94
129
|
retryable: false,
|
|
95
130
|
httpStatus: 500,
|
|
96
131
|
terminalStatus: "failed"
|
|
@@ -150,9 +185,10 @@ function createHarvestAttemptRecorder(jobId, userId) {
|
|
|
150
185
|
}
|
|
151
186
|
|
|
152
187
|
export {
|
|
188
|
+
deliverWebhook,
|
|
153
189
|
classifyHarvestProblem,
|
|
154
190
|
serializeHarvestProblem,
|
|
155
191
|
harvestProblemResponse,
|
|
156
192
|
createHarvestAttemptRecorder
|
|
157
193
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
194
|
+
//# sourceMappingURL=chunk-CS4HE6QY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/webhook.ts","../src/api/harvest-problems.ts","../src/api/harvest-attempt-events.ts"],"sourcesContent":["export async function deliverWebhook(url: string, payload: object, retries = 3): Promise<void> {\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(10_000),\n })\n if (res.ok) return\n console.warn(`[webhook] attempt ${attempt} → ${res.status} from ${url}`)\n } catch (err) {\n console.warn(`[webhook] attempt ${attempt} failed:`, err instanceof Error ? err.message : err)\n }\n if (attempt < retries) await new Promise((r) => setTimeout(r, 1000 * attempt * 2))\n }\n console.error(`[webhook] gave up after ${retries} attempts for ${url}`)\n}\n","import {\n CaptchaError,\n LocationMismatchError,\n PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,\n RequestAbortedError,\n publicErrorMessage,\n} from '../errors.js'\n\nexport type HarvestProblemCode =\n | 'request_aborted'\n | 'captcha_exhausted'\n | 'location_mismatch'\n | 'proxy_tunnel_failed'\n | 'proxy_unavailable'\n | 'harvest_timeout'\n | 'service_unavailable'\n | 'extraction_failed'\n\nexport interface HarvestProblem {\n error_code: HarvestProblemCode\n error_type: string\n message: string\n retryable: boolean\n httpStatus: number\n terminalStatus: 'cancelled' | 'failed'\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nfunction looksLikeTimeout(err: unknown, message: string): boolean {\n if (err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError')) return true\n return /timeout|timed out|Timeout \\d+ms exceeded|deadline/i.test(message)\n}\n\nfunction looksLikeCaptcha(message: string): boolean {\n return /captcha|recaptcha|unusual traffic|google\\.com\\/sorry|blocked/i.test(message)\n}\n\nfunction looksLikeProxyTunnelFailure(message: string): boolean {\n return /ERR_TUNNEL_CONNECTION_FAILED|ERR_PROXY_CONNECTION_FAILED|ERR_SOCKS_CONNECTION_FAILED|tunnel connection failed|proxy connection failed|transport error: proxy/i.test(message)\n}\n\nfunction looksLikeProxyUnavailable(message: string): boolean {\n return /proxy unavailable|proxy_unavailable|connection_test_failed|did not return a proxy id|configured fallback/i.test(message)\n}\n\nfunction looksLikeInternalServiceUnavailable(message: string): boolean {\n return /(?:spending|billing|organization|account).{0,80}(?:cap|limit|blocked|disabled)|(?:quota|capacity).{0,40}(?:reached|exceeded)|https?:\\/\\/\\S*dashboard/i.test(message)\n}\n\nexport function classifyHarvestProblem(err: unknown): HarvestProblem {\n const message = errorMessage(err)\n\n if (err instanceof RequestAbortedError) {\n return {\n error_code: 'request_aborted',\n error_type: 'request_aborted',\n message: publicErrorMessage('request_aborted'),\n retryable: true,\n httpStatus: 408,\n terminalStatus: 'cancelled',\n }\n }\n\n if (err instanceof CaptchaError || looksLikeCaptcha(message)) {\n return {\n error_code: 'captcha_exhausted',\n error_type: 'captcha',\n message: publicErrorMessage('captcha_exhausted'),\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (err instanceof LocationMismatchError) {\n return {\n error_code: 'location_mismatch',\n error_type: 'location_mismatch',\n message: publicErrorMessage('location_mismatch'),\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyTunnelFailure(message)) {\n return {\n error_code: 'proxy_tunnel_failed',\n error_type: 'proxy_tunnel_failed',\n message: publicErrorMessage('proxy_tunnel_failed'),\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyUnavailable(message)) {\n return {\n error_code: 'proxy_unavailable',\n error_type: 'proxy_unavailable',\n message: publicErrorMessage('proxy_unavailable'),\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeTimeout(err, message)) {\n return {\n error_code: 'harvest_timeout',\n error_type: 'timeout',\n message: publicErrorMessage('harvest_timeout'),\n retryable: true,\n httpStatus: 504,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeInternalServiceUnavailable(message)) {\n return {\n error_code: 'service_unavailable',\n error_type: 'service_unavailable',\n message: PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n return {\n error_code: 'extraction_failed',\n error_type: 'extraction',\n message: PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,\n retryable: false,\n httpStatus: 500,\n terminalStatus: 'failed',\n }\n}\n\nexport function serializeHarvestProblem(problem: HarvestProblem): string {\n return JSON.stringify({\n error_code: problem.error_code,\n error_type: problem.error_type,\n message: problem.message,\n retryable: problem.retryable,\n })\n}\n\nexport function harvestProblemResponse(problem: HarvestProblem): {\n error: string\n error_code: HarvestProblemCode\n error_type: string\n retryable: boolean\n} {\n return {\n error: problem.message,\n error_code: problem.error_code,\n error_type: problem.error_type,\n retryable: problem.retryable,\n }\n}\n","import type { HarvestAttemptLogEvent } from '../harvest.js'\nimport { finishHarvestAttempt, startHarvestAttempt } from './db.js'\n\nexport function createHarvestAttemptRecorder(jobId: string, userId: number | bigint) {\n return async (event: HarvestAttemptLogEvent): Promise<void> => {\n if (event.type === 'started') {\n await startHarvestAttempt({\n jobId,\n userId,\n attemptNumber: event.attemptNumber,\n maxAttempts: event.maxAttempts,\n query: event.query,\n location: event.location,\n maxQuestions: event.maxQuestions,\n startedAt: event.startedAt,\n })\n return\n }\n\n await finishHarvestAttempt({\n jobId,\n attemptNumber: event.attemptNumber,\n outcome: event.outcome,\n kernelSessionId: event.kernelSessionId,\n questionCount: event.questionCount,\n durationMs: event.durationMs,\n error: event.error,\n willRetry: event.willRetry,\n kernelDeleteStarted: event.cleanup.kernelDeleteStarted,\n kernelDeleteSucceeded: event.cleanup.kernelDeleteSucceeded,\n kernelDeleteError: event.cleanup.kernelDeleteError,\n browserCloseSucceeded: event.cleanup.browserCloseSucceeded,\n browserCloseError: event.cleanup.browserCloseError,\n debug: event.debug,\n completedAt: event.completedAt,\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,eAAsB,eAAe,KAAa,SAAiB,UAAU,GAAkB;AAC7F,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACnD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AACD,UAAI,IAAI,GAAI;AACZ,cAAQ,KAAK,qBAAqB,OAAO,WAAM,IAAI,MAAM,SAAS,GAAG,EAAE;AAAA,IACzE,SAAS,KAAK;AACZ,cAAQ,KAAK,qBAAqB,OAAO,YAAY,eAAe,QAAQ,IAAI,UAAU,GAAG;AAAA,IAC/F;AACA,QAAI,UAAU,QAAS,OAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAO,UAAU,CAAC,CAAC;AAAA,EACnF;AACA,UAAQ,MAAM,2BAA2B,OAAO,iBAAiB,GAAG,EAAE;AACxE;;;ACUA,SAAS,aAAa,KAAsB;AAC1C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAEA,SAAS,iBAAiB,KAAc,SAA0B;AAChE,MAAI,eAAe,iBAAiB,IAAI,SAAS,kBAAkB,IAAI,SAAS,cAAe,QAAO;AACtG,SAAO,qDAAqD,KAAK,OAAO;AAC1E;AAEA,SAAS,iBAAiB,SAA0B;AAClD,SAAO,gEAAgE,KAAK,OAAO;AACrF;AAEA,SAAS,4BAA4B,SAA0B;AAC7D,SAAO,gKAAgK,KAAK,OAAO;AACrL;AAEA,SAAS,0BAA0B,SAA0B;AAC3D,SAAO,4GAA4G,KAAK,OAAO;AACjI;AAEA,SAAS,oCAAoC,SAA0B;AACrE,SAAO,wJAAwJ,KAAK,OAAO;AAC7K;AAEO,SAAS,uBAAuB,KAA8B;AACnE,QAAM,UAAU,aAAa,GAAG;AAEhC,MAAI,eAAe,qBAAqB;AACtC,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,iBAAiB;AAAA,MAC7C,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,eAAe,gBAAgB,iBAAiB,OAAO,GAAG;AAC5D,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,mBAAmB;AAAA,MAC/C,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,eAAe,uBAAuB;AACxC,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,mBAAmB;AAAA,MAC/C,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,4BAA4B,OAAO,GAAG;AACxC,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,qBAAqB;AAAA,MACjD,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,0BAA0B,OAAO,GAAG;AACtC,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,mBAAmB;AAAA,MAC/C,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,iBAAiB,KAAK,OAAO,GAAG;AAClC,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,mBAAmB,iBAAiB;AAAA,MAC7C,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,oCAAoC,OAAO,GAAG;AAChD,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB;AACF;AAEO,SAAS,wBAAwB,SAAiC;AACvE,SAAO,KAAK,UAAU;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,SAAS,QAAQ;AAAA,IACjB,WAAW,QAAQ;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,uBAAuB,SAKrC;AACA,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,WAAW,QAAQ;AAAA,EACrB;AACF;;;AChKO,SAAS,6BAA6B,OAAe,QAAyB;AACnF,SAAO,OAAO,UAAiD;AAC7D,QAAI,MAAM,SAAS,WAAW;AAC5B,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA;AAAA,QACA,eAAe,MAAM;AAAA,QACrB,aAAa,MAAM;AAAA,QACnB,OAAO,MAAM;AAAA,QACb,UAAU,MAAM;AAAA,QAChB,cAAc,MAAM;AAAA,QACpB,WAAW,MAAM;AAAA,MACnB,CAAC;AACD;AAAA,IACF;AAEA,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA,eAAe,MAAM;AAAA,MACrB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,eAAe,MAAM;AAAA,MACrB,YAAY,MAAM;AAAA,MAClB,OAAO,MAAM;AAAA,MACb,WAAW,MAAM;AAAA,MACjB,qBAAqB,MAAM,QAAQ;AAAA,MACnC,uBAAuB,MAAM,QAAQ;AAAA,MACrC,mBAAmB,MAAM,QAAQ;AAAA,MACjC,uBAAuB,MAAM,QAAQ;AAAA,MACrC,mBAAmB,MAAM,QAAQ;AAAA,MACjC,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,IACrB,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDb
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IQTT7CAB.js";
|
|
4
4
|
|
|
5
5
|
// src/lib/browser-service-env.ts
|
|
6
6
|
function browserServiceApiKey() {
|
|
@@ -273,4 +273,4 @@ export {
|
|
|
273
273
|
recordKernelSession,
|
|
274
274
|
recordVendorUsage
|
|
275
275
|
};
|
|
276
|
-
//# sourceMappingURL=chunk-
|
|
276
|
+
//# sourceMappingURL=chunk-EHES33KB.js.map
|
|
@@ -1389,11 +1389,15 @@ function deserialize(raw) {
|
|
|
1389
1389
|
}
|
|
1390
1390
|
async function createJob(userId, query, options, callbackUrl) {
|
|
1391
1391
|
const id = randomUUID();
|
|
1392
|
+
await createJobWithId(id, userId, query, options, callbackUrl);
|
|
1393
|
+
return id;
|
|
1394
|
+
}
|
|
1395
|
+
async function createJobWithId(id, userId, query, options, callbackUrl) {
|
|
1396
|
+
if (!/^[a-zA-Z0-9-]{1,100}$/.test(id)) throw new Error("job id is invalid");
|
|
1392
1397
|
await getDb().execute({
|
|
1393
1398
|
sql: "INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)",
|
|
1394
1399
|
args: [id, userId, query, JSON.stringify(options), callbackUrl ?? null]
|
|
1395
1400
|
});
|
|
1396
|
-
return id;
|
|
1397
1401
|
}
|
|
1398
1402
|
async function createRunningJob(userId, query, options) {
|
|
1399
1403
|
const id = randomUUID();
|
|
@@ -1924,7 +1928,13 @@ async function countActiveJobsForUser(userId) {
|
|
|
1924
1928
|
}
|
|
1925
1929
|
async function claimPendingJob() {
|
|
1926
1930
|
const db = getDb();
|
|
1927
|
-
const res = await db.execute(`
|
|
1931
|
+
const res = await db.execute(`
|
|
1932
|
+
SELECT * FROM jobs
|
|
1933
|
+
WHERE status = 'pending'
|
|
1934
|
+
AND COALESCE(json_extract(options, '$.executionOwner'), 'cron') != 'inngest'
|
|
1935
|
+
ORDER BY created_at
|
|
1936
|
+
LIMIT 1
|
|
1937
|
+
`);
|
|
1928
1938
|
const job = res.rows[0] ? rowToRawJob(res.rows[0]) : void 0;
|
|
1929
1939
|
if (!job) return void 0;
|
|
1930
1940
|
const upd = await db.execute({
|
|
@@ -1933,6 +1943,80 @@ async function claimPendingJob() {
|
|
|
1933
1943
|
});
|
|
1934
1944
|
return upd.rowsAffected === 0 ? void 0 : { ...job, status: "running" };
|
|
1935
1945
|
}
|
|
1946
|
+
async function claimInngestPaaJob(id) {
|
|
1947
|
+
const db = getDb();
|
|
1948
|
+
const current = await getJob(id);
|
|
1949
|
+
if (!current || current.options.executionOwner !== "inngest" || current.options.serpOnly === true) return void 0;
|
|
1950
|
+
if (current.status === "running") return void 0;
|
|
1951
|
+
if (current.status === "pending") {
|
|
1952
|
+
const updated = await db.execute({
|
|
1953
|
+
sql: `UPDATE jobs
|
|
1954
|
+
SET status = 'running', started_at = COALESCE(started_at, datetime('now')), error = NULL
|
|
1955
|
+
WHERE id = ? AND status = 'pending'`,
|
|
1956
|
+
args: [id]
|
|
1957
|
+
});
|
|
1958
|
+
if (updated.rowsAffected === 0) return getJob(id);
|
|
1959
|
+
return { ...current, status: "running", started_at: current.started_at ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
1960
|
+
}
|
|
1961
|
+
return current;
|
|
1962
|
+
}
|
|
1963
|
+
async function checkpointJob(id, result) {
|
|
1964
|
+
const updated = await getDb().execute({
|
|
1965
|
+
sql: `UPDATE jobs SET result = ? WHERE id = ? AND status = 'running'`,
|
|
1966
|
+
args: [JSON.stringify(result), id]
|
|
1967
|
+
});
|
|
1968
|
+
return updated.rowsAffected === 1;
|
|
1969
|
+
}
|
|
1970
|
+
async function listPendingInngestPaaJobs(limit = 25) {
|
|
1971
|
+
const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)));
|
|
1972
|
+
const rows = await getDb().execute({
|
|
1973
|
+
sql: `SELECT * FROM jobs
|
|
1974
|
+
WHERE status = 'pending'
|
|
1975
|
+
AND json_extract(options, '$.executionOwner') = 'inngest'
|
|
1976
|
+
AND COALESCE(json_extract(options, '$.serpOnly'), 0) = 0
|
|
1977
|
+
ORDER BY created_at
|
|
1978
|
+
LIMIT ?`,
|
|
1979
|
+
args: [safeLimit]
|
|
1980
|
+
});
|
|
1981
|
+
return rows.rows.map((row) => deserialize(rowToRawJob(row)));
|
|
1982
|
+
}
|
|
1983
|
+
async function listRecentTerminalInngestPaaJobs(limit = 25) {
|
|
1984
|
+
const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)));
|
|
1985
|
+
const rows = await getDb().execute({
|
|
1986
|
+
sql: `SELECT * FROM jobs
|
|
1987
|
+
WHERE status IN ('done', 'failed', 'cancelled')
|
|
1988
|
+
AND json_extract(options, '$.executionOwner') = 'inngest'
|
|
1989
|
+
AND json_extract(options, '$.billingDebitKey') IS NOT NULL
|
|
1990
|
+
AND json_extract(options, '$.billingSettledAt') IS NULL
|
|
1991
|
+
ORDER BY completed_at DESC
|
|
1992
|
+
LIMIT ?`,
|
|
1993
|
+
args: [safeLimit]
|
|
1994
|
+
});
|
|
1995
|
+
return rows.rows.map((row) => deserialize(rowToRawJob(row)));
|
|
1996
|
+
}
|
|
1997
|
+
async function listStaleRunningInngestPaaJobs(limit = 25) {
|
|
1998
|
+
const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)));
|
|
1999
|
+
const rows = await getDb().execute({
|
|
2000
|
+
sql: `SELECT * FROM jobs
|
|
2001
|
+
WHERE status = 'running'
|
|
2002
|
+
AND started_at <= datetime('now', '-10 minutes')
|
|
2003
|
+
AND json_extract(options, '$.executionOwner') = 'inngest'
|
|
2004
|
+
ORDER BY started_at
|
|
2005
|
+
LIMIT ?`,
|
|
2006
|
+
args: [safeLimit]
|
|
2007
|
+
});
|
|
2008
|
+
return rows.rows.map((row) => deserialize(rowToRawJob(row)));
|
|
2009
|
+
}
|
|
2010
|
+
async function markJobBillingSettled(id, billedMc, settledAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2011
|
+
if (!Number.isSafeInteger(billedMc) || billedMc < 0) throw new Error("job billed amount must be a non-negative integer");
|
|
2012
|
+
const updated = await getDb().execute({
|
|
2013
|
+
sql: `UPDATE jobs
|
|
2014
|
+
SET options = json_set(options, '$.billingSettledAt', ?, '$.billedMc', ?)
|
|
2015
|
+
WHERE id = ?`,
|
|
2016
|
+
args: [settledAt, billedMc, id]
|
|
2017
|
+
});
|
|
2018
|
+
return updated.rowsAffected === 1;
|
|
2019
|
+
}
|
|
1936
2020
|
async function completeJob(id, result) {
|
|
1937
2021
|
await getDb().execute({ sql: `UPDATE jobs SET status = 'done', result = ?, completed_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), id] });
|
|
1938
2022
|
}
|
|
@@ -2965,6 +3049,7 @@ export {
|
|
|
2965
3049
|
listUsers,
|
|
2966
3050
|
deactivateUser,
|
|
2967
3051
|
createJob,
|
|
3052
|
+
createJobWithId,
|
|
2968
3053
|
createRunningJob,
|
|
2969
3054
|
startHarvestAttempt,
|
|
2970
3055
|
finishHarvestAttempt,
|
|
@@ -2999,6 +3084,12 @@ export {
|
|
|
2999
3084
|
countActiveUsers,
|
|
3000
3085
|
countActiveJobsForUser,
|
|
3001
3086
|
claimPendingJob,
|
|
3087
|
+
claimInngestPaaJob,
|
|
3088
|
+
checkpointJob,
|
|
3089
|
+
listPendingInngestPaaJobs,
|
|
3090
|
+
listRecentTerminalInngestPaaJobs,
|
|
3091
|
+
listStaleRunningInngestPaaJobs,
|
|
3092
|
+
markJobBillingSettled,
|
|
3002
3093
|
completeJob,
|
|
3003
3094
|
failJob,
|
|
3004
3095
|
cancelJob,
|
|
@@ -3047,4 +3138,4 @@ export {
|
|
|
3047
3138
|
insertInboxMessage,
|
|
3048
3139
|
listActiveUserIds
|
|
3049
3140
|
};
|
|
3050
|
-
//# sourceMappingURL=chunk-
|
|
3141
|
+
//# sourceMappingURL=chunk-IQTT7CAB.js.map
|