scrapely 1.2.30 → 1.2.33
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/client-loader.d.ts +1 -1
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +17 -1
- package/dist/index.mjs +13 -0
- package/dist/lazy-exports.js +14 -1
- package/dist/node-wrapper.d.ts +1 -1
- package/dist/patch-browser-pool.d.ts +1 -1
- package/dist/patch-browser-pool.js +18 -18
- package/dist/solver.js +1 -1
- package/package.json +1 -1
- package/dist/runner/docker-manager.d.ts +0 -88
- package/dist/runner/docker-manager.js +0 -283
- package/dist/runner/index.d.ts +0 -2
- package/dist/runner/index.js +0 -7
- package/dist/runner/timeout-manager.d.ts +0 -43
- package/dist/runner/timeout-manager.js +0 -90
package/dist/client-loader.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* scrapely-client, loaded on first use (axios, proxy-agent and friends cost
|
|
3
|
-
* ~0.7 s
|
|
3
|
+
* ~0.7 s on the platform, and most runs never need the API client).
|
|
4
4
|
*
|
|
5
5
|
* `USES_PUSH_DATA_INTERCEPTION` and `pushDataChargingContext` come from this
|
|
6
6
|
* module (not declared in the SDK) so the SDK and the client share a single
|
package/dist/index.d.mts
CHANGED
|
@@ -11,6 +11,19 @@ export declare const BasicCrawler: typeof import("crawlee").BasicCrawler;
|
|
|
11
11
|
export declare const BrowserCrawler: typeof import("crawlee").BrowserCrawler;
|
|
12
12
|
export declare const JSDOMCrawler: typeof import("crawlee").JSDOMCrawler;
|
|
13
13
|
export declare const LinkeDOMCrawler: typeof import("crawlee").LinkeDOMCrawler;
|
|
14
|
+
export declare const AdaptivePlaywrightCrawler: typeof import("crawlee").AdaptivePlaywrightCrawler;
|
|
15
|
+
export declare const RenderingTypePredictor: typeof import("crawlee").RenderingTypePredictor;
|
|
16
|
+
export declare const SessionPool: typeof import("crawlee").SessionPool;
|
|
17
|
+
export declare const Session: typeof import("crawlee").Session;
|
|
18
|
+
export declare const AutoscaledPool: typeof import("crawlee").AutoscaledPool;
|
|
19
|
+
export declare const createBasicRouter: typeof import("crawlee").createBasicRouter;
|
|
20
|
+
export declare const createHttpRouter: typeof import("crawlee").createHttpRouter;
|
|
21
|
+
export declare const createCheerioRouter: typeof import("crawlee").createCheerioRouter;
|
|
22
|
+
export declare const createJSDOMRouter: typeof import("crawlee").createJSDOMRouter;
|
|
23
|
+
export declare const createLinkeDOMRouter: typeof import("crawlee").createLinkeDOMRouter;
|
|
24
|
+
export declare const createPlaywrightRouter: typeof import("crawlee").createPlaywrightRouter;
|
|
25
|
+
export declare const createAdaptivePlaywrightRouter: typeof import("crawlee").createAdaptivePlaywrightRouter;
|
|
26
|
+
export declare const createPuppeteerRouter: typeof import("crawlee").createPuppeteerRouter;
|
|
14
27
|
export declare const ApifyClient: typeof import("scrapely-client").ScrapelyClient;
|
|
15
28
|
export declare const playwrightUtils: typeof Sdk.playwrightUtils;
|
|
16
29
|
export declare const puppeteerUtils: typeof Sdk.puppeteerUtils;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import './log-env.js';
|
|
2
2
|
import type * as CrawleeBasic from '@crawlee/basic';
|
|
3
3
|
import type * as CrawleeBrowser from '@crawlee/browser';
|
|
4
|
+
import type * as CrawleeCore from '@crawlee/core';
|
|
4
5
|
import type * as CrawleeCheerio from '@crawlee/cheerio';
|
|
5
6
|
import type * as CrawleeHttp from '@crawlee/http';
|
|
6
7
|
import type * as CrawleeJsdom from '@crawlee/jsdom';
|
|
@@ -27,6 +28,7 @@ export { ProxyConfiguration, isPlatformProxySentinel, validateProxySessionId } f
|
|
|
27
28
|
export { createProxyConfiguration, getPlatformProxyEnv, type ProxyConfigurationOptions, type CreateProxyConfigurationOptions, type PlatformProxyEnv, type ProxyInfo, type ProxyConfigurationFunction, } from './proxy-configuration.js';
|
|
28
29
|
export { BrowserConfiguration, createBrowserConfiguration, type BrowserConfigurationOptions, } from './browser-configuration.js';
|
|
29
30
|
export { Dataset, KeyValueStore, RequestQueue, log, Log, LogLevel, Logger, LoggerJson, LoggerText, } from '@crawlee/core';
|
|
31
|
+
export { Request, RequestList, Router, RequestState, EventType, Statistics, Snapshotter, SystemStatus, NonRetryableError, RetryRequestError, MissingRouteError, CriticalError, useState, mergeCookies, } from '@crawlee/core';
|
|
30
32
|
export type { LoggerOptions, KeyValueStoreIteratorOptions, KeyValueStoreOptions, DatasetConsumer, DatasetContent, DatasetDataOptions, DatasetIteratorOptions, DatasetMapper, DatasetOptions, DatasetReducer, QueueOperationInfo, RequestQueueOperationOptions, RequestQueueOptions, } from '@crawlee/core';
|
|
31
33
|
export type { KeyConsumer } from './storage/key-value-store-client.js';
|
|
32
34
|
export { utils, enqueueLinks, type EnqueueLinksOptions } from './utils.js';
|
|
@@ -47,5 +49,23 @@ export declare const JSDOMCrawler: typeof CrawleeJsdom.JSDOMCrawler;
|
|
|
47
49
|
export type JSDOMCrawler = CrawleeJsdom.JSDOMCrawler;
|
|
48
50
|
export declare const LinkeDOMCrawler: typeof CrawleeLinkedom.LinkeDOMCrawler;
|
|
49
51
|
export type LinkeDOMCrawler = CrawleeLinkedom.LinkeDOMCrawler;
|
|
52
|
+
export declare const SessionPool: typeof CrawleeCore.SessionPool;
|
|
53
|
+
export type SessionPool = CrawleeCore.SessionPool;
|
|
54
|
+
export declare const Session: typeof CrawleeCore.Session;
|
|
55
|
+
export type Session = CrawleeCore.Session;
|
|
56
|
+
export declare const AutoscaledPool: typeof CrawleeCore.AutoscaledPool;
|
|
57
|
+
export type AutoscaledPool = CrawleeCore.AutoscaledPool;
|
|
58
|
+
export declare const AdaptivePlaywrightCrawler: typeof CrawleePlaywright.AdaptivePlaywrightCrawler;
|
|
59
|
+
export type AdaptivePlaywrightCrawler = CrawleePlaywright.AdaptivePlaywrightCrawler;
|
|
60
|
+
export declare const RenderingTypePredictor: typeof CrawleePlaywright.RenderingTypePredictor;
|
|
61
|
+
export type RenderingTypePredictor = CrawleePlaywright.RenderingTypePredictor;
|
|
62
|
+
export declare const createBasicRouter: typeof CrawleeBasic.createBasicRouter;
|
|
63
|
+
export declare const createHttpRouter: typeof CrawleeHttp.createHttpRouter;
|
|
64
|
+
export declare const createCheerioRouter: typeof CrawleeCheerio.createCheerioRouter;
|
|
65
|
+
export declare const createJSDOMRouter: typeof CrawleeJsdom.createJSDOMRouter;
|
|
66
|
+
export declare const createLinkeDOMRouter: typeof CrawleeLinkedom.createLinkeDOMRouter;
|
|
67
|
+
export declare const createPlaywrightRouter: typeof CrawleePlaywright.createPlaywrightRouter;
|
|
68
|
+
export declare const createAdaptivePlaywrightRouter: typeof CrawleePlaywright.createAdaptivePlaywrightRouter;
|
|
69
|
+
export declare const createPuppeteerRouter: typeof CrawleePuppeteer.createPuppeteerRouter;
|
|
50
70
|
export declare const playwrightUtils: typeof CrawleePlaywright.playwrightUtils;
|
|
51
71
|
export declare const puppeteerUtils: typeof CrawleePuppeteer.puppeteerUtils;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Logger = exports.LogLevel = exports.Log = exports.log = exports.RequestQueue = exports.KeyValueStore = exports.Dataset = exports.createBrowserConfiguration = exports.BrowserConfiguration = exports.getPlatformProxyEnv = exports.createProxyConfiguration = exports.validateProxySessionId = exports.isPlatformProxySentinel = exports.ProxyConfiguration = exports.ApiStorageClientFactory = exports.BillingTracker = exports.createChargingClient = exports.validateRowCharges = exports.pushDataWithCharges = exports.pushDataAndCharge = exports.mergeChargeResults = exports.resolveDefaultDatasetItemEventName = exports.isDefaultDatasetItemEvent = exports.hasDefaultDatasetItemEventInPricing = exports.SCRAPELY_DEFAULT_DATASET_ITEM_EVENT = exports.APIFY_DEFAULT_DATASET_ITEM_EVENT = exports.DEFAULT_DATASET_ITEM_EVENT = exports.ChargeRejectedError = exports.ChargeUncertainError = exports.ChargingManager = exports.PlatformEventManager = exports.stepAllowed = exports.solverStatus = exports.solverConfigured = exports.solve = exports.reportSolveOutcome = exports.isBlocked = exports.detectVendor = exports.degradedStatusMessage = exports.VendorUnavailable = exports.SolverRateLimited = exports.SolverAuthError = exports.SolveAttemptError = exports.IpBanned = exports.DEGRADED_STATUS_MESSAGE = exports.Configuration = exports.EXIT_CODES = exports.ACTOR_PERMISSION_LEVEL = exports.Actor = exports.setCdpUrl = void 0;
|
|
4
|
-
exports.ScrapelyRequestList = exports.enqueueLinks = exports.utils = exports.LoggerText = exports.LoggerJson = void 0;
|
|
4
|
+
exports.ScrapelyRequestList = exports.enqueueLinks = exports.utils = exports.mergeCookies = exports.useState = exports.CriticalError = exports.MissingRouteError = exports.RetryRequestError = exports.NonRetryableError = exports.SystemStatus = exports.Snapshotter = exports.Statistics = exports.RequestState = exports.Router = exports.RequestList = exports.Request = exports.LoggerText = exports.LoggerJson = void 0;
|
|
5
5
|
require("./log-env.js");
|
|
6
6
|
const lazy_exports_js_1 = require("./lazy-exports.js");
|
|
7
7
|
var patch_browser_pool_js_1 = require("./patch-browser-pool.js");
|
|
@@ -74,6 +74,22 @@ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function ()
|
|
|
74
74
|
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return core_1.Logger; } });
|
|
75
75
|
Object.defineProperty(exports, "LoggerJson", { enumerable: true, get: function () { return core_1.LoggerJson; } });
|
|
76
76
|
Object.defineProperty(exports, "LoggerText", { enumerable: true, get: function () { return core_1.LoggerText; } });
|
|
77
|
+
// Cheap core classes the reference SDK re-exports from crawlee. @crawlee/core is
|
|
78
|
+
// already loaded by the exports above, so these are plain re-exports.
|
|
79
|
+
var core_2 = require("@crawlee/core");
|
|
80
|
+
Object.defineProperty(exports, "Request", { enumerable: true, get: function () { return core_2.Request; } });
|
|
81
|
+
Object.defineProperty(exports, "RequestList", { enumerable: true, get: function () { return core_2.RequestList; } });
|
|
82
|
+
Object.defineProperty(exports, "Router", { enumerable: true, get: function () { return core_2.Router; } });
|
|
83
|
+
Object.defineProperty(exports, "RequestState", { enumerable: true, get: function () { return core_2.RequestState; } });
|
|
84
|
+
Object.defineProperty(exports, "Statistics", { enumerable: true, get: function () { return core_2.Statistics; } });
|
|
85
|
+
Object.defineProperty(exports, "Snapshotter", { enumerable: true, get: function () { return core_2.Snapshotter; } });
|
|
86
|
+
Object.defineProperty(exports, "SystemStatus", { enumerable: true, get: function () { return core_2.SystemStatus; } });
|
|
87
|
+
Object.defineProperty(exports, "NonRetryableError", { enumerable: true, get: function () { return core_2.NonRetryableError; } });
|
|
88
|
+
Object.defineProperty(exports, "RetryRequestError", { enumerable: true, get: function () { return core_2.RetryRequestError; } });
|
|
89
|
+
Object.defineProperty(exports, "MissingRouteError", { enumerable: true, get: function () { return core_2.MissingRouteError; } });
|
|
90
|
+
Object.defineProperty(exports, "CriticalError", { enumerable: true, get: function () { return core_2.CriticalError; } });
|
|
91
|
+
Object.defineProperty(exports, "useState", { enumerable: true, get: function () { return core_2.useState; } });
|
|
92
|
+
Object.defineProperty(exports, "mergeCookies", { enumerable: true, get: function () { return core_2.mergeCookies; } });
|
|
77
93
|
// Apify v2 compatible utils namespace
|
|
78
94
|
var utils_js_1 = require("./utils.js");
|
|
79
95
|
Object.defineProperty(exports, "utils", { enumerable: true, get: function () { return utils_js_1.utils; } });
|
package/dist/index.mjs
CHANGED
|
@@ -24,6 +24,19 @@ export const BasicCrawler = lazyExport('BasicCrawler', 'class');
|
|
|
24
24
|
export const BrowserCrawler = lazyExport('BrowserCrawler', 'class');
|
|
25
25
|
export const JSDOMCrawler = lazyExport('JSDOMCrawler', 'class');
|
|
26
26
|
export const LinkeDOMCrawler = lazyExport('LinkeDOMCrawler', 'class');
|
|
27
|
+
export const AdaptivePlaywrightCrawler = lazyExport('AdaptivePlaywrightCrawler', 'class');
|
|
28
|
+
export const RenderingTypePredictor = lazyExport('RenderingTypePredictor', 'class');
|
|
29
|
+
export const SessionPool = lazyExport('SessionPool', 'class');
|
|
30
|
+
export const Session = lazyExport('Session', 'class');
|
|
31
|
+
export const AutoscaledPool = lazyExport('AutoscaledPool', 'class');
|
|
32
|
+
export const createBasicRouter = lazyExport('createBasicRouter', 'class');
|
|
33
|
+
export const createHttpRouter = lazyExport('createHttpRouter', 'class');
|
|
34
|
+
export const createCheerioRouter = lazyExport('createCheerioRouter', 'class');
|
|
35
|
+
export const createJSDOMRouter = lazyExport('createJSDOMRouter', 'class');
|
|
36
|
+
export const createLinkeDOMRouter = lazyExport('createLinkeDOMRouter', 'class');
|
|
37
|
+
export const createPlaywrightRouter = lazyExport('createPlaywrightRouter', 'class');
|
|
38
|
+
export const createAdaptivePlaywrightRouter = lazyExport('createAdaptivePlaywrightRouter', 'class');
|
|
39
|
+
export const createPuppeteerRouter = lazyExport('createPuppeteerRouter', 'class');
|
|
27
40
|
export const ApifyClient = lazyExport('ApifyClient', 'class');
|
|
28
41
|
export const playwrightUtils = lazyExport('playwrightUtils', 'object');
|
|
29
42
|
export const puppeteerUtils = lazyExport('puppeteerUtils', 'object');
|
package/dist/lazy-exports.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.lazyExport = lazyExport;
|
|
|
7
7
|
/**
|
|
8
8
|
* Public SDK names that load heavy packages: Crawlee's crawlers (jsdom,
|
|
9
9
|
* linkedom, cheerio, the browser pool, playwright) and scrapely-client.
|
|
10
|
-
* Importing the SDK used to load all of them, ~4 s at 1 core
|
|
10
|
+
* Importing the SDK used to load all of them, ~4 s at 1 core on the platform;
|
|
11
11
|
* they now load on first access.
|
|
12
12
|
*
|
|
13
13
|
* - CommonJS (`require('scrapely').PlaywrightCrawler`): a getter on the
|
|
@@ -30,6 +30,19 @@ exports.LAZY_EXPORTS = {
|
|
|
30
30
|
BrowserCrawler: ['@crawlee/browser', 'BrowserCrawler'],
|
|
31
31
|
JSDOMCrawler: ['@crawlee/jsdom', 'JSDOMCrawler'],
|
|
32
32
|
LinkeDOMCrawler: ['@crawlee/linkedom', 'LinkeDOMCrawler'],
|
|
33
|
+
AdaptivePlaywrightCrawler: ['@crawlee/playwright', 'AdaptivePlaywrightCrawler'],
|
|
34
|
+
RenderingTypePredictor: ['@crawlee/playwright', 'RenderingTypePredictor'],
|
|
35
|
+
SessionPool: ['@crawlee/core', 'SessionPool'],
|
|
36
|
+
Session: ['@crawlee/core', 'Session'],
|
|
37
|
+
AutoscaledPool: ['@crawlee/core', 'AutoscaledPool'],
|
|
38
|
+
createBasicRouter: ['@crawlee/basic', 'createBasicRouter'],
|
|
39
|
+
createHttpRouter: ['@crawlee/http', 'createHttpRouter'],
|
|
40
|
+
createCheerioRouter: ['@crawlee/cheerio', 'createCheerioRouter'],
|
|
41
|
+
createJSDOMRouter: ['@crawlee/jsdom', 'createJSDOMRouter'],
|
|
42
|
+
createLinkeDOMRouter: ['@crawlee/linkedom', 'createLinkeDOMRouter'],
|
|
43
|
+
createPlaywrightRouter: ['@crawlee/playwright', 'createPlaywrightRouter'],
|
|
44
|
+
createAdaptivePlaywrightRouter: ['@crawlee/playwright', 'createAdaptivePlaywrightRouter'],
|
|
45
|
+
createPuppeteerRouter: ['@crawlee/puppeteer', 'createPuppeteerRouter'],
|
|
33
46
|
playwrightUtils: ['@crawlee/playwright', 'playwrightUtils'],
|
|
34
47
|
puppeteerUtils: ['@crawlee/puppeteer', 'puppeteerUtils'],
|
|
35
48
|
ApifyClient: ['scrapely-client', 'ApifyClient'],
|
package/dist/node-wrapper.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The base images preload the SDK's browser patch into every Node process
|
|
5
5
|
* (NODE_OPTIONS=--import /opt/scrapely/register.mjs). `npm start` is two Node
|
|
6
6
|
* processes: npm, which only spawns the start script, and the actor. npm itself
|
|
7
|
-
* costs ~0.6 s at 1 core and ~2.7 s at 0.25 core (1 GB run)
|
|
7
|
+
* costs ~0.6 s at 1 core and ~2.7 s at 0.25 core (1 GB run) on the platform.
|
|
8
8
|
*
|
|
9
9
|
* - npm / npx / yarn / pnpm processes never run actor code, so they get no
|
|
10
10
|
* loader hooks. Their children inherit NODE_OPTIONS and get the preload.
|
|
@@ -66,7 +66,7 @@ export declare function patchLoadedCrawleeCopies(): void;
|
|
|
66
66
|
* - `cdpUrl` becomes a valid launcher option (PlaywrightLauncher and
|
|
67
67
|
* PuppeteerLauncher copy BrowserLauncher's shape when their file loads, so
|
|
68
68
|
* each class gets it; browser-launcher.js loads first, so the copies inherit it).
|
|
69
|
-
* - Local launches get
|
|
69
|
+
* - Local launches get sandbox-safe Chrome args on the platform. Playwright/Puppeteer
|
|
70
70
|
* pool launches use CDP and skip this.
|
|
71
71
|
*
|
|
72
72
|
* @internal Exported for tests.
|
|
@@ -60,20 +60,20 @@ function isAtHome() {
|
|
|
60
60
|
function browserPatchDisabled() {
|
|
61
61
|
return process.env.SCRAPELY_BROWSER_PATCH === '0';
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
const
|
|
63
|
+
/** Platform sandbox: no crash reporter; read-only rootfs + tmpfs /tmp loses crash dirs. */
|
|
64
|
+
const SANDBOX_CHROME_ARGS = [
|
|
65
65
|
'--disable-breakpad',
|
|
66
66
|
'--disable-crash-reporter',
|
|
67
67
|
'--disable-dev-shm-usage',
|
|
68
68
|
'--crash-dumps-dir=/tmp',
|
|
69
69
|
];
|
|
70
|
-
/** Add
|
|
71
|
-
function
|
|
70
|
+
/** Add sandbox-safe Chrome args to local launch options when running on the platform. */
|
|
71
|
+
function withSandboxArgs(options) {
|
|
72
72
|
const opts = { ...(options ?? {}) };
|
|
73
|
-
if (
|
|
73
|
+
if (!isAtHome())
|
|
74
74
|
return opts;
|
|
75
75
|
const args = [...(opts.args ?? [])];
|
|
76
|
-
for (const arg of
|
|
76
|
+
for (const arg of SANDBOX_CHROME_ARGS) {
|
|
77
77
|
if (!args.includes(arg))
|
|
78
78
|
args.push(arg);
|
|
79
79
|
}
|
|
@@ -84,7 +84,7 @@ function withGVisorArgs(options) {
|
|
|
84
84
|
* Require CDP_URL for platform browser runs. Local launch is disabled on the
|
|
85
85
|
* platform unless the build is unpatched (SCRAPELY_BROWSER_PATCH=0) — otherwise
|
|
86
86
|
* a worker container without CDP_URL is a misconfiguration and must fail
|
|
87
|
-
* loudly instead of launching Chromium inside the
|
|
87
|
+
* loudly instead of launching Chromium inside the platform container.
|
|
88
88
|
*/
|
|
89
89
|
function requireCdpUrl() {
|
|
90
90
|
const cdpUrl = getCdpUrl();
|
|
@@ -491,11 +491,11 @@ function patchLoadedCrawleeCopies() {
|
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
493
|
}
|
|
494
|
-
// ─── Crawlee launchers: `cdpUrl` option +
|
|
494
|
+
// ─── Crawlee launchers: `cdpUrl` option + sandbox-safe local launch args ─────
|
|
495
495
|
//
|
|
496
496
|
// Applied when a launcher module loads (Module._load hook below), never by
|
|
497
497
|
// importing Crawlee up front: loading @crawlee/browser-pool and playwright
|
|
498
|
-
// eagerly cost ~2.4 s per Node process
|
|
498
|
+
// eagerly cost ~2.4 s per Node process on the platform.
|
|
499
499
|
/** Requests that load a Crawlee launcher file (`require('./internals/browser-launcher')`). */
|
|
500
500
|
const LAUNCHER_REQUEST_RE = /(^|[/\\])internals[/\\](browser|playwright|puppeteer)-launcher(\.js)?$/;
|
|
501
501
|
/** Cached module filenames of Crawlee launcher files (any copy). */
|
|
@@ -533,7 +533,7 @@ function addCdpUrlOption(Launcher, parentFilename) {
|
|
|
533
533
|
* - `cdpUrl` becomes a valid launcher option (PlaywrightLauncher and
|
|
534
534
|
* PuppeteerLauncher copy BrowserLauncher's shape when their file loads, so
|
|
535
535
|
* each class gets it; browser-launcher.js loads first, so the copies inherit it).
|
|
536
|
-
* - Local launches get
|
|
536
|
+
* - Local launches get sandbox-safe Chrome args on the platform. Playwright/Puppeteer
|
|
537
537
|
* pool launches use CDP and skip this.
|
|
538
538
|
*
|
|
539
539
|
* @internal Exported for tests.
|
|
@@ -555,17 +555,17 @@ function patchBrowserLauncherModule(mod, parentFilename) {
|
|
|
555
555
|
}
|
|
556
556
|
const BrowserLauncher = mod.BrowserLauncher;
|
|
557
557
|
const proto = BrowserLauncher?.prototype;
|
|
558
|
-
if (!proto || typeof proto.createLaunchOptions !== 'function' || proto.
|
|
558
|
+
if (!proto || typeof proto.createLaunchOptions !== 'function' || proto._scrapelySandboxArgsPatched)
|
|
559
559
|
return;
|
|
560
560
|
const origCreateLaunchOptions = proto.createLaunchOptions;
|
|
561
561
|
proto.createLaunchOptions = function (...args) {
|
|
562
562
|
const launchOptions = origCreateLaunchOptions.apply(this, args);
|
|
563
|
-
//
|
|
563
|
+
// The sandbox has no Chrome crashpad_handler support. Also needed
|
|
564
564
|
// with ReadonlyRootfs + tmpfs /tmp where /tmp/.X11-unix and the crashpad
|
|
565
565
|
// database dir don't persist from the base image.
|
|
566
|
-
if (
|
|
566
|
+
if (isAtHome() && launchOptions) {
|
|
567
567
|
launchOptions.args = launchOptions.args || [];
|
|
568
|
-
for (const arg of
|
|
568
|
+
for (const arg of SANDBOX_CHROME_ARGS) {
|
|
569
569
|
if (!launchOptions.args.includes(arg)) {
|
|
570
570
|
launchOptions.args.push(arg);
|
|
571
571
|
}
|
|
@@ -573,7 +573,7 @@ function patchBrowserLauncherModule(mod, parentFilename) {
|
|
|
573
573
|
}
|
|
574
574
|
return launchOptions;
|
|
575
575
|
};
|
|
576
|
-
proto.
|
|
576
|
+
proto._scrapelySandboxArgsPatched = true;
|
|
577
577
|
}
|
|
578
578
|
/**
|
|
579
579
|
* Split our `browserConfiguration` option off raw launch options. The options
|
|
@@ -655,7 +655,7 @@ function patchRawBrowserType(chromium, browserType, name) {
|
|
|
655
655
|
browserType.launch = async function rawLaunchPatched(launchOptions = {}) {
|
|
656
656
|
const { options, browserConfiguration } = takeBrowserConfiguration(launchOptions);
|
|
657
657
|
if (browserPatchDisabled())
|
|
658
|
-
return origLaunch.call(this,
|
|
658
|
+
return origLaunch.call(this, withSandboxArgs(options));
|
|
659
659
|
const cdpUrl = rawLaunchCdpUrl(options, browserConfiguration);
|
|
660
660
|
if (!cdpUrl) {
|
|
661
661
|
if (!isAtHome())
|
|
@@ -674,7 +674,7 @@ function patchRawBrowserType(chromium, browserType, name) {
|
|
|
674
674
|
const { options, browserConfiguration } = takeBrowserConfiguration(lpcArgs[optionsIndex]);
|
|
675
675
|
const withOptions = (opts) => optionsIndex === 1 ? [userDataDir, opts] : [opts];
|
|
676
676
|
if (browserPatchDisabled())
|
|
677
|
-
return origLpc.call(this, ...withOptions(
|
|
677
|
+
return origLpc.call(this, ...withOptions(withSandboxArgs(options)));
|
|
678
678
|
const cdpUrl = rawLaunchCdpUrl(options, browserConfiguration);
|
|
679
679
|
if (!cdpUrl) {
|
|
680
680
|
if (!isAtHome()) {
|
|
@@ -719,7 +719,7 @@ function patchRawPlaywrightModule(pw) {
|
|
|
719
719
|
async function puppeteerLaunchPatched(self, origLaunch, moduleName, launchOptions = {}) {
|
|
720
720
|
const { options, browserConfiguration } = takeBrowserConfiguration(launchOptions);
|
|
721
721
|
if (browserPatchDisabled())
|
|
722
|
-
return origLaunch.call(self,
|
|
722
|
+
return origLaunch.call(self, withSandboxArgs(options));
|
|
723
723
|
const cdpUrl = rawLaunchCdpUrl(options, browserConfiguration);
|
|
724
724
|
if (!cdpUrl) {
|
|
725
725
|
if (!isAtHome())
|
package/dist/solver.js
CHANGED
|
@@ -31,7 +31,7 @@ exports.solve = solve;
|
|
|
31
31
|
exports.reportSolveOutcome = reportSolveOutcome;
|
|
32
32
|
exports.clearSolverStatusCache = clearSolverStatusCache;
|
|
33
33
|
exports.DEGRADED_STATUS_MESSAGE = 'Site protection temporarily unavailable, results may be incomplete';
|
|
34
|
-
const COMPAT_SUFFIXES = ['/compat/v1'
|
|
34
|
+
const COMPAT_SUFFIXES = ['/compat/v1'];
|
|
35
35
|
const STATUS_TTL_MS = 15_000;
|
|
36
36
|
const MAX_BODY = 4 * 1024 * 1024;
|
|
37
37
|
const UNAVAILABLE_CODES = new Set(['vendor_degraded', 'vendor_down', 'provider_unavailable', 'vendor_unsupported', 'solver_not_enabled']);
|
package/package.json
CHANGED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import Docker from 'dockerode';
|
|
2
|
-
import { TimeoutManager } from './timeout-manager.js';
|
|
3
|
-
import { DragonflyClient } from '../storage/dragonfly-client.js';
|
|
4
|
-
import type { UserPlan } from '../storage/ttl-manager.js';
|
|
5
|
-
export interface RunOptions {
|
|
6
|
-
/** Path to the actor package directory */
|
|
7
|
-
packagePath: string;
|
|
8
|
-
/** Path to the .env file */
|
|
9
|
-
envPath?: string;
|
|
10
|
-
/** Environment variables to pass to container */
|
|
11
|
-
env?: Record<string, string>;
|
|
12
|
-
/** Maximum memory in MB (default: from manifest or 512) */
|
|
13
|
-
memoryMb?: number;
|
|
14
|
-
/** Maximum timeout in seconds (default: from manifest or 1800) */
|
|
15
|
-
timeout?: number;
|
|
16
|
-
/** Run ID for tracking */
|
|
17
|
-
runId?: string;
|
|
18
|
-
/** User ID */
|
|
19
|
-
userId?: string;
|
|
20
|
-
/** User plan for TTL */
|
|
21
|
-
userPlan?: UserPlan;
|
|
22
|
-
/** Dragonfly client for storage cleanup */
|
|
23
|
-
dragonflyClient?: DragonflyClient;
|
|
24
|
-
}
|
|
25
|
-
export interface RunResult {
|
|
26
|
-
containerId: string;
|
|
27
|
-
exitCode: number;
|
|
28
|
-
logs: string;
|
|
29
|
-
status: 'success' | 'failed' | 'timed_out' | 'killed';
|
|
30
|
-
duration: number;
|
|
31
|
-
/** Storage IDs that were cleaned up */
|
|
32
|
-
cleanedUp?: {
|
|
33
|
-
requestQueueDeleted: boolean;
|
|
34
|
-
logsUploadedToS3: boolean;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export declare class DockerManager {
|
|
38
|
-
private docker;
|
|
39
|
-
private timeoutManager;
|
|
40
|
-
private imageName;
|
|
41
|
-
constructor(options?: {
|
|
42
|
-
socketPath?: string;
|
|
43
|
-
imageName?: string;
|
|
44
|
-
});
|
|
45
|
-
/**
|
|
46
|
-
* Run an actor package in a container
|
|
47
|
-
*/
|
|
48
|
-
run(options: RunOptions): Promise<RunResult>;
|
|
49
|
-
/**
|
|
50
|
-
* Cleanup storage after run completes.
|
|
51
|
-
* - Deletes request queue immediately (no longer needed)
|
|
52
|
-
* - Uploads logs to S3 for long-term storage
|
|
53
|
-
* - Updates run metadata with final status
|
|
54
|
-
*/
|
|
55
|
-
private cleanupAfterRun;
|
|
56
|
-
/**
|
|
57
|
-
* Stop a container (SIGTERM)
|
|
58
|
-
*/
|
|
59
|
-
stop(containerId: string, timeoutSeconds?: number): Promise<void>;
|
|
60
|
-
/**
|
|
61
|
-
* Kill a container (SIGKILL)
|
|
62
|
-
*/
|
|
63
|
-
kill(containerId: string): Promise<void>;
|
|
64
|
-
/**
|
|
65
|
-
* Get logs from a container
|
|
66
|
-
*/
|
|
67
|
-
getLogs(containerId: string): Promise<string>;
|
|
68
|
-
/**
|
|
69
|
-
* Get container status
|
|
70
|
-
*/
|
|
71
|
-
getStatus(containerId: string): Promise<string | null>;
|
|
72
|
-
/**
|
|
73
|
-
* List all scrapely containers
|
|
74
|
-
*/
|
|
75
|
-
listContainers(): Promise<Docker.ContainerInfo[]>;
|
|
76
|
-
/**
|
|
77
|
-
* Get the timeout manager
|
|
78
|
-
*/
|
|
79
|
-
getTimeoutManager(): TimeoutManager;
|
|
80
|
-
/**
|
|
81
|
-
* Track internal data transfer from container network stats.
|
|
82
|
-
* Following Apify's model:
|
|
83
|
-
* - Ingress (rx_bytes) is FREE - scraped content coming in
|
|
84
|
-
* - Internal transfer is tracked separately in storage clients
|
|
85
|
-
* - External egress is tracked in API layer when users fetch results
|
|
86
|
-
*/
|
|
87
|
-
private trackDataTransfer;
|
|
88
|
-
}
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DockerManager = void 0;
|
|
7
|
-
const dockerode_1 = __importDefault(require("dockerode"));
|
|
8
|
-
const timeout_manager_js_1 = require("./timeout-manager.js");
|
|
9
|
-
const validator_js_1 = require("../validator.js");
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const run_metadata_js_1 = require("../storage/run-metadata.js");
|
|
12
|
-
const log_manager_js_1 = require("../storage/log-manager.js");
|
|
13
|
-
class DockerManager {
|
|
14
|
-
docker;
|
|
15
|
-
timeoutManager;
|
|
16
|
-
imageName;
|
|
17
|
-
constructor(options) {
|
|
18
|
-
this.docker = new dockerode_1.default({
|
|
19
|
-
socketPath: options?.socketPath || '/var/run/docker.sock',
|
|
20
|
-
});
|
|
21
|
-
this.timeoutManager = new timeout_manager_js_1.TimeoutManager();
|
|
22
|
-
this.imageName = options?.imageName || 'scrapely-runner:latest';
|
|
23
|
-
// Set up timeout event handlers
|
|
24
|
-
this.timeoutManager.on('timeout', async ({ containerId }) => {
|
|
25
|
-
console.log(`[DockerManager] Container ${containerId} timed out, sending SIGTERM...`);
|
|
26
|
-
await this.stop(containerId, 30);
|
|
27
|
-
});
|
|
28
|
-
this.timeoutManager.on('kill', async ({ containerId }) => {
|
|
29
|
-
console.log(`[DockerManager] Container ${containerId} grace period expired, sending SIGKILL...`);
|
|
30
|
-
await this.kill(containerId);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Run an actor package in a container
|
|
35
|
-
*/
|
|
36
|
-
async run(options) {
|
|
37
|
-
// Validate package
|
|
38
|
-
const validation = (0, validator_js_1.validatePackage)(options.packagePath);
|
|
39
|
-
if (!validation.valid) {
|
|
40
|
-
throw new Error(`Invalid package: ${validation.errors.join(', ')}`);
|
|
41
|
-
}
|
|
42
|
-
const manifest = validation.manifest;
|
|
43
|
-
const startTime = Date.now();
|
|
44
|
-
const memoryMb = options.memoryMb || manifest.maxMemory || 512;
|
|
45
|
-
const timeout = options.timeout || manifest.maxTimeout || 1800;
|
|
46
|
-
// Build environment variables
|
|
47
|
-
const envVars = [];
|
|
48
|
-
if (options.env) {
|
|
49
|
-
for (const [key, value] of Object.entries(options.env)) {
|
|
50
|
-
envVars.push(`${key}=${value}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Build volume mounts
|
|
54
|
-
const binds = [
|
|
55
|
-
`${path_1.default.resolve(options.packagePath)}:/actor/package:ro`,
|
|
56
|
-
];
|
|
57
|
-
if (options.envPath) {
|
|
58
|
-
binds.push(`${path_1.default.resolve(options.envPath)}:/actor/.env:ro`);
|
|
59
|
-
}
|
|
60
|
-
// Create container
|
|
61
|
-
console.log(`[DockerManager] Creating container for ${manifest.name}@${manifest.version}...`);
|
|
62
|
-
const container = await this.docker.createContainer({
|
|
63
|
-
Image: this.imageName,
|
|
64
|
-
Env: envVars,
|
|
65
|
-
HostConfig: {
|
|
66
|
-
Binds: binds,
|
|
67
|
-
Memory: memoryMb * 1024 * 1024, // Convert MB to bytes
|
|
68
|
-
AutoRemove: false, // We'll remove it manually after getting logs
|
|
69
|
-
},
|
|
70
|
-
Labels: {
|
|
71
|
-
'scrapely.actor.name': manifest.name,
|
|
72
|
-
'scrapely.actor.version': manifest.version,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
const containerId = container.id;
|
|
76
|
-
console.log(`[DockerManager] Container created: ${containerId.substring(0, 12)}`);
|
|
77
|
-
// Start timeout timer
|
|
78
|
-
const timeoutOptions = {
|
|
79
|
-
maxTimeout: timeout,
|
|
80
|
-
gracePeriod: 30,
|
|
81
|
-
};
|
|
82
|
-
this.timeoutManager.startTimer(containerId, timeoutOptions);
|
|
83
|
-
// Set log TTL now so the key has the correct expiry from the first byte written
|
|
84
|
-
if (options.dragonflyClient && options.runId) {
|
|
85
|
-
const logManager = new log_manager_js_1.LogManager(options.dragonflyClient);
|
|
86
|
-
await logManager.setLogTtl(options.runId, options.userPlan ?? 'free');
|
|
87
|
-
}
|
|
88
|
-
// Start container
|
|
89
|
-
await container.start();
|
|
90
|
-
console.log(`[DockerManager] Container started with ${timeout}s timeout`);
|
|
91
|
-
// Wait for container to finish
|
|
92
|
-
let exitCode = 0;
|
|
93
|
-
let status = 'success';
|
|
94
|
-
try {
|
|
95
|
-
const result = await container.wait();
|
|
96
|
-
exitCode = result.StatusCode || 0;
|
|
97
|
-
// Check if it was timed out
|
|
98
|
-
const timerState = this.timeoutManager.getState(containerId);
|
|
99
|
-
if (timerState?.status === 'killed') {
|
|
100
|
-
status = 'killed';
|
|
101
|
-
}
|
|
102
|
-
else if (timerState?.status === 'timed_out') {
|
|
103
|
-
status = 'timed_out';
|
|
104
|
-
}
|
|
105
|
-
else if (exitCode !== 0) {
|
|
106
|
-
status = 'failed';
|
|
107
|
-
}
|
|
108
|
-
// Stop the timer
|
|
109
|
-
this.timeoutManager.stopTimer(containerId);
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
// Container might have been killed
|
|
113
|
-
const timerState = this.timeoutManager.getState(containerId);
|
|
114
|
-
if (timerState?.status === 'killed') {
|
|
115
|
-
status = 'killed';
|
|
116
|
-
exitCode = 137; // SIGKILL exit code
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
status = 'failed';
|
|
120
|
-
exitCode = 1;
|
|
121
|
-
}
|
|
122
|
-
this.timeoutManager.stopTimer(containerId);
|
|
123
|
-
}
|
|
124
|
-
// Get logs
|
|
125
|
-
const logs = await this.getLogs(containerId);
|
|
126
|
-
// Get network stats and track data transfer before removing container
|
|
127
|
-
await this.trackDataTransfer(container, options);
|
|
128
|
-
// Remove container
|
|
129
|
-
try {
|
|
130
|
-
await container.remove();
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
// Ignore removal errors
|
|
134
|
-
}
|
|
135
|
-
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
136
|
-
console.log(`[DockerManager] Container finished: ${status} (exit code: ${exitCode}, duration: ${duration}s)`);
|
|
137
|
-
// Cleanup after run completes
|
|
138
|
-
const cleanedUp = await this.cleanupAfterRun(options, status);
|
|
139
|
-
return {
|
|
140
|
-
containerId,
|
|
141
|
-
exitCode,
|
|
142
|
-
logs,
|
|
143
|
-
status,
|
|
144
|
-
duration,
|
|
145
|
-
cleanedUp,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Cleanup storage after run completes.
|
|
150
|
-
* - Deletes request queue immediately (no longer needed)
|
|
151
|
-
* - Uploads logs to S3 for long-term storage
|
|
152
|
-
* - Updates run metadata with final status
|
|
153
|
-
*/
|
|
154
|
-
async cleanupAfterRun(options, status) {
|
|
155
|
-
const result = { requestQueueDeleted: false, logsUploadedToS3: false };
|
|
156
|
-
if (!options.dragonflyClient || !options.runId) {
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
// Get run metadata to find storage IDs
|
|
161
|
-
const metadataManager = new run_metadata_js_1.RunMetadataManager(options.dragonflyClient);
|
|
162
|
-
const metadata = await metadataManager.get(options.runId);
|
|
163
|
-
if (metadata) {
|
|
164
|
-
// Delete request queue immediately - it's only useful during execution
|
|
165
|
-
const rqClient = options.dragonflyClient.requestQueue(metadata.storageIds.requestQueue);
|
|
166
|
-
await rqClient.delete();
|
|
167
|
-
result.requestQueueDeleted = true;
|
|
168
|
-
console.log(`[DockerManager] Deleted request queue: ${metadata.storageIds.requestQueue}`);
|
|
169
|
-
// Upload logs to S3 for long-term storage
|
|
170
|
-
const logManager = new log_manager_js_1.LogManager(options.dragonflyClient);
|
|
171
|
-
const s3Key = await logManager.uploadToS3(options.runId, metadata.userId);
|
|
172
|
-
result.logsUploadedToS3 = s3Key !== null;
|
|
173
|
-
// Delete logs from Redis now that they're in S3 — saves RAM.
|
|
174
|
-
// If S3 upload wasn't configured/failed, TTL set at run start handles expiry.
|
|
175
|
-
if (result.logsUploadedToS3) {
|
|
176
|
-
await logManager.deleteLogs(options.runId);
|
|
177
|
-
console.log(`[DockerManager] Deleted Redis logs for run ${options.runId} (persisted to S3)`);
|
|
178
|
-
}
|
|
179
|
-
// Update run metadata with final status
|
|
180
|
-
const runStatus = status === 'success' ? 'success'
|
|
181
|
-
: status === 'timed_out' ? 'timed_out'
|
|
182
|
-
: status === 'killed' ? 'killed'
|
|
183
|
-
: 'failed';
|
|
184
|
-
await metadataManager.finish(options.runId, runStatus);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
catch (error) {
|
|
188
|
-
console.error(`[DockerManager] Cleanup error: ${error.message}`);
|
|
189
|
-
}
|
|
190
|
-
return result;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Stop a container (SIGTERM)
|
|
194
|
-
*/
|
|
195
|
-
async stop(containerId, timeoutSeconds = 30) {
|
|
196
|
-
const container = this.docker.getContainer(containerId);
|
|
197
|
-
try {
|
|
198
|
-
await container.stop({ t: timeoutSeconds });
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
if (error.statusCode !== 304) { // Not modified - already stopped
|
|
202
|
-
throw error;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Kill a container (SIGKILL)
|
|
208
|
-
*/
|
|
209
|
-
async kill(containerId) {
|
|
210
|
-
const container = this.docker.getContainer(containerId);
|
|
211
|
-
try {
|
|
212
|
-
await container.kill();
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
if (error.statusCode !== 409) { // Not running
|
|
216
|
-
throw error;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Get logs from a container
|
|
222
|
-
*/
|
|
223
|
-
async getLogs(containerId) {
|
|
224
|
-
const container = this.docker.getContainer(containerId);
|
|
225
|
-
try {
|
|
226
|
-
const logStream = await container.logs({
|
|
227
|
-
stdout: true,
|
|
228
|
-
stderr: true,
|
|
229
|
-
timestamps: false,
|
|
230
|
-
});
|
|
231
|
-
// Docker logs are prefixed with headers, clean them
|
|
232
|
-
return logStream.toString('utf-8').replace(/\x00\x00\x00[\x00-\xff][\x00-\xff][\x00-\xff]/g, '');
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
return '';
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Get container status
|
|
240
|
-
*/
|
|
241
|
-
async getStatus(containerId) {
|
|
242
|
-
const container = this.docker.getContainer(containerId);
|
|
243
|
-
try {
|
|
244
|
-
const info = await container.inspect();
|
|
245
|
-
return info.State.Status;
|
|
246
|
-
}
|
|
247
|
-
catch (error) {
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* List all scrapely containers
|
|
253
|
-
*/
|
|
254
|
-
async listContainers() {
|
|
255
|
-
const containers = await this.docker.listContainers({
|
|
256
|
-
all: true,
|
|
257
|
-
filters: {
|
|
258
|
-
label: ['scrapely.actor.name'],
|
|
259
|
-
},
|
|
260
|
-
});
|
|
261
|
-
return containers;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Get the timeout manager
|
|
265
|
-
*/
|
|
266
|
-
getTimeoutManager() {
|
|
267
|
-
return this.timeoutManager;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Track internal data transfer from container network stats.
|
|
271
|
-
* Following Apify's model:
|
|
272
|
-
* - Ingress (rx_bytes) is FREE - scraped content coming in
|
|
273
|
-
* - Internal transfer is tracked separately in storage clients
|
|
274
|
-
* - External egress is tracked in API layer when users fetch results
|
|
275
|
-
*/
|
|
276
|
-
async trackDataTransfer(container, options) {
|
|
277
|
-
// Ingress is free - no billing for data coming into the platform from internet
|
|
278
|
-
// Internal transfer (actor ↔ storage) is tracked in storage clients
|
|
279
|
-
// External egress (platform → user) is tracked in API routes
|
|
280
|
-
// This method is kept for future use if needed
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
exports.DockerManager = DockerManager;
|
package/dist/runner/index.d.ts
DELETED
package/dist/runner/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TimeoutManager = exports.DockerManager = void 0;
|
|
4
|
-
var docker_manager_js_1 = require("./docker-manager.js");
|
|
5
|
-
Object.defineProperty(exports, "DockerManager", { enumerable: true, get: function () { return docker_manager_js_1.DockerManager; } });
|
|
6
|
-
var timeout_manager_js_1 = require("./timeout-manager.js");
|
|
7
|
-
Object.defineProperty(exports, "TimeoutManager", { enumerable: true, get: function () { return timeout_manager_js_1.TimeoutManager; } });
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
export interface TimeoutOptions {
|
|
3
|
-
/** Maximum runtime in seconds */
|
|
4
|
-
maxTimeout: number;
|
|
5
|
-
/** Grace period after SIGTERM before SIGKILL (default: 30s) */
|
|
6
|
-
gracePeriod?: number;
|
|
7
|
-
}
|
|
8
|
-
export interface TimerState {
|
|
9
|
-
containerId: string;
|
|
10
|
-
startedAt: Date;
|
|
11
|
-
timeoutAt: Date;
|
|
12
|
-
status: 'running' | 'stopping' | 'timed_out' | 'completed' | 'killed';
|
|
13
|
-
timeoutTimer?: NodeJS.Timeout;
|
|
14
|
-
killTimer?: NodeJS.Timeout;
|
|
15
|
-
}
|
|
16
|
-
export declare class TimeoutManager extends EventEmitter {
|
|
17
|
-
private timers;
|
|
18
|
-
private defaultGracePeriod;
|
|
19
|
-
/**
|
|
20
|
-
* Start a timeout timer for a container
|
|
21
|
-
*/
|
|
22
|
-
startTimer(containerId: string, options: TimeoutOptions): TimerState;
|
|
23
|
-
/**
|
|
24
|
-
* Stop a timer (container completed successfully)
|
|
25
|
-
*/
|
|
26
|
-
stopTimer(containerId: string): void;
|
|
27
|
-
/**
|
|
28
|
-
* Get timer state for a container
|
|
29
|
-
*/
|
|
30
|
-
getState(containerId: string): TimerState | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* Get remaining time in seconds for a container
|
|
33
|
-
*/
|
|
34
|
-
getRemainingTime(containerId: string): number | undefined;
|
|
35
|
-
/**
|
|
36
|
-
* Get all active timers
|
|
37
|
-
*/
|
|
38
|
-
getActiveTimers(): TimerState[];
|
|
39
|
-
/**
|
|
40
|
-
* Clear all timers
|
|
41
|
-
*/
|
|
42
|
-
clearAll(): void;
|
|
43
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TimeoutManager = void 0;
|
|
4
|
-
const events_1 = require("events");
|
|
5
|
-
class TimeoutManager extends events_1.EventEmitter {
|
|
6
|
-
timers = new Map();
|
|
7
|
-
defaultGracePeriod = 30; // seconds
|
|
8
|
-
/**
|
|
9
|
-
* Start a timeout timer for a container
|
|
10
|
-
*/
|
|
11
|
-
startTimer(containerId, options) {
|
|
12
|
-
// Stop existing timer if any
|
|
13
|
-
this.stopTimer(containerId);
|
|
14
|
-
const startedAt = new Date();
|
|
15
|
-
const timeoutAt = new Date(startedAt.getTime() + options.maxTimeout * 1000);
|
|
16
|
-
const gracePeriod = options.gracePeriod ?? this.defaultGracePeriod;
|
|
17
|
-
const state = {
|
|
18
|
-
containerId,
|
|
19
|
-
startedAt,
|
|
20
|
-
timeoutAt,
|
|
21
|
-
status: 'running',
|
|
22
|
-
};
|
|
23
|
-
// Set up timeout timer
|
|
24
|
-
state.timeoutTimer = setTimeout(async () => {
|
|
25
|
-
this.emit('timeout', { containerId, state });
|
|
26
|
-
state.status = 'timed_out';
|
|
27
|
-
this.emit('stopping', { containerId, gracePeriod });
|
|
28
|
-
// Set up kill timer after timeout
|
|
29
|
-
state.killTimer = setTimeout(() => {
|
|
30
|
-
state.status = 'killed';
|
|
31
|
-
this.emit('kill', { containerId });
|
|
32
|
-
this.timers.delete(containerId);
|
|
33
|
-
}, gracePeriod * 1000);
|
|
34
|
-
}, options.maxTimeout * 1000);
|
|
35
|
-
this.timers.set(containerId, state);
|
|
36
|
-
this.emit('started', { containerId, timeoutAt });
|
|
37
|
-
return state;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Stop a timer (container completed successfully)
|
|
41
|
-
*/
|
|
42
|
-
stopTimer(containerId) {
|
|
43
|
-
const state = this.timers.get(containerId);
|
|
44
|
-
if (!state)
|
|
45
|
-
return;
|
|
46
|
-
// Clear timers
|
|
47
|
-
if (state.timeoutTimer) {
|
|
48
|
-
clearTimeout(state.timeoutTimer);
|
|
49
|
-
}
|
|
50
|
-
if (state.killTimer) {
|
|
51
|
-
clearTimeout(state.killTimer);
|
|
52
|
-
}
|
|
53
|
-
if (state.status === 'running') {
|
|
54
|
-
state.status = 'completed';
|
|
55
|
-
this.emit('completed', { containerId, state });
|
|
56
|
-
}
|
|
57
|
-
this.timers.delete(containerId);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get timer state for a container
|
|
61
|
-
*/
|
|
62
|
-
getState(containerId) {
|
|
63
|
-
return this.timers.get(containerId);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Get remaining time in seconds for a container
|
|
67
|
-
*/
|
|
68
|
-
getRemainingTime(containerId) {
|
|
69
|
-
const state = this.timers.get(containerId);
|
|
70
|
-
if (!state || state.status !== 'running')
|
|
71
|
-
return undefined;
|
|
72
|
-
const remaining = Math.max(0, (state.timeoutAt.getTime() - Date.now()) / 1000);
|
|
73
|
-
return remaining;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Get all active timers
|
|
77
|
-
*/
|
|
78
|
-
getActiveTimers() {
|
|
79
|
-
return Array.from(this.timers.values()).filter(s => s.status === 'running');
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Clear all timers
|
|
83
|
-
*/
|
|
84
|
-
clearAll() {
|
|
85
|
-
for (const [containerId] of this.timers) {
|
|
86
|
-
this.stopTimer(containerId);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
exports.TimeoutManager = TimeoutManager;
|